/**********************************************************************
* dupescan109 - Diablo II 1.09d Dupescanner *
* written by Florian 'fw' Weingarten *
* *
* See README for additional information on implementation and usage. *
* *
* This piece of software is distributed under the terms of the *
* GNU General Public License (GPL). The complete license comes *
* with this file package. *
**********************************************************************
* CHANGELOG: *
* - Aug 13 2006: fixed a possible heap overflow if the number of *
* items which is told us by the JM header does not match the *
* actually following JMs (items), added a realloc if buffer is *
* too small
* - Aug 10 2006: fixed a possible misinterpretation of JM in the *
* binary file which lead to a possible memory allocation error *
* and added some additional debug information *
**********************************************************************/
#define DEFAULT_DIRNAME "/home/diablo/var/charsave"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define COLOROK "\033[32;;32m"
#define COLORERROR "\033[31;;31m"
#define COLORCLOSE "\033[0;;0m"
#define COLORDEBUG "\033[33;;33m"
#define COLORDUPE COLORERROR
struct fingerprintcontainer {
int first:7;
int data:32;
int last:1;
} __attribute__((__packed__));
typedef struct fingerprintcontainer fpc;
fpc *fpcptr;
struct itemtypecontainer {
int first:4;
int one:8;
int two:8;
int three:8;
int four:8;
int last:4;
} __attribute__((__packed__));
typedef struct itemtypecontainer itc;
itc *itcptr;
struct character {
char* charname;
char* filename;
struct item* firstitem;
};
struct item {
int number;
int base;
int length;
char* itemtype;
unsigned long fingerprint;
struct item* next;
};
int debug=0, verbose=0, overalldupes=0;
char *me;
void fehler(char*, char*);
struct character* parsefile(char *);
void dumpchar(struct character*);
void char_compare(struct character*, struct character*);
void usage(void)
{
puts("");
puts("Diablo II 1.09 Dupe Scanner");
puts("===========================");
puts(" -s[directory]\tScan files in directory for duplicate items");
printf("\t\t(default directory=%s)\n", DEFAULT_DIRNAME);
puts(" -v\t\tBe verbose. Print the itemlist with fingerprints for each char");
puts(" -d\t\tDebug Mode. Print additional debugging information (-d includes -v)");
puts(" -q\t\tQuiet Mode. Just do the scanning, no status crap");
puts("");
puts(" -c[charfile]\tShow the itemlist of just one single character and dont do any dupe scanning");
puts(" -d\t\tDebug Mode. Print additional debugging information");
puts("");
exit(EXIT_SUCCESS);
}
int main(int argc, char **argv)
{
char *dirname=NULL, *filename=NULL, c, mode=0;
DIR* dir;
struct dirent* dirlist;
int filecount=0, charcount=0, size, i, j;
struct character **charlist, *singlechar;
me = argv[0];
dirname = malloc(strlen(DEFAULT_DIRNAME)+1);
strcpy(dirname, DEFAULT_DIRNAME);
while((c = getopt(argc, argv, "vdsq::c:h")) != -1) {
switch(c)
{
case 's':
if(optarg != NULL) {
free(dirname);
dirname = malloc(strlen(optarg)+1);
strcpy(dirname, optarg);
}
if(mode == 0)
mode=c;
break;
case 'v':
if(verbose == 0)
verbose=1;
break;
case 'd':
debug=verbose=1;
break;
case 'c':
if(mode == 0) {
mode=c;
if(optarg != NULL) {
filename = malloc(strlen(optarg)+1);
strcpy(filename, optarg);
}
}
break;
case 'q':
if(verbose == 0)
verbose = -1;
break;
default:
usage();
break;
}
}
if(argc == 1) {
usage();
}
if(mode == 0) {
puts("You have to use -c or -s!");
free(dirname);
exit(EXIT_SUCCESS);
}
if(mode == 's') {
if((dir = opendir(dirname)) == NULL) {
fehler(strerror(errno), dirname);
}
while((dirlist = readdir(dir)) != NULL) {
filecount++;
}
rewinddir(dir);
charlist = malloc(filecount * sizeof(struct character*));
while((dirlist = readdir(dir)) != NULL) {
if(strcmp(dirlist->d_name, ".") != 0 && strcmp(dirlist->d_name, "..") != 0) {
size = strlen(dirname) + strlen(dirlist->d_name) + 1 + 1;
filename = malloc(size);
snprintf(filename, size, "%s/%s", dirname, dirlist->d_name);
charlist[charcount] = parsefile(filename);
if(charlist[charcount] != NULL) {
if(verbose==1) dumpchar(charlist[charcount]);
charcount++;
}
free(filename);
}
}
printf("\nChecking for Dupes in %d files...\n", charcount-1);
// compare chars
for(i=0; ifirstitem) == NULL) {
return;
}
while(itemone != NULL) {
itemtwo = two->firstitem;
while(itemtwo != NULL) {
if(itemone->fingerprint == itemtwo->fingerprint && itemone->fingerprint != 0) {
// not really sure about that. ibk and tbk seem to be tp and id books
// not sure why they seem to be extended items either.. just ignore them for now
if(strcmp(itemone->itemtype, "ibk ") == 0 || strcmp(itemone->itemtype, "tbk ") == 0) {
itemtwo = itemtwo->next;
continue;
}
printf("%sPOSSIBLE DUPE:%s \"%s\" and \"%s\" both have an item of type \"%s\" with fingerprint 0x%lx\n", COLORDUPE, COLORCLOSE, one->charname, two->charname, itemone->itemtype, itemone->fingerprint);
overalldupes++;
}
itemtwo = itemtwo->next;
}
itemone = itemone->next;
}
}
void dumpchar(struct character* theChar)
{
struct item* ptr;
if((ptr = theChar->firstitem) == NULL) {
return;
}
printf("%s has the following extended items\n", theChar->charname);
while(ptr != NULL) {
if(ptr->fingerprint != 0) {
if(debug) printf(" Adr: %p, next: %p,", (void*)ptr, (void*)ptr->next);
printf(" Number: %3d, Base: %4d, Length: %3d, Fingerprint: 0x%08lx, Item type: %s\n", ptr->number, ptr->base, ptr->length, ptr->fingerprint, ptr->itemtype);
}
ptr = ptr->next;
}
puts("");
}
struct character* parsefile(char *filename)
{
FILE *file;
char *buf, *data;
int size, i, bufsize, jmcount=0;
short *itemcount=NULL, myitemcount=0;
struct stat *filestruct = malloc(sizeof(struct stat));
int itemlist_base=-1, itemnumber=0;
struct character* theChar = malloc(sizeof(struct character));
struct item** theItemlist = NULL;
size = strlen(filename)+1;
if((theChar->filename = malloc(size)) == NULL)
fehler("Memory allocation error", filename);
strncpy(theChar->filename, filename, size);
// 15 bytes for charname + nullbyte
theChar->charname = malloc(16);
theChar->firstitem = NULL;
if(stat(filename, filestruct) == -1) {
fehler(strerror(errno), filename);
}
bufsize = filestruct->st_size;
if((buf = malloc(bufsize)) == NULL) {
fehler("Unable to allocate memory for file buffer", filename);
}
if((file = fopen(filename, "r")) == NULL) {
fehler(strerror(errno), filename);
}
while(!feof(file)) {
size = fread(buf, 1, bufsize+1, file);
if(debug) printf("%sDEBUG:%s read %d bytes of file \"%s\"\n", COLORDEBUG, COLORCLOSE, size, filename);
}
strncpy(theChar->charname, &buf[20], 16);
if((int)buf[4] != 92) {
if(verbose != -1)
printf("%sError%s: %s is not a valid v1.09 character file or he has never been in a game (File version is %d, should be 92)\n", COLORERROR, COLORCLOSE, filename, buf[4]);
if(verbose==1) puts("");
free(filestruct);
free(theChar->filename);
free(theChar->charname);
free(theChar);
free(buf);
if(debug) puts("");
return NULL;
}
if(verbose != -1)
printf("%sOK%s: Read %4d bytes of character data for \"%s\" from \"%s\"...\n", COLOROK, COLORCLOSE, size, theChar->charname, theChar->filename);
// check if bit 3 (fourth) is set (0x8 = 1000)
if(buf[36] & 0x8) {
if(verbose != -1)
printf("%sError%s: %s (%s) is not alive.. skipping..\n", COLORERROR, COLORCLOSE, theChar->charname, theChar->filename);
if(verbose==1) puts("");
free(filestruct);
free(theChar->filename);
free(theChar->charname);
free(theChar);
free(buf);
if(debug) puts("");
return NULL;
}
for(i=0; i *itemcount) {
if(debug) printf("%sDEBUG%s: itemnumber is larger than expected (%d>%d). Enlarging buffer.\n", COLORDEBUG, COLORCLOSE, itemnumber, *itemcount);
theItemlist = realloc(theItemlist, (itemnumber+1) * sizeof(struct item*));
if(theItemlist == NULL) {
fehler("Memory allocation error (realloc())", filename);
}
}
}
if(itemlist_base == -1) {
if(i+5 >= bufsize || buf[i+4] != 'J' || buf[i+5] != 'M') {
if(debug) printf("%sDEBUG:%s %s: Found first JM at buf[%d] but it is not followed by another JM, so its not the itemlist base! Skipping.\n", COLORDEBUG, COLORCLOSE, filename, i);
continue;
}
itemcount = (short*)&buf[i+2];
if(debug) printf("%sDEBUG:%s %s: Found first JM: Itemlist base is buf[%d], following %hd items (JM 0x%x 0x%x)\n", COLORDEBUG, COLORCLOSE, filename, i, *itemcount, buf[i+2], buf[i+3]);
itemlist_base = i;
if((theItemlist = malloc((*itemcount+1) * sizeof(struct item*))) == NULL) {
fehler("Memory allocation error", filename);
}
} else if(buf[i+2] == 0 && buf[i+3] == 0) {
if(*itemcount != 0) {
if(itemnumber>0) {
theItemlist[itemnumber-1]->length = (i) - theItemlist[itemnumber-1]->base;
if(debug) printf("%sDEBUG:%s %s: Found itembase %2d at buf[%04d], length: %2d\n", COLORDEBUG, COLORCLOSE, filename, itemnumber-1, theItemlist[itemnumber-1]->base, theItemlist[itemnumber-1]->length);
}
}
if(debug) printf("%sDEBUG:%s %s: Found last JM: Itemlist ends at buf[%d]\n", COLORDEBUG, COLORCLOSE, filename, i-1);
break;
} else {
// printf("unknown: %x\n", buf[2] >> 4);
data = &buf[i];
itcptr = (itc*)&data[9];
theItemlist[itemnumber] = malloc(sizeof(struct item));
theItemlist[itemnumber]->itemtype = malloc(5);
sprintf(theItemlist[itemnumber]->itemtype, "%c%c%c%c", itcptr->one, itcptr->two, itcptr->three, itcptr->four);
theItemlist[itemnumber]->number = itemnumber;
theItemlist[itemnumber]->next = NULL;
theItemlist[itemnumber]->fingerprint = 0;
theItemlist[itemnumber]->base = i;
theItemlist[itemnumber]->length = 0;
if(itemnumber == 0) {
theChar->firstitem = theItemlist[0];
} else {
myitemcount++;
theItemlist[itemnumber-1]->length = theItemlist[itemnumber]->base - theItemlist[itemnumber-1]->base;
theItemlist[itemnumber-1]->next = theItemlist[itemnumber];
if(debug) {
printf("%sDEBUG:%s %s: Found itembase %2d at buf[%04d], length: %2d\n", COLORDEBUG, COLORCLOSE, filename, itemnumber-1, theItemlist[itemnumber-1]->base, theItemlist[itemnumber-1]->length);
}
}
itemnumber++;
}
}
}
// if the char does not have any items: set the first item to zero
if(jmcount == 0 && itemcount == NULL) {
theChar->firstitem = NULL;
}
for(i=0; i < myitemcount; i++) {
// standard items are 14 bytes long, everything thats longer is an extended item
// 19: dunno
// 20: -
// 21: itemtype=box (maybe cube?)
// 22: key
// 23: ibk, tbk (identify/townportal??)
// HACK: again, not really sure about that stuff here.. we just ignore everything
// that is shorter than 23 bytes for now
if(theItemlist[i]->length >= 23) {
// read the bitstructure at offset 13
data = &buf[theItemlist[i]->base];
fpcptr = (fpc*)&data[13];
theItemlist[i]->fingerprint = fpcptr->data;
if(debug) printf("%sDEBUG:%s Item %3d is an extended item and therefore has a Fingerprint (0x%lx) (type=%s)\n", COLORDEBUG, COLORCLOSE, i, theItemlist[i]->fingerprint, theItemlist[i]->itemtype);
}
}
if(debug) printf("%sDEBUG:%s End of file reached for %s\n", COLORDEBUG, COLORCLOSE, filename);
// if(theItemlist != NULL) free(theItemlist);
free(filestruct);
free(buf);
return theChar;
}
void fehler(char *fehler, char *filename)
{
fprintf(stderr, "%s: %s (%s)\n", me, fehler, filename);
fputs("Exiting.", stderr);
exit(EXIT_FAILURE);
}