From cfe288a28efcb1687c5b7d60e6fe80d204f4f555 Mon Sep 17 00:00:00 2001 From: Gleb Mazovetskiy Date: Sun, 26 Jan 2020 13:40:12 +0000 Subject: [PATCH] mpqapi: Use instead of custom functions --- Source/mpqapi.cpp | 195 ++++++++++++++++++++++---------------------- SourceS/file_util.h | 43 +++++++++- 2 files changed, 140 insertions(+), 98 deletions(-) diff --git a/Source/mpqapi.cpp b/Source/mpqapi.cpp index c249f050c..c26a488ec 100644 --- a/Source/mpqapi.cpp +++ b/Source/mpqapi.cpp @@ -1,4 +1,8 @@ +#include +#include + #include "diablo.h" +#include "../SourceS/file_util.h" #include "../3rdParty/Storm/Source/storm.h" DEVILUTION_BEGIN_NAMESPACE @@ -13,7 +17,25 @@ _BLOCKENTRY *sgpBlockTbl; /* data */ -HANDLE sghArchive = INVALID_HANDLE_VALUE; +#define RETURN_IF_FAIL(obj, op, ...) \ + obj->op(__VA_ARGS__); \ + if (obj->fail()) { \ + SDL_Log("%s->%s(%s) failed in %s at %s:%d\n", #obj, #op, #__VA_ARGS__, __func__, __FILE__, __LINE__); \ + return FALSE; \ + } + +#define GOTO_IF_FAIL(label, obj, op, ...) \ + obj->op(__VA_ARGS__); \ + if (obj->fail()) { \ + SDL_Log("%s->%s(%s) failed in %s at %s:%d\n", #obj, #op, #__VA_ARGS__, __func__, __FILE__, __LINE__); \ + goto label; \ + } + +namespace { + +static std::fstream *archive = nullptr; + +} // namespace void mpqapi_remove_hash_entry(const char *pszName) { @@ -192,10 +214,10 @@ BOOL mpqapi_write_file_contents(const char *pszName, const BYTE *pbData, DWORD d pBlk->offset = mpqapi_find_free_block(dwLen + nNumberOfBytesToWrite, &pBlk->sizealloc); pBlk->sizefile = dwLen; pBlk->flags = 0x80000100; - if (SetFilePointer(sghArchive, pBlk->offset, NULL, FILE_BEGIN) == (DWORD)-1) - return FALSE; + RETURN_IF_FAIL(archive, seekp, pBlk->offset); j = 0; - destsize = 0; + const auto start_pos = archive->tellp(); + std::fstream::streampos end_pos; sectoroffsettable = NULL; while (dwLen != 0) { DWORD len; @@ -211,35 +233,23 @@ BOOL mpqapi_write_file_contents(const char *pszName, const BYTE *pbData, DWORD d nNumberOfBytesToWrite = 4 * num_bytes + 4; sectoroffsettable = (DWORD *)DiabloAllocPtr(nNumberOfBytesToWrite); memset(sectoroffsettable, 0, nNumberOfBytesToWrite); - if (!WriteFile(sghArchive, sectoroffsettable, nNumberOfBytesToWrite, &nNumberOfBytesToWrite, 0)) { - goto on_error; - } - destsize += nNumberOfBytesToWrite; - } - sectoroffsettable[j] = SwapLE32(destsize); - if (!WriteFile(sghArchive, mpq_buf, len, &len, NULL)) { - goto on_error; + GOTO_IF_FAIL(on_error, archive, write, reinterpret_cast(sectoroffsettable), nNumberOfBytesToWrite); } + sectoroffsettable[j] = SwapLE32(archive->tellp() - start_pos); + GOTO_IF_FAIL(on_error, archive, write, mpq_buf, len); j++; if (dwLen > 4096) dwLen -= 4096; else dwLen = 0; - destsize += len; } + end_pos = archive->tellp(); + destsize = end_pos - start_pos; sectoroffsettable[j] = SwapLE32(destsize); - if (SetFilePointer(sghArchive, -destsize, NULL, FILE_CURRENT) == (DWORD)-1) { - goto on_error; - } - - if (!WriteFile(sghArchive, sectoroffsettable, nNumberOfBytesToWrite, &nNumberOfBytesToWrite, 0)) { - goto on_error; - } - - if (SetFilePointer(sghArchive, destsize - nNumberOfBytesToWrite, NULL, FILE_CURRENT) == (DWORD)-1) { - goto on_error; - } + GOTO_IF_FAIL(on_error, archive, seekp, start_pos); + GOTO_IF_FAIL(on_error, archive, write, reinterpret_cast(sectoroffsettable), nNumberOfBytesToWrite); + GOTO_IF_FAIL(on_error, archive, seekp, end_pos); mem_free_dbg(sectoroffsettable); if (destsize < pBlk->sizealloc) { @@ -313,18 +323,28 @@ BOOL OpenMPQ(const char *pszArchive, DWORD dwChar) { DWORD dwFlagsAndAttributes; DWORD key; - DWORD dwTemp; _FILEHEADER fhdr; InitHash(); dwFlagsAndAttributes = gbMaxPlayers > 1 ? FILE_FLAG_WRITE_THROUGH : 0; - sghArchive = CreateFile(pszArchive, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, dwFlagsAndAttributes, NULL); - if (sghArchive == INVALID_HANDLE_VALUE) { - sghArchive = CreateFile(pszArchive, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, dwFlagsAndAttributes, NULL); - if (sghArchive == INVALID_HANDLE_VALUE) - return FALSE; - save_archive_modified = TRUE; + + if (archive != nullptr) { + delete archive; + archive = nullptr; } + const bool exists = FileExists(pszArchive); + if (exists) { + archive = new std::fstream(pszArchive, std::ios::in | std::ios::out | std::ios::binary); + } else { + archive = new std::fstream(pszArchive, std::ios::in | std::ios::out | std::ios::binary | std::ios::trunc); + } + if (archive->fail()) { + SDL_Log("Failed to OpenMPQ at %s\n", pszArchive); + delete archive; + archive = nullptr; + return FALSE; + } + save_archive_modified = !exists; if (sgpBlockTbl == NULL || sgpHashTbl == NULL) { memset(&fhdr, 0, sizeof(fhdr)); if (ParseMPQHeader(&fhdr, &sgdwMpqOffset) == FALSE) { @@ -333,20 +353,16 @@ BOOL OpenMPQ(const char *pszArchive, DWORD dwChar) sgpBlockTbl = (_BLOCKENTRY *)DiabloAllocPtr(0x8000); memset(sgpBlockTbl, 0, 0x8000); if (fhdr.blockcount) { - if (SetFilePointer(sghArchive, 104, NULL, FILE_BEGIN) == -1) - goto on_error; - if (!ReadFile(sghArchive, sgpBlockTbl, 0x8000, &dwTemp, NULL)) - goto on_error; + GOTO_IF_FAIL(on_error, archive, seekg, 104); + GOTO_IF_FAIL(on_error, archive, read, reinterpret_cast(sgpBlockTbl), 0x8000); key = Hash("(block table)", 3); Decrypt(sgpBlockTbl, 0x8000, key); } sgpHashTbl = (_HASHENTRY *)DiabloAllocPtr(0x8000); memset(sgpHashTbl, 255, 0x8000); if (fhdr.hashcount) { - if (SetFilePointer(sghArchive, 32872, NULL, FILE_BEGIN) == -1) - goto on_error; - if (!ReadFile(sghArchive, sgpHashTbl, 0x8000, &dwTemp, NULL)) - goto on_error; + GOTO_IF_FAIL(on_error, archive, seekg, 32872); + GOTO_IF_FAIL(on_error, archive, read, reinterpret_cast(sgpHashTbl), 0x8000); key = Hash("(hash table)", 3); Decrypt(sgpHashTbl, 0x8000, key); } @@ -358,7 +374,7 @@ on_error: return FALSE; } -static BOOL byteSwapHdr(_FILEHEADER *pHdr) +static void byteSwapHdr(_FILEHEADER *pHdr) { pHdr->signature = SDL_SwapLE32(pHdr->signature); pHdr->headersize = SDL_SwapLE32(pHdr->headersize); @@ -369,37 +385,31 @@ static BOOL byteSwapHdr(_FILEHEADER *pHdr) pHdr->blockoffset = SDL_SwapLE32(pHdr->blockoffset); pHdr->hashcount = SDL_SwapLE32(pHdr->hashcount); pHdr->blockcount = SDL_SwapLE32(pHdr->blockcount); - return false; } BOOL ParseMPQHeader(_FILEHEADER *pHdr, DWORD *pdwNextFileStart) { - DWORD size; - DWORD NumberOfBytesRead; - - size = GetFileSize(sghArchive, 0); + RETURN_IF_FAIL(archive, seekg, 0, std::ios::end); + const std::uint32_t size = archive->tellg(); *pdwNextFileStart = size; - if (size == -1 - || size < sizeof(*pHdr) - || !ReadFile(sghArchive, pHdr, sizeof(*pHdr), &NumberOfBytesRead, NULL) - || byteSwapHdr(pHdr) - || NumberOfBytesRead != 104 - || pHdr->signature != '\x1AQPM' - || pHdr->headersize != 32 - || pHdr->version > 0 - || pHdr->sectorsizeid != 3 - || pHdr->filesize != size - || pHdr->hashoffset != 32872 - || pHdr->blockoffset != 104 - || pHdr->hashcount != 2048 - || pHdr->blockcount != 2048) { - - if (SetFilePointer(sghArchive, 0, NULL, FILE_BEGIN) == -1) - return FALSE; - if (!SetEndOfFile(sghArchive)) - return FALSE; - + bool ok = size >= sizeof(*pHdr); + if (ok) { + RETURN_IF_FAIL(archive, seekg, 0); + RETURN_IF_FAIL(archive, read, reinterpret_cast(pHdr), sizeof(*pHdr)); + byteSwapHdr(pHdr); + } + ok = ok && pHdr->signature == '\x1AQPM' + && pHdr->headersize == 32 + && pHdr->version <= 0 + && pHdr->sectorsizeid == 3 + && pHdr->filesize == size + && pHdr->hashoffset == 32872 + && pHdr->blockoffset == 104 + && pHdr->hashcount == 2048 + && pHdr->blockcount == 2048; + if (!ok) { + RETURN_IF_FAIL(archive, seekg, 0, std::ios::end); memset(pHdr, 0, sizeof(*pHdr)); pHdr->signature = '\x1AQPM'; pHdr->headersize = 32; @@ -418,9 +428,9 @@ void CloseMPQ(const char *pszArchive, BOOL bFree, DWORD dwChar) MemFreeDbg(sgpBlockTbl); MemFreeDbg(sgpHashTbl); } - if (sghArchive != INVALID_HANDLE_VALUE) { - CloseHandle(sghArchive); - sghArchive = INVALID_HANDLE_VALUE; + if (archive != nullptr) { + delete archive; + archive = nullptr; } save_archive_modified = FALSE; } @@ -428,7 +438,7 @@ void CloseMPQ(const char *pszArchive, BOOL bFree, DWORD dwChar) BOOL mpqapi_flush_and_close(const char *pszArchive, BOOL bFree, DWORD dwChar) { BOOL ret = FALSE; - if (sghArchive == INVALID_HANDLE_VALUE) + if (archive == nullptr) ret = TRUE; else { ret = FALSE; @@ -442,18 +452,19 @@ BOOL mpqapi_flush_and_close(const char *pszArchive, BOOL bFree, DWORD dwChar) } } CloseMPQ(pszArchive, bFree, dwChar); + if (ret) + ret = ResizeFile(pszArchive, sgdwMpqOffset); return ret; } BOOL WriteMPQHeader() { _FILEHEADER fhdr; - DWORD NumberOfBytesWritten; memset(&fhdr, 0, sizeof(fhdr)); fhdr.signature = SDL_SwapLE32('\x1AQPM'); fhdr.headersize = SDL_SwapLE32(32); - fhdr.filesize = SDL_SwapLE32(GetFileSize(sghArchive, 0)); + fhdr.filesize = SDL_SwapLE32(sgdwMpqOffset); fhdr.version = SDL_SwapLE16(0); fhdr.sectorsizeid = SDL_SwapLE16(3); fhdr.hashoffset = SDL_SwapLE32(32872); @@ -461,47 +472,39 @@ BOOL WriteMPQHeader() fhdr.hashcount = SDL_SwapLE32(2048); fhdr.blockcount = SDL_SwapLE32(2048); - if (SetFilePointer(sghArchive, 0, NULL, FILE_BEGIN) == -1) - return 0; - if (!WriteFile(sghArchive, &fhdr, sizeof(fhdr), &NumberOfBytesWritten, 0)) - return 0; - - return NumberOfBytesWritten == 104; + RETURN_IF_FAIL(archive, seekp, 0); + RETURN_IF_FAIL(archive, write, reinterpret_cast(&fhdr), sizeof(fhdr)); + return TRUE; } BOOL mpqapi_write_block_table() { - BOOL success; - DWORD NumberOfBytesWritten; - - if (SetFilePointer(sghArchive, 104, NULL, FILE_BEGIN) == -1) - return FALSE; - + RETURN_IF_FAIL(archive, seekp, 104); Encrypt(sgpBlockTbl, 0x8000, Hash("(block table)", 3)); - success = WriteFile(sghArchive, sgpBlockTbl, 0x8000, &NumberOfBytesWritten, 0); + const BOOL success = [=]() { + RETURN_IF_FAIL(archive, write, reinterpret_cast(sgpBlockTbl), 0x8000); + return TRUE; + }(); Decrypt(sgpBlockTbl, 0x8000, Hash("(block table)", 3)); - return success && NumberOfBytesWritten == 0x8000; + return success; } BOOL mpqapi_write_hash_table() { - BOOL success; - DWORD NumberOfBytesWritten; - - if (SetFilePointer(sghArchive, 32872, NULL, FILE_BEGIN) == -1) - return FALSE; - + RETURN_IF_FAIL(archive, seekp, 32872); Encrypt(sgpHashTbl, 0x8000, Hash("(hash table)", 3)); - success = WriteFile(sghArchive, sgpHashTbl, 0x8000, &NumberOfBytesWritten, 0); + const BOOL success = [=]() { + RETURN_IF_FAIL(archive, write, reinterpret_cast(sgpHashTbl), 0x8000); + return TRUE; + }(); Decrypt(sgpHashTbl, 0x8000, Hash("(hash table)", 3)); - return success && NumberOfBytesWritten == 0x8000; + return success; } BOOL mpqapi_can_seek() { - if (SetFilePointer(sghArchive, sgdwMpqOffset, NULL, FILE_BEGIN) == -1) - return FALSE; - return SetEndOfFile(sghArchive); + RETURN_IF_FAIL(archive, seekp, sgdwMpqOffset); + return TRUE; } DEVILUTION_END_NAMESPACE diff --git a/SourceS/file_util.h b/SourceS/file_util.h index 44ef9c940..c7dc9d3a9 100644 --- a/SourceS/file_util.h +++ b/SourceS/file_util.h @@ -1,6 +1,14 @@ #pragma once -#if _POSIX_C_SOURCE >= 200112L +#include + +#if defined(_WIN64) || defined(_WIN32) +// Suppress definitions of `min` and `max` macros by : +#define NOMINMAX 1 +#include +#endif + +#if _POSIX_C_SOURCE >= 200112L || defined(_BSD_SOURCE) || defined(__APPLE__) #include #else #include @@ -10,7 +18,7 @@ namespace dvl { inline bool FileExists(const char *path) { -#if _POSIX_C_SOURCE >= 200112L +#if _POSIX_C_SOURCE >= 200112L || defined(_BSD_SOURCE) || defined(__APPLE__) return ::access(path, F_OK) == 0; #else auto *file = std::fopen(path, "rb"); @@ -20,4 +28,35 @@ inline bool FileExists(const char *path) #endif } +inline bool ResizeFile(const char *path, std::uint32_t size) +{ +#if defined(_WIN64) || defined(_WIN32) + LARGE_INTEGER lisize; + lisize.QuadPart = static_cast(size); + if (lisize.QuadPart < 0) { + return false; + } + int path_utf16_size = MultiByteToWideChar(CP_UTF8, 0, path, -1, nullptr, 0); + auto path_utf16 = new wchar_t[path_utf16_size]; + if (MultiByteToWideChar(CP_UTF8, 0, path, -1, path_utf16, path_utf16_size) != path_utf16_size) { + delete[] path_utf16; + return false; + } + HANDLE file = ::CreateFileW(path_utf16, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); + delete[] path_utf16; + if (file == INVALID_HANDLE_VALUE) { + return false; + } else if (::SetFilePointerEx(file, lisize, NULL, FILE_BEGIN) == 0 || ::SetEndOfFile(file) == 0) { + ::CloseHandle(file); + return false; + } + ::CloseHandle(file); + return true; +#elif _POSIX_C_SOURCE >= 200112L || defined(_BSD_SOURCE) || defined(__APPLE__) + return ::truncate(path, static_cast(size)) == 0; +#else + static_assert(false, "truncate not implemented for the current platform"); +#endif +} + } // namespace dvl