#include #include #define _COMMON_NO_THREADS #include "common.h" #include "Auth/MD5Hash.h" #include "tools.h" #include "MPQHelper.h" #include "dbcfile.h" #include "ADTFile.h" #include "WDTFile.h" #include "StuffExtract.h" #include "DBCFieldData.h" #include "Locale.h" #include "ProgressBar.h" #include "../../Client/Gui/CM2MeshFileLoader.h" int replaceSpaces (int i) { return i==(int)' ' ? (int)'_' : i; } std::map mapNames; std::set texNames; std::set modelNames; std::set wmoNames; std::set soundFileSet; // default config; SCPs are done always bool doMaps=true, doSounds=false, doTextures=false, doWmos=false, doModels=false, doMd5=true, doAutoclose=false; int main(int argc, char *argv[]) { char input[200]; printf("StuffExtract [version %u]\n",SE_VERSION); printf("Use -help or -? to display help about command line arguments and config.\n\n",SE_VERSION); ProcessCmdArgs(argc, argv); PrintConfig(); if(!GetLocale()) { printf("Enter your locale (enUS, enGB, deDE, ...) or leave blank to autodetect: "); fgets(input,sizeof(input),stdin); char loc[5]; input[strlen(input)-1] = 0; memcpy(loc,input,4); loc[4]=0; SetLocale(loc); } if(GetLocale() && FileExists(std::string("Data/")+GetLocale()+"/locale-"+GetLocale()+".MPQ")) { printf("Locale \"%s\" seems valid, starting conversion...\n",GetLocale()); CreateDir("stuffextract"); CreateDir("stuffextract/data"); ConvertDBC(); if(doMaps) ExtractMaps(); if(doTextures || doModels || doWmos) ExtractMapDependencies(); if(doSounds) ExtractSoundFiles(); //... if (!doAutoclose) printf("\n -- finished, press enter to exit --\n"); } else { printf("ERROR: Invalid locale \"%s\"! Press Enter to exit...\n",GetLocale()); } if (!doAutoclose) fgets(input,sizeof(input),stdin); //while(true); return 0; } void ProcessCmdArgs(int argc, char *argv[]) { bool on,help=false; char *what; for(int i = 1; i < argc; i++) { if(strlen(argv[i]) > 1) { if(argv[i][0] == '-') on = false; else if(argv[i][0] == '+') on = true; else if(!stricmp(argv[i],"/?") || !stricmp(argv[i],"/help")) { help = true; break; } else { printf("Incorrect cmd arg: \"%s\"\n",argv[i]); continue; } what = argv[i]+1; // skip first byte (+/-) if (!stricmp(what,"maps")) doMaps = on; else if(!stricmp(what,"textures")) doTextures = on; else if(!stricmp(what,"wmos")) doWmos = on; else if(!stricmp(what,"models")) doModels = on; else if(!stricmp(what,"sounds")) doSounds = on; else if(!stricmp(what,"md5")) doMd5 = on; else if(!stricmp(what,"autoclose")) doAutoclose = on; // autodetect or use given locale. + or - as arg start doesnt matter here else if(!strnicmp(what,"locale:",7)) { if(!stricmp(what+7,"auto")) SetLocale(NULL); else SetLocale(what+7); } else if(!stricmp(what,"?") || !stricmp(what,"help")) { help = true; break; } else { printf("Unknown cmd arg: \"%s\"\n",what); } } } // fix up wrong configs. not necessary but better for display. // TODO: as soon as M2 model or WMO reading is done, extract those textures to, but independent from maps!! if(!doMaps) { doWmos = false; } if(help) { PrintHelp(); printf("\n\n- Press any key to exit -\n"); getchar(); exit(0); } } void PrintConfig(void) { printf("config: Do maps: %s\n",doMaps?"yes":"no"); printf("config: Do textures: %s\n",doTextures?"yes":"no"); printf("config: Do wmos: %s\n",doWmos?"yes":"no"); printf("config: Do models: %s\n",doModels?"yes":"no"); printf("config: Do sounds: %s\n",doSounds?"yes":"no"); printf("config: Calc md5: %s\n",doMd5?"yes":"no"); printf("config: Autoclose: %s\n",doAutoclose?"yes":"no"); } void PrintHelp(void) { printf("Usage information:\n\n"); printf("Use + or - to turn a feature on or off.\n"); printf("Features are:\n"); printf("maps - map extraction\n"); printf("textures - extract textures\n"); printf("wmos - extract map WMOs (requires maps extraction)\n"); printf("models - extract models\n"); printf("sounds - extract sound files (wav/mp3)\n"); printf("md5 - write MD5 checksum lists of extracted files\n"); printf("autoclose - close program when done\n"); printf("\n"); printf("Use -locale:xxXX to set a locale. If you don't use this, you will be asked.\n"); printf("Use -locale:auto to autodetect currently used locale.\n"); printf("\n"); printf("Examples:\n"); printf("stuffextract +sounds +md5 -maps +autoclose -locale:enGB\n"); printf("stuffextract +md5 -wmos -sounds -locale:auto -autoclose\n"); printf("\nDefault is: +maps -sounds -textures -wmos -models +md5 -autoclose\n"); } // be careful using this, that you supply correct format string std::string AutoGetDataString(DBCFile::Iterator& it, const char* format, uint32 field, bool skip_null = true) { if(format[field]=='i') { if((*it).getInt(field) == 0 && skip_null) return ""; // do not explicitly write int fields that are 0 std::stringstream s; s << (*it).getInt(field); return s.str(); } else if(format[field]=='f') { if((*it).getFloat(field) == 0 && skip_null) return ""; // do not explicitly write float fields that are 0 std::stringstream s; s << (*it).getFloat(field); return s.str(); } else if(format[field]=='s' && (*it).getUInt(field)) { return (*it).getString(field); } return ""; } // output a formatted scp file void OutSCP(char *fn, SCPStorageMap& scp, std::string dbName="") { std::fstream f; f.open(fn, std::ios_base::out); if(f.is_open()) { if(dbName.length()) { f << "#dbname=" << dbName << "\n"; } for(SCPStorageMap::iterator mi = scp.begin(); mi != scp.end(); mi++) { f << "[" << mi->first << "]\n"; for(std::list::iterator li = mi->second.begin(); li != mi->second.end(); li++) { f << *li << "\n"; } f << "\n"; } f.close(); } else { printf("OutSCP: unable to write '%s'\n",fn); } } void OutMD5(char *path, MD5FileMap& fm) { if(!doMd5) return; std::string fullname(path); fullname += "/md5.txt"; printf("Writing MD5 file checksums to '%s'\n",fullname.c_str()); std::fstream fh; fh.open(fullname.c_str(), std::ios_base::out); if(fh.is_open()) { for(MD5FileMap::iterator i = fm.begin(); i != fm.end(); i++) { fh << i->first << "|" << toHexDump(i->second,MD5_DIGEST_LENGTH,false) << std::endl; // write file content delete [] i->second; // and delete previously allocated memory } fh.close(); } else { printf("Couldn't output MD5 list to '%s'\n",fullname.c_str()); } } bool ConvertDBC(void) { std::map racemap; // needed to extract other dbc files correctly SCPStorageMap EmoteDataStorage,RaceDataStorage,SoundDataStorage,MapDataStorage,ZoneDataStorage,ItemDisplayInfoStorage, CreatureModelStorage,CreatureDisplayInfoStorage,NPCSoundStorage,CharSectionStorage; // will store the converted data from dbc files DBCFile EmotesText,EmotesTextData,EmotesTextSound,ChrRaces,SoundEntries,Map,AreaTable,ItemDisplayInfo, CreatureModelData,CreatureDisplayInfo,NPCSounds,CharSections; printf("Opening DBC archive...\n"); MPQHelper mpq("dbc"); printf("Opening DBC files...\n"); EmotesText.openmem(mpq.ExtractFile("DBFilesClient\\EmotesText.dbc")); EmotesTextData.openmem(mpq.ExtractFile("DBFilesClient\\EmotesTextData.dbc")); EmotesTextSound.openmem(mpq.ExtractFile("DBFilesClient\\EmotesTextSound.dbc")); ChrRaces.openmem(mpq.ExtractFile("DBFilesClient\\ChrRaces.dbc")); SoundEntries.openmem(mpq.ExtractFile("DBFilesClient\\SoundEntries.dbc")); Map.openmem(mpq.ExtractFile("DBFilesClient\\Map.dbc")); AreaTable.openmem(mpq.ExtractFile("DBFilesClient\\AreaTable.dbc")); ItemDisplayInfo.openmem(mpq.ExtractFile("DBFilesClient\\ItemDisplayInfo.dbc")); CreatureModelData.openmem(mpq.ExtractFile("DBFilesClient\\CreatureModelData.dbc")); CreatureDisplayInfo.openmem(mpq.ExtractFile("DBFilesClient\\CreatureDisplayInfo.dbc")); NPCSounds.openmem(mpq.ExtractFile("DBFilesClient\\NPCSounds.dbc")); CharSections.openmem(mpq.ExtractFile("DBFilesClient\\CharSections.dbc")); //... printf("DBC files opened.\n"); //... printf("Reading data: races.."); for(DBCFile::Iterator it = ChrRaces.begin(); it != ChrRaces.end(); ++it) { uint32 id = (*it).getUInt(CHRRACES_RACEID); racemap[id] = (*it).getString(CHRRACES_NAME_GENERAL); // for later use for(uint32 field=CHRRACES_RACEID; field < CHARRACES_END; field++) { if(strlen(ChrRacesFieldNames[field])) { std::string value = AutoGetDataString(it,ChrRacesFormat,field); if(!value.empty()) RaceDataStorage[id].push_back(std::string(ChrRacesFieldNames[field]).append("=").append(value)); } } } printf("emotes.."); for(DBCFile::Iterator it = EmotesText.begin(); it != EmotesText.end(); ++it) { uint32 em = (*it).getUInt(EMOTESTEXT_EMOTE_ID); EmoteDataStorage[em].push_back(std::string("name=") + (*it).getString(EMOTESTEXT_EMOTE_STRING)); EmoteDataStorage[em].push_back(std::string("anim=") + toString( (*it).getUInt(EMOTESTEXT_ANIM)) ); for(uint32 field=EMOTESTEXT_EMOTE_ID; fieldgetInt(MAP_ID)] = it->getString(MAP_NAME_GENERAL); uint32 id = it->getUInt(MAP_ID); for(uint32 field=MAP_ID; field < MAP_END; field++) { if(strlen(MapFieldNames[field])) { std::string value = AutoGetDataString(it,MapFormat,field); if(value.size()) // only store if not null MapDataStorage[id].push_back(std::string(MapFieldNames[field]) + "=" + value); } } } printf("zonedata.."); for(DBCFile::Iterator it = AreaTable.begin(); it != AreaTable.end(); ++it) { uint32 id = it->getUInt(MAP_ID); for(uint32 field=AREATABLE_ID; field < AREATABLE_END; field++) { if(strlen(AreaTableFieldNames[field])) { std::string value = AutoGetDataString(it,AreaTableFormat,field,false); if(value.size()) // only store if not null ZoneDataStorage[id].push_back(std::string(AreaTableFieldNames[field]) + "=" + value); } } } printf("itemdisplayinfo.."); for(DBCFile::Iterator it = ItemDisplayInfo.begin(); it != ItemDisplayInfo.end(); ++it) { uint32 id = it->getUInt(ITEMDISPLAYINFO_ID); for(uint32 field=ITEMDISPLAYINFO_ID; field < ITEMDISPLAYINFO_END; field++) { if(strlen(ItemDisplayInfoFieldNames[field])) { // TODO: need to get std::string value = AutoGetDataString(it,ItemDisplayInfoFormat,field); if(value.size()) // only store if not null ItemDisplayInfoStorage[id].push_back(std::string(ItemDisplayInfoFieldNames[field]) + "=" + value); } } } printf("creaturemodeldata.."); for(DBCFile::Iterator it = CreatureModelData.begin(); it != CreatureModelData.end(); ++it) { uint32 id = it->getUInt(CREATUREMODELDATA_ID); for(uint32 field=CREATUREMODELDATA_ID; field < CREATUREMODELDATA_END; field++) { if(strlen(CreatureModelDataFieldNames[field])) { std::string value = AutoGetDataString(it,CreatureModelDataFormat,field); if(value.size()) // only store if not null { if(doModels) modelNames.insert(NameAndAlt(value)); // we need to extract model later, store it std::string fn = _PathToFileName(value); if(stricmp(fn.c_str()+fn.length()-4, "mdx")) fn = fn.substr(0,fn.length()-3) + "m2"; CreatureModelStorage[id].push_back(std::string(CreatureModelDataFieldNames[field]) + "=" + fn); } } } } printf("creaturedisplayinfo.."); for(DBCFile::Iterator it = CreatureDisplayInfo.begin(); it != CreatureDisplayInfo.end(); ++it) { uint32 id = it->getUInt(CREATUREDISPLAYINFO_ID); uint32 modelid = it->getUInt(CREATUREDISPLAYINFO_MODEL); for(uint32 field=CREATUREDISPLAYINFO_ID; field < CREATUREDISPLAYINFO_END; field++) { if(strlen(CreatureDisplayInfoFieldNames[field])) { std::string value = AutoGetDataString(it,CreatureDisplayInfoFormat,field); if(value.size()) // only store if not null { if(doTextures && field >= CREATUREDISPLAYINFO_NAME1 && field <= CREATUREDISPLAYINFO_NAME3) { // lookup for model path DBCFile::Iterator itm = CreatureModelData.begin(); for(; itm != CreatureDisplayInfo.end() && itm->getInt(CREATUREMODELDATA_ID) != modelid;) ++itm; std::string str = itm->getString(CREATUREMODELDATA_FILE); uint32 pathend = str.find_last_of("/\\"); if(pathend != std::string::npos) // replace model with texture name str = str.substr(0, pathend); str += "\\"; str += value; str += ".blp"; texNames.insert(NameAndAlt(str)); value = NormalizeFilename(str); } CreatureDisplayInfoStorage[id].push_back(std::string(CreatureDisplayInfoFieldNames[field]) + "=" + value); } } } } printf("npcsounds.."); for(DBCFile::Iterator it = NPCSounds.begin(); it != NPCSounds.end(); ++it) { uint32 id = it->getUInt(NPCSOUNDS_ID); for(uint32 field=NPCSOUNDS_ID; field < NPCSOUNDS_END; field++) { if(strlen(NPCSoundsFieldNames[field])) { std::string value = AutoGetDataString(it,NPCSoundsFormat,field); if(value.size()) // only store if not null NPCSoundStorage[id].push_back(std::string(NPCSoundsFieldNames[field]) + "=" + value); } } } printf("charsections.."); for(DBCFile::Iterator it = CharSections.begin(); it != CharSections.end(); ++it) { uint32 id = it->getUInt(CHARSECTIONS_ID); for(uint32 field=CHARSECTIONS_ID; field < CHARSECTIONS_END; field++) { if(strlen(CharSectionsFieldNames[field])) { std::string value = AutoGetDataString(it,CharSectionsFormat,field); if(value.size()) // only store if not null { // ok we have a little problem here: // some textures used for different races have the same file name, but we are storing them all // in one directory. Texture path format is: "Character\\ // so we have to use good names to store all textures without overwriting each other if(field >= CHARSECTIONS_TEXTURE1 && field <= CHARSECTIONS_TEXTURE3) { /*char buf[100]; sprintf(buf,"charsection_%u_%u_%u_%u_%u_%u.blp", it->getUInt(CHARSECTIONS_RACE_ID), it->getUInt(CHARSECTIONS_GENDER), it->getUInt(CHARSECTIONS_TYPE), it->getUInt(CHARSECTIONS_SECTION), it->getUInt(CHARSECTIONS_COLOR), field - CHARSECTIONS_TEXTURE1); // texture ID */ texNames.insert(NameAndAlt(value)); } CharSectionStorage[id].push_back(std::string(CharSectionsFieldNames[field]) + "=" + NormalizeFilename(value)); } } } } //... printf("DONE!\n"); //... CreateDir("stuffextract/data/scp"); printf("Writing SCP files:\n"); printf("emote.."); OutSCP(SCPDIR "/emote.scp",EmoteDataStorage, "emote"); printf("race.."); OutSCP(SCPDIR "/race.scp",RaceDataStorage, "race"); printf("sound.."); OutSCP(SCPDIR "/sound.scp",SoundDataStorage, "sound"); printf("map.."); OutSCP(SCPDIR "/map.scp",MapDataStorage, "map"); printf("area.."); OutSCP(SCPDIR "/zone.scp",ZoneDataStorage, "zone"); printf("itemdisplayinfo.."); OutSCP(SCPDIR "/itemdisplayinfo.scp",ItemDisplayInfoStorage, "itemdisplayinfo"); printf("creaturemodeldata.."); OutSCP(SCPDIR "/creaturemodeldata.scp",CreatureModelStorage,"creaturemodeldata"); printf("creaturedisplayinfo.."); OutSCP(SCPDIR "/creaturedisplayinfo.scp",CreatureDisplayInfoStorage,"creaturedisplayinfo"); printf("npcsound.."); OutSCP(SCPDIR "/npcsound.scp",NPCSoundStorage,"npcsound"); printf("charsections.."); OutSCP(SCPDIR "/charsections.scp",CharSectionStorage,"charsections"); //... printf("DONE!\n"); // wait for all container destructors to finish printf("DBC files converted, cleaning up...\n"); return true; } void ExtractMaps(void) { printf("\nExtracting maps...\n"); char namebuf[200]; char outbuf[2000]; uint32 extr,extrtotal=0; MPQHelper mpq("terrain"); MD5FileMap md5map; CreateDir("stuffextract/data/maps"); for(std::map::iterator it = mapNames.begin(); it != mapNames.end(); it++) { // extract the WDT file that stores tile information char wdt_name[300], wdt_out[300]; sprintf(wdt_name,"World\\Maps\\%s\\%s.wdt",it->second.c_str(),it->second.c_str()); sprintf(wdt_out,MAPSDIR"/%u.wdt",it->first); ByteBuffer& wdt_bb = mpq.ExtractFile(wdt_name); std::fstream wdt_fh; wdt_fh.open(wdt_out, std::ios_base::out|std::ios_base::binary); if(!wdt_fh.is_open()) { printf("\nERROR: Map extraction failed: could not save file %s\n",wdt_out); return; } if (wdt_bb.size()) wdt_fh.write((char*)wdt_bb.contents(),wdt_bb.size()); wdt_fh.close(); printf("Extracted WDT '%s'\n",wdt_name); // then extract all ADT files extr=0; for(uint32 x=0; x<64; x++) { for(uint32 y=0;y<64; y++) { uint32 olddeps; uint32 depdiff; sprintf(namebuf,"World\\Maps\\%s\\%s_%u_%u.adt",it->second.c_str(),it->second.c_str(),x,y); sprintf(outbuf,MAPSDIR"/%u_%u_%u.adt",it->first,x,y); if(mpq.FileExists(namebuf)) { ByteBuffer& bb = mpq.ExtractFile(namebuf); if(bb.size()) { std::fstream fh; //printf("Extracting map [ %s ]\n",outbuf); fh.open(outbuf, std::ios_base::out|std::ios_base::binary); if(!fh.is_open()) { printf("\nERROR: Map extraction failed: could not save file %s\n",outbuf); return; } fh.write((char*)bb.contents(),bb.size()); fh.flush(); fh.close(); olddeps = texNames.size() + modelNames.size() + wmoNames.size(); if(doTextures) ADT_FillTextureData(bb.contents(),texNames); if(doModels) ADT_FillModelData(bb.contents(),modelNames); if(doWmos) ADT_FillWMOData(bb.contents(),wmoNames); depdiff = texNames.size() + modelNames.size() + wmoNames.size() - olddeps; if(doMd5) { MD5Hash h; h.Update((uint8*)bb.contents(), bb.size()); h.Finalize(); uint8 *md5ptr = new uint8[MD5_DIGEST_LENGTH]; md5map[_PathToFileName(outbuf)] = md5ptr; memcpy(md5ptr, h.GetDigest(), MD5_DIGEST_LENGTH); } extr++; printf("[%u:%u] %s; %u new deps.\n",extr,it->first,namebuf,depdiff); } } } } extrtotal+=extr; printf("\n"); } printf("\nDONE - %u maps extracted, %u total dependencies.\n",extrtotal, texNames.size() + modelNames.size() + wmoNames.size()); OutMD5(MAPSDIR,md5map); } void ExtractMapDependencies(void) { barGoLink *bar; printf("\nExtracting map dependencies...\n\n"); printf("- Preparing to read MPQ arcives...\n"); MPQHelper mpqmodel("model"); MPQHelper mpqtex("texture"); MPQHelper mpqwmo("wmo"); std::string path = "stuffextract/data"; std::string pathtex = path + "/texture"; std::string pathmodel = path + "/model"; std::string pathwmo = path + "/wmo"; std::string mpqfn,realfn,altfn; MD5FileMap md5Tex, md5Wmo, md5Model; CreateDir(pathtex.c_str()); CreateDir(pathmodel.c_str()); CreateDir(pathwmo.c_str()); uint32 wmosdone=0,texdone=0,mdone=0; if(doModels) { printf("Extracting models...\n"); bar = new barGoLink(modelNames.size(),true); for(std::set::iterator i = modelNames.begin(); i != modelNames.end(); i++) { bar->step(); mpqfn = i->name; // no idea what bliz intended by this. the ADT files refer to .mdx models, // however there are only .m2 files in the MPQ archives. // so we just need to check if there is a .m2 file instead of the .mdx file, and load that one. if(!mpqmodel.FileExists((char*)mpqfn.c_str())) { std::string alt = mpqfn.substr(0,mpqfn.length()-3) + "m2"; if(!mpqmodel.FileExists((char*)alt.c_str())) { printf("Failed to extract model: '%s'\n",alt.c_str()); continue; } else { mpqfn = alt; } } altfn = i->alt; if(altfn.empty()) altfn = mpqfn; realfn = pathmodel + "/" + _PathToFileName(altfn); std::fstream fh; fh.open(realfn.c_str(),std::ios_base::out | std::ios_base::binary); if(fh.is_open()) { ByteBuffer& bb = mpqmodel.ExtractFile((char*)mpqfn.c_str()); fh.write((const char*)bb.contents(),bb.size()); if (doTextures) FetchTexturesFromModel(bb); if(doMd5) { MD5Hash h; h.Update((uint8*)bb.contents(), bb.size()); h.Finalize(); uint8 *md5ptr = new uint8[MD5_DIGEST_LENGTH]; md5Model[_PathToFileName(realfn)] = md5ptr; memcpy(md5ptr, h.GetDigest(), MD5_DIGEST_LENGTH); } mdone++; } else printf("Could not write model %s\n",realfn.c_str()); fh.close(); } printf("\n"); if(modelNames.size()) OutMD5((char*)pathmodel.c_str(),md5Model); delete bar; } if(doTextures) { printf("Extracting textures...\n"); bar = new barGoLink(texNames.size(), true); for(std::set::iterator i = texNames.begin(); i != texNames.end(); i++) { bar->step(); mpqfn = i->name; altfn = i->alt; if(altfn.empty()) altfn = mpqfn; if(!mpqtex.FileExists((char*)mpqfn.c_str())) continue; // prepare lowercased and "underlined" path for file std::string copy = NormalizeFilename(mpqfn); if (copy.find_first_of("/") != std::string::npos) { std::string copy2 = copy; char* tok = strtok((char*)copy2.c_str(),"/"); std::string fullpath = pathtex; while (tok && !strstr(tok, ".blp")) { fullpath += "/"; fullpath += tok; CreateDir(fullpath.c_str()); tok = strtok(NULL, "/"); } } realfn = pathtex + "/" + copy; //_PathToFileName(altfn); std::fstream fh; fh.open(realfn.c_str(),std::ios_base::out | std::ios_base::binary); if(fh.is_open()) { ByteBuffer& bb = mpqtex.ExtractFile((char*)mpqfn.c_str()); fh.write((const char*)bb.contents(),bb.size()); if(doMd5) { MD5Hash h; h.Update((uint8*)bb.contents(), bb.size()); h.Finalize(); uint8 *md5ptr = new uint8[MD5_DIGEST_LENGTH]; md5Tex[_PathToFileName(realfn)] = md5ptr; memcpy(md5ptr, h.GetDigest(), MD5_DIGEST_LENGTH); } texdone++; } else printf("Could not write texture %s\n",realfn.c_str()); fh.close(); } printf("\n"); if(texNames.size()) OutMD5((char*)pathtex.c_str(),md5Tex); delete bar; } if(doWmos) { printf("Extracting WMOS...\n"); bar = new barGoLink(wmoNames.size(),true); for(std::set::iterator i = wmoNames.begin(); i != wmoNames.end(); i++) { bar->step(); mpqfn = i->name; altfn = i->alt; if(altfn.empty()) altfn = mpqfn; if(!mpqwmo.FileExists((char*)mpqfn.c_str())) continue; realfn = pathwmo + "/" + _PathToFileName(altfn); std::fstream fh; fh.open(realfn.c_str(),std::ios_base::out | std::ios_base::binary); if(fh.is_open()) { ByteBuffer& bb = mpqwmo.ExtractFile((char*)mpqfn.c_str()); fh.write((const char*)bb.contents(),bb.size()); if(doMd5) { MD5Hash h; h.Update((uint8*)bb.contents(), bb.size()); h.Finalize(); uint8 *md5ptr = new uint8[MD5_DIGEST_LENGTH]; md5Wmo[_PathToFileName(realfn)] = md5ptr; memcpy(md5ptr, h.GetDigest(), MD5_DIGEST_LENGTH); } wmosdone++; } else printf("Could not write WMO %s\n",realfn.c_str()); fh.close(); } printf("\n"); if(wmoNames.size()) OutMD5((char*)pathwmo.c_str(),md5Wmo); delete bar; } } void ExtractSoundFiles(void) { MD5FileMap md5data; uint32 done = 0; printf("\nExtracting game audio files, %u found in DBC...\n",soundFileSet.size()); CreateDir(SOUNDDIR); MPQHelper smpq("sound"); std::string outfn, altfn; barGoLink bar(soundFileSet.size(),true); for(std::set::iterator i = soundFileSet.begin(); i != soundFileSet.end(); i++) { bar.step(); if(!smpq.FileExists((char*)i->name.c_str())) { DEBUG( printf("MPQ: File not found: '%s'\n",i->name.c_str()) ); continue; } altfn = i->alt.empty() ? _PathToFileName(i->name) : i->alt; outfn = std::string(SOUNDDIR) + "/" + altfn; std::fstream fh; fh.open(outfn.c_str(), std::ios_base::out | std::ios_base::binary); if(fh.is_open()) { ByteBuffer& bb = smpq.ExtractFile((char*)i->name.c_str()); if(bb.size()) { fh.write((const char*)bb.contents(),bb.size()); if(doMd5) { MD5Hash h; h.Update((uint8*)bb.contents(), bb.size()); h.Finalize(); uint8 *md5ptr = new uint8[MD5_DIGEST_LENGTH]; md5data[altfn] = md5ptr; memcpy(md5ptr, h.GetDigest(), MD5_DIGEST_LENGTH); } done++; } } else { printf("Could not write sound file '%s'\n",outfn.c_str()); } fh.close(); } OutMD5(SOUNDDIR,md5data); printf("\n"); } void ADT_ExportStringSetByOffset(const uint8* data, uint32 off, std::set& st, char* stop) { data += ((uint32*)data)[off]; // seek to correct absolute offset data += 28; // move ptr to real start of data uint32 offset=0; std::string s; char c; while(memcmp(data+offset,stop,4)) { c = data[offset]; if(!c) { if(s.length()) { DEBUG(printf("DEP: %s\n",s.c_str())); st.insert(NameAndAlt(s)); s.clear(); } } else s += c; offset++; } } void ADT_FillTextureData(const uint8* data,std::set& st) { ADT_ExportStringSetByOffset(data,OFFSET_TEXTURES,st,"XDMM"); } void ADT_FillWMOData(const uint8* data,std::set& st) { ADT_ExportStringSetByOffset(data,OFFSET_WMOS,st,"DIWM"); } void ADT_FillModelData(const uint8* data,std::set& st) { ADT_ExportStringSetByOffset(data,OFFSET_MODELS,st,"DIMM"); } void FetchTexturesFromModel(ByteBuffer& bb) { bb.rpos(0); irr::scene::ModelHeader header; bb.read((uint8*)&header, sizeof(header)); if (header.version[0] != 4 && header.version[1] != 1 && header.version[2] != 0 && header.version[3] != 0) { printf("Not model file!"); return; } irr::core::array M2MTextureDef; M2MTextureDef.clear(); irr::scene::TextureDefinition tempM2TexDef; bb.rpos(header.ofsTextures); for(irr::u32 i=0;i