Browse Source

mpqapi: Use <fstream> instead of custom functions

pull/567/head
Gleb Mazovetskiy 6 years ago committed by Anders Jenbo
parent
commit
cfe288a28e
  1. 195
      Source/mpqapi.cpp
  2. 43
      SourceS/file_util.h

195
Source/mpqapi.cpp

@ -1,4 +1,8 @@
#include <cstdint>
#include <fstream>
#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<const char *>(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<const char *>(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<char *>(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<char *>(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<char *>(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<const char *>(&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<const char *>(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<const char *>(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

43
SourceS/file_util.h

@ -1,6 +1,14 @@
#pragma once
#if _POSIX_C_SOURCE >= 200112L
#include <cstdint>
#if defined(_WIN64) || defined(_WIN32)
// Suppress definitions of `min` and `max` macros by <windows.h>:
#define NOMINMAX 1
#include <windows.h>
#endif
#if _POSIX_C_SOURCE >= 200112L || defined(_BSD_SOURCE) || defined(__APPLE__)
#include <unistd.h>
#else
#include <cstdio>
@ -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<LONGLONG>(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<off_t>(size)) == 0;
#else
static_assert(false, "truncate not implemented for the current platform");
#endif
}
} // namespace dvl

Loading…
Cancel
Save