void ExtractDBCFiles(int l, bool basicLocale) { printf("Extracting dbc files...\n"); SFILE_FIND_DATA foundFile; memset(&foundFile, 0, sizeof(foundFile)); HANDLE listFile = SFileFindFirstFile(LocaleMpq, "DBFilesClient\\*dbc", &foundFile, NULL); HANDLE dbcFile = NULL; uint32 count = 0; if (listFile) { std::string outputPath = output_path; outputPath += "/dbc/"; CreateDir(outputPath); if (!basicLocale) { outputPath += Locales[l]; outputPath += "/"; CreateDir(outputPath); } std::string filename; do { if (!SFileOpenFileEx(LocaleMpq, foundFile.cFileName, SFILE_OPEN_PATCHED_FILE, &dbcFile)) { printf("Unable to open file %s in the archive\n", foundFile.cFileName); continue; } filename = foundFile.cFileName; filename = outputPath + filename.substr(filename.rfind('\\')); if (FileExists(filename.c_str())) continue; if (ExtractFile(dbcFile, filename.c_str())) ++count; SFileCloseFile(dbcFile); } while (SFileFindNextFile(listFile, &foundFile)); SFileFindClose(listFile); } printf("Extracted %u DBC files\n\n", count); }
static bool QueryMpqSignatureInfo( TMPQArchive * ha, PMPQ_SIGNATURE_INFO pSI) { ULONGLONG ExtraBytes; TMPQFile * hf; HANDLE hFile; DWORD dwFileSize; // Calculate the range of the MPQ CalculateArchiveRange(ha, pSI); // If there is "(signature)" file in the MPQ, it has a weak signature if (SFileOpenFileEx((HANDLE)ha, SIGNATURE_NAME, SFILE_OPEN_FROM_MPQ, &hFile)) { // Get the content of the signature SFileReadFile(hFile, pSI->Signature, sizeof(pSI->Signature), &pSI->cbSignatureSize); // Verify the size of the signature hf = (TMPQFile *)hFile; // We have to exclude the signature file from the digest pSI->BeginExclude = ha->MpqPos + hf->pFileEntry->ByteOffset; pSI->EndExclude = pSI->BeginExclude + hf->pFileEntry->dwCmpSize; dwFileSize = hf->dwDataSize; // Close the file SFileCloseFile(hFile); pSI->nSignatureType = SIGNATURE_TYPE_WEAK; return (dwFileSize == (MPQ_WEAK_SIGNATURE_SIZE + 8)) ? true : false; } // If there is extra bytes beyond the end of the archive, // it's the strong signature ExtraBytes = pSI->EndOfFile - pSI->EndMpqData; if (ExtraBytes >= (MPQ_STRONG_SIGNATURE_SIZE + 4)) { // Read the strong signature if (!FileStream_Read(ha->pStream, &pSI->EndMpqData, pSI->Signature, (MPQ_STRONG_SIGNATURE_SIZE + 4))) return false; // Check the signature header "NGIS" if (pSI->Signature[0] != 'N' || pSI->Signature[1] != 'G' || pSI->Signature[2] != 'I' || pSI->Signature[3] != 'S') return false; pSI->nSignatureType = SIGNATURE_TYPE_STRONG; return true; } // Succeeded, but no known signature found return true; }
int SC2Map::readArchiveFile( const HANDLE archive, const char* strFilename, int* bufferOutSize, u8** bufferOut ) { HANDLE hFile; if( !SFileOpenFileEx( archive, strFilename, 0, &hFile ) ) { //printWarning( "Could not open %s for reading.\n", strFilename ); return -1; } DWORD fileSizeBytes = SFileGetFileSize( hFile, NULL ); if( fileSizeBytes == SFILE_INVALID_SIZE || fileSizeBytes <= 0 ) { printWarning( "%s is empty or invalid.\n", strFilename ); return -1; } u8* buffer = new u8[fileSizeBytes]; DWORD numBytesRead; // initialize buffer to easily recognized values, // after a successful read all of them are overwritten memset( buffer, 'q', fileSizeBytes ); if( !SFileReadFile( hFile, (void*)buffer, fileSizeBytes, &numBytesRead, NULL ) ) { delete buffer; printWarning( "Could not read %s from archive.\n", strFilename ); return -1; } if( numBytesRead != fileSizeBytes ) { delete buffer; printWarning( "Could not read %s from archive. [NOT EXPECTING TO SEE THIS WARNING!]\n", strFilename ); return -1; } *bufferOutSize = fileSizeBytes; *bufferOut = buffer; SFileCloseFile( hFile ); return 0; }
int ExtractFileToHardDrive(HANDLE &MPQ_handle, const char * szArchivedFile, const char * szFileName) { HANDLE hFile = NULL; // Archived file handle TFileStream* handle = NULL; // Disk file handle int nError = ERROR_SUCCESS; // Result value if (nError == ERROR_SUCCESS) { if (!SFileOpenFileEx(MPQ_handle, szArchivedFile, SFILE_OPEN_PATCHED_FILE, &hFile)) nError = GetLastError(); } // Create the target file if (nError == ERROR_SUCCESS) { handle = FileStream_CreateFile(szFileName); if (handle == NULL) nError = GetLastError(); } // Read the file from the archive if (nError == ERROR_SUCCESS) { // Get the size of the full patched file DWORD dwFileSize = SFileGetFileSize(hFile, NULL); if (dwFileSize != 0) { // Allocate space for the full file BYTE * pbFullFile = new BYTE[dwFileSize]; if (!SFileReadFile(hFile, pbFullFile, dwFileSize)) { nError = GetLastError(); printf("Failed to read full patched file data \"%s\"\n", szFileName); assert(false); } FileStream_Write(handle, NULL, pbFullFile, dwFileSize); delete [] pbFullFile; } } // Cleanup and exit if (handle != NULL) FileStream_Close(handle); if (hFile != NULL) SFileCloseFile(hFile); return nError; }
static int SFileAddExternalListFile( TMPQArchive * ha, HANDLE hMpq, const char * szListFile) { HANDLE hListFile; int nError = ERROR_SUCCESS; // Open the external list file if(SFileOpenFileEx(hMpq, szListFile, SFILE_OPEN_LOCAL_FILE, &hListFile)) { // Add the data from the listfile to MPQ nError = SFileAddArbitraryListFile(ha, hListFile); SFileCloseFile(hListFile); } return nError; }
uint32 ReadBuild(int locale) { // include build info file also std::string filename = std::string("component.wow-") + Locales[locale] + ".txt"; //printf("Read %s file... ", filename.c_str()); HANDLE dbcFile; if (!SFileOpenFileEx(LocaleMpq, filename.c_str(), SFILE_OPEN_PATCHED_FILE, &dbcFile)) { printf("Fatal error: Not found %s file!\n", filename.c_str()); exit(1); } char buff[512]; DWORD readBytes = 0; SFileReadFile(dbcFile, buff, 512, &readBytes, NULL); if (!readBytes) { printf("Fatal error: Not found %s file!\n", filename.c_str()); exit(1); } std::string text = buff; SFileCloseFile(dbcFile); size_t pos = text.find("version=\""); size_t pos1 = pos + strlen("version=\""); size_t pos2 = text.find("\"", pos1); if (pos == text.npos || pos2 == text.npos || pos1 >= pos2) { printf("Fatal error: Invalid %s file format!\n", filename.c_str()); exit(1); } std::string build_str = text.substr(pos1,pos2-pos1); int build = atoi(build_str.c_str()); if (build <= 0) { printf("Fatal error: Invalid %s file format!\n", filename.c_str()); exit(1); } return build; }
static bool IsMatchingPatchFile( TMPQArchive * ha, const char * szFileName, LPBYTE pbBaseFileMd5) { MPQ_PATCH_HEADER PatchHeader = {0}; HANDLE hFile = NULL; DWORD dwTransferred = 0; DWORD dwFlags = 0; bool bResult = false; // Open the file and load the patch header if(SFileOpenFileEx((HANDLE)ha, szFileName, SFILE_OPEN_BASE_FILE, &hFile)) { // Retrieve the flags. We need to know whether the file is a patch or not SFileGetFileInfo(hFile, SFileInfoFlags, &dwFlags, sizeof(DWORD), &dwTransferred); if(dwFlags & MPQ_FILE_PATCH_FILE) { // Load the patch header SFileReadFile(hFile, &PatchHeader, sizeof(MPQ_PATCH_HEADER), &dwTransferred, NULL); BSWAP_ARRAY32_UNSIGNED(pPatchHeader, sizeof(DWORD) * 6); // If the file contains an incremental patch, // compare the "MD5 before patching" with the base file MD5 if(dwTransferred == sizeof(MPQ_PATCH_HEADER) && PatchHeader.dwSignature == PATCH_SIGNATURE_HEADER) bResult = (!memcmp(PatchHeader.md5_before_patch, pbBaseFileMd5, MD5_DIGEST_SIZE)); } else { // TODO: How to match it if it's not an incremental patch? // Example: StarCraft II\Updates\enGB\s2-update-enGB-23258.MPQ: // Mods\Core.SC2Mod\enGB.SC2Assets\StreamingBuckets.txt" bResult = false; } // Close the file SFileCloseFile(hFile); } return bResult; }
int FAfclose(FAFile* stream) { int retval = 0; switch(stream->mode) { case FAFile::PlainFile: delete stream->data.plainFile.filename; return fclose(stream->data.plainFile.file); case FAFile::MPQFile: int res = SFileCloseFile(*((HANDLE*)stream->data.mpqFile)); free(stream->data.mpqFile); if(res != 0) retval = EOF; } delete stream; return retval; }
bool FindFileInMpq(MPQHANDLE mpq, const char* fileName) { if ( mpq == nullptr ) CHKD_ERR("NULL MPQ file specified for opening %s", fileName); else { u32 bytesRead = 0; HANDLE openFile = NULL; if ( SFileGetFileInfo(mpq, SFILE_INFO_NUM_FILES) != 0xFFFFFFFF ) { if ( SFileOpenFileEx(mpq, fileName, SFILE_SEARCH_CURRENT_ONLY, &openFile) == TRUE ) { SFileCloseFile(openFile); return true; } else CHKD_ERR("Failed to get %s from MPQ file", fileName); } else CHKD_ERR("File is already open", fileName); } return false; }
char* ExtractFileToMemory(HANDLE &MPQ_handle, const char * szArchivedFile, int & size ) { HANDLE hFile = NULL; // Archived file handle int nError = ERROR_SUCCESS; // Result value char * pbFullFile = NULL; if (nError == ERROR_SUCCESS) { if (!SFileOpenFileEx(MPQ_handle, szArchivedFile, SFILE_OPEN_PATCHED_FILE, &hFile)) nError = GetLastError(); } // Read the file from the archive if (nError == ERROR_SUCCESS) { // Get the size of the full patched file size = SFileGetFileSize(hFile, NULL); if (size != 0) { // Allocate space for the full file pbFullFile = new char[size]; if (!SFileReadFile(hFile, pbFullFile, size)) { nError = GetLastError(); printf("Failed to read full patched file data \"%s\"\n", szArchivedFile); assert(false); } } } // Cleanup and exit if (nError == ERROR_SUCCESS && hFile != NULL) SFileCloseFile(hFile); return pbFullFile; }
void CMap :: Load( CConfig *CFG, string nCFGFile ) { m_Valid = true; m_CFGFile = nCFGFile; // load the map data m_MapLocalPath = CFG->GetString( "map_localpath", string( ) ); m_MapData.clear( ); if( !m_MapLocalPath.empty( ) ) m_MapData = UTIL_FileRead( m_GHost->m_MapPath + m_MapLocalPath ); // load the map MPQ string MapMPQFileName = m_GHost->m_MapPath + m_MapLocalPath; HANDLE MapMPQ; bool MapMPQReady = false; if( SFileOpenArchive( MapMPQFileName.c_str( ), 0, MPQ_OPEN_FORCE_MPQ_V1, &MapMPQ ) ) { CONSOLE_Print( "[MAP] loading MPQ file [" + MapMPQFileName + "]" ); MapMPQReady = true; } else CONSOLE_Print( "[MAP] warning - unable to load MPQ file [" + MapMPQFileName + "]" ); // try to calculate map_size, map_info, map_crc, map_sha1 BYTEARRAY MapSize; BYTEARRAY MapInfo; BYTEARRAY MapCRC; BYTEARRAY MapSHA1; if( !m_MapData.empty( ) ) { m_GHost->m_SHA->Reset( ); // calculate map_size MapSize = UTIL_CreateByteArray( (uint32_t)m_MapData.size( ), false ); CONSOLE_Print( "[MAP] calculated map_size = " + UTIL_ByteArrayToDecString( MapSize ) ); // calculate map_info (this is actually the CRC) MapInfo = UTIL_CreateByteArray( (uint32_t)m_GHost->m_CRC->FullCRC( (unsigned char *)m_MapData.c_str( ), m_MapData.size( ) ), false ); CONSOLE_Print( "[MAP] calculated map_info = " + UTIL_ByteArrayToDecString( MapInfo ) ); // calculate map_crc (this is not the CRC) and map_sha1 // a big thank you to Strilanc for figuring the map_crc algorithm out string CommonJ = UTIL_FileRead( m_GHost->m_MapCFGPath + "common.j" ); if( CommonJ.empty( ) ) CONSOLE_Print( "[MAP] unable to calculate map_crc/sha1 - unable to read file [" + m_GHost->m_MapCFGPath + "common.j]" ); else { string BlizzardJ = UTIL_FileRead( m_GHost->m_MapCFGPath + "blizzard.j" ); if( BlizzardJ.empty( ) ) CONSOLE_Print( "[MAP] unable to calculate map_crc/sha1 - unable to read file [" + m_GHost->m_MapCFGPath + "blizzard.j]" ); else { uint32_t Val = 0; // update: it's possible for maps to include their own copies of common.j and/or blizzard.j // this code now overrides the default copies if required bool OverrodeCommonJ = false; bool OverrodeBlizzardJ = false; if( MapMPQReady ) { HANDLE SubFile; // override common.j if( SFileOpenFileEx( MapMPQ, "Scripts\\common.j", 0, &SubFile ) ) { uint32_t FileLength = SFileGetFileSize( SubFile, NULL ); if( FileLength > 0 && FileLength != 0xFFFFFFFF ) { char *SubFileData = new char[FileLength]; DWORD BytesRead = 0; if( SFileReadFile( SubFile, SubFileData, FileLength, &BytesRead ) ) { CONSOLE_Print( "[MAP] overriding default common.j with map copy while calculating map_crc/sha1" ); OverrodeCommonJ = true; Val = Val ^ XORRotateLeft( (unsigned char *)SubFileData, BytesRead ); m_GHost->m_SHA->Update( (unsigned char *)SubFileData, BytesRead ); } delete [] SubFileData; } SFileCloseFile( SubFile ); } } if( !OverrodeCommonJ ) { Val = Val ^ XORRotateLeft( (unsigned char *)CommonJ.c_str( ), CommonJ.size( ) ); m_GHost->m_SHA->Update( (unsigned char *)CommonJ.c_str( ), CommonJ.size( ) ); } if( MapMPQReady ) { HANDLE SubFile; // override blizzard.j if( SFileOpenFileEx( MapMPQ, "Scripts\\blizzard.j", 0, &SubFile ) ) { uint32_t FileLength = SFileGetFileSize( SubFile, NULL ); if( FileLength > 0 && FileLength != 0xFFFFFFFF ) { char *SubFileData = new char[FileLength]; DWORD BytesRead = 0; if( SFileReadFile( SubFile, SubFileData, FileLength, &BytesRead ) ) { CONSOLE_Print( "[MAP] overriding default blizzard.j with map copy while calculating map_crc/sha1" ); OverrodeBlizzardJ = true; Val = Val ^ XORRotateLeft( (unsigned char *)SubFileData, BytesRead ); m_GHost->m_SHA->Update( (unsigned char *)SubFileData, BytesRead ); } delete [] SubFileData; } SFileCloseFile( SubFile ); } } if( !OverrodeBlizzardJ ) { Val = Val ^ XORRotateLeft( (unsigned char *)BlizzardJ.c_str( ), BlizzardJ.size( ) ); m_GHost->m_SHA->Update( (unsigned char *)BlizzardJ.c_str( ), BlizzardJ.size( ) ); } Val = ROTL( Val, 3 ); Val = ROTL( Val ^ 0x03F1379E, 3 ); m_GHost->m_SHA->Update( (unsigned char *)"\x9E\x37\xF1\x03", 4 ); if( MapMPQReady ) { vector<string> FileList; FileList.push_back( "war3map.j" ); FileList.push_back( "scripts\\war3map.j" ); FileList.push_back( "war3map.w3e" ); FileList.push_back( "war3map.wpm" ); FileList.push_back( "war3map.doo" ); FileList.push_back( "war3map.w3u" ); FileList.push_back( "war3map.w3b" ); FileList.push_back( "war3map.w3d" ); FileList.push_back( "war3map.w3a" ); FileList.push_back( "war3map.w3q" ); bool FoundScript = false; for( vector<string> :: iterator i = FileList.begin( ); i != FileList.end( ); i++ ) { // don't use scripts\war3map.j if we've already used war3map.j (yes, some maps have both but only war3map.j is used) if( FoundScript && *i == "scripts\\war3map.j" ) continue; HANDLE SubFile; if( SFileOpenFileEx( MapMPQ, (*i).c_str( ), 0, &SubFile ) ) { uint32_t FileLength = SFileGetFileSize( SubFile, NULL ); if( FileLength > 0 && FileLength != 0xFFFFFFFF ) { char *SubFileData = new char[FileLength]; DWORD BytesRead = 0; if( SFileReadFile( SubFile, SubFileData, FileLength, &BytesRead ) ) { if( *i == "war3map.j" || *i == "scripts\\war3map.j" ) FoundScript = true; Val = ROTL( Val ^ XORRotateLeft( (unsigned char *)SubFileData, BytesRead ), 3 ); m_GHost->m_SHA->Update( (unsigned char *)SubFileData, BytesRead ); // DEBUG_Print( "*** found: " + *i ); } delete [] SubFileData; } SFileCloseFile( SubFile ); } else { // DEBUG_Print( "*** not found: " + *i ); } } if( !FoundScript ) CONSOLE_Print( "[MAP] couldn't find war3map.j or scripts\\war3map.j in MPQ file, calculated map_crc/sha1 is probably wrong" ); MapCRC = UTIL_CreateByteArray( Val, false ); CONSOLE_Print( "[MAP] calculated map_crc = " + UTIL_ByteArrayToDecString( MapCRC ) ); m_GHost->m_SHA->Final( ); unsigned char SHA1[20]; memset( SHA1, 0, sizeof( unsigned char ) * 20 ); m_GHost->m_SHA->GetHash( SHA1 ); MapSHA1 = UTIL_CreateByteArray( SHA1, 20 ); CONSOLE_Print( "[MAP] calculated map_sha1 = " + UTIL_ByteArrayToDecString( MapSHA1 ) ); } else CONSOLE_Print( "[MAP] unable to calculate map_crc/sha1 - map MPQ file not loaded" ); } } } else CONSOLE_Print( "[MAP] no map data available, using config file for map_size, map_info, map_crc, map_sha1" ); // try to calculate map_width, map_height, map_slot<x>, map_numplayers, map_numteams uint32_t MapOptions = 0; BYTEARRAY MapWidth; BYTEARRAY MapHeight; uint32_t MapNumPlayers = 0; uint32_t MapNumTeams = 0; vector<CGameSlot> Slots; if( !m_MapData.empty( ) ) { if( MapMPQReady ) { HANDLE SubFile; if( SFileOpenFileEx( MapMPQ, "war3map.w3i", 0, &SubFile ) ) { uint32_t FileLength = SFileGetFileSize( SubFile, NULL ); if( FileLength > 0 && FileLength != 0xFFFFFFFF ) { char *SubFileData = new char[FileLength]; DWORD BytesRead = 0; if( SFileReadFile( SubFile, SubFileData, FileLength, &BytesRead ) ) { istringstream ISS( string( SubFileData, BytesRead ) ); // war3map.w3i format found at http://www.wc3campaigns.net/tools/specs/index.html by Zepir/PitzerMike string GarbageString; uint32_t FileFormat; uint32_t RawMapWidth; uint32_t RawMapHeight; uint32_t RawMapFlags; uint32_t RawMapNumPlayers; uint32_t RawMapNumTeams; ISS.read( (char *)&FileFormat, 4 ); // file format (18 = ROC, 25 = TFT) if( FileFormat == 18 || FileFormat == 25 ) { ISS.seekg( 4, ios :: cur ); // number of saves ISS.seekg( 4, ios :: cur ); // editor version getline( ISS, GarbageString, '\0' ); // map name getline( ISS, GarbageString, '\0' ); // map author getline( ISS, GarbageString, '\0' ); // map description getline( ISS, GarbageString, '\0' ); // players recommended ISS.seekg( 32, ios :: cur ); // camera bounds ISS.seekg( 16, ios :: cur ); // camera bounds complements ISS.read( (char *)&RawMapWidth, 4 ); // map width ISS.read( (char *)&RawMapHeight, 4 ); // map height ISS.read( (char *)&RawMapFlags, 4 ); // flags ISS.seekg( 1, ios :: cur ); // map main ground type if( FileFormat == 18 ) ISS.seekg( 4, ios :: cur ); // campaign background number else if( FileFormat == 25 ) { ISS.seekg( 4, ios :: cur ); // loading screen background number getline( ISS, GarbageString, '\0' ); // path of custom loading screen model } getline( ISS, GarbageString, '\0' ); // map loading screen text getline( ISS, GarbageString, '\0' ); // map loading screen title getline( ISS, GarbageString, '\0' ); // map loading screen subtitle if( FileFormat == 18 ) ISS.seekg( 4, ios :: cur ); // map loading screen number else if( FileFormat == 25 ) { ISS.seekg( 4, ios :: cur ); // used game data set getline( ISS, GarbageString, '\0' ); // prologue screen path } getline( ISS, GarbageString, '\0' ); // prologue screen text getline( ISS, GarbageString, '\0' ); // prologue screen title getline( ISS, GarbageString, '\0' ); // prologue screen subtitle if( FileFormat == 25 ) { ISS.seekg( 4, ios :: cur ); // uses terrain fog ISS.seekg( 4, ios :: cur ); // fog start z height ISS.seekg( 4, ios :: cur ); // fog end z height ISS.seekg( 4, ios :: cur ); // fog density ISS.seekg( 1, ios :: cur ); // fog red value ISS.seekg( 1, ios :: cur ); // fog green value ISS.seekg( 1, ios :: cur ); // fog blue value ISS.seekg( 1, ios :: cur ); // fog alpha value ISS.seekg( 4, ios :: cur ); // global weather id getline( ISS, GarbageString, '\0' ); // custom sound environment ISS.seekg( 1, ios :: cur ); // tileset id of the used custom light environment ISS.seekg( 1, ios :: cur ); // custom water tinting red value ISS.seekg( 1, ios :: cur ); // custom water tinting green value ISS.seekg( 1, ios :: cur ); // custom water tinting blue value ISS.seekg( 1, ios :: cur ); // custom water tinting alpha value } ISS.read( (char *)&RawMapNumPlayers, 4 ); // number of players uint32_t ClosedSlots = 0; for( uint32_t i = 0; i < RawMapNumPlayers; i++ ) { CGameSlot Slot( 0, 255, SLOTSTATUS_OPEN, 0, 0, 1, SLOTRACE_RANDOM ); uint32_t Colour; uint32_t Status; uint32_t Race; ISS.read( (char *)&Colour, 4 ); // colour Slot.SetColour( Colour ); ISS.read( (char *)&Status, 4 ); // status if( Status == 1 ) Slot.SetSlotStatus( SLOTSTATUS_OPEN ); else if( Status == 2 ) { Slot.SetSlotStatus( SLOTSTATUS_OCCUPIED ); Slot.SetComputer( 1 ); Slot.SetComputerType( SLOTCOMP_NORMAL ); } else { Slot.SetSlotStatus( SLOTSTATUS_CLOSED ); ClosedSlots++; } ISS.read( (char *)&Race, 4 ); // race if( Race == 1 ) Slot.SetRace( SLOTRACE_HUMAN ); else if( Race == 2 ) Slot.SetRace( SLOTRACE_ORC ); else if( Race == 3 ) Slot.SetRace( SLOTRACE_UNDEAD ); else if( Race == 4 ) Slot.SetRace( SLOTRACE_NIGHTELF ); else Slot.SetRace( SLOTRACE_RANDOM ); ISS.seekg( 4, ios :: cur ); // fixed start position getline( ISS, GarbageString, '\0' ); // player name ISS.seekg( 4, ios :: cur ); // start position x ISS.seekg( 4, ios :: cur ); // start position y ISS.seekg( 4, ios :: cur ); // ally low priorities ISS.seekg( 4, ios :: cur ); // ally high priorities if( Slot.GetSlotStatus( ) != SLOTSTATUS_CLOSED ) Slots.push_back( Slot ); } ISS.read( (char *)&RawMapNumTeams, 4 ); // number of teams for( uint32_t i = 0; i < RawMapNumTeams; i++ ) { uint32_t Flags; uint32_t PlayerMask; ISS.read( (char *)&Flags, 4 ); // flags ISS.read( (char *)&PlayerMask, 4 ); // player mask for( unsigned char j = 0; j < 12; j++ ) { if( PlayerMask & 1 ) { for( vector<CGameSlot> :: iterator k = Slots.begin( ); k != Slots.end( ); k++ ) { if( (*k).GetColour( ) == j ) (*k).SetTeam( i ); } } PlayerMask >>= 1; } getline( ISS, GarbageString, '\0' ); // team name } // the bot only cares about the following options: melee, fixed player settings, custom forces // let's not confuse the user by displaying erroneous map options so zero them out now MapOptions = RawMapFlags & ( MAPOPT_MELEE | MAPOPT_FIXEDPLAYERSETTINGS | MAPOPT_CUSTOMFORCES ); CONSOLE_Print( "[MAP] calculated map_options = " + UTIL_ToString( MapOptions ) ); MapWidth = UTIL_CreateByteArray( (uint16_t)RawMapWidth, false ); CONSOLE_Print( "[MAP] calculated map_width = " + UTIL_ByteArrayToDecString( MapWidth ) ); MapHeight = UTIL_CreateByteArray( (uint16_t)RawMapHeight, false ); CONSOLE_Print( "[MAP] calculated map_height = " + UTIL_ByteArrayToDecString( MapHeight ) ); MapNumPlayers = RawMapNumPlayers - ClosedSlots; CONSOLE_Print( "[MAP] calculated map_numplayers = " + UTIL_ToString( MapNumPlayers ) ); MapNumTeams = RawMapNumTeams; CONSOLE_Print( "[MAP] calculated map_numteams = " + UTIL_ToString( MapNumTeams ) ); uint32_t SlotNum = 1; for( vector<CGameSlot> :: iterator i = Slots.begin( ); i != Slots.end( ); i++ ) { CONSOLE_Print( "[MAP] calculated map_slot" + UTIL_ToString( SlotNum ) + " = " + UTIL_ByteArrayToDecString( (*i).GetByteArray( ) ) ); SlotNum++; } if( MapOptions & MAPOPT_MELEE ) { CONSOLE_Print( "[MAP] found melee map, initializing slots" ); // give each slot a different team and set the race to random unsigned char Team = 0; for( vector<CGameSlot> :: iterator i = Slots.begin( ); i != Slots.end( ); i++ ) { (*i).SetTeam( Team++ ); (*i).SetRace( SLOTRACE_RANDOM ); } } if( !( MapOptions & MAPOPT_FIXEDPLAYERSETTINGS ) ) { // make races selectable for( vector<CGameSlot> :: iterator i = Slots.begin( ); i != Slots.end( ); i++ ) (*i).SetRace( (*i).GetRace( ) | SLOTRACE_SELECTABLE ); } } } else
DWORD WINAPI SFileVerifyFile(HANDLE hMpq, const char * szFileName, DWORD dwFlags) { hash_state md5_state; unsigned char md5[MD5_DIGEST_SIZE]; TFileEntry * pFileEntry; TMPQFile * hf; BYTE Buffer[0x1000]; HANDLE hFile = NULL; DWORD dwVerifyResult = 0; DWORD dwSearchScope = SFILE_OPEN_FROM_MPQ; DWORD dwTotalBytes = 0; DWORD dwBytesRead; DWORD dwCrc32; // Fix the open type for patched archives if (SFileIsPatchedArchive(hMpq)) dwSearchScope = SFILE_OPEN_PATCHED_FILE; // Attempt to open the file if (SFileOpenFileEx(hMpq, szFileName, dwSearchScope, &hFile)) { // Get the file size hf = (TMPQFile *)hFile; pFileEntry = hf->pFileEntry; dwTotalBytes = SFileGetFileSize(hFile, NULL); // Initialize the CRC32 and MD5 contexts md5_init(&md5_state); dwCrc32 = crc32(0, Z_NULL, 0); // Also turn on sector checksum verification hf->bCheckSectorCRCs = true; // Go through entire file and update both CRC32 and MD5 for (;;) { // Read data from file SFileReadFile(hFile, Buffer, sizeof(Buffer), &dwBytesRead, NULL); if (dwBytesRead == 0) { if (GetLastError() == ERROR_CHECKSUM_ERROR) dwVerifyResult |= VERIFY_SECTOR_CHECKSUM_ERROR; break; } // Update CRC32 value if (dwFlags & MPQ_ATTRIBUTE_CRC32) dwCrc32 = crc32(dwCrc32, Buffer, dwBytesRead); // Update MD5 value if (dwFlags & MPQ_ATTRIBUTE_MD5) md5_process(&md5_state, Buffer, dwBytesRead); // Decrement the total size dwTotalBytes -= dwBytesRead; } // If the file has sector checksums, indicate it in the flags if ((hf->pFileEntry->dwFlags & MPQ_FILE_SECTOR_CRC) && hf->SectorChksums != NULL && hf->SectorChksums[0] != 0) dwVerifyResult |= VERIFY_SECTORS_HAVE_CHECKSUM; // Check if the entire file has been read // No point in checking CRC32 and MD5 if not // Skip checksum checks if the file has patches if (dwTotalBytes == 0) { // Check CRC32 and MD5 only if there is no patches if (hf->hfPatchFile == NULL) { // Check if the CRC32 matches. if (dwFlags & MPQ_ATTRIBUTE_CRC32) { // Some files may have their CRC zeroed if (pFileEntry->dwCrc32 != 0) { dwVerifyResult |= VERIFY_FILE_HAS_CHECKSUM; if (dwCrc32 != pFileEntry->dwCrc32) dwVerifyResult |= VERIFY_FILE_CHECKSUM_ERROR; } } // Check if MD5 matches if (dwFlags & MPQ_ATTRIBUTE_MD5) { md5_done(&md5_state, md5); // Some files have the MD5 zeroed. Don't check MD5 in that case if (is_valid_md5(pFileEntry->md5)) { dwVerifyResult |= VERIFY_FILE_HAS_MD5; if (memcmp(md5, pFileEntry->md5, MD5_DIGEST_SIZE)) dwVerifyResult |= VERIFY_FILE_MD5_ERROR; } } } else { // Patched files are MD5-checked automatically dwVerifyResult |= VERIFY_FILE_HAS_MD5; } } else { dwVerifyResult |= VERIFY_READ_ERROR; } SFileCloseFile(hFile); } else { // Remember that the file couldn't be open dwVerifyResult |= VERIFY_OPEN_ERROR; } return dwVerifyResult; }
int SAttrFileLoad(TMPQArchive * ha) { TMPQAttr * pAttr = NULL; HANDLE hFile = NULL; DWORD dwBytesRead; DWORD dwToRead; int nError = ERROR_SUCCESS; // Initially, set the attrobutes to NULL ha->pAttributes = NULL; // Attempt to open the "(attributes)" file. // If it's not there, we don't support attributes if(!SFileOpenFileEx((HANDLE)ha, ATTRIBUTES_NAME, 0, &hFile)) nError = GetLastError(); // Allocate space for the TMPQAttributes if(nError == ERROR_SUCCESS) { pAttr = ALLOCMEM(TMPQAttr, 1); if(pAttr == NULL) nError = ERROR_NOT_ENOUGH_MEMORY; } // Load the content of the attributes file if(nError == ERROR_SUCCESS) { memset(pAttr, 0, sizeof(TMPQAttr)); dwToRead = sizeof(DWORD) + sizeof(DWORD); SFileReadFile(hFile, pAttr, dwToRead, &dwBytesRead, NULL); if(dwBytesRead != dwToRead) nError = ERROR_FILE_CORRUPT; } // Verify format of the attributes if(nError == ERROR_SUCCESS) { if(pAttr->dwVersion > MPQ_ATTRIBUTES_V1) nError = ERROR_BAD_FORMAT; } // Load the CRC32 (if any) if(nError == ERROR_SUCCESS && (pAttr->dwFlags & MPQ_ATTRIBUTE_CRC32)) { pAttr->pCrc32 = ALLOCMEM(TMPQCRC32, ha->pHeader->dwHashTableSize); if(pAttr->pCrc32 != NULL) { memset(pAttr->pCrc32, 0, sizeof(TMPQCRC32) * ha->pHeader->dwHashTableSize); dwToRead = sizeof(TMPQCRC32) * ha->pHeader->dwBlockTableSize; SFileReadFile(hFile, pAttr->pCrc32, dwToRead, &dwBytesRead, NULL); if(dwBytesRead != dwToRead) nError = ERROR_FILE_CORRUPT; } else { nError = ERROR_NOT_ENOUGH_MEMORY; } } // Read the FILETIMEs (if any) if(nError == ERROR_SUCCESS && (pAttr->dwFlags & MPQ_ATTRIBUTE_FILETIME)) { pAttr->pFileTime = ALLOCMEM(TMPQFileTime, ha->pHeader->dwHashTableSize); if(pAttr->pFileTime != NULL) { memset(pAttr->pFileTime, 0, sizeof(TMPQFileTime) * ha->pHeader->dwHashTableSize); dwToRead = sizeof(TMPQFileTime) * ha->pHeader->dwBlockTableSize; SFileReadFile(hFile, pAttr->pFileTime, dwToRead, &dwBytesRead, NULL); if(dwBytesRead != dwToRead) nError = ERROR_FILE_CORRUPT; } else { nError = ERROR_NOT_ENOUGH_MEMORY; } } // Read the MD5 (if any) if(nError == ERROR_SUCCESS && (pAttr->dwFlags & MPQ_ATTRIBUTE_MD5)) { pAttr->pMd5 = ALLOCMEM(TMPQMD5, ha->pHeader->dwHashTableSize); if(pAttr->pMd5 != NULL) { memset(pAttr->pMd5, 0, sizeof(TMPQMD5) * ha->pHeader->dwHashTableSize); dwToRead = sizeof(TMPQMD5) * ha->pHeader->dwBlockTableSize; SFileReadFile(hFile, pAttr->pMd5, dwToRead, &dwBytesRead, NULL); if(dwBytesRead != dwToRead) nError = ERROR_FILE_CORRUPT; } else { nError = ERROR_NOT_ENOUGH_MEMORY; } } // Set the attributes into the MPQ archive if(nError == ERROR_SUCCESS) { ha->pAttributes = pAttr; pAttr = NULL; } // Cleanup & exit FreeMPQAttributes(pAttr); SFileCloseFile(hFile); return nError; }
int ExtractLocaleFile(int MPQId, const char * szArchivedFile, const char * szFileName) { HANDLE hFile = NULL; // Archived file handle TFileStream* handle = NULL; // Disk file handle int nError = ERROR_SUCCESS; // Result value // Open a file in the archive, e.g. "data\global\music\Act1\tristram.wav" if(nError == ERROR_SUCCESS) { if(!SFileOpenFileEx(localeMPQ[MPQId], szArchivedFile, SFILE_OPEN_PATCHED_FILE, &hFile)) nError = GetLastError(); } // Create the target file if(nError == ERROR_SUCCESS) { //handle = CreateFile(szFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL); //if(handle == INVALID_HANDLE_VALUE) // nError = GetLastError(); handle = FileStream_CreateFile(szFileName); if(handle == NULL) nError = GetLastError(); } /* // Get the size of the full patched file dwFileSize = SFileGetFileSize(hFile, NULL); if(dwFileSize != 0) { // Allocate space for the full file pbFullFile = new BYTE[dwFileSize]; if(pbFullFile != NULL) { if(!SFileReadFile(hFile, pbFullFile, dwFileSize)) { nError = GetLastError(); printf("Failed to read full patched file data \"%s\"\n", szFileName); } if(nError == ERROR_SUCCESS) { strcpy(szLocFileName, MAKE_PATH("Work//")); strcat(szLocFileName, GetPlainName(szFileName)); pStream = FileStream_CreateFile(szLocFileName); if(pStream != NULL) { FileStream_Write(pStream, NULL, pbFullFile, dwFileSize); FileStream_Close(pStream); } } delete [] pbFullFile; } } */ // Read the file from the archive if(nError == ERROR_SUCCESS) { // Get the size of the full patched file DWORD dwFileSize = SFileGetFileSize(hFile, NULL); if(dwFileSize != 0) { // Allocate space for the full file BYTE * pbFullFile = new BYTE[dwFileSize]; if(!SFileReadFile(hFile, pbFullFile, dwFileSize)) { nError = GetLastError(); printf("Failed to read full patched file data \"%s\"\n", szFileName); assert(false); } FileStream_Write(handle, NULL, pbFullFile, dwFileSize); delete [] pbFullFile; } } // Cleanup and exit if(handle != NULL) FileStream_Close(handle); if(hFile != NULL) SFileCloseFile(hFile); return nError; }
bool OpenPatchedFile(HANDLE hMpq, const char * szFileName, DWORD dwReserved, HANDLE * phFile) { TMPQArchive * ha = (TMPQArchive *)hMpq; TMPQFile * hfPatch; // Pointer to patch file TMPQFile * hfBase = NULL; // Pointer to base open file TMPQFile * hfLast = NULL; // The highest file in the chain that is not patch file TMPQFile * hf = NULL; HANDLE hPatchFile; char szPatchFileName[MAX_PATH]; // Keep this flag here for future updates dwReserved = dwReserved; // First of all, try to open the original version of the file in any of the patch chain while(ha != NULL) { // Construct the name of the patch file strcpy(szPatchFileName, ha->szPatchPrefix); strcpy(&szPatchFileName[ha->cchPatchPrefix], szFileName); if(SFileOpenFileEx((HANDLE)ha, szPatchFileName, SFILE_OPEN_BASE_FILE, (HANDLE *)&hfBase)) { // The file must be a base file, i.e. without MPQ_FILE_PATCH_FILE if((hfBase->pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE) == 0) { hf = hfLast = hfBase; break; } SFileCloseFile((HANDLE)hfBase); } // Move to the next file in the patch chain ha = ha->haPatch; } // If we couldn't find the file in any of the patches, it doesn't exist if(hf == NULL) { SetLastError(ERROR_FILE_NOT_FOUND); return false; } // Now keep going in the patch chain and open every patch file that is there for(ha = ha->haPatch; ha != NULL; ha = ha->haPatch) { // Construct patch file name strcpy(szPatchFileName, ha->szPatchPrefix); strcpy(&szPatchFileName[ha->cchPatchPrefix], szFileName); if(SFileOpenFileEx((HANDLE)ha, szPatchFileName, SFILE_OPEN_BASE_FILE, &hPatchFile)) { // Remember the new version hfPatch = (TMPQFile *)hPatchFile; // If we encountered a full replacement of the file, // we have to remember the highest full file if((hfPatch->pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE) == 0) hfLast = hfPatch; // Set current patch to base file and move on hf->hfPatchFile = hfPatch; hf = hfPatch; } } // Now we need to free all files that are below the highest unpatched version while(hfBase != hfLast) { TMPQFile * hfNext = hfBase->hfPatchFile; // Free the file below hfBase->hfPatchFile = NULL; FreeMPQFile(hfBase); // Move the base to the next file hfBase = hfNext; } // Give the updated base MPQ if(phFile != NULL) *phFile = (HANDLE)hfBase; return true; }
DBCFile::~DBCFile() { delete [] _data; if (_file != NULL) SFileCloseFile(_file); }
HANDLE WINAPI SListFileFindFirstFile(HANDLE hMpq, const char * szListFile, const char * szMask, SFILE_FIND_DATA * lpFindFileData) { TListFileCache * pCache = NULL; HANDLE hListFile = NULL; size_t nLength = 0; DWORD dwSearchScope = SFILE_OPEN_LOCAL_FILE; int nError = ERROR_SUCCESS; // Initialize the structure with zeros memset(lpFindFileData, 0, sizeof(SFILE_FIND_DATA)); // If the szListFile is NULL, it means we have to open internal listfile if(szListFile == NULL) { // Use SFILE_OPEN_ANY_LOCALE for listfile. This will allow us to load // the listfile even if there is only non-neutral version of the listfile in the MPQ dwSearchScope = SFILE_OPEN_ANY_LOCALE; szListFile = LISTFILE_NAME; } // Open the local/internal listfile if(!SFileOpenFileEx(hMpq, szListFile, dwSearchScope, &hListFile)) nError = GetLastError(); // Load the listfile to cache if(nError == ERROR_SUCCESS) { pCache = CreateListFileCache(hListFile, szMask); if(pCache == NULL) nError = ERROR_FILE_CORRUPT; } // Perform file search if(nError == ERROR_SUCCESS) { for(;;) { // Read the (next) line nLength = ReadListFileLine(pCache, lpFindFileData->cFileName, sizeof(lpFindFileData->cFileName)); if(nLength == 0) { nError = ERROR_NO_MORE_FILES; break; } // If some mask entered, check it if(CheckWildCard(lpFindFileData->cFileName, pCache->szMask)) break; } } // Cleanup & exit if(nError != ERROR_SUCCESS) { memset(lpFindFileData, 0, sizeof(SFILE_FIND_DATA)); SetLastError(nError); } if(pCache != NULL) FreeListFileCache(pCache); if(hListFile != NULL) SFileCloseFile(hListFile); return (HANDLE)pCache; }
static int CheckIfAllFilesKnown(TMPQArchive * ha, const char * szListFile, DWORD * pFileSeeds) { TMPQHash * pHashTableCopy = NULL; // Copy of the hash table TMPQHash * pHash; TMPQHash * pHashEnd = NULL; // End of the hash table DWORD dwFileCount = 0; int nError = ERROR_SUCCESS; // First of all, create a copy of hash table if(nError == ERROR_SUCCESS) { if((pHashTableCopy = CopyHashTable(ha)) == NULL) nError = ERROR_NOT_ENOUGH_MEMORY; pHashEnd = pHashTableCopy + ha->pHeader->dwHashTableSize; // Notify the user if(CompactCB != NULL) CompactCB(lpUserData, CCB_CHECKING_FILES, 0, ha->pHeader->dwBlockTableSize); } // Now check all the files from the filelist if(nError == ERROR_SUCCESS) { SFILE_FIND_DATA wf; HANDLE hFind = SFileFindFirstFile((HANDLE)ha, "*", &wf, szListFile); BOOL bResult = TRUE; // Do while some files have been found while(hFind != NULL && bResult) { TMPQHash * pHash = GetHashEntry(ha, wf.cFileName); // If the hash table entry has been found, find it's position // in the hash table copy if(pHash != NULL) { pHash = pHashTableCopy + (pHash - ha->pHashTable); if(pHash->dwName1 != (DWORD)-1 && pHash->dwName2 != (DWORD)-1) { TMPQBlock * pBlock = ha->pBlockTable + pHash->dwBlockIndex; DWORD dwSeed = 0; // Resolve the file seed. Use plain file name for it if(pBlock->dwFlags & MPQ_FILE_ENCRYPTED) { char * szFileName = strrchr(wf.cFileName, '\\'); if(szFileName == NULL) szFileName = wf.cFileName; else szFileName++; dwSeed = DecryptFileSeed(szFileName); if(pBlock->dwFlags & MPQ_FILE_FIXSEED) dwSeed = (dwSeed + pBlock->dwFilePos) ^ pBlock->dwFSize; } pFileSeeds[pHash->dwBlockIndex] = dwSeed; pHash->dwName1 = 0xFFFFFFFF; pHash->dwName2 = 0xFFFFFFFF; pHash->lcLocale = 0xFFFF; pHash->wPlatform = 0xFFFF; pHash->dwBlockIndex = 0xFFFFFFFF; } } // Notify the user if(CompactCB != NULL) CompactCB(lpUserData, CCB_CHECKING_FILES, ++dwFileCount, ha->pHeader->dwBlockTableSize); // Find the next file in the archive bResult = SFileFindNextFile(hFind, &wf); } if(hFind != NULL) SFileFindClose(hFind); } // When the filelist checking is complete, parse the hash table copy and find the if(nError == ERROR_SUCCESS) { // Notify the user about checking hash table dwFileCount = 0; if(CompactCB != NULL) CompactCB(lpUserData, CCB_CHECKING_HASH_TABLE, dwFileCount, ha->pHeader->dwBlockTableSize); for(pHash = pHashTableCopy; pHash < pHashEnd; pHash++) { // If there is an unresolved entry, try to detect its seed. If it fails, // we cannot complete the work if(pHash->dwName1 != (DWORD)-1 && pHash->dwName2 != (DWORD)-1) { HANDLE hFile = NULL; DWORD dwFlags = 0; DWORD dwSeed = 0; if(SFileOpenFileEx((HANDLE)ha, (char *)(DWORD_PTR)pHash->dwBlockIndex, SFILE_OPEN_BY_INDEX, &hFile)) { TMPQFile * hf = (TMPQFile *)hFile; dwFlags = hf->pBlock->dwFlags; dwSeed = hf->dwSeed1; SFileCloseFile(hFile); } // If the file is encrypted, we have to check // If we can apply the file decryption seed if(dwFlags & MPQ_FILE_ENCRYPTED && dwSeed == 0) { nError = ERROR_CAN_NOT_COMPLETE; break; } // Remember the seed pFileSeeds[pHash->dwBlockIndex] = dwSeed; // Notify the user if(CompactCB != NULL) CompactCB(lpUserData, CCB_CHECKING_HASH_TABLE, ++dwFileCount, ha->pHeader->dwBlockTableSize); } } } // Delete the copy of hash table if(pHashTableCopy != NULL) FREEMEM(pHashTableCopy); return nError; }
// Performs one MPQ search static int DoMPQSearch(TMPQSearch * hs, SFILE_FIND_DATA * lpFindFileData) { TMPQArchive * ha = hs->ha; TFileEntry * pFileTableEnd; TFileEntry * pFileEntry; const char * szFileName; char szPseudoName[20]; DWORD dwBlockIndex; size_t nPrefixLength; // Do that for all files in the patch chain while(ha != NULL) { // Now parse the file entry table in order to get all files. pFileTableEnd = ha->pFileTable + ha->dwFileTableSize; pFileEntry = ha->pFileTable + hs->dwNextIndex; // Get the start and end of the hash table nPrefixLength = strlen(ha->szPatchPrefix); // Parse the file table while(pFileEntry < pFileTableEnd) { // Increment the next index for subsequent search hs->dwNextIndex++; // Does the block exist ? if(pFileEntry->dwFlags & MPQ_FILE_EXISTS) { // Prepare the block index dwBlockIndex = (DWORD)(pFileEntry - ha->pFileTable); // Get the file name. If it's not known, we will create pseudo-name szFileName = pFileEntry->szFileName; if(szFileName == NULL) { HANDLE hFile; // Open the file by index in order to check if the file exists if(SFileOpenFileEx((HANDLE)hs->ha, (char *)(DWORD_PTR)dwBlockIndex, SFILE_OPEN_BY_INDEX, &hFile)) SFileCloseFile(hFile); // If the name was retrieved, use that one. Otherwise, just use generic pseudo-name szFileName = pFileEntry->szFileName; if(szFileName == NULL) { sprintf(szPseudoName, "File%08u.xxx", dwBlockIndex); szFileName = szPseudoName; } } // If we are already in the patch MPQ, we skip all files // that don't have the appropriate patch prefix and are patch files if(ha->haBase != NULL) { // If the file has different patch prefix, don't report it if(nPrefixLength != 0 && _strnicmp(szFileName, ha->szPatchPrefix, nPrefixLength)) goto __SkipThisFile; // // We need to properly handle the following case: // // 1) Base MPQ file doesn't contain the desired file // 2) First patch MPQ contains the file with MPQ_FILE_PATCH_FILE // 3) Second patch contains full version of the file (MPQ_FILE_PATCH_FILE is not set) // if(IsBaseFileMissing(ha, szFileName, szFileName + nPrefixLength, pFileEntry->lcLocale)) goto __SkipThisFile; } // Check the file name. if(CheckWildCard(szFileName, hs->szSearchMask)) { // Fill the found entry lpFindFileData->dwHashIndex = pFileEntry->dwHashIndex; lpFindFileData->dwBlockIndex = dwBlockIndex; lpFindFileData->dwFileSize = pFileEntry->dwFileSize; lpFindFileData->dwFileFlags = pFileEntry->dwFlags; lpFindFileData->dwCompSize = pFileEntry->dwCmpSize; lpFindFileData->lcLocale = pFileEntry->lcLocale; // Fill the filetime lpFindFileData->dwFileTimeHi = (DWORD)(pFileEntry->FileTime >> 32); lpFindFileData->dwFileTimeLo = (DWORD)(pFileEntry->FileTime); // Fill the file name and plain file name strcpy(lpFindFileData->cFileName, szFileName + nPrefixLength); lpFindFileData->szPlainName = (char *)GetPlainFileName(lpFindFileData->cFileName); return ERROR_SUCCESS; } } // Move to the next file entry __SkipThisFile: pFileEntry++; }
void CMpqDataStream::Close() { SFileCloseFile(m_hFile); }
QByteArray MPQ::readFile(const QString &fileName) { static QHash<QString, QByteArray> files; static const QString mpqs[] = { "patch-2.MPQ", "patch.MPQ", "dbc.MPQ", "model.MPQ", "interface.MPQ", "texture.MPQ", "" }; if (files.contains(fileName)) return files[fileName]; const QString *mpq = mpqs; HANDLE hMPQ = 0; while (!mpq->isEmpty()) { QString path = MPQ::gameDir() + "Data/" + *mpq; hMPQ = getHandle(path); if (hMPQ && SFileHasFile(hMPQ, fileName.toUtf8().constData())) break; else hMPQ = 0; mpq++; } if (!hMPQ) { qCritical("File '%s' not found", qPrintable(fileName)); return files[fileName]; } HANDLE hFile; if (!SFileOpenFileEx(hMPQ, fileName.toUtf8().constData(), SFILE_OPEN_FROM_MPQ, &hFile)) { qCritical("Cannot open file '%s' from archive", qPrintable(fileName)); return files[fileName]; } DWORD size = SFileGetFileSize(hFile, NULL); if (size == SFILE_INVALID_SIZE) { qCritical("Cannot read file '%s' from archive", qPrintable(fileName)); return files[fileName]; } char *data = new char[size]; if (!SFileReadFile(hFile, data, size, NULL, NULL)) { qCritical("Cannot read file '%s' from archive", qPrintable(fileName)); SFileCloseFile(hFile); delete[] data; return files[fileName]; } SFileCloseFile(hFile); files[fileName] = QByteArray(data, size); delete[] data; return files[fileName]; }
// Performs one MPQ search static int DoMPQSearch(TMPQSearch * hs, SFILE_FIND_DATA * lpFindFileData) { TMPQArchive * ha = hs->ha; TFileEntry * pFileTableEnd; TFileEntry * pPatchEntry; TFileEntry * pFileEntry; const char * szFileName; HANDLE hFile; char szPseudoName[20]; DWORD dwBlockIndex; size_t nPrefixLength; // Start searching with base MPQ while(ha != NULL) { // Now parse the file entry table in order to get all files. pFileTableEnd = ha->pFileTable + ha->dwFileTableSize; pFileEntry = ha->pFileTable + hs->dwNextIndex; // Get the length of the patch prefix (0 if none) nPrefixLength = strlen(ha->szPatchPrefix); // Parse the file table while(pFileEntry < pFileTableEnd) { // Increment the next index for subsequent search hs->dwNextIndex++; // Is it a file and not a patch file? if((pFileEntry->dwFlags & hs->dwFlagMask) == MPQ_FILE_EXISTS) { // Now we have to check if this file was not enumerated before if(!FileWasFoundBefore(ha, hs, pFileEntry)) { // Find a patch to this file pPatchEntry = FindPatchEntry(ha, pFileEntry); if(pPatchEntry == NULL) pPatchEntry = pFileEntry; // Prepare the block index dwBlockIndex = (DWORD)(pFileEntry - ha->pFileTable); // Get the file name. If it's not known, we will create pseudo-name szFileName = pFileEntry->szFileName; if(szFileName == NULL) { // Open the file by its pseudo-name. // This also generates the file name with a proper extension sprintf(szPseudoName, "File%08u.xxx", dwBlockIndex); if(SFileOpenFileEx((HANDLE)hs->ha, szPseudoName, SFILE_OPEN_BASE_FILE, &hFile)) { szFileName = (pFileEntry->szFileName != NULL) ? pFileEntry->szFileName : szPseudoName; SFileCloseFile(hFile); } } // Check the file name against the wildcard if(CheckWildCard(szFileName + nPrefixLength, hs->szSearchMask)) { // Fill the found entry lpFindFileData->dwHashIndex = pPatchEntry->dwHashIndex; lpFindFileData->dwBlockIndex = dwBlockIndex; lpFindFileData->dwFileSize = pPatchEntry->dwFileSize; lpFindFileData->dwFileFlags = pPatchEntry->dwFlags; lpFindFileData->dwCompSize = pPatchEntry->dwCmpSize; lpFindFileData->lcLocale = pPatchEntry->lcLocale; // Fill the filetime lpFindFileData->dwFileTimeHi = (DWORD)(pPatchEntry->FileTime >> 32); lpFindFileData->dwFileTimeLo = (DWORD)(pPatchEntry->FileTime); // Fill the file name and plain file name strcpy(lpFindFileData->cFileName, szFileName + nPrefixLength); lpFindFileData->szPlainName = (char *)GetPlainFileNameA(lpFindFileData->cFileName); return ERROR_SUCCESS; } } } pFileEntry++; }
static int CheckIfAllFilesKnown(TMPQArchive * ha, const char * szListFile, DWORD * pFileKeys) { TMPQHash * pHashTableCopy = NULL; // Copy of the hash table TMPQHash * pHash; TMPQHash * pHashEnd = NULL; // End of the hash table DWORD dwFileCount = 0; int nError = ERROR_SUCCESS; // First of all, create a copy of hash table if(nError == ERROR_SUCCESS) { if((pHashTableCopy = CopyHashTable(ha)) == NULL) nError = ERROR_NOT_ENOUGH_MEMORY; pHashEnd = pHashTableCopy + ha->pHeader->dwHashTableSize; // Notify the user if(CompactCB != NULL) CompactCB(pvUserData, CCB_CHECKING_FILES, &CompactBytesProcessed, &CompactTotalBytes); } // Now check all the files from the listfile if(nError == ERROR_SUCCESS) { SFILE_FIND_DATA wf; HANDLE hFind = SFileFindFirstFile((HANDLE)ha, "*", &wf, szListFile); bool bResult = true; // Do while something has been found while(hFind != NULL && bResult) { TMPQHash * pHash = ha->pHashTable + wf.dwHashIndex; // Find the entry in the hash table copy pHash = pHashTableCopy + (pHash - ha->pHashTable); if(pHash->dwBlockIndex != HASH_ENTRY_FREE) { TMPQBlock * pBlock = ha->pBlockTable + pHash->dwBlockIndex; DWORD dwFileKey = 0; // Resolve the file key. Use plain file name for it if(pBlock->dwFlags & MPQ_FILE_ENCRYPTED) { const char * szPlainName = GetPlainMpqFileName(wf.cFileName); dwFileKey = DecryptFileKey(szPlainName); if(pBlock->dwFlags & MPQ_FILE_FIX_KEY) dwFileKey = (dwFileKey + pBlock->dwFilePos) ^ pBlock->dwFSize; } // Give the key to the caller pFileKeys[pHash->dwBlockIndex] = dwFileKey; pHash->dwName1 = 0xFFFFFFFF; pHash->dwName2 = 0xFFFFFFFF; pHash->lcLocale = 0xFFFF; pHash->wPlatform = 0xFFFF; pHash->dwBlockIndex = HASH_ENTRY_FREE; } // Find the next file in the archive bResult = SFileFindNextFile(hFind, &wf); } if(hFind != NULL) SFileFindClose(hFind); } // When the filelist checking is complete, parse the hash table copy and find the if(nError == ERROR_SUCCESS) { // Notify the user about checking hash table dwFileCount = 0; if(CompactCB != NULL) CompactCB(pvUserData, CCB_CHECKING_HASH_TABLE, &CompactBytesProcessed, &CompactTotalBytes); for(pHash = pHashTableCopy; pHash < pHashEnd; pHash++) { // If there is an unresolved entry, try to detect its key. If it fails, // we cannot complete the work if(pHash->dwBlockIndex < ha->pHeader->dwBlockTableSize) { HANDLE hFile = NULL; DWORD dwFlags = 0; DWORD dwFileKey = 0; if(SFileOpenFileEx((HANDLE)ha, (char *)(DWORD_PTR)pHash->dwBlockIndex, SFILE_OPEN_BY_INDEX, &hFile)) { TMPQFile * hf = (TMPQFile *)hFile; dwFlags = hf->pBlock->dwFlags; dwFileKey = hf->dwFileKey; SFileCloseFile(hFile); } // If the file is encrypted, we have to check // If we can apply the file decryption key if((dwFlags & MPQ_FILE_ENCRYPTED) && dwFileKey == 0) { nError = ERROR_CAN_NOT_COMPLETE; break; } // Give the key to the caller pFileKeys[pHash->dwBlockIndex] = dwFileKey; } } } // Delete the copy of hash table if(pHashTableCopy != NULL) FREEMEM(pHashTableCopy); return nError; }
static DWORD VerifyFile( HANDLE hMpq, const char * szFileName, LPDWORD pdwCrc32, char * pMD5, DWORD dwFlags) { hash_state md5_state; unsigned char * pFileMd5; unsigned char md5[MD5_DIGEST_SIZE]; TFileEntry * pFileEntry; TMPQFile * hf; BYTE Buffer[0x1000]; HANDLE hFile = NULL; DWORD dwVerifyResult = 0; DWORD dwSearchScope = SFILE_OPEN_FROM_MPQ; DWORD dwTotalBytes = 0; DWORD dwBytesRead; DWORD dwCrc32 = 0; // Fix the open type for patched archives if(SFileIsPatchedArchive(hMpq)) dwSearchScope = SFILE_OPEN_PATCHED_FILE; // Attempt to open the file if(SFileOpenFileEx(hMpq, szFileName, dwSearchScope, &hFile)) { // Get the file size hf = (TMPQFile *)hFile; pFileEntry = hf->pFileEntry; dwTotalBytes = SFileGetFileSize(hFile, NULL); // Initialize the CRC32 and MD5 contexts md5_init(&md5_state); dwCrc32 = crc32(0, Z_NULL, 0); // If we have to verify raw data MD5, do it if(dwFlags & SFILE_VERIFY_RAW_MD5) { if(hf->ha->pHeader->dwRawChunkSize != 0) { // Note: we have to open the file from the MPQ where it was open from if(VerifyRawMpqData(hf->ha, pFileEntry->ByteOffset, pFileEntry->dwCmpSize) != ERROR_SUCCESS) dwVerifyResult |= VERIFY_FILE_RAW_MD5_ERROR; dwVerifyResult |= VERIFY_FILE_HAS_RAW_MD5; } } // Also turn on sector checksum verification if(dwFlags & SFILE_VERIFY_SECTOR_CRC) hf->bCheckSectorCRCs = true; // Go through entire file and update both CRC32 and MD5 for(;;) { // Read data from file SFileReadFile(hFile, Buffer, sizeof(Buffer), &dwBytesRead, NULL); if(dwBytesRead == 0) { if(GetLastError() == ERROR_CHECKSUM_ERROR) dwVerifyResult |= VERIFY_FILE_SECTOR_CRC_ERROR; break; } // Update CRC32 value if(dwFlags & SFILE_VERIFY_FILE_CRC) dwCrc32 = crc32(dwCrc32, Buffer, dwBytesRead); // Update MD5 value if(dwFlags & SFILE_VERIFY_FILE_MD5) md5_process(&md5_state, Buffer, dwBytesRead); // Decrement the total size dwTotalBytes -= dwBytesRead; } // If the file has sector checksums, indicate it in the flags if(dwFlags & SFILE_VERIFY_SECTOR_CRC) { if((hf->pFileEntry->dwFlags & MPQ_FILE_SECTOR_CRC) && hf->SectorChksums != NULL && hf->SectorChksums[0] != 0) dwVerifyResult |= VERIFY_FILE_HAS_SECTOR_CRC; } // Check if the entire file has been read // No point in checking CRC32 and MD5 if not // Skip checksum checks if the file has patches if(dwTotalBytes == 0) { // Check CRC32 and MD5 only if there is no patches if(hf->hfPatchFile == NULL) { // Check if the CRC32 matches. if(dwFlags & SFILE_VERIFY_FILE_CRC) { // Only check the CRC32 if it is valid if(pFileEntry->dwCrc32 != 0) { dwVerifyResult |= VERIFY_FILE_HAS_CHECKSUM; if(dwCrc32 != pFileEntry->dwCrc32) dwVerifyResult |= VERIFY_FILE_CHECKSUM_ERROR; } } // Check if MD5 matches if(dwFlags & SFILE_VERIFY_FILE_MD5) { // Patch files have their MD5 saved in the patch info pFileMd5 = (hf->pPatchInfo != NULL) ? hf->pPatchInfo->md5 : pFileEntry->md5; md5_done(&md5_state, md5); // Only check the MD5 if it is valid if(is_valid_md5(pFileMd5)) { dwVerifyResult |= VERIFY_FILE_HAS_MD5; if(memcmp(md5, pFileMd5, MD5_DIGEST_SIZE)) dwVerifyResult |= VERIFY_FILE_MD5_ERROR; } } } else { // Patched files are MD5-checked automatically dwVerifyResult |= VERIFY_FILE_HAS_MD5; } } else { dwVerifyResult |= VERIFY_READ_ERROR; } SFileCloseFile(hFile); } else { // Remember that the file couldn't be open dwVerifyResult |= VERIFY_OPEN_ERROR; } // If the caller required CRC32 and/or MD5, give it to him if(pdwCrc32 != NULL) *pdwCrc32 = dwCrc32; if(pMD5 != NULL) memcpy(pMD5, md5, MD5_DIGEST_SIZE); return dwVerifyResult; }
bool DBCFile::open() { //if (!OpenNewestFile(filename.c_str(), &fileHandle)) // return false; char header[4]; unsigned int na, nb, es, ss; if (!SFileReadFile(fileHandle, header, 4, NULL, NULL)) // Magic header { SFileCloseFile(fileHandle); return false; } if (header[0] != 'W' || header[1] != 'D' || header[2] != 'B' || header[3] != 'C') { SFileCloseFile(fileHandle); return false; } if (!SFileReadFile(fileHandle, &na, 4, NULL, NULL)) // Number of records { SFileCloseFile(fileHandle); return false; } if (!SFileReadFile(fileHandle, &nb, 4, NULL, NULL)) // Number of fields { SFileCloseFile(fileHandle); return false; } if (!SFileReadFile(fileHandle, &es, 4, NULL, NULL)) // Size of a record { SFileCloseFile(fileHandle); return false; } if (!SFileReadFile(fileHandle, &ss, 4, NULL, NULL)) // String size { SFileCloseFile(fileHandle); return false; } recordSize = es; recordCount = na; fieldCount = nb; stringSize = ss; if (fieldCount * 4 != recordSize) { SFileCloseFile(fileHandle); return false; } data = new unsigned char[recordSize * recordCount + stringSize]; stringTable = data + recordSize * recordCount; size_t data_size = recordSize * recordCount + stringSize; if (!SFileReadFile(fileHandle, data, data_size, NULL, NULL)) { SFileCloseFile(fileHandle); return false; } SFileCloseFile(fileHandle); return true; }
static bool DoMPQSearch_FileEntry( TMPQSearch * hs, SFILE_FIND_DATA * lpFindFileData, TMPQArchive * ha, TMPQHash * pHashEntry, TFileEntry * pFileEntry) { TFileEntry * pPatchEntry; HANDLE hFile = NULL; const char * szFileName; size_t nPrefixLength = (ha->pPatchPrefix != NULL) ? ha->pPatchPrefix->nLength : 0; DWORD dwBlockIndex; char szNameBuff[MAX_PATH]; // Is it a file but not a patch file? if((pFileEntry->dwFlags & hs->dwFlagMask) == MPQ_FILE_EXISTS) { // Now we have to check if this file was not enumerated before if(!FileWasFoundBefore(ha, hs, pFileEntry)) { // if(pFileEntry != NULL && !_stricmp(pFileEntry->szFileName, "TriggerLibs\\NativeLib.galaxy")) // DebugBreak(); // Find a patch to this file pPatchEntry = FindPatchEntry(ha, pFileEntry); if(pPatchEntry == NULL) pPatchEntry = pFileEntry; // Prepare the block index dwBlockIndex = (DWORD)(pFileEntry - ha->pFileTable); // Get the file name. If it's not known, we will create pseudo-name szFileName = pFileEntry->szFileName; if(szFileName == NULL) { // Open the file by its pseudo-name. sprintf(szNameBuff, "File%08u.xxx", (unsigned int)dwBlockIndex); if(SFileOpenFileEx((HANDLE)hs->ha, szNameBuff, SFILE_OPEN_BASE_FILE, &hFile)) { SFileGetFileName(hFile, szNameBuff); szFileName = szNameBuff; SFileCloseFile(hFile); } } // If the file name is still NULL, we cannot include the file to search results if(szFileName != NULL) { // Check the file name against the wildcard if(CheckWildCard(szFileName + nPrefixLength, hs->szSearchMask)) { // Fill the found entry. hash entry and block index are taken from the base MPQ lpFindFileData->dwHashIndex = HASH_ENTRY_FREE; lpFindFileData->dwBlockIndex = dwBlockIndex; lpFindFileData->dwFileSize = pPatchEntry->dwFileSize; lpFindFileData->dwFileFlags = pPatchEntry->dwFlags; lpFindFileData->dwCompSize = pPatchEntry->dwCmpSize; lpFindFileData->lcLocale = 0; // pPatchEntry->lcLocale; // Fill the filetime lpFindFileData->dwFileTimeHi = (DWORD)(pPatchEntry->FileTime >> 32); lpFindFileData->dwFileTimeLo = (DWORD)(pPatchEntry->FileTime); // Fill-in the entries from hash table entry, if given if(pHashEntry != NULL) { lpFindFileData->dwHashIndex = (DWORD)(pHashEntry - ha->pHashTable); lpFindFileData->lcLocale = pHashEntry->lcLocale; } // Fill the file name and plain file name StringCopyA(lpFindFileData->cFileName, szFileName + nPrefixLength, MAX_PATH-1); lpFindFileData->szPlainName = (char *)GetPlainFileName(lpFindFileData->cFileName); return true; } } }
//------------------------------------------------- DLL MAIN ------------------------------------------------- BOOL APIENTRY DllMain(HMODULE, DWORD ul_reason_for_call, LPVOID) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: { #ifdef _DEBUG _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); #endif // Retrieve the initial configuration stuff if not already InitPrimaryConfig(); // Get revision/build automatically char szDllPath[MAX_PATH]; DWORD dwDesiredRevision = 0; std::string aicfg = LoadConfigString("ai", BUILD_DEBUG ? "ai_dbg" : "ai", "_NULL"); strncpy(szDllPath, aicfg.c_str(), MAX_PATH); if ( aicfg == "_NULL" ) { BWAPIError("Could not find %s under ai in \"%s\" for revision identification.", BUILD_DEBUG ? "ai_dbg" : "ai", szConfigPath); } else { DWORD dwDesiredBuild = 0; // 0 = undefined, 1 = release, 2 = debug // Tokenize and retrieve correct path for the instance number char *pszDll = strtok(szDllPath, ","); for ( unsigned int i = 0; i < gdwProcNum-1; ++i ) { char *pszNext = strtok(NULL, ","); if ( !pszNext ) break; pszDll = pszNext; } // Retrieve revision info if it exists char *pszLoadRevCheck = strchr(pszDll, ':'); if ( pszLoadRevCheck ) { pszLoadRevCheck[0] = 0; ++pszLoadRevCheck; sscanf(pszLoadRevCheck, "%u", &dwDesiredRevision); } // Remove spaces while ( isspace(pszDll[0]) ) ++pszDll; // Open File HANDLE hFile = NULL; if ( !SFileOpenFileEx(NULL, pszDll, SFILE_FROM_ABSOLUTE, &hFile) || !hFile) { BWAPIError("Could not load module \"%s\" for revision identification.", pszDll); } else { // Obtain file size DWORD dwFileSize = SFileGetFileSize(hFile, 0); // Allocate memory char *pbBuffer = (char*)SMAlloc(dwFileSize); if ( !pbBuffer ) { BWAPIError("Unable to allocate enough memory for module \"%s\" for revision identification.", pszDll); } else { // Read file DWORD dwBytesRead = 0; SFileReadFile(hFile, pbBuffer, dwFileSize, &dwBytesRead, 0); for ( u32 i = 0; i < dwBytesRead && (dwDesiredRevision == 0 || dwDesiredBuild == 0); ++i ) { if ( dwDesiredRevision == 0 && memcmp(&pbBuffer[i], "XBWAPIXREVISIONXSTAMPX", 22) == 0 ) { i += 22; sscanf(&pbBuffer[i], "%u", &dwDesiredRevision); i += 5; } // if REVISION if ( memcmp(&pbBuffer[i], "XBWAPIXBUILDXSTAMPX", 19) == 0 ) { i += 19; if ( strcmp(&pbBuffer[i], "DEBUG") == 0 ) { dwDesiredBuild = 2; i += 6; } else if ( strcmp(&pbBuffer[i], "RELEASE") == 0 ) { dwDesiredBuild = 1; i += 8; } } // if BUILD } // for (iterate file) // Free memory and close file SMFree(pbBuffer); SFileCloseFile(hFile); } // buffer was allocated } // file was opened /* Do revision checking */ if ( dwDesiredRevision > 0 && dwDesiredRevision != SVN_REV ) { // revision that ai_dll_# for multiple instances was introduced if ( gdwProcNum && dwDesiredRevision < 2753 && showWarn ) { char err[512]; sprintf(err, "Revision %u is not compatible with multiple instances.\nExpecting revision 2753 (BWAPI Beta 3.1) or greater. If you proceed, the older revision of BWAPI will attempt to load its module from ai_dll instead of the multi-instance specification. Do you want to continue anyway?", dwDesiredRevision); BWAPIError("%s", err); if ( MessageBox(NULL, err, "Error", MB_YESNO | MB_ICONWARNING | MB_DEFBUTTON1 | MB_TASKMODAL) == IDNO ) return TRUE; } // revision is old if ( dwDesiredBuild == 0 ) dwDesiredBuild = BUILD_DEBUG + 1; char szRevModule[MAX_PATH]; sprintf_s(szRevModule, MAX_PATH, "%sbwapi-data\\revisions\\%u%s.dll", szInstallPath, dwDesiredRevision, dwDesiredBuild == 2 ? "d" : ""); HMODULE hLib = LoadLibrary(szRevModule); if ( hLib ) { if ( showWarn ) { char msg[MAX_PATH+32]; char szLoadedName[MAX_PATH]; GetModuleFileName(hLib, szLoadedName, MAX_PATH); sprintf_s(msg, MAX_PATH+32, "Loaded \"%s\" instead.", szLoadedName); MessageBox(NULL, msg, "Success", MB_OK | MB_ICONINFO); } return TRUE; } if ( showWarn ) { char err[512]; sprintf(err, "Couldn't find revision module \"%s\" of which the AI DLL was compiled for. Do you want to try using the current revision instead?", szRevModule); BWAPIError("%s", err); if ( MessageBox(NULL, err, "Error", MB_YESNO | MB_ICONWARNING | MB_DEFBUTTON1 | MB_TASKMODAL) == IDNO ) return TRUE; } } // specified rev is not this one else if ( dwDesiredBuild && BUILD_DEBUG + 1 != dwDesiredBuild ) { char envBuffer[MAX_PATH]; if ( !GetEnvironmentVariable("ChaosDir", envBuffer, MAX_PATH) ) if ( !GetCurrentDirectory(MAX_PATH, envBuffer) && showWarn ) BWAPIError("Could not find ChaosDir or current directory for build identification."); SStrNCat(envBuffer, "\\BWAPI", MAX_PATH); if ( dwDesiredBuild == 2 ) SStrNCat(envBuffer, "d", MAX_PATH); SStrNCat(envBuffer, ".dll", MAX_PATH); HMODULE hLib = LoadLibrary(envBuffer); if ( hLib ) { if ( showWarn ) { char msg[MAX_PATH+32]; sprintf_s(msg, MAX_PATH+32, "Loaded \"%s\" instead.", envBuffer); MessageBox(NULL, msg, "Success", MB_OK | MB_ICONINFO); } return TRUE; } if ( showWarn ) { char err[512]; sprintf(err, "Couldn't find build module \"%s\" of which the AI DLL was compiled for. Do you want to try using the current build instead?", envBuffer); BWAPIError("%s", err); if ( MessageBox(NULL, err, "Error", MB_YESNO | MB_ICONWARNING | MB_DEFBUTTON1 | MB_TASKMODAL) == IDNO ) return TRUE; } return TRUE; } } // module str was found // Do version checking CheckVersion(); // Load the auto-menu config BWAPI::BroodwarImpl.loadAutoMenuData(); // Apply all hacks and patches to the game ApplyCodePatches(); // Initialize BWAPI BWAPI::BWAPI_init(); // Create our thread that persistently applies hacks CreateThread(NULL, 0, &PersistentPatch, NULL, 0, NULL); return TRUE; } } return TRUE; }