#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 "MPQLocale.h" #include "ProgressBar.h" #include "../../Client/GUI/CM2MeshFileLoader.h" #include "../../Client/GUI/CWMOMeshFileLoader.h" int replaceSpaces (int i) { return i==(int)' ' ? (int)'_' : i; } std::map mapNames; std::set texNames; std::set modelNames; std::set wmoNames; std::set wmoGroupNames; std::set soundFileSet; MPQHelper mpq; // default config; SCPs are done always bool doMaps=true, doSounds=false, doTextures=false, doWmos=false, doWmogroups=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"); 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("extractedstuff"); CreateDir("extractedstuff/data"); mpq.Init(); ConvertDBC(); if(doMaps) ExtractMaps(); if(doTextures || doModels || doWmos || doWmogroups) 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,"wmogroups")) doWmogroups = 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(!doWmos) { doWmogroups = 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 wmogroups: %s\n",doWmogroups?"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("wmogroups - extract map WMO group files (requires maps and wmos 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]=='c') { if((*it).getUChar(field) == 0 && skip_null) return ""; // do not explicitly write float fields that are 0 std::stringstream s; s << (int)(*it).getUChar(field); return s.str(); } else if(format[field]=='s' && (*it).getUInt(field)) { return (*it).getString(field); } return ""; } // output a formatted scp file void OutSCP(const 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(const 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 std::map classmask; //from CharBaseInfo.dbc SCPStorageMap EmoteDataStorage,RaceDataStorage,SoundDataStorage,MapDataStorage,ZoneDataStorage,ItemDisplayInfoStorage, CreatureModelStorage,CreatureDisplayInfoStorage,NPCSoundStorage,CharSectionStorage, GameObjectDisplayInfoStorage, ChrBaseInfoStorage; // will store the converted data from dbc files DBCFile EmotesText,EmotesTextData,EmotesTextSound,ChrRaces,SoundEntries,Map,AreaTable,ItemDisplayInfo, CreatureModelData,CreatureDisplayInfo,NPCSounds,CharSections,GameObjectDisplayInfo, ChrBaseInfo; printf("Opening DBC archive...\n"); 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")); GameObjectDisplayInfo.openmem(mpq.ExtractFile("DBFilesClient\\GameObjectDisplayInfo.dbc")); NPCSounds.openmem(mpq.ExtractFile("DBFilesClient\\NPCSounds.dbc")); CharSections.openmem(mpq.ExtractFile("DBFilesClient\\CharSections.dbc")); ChrBaseInfo.openmem(mpq.ExtractFile("DBFilesClient\\CharBaseInfo.dbc")); //... printf("DBC files opened.\n"); //... printf("Reading data: chrbaseinfo.."); for(DBCFile::Iterator it = ChrBaseInfo.begin(); it != ChrBaseInfo.end(); ++it) { uint32 race = (uint32)(*it).getUChar(CBI_RACE); uint32 cclass = (uint32)(*it).getUChar(CBI_CLASS); classmask[race] |= 1<getInt(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()-2) + "2"; value = value.substr(0,value.length()-2) + "2"; } CreatureModelStorage[id].push_back(std::string(CreatureModelDataFieldNames[field]) + "=" + fn); CreatureModelStorage[id].push_back("mpqfilename=" + value); } } } } 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->getUInt(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("gameobjectdisplayinfo.."); for(DBCFile::Iterator it = GameObjectDisplayInfo.begin(); it != GameObjectDisplayInfo.end(); ++it) { uint32 id = it->getUInt(GAMEOBJECTDISPLAYINFO_ID); for(uint32 field=GAMEOBJECTDISPLAYINFO_ID; field < GAMEOBJECTDISPLAYINFO_END; field++) { if(strlen(GameObjectDisplayInfoFieldNames[field])) { std::string value = AutoGetDataString(it,GameObjectDisplayInfoFormat,field); if(value.size()) // only store if not null { // TODO: add check for wmo model files ? if(doModels && stricmp(value.c_str()+value.length()-4,".wmo")) modelNames.insert(NameAndAlt(value)); // we need to extract model later, store it else if (doWmos && !stricmp(value.c_str()+value.length()-4,".wmo")) wmoNames.insert(NameAndAlt(value)); //this is a WMO //Interestingly, some of the files referenced here have MDL extension - WTF? std::string fn = _PathToFileName(value); if(!stricmp(fn.c_str()+fn.length()-3, "mdx") || !stricmp(fn.c_str()+fn.length()-3, "mdl")) { fn = fn.substr(0,fn.length()-2) + "2"; value = value.substr(0,value.length()-2) + "2"; } else printf("This should be a WMO: %s\n",fn.c_str()); GameObjectDisplayInfoStorage[id].push_back(std::string(GameObjectDisplayInfoFieldNames[field]) + "=" + fn); GameObjectDisplayInfoStorage[id].push_back("mpqfilename=" + value); std::string texture = value.substr(0,value.length()-3) + "blp"; if (mpq.FileExists((char*)texture.c_str())) { if(doTextures) texNames.insert(NameAndAlt(texture)); GameObjectDisplayInfoStorage[id].push_back("texture=" + texture); } } } } } 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("extractedstuff/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("gameobjectdisplayinfo.."); OutSCP(SCPDIR "/gameobjectdisplayinfo.scp",GameObjectDisplayInfoStorage,"gameobjectdisplayinfo"); 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; MD5FileMap md5map; CreateDir("extractedstuff/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"/%lu.wdt",it->first); const 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_%lu_%lu.adt",it->second.c_str(),it->second.c_str(),x,y); sprintf(outbuf,MAPSDIR"/%lu_%lu_%lu.adt",it->first,x,y); if(mpq.FileExists(namebuf)) { const 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("[%lu:%lu] %s; %lu new deps.\n",extr,it->first,namebuf,depdiff); } } } } extrtotal+=extr; printf("\n"); } printf("\nDONE - %lu 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"); std::string path = "extractedstuff/data"; std::string pathtex = path + "/texture"; std::string pathmodel = path + "/model"; std::string pathwmo = path + "/wmo"; std::string mpqfn,realfn,altfn; MD5FileMap md5Tex, md5Wmo, md5Wmogroup, md5Model; CreateDir(pathtex.c_str()); CreateDir(pathmodel.c_str()); CreateDir(pathwmo.c_str()); uint32 wmosdone=0,texdone=0,mdone=0; if(doWmos) { printf("Extracting %u WMOS...\n",wmoNames.size()); 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(!mpq.FileExists((char*)mpqfn.c_str())) continue; realfn = pathwmo + "/" + NormalizeFilename(_PathToFileName(altfn)); std::fstream fh; fh.open(realfn.c_str(),std::ios_base::out | std::ios_base::binary); if(fh.is_open()) { const ByteBuffer& bb = mpq.ExtractFile((char*)mpqfn.c_str()); fh.write((const char*)bb.contents(),bb.size()); //Extract number of group files, Texture file names and M2s from WMO if(doWmogroups || doTextures || doModels) WMO_Parse_Data(bb,mpqfn.c_str(),doWmogroups,doTextures,doModels); 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; } if(doWmogroups) { printf("Extracting WMO Group Files...\n"); bar = new barGoLink(wmoGroupNames.size(),true); for(std::set::iterator i = wmoGroupNames.begin(); i != wmoGroupNames.end(); i++) { bar->step(); mpqfn = i->name; altfn = i->alt; if(altfn.empty()) altfn = mpqfn; if(!mpq.FileExists((char*)mpqfn.c_str())) continue; realfn = pathwmo + "/" + NormalizeFilename(_PathToFileName(altfn)); std::fstream fh; fh.open(realfn.c_str(),std::ios_base::out | std::ios_base::binary); if(fh.is_open()) { const ByteBuffer& bb = mpq.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]; md5Wmogroup[_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(wmoGroupNames.size()) OutMD5((char*)pathwmo.c_str(),md5Wmogroup); delete bar; } 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(!mpq.FileExists((char*)mpqfn.c_str())) { std::string alt = mpqfn.substr(0,mpqfn.length()-2) + "2"; if(!mpq.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 + "/" + NormalizeFilename(_PathToFileName(altfn)); std::fstream fh; fh.open(realfn.c_str(),std::ios_base::out | std::ios_base::binary); if(fh.is_open()) { ByteBuffer bb = mpq.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]; md5Model[_PathToFileName(realfn)] = md5ptr; memcpy(md5ptr, h.GetDigest(), MD5_DIGEST_LENGTH); } mdone++; // model ok, now extract skins // for now first skin is all what we need std::string copy = mpqfn; std::transform(copy.begin(), copy.end(), copy.begin(), tolower); if (copy.find(".wmo") == std::string::npos) { if (doTextures) FetchTexturesFromModel(bb); std::string skin = mpqfn.substr(0,mpqfn.length()-3) + "00.skin"; std::string skinrealfn = pathmodel + "/" + NormalizeFilename(_PathToFileName(skin)); if (mpq.FileExists((char*)skin.c_str())) { std::fstream fhs; fhs.open(skinrealfn.c_str(),std::ios_base::out | std::ios_base::binary); if(fhs.is_open()) { ByteBuffer bbs = mpq.ExtractFile((char*)skin.c_str()); fhs.write((const char*)bbs.contents(),bbs.size()); } else printf("Could not write skin %s\n",skinrealfn.c_str()); fhs.close(); } else printf("Could not open skin %s\n",skin.c_str()); } } 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(!mpq.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.c_str(); 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()) { const ByteBuffer& bb = mpq.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; } } void ExtractSoundFiles(void) { MD5FileMap md5data; uint32 done = 0; printf("\nExtracting game audio files, %u found in DBC...\n",soundFileSet.size()); CreateDir(SOUNDDIR); std::string outfn, altfn; barGoLink bar(soundFileSet.size(),true); for(std::set::iterator i = soundFileSet.begin(); i != soundFileSet.end(); i++) { bar.step(); if(!mpq.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) + "/" + NormalizeFilename(altfn); std::fstream fh; fh.open(outfn.c_str(), std::ios_base::out | std::ios_base::binary); if(fh.is_open()) { const ByteBuffer& bb = mpq.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 WMO_Parse_Data(ByteBuffer bb, const char* _filename, bool groups, bool textures, bool models) { bb.rpos(20); //Skip MVER chunk and header of MHDR irr::scene::RootHeader header; if (bb.size() < sizeof(header)) return; bb.read((uint8*)&header, sizeof(header)); if(groups) { std::string filename=_filename; for(uint32 i=0; i0) { texNames.insert(NameAndAlt(temp)); temp.clear(); } else if(c!=0) temp += c; read++; } } else if(!strcmp((char*)fourcc,"NDOM") && models) { std::string temp; char c; uint32 read=0; while(read0) { modelNames.insert(NameAndAlt(temp)); temp.clear(); } else if(c!=0) temp += c; read++; } } else bb.rpos(bb.rpos()+size); } } } void ADT_ExportStringSetByOffset(const uint8* data, uint32 off, std::set& st,const 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; if (bb.size() < sizeof(header)) return; bb.read((uint8*)&header, sizeof(header)); if (header.version != 0x108) { printf("Not M2 model file!"); return; } irr::core::array M2MTextureDef; M2MTextureDef.clear(); irr::scene::TextureDefinition tempM2TexDef; bb.rpos(header.Textures.ofs); for(irr::u32 i=0;i