/** * This code is part of MaNGOS. Contributor & Copyright details are in AUTHORS/THANKS. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #define _CRT_SECURE_NO_DEPRECATE #include #include #include #include #include #if defined WIN32 #include #include #include #define mkdir _mkdir #else #include #endif #undef min #undef max //#pragma warning(disable : 4505) //#pragma comment(lib, "Winmm.lib") #include //From Extractor #include "adtfile.h" #include "wdtfile.h" #include "dbcfile.h" #include "wmo.h" #include "mpqfile.h" #include "vmapexport.h" #include "vmapexport.h" //------------------------------------------------------------------------------ // Defines #define MPQ_BLOCK_SIZE 0x1000 //----------------------------------------------------------------------------- HANDLE WorldMpq = NULL; HANDLE LocaleMpq = NULL; uint32 CONF_TargetBuild = 15595; // 4.3.4.15595 // List MPQ for extract maps from char const* CONF_mpq_list[] = { "world.MPQ", "art.MPQ", "expansion1.MPQ", "expansion2.MPQ", "expansion3.MPQ", "world2.MPQ", }; uint32 const Builds[] = {13164, 13205, 13287, 13329, 13596, 13623, 13914, 14007, 14333, 14480, 14545, 15005, 15050, 15211, 15354, 15595, 0}; #define LAST_DBC_IN_DATA_BUILD 13623 // after this build mpqs with dbc are back to locale folder char* const Locales[] = {"enGB", "enUS", "deDE", "esES", "frFR", "koKR", "zhCN", "zhTW", "enCN", "enTW", "esMX", "ruRU"}; TCHAR* const LocalesT[] = { _T("enGB"), _T("enUS"), _T("deDE"), _T("esES"), _T("frFR"), _T("koKR"), _T("zhCN"), _T("zhTW"), _T("enCN"), _T("enTW"), _T("esMX"), _T("ruRU"), }; #define LOCALES_COUNT 12 typedef struct { char name[64]; unsigned int id; } map_id; map_id* map_ids; uint16* LiqType = 0; uint32 map_count; char output_path[128] = "."; char input_path[1024] = "."; bool preciseVectorData = false; // Constants //static const char * szWorkDirMaps = ".\\Maps"; const char* szWorkDirWmo = "./Buildings"; const char* szRawVMAPMagic = "VMAPc04"; bool LoadLocaleMPQFile(int locale) { TCHAR buff[512]; memset(buff, 0, sizeof(buff)); _stprintf(buff, _T("%s/Data/%s/locale-%s.MPQ"), input_path, LocalesT[locale], LocalesT[locale]); if (!SFileOpenArchive(buff, 0, MPQ_OPEN_READ_ONLY, &LocaleMpq)) { if (GetLastError() != ERROR_FILE_NOT_FOUND) _tprintf(_T("Cannot open archive %s\n"), buff); return false; } char const* prefix = NULL; for (int i = 0; Builds[i] && Builds[i] <= CONF_TargetBuild; ++i) { memset(buff, 0, sizeof(buff)); if (Builds[i] > LAST_DBC_IN_DATA_BUILD) { prefix = ""; _stprintf(buff, _T("%s/Data/%s/wow-update-%s-%u.MPQ"), input_path, LocalesT[locale], LocalesT[locale], Builds[i]); } else { prefix = Locales[locale]; _stprintf(buff, _T("%s/Data/wow-update-%u.MPQ"), input_path, Builds[i]); } if (!SFileOpenPatchArchive(LocaleMpq, buff, prefix, 0)) { if (GetLastError() != ERROR_FILE_NOT_FOUND) _tprintf(_T("Cannot open patch archive %s\n"), buff); continue; } } return true; } void LoadCommonMPQFiles(uint32 build) { TCHAR filename[512]; _stprintf(filename, _T("%s/Data/world.MPQ"), input_path); if (!SFileOpenArchive(filename, 0, MPQ_OPEN_READ_ONLY, &WorldMpq)) { if (GetLastError() != ERROR_FILE_NOT_FOUND) _tprintf(_T("Cannot open archive %s\n"), filename); return; } int count = sizeof(CONF_mpq_list) / sizeof(char*); for (int i = 1; i < count; ++i) { if (build < 15211 && !strcmp("world2.MPQ", CONF_mpq_list[i])) // 4.3.2 and higher MPQ continue; _stprintf(filename, _T("%s/Data/%s"), input_path, CONF_mpq_list[i]); if (!SFileOpenPatchArchive(WorldMpq, filename, "", 0)) { if (GetLastError() != ERROR_FILE_NOT_FOUND) _tprintf(_T("Cannot open archive %s\n"), filename); else _tprintf(_T("Not found %s\n"), filename); } else { _tprintf(_T("Loaded %s\n"), filename); bool found = false; int count = 0; SFILE_FIND_DATA data; HANDLE find = SFileFindFirstFile(WorldMpq, "*.*", &data, NULL); if (find != NULL) { do { ++count; if (data.dwFileFlags & MPQ_FILE_PATCH_FILE) { found = true; break; } } while (SFileFindNextFile(find, &data)); } SFileFindClose(find); printf("Scanned %d files, found patch = %d\n", count, found); } } char const* prefix = NULL; for (int i = 0; Builds[i] && Builds[i] <= CONF_TargetBuild; ++i) { memset(filename, 0, sizeof(filename)); if (Builds[i] > LAST_DBC_IN_DATA_BUILD) { prefix = ""; _stprintf(filename, _T("%s/Data/wow-update-base-%u.MPQ"), input_path, Builds[i]); } else { prefix = "base"; _stprintf(filename, _T("%s/Data/wow-update-%u.MPQ"), input_path, Builds[i]); } if (!SFileOpenPatchArchive(WorldMpq, filename, prefix, 0)) { if (GetLastError() != ERROR_FILE_NOT_FOUND) _tprintf(_T("Cannot open patch archive %s\n"), filename); else _tprintf(_T("Not found %s\n"), filename); continue; } else { _tprintf(_T("Loaded %s\n"), filename); bool found = false; int count = 0; SFILE_FIND_DATA data; HANDLE find = SFileFindFirstFile(WorldMpq, "*.*", &data, NULL); if (find != NULL) { do { ++count; if (data.dwFileFlags & MPQ_FILE_PATCH_FILE) { found = true; break; } } while (SFileFindNextFile(find, &data)); } SFileFindClose(find); printf("Scanned %d files, found patch = %d\n", count, found); } } } // Local testing functions bool FileExists(const char* file) { if (FILE* n = fopen(file, "rb")) { fclose(n); return true; } return false; } void strToLower(char* str) { while (*str) { *str = tolower(*str); ++str; } } // copied from contrib/extractor/System.cpp void ReadLiquidTypeTableDBC() { printf("Read LiquidType.dbc file..."); DBCFile dbc(LocaleMpq, "DBFilesClient/LiquidType.dbc"); if (!dbc.open()) { printf("Fatal error: Invalid LiquidType.dbc file format!\n"); exit(1); } size_t LiqType_count = dbc.getRecordCount(); size_t LiqType_maxid = dbc.getRecord(LiqType_count - 1).getUInt(0); LiqType = new uint16[LiqType_maxid + 1]; memset(LiqType, 0xff, (LiqType_maxid + 1) * sizeof(uint16)); for (uint32 x = 0; x < LiqType_count; ++x) LiqType[dbc.getRecord(x).getUInt(0)] = dbc.getRecord(x).getUInt(3); printf("Done! (%u LiqTypes loaded)\n", (unsigned int)LiqType_count); } bool ExtractWmo() { bool success = false; //const char* ParsArchiveNames[] = {"patch-2.MPQ", "patch.MPQ", "common.MPQ", "expansion.MPQ"}; SFILE_FIND_DATA data; HANDLE find = SFileFindFirstFile(WorldMpq, "*.wmo", &data, NULL); if (find != NULL) { do { std::string str = data.cFileName; //printf("Extracting wmo %s\n", str.c_str()); success |= ExtractSingleWmo(str); } while (SFileFindNextFile(find, &data)); } SFileFindClose(find); if (success) printf("\nExtract wmo complete (No (fatal) errors)\n"); return success; } bool ExtractSingleWmo(std::string& fname) { // Copy files from archive char szLocalFile[1024]; const char* plain_name = GetPlainName(fname.c_str()); sprintf(szLocalFile, "%s/%s", szWorkDirWmo, plain_name); fixnamen(szLocalFile, strlen(szLocalFile)); if (FileExists(szLocalFile)) return true; int p = 0; //Select root wmo files const char* rchr = strrchr(plain_name, '_'); if (rchr != NULL) { char cpy[4]; strncpy((char*)cpy, rchr, 4); for (int i = 0; i < 4; ++i) { int m = cpy[i]; if (isdigit(m)) p++; } } if (p == 3) return true; bool file_ok = true; std::cout << "Extracting " << fname << std::endl; WMORoot froot(fname); if (!froot.open()) { printf("Couldn't open RootWmo!!!\n"); return true; } FILE* output = fopen(szLocalFile, "wb"); if (!output) { printf("couldn't open %s for writing!\n", szLocalFile); return false; } froot.ConvertToVMAPRootWmo(output); int Wmo_nVertices = 0; //printf("root has %d groups\n", froot->nGroups); if (froot.nGroups != 0) { for (uint32 i = 0; i < froot.nGroups; ++i) { char temp[1024]; strcpy(temp, fname.c_str()); temp[fname.length() - 4] = 0; char groupFileName[1024]; sprintf(groupFileName, "%s_%03d.wmo", temp, i); //printf("Trying to open groupfile %s\n",groupFileName); string s = groupFileName; WMOGroup fgroup(s); if (!fgroup.open()) { printf("Could not open all Group file for: %s\n", plain_name); file_ok = false; break; } Wmo_nVertices += fgroup.ConvertToVMAPGroupWmo(output, &froot, preciseVectorData); } } fseek(output, 8, SEEK_SET); // store the correct no of vertices fwrite(&Wmo_nVertices, sizeof(int), 1, output); fclose(output); // Delete the extracted file in the case of an error if (!file_ok) remove(szLocalFile); return true; } void ParsMapFiles() { char fn[512]; //char id_filename[64]; char id[10]; StringSet failedPaths; for (unsigned int i = 0; i < map_count; ++i) { sprintf(id, "%03u", map_ids[i].id); sprintf(fn, "World/Maps/%s/%s.wdt", map_ids[i].name, map_ids[i].name); WDTFile WDT(fn, map_ids[i].name); if (WDT.init(id, map_ids[i].id)) { printf("Processing Map %u\n[", map_ids[i].id); for (int x = 0; x < 64; ++x) { for (int y = 0; y < 64; ++y) { if (ADTFile* ADT = WDT.GetMap(x, y)) { //sprintf(id_filename,"%02u %02u %03u",x,y,map_ids[i].id);//!!!!!!!!! ADT->init(map_ids[i].id, x, y, failedPaths); delete ADT; } } printf("#"); fflush(stdout); } printf("]\n"); } } if (!failedPaths.empty()) { printf("Warning: Some models could not be extracted, see below\n"); for (StringSet::const_iterator itr = failedPaths.begin(); itr != failedPaths.end(); ++itr) printf("Could not find file of model %s\n", itr->c_str()); printf("A few not found models can be expected and are not alarming.\n"); } } void getGamePath() { #ifdef _WIN32 strcpy(input_path, "Data\\"); #else strcpy(input_path, "Data/"); #endif } bool scan_patches(char* scanmatch, std::vector& pArchiveNames) { int i; char path[512]; for (i = 1; i <= 99; i++) { if (i != 1) { sprintf(path, "%s-%d.MPQ", scanmatch, i); } else { sprintf(path, "%s.MPQ", scanmatch); } #ifdef __linux__ if (FILE* h = fopen64(path, "rb")) #else if (FILE* h = fopen(path, "rb")) #endif { fclose(h); //matches.push_back(path); pArchiveNames.push_back(path); } } return(true); } bool processArgv(int argc, char** argv) { bool result = true; bool hasInputPathParam = false; bool preciseVectorData = false; for (int i = 1; i < argc; ++i) { if (strcmp("-s", argv[i]) == 0) { preciseVectorData = false; } else if (strcmp("-d", argv[i]) == 0) { if ((i + 1) < argc) { hasInputPathParam = true; strcpy(input_path, argv[i + 1]); if (input_path[strlen(input_path) - 1] != '\\' || input_path[strlen(input_path) - 1] != '/') strcat(input_path, "/"); ++i; } else { result = false; } } else if (strcmp("-?", argv[1]) == 0) { result = false; } else if (strcmp("-l", argv[i]) == 0) { preciseVectorData = true; } else if (strcmp("-b", argv[i]) == 0) { if (i + 1 < argc) // all ok CONF_TargetBuild = atoi(argv[i++ + 1]); } else { result = false; break; } } if (!result) { printf("Extract for %s.\n", szRawVMAPMagic); printf("%s [-?][-s][-l][-d ]\n", argv[0]); printf(" -s : (default) small size (data size optimization), ~500MB less vmap data.\n"); printf(" -l : large size, ~500MB more vmap data. (might contain more details)\n"); printf(" -d : Path to the vector data source folder.\n"); printf(" -b : target build (default %u)", CONF_TargetBuild); printf(" -? : This message.\n"); } return result; } //xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx // Main // // The program must be run with two command line arguments // // Arg1 - The source MPQ name (for testing reading and file find) // Arg2 - Listfile name // int main(int argc, char** argv) { bool success = true; // Use command line arguments, when some if (!processArgv(argc, argv)) return 1; // some simple check if working dir is dirty else { std::string sdir = std::string(szWorkDirWmo) + "/dir"; std::string sdir_bin = std::string(szWorkDirWmo) + "/dir_bin"; struct stat status; if (!stat(sdir.c_str(), &status) || !stat(sdir_bin.c_str(), &status)) { printf("Your output directory seems to be polluted, please use an empty directory!\n"); printf(""); char garbage[2]; scanf("%c", garbage); return 1; } } printf("Extract for %s. Beginning work ....\n", szRawVMAPMagic); //xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx // Create the working directory if (mkdir(szWorkDirWmo #ifdef __linux__ , 0711 #endif )) success = (errno == EEXIST); LoadCommonMPQFiles(CONF_TargetBuild); int FirstLocale = -1; for (int i = 0; i < LOCALES_COUNT; ++i) { //Open MPQs if (!LoadLocaleMPQFile(i)) { if (GetLastError() != ERROR_FILE_NOT_FOUND) printf("Unable to load %s locale archives!\n", Locales[i]); continue; } printf("Detected and using locale locale: %s\n", Locales[i]); break; } ReadLiquidTypeTableDBC(); // extract data if (success) success = ExtractWmo(); //xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx //map.dbc if (success) { DBCFile* dbc = new DBCFile(LocaleMpq, "DBFilesClient\\Map.dbc"); if (!dbc->open()) { delete dbc; printf("FATAL ERROR: Map.dbc not found in data file.\n"); return 1; } map_count = dbc->getRecordCount(); map_ids = new map_id[map_count]; for (unsigned int x = 0; x < map_count; ++x) { map_ids[x].id = dbc->getRecord(x).getUInt(0); strcpy(map_ids[x].name, dbc->getRecord(x).getString(1)); printf("Map - %s\n", map_ids[x].name); } delete dbc; ParsMapFiles(); delete [] map_ids; //nError = ERROR_SUCCESS; // Extract models, listed in DameObjectDisplayInfo.dbc ExtractGameobjectModels(); } SFileCloseArchive(LocaleMpq); SFileCloseArchive(WorldMpq); printf("\n"); if (!success) { printf("ERROR: Extract for %s. Work NOT complete.\n Precise vector data=%d.\nPress any key.\n", szRawVMAPMagic, preciseVectorData); getchar(); } printf("Extract for %s. Work complete. No errors.\n", szRawVMAPMagic); delete [] LiqType; return 0; }