From c7338f2f48f6d6c63c55d6e56a7f40af01d82360 Mon Sep 17 00:00:00 2001 From: Gleb Mazovetskiy Date: Fri, 9 Dec 2022 06:01:56 +0000 Subject: [PATCH] language.cpp: Load whole translations file if MPQ Fixes very slow translation loading from MPQs. Compressed MPQs files are very slow to seek in. If the RWops is and MPQ, load the whole file first. --- Source/utils/language.cpp | 67 ++++++++++++++++++++++------ Source/utils/sdl2_to_1_2_backports.h | 1 + 2 files changed, 55 insertions(+), 13 deletions(-) diff --git a/Source/utils/language.cpp b/Source/utils/language.cpp index 3e5286fa7..9aa154309 100644 --- a/Source/utils/language.cpp +++ b/Source/utils/language.cpp @@ -13,6 +13,10 @@ #include "utils/paths.h" #include "utils/stdcompat/string_view.hpp" +#ifdef USE_SDL1 +#include "utils/sdl2_to_1_2_backports.h" +#endif + #define MO_MAGIC 0x950412de namespace { @@ -264,6 +268,22 @@ bool ReadEntry(AssetHandle &handle, const MoEntry &e, char *result) return handle.read(result, e.length); } +bool CopyData(void *dst, const byte *data, size_t dataSize, size_t offset, size_t length) +{ + if (offset + length > dataSize) + return false; + memcpy(dst, data + offset, length); + return true; +} + +bool ReadEntry(const byte *data, size_t dataSize, const MoEntry &e, char *result) +{ + if (!CopyData(result, data, dataSize, e.offset, e.length)) + return false; + result[e.length] = '\0'; + return true; +} + } // namespace string_view LanguageParticularTranslate(string_view context, string_view message) @@ -346,12 +366,13 @@ void LanguageInitialize() const std::string lang(*sgOptions.Language.code); AssetHandle handle; - uint32_t loadTranslationsStart = SDL_GetTicks(); + const uint32_t loadTranslationsStart = SDL_GetTicks(); std::string translationsPath; + size_t fileSize; for (const char *ext : Extensions) { translationsPath = lang + ext; - handle = OpenAsset(translationsPath.c_str()); + handle = OpenAsset(translationsPath.c_str(), fileSize); if (handle.ok()) break; } @@ -360,9 +381,27 @@ void LanguageInitialize() return; } +#ifdef UNPACKED_MPQS + const bool readWholeFile = false; +#else + // If reading from an MPQ, it is much faster to + // load the whole file instead of seeking. + const bool readWholeFile = handle.handle->type == SDL_RWOPS_UNKNOWN; +#endif + + std::unique_ptr data; + if (readWholeFile) { + data.reset(new byte[fileSize]); + if (!handle.read(data.get(), fileSize)) + return; + handle = {}; + } + // Read header and do sanity checks MoHead head; - if (!handle.read(&head, sizeof(MoHead))) { + if (readWholeFile + ? !CopyData(&head, data.get(), fileSize, 0, sizeof(MoHead)) + : !handle.read(&head, sizeof(MoHead))) { return; } SwapLE(head); @@ -377,10 +416,9 @@ void LanguageInitialize() // Read entries of source strings std::unique_ptr src { new MoEntry[head.nbMappings] }; - if (!handle.seek(head.srcOffset)) { - return; - } - if (!handle.read(src.get(), head.nbMappings * sizeof(MoEntry))) { + if (readWholeFile + ? !CopyData(src.get(), data.get(), fileSize, head.srcOffset, head.nbMappings * sizeof(MoEntry)) + : !handle.seek(head.srcOffset) || !handle.read(src.get(), head.nbMappings * sizeof(MoEntry))) { return; } for (size_t i = 0; i < head.nbMappings; ++i) { @@ -389,10 +427,9 @@ void LanguageInitialize() // Read entries of target strings std::unique_ptr dst { new MoEntry[head.nbMappings] }; - if (!handle.seek(head.dstOffset)) { - return; - } - if (!handle.read(dst.get(), head.nbMappings * sizeof(MoEntry))) { + if (readWholeFile + ? !CopyData(dst.get(), data.get(), fileSize, head.dstOffset, head.nbMappings * sizeof(MoEntry)) + : !handle.seek(head.dstOffset) || !handle.read(dst.get(), head.nbMappings * sizeof(MoEntry))) { return; } for (size_t i = 0; i < head.nbMappings; ++i) { @@ -405,7 +442,9 @@ void LanguageInitialize() } { auto headerValue = std::unique_ptr { new char[dst[0].length + 1] }; - if (!ReadEntry(handle, dst[0], &headerValue[0])) { + if (readWholeFile + ? !ReadEntry(data.get(), fileSize, dst[0], &headerValue[0]) + : !ReadEntry(handle, dst[0], &headerValue[0])) { return; } ParseMetadata(&headerValue[0]); @@ -428,7 +467,9 @@ void LanguageInitialize() char *keyPtr = &translationKeys[0]; char *valuePtr = &translationValues[0]; for (uint32_t i = 1; i < head.nbMappings; i++) { - if (ReadEntry(handle, src[i], keyPtr) && ReadEntry(handle, dst[i], valuePtr)) { + if (readWholeFile + ? ReadEntry(data.get(), fileSize, src[i], keyPtr) && ReadEntry(data.get(), fileSize, dst[i], valuePtr) + : ReadEntry(handle, src[i], keyPtr) && ReadEntry(handle, dst[i], valuePtr)) { // Plural keys also have a plural form but it does not participate in lookup. // Plural values are \0-terminated. string_view value { valuePtr, dst[i].length + 1 }; diff --git a/Source/utils/sdl2_to_1_2_backports.h b/Source/utils/sdl2_to_1_2_backports.h index 0db4b2eec..93a5ac949 100644 --- a/Source/utils/sdl2_to_1_2_backports.h +++ b/Source/utils/sdl2_to_1_2_backports.h @@ -315,6 +315,7 @@ int SDL_BlitScaled(SDL_Surface *src, SDL_Rect *srcrect, SDL_Surface *dst, SDL_Rect *dstrect); //== Filesystem +#define SDL_RWOPS_UNKNOWN 0U Sint64 SDL_RWsize(SDL_RWops *context);