/* * Neverwinter Nights MOD file parser example. * * This small program will read a MOD file and output the key/value pairs * of the main module description file. It is simply an example of how * to parse the MOD file format, as well as the Key/Value filetype so commonly * used by NWN. * * (c) Copyright 2002 Sean Kasun * Use however you wish. */ #include typedef unsigned char byte; // hey, old assembly habits die hard. typedef unsigned long dword; // must have my types :) typedef struct { char signature[4]; char version[4]; dword unknown; dword descriptionBlockSize; dword numFiles; dword descriptionBlockOffset; dword fileHeaderBlockOffset; dword lookupBlockOffset; dword unknown2; dword unknown3; dword terminator; } MODHeader; typedef struct { char filename[16]; dword index; dword type; } MODFileHeader; typedef struct { char type[4]; char version[4]; dword groupOffset; dword numGroups; dword itemOffset; dword numItems; dword keyOffset; dword numKeys; dword stringTableOffset; dword stringTableSize; dword nodeTableOffset; dword nodeTableSize; dword listTableOffset; dword listTableSize; } IFOHeader; typedef struct { dword type; dword startItem; dword numItems; } GroupEntry; typedef struct { dword type; dword key; dword value; } ItemEntry; typedef struct { IFOHeader header; GroupEntry *groups; ItemEntry *items; byte *keys; byte *strings; dword *nodes; dword *lists; } IFO; static void descModule(dword offset,dword size,FILE *f); static void printGroup(IFO *ifo,dword id,dword indent); static void printItem(IFO *ifo,dword id,dword indent); char *varTypes[]={ "Byte","Unknown1","ID","HP","Hex","LongInt","Unknown6", "Unknown7","Float","Unknown9","String","Key","Text", "Identifier","Unknown14","List" }; int main(int argc,char *argv[]) { FILE *f; MODHeader header; MODFileHeader fileHeader; int i; if (argc!=2) { printf("Usage: %s [filename.mod]\n",argv[0]); exit(1); } if (!(f=fopen(argv[1],"r"))) { printf("Couldn't open %s\n",argv[1]); exit(1); } /* Now let's read the MOD Header */ fread(&header,1,sizeof(MODHeader),f); if (strncmp(header.signature,"MOD ",4) || strncmp(header.version,"V1.0",4)) { printf("Error: %s is not a supported format\n",argv[1]); exit(1); } /* Read the files, looking for module.ifo */ fseek(f,header.fileHeaderBlockOffset,SEEK_SET); for (i=0;igroups[id].numItems==1) printItem(ifo,ifo->groups[id].startItem,indent); else { /* we divide by 4 because it's an index into a byte array, and we're using it as an index into a dword array */ item=ifo->groups[id].startItem/4; /* now loop through all the nodes and print each item */ for (i=0;igroups[id].numItems;i++) printItem(ifo,ifo->nodes[item+i],indent); } } /* For generating indentation */ static char tabStr[255]; static char *tab(dword len) { int i; if (len>254) len=254; // we only have 254+NULL characters memset(tabStr,' ',len); tabStr[len]=0; return tabStr; } static void printItem(IFO *ifo,dword id,dword indent) { /* union for converting from dword-packed float, into real float */ union { long l; float f; } fl; char key[17],*string; dword slen,i,list,numStrs,strOffs; /* look up the key for the item */ memcpy(key,ifo->keys+ifo->items[id].key*16,16); key[16]=0; /* remember, not always NULL terminated */ /* output our tabs, our type, and our key */ printf("%s%s %s=",tab(indent),varTypes[ifo->items[id].type],key); switch (ifo->items[id].type) { case 0: //byte printf("%d\n",ifo->items[id].value); break; case 2: //ID printf("0x%x\n",ifo->items[id].value); break; case 3: //HP printf("%d\n",ifo->items[id].value); break; case 4: //HEX printf("0x%x\n",ifo->items[id].value); break; case 5: //large int printf("%ld\n",ifo->items[id].value); break; case 8: //float fl.l=ifo->items[id].value; printf("%f\n",fl.f); break; case 10: //string slen=*(dword *)(ifo->strings+ifo->items[id].value); string=(char *)malloc(slen+1); memcpy(string,ifo->strings+ifo->items[id].value+4,slen); string[slen]=0; printf("\"%s\"\n",string); free(string); break; case 11: //key slen=ifo->strings[ifo->items[id].value]; string=(char *)malloc(slen+1); memcpy(string,ifo->strings+ifo->items[id].value+1,slen); string[slen]=0; printf("\"%s\"\n",string); free(string); break; case 12: //text numStrs=*(dword *)(ifo->strings+ifo->items[id].value+8); strOffs=ifo->items[id].value+12; for (i=0;istrings+strOffs+4); string=(char *)malloc(slen+1); memcpy(string,ifo->strings+strOffs+8,slen); string[slen]=0; printf("\"%s\"\n",string); free(string); strOffs+=slen+8; } break; case 13: //Identifier printf("%d\n",ifo->items[id].value); break; case 15: //list printf("{\n"); list=ifo->items[id].value/4; for (i=0;ilists[list];i++) printGroup(ifo,ifo->lists[list+i+1],indent+2); printf("%s}\n",tab(indent)); break; default: //everything else, treat as int printf("%d\n",ifo->items[id].value); break; } }