#include "diablo.h" #include "../3rdParty/Storm/Source/storm.h" int sgdwMpqOffset; // idb char mpq_buf[4096]; _HASHENTRY *sgpHashTbl; _bool save_archive_modified; // weak _BLOCKENTRY *sgpBlockTbl; _bool save_archive_open; // weak //note: 32872 = 32768 + 104 (sizeof(_FILEHEADER)) /* data */ HANDLE sghArchive = (HANDLE)0xFFFFFFFF; // idb _bool mpqapi_set_hidden(char *pszArchive, _bool hidden) { char *v2; // edi BOOL v3; // esi DWORD v4; // eax _bool result; // al DWORD v6; // esi v2 = pszArchive; v3 = hidden; v4 = GetFileAttributes(pszArchive); if ( v4 == -1 ) return GetLastError() == ERROR_FILE_NOT_FOUND; v6 = v3 != 0 ? FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_HIDDEN : 0; if ( v4 == v6 ) result = 1; else result = SetFileAttributes(v2, v6); return result; } void mpqapi_store_creation_time(char *pszArchive, int dwChar) { int v2; // esi char *v3; // ebx HANDLE v4; // eax int v5; // esi struct _WIN32_FIND_DATAA FindFileData; // [esp+8h] [ebp-1E0h] char dst[160]; // [esp+148h] [ebp-A0h] v2 = dwChar; v3 = pszArchive; if ( gbMaxPlayers != 1 ) { mpqapi_reg_load_modification_time(dst, 160); v4 = FindFirstFile(v3, &FindFileData); if ( v4 != (HANDLE)-1 ) { FindClose(v4); v5 = 16 * v2; *(_DWORD *)&dst[v5] = FindFileData.ftCreationTime.dwLowDateTime; *(_DWORD *)&dst[v5 + 4] = FindFileData.ftCreationTime.dwHighDateTime; mpqapi_reg_store_modification_time(dst, 160); } } } _bool mpqapi_reg_load_modification_time(char *dst, int size) { unsigned int v2; // esi char *v3; // edi unsigned int v6; // esi char *v7; // ecx int nbytes_read; // [esp+8h] [ebp-4h] v2 = size; v3 = dst; memset(dst, 0, size); if ( !SRegLoadData("Diablo", "Video Player ", 0, (unsigned char *)v3, v2, (unsigned long *)&nbytes_read) || nbytes_read != v2 ) return 0; if ( v2 >= 8 ) { v6 = v2 >> 3; do { v7 = v3; v3 += 8; mpqapi_xor_buf(v7); --v6; } while ( v6 ); } return 1; } void mpqapi_xor_buf(char *pbData) { signed int v1; // eax char *v2; // esi signed int v3; // edi v1 = 0xF0761AB; v2 = pbData; v3 = 8; do { *v2 ^= v1; ++v2; v1 = _rotl(v1, 1); --v3; } while ( v3 ); } void mpqapi_store_default_time(DWORD dwChar) { /* DWORD idx; char dst[160]; if(gbMaxPlayers == 1) { return; } /// ASSERT: assert(dwChar < MAX_CHARACTERS); idx = 16 * dwChar; mpqapi_reg_load_modification_time(dst, sizeof(dst)); *(_DWORD *)&dst[idx + 4] = 0x78341348; // dwHighDateTime mpqapi_reg_store_modification_time(dst, sizeof(dst)); */ } _bool mpqapi_reg_store_modification_time(char *pbData, int dwLen) { int v2; // ebx char *v3; // ebp char *v4; // edi unsigned int v5; // esi char *v6; // ecx v2 = dwLen; v3 = pbData; v4 = pbData; if ( (unsigned int)dwLen >= 8 ) { v5 = (unsigned int)dwLen >> 3; do { v6 = v4; v4 += 8; mpqapi_xor_buf(v6); --v5; } while ( v5 ); } return SRegSaveData("Diablo", "Video Player ", 0, (unsigned char *)v3, v2); } void mpqapi_remove_hash_entry(char *pszName) { int v1; // eax _HASHENTRY *v2; // ecx _BLOCKENTRY *v3; // eax int v4; // esi int v5; // edi v1 = mpqapi_get_hash_index_of_path(pszName); if ( v1 != -1 ) { v2 = &sgpHashTbl[v1]; v3 = &sgpBlockTbl[v2->block]; v2->block = -2; v4 = v3->offset; v5 = v3->sizealloc; memset(v3, 0, 0x10u); mpqapi_free_block(v4, v5); save_archive_modified = 1; } } void mpqapi_free_block(int block_offset, int block_size) { int v2; // esi int v3; // edi _BLOCKENTRY *v4; // eax signed int v5; // edx signed int v6; // ecx int v7; // ecx _bool v8; // zf _BLOCKENTRY *v9; // eax v2 = block_size; v3 = block_offset; LABEL_2: v4 = sgpBlockTbl; v5 = 2048; while ( 1 ) { v6 = v5--; if ( !v6 ) break; v7 = v4->offset; if ( v4->offset && !v4->flags && !v4->sizefile ) { if ( v7 + v4->sizealloc == v3 ) { v3 = v4->offset; LABEL_11: v2 += v4->sizealloc; memset(v4, 0, 0x10u); goto LABEL_2; } if ( v3 + v2 == v7 ) goto LABEL_11; } ++v4; } v8 = v3 + v2 == sgdwMpqOffset; if ( v3 + v2 > sgdwMpqOffset ) { app_fatal("MPQ free list error"); v8 = v3 + v2 == sgdwMpqOffset; } if ( v8 ) { sgdwMpqOffset = v3; } else { v9 = mpqapi_new_block(0); v9->offset = v3; v9->sizealloc = v2; v9->sizefile = 0; v9->flags = 0; } } _BLOCKENTRY *mpqapi_new_block(int *block_index) { _BLOCKENTRY *result; // eax unsigned int v2; // edx result = sgpBlockTbl; v2 = 0; while ( result->offset || result->sizealloc || result->flags || result->sizefile ) { ++v2; ++result; if ( v2 >= 0x800 ) { app_fatal("Out of free block entries"); return 0; } } if ( block_index ) *block_index = v2; return result; } int mpqapi_get_hash_index_of_path(char *pszName) // FetchHandle { char *v1; // esi int v2; // ST00_4 int v3; // edi short v4; // ax v1 = pszName; v2 = Hash(pszName, 2); // MPQ_HASH_NAME_B v3 = Hash(v1, 1); // MPQ_HASH_NAME_A v4 = Hash(v1, 0); // MPQ_HASH_TABLE_INDEX return mpqapi_get_hash_index(v4, v3, v2, 0); } int mpqapi_get_hash_index(short index, int hash_a, int hash_b, int locale) { int v4; // ecx signed int v5; // eax signed int v6; // edx _HASHENTRY *v7; // ecx int v8; // edi int v10; // [esp+Ch] [ebp-8h] int i; // [esp+10h] [ebp-4h] v4 = index & 0x7FF; v10 = hash_a; v5 = 2048; for ( i = v4; ; i = (i + 1) & 0x7FF ) { v7 = &sgpHashTbl[v4]; v8 = v7->block; if ( v8 == -1 ) return -1; v6 = v5--; if ( !v6 ) return -1; if ( v7->hashcheck[0] == v10 && v7->hashcheck[1] == hash_b && v7->lcid == locale && v8 != -2 ) break; v4 = (i + 1) & 0x7FF; } return i; } void mpqapi_remove_hash_entries(_bool (__stdcall *fnGetName)(int, char *)) { _bool (__stdcall *v1)(int, char *); // edi signed int v2; // esi int i; // eax int v4; // eax char v5[260]; // [esp+8h] [ebp-104h] v1 = fnGetName; v2 = 1; for ( i = fnGetName(0, v5); i; i = v1(v4, v5) ) { mpqapi_remove_hash_entry(v5); v4 = v2++; } } _bool mpqapi_write_file(char *pszName, char *pbData, int dwLen) { char *v3; // edi char *v4; // esi _BLOCKENTRY *v5; // eax v3 = pbData; v4 = pszName; save_archive_modified = 1; mpqapi_remove_hash_entry(pszName); v5 = mpqapi_add_file(v4, 0, 0); if ( mpqapi_write_file_contents(v4, v3, dwLen, v5) ) return 1; mpqapi_remove_hash_entry(v4); return 0; } _BLOCKENTRY *mpqapi_add_file(char *pszName, _BLOCKENTRY *pBlk, int block_index) { char *v3; // edi short v4; // si int v5; // ebx signed int v6; // edx int v7; // esi int v8; // ecx int v9; // esi int v11; // [esp+Ch] [ebp-8h] _BLOCKENTRY *v12; // [esp+10h] [ebp-4h] v12 = pBlk; v3 = pszName; v4 = Hash(pszName, 0); v5 = Hash(v3, 1); v11 = Hash(v3, 2); if ( mpqapi_get_hash_index(v4, v5, v11, 0) != -1 ) app_fatal("Hash collision between \"%s\" and existing file\n", v3); v6 = 2048; v7 = v4 & 0x7FF; while ( 1 ) { --v6; v8 = sgpHashTbl[v7].block; if ( v8 == -1 || v8 == -2 ) break; v7 = (v7 + 1) & 0x7FF; if ( !v6 ) { v6 = -1; break; } } if ( v6 < 0 ) app_fatal("Out of hash space"); if ( !v12 ) v12 = mpqapi_new_block(&block_index); v9 = v7; sgpHashTbl[v9].hashcheck[0] = v5; sgpHashTbl[v9].hashcheck[1] = v11; sgpHashTbl[v9].lcid = 0; sgpHashTbl[v9].block = block_index; return v12; } _bool mpqapi_write_file_contents(char *pszName, char *pbData, int dwLen, _BLOCKENTRY *pBlk) { char *v4; // esi char *v5; // eax unsigned int destsize; // ebx char *v7; // eax unsigned int v8; // esi _BLOCKENTRY *v9; // edi int v10; // eax signed int v11; // eax unsigned int v13; // eax unsigned int v14; // eax int v15; // ecx int size; // [esp+Ch] [ebp-10h] char *v17; // [esp+10h] [ebp-Ch] int v18; // [esp+14h] [ebp-8h] DWORD nNumberOfBytesToWrite; // [esp+18h] [ebp-4h] v4 = pszName; v17 = pbData; v5 = strchr(pszName, ':'); destsize = 0; while ( v5 ) { v4 = v5 + 1; v5 = strchr(v5 + 1, ':'); } while ( 1 ) { v7 = strchr(v4, '\\'); if ( !v7 ) break; v4 = v7 + 1; } Hash(v4, 3); v8 = dwLen; v9 = pBlk; size = 4 * ((unsigned int)(dwLen + 4095) >> 12) + 4; nNumberOfBytesToWrite = 4 * ((unsigned int)(dwLen + 4095) >> 12) + 4; v10 = mpqapi_find_free_block(size + dwLen, &pBlk->sizealloc); v9->offset = v10; v9->sizefile = v8; v9->flags = 0x80000100; if ( SetFilePointer(sghArchive, v10, NULL, FILE_BEGIN) == -1 ) return 0; pBlk = 0; v18 = 0; while ( v8 ) { v11 = 0; do mpq_buf[v11++] -= 86; while ( v11 < 4096 ); dwLen = v8; if ( v8 >= 0x1000 ) dwLen = 4096; memcpy(mpq_buf, v17, dwLen); v17 += dwLen; dwLen = PkwareCompress(mpq_buf, dwLen); if ( !v18 ) { nNumberOfBytesToWrite = size; pBlk = (_BLOCKENTRY *)DiabloAllocPtr(size); memset(pBlk, 0, nNumberOfBytesToWrite); if ( !WriteFile(sghArchive, pBlk, nNumberOfBytesToWrite, &nNumberOfBytesToWrite, 0) ) goto LABEL_25; destsize += nNumberOfBytesToWrite; } *(&pBlk->offset + v18) = destsize; if ( !WriteFile(sghArchive, mpq_buf, dwLen, (LPDWORD)&dwLen, 0) ) goto LABEL_25; ++v18; if ( v8 <= 0x1000 ) v8 = 0; else v8 -= 4096; destsize += dwLen; } *(&pBlk->offset + v18) = destsize; if ( SetFilePointer(sghArchive, -destsize, NULL, FILE_CURRENT) == -1 || !WriteFile(sghArchive, pBlk, nNumberOfBytesToWrite, &nNumberOfBytesToWrite, 0) || SetFilePointer(sghArchive, destsize - nNumberOfBytesToWrite, NULL, FILE_CURRENT) == -1 ) { LABEL_25: if ( pBlk ) mem_free_dbg(pBlk); return 0; } mem_free_dbg(pBlk); v13 = v9->sizealloc; if ( destsize < v13 ) { v14 = v13 - destsize; if ( v14 >= 0x400 ) { v15 = destsize + v9->offset; v9->sizealloc = destsize; mpqapi_free_block(v15, v14); } } return 1; } int mpqapi_find_free_block(int size, int *block_size) { _BLOCKENTRY *v2; // eax signed int v3; // esi int result; // eax int v5; // esi _bool v6; // zf v2 = sgpBlockTbl; v3 = 2048; while ( 1 ) { --v3; if ( v2->offset ) { if ( !v2->flags && !v2->sizefile && v2->sizealloc >= (unsigned int)size ) break; } ++v2; if ( !v3 ) { *block_size = size; result = sgdwMpqOffset; sgdwMpqOffset += size; return result; } } v5 = v2->offset; *block_size = size; v2->offset += size; v6 = v2->sizealloc == size; v2->sizealloc -= size; if ( v6 ) memset(v2, 0, 0x10u); return v5; } void mpqapi_rename(char *pszOld, char *pszNew) { char *v2; // esi int v3; // eax _HASHENTRY *v4; // eax int v5; // ST00_4 _BLOCKENTRY *v6; // edx v2 = pszNew; v3 = mpqapi_get_hash_index_of_path(pszOld); if ( v3 != -1 ) { v4 = &sgpHashTbl[v3]; v5 = v4->block; v6 = &sgpBlockTbl[v5]; v4->block = -2; mpqapi_add_file(v2, v6, v5); save_archive_modified = 1; } } _bool mpqapi_has_file(char *pszName) { return mpqapi_get_hash_index_of_path(pszName) != -1; } _bool mpqapi_open_archive(char *pszArchive, _bool hidden, int dwChar) // OpenMPQ { char *v3; // ebp BOOL v4; // esi DWORD v6; // edi int v8; // eax int v10; // eax char *lpFileName; // [esp+10h] [ebp-70h] DWORD dwTemp; // [esp+14h] [ebp-6Ch] _FILEHEADER fhdr; // [esp+18h] [ebp-68h] v3 = pszArchive; v4 = hidden; lpFileName = pszArchive; InitHash(); if ( !mpqapi_set_hidden(v3, v4) ) return 0; v6 = (unsigned char)gbMaxPlayers > 1u ? FILE_FLAG_WRITE_THROUGH : 0; save_archive_open = 0; sghArchive = CreateFile(v3, GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, v6, NULL); if ( sghArchive == (HANDLE)-1 ) { sghArchive = CreateFile(lpFileName, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, v6 | (v4 != 0 ? FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_HIDDEN : 0), NULL); if ( sghArchive == (HANDLE)-1 ) return 0; save_archive_open = 1; save_archive_modified = 1; } if ( !sgpBlockTbl || !sgpHashTbl ) { memset(&fhdr, 0, 0x68u); if ( !mpqapi_parse_archive_header(&fhdr, &sgdwMpqOffset) ) { LABEL_15: mpqapi_close_archive(lpFileName, 1, dwChar); return 0; } sgpBlockTbl = (_BLOCKENTRY *)DiabloAllocPtr(0x8000); memset(sgpBlockTbl, 0, 0x8000u); if ( fhdr.blockcount ) { if ( SetFilePointer(sghArchive, 104, NULL, FILE_BEGIN) == -1 || !ReadFile(sghArchive, sgpBlockTbl, 0x8000u, &dwTemp, NULL) ) { goto LABEL_15; } v8 = Hash("(block table)", 3); Decrypt(sgpBlockTbl, 0x8000, v8); } sgpHashTbl = (_HASHENTRY *)DiabloAllocPtr(0x8000); memset(sgpHashTbl, 255, 0x8000u); if ( fhdr.hashcount ) { if ( SetFilePointer(sghArchive, 32872, NULL, FILE_BEGIN) == -1 || !ReadFile(sghArchive, sgpHashTbl, 0x8000u, &dwTemp, NULL) ) { goto LABEL_15; } v10 = Hash("(hash table)", 3); Decrypt(sgpHashTbl, 0x8000, v10); } } return 1; } _bool mpqapi_parse_archive_header(_FILEHEADER *pHdr, int *pdwNextFileStart) // ParseMPQHeader { int *v2; // ebp _FILEHEADER *v3; // esi DWORD v4; // eax DWORD v5; // edi DWORD NumberOfBytesRead; // [esp+10h] [ebp-4h] v2 = pdwNextFileStart; v3 = pHdr; v4 = GetFileSize(sghArchive, 0); v5 = v4; *v2 = v4; if ( v4 == -1 || v4 < 0x68 || !ReadFile(sghArchive, v3, 0x68u, &NumberOfBytesRead, NULL) || NumberOfBytesRead != 104 || v3->signature != '\x1AQPM' || v3->headersize != 32 || v3->version > 0u || v3->sectorsizeid != 3 || v3->filesize != v5 || v3->hashoffset != 32872 || v3->blockoffset != 104 || v3->hashcount != 2048 || v3->blockcount != 2048 ) { if ( SetFilePointer(sghArchive, 0, NULL, FILE_BEGIN) == -1 || !SetEndOfFile(sghArchive) ) return 0; memset(v3, 0, 0x68u); v3->signature = '\x1AQPM'; v3->headersize = 32; v3->sectorsizeid = 3; v3->version = 0; *v2 = 0x10068; save_archive_modified = 1; save_archive_open = 1; } return 1; } void mpqapi_close_archive(char *pszArchive, _bool bFree, int dwChar) // CloseMPQ { char *v3; // esi _BLOCKENTRY *v4; // ecx _HASHENTRY *v5; // ecx v3 = pszArchive; if ( bFree ) { v4 = sgpBlockTbl; sgpBlockTbl = 0; mem_free_dbg(v4); v5 = sgpHashTbl; sgpHashTbl = 0; mem_free_dbg(v5); } if ( sghArchive != (HANDLE)-1 ) { CloseHandle(sghArchive); sghArchive = (HANDLE)-1; } if ( save_archive_modified ) { save_archive_modified = 0; mpqapi_store_modified_time(v3, dwChar); } if ( save_archive_open ) { save_archive_open = 0; mpqapi_store_creation_time(v3, dwChar); } } void mpqapi_store_modified_time(char *pszArchive, int dwChar) { int v2; // esi char *v3; // ebx HANDLE v4; // eax int v5; // esi struct _WIN32_FIND_DATAA FindFileData; // [esp+8h] [ebp-1E0h] char dst[160]; // [esp+148h] [ebp-A0h] v2 = dwChar; v3 = pszArchive; if ( gbMaxPlayers != 1 ) { mpqapi_reg_load_modification_time(dst, 160); v4 = FindFirstFile(v3, &FindFileData); if ( v4 != (HANDLE)-1 ) { FindClose(v4); v5 = 16 * v2; *(_DWORD *)&dst[v5 + 8] = FindFileData.ftLastWriteTime.dwLowDateTime; *(_DWORD *)&dst[v5 + 12] = FindFileData.ftLastWriteTime.dwHighDateTime; mpqapi_reg_store_modification_time(dst, 160); } } } void mpqapi_flush_and_close(char *pszArchive, _bool bFree, int dwChar) { if ( sghArchive != (HANDLE)-1 ) { if ( save_archive_modified ) { if ( mpqapi_can_seek() ) { if ( mpqapi_write_header() ) { if ( mpqapi_write_block_table() ) mpqapi_write_hash_table(); } } } } mpqapi_close_archive(pszArchive, bFree, dwChar); } _bool mpqapi_write_header() // WriteMPQHeader { _bool result; // al _FILEHEADER fhdr; // [esp+8h] [ebp-6Ch] DWORD NumberOfBytesWritten; // [esp+70h] [ebp-4h] memset(&fhdr, 0, 0x68u); fhdr.signature = '\x1AQPM'; fhdr.headersize = 32; fhdr.filesize = GetFileSize(sghArchive, 0); fhdr.version = 0; fhdr.sectorsizeid = 3; fhdr.hashoffset = 32872; fhdr.blockoffset = 104; fhdr.hashcount = 2048; fhdr.blockcount = 2048; if ( SetFilePointer(sghArchive, 0, NULL, FILE_BEGIN) != -1 && WriteFile(sghArchive, &fhdr, 0x68u, &NumberOfBytesWritten, 0) ) result = NumberOfBytesWritten == 104; else result = 0; return result; } _bool mpqapi_write_block_table() { int v1; // eax BOOL v2; // ebx int v3; // eax DWORD NumberOfBytesWritten; // [esp+4h] [ebp-4h] if ( SetFilePointer(sghArchive, 104, NULL, FILE_BEGIN) == -1 ) return 0; v1 = Hash("(block table)", 3); Encrypt(sgpBlockTbl, 0x8000, v1); v2 = WriteFile(sghArchive, sgpBlockTbl, 0x8000u, &NumberOfBytesWritten, 0); v3 = Hash("(block table)", 3); Decrypt(sgpBlockTbl, 0x8000, v3); return v2 && NumberOfBytesWritten == 0x8000; } _bool mpqapi_write_hash_table() { int v1; // eax BOOL v2; // ebx int v3; // eax DWORD NumberOfBytesWritten; // [esp+4h] [ebp-4h] if ( SetFilePointer(sghArchive, 32872, NULL, FILE_BEGIN) == -1 ) return 0; v1 = Hash("(hash table)", 3); Encrypt(sgpHashTbl, 0x8000, v1); v2 = WriteFile(sghArchive, sgpHashTbl, 0x8000u, &NumberOfBytesWritten, 0); v3 = Hash("(hash table)", 3); Decrypt(sgpHashTbl, 0x8000, v3); return v2 && NumberOfBytesWritten == 0x8000; } _bool mpqapi_can_seek() { _bool result; // al if ( SetFilePointer(sghArchive, sgdwMpqOffset, NULL, FILE_BEGIN) == -1 ) result = 0; else result = SetEndOfFile(sghArchive); return result; }