bool FileToBuffer(MPQHANDLE &hMpq, const std::string &fileName, buffer &buf) { if ( hMpq == nullptr ) CHKD_ERR("NULL MPQ file specified for opening %s", fileName.c_str()); else { u32 bytesRead = 0; HANDLE openFile = NULL; if ( SFileGetFileInfo(hMpq, SFILE_INFO_NUM_FILES) != 0xFFFFFFFF ) { if ( SFileOpenFileEx(hMpq, fileName.c_str(), SFILE_SEARCH_CURRENT_ONLY, &openFile) ) { u32 fileSize = (u32)SFileGetFileSize(openFile, NULL); if ( buf.setSize(fileSize) ) { buf.sizeUsed = fileSize; SFileReadFile(openFile, (LPVOID)buf.data, buf.sizeUsed, (LPDWORD)(&bytesRead), NULL); SFileCloseFile(openFile); if ( buf.sizeUsed == bytesRead ) return true; } else SFileCloseFile(openFile); } else CHKD_ERR("Failed to get %s from MPQ file", fileName.c_str()); } else CHKD_ERR("File is already open", fileName.c_str()); } return false; }
int MPQStream::GetLength() { if (handle != nullptr) return SFileGetFileSize(handle, nullptr); else return 0; }
bool ExtractFile( char const* mpq_name, std::string const& filename ) { for(ArchiveSet::const_reverse_iterator i=gOpenArchives.rbegin(); i!=gOpenArchives.rend();++i) { HANDLE fileHandle; if (!SFileOpenFileEx(*i, mpq_name, SFILE_OPEN_PATCHED_FILE, &fileHandle)) continue; if (SFileGetFileSize(fileHandle, NULL) == 0) // some files removed in next updates and its reported size 0 { SFileCloseFile(fileHandle); return true; } SFileCloseFile(fileHandle); if (!SFileExtractFile(*i, mpq_name, filename.c_str(), SFILE_OPEN_PATCHED_FILE)) { printf("Can't extract file: %s\n", mpq_name); return false; } return true; } printf("Extracting file not found: %s\n", filename.c_str()); return false; }
int getFileType(const char *szFileName) { if ( !szFileName ) return 0; int rVal = 0; HANDLE hMPQ; HANDLE hFile; // Open archive for map checking if ( SFileOpenArchive(szFileName, 0, 0, &hMPQ) && hMPQ ) { // Open scenario.chk file if ( SFileOpenFileEx(hMPQ, "staredit\\scenario.chk", SFILE_FROM_MPQ, &hFile) && hFile ) { rVal = 1; SFileCloseFile(hFile); } // Close archive SFileCloseArchive(hMPQ); } else if ( SFileOpenFileEx(NULL, szFileName, SFILE_FROM_ABSOLUTE, &hFile) && hFile ) { DWORD dwRead = 0; char tbuff[16]; DWORD dwSize = SFileGetFileSize(hFile, 0); // Read file data to check if it's a replay if ( dwSize > 16 && SFileReadFile(hFile, &tbuff, 16, &dwRead, 0) && dwRead == 16 && *(DWORD*)&tbuff[12] == 'SRer' ) rVal = 2; // Close file SFileCloseFile(hFile); } return rVal; }
bool FileLoader::loadFile(HANDLE mpq, char* filename, bool log) { free(); HANDLE file; if (!SFileOpenFileEx(mpq, filename, SFILE_OPEN_PATCHED_FILE, &file)) { if (log) printf("No such file %s\n", filename); return false; } data_size = SFileGetFileSize(file, NULL); data = new uint8[data_size]; if (data) { SFileReadFile(file, data, data_size, NULL/*bytesRead*/, NULL); if (prepareLoadedData()) { SFileCloseFile(file); return true; } } printf("Error loading %s\n", filename); SFileCloseFile(file); free(); return false; }
MPQFile::MPQFile(const char* filename): eof(false), buffer(0), pointer(0), size(0) { for(ArchiveSet::const_iterator i=gOpenArchives.archives.begin(); i!=gOpenArchives.archives.end();++i) { HANDLE hFile = ""; hMPQ = i->hMPQ; BOOL succ = SFileOpenFileEx(hMPQ,filename,0, &hFile); if (succ) { DWORD s = SFileGetFileSize(hFile, 0); if (!s) { eof = true; buffer = 0; return; } size = (size_t)s; buffer = new char[s]; SFileReadFile(hFile, buffer, s, 0, 0); SFileCloseFile(hFile); eof = false; return; } } eof = true; buffer = 0; }
DWORD WINAPI SFileGetFileSize_stub(MPQHANDLE hFile, LPDWORD lpFileSizeHigh) { LoadSFMpqDll(); if (hSFMpq) { *(FARPROC *)&SFileGetFileSize = GetProcAddress(hSFMpq,"SFileGetFileSize"); if (SFileGetFileSize) return SFileGetFileSize(hFile,lpFileSizeHigh); } return (DWORD)-1; }
static PHYSFS_Io *MPQ_openRead(void *opaque, const char *filename) { char *filename2 = NULL; HANDLE hFile; PHYSFS_Io *retval = NULL; MPQFileHandle *handle = NULL; DWORD dwFileSizeHi = 0xCCCCCCCC; DWORD dwFileSizeLo = 0; char success; if (!opaque) return NULL; filename2 = MPQ_getValidFilename(filename); if (!filename2) return NULL; success = SFileOpenFileEx(((MPQHandle *)opaque)->mpqHandle, filename2, 0, &hFile); allocator.Free(filename2); if (!success) return NULL; retval = (PHYSFS_Io *)allocator.Malloc(sizeof(PHYSFS_Io)); if (!retval) { SFileCloseFile(hFile); return NULL; } handle = (MPQFileHandle *)allocator.Malloc(sizeof(MPQFileHandle)); if (!handle) { allocator.Free(retval); SFileCloseFile(hFile); return NULL; } dwFileSizeLo = SFileGetFileSize(hFile, &dwFileSizeHi); if (dwFileSizeLo == SFILE_INVALID_SIZE || dwFileSizeHi != 0) { allocator.Free(retval); allocator.Free(hFile); SFileCloseFile(hFile); return NULL; } handle->fileHandle = hFile; handle->size = (PHYSFS_sint64)dwFileSizeLo; memcpy(retval, &MPQ_Io, sizeof(PHYSFS_Io)); retval->opaque = handle; return retval; }
void CMpqDataStream::Open(CString filename) { SFileOpenFileEx(m_hMpq, filename.c_str(), SFILE_OPEN_FROM_MPQ, &m_hFile); if (m_hFile == NULL) { // do something ... } m_nSize = SFileGetFileSize(m_hFile, 0); m_pByte = new byte[m_nSize]; Read(m_pByte, m_nSize); }
uint32 ReadBuild(int locale) { // include build info file also std::string filename = std::string("component.wow-")+langs[locale]+".txt"; //printf("Read %s file... ", filename.c_str()); HANDLE fileHandle; if (!OpenNewestFile(filename.c_str(), &fileHandle)) { printf("Fatal error: Not found %s file!\n", filename.c_str()); exit(1); } unsigned int data_size = SFileGetFileSize(fileHandle); std::string text; text.resize(data_size); if (!SFileReadFile(fileHandle, &text[0], data_size)) { printf("Fatal error: Can't read %s file!\n", filename.c_str()); exit(1); } SFileCloseFile(fileHandle); 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); } if (build < MIN_SUPPORTED_BUILD) { printf("Fatal error: tool can correctly extract data only for build %u or later (detected: %u)!\n", MIN_SUPPORTED_BUILD, build); exit(1); } return build; }
size_t FAsize(FAFile* stream) { switch(stream->mode) { case FAFile::PlainFile: return bfs::file_size(*(stream->data.plainFile.filename)); case FAFile::MPQFile: return SFileGetFileSize(*((HANDLE*)stream->data.mpqFile), NULL); } return 0; }
static TListFileCache * CreateListFileCache(HANDLE hListFile, const char * szMask) { TListFileCache * pCache = NULL; size_t nMaskLength = 0; DWORD dwBytesRead = 0; DWORD dwFileSize; // Get the amount of bytes that need to be allocated dwFileSize = SFileGetFileSize(hListFile, NULL); if(dwFileSize == 0) return NULL; // Append buffer for name mask, if any if(szMask != NULL) nMaskLength = strlen(szMask) + 1; // Allocate cache for one file block pCache = (TListFileCache *)STORM_ALLOC(BYTE, sizeof(TListFileCache)); if(pCache != NULL) { // Clear the entire structure memset(pCache, 0, sizeof(TListFileCache) + nMaskLength); // Shall we copy the mask? if(szMask != NULL) { pCache->szMask = (char *)(pCache + 1); memcpy(pCache->szMask, szMask, nMaskLength); } // Load the file cache from the file SFileReadFile(hListFile, pCache->Buffer, CACHE_BUFFER_SIZE, &dwBytesRead, NULL); if(dwBytesRead != 0) { // Allocate pointers pCache->pBegin = pCache->pPos = &pCache->Buffer[0]; pCache->pEnd = pCache->pBegin + dwBytesRead; pCache->dwFileSize = dwFileSize; pCache->hFile = hListFile; } else { FreeListFileCache(pCache); pCache = NULL; } } // Return the cache return pCache; }
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; }
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; }
size_t FAsize(FAFile* stream) { switch(stream->mode) { case FAFile::PlainFile: return static_cast<size_t> (bfs::file_size(*(stream->data.plainFile.filename))); case FAFile::MPQFile: { std::lock_guard<std::mutex> lock(m); return SFileGetFileSize(*((HANDLE*)stream->data.mpqFile), NULL); } } return 0; }
MPQFile::MPQFile(HANDLE mpq, const char* filename, bool warnNoExist /*= true*/) : eof(false), buffer(0), pointer(0), size(0) { HANDLE file; if (!SFileOpenFileEx(mpq, filename, SFILE_OPEN_PATCHED_FILE, &file)) { if (warnNoExist || GetLastError() != ERROR_FILE_NOT_FOUND) fprintf(stderr, "Can't open %s, err=%u!\n", filename, GetLastError()); eof = true; return; } DWORD hi = 0; size = SFileGetFileSize(file, &hi); if (hi) { fprintf(stderr, "Can't open %s, size[hi] = %u!\n", filename, uint32(hi)); SFileCloseFile(file); eof = true; return; } if (size <= 1) { fprintf(stderr, "Can't open %s, size = %u!\n", filename, uint32(size)); SFileCloseFile(file); eof = true; return; } DWORD read = 0; buffer = new char[size]; if (!SFileReadFile(file, buffer, size, &read) || size != read) { fprintf(stderr, "Can't read %s, size=%u read=%u!\n", filename, uint32(size), uint32(read)); SFileCloseFile(file); eof = true; return; } SFileCloseFile(file); }
bool FileLoader::loadFile(char *filename, bool log) { free(); HANDLE fileHandle = 0; if (!OpenNewestFile(filename, &fileHandle)) { if (log) printf("No such file %s\n", filename); return false; } data_size = SFileGetFileSize(fileHandle, NULL); data = new uint8 [data_size]; if (!data) { SFileCloseFile(fileHandle); return false; } if (!SFileReadFile(fileHandle, data, data_size, NULL, NULL)) { if (log) printf("Can't read file %s\n", filename); SFileCloseFile(fileHandle); return false; } SFileCloseFile(fileHandle); // ToDo: Fix WDT errors... if (!prepareLoadedData()) { //printf("Error loading %s\n\n", filename); //free(); return true; } return true; }
//------------------------------------------ GET MAP HASH -------------------------------------------------- std::string Map::getMapHash() { unsigned char hash[20]; char hexstring[42]; std::string filename = Map::getPathName(); // Open File HANDLE hFile = NULL; if ( !SFileOpenFileEx(NULL, filename.c_str(), SFILE_FROM_ABSOLUTE, &hFile) || !hFile) { char szPath[MAX_PATH]; SStrCopy(szPath, filename.c_str(), MAX_PATH); SStrNCat(szPath, "\\staredit\\scenario.chk", MAX_PATH); if ( !SFileOpenFileEx(NULL, szPath, SFILE_FROM_MPQ, &hFile) || !hFile) return std::string("Error_map_cannot_be_opened"); } // Obtain file size DWORD dwFileSize = SFileGetFileSize(hFile, 0); // Allocate memory void *pBuffer = SMAlloc(dwFileSize); if ( !pBuffer ) { SFileCloseFile(hFile); return std::string("Error_could_not_allocate_memory"); } // Read file DWORD dwBytesRead = 0; SFileReadFile(hFile, pBuffer, dwFileSize, &dwBytesRead, 0); // Calculate hash sha1::calc(pBuffer, dwBytesRead, hash); sha1::toHexString(hash, hexstring); // Free memory and return SMFree(pBuffer); SFileCloseFile(hFile); return string(hexstring); }
ULONG64 CMPQFileAccessor::GetSize() { if(m_hFile) { ULONG64_CONVERTER FileSize; FileSize.LowPart=SFileGetFileSize(m_hFile,&FileSize.HighPart); if(FileSize.LowPart==0xFFFFFFFF) { if(GetLastError()==NO_ERROR) return FileSize.QuadPart; else return 0; } else { return FileSize.QuadPart; } } else if(m_pRealFSFile) { return m_pRealFSFile->GetSize(); } return 0; }
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; }
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]; }
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; }
DWORD WINAPI SFileSetFilePointer(HANDLE hFile, LONG lFilePos, LONG * plFilePosHigh, DWORD dwMoveMethod) { TMPQFile * hf = (TMPQFile *)hFile; ULONGLONG FilePosition; ULONGLONG MoveOffset; DWORD dwFilePosHi; // If the hFile is not a valid file handle, return an error. if(!IsValidFileHandle(hf)) { SetLastError(ERROR_INVALID_HANDLE); return SFILE_INVALID_POS; } // Get the relative point where to move from switch(dwMoveMethod) { case FILE_BEGIN: FilePosition = 0; break; case FILE_CURRENT: if(hf->pStream != NULL) { FileStream_GetPos(hf->pStream, FilePosition); } else { FilePosition = hf->dwFilePos; } break; case FILE_END: if(hf->pStream != NULL) { FileStream_GetSize(hf->pStream, FilePosition); } else { FilePosition = SFileGetFileSize(hFile, NULL); } break; default: SetLastError(ERROR_INVALID_PARAMETER); return SFILE_INVALID_POS; } // Now get the move offset. Note that both values form // a signed 64-bit value (a file pointer can be moved backwards) if(plFilePosHigh != NULL) dwFilePosHi = *plFilePosHigh; else dwFilePosHi = (lFilePos & 0x80000000) ? 0xFFFFFFFF : 0; MoveOffset = MAKE_OFFSET64(dwFilePosHi, lFilePos); // Now calculate the new file pointer // Do not allow the file pointer to go before the begin of the file FilePosition += MoveOffset; if(FilePosition < 0) FilePosition = 0; // Now apply the file pointer to the file if(hf->pStream != NULL) { // Apply the new file position if(!FileStream_Read(hf->pStream, &FilePosition, NULL, 0)) return SFILE_INVALID_POS; // Return the new file position if(plFilePosHigh != NULL) *plFilePosHigh = (LONG)(FilePosition >> 32); return (DWORD)FilePosition; } else { // Files in MPQ can't be bigger than 4 GB. // We don't allow to go past 4 GB if(FilePosition >> 32)
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; }
//------------------------------------------------- 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; }
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; }
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