From a97ffd16403f0d71574465d57d47c8f00d12e828 Mon Sep 17 00:00:00 2001 From: Gleb Mazovetskiy Date: Sat, 23 Oct 2021 10:12:14 +0100 Subject: [PATCH] Load MPQ file overrides without StormLib Also logs the overrides in verbose mode. --- Source/diablo.cpp | 1 - Source/init.cpp | 2 +- Source/storm/storm.cpp | 50 ------------------------------- Source/storm/storm.h | 2 -- Source/storm/storm_sdl_rw.cpp | 56 +++++++++++++++++++++++++++++------ Source/utils/paths.cpp | 7 ++++- Source/utils/paths.h | 3 ++ 7 files changed, 57 insertions(+), 64 deletions(-) diff --git a/Source/diablo.cpp b/Source/diablo.cpp index c6f3c3640..d05fa873a 100644 --- a/Source/diablo.cpp +++ b/Source/diablo.cpp @@ -904,7 +904,6 @@ void DiabloInit() init_create_window(); was_window_init = true; - SFileEnableDirectAccess(true); init_archives(); was_archives_init = true; diff --git a/Source/init.cpp b/Source/init.cpp index 4dd545327..a218ce816 100644 --- a/Source/init.cpp +++ b/Source/init.cpp @@ -65,7 +65,7 @@ HANDLE LoadMPQ(const std::vector &paths, const char *mpqName) mpqAbsPath = path + mpqName; if (SFileOpenArchive(mpqAbsPath.c_str(), 0, MPQ_OPEN_READ_ONLY, &archive)) { LogVerbose(" Found: {} in {}", mpqName, path); - SFileSetBasePath(path); + paths::SetMpqDir(path); return archive; } if (SErrGetLastError() != STORM_ERROR_FILE_NOT_FOUND) { diff --git a/Source/storm/storm.cpp b/Source/storm/storm.cpp index 64a1be1c4..287fc993a 100644 --- a/Source/storm/storm.cpp +++ b/Source/storm/storm.cpp @@ -30,9 +30,6 @@ extern "C" std::uint32_t GetLastError(); namespace devilution { namespace { -bool directFileAccess = false; -std::optional SBasePath; - SdlMutex Mutex; } // namespace @@ -49,46 +46,10 @@ bool SFileCloseFileThreadSafe(HANDLE hFile) return SFileCloseFile(hFile); } -// Converts ASCII characters to lowercase -// Converts slash (0x2F) / backslash (0x5C) to system file-separator -unsigned char AsciiToLowerTable_Path[256] = { - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, -#ifdef _WIN32 - 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x5C, -#else - 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, -#endif - 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, - 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, -#ifdef _WIN32 - 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, -#else - 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x5B, 0x2F, 0x5D, 0x5E, 0x5F, -#endif - 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, - 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, - 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, - 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F, - 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, - 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF, - 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, - 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, - 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, - 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF -}; - bool SFileOpenFile(const char *filename, HANDLE *phFile) { bool result = false; - if (directFileAccess && SBasePath) { - std::string path = *SBasePath + filename; - for (std::size_t i = SBasePath->size(); i < path.size(); ++i) - path[i] = AsciiToLowerTable_Path[static_cast(path[i])]; - result = SFileOpenFileEx((HANDLE) nullptr, path.c_str(), SFILE_OPEN_LOCAL_FILE, phFile); - } - if (!result && font_mpq != nullptr) { result = SFileOpenFileEx((HANDLE)font_mpq, filename, SFILE_OPEN_FROM_MPQ, phFile); } @@ -138,17 +99,6 @@ void SErrSetLastError(uint32_t dwErrCode) ::SetLastError(dwErrCode); } -void SFileSetBasePath(string_view path) -{ - SBasePath.emplace(path); -} - -bool SFileEnableDirectAccess(bool enable) -{ - directFileAccess = enable; - return true; -} - #if defined(_WIN64) || defined(_WIN32) bool SFileOpenArchive(const char *szMpqName, DWORD dwPriority, DWORD dwFlags, HANDLE *phMpq) { diff --git a/Source/storm/storm.h b/Source/storm/storm.h index cd7bf4cfa..b8b0fca6a 100644 --- a/Source/storm/storm.h +++ b/Source/storm/storm.h @@ -249,14 +249,12 @@ void SErrSetLastError(uint32_t dwErrCode); */ void SStrCopy(char *dest, const char *src, int max_length); -void SFileSetBasePath(string_view path); bool SNetGetOwnerTurnsWaiting(uint32_t *); bool SNetUnregisterEventHandler(event_type); bool SNetRegisterEventHandler(event_type, SEVTHANDLER); bool SNetSetBasePlayer(int); bool SNetInitializeProvider(uint32_t provider, struct GameData *gameData); void SNetGetProviderCaps(struct _SNETCAPS *); -bool SFileEnableDirectAccess(bool enable); #if defined(__GNUC__) || defined(__cplusplus) } diff --git a/Source/storm/storm_sdl_rw.cpp b/Source/storm/storm_sdl_rw.cpp index f8ff5a25e..838570c24 100644 --- a/Source/storm/storm_sdl_rw.cpp +++ b/Source/storm/storm_sdl_rw.cpp @@ -6,6 +6,7 @@ #include "engine.h" #include "storm/storm.h" +#include "utils/file_util.h" #include "utils/log.hpp" #include "utils/paths.h" @@ -100,24 +101,61 @@ SDL_RWops *SFileRw_FromStormHandle(HANDLE handle) return result; } +#ifdef _WIN32 +constexpr char PathSeparator = '\\'; +#else +constexpr char PathSeparator = '/'; +#endif + +// Converts ASCII characters to lowercase +// Converts slash / backslash to system file-separator +const char LowerCaseAsciiPathTable[256] { + '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07', '\x08', '\x09', '\x0A', '\x0B', '\x0C', '\x0D', '\x0E', '\x0F', + '\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17', '\x18', '\x19', '\x1A', '\x1B', '\x1C', '\x1D', '\x1E', '\x1F', + '\x20', '\x21', '\x22', '\x23', '\x24', '\x25', '\x26', '\x27', '\x28', '\x29', '\x2A', '\x2B', '\x2C', '\x2D', '\x2E', PathSeparator, + '\x30', '\x31', '\x32', '\x33', '\x34', '\x35', '\x36', '\x37', '\x38', '\x39', '\x3A', '\x3B', '\x3C', '\x3D', '\x3E', '\x3F', + '\x40', '\x61', '\x62', '\x63', '\x64', '\x65', '\x66', '\x67', '\x68', '\x69', '\x6A', '\x6B', '\x6C', '\x6D', '\x6E', '\x6F', + '\x70', '\x71', '\x72', '\x73', '\x74', '\x75', '\x76', '\x77', '\x78', '\x79', '\x7A', '\x5B', PathSeparator, '\x5D', '\x5E', '\x5F', + '\x60', '\x61', '\x62', '\x63', '\x64', '\x65', '\x66', '\x67', '\x68', '\x69', '\x6A', '\x6B', '\x6C', '\x6D', '\x6E', '\x6F', + '\x70', '\x71', '\x72', '\x73', '\x74', '\x75', '\x76', '\x77', '\x78', '\x79', '\x7A', '\x7B', '\x7C', '\x7D', '\x7E', '\x7F', + '\x80', '\x81', '\x82', '\x83', '\x84', '\x85', '\x86', '\x87', '\x88', '\x89', '\x8A', '\x8B', '\x8C', '\x8D', '\x8E', '\x8F', + '\x90', '\x91', '\x92', '\x93', '\x94', '\x95', '\x96', '\x97', '\x98', '\x99', '\x9A', '\x9B', '\x9C', '\x9D', '\x9E', '\x9F', + '\xA0', '\xA1', '\xA2', '\xA3', '\xA4', '\xA5', '\xA6', '\xA7', '\xA8', '\xA9', '\xAA', '\xAB', '\xAC', '\xAD', '\xAE', '\xAF', + '\xB0', '\xB1', '\xB2', '\xB3', '\xB4', '\xB5', '\xB6', '\xB7', '\xB8', '\xB9', '\xBA', '\xBB', '\xBC', '\xBD', '\xBE', '\xBF', + '\xC0', '\xC1', '\xC2', '\xC3', '\xC4', '\xC5', '\xC6', '\xC7', '\xC8', '\xC9', '\xCA', '\xCB', '\xCC', '\xCD', '\xCE', '\xCF', + '\xD0', '\xD1', '\xD2', '\xD3', '\xD4', '\xD5', '\xD6', '\xD7', '\xD8', '\xD9', '\xDA', '\xDB', '\xDC', '\xDD', '\xDE', '\xDF', + '\xE0', '\xE1', '\xE2', '\xE3', '\xE4', '\xE5', '\xE6', '\xE7', '\xE8', '\xE9', '\xEA', '\xEB', '\xEC', '\xED', '\xEE', '\xEF', + '\xF0', '\xF1', '\xF2', '\xF3', '\xF4', '\xF5', '\xF6', '\xF7', '\xF8', '\xF9', '\xFA', '\xFB', '\xFC', '\xFD', '\xFE', '\xFF' +}; + } // namespace SDL_RWops *SFileOpenRw(const char *filename) { - HANDLE handle; - if (SFileOpenFile(filename, &handle)) - return SFileRw_FromStormHandle(handle); - std::string relativePath = filename; -#ifndef _WIN32 - std::replace(relativePath.begin(), relativePath.end(), '\\', '/'); -#endif + for (char &c : relativePath) + c = LowerCaseAsciiPathTable[static_cast(c)]; - SDL_RWops *rwops; - if (relativePath[0] == '/') { + if (relativePath[0] == '/') return SDL_RWFromFile(relativePath.c_str(), "rb"); + + // Files next to the MPQ archives override MPQ contents. + SDL_RWops *rwops; + if (paths::MpqDir()) { + const std::string path = *paths::MpqDir() + relativePath; + // Avoid spamming DEBUG messages if the file does not exist. + if ((FileExists(path.c_str())) && (rwops = SDL_RWFromFile(path.c_str(), "rb")) != nullptr) { + LogVerbose("Loaded MPQ file override: {}", path); + return rwops; + } } + // Load from all the MPQ archives. + HANDLE handle; + if (SFileOpenFile(filename, &handle)) + return SFileRw_FromStormHandle(handle); + + // Load from the `/assets` directory next to the devilutionx binary. const std::string path = paths::AssetsPath() + relativePath; if ((rwops = SDL_RWFromFile(path.c_str(), "rb")) != nullptr) return rwops; diff --git a/Source/utils/paths.cpp b/Source/utils/paths.cpp index d41fc5a5e..db8429c4e 100644 --- a/Source/utils/paths.cpp +++ b/Source/utils/paths.cpp @@ -4,7 +4,6 @@ #include "utils/file_util.h" #include "utils/log.hpp" -#include "utils/stdcompat/optional.hpp" #include "utils/sdl_ptrs.h" #ifdef USE_SDL1 @@ -22,6 +21,7 @@ std::optional basePath; std::optional prefPath; std::optional configPath; std::optional assetsPath; +std::optional mpqDir; void AddTrailingSlash(std::string &path) { @@ -92,6 +92,11 @@ const std::string &AssetsPath() return *assetsPath; } +const std::optional &MpqDir() +{ + return mpqDir; +} + void SetBasePath(const std::string &path) { basePath = path; diff --git a/Source/utils/paths.h b/Source/utils/paths.h index e43450e32..d20da7370 100644 --- a/Source/utils/paths.h +++ b/Source/utils/paths.h @@ -2,6 +2,8 @@ #include +#include "utils/stdcompat/optional.hpp" + namespace devilution { namespace paths { @@ -11,6 +13,7 @@ const std::string &BasePath(); const std::string &PrefPath(); const std::string &ConfigPath(); const std::string &AssetsPath(); +const std::optional &MpqDir(); void SetBasePath(const std::string &path); void SetPrefPath(const std::string &path);