From 32dcb2a5b51d30877266935ef44c3c95a221133e Mon Sep 17 00:00:00 2001 From: Michael Oliver Date: Sun, 14 Jun 2026 10:06:25 +0100 Subject: [PATCH] fix(IW4-TU6): avoid large string copies for file overrides --- src/game/iw4/mp_tu6/components/mpsp.cpp | 45 ++++++++++++++----- src/game/iw4/mp_tu6/components/scr_parser.cpp | 31 +++++++++---- 2 files changed, 57 insertions(+), 19 deletions(-) diff --git a/src/game/iw4/mp_tu6/components/mpsp.cpp b/src/game/iw4/mp_tu6/components/mpsp.cpp index 814615c..bca5786 100644 --- a/src/game/iw4/mp_tu6/components/mpsp.cpp +++ b/src/game/iw4/mp_tu6/components/mpsp.cpp @@ -308,7 +308,7 @@ void override_(MapEnts *asset) namespace RawFile_ { -std::unordered_map> rawfile_buffers; +std::unordered_map> rawfile_buffers; void override_(RawFile *asset) { @@ -330,14 +330,40 @@ void override_(RawFile *asset) } const std::string filename = Asset::get_load_dir() + "\\" + asset->name; - const std::string buffer = FS::ReadTextFile(filename); + const std::string normalized = FS::NormalizePath(filename); + FILE *file = fopen(normalized.c_str(), "rb"); - if (buffer.empty()) + if (!file) + { + return; + } + + fseek(file, 0, SEEK_END); + const long file_size = ftell(file); + fseek(file, 0, SEEK_SET); + + if (file_size <= 0) + { + fclose(file); + return; + } + + std::unique_ptr buffer(new (std::nothrow) char[file_size + 1]); + if (!buffer) { + fclose(file); return; } - // NOTE: It seems that the engine will handle us giving back uncompressed strings (tested at least for .gsc files!) + const size_t bytes_read = fread(buffer.get(), 1, file_size, file); + fclose(file); + + if (bytes_read != static_cast(file_size)) + { + return; + } + + buffer[bytes_read] = '\0'; DbgPrint("Overriding rawfile asset %s from fastfile %s\n", asset->name, g_load->file->name); @@ -346,15 +372,12 @@ void override_(RawFile *asset) { rawfile_buffers.erase(itr); } - rawfile_buffers[asset->name] = make_unique(); - itr = rawfile_buffers.find(asset->name); - auto rawfile_buffer = itr->second.get(); - - rawfile_buffer->assign(buffer); + rawfile_buffers[asset->name] = std::move(buffer); + char *rawfile_buffer = rawfile_buffers[asset->name].get(); asset->compressedLen = 0; // Force the engine to treat it as an uncompressed buffer! - asset->len = rawfile_buffer->length(); - asset->buffer = rawfile_buffer->data(); + asset->len = static_cast(bytes_read); + asset->buffer = rawfile_buffer; } } // namespace RawFile_ diff --git a/src/game/iw4/mp_tu6/components/scr_parser.cpp b/src/game/iw4/mp_tu6/components/scr_parser.cpp index 4678caa..3e8f971 100644 --- a/src/game/iw4/mp_tu6/components/scr_parser.cpp +++ b/src/game/iw4/mp_tu6/components/scr_parser.cpp @@ -32,19 +32,34 @@ char *Scr_AddSourceBuffer_Hook(const char *filename, const char *extFilename) std::string overridePath = modBasePath + "\\" + extFilename; std::replace(overridePath.begin(), overridePath.end(), '/', '\\'); - // Try to load override file - std::string fileContent = filesystem::read_file_to_string(overridePath); - if (fileContent.empty()) + FILE *file = fopen(overridePath.c_str(), "rb"); + if (!file) return callOriginal(); - // Allocate buffer using game's memory allocator - char *buffer = (char *)Hunk_AllocateTempMemoryHighInternal(fileContent.size() + 1); + fseek(file, 0, SEEK_END); + const long fileSize = ftell(file); + fseek(file, 0, SEEK_SET); + + if (fileSize <= 0) + { + fclose(file); + return callOriginal(); + } + + char *buffer = (char *)Hunk_AllocateTempMemoryHighInternal(fileSize + 1); if (!buffer) + { + fclose(file); + return callOriginal(); + } + + const size_t bytesRead = fread(buffer, 1, fileSize, file); + fclose(file); + + if (bytesRead != static_cast(fileSize)) return callOriginal(); - // Copy content and null terminate - memcpy(buffer, fileContent.c_str(), fileContent.size()); - buffer[fileContent.size()] = '\0'; + buffer[bytesRead] = '\0'; DbgPrint("scr_parser: Loaded override script: %s\n", overridePath.c_str()); return buffer;