mirror of
https://github.com/gwdevhub/gMod.git
synced 2026-07-15 15:09:30 +00:00
fix potentially wrong texture loading order (#20)
* add gsl library to track memory allocations * rework creation to respect mod order slight slowdown but just to be safe * 1.6.1 * print info on timing * specify largeaddressaware (shouldn't matter since gw is linked with it, but can't hurt)
This commit is contained in:
+4
-1
@@ -17,7 +17,7 @@ endif()
|
|||||||
|
|
||||||
set(VERSION_MAJOR 1)
|
set(VERSION_MAJOR 1)
|
||||||
set(VERSION_MINOR 6)
|
set(VERSION_MINOR 6)
|
||||||
set(VERSION_PATCH 0)
|
set(VERSION_PATCH 1)
|
||||||
set(VERSION_TWEAK 0)
|
set(VERSION_TWEAK 0)
|
||||||
|
|
||||||
set(VERSION_RC "${CMAKE_CURRENT_BINARY_DIR}/version.rc")
|
set(VERSION_RC "${CMAKE_CURRENT_BINARY_DIR}/version.rc")
|
||||||
@@ -35,6 +35,7 @@ list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake")
|
|||||||
include(libzippp)
|
include(libzippp)
|
||||||
include(minhook)
|
include(minhook)
|
||||||
include(dxtk)
|
include(dxtk)
|
||||||
|
include(msgsl)
|
||||||
|
|
||||||
add_library(gMod SHARED)
|
add_library(gMod SHARED)
|
||||||
|
|
||||||
@@ -58,9 +59,11 @@ target_link_libraries(gMod PRIVATE
|
|||||||
psapi
|
psapi
|
||||||
minhook
|
minhook
|
||||||
directxtex
|
directxtex
|
||||||
|
Microsoft.GSL::GSL
|
||||||
)
|
)
|
||||||
target_link_options(gMod PRIVATE
|
target_link_options(gMod PRIVATE
|
||||||
"$<$<CONFIG:DEBUG>:/NODEFAULTLIB:LIBCMT>"
|
"$<$<CONFIG:DEBUG>:/NODEFAULTLIB:LIBCMT>"
|
||||||
|
"/LARGEADDRESSAWARE"
|
||||||
)
|
)
|
||||||
source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" FILES ${SOURCES})
|
source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" FILES ${SOURCES})
|
||||||
target_sources(gMod PRIVATE ${SOURCES})
|
target_sources(gMod PRIVATE ${SOURCES})
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.14)
|
||||||
|
|
||||||
|
include(FetchContent)
|
||||||
|
|
||||||
|
FetchContent_Declare(GSL
|
||||||
|
GIT_REPOSITORY "https://github.com/microsoft/GSL"
|
||||||
|
GIT_TAG "v4.0.0"
|
||||||
|
GIT_SHALLOW ON
|
||||||
|
)
|
||||||
|
|
||||||
|
FetchContent_MakeAvailable(GSL)
|
||||||
@@ -26,9 +26,21 @@ inline void Message(const char* format, ...)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void Info(const char* format, ...)
|
||||||
|
{
|
||||||
|
#ifdef _DEBUG
|
||||||
|
va_list args;
|
||||||
|
va_start(args, format);
|
||||||
|
vprintf(format, args);
|
||||||
|
va_end(args);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
inline void Warning(const char* format, ...)
|
inline void Warning(const char* format, ...)
|
||||||
{
|
{
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
|
static HANDLE hConsole = GetStdHandle(STD_ERROR_HANDLE);
|
||||||
|
[[maybe_unused]] static auto success = SetConsoleTextAttribute(hConsole, 6);
|
||||||
va_list args;
|
va_list args;
|
||||||
va_start(args, format);
|
va_start(args, format);
|
||||||
vfprintf(stderr, format, args);
|
vfprintf(stderr, format, args);
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
#include "Utils.h"
|
#include "Utils.h"
|
||||||
|
|
||||||
#include <d3d9.h>
|
#include <d3d9.h>
|
||||||
|
#include <gsl/gsl>
|
||||||
|
|
||||||
#include "Defines.h"
|
#include "Defines.h"
|
||||||
#include "Error.h"
|
#include "Error.h"
|
||||||
|
|||||||
+36
-30
@@ -27,9 +27,6 @@ public:
|
|||||||
int MergeUpdate(); //called from uMod_IDirect3DDevice9::BeginScene()
|
int MergeUpdate(); //called from uMod_IDirect3DDevice9::BeginScene()
|
||||||
void Initialize();
|
void Initialize();
|
||||||
|
|
||||||
// Add TextureFileStruct data, return size of data added. 0 on failure.
|
|
||||||
unsigned long AddFile(TexEntry& entry, bool compress = false);
|
|
||||||
|
|
||||||
std::vector<uMod_IDirect3DTexture9*> OriginalTextures;
|
std::vector<uMod_IDirect3DTexture9*> OriginalTextures;
|
||||||
// stores the pointer to the uMod_IDirect3DTexture9 objects created by the game
|
// stores the pointer to the uMod_IDirect3DTexture9 objects created by the game
|
||||||
std::vector<uMod_IDirect3DVolumeTexture9*> OriginalVolumeTextures;
|
std::vector<uMod_IDirect3DVolumeTexture9*> OriginalVolumeTextures;
|
||||||
@@ -53,9 +50,8 @@ private:
|
|||||||
int LockMutex();
|
int LockMutex();
|
||||||
int UnlockMutex();
|
int UnlockMutex();
|
||||||
HANDLE hMutex;
|
HANDLE hMutex;
|
||||||
std::mutex mutex;
|
|
||||||
|
|
||||||
std::unordered_map<HashType, TextureFileStruct*> modded_textures;
|
std::unordered_map<HashType, gsl::owner<TextureFileStruct*>> modded_textures;
|
||||||
// array which stores the file in memory and the hash of each texture to be modded
|
// array which stores the file in memory and the hash of each texture to be modded
|
||||||
|
|
||||||
// called if a target texture is found
|
// called if a target texture is found
|
||||||
@@ -83,8 +79,8 @@ TextureClient::~TextureClient()
|
|||||||
if (hMutex != nullptr) {
|
if (hMutex != nullptr) {
|
||||||
CloseHandle(hMutex);
|
CloseHandle(hMutex);
|
||||||
}
|
}
|
||||||
for (const auto& it : modded_textures) {
|
for (const auto texture_file_struct : modded_textures | std::views::values) {
|
||||||
delete it.second;
|
delete texture_file_struct;
|
||||||
}
|
}
|
||||||
modded_textures.clear();
|
modded_textures.clear();
|
||||||
}
|
}
|
||||||
@@ -159,47 +155,44 @@ int TextureClient::UnlockMutex()
|
|||||||
return RETURN_OK;
|
return RETURN_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned long TextureClient::AddFile(TexEntry& entry, const bool compress)
|
gsl::owner<TextureFileStruct*> AddFile(TexEntry& entry, const bool compress, const std::filesystem::path& dll_path)
|
||||||
{
|
{
|
||||||
if (modded_textures.contains(entry.crc_hash)) {
|
const auto texture_file_struct = new TextureFileStruct();
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
auto texture_file_struct = new TextureFileStruct();
|
|
||||||
texture_file_struct->crc_hash = entry.crc_hash;
|
texture_file_struct->crc_hash = entry.crc_hash;
|
||||||
should_update = true;
|
|
||||||
const auto dds_blob = TextureFunction::ConvertToCompressedDDS(entry, compress, dll_path);
|
const auto dds_blob = TextureFunction::ConvertToCompressedDDS(entry, compress, dll_path);
|
||||||
texture_file_struct->data.assign(static_cast<BYTE*>(dds_blob.GetBufferPointer()), static_cast<BYTE*>(dds_blob.GetBufferPointer()) + dds_blob.GetBufferSize());
|
texture_file_struct->data.assign(static_cast<BYTE*>(dds_blob.GetBufferPointer()), static_cast<BYTE*>(dds_blob.GetBufferPointer()) + dds_blob.GetBufferSize());
|
||||||
std::lock_guard lock(mutex);
|
return texture_file_struct;
|
||||||
modded_textures.emplace(entry.crc_hash, texture_file_struct);
|
|
||||||
return texture_file_struct->data.size();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned ProcessModfile(TextureClient& client, const std::string& modfile, const bool compress)
|
std::vector<gsl::owner<TextureFileStruct*>> ProcessModfile(const std::string& modfile, const std::filesystem::path& dll_path, const bool compress)
|
||||||
{
|
{
|
||||||
const auto hr = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
|
const auto hr = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
|
||||||
if (FAILED(hr)) return 0;
|
if (FAILED(hr)) return {};
|
||||||
Message("Initialize: loading file %s... ", modfile.c_str());
|
Message("Initialize: loading file %s... ", modfile.c_str());
|
||||||
auto file_loader = ModfileLoader(modfile);
|
auto file_loader = ModfileLoader(modfile);
|
||||||
auto entries = file_loader.GetContents();
|
auto entries = file_loader.GetContents();
|
||||||
if (entries.empty()) {
|
if (entries.empty()) {
|
||||||
Message("No entries found.\n");
|
Message("No entries found.\n");
|
||||||
return 0;
|
return {};
|
||||||
}
|
}
|
||||||
Message("%zu textures... ", entries.size());
|
Message("%zu textures... ", entries.size());
|
||||||
unsigned long file_bytes_loaded = 0;
|
std::vector<gsl::owner<TextureFileStruct*>> texture_file_structs;
|
||||||
|
texture_file_structs.reserve(entries.size());
|
||||||
|
unsigned file_bytes_loaded = 0;
|
||||||
for (auto& tpf_entry : entries) {
|
for (auto& tpf_entry : entries) {
|
||||||
file_bytes_loaded += client.AddFile(tpf_entry, compress);
|
const auto tex_file_struct = AddFile(tpf_entry, compress, dll_path);
|
||||||
|
texture_file_structs.push_back(tex_file_struct);
|
||||||
|
file_bytes_loaded += texture_file_structs.back()->data.size();
|
||||||
}
|
}
|
||||||
entries.clear();
|
entries.clear();
|
||||||
Message("%d bytes loaded.\n", file_bytes_loaded);
|
Message("%d bytes loaded.\n", file_bytes_loaded);
|
||||||
CoUninitialize();
|
CoUninitialize();
|
||||||
return file_bytes_loaded;
|
return texture_file_structs;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextureClient::LoadModsFromFile(const char* source)
|
void TextureClient::LoadModsFromFile(const char* source)
|
||||||
{
|
{
|
||||||
static std::vector<std::string> loaded_modfiles{};
|
static std::vector<std::string> loaded_modfiles{};
|
||||||
static unsigned long loaded_size = 0;
|
|
||||||
Message("Initialize: searching in %s\n", source);
|
Message("Initialize: searching in %s\n", source);
|
||||||
|
|
||||||
std::ifstream file(source);
|
std::ifstream file(source);
|
||||||
@@ -226,20 +219,32 @@ void TextureClient::LoadModsFromFile(const char* source)
|
|||||||
files_size += std::filesystem::file_size(modfile);
|
files_size += std::filesystem::file_size(modfile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
std::vector<std::future<unsigned>> futures;
|
std::vector<std::future<std::vector<gsl::owner<TextureFileStruct*>>>> futures;
|
||||||
for (const auto modfile : modfiles) {
|
for (const auto modfile : modfiles) {
|
||||||
futures.emplace_back(std::async(std::launch::async, ProcessModfile, std::ref(*this), modfile, files_size > 400'000'000));
|
futures.emplace_back(std::async(std::launch::async, ProcessModfile, modfile, dll_path, files_size > 400'000'000));
|
||||||
}
|
}
|
||||||
// Join all threads
|
auto loaded_size = 0u;
|
||||||
for (auto& future : futures) {
|
for (auto& future : futures) {
|
||||||
loaded_size += future.get();
|
const auto texture_file_structs = future.get();
|
||||||
|
for (const auto texture_file_struct : texture_file_structs) {
|
||||||
|
if (!texture_file_struct->crc_hash) continue;
|
||||||
|
if (!modded_textures.contains(texture_file_struct->crc_hash)) {
|
||||||
|
modded_textures.emplace(texture_file_struct->crc_hash, texture_file_struct);
|
||||||
|
loaded_size += texture_file_struct->data.size();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
delete texture_file_struct;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
should_update = true;
|
||||||
}
|
}
|
||||||
Message("Finished loading mods from %s: Loaded %u bytes (%u mb)", source, loaded_size, loaded_size / 1024 / 1024);
|
Message("Finished loading mods from %s: Loaded %u bytes (%u mb)", source, loaded_size, loaded_size / 1024 / 1024);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextureClient::Initialize()
|
void TextureClient::Initialize()
|
||||||
{
|
{
|
||||||
Message("Initialize: begin\n");
|
const auto t1 = std::chrono::high_resolution_clock::now();
|
||||||
|
Info("Initialize: begin\n");
|
||||||
Message("Initialize: searching for modlist.txt\n");
|
Message("Initialize: searching for modlist.txt\n");
|
||||||
char gwpath[MAX_PATH]{};
|
char gwpath[MAX_PATH]{};
|
||||||
GetModuleFileName(GetModuleHandle(nullptr), gwpath, MAX_PATH); //ask for name and path of this executable
|
GetModuleFileName(GetModuleHandle(nullptr), gwpath, MAX_PATH); //ask for name and path of this executable
|
||||||
@@ -254,8 +259,9 @@ void TextureClient::Initialize()
|
|||||||
LoadModsFromFile(modlist.string().c_str());
|
LoadModsFromFile(modlist.string().c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const auto t2 = std::chrono::high_resolution_clock::now();
|
||||||
Message("Initialize: end\n");
|
const auto ms = duration_cast<std::chrono::milliseconds>(t2 - t1);
|
||||||
|
Info("Initialize: end, took %d ms\n", ms);
|
||||||
}
|
}
|
||||||
|
|
||||||
int TextureClient::AddTexture(uModTexturePtr auto pTexture)
|
int TextureClient::AddTexture(uModTexturePtr auto pTexture)
|
||||||
|
|||||||
Reference in New Issue
Block a user