mirror of
https://github.com/gwdevhub/gMod.git
synced 2026-07-15 15:09:30 +00:00
make umod able to load and unload per dll export call
This commit is contained in:
+250
-54
@@ -5,12 +5,21 @@ module;
|
|||||||
#include "uMod_IDirect3DTexture9.h"
|
#include "uMod_IDirect3DTexture9.h"
|
||||||
#include <DDSTextureLoader/DDSTextureLoader9.h>
|
#include <DDSTextureLoader/DDSTextureLoader9.h>
|
||||||
#include <DirectXTex/DirectXTex.h>
|
#include <DirectXTex/DirectXTex.h>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
export module TextureClient;
|
export module TextureClient;
|
||||||
import TextureFunction;
|
import TextureFunction;
|
||||||
import ModfileLoader;
|
import ModfileLoader;
|
||||||
|
|
||||||
export std::vector<std::pair<std::string, std::string>> modlists_contents;
|
export std::vector<std::pair<std::string, std::string>> modlists_contents;
|
||||||
|
|
||||||
|
struct PendingOp {
|
||||||
|
enum class Kind { Add, Remove };
|
||||||
|
Kind kind;
|
||||||
|
HashType hash;
|
||||||
|
std::vector<BYTE> data;
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* An object of this class is owned by each d3d9 device.
|
* An object of this class is owned by each d3d9 device.
|
||||||
* All other functions are called from the render thread instance of the game itself.
|
* All other functions are called from the render thread instance of the game itself.
|
||||||
@@ -28,6 +37,13 @@ public:
|
|||||||
int MergeUpdate(); //called from uMod_IDirect3DDevice9::BeginScene()
|
int MergeUpdate(); //called from uMod_IDirect3DDevice9::BeginScene()
|
||||||
void Initialize();
|
void Initialize();
|
||||||
|
|
||||||
|
void EnqueueAdd(HashType hash, std::vector<BYTE> data);
|
||||||
|
void EnqueueRemove(HashType hash);
|
||||||
|
|
||||||
|
static int AddFile(const std::filesystem::path& path);
|
||||||
|
static int RemoveFile(const std::filesystem::path& path);
|
||||||
|
static std::vector<std::filesystem::path> GetFiles();
|
||||||
|
|
||||||
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;
|
||||||
@@ -51,14 +67,22 @@ private:
|
|||||||
int UnlockMutex();
|
int UnlockMutex();
|
||||||
HANDLE hMutex;
|
HANDLE hMutex;
|
||||||
|
|
||||||
|
std::mutex pending_mutex;
|
||||||
|
std::vector<PendingOp> pending_ops;
|
||||||
|
void ProcessPendingOps();
|
||||||
|
void RemoveModdedTexture(HashType hash);
|
||||||
|
|
||||||
std::unordered_map<HashType, gsl::owner<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
|
||||||
int LoadTexture(TextureFileStruct* file_in_memory, uModTexturePtrPtr auto ppTexture);
|
int LoadTexture(TextureFileStruct* file_in_memory, uModTexturePtrPtr auto ppTexture);
|
||||||
|
|
||||||
void LoadModsFromModlist(std::pair<std::string, std::string> modfile_tuple);
|
static void LoadStartupModlists();
|
||||||
std::filesystem::path dll_path; // path to gmod dll
|
|
||||||
|
static inline std::mutex global_mutex;
|
||||||
|
static inline TextureClient* current_client = nullptr;
|
||||||
|
static inline std::map<std::filesystem::path, std::vector<HashType>> loaded_files;
|
||||||
};
|
};
|
||||||
|
|
||||||
TextureClient::TextureClient(IDirect3DDevice9* device)
|
TextureClient::TextureClient(IDirect3DDevice9* device)
|
||||||
@@ -70,11 +94,19 @@ TextureClient::TextureClient(IDirect3DDevice9* device)
|
|||||||
isDirectXExDevice = D3D9Device->QueryInterface(IID_IDirect3DTexture9, &cpy) == 0x01000001L;
|
isDirectXExDevice = D3D9Device->QueryInterface(IID_IDirect3DTexture9, &cpy) == 0x01000001L;
|
||||||
|
|
||||||
hMutex = CreateMutex(nullptr, false, nullptr);
|
hMutex = CreateMutex(nullptr, false, nullptr);
|
||||||
|
|
||||||
|
std::lock_guard lk(global_mutex);
|
||||||
|
ASSERT(current_client == nullptr); // gMod assumes a single d3d9 device per process
|
||||||
|
current_client = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
TextureClient::~TextureClient()
|
TextureClient::~TextureClient()
|
||||||
{
|
{
|
||||||
Message("TextureClient::~TextureClient(): %p\n", this);
|
Message("TextureClient::~TextureClient(): %p\n", this);
|
||||||
|
{
|
||||||
|
std::lock_guard lk(global_mutex);
|
||||||
|
if (current_client == this) current_client = nullptr;
|
||||||
|
}
|
||||||
if (hMutex != nullptr) {
|
if (hMutex != nullptr) {
|
||||||
CloseHandle(hMutex);
|
CloseHandle(hMutex);
|
||||||
}
|
}
|
||||||
@@ -86,8 +118,12 @@ TextureClient::~TextureClient()
|
|||||||
|
|
||||||
int TextureClient::MergeUpdate()
|
int TextureClient::MergeUpdate()
|
||||||
{
|
{
|
||||||
if (!should_update) return RETURN_OK;
|
bool has_pending;
|
||||||
should_update = false;
|
{
|
||||||
|
std::lock_guard lk(pending_mutex);
|
||||||
|
has_pending = !pending_ops.empty();
|
||||||
|
}
|
||||||
|
if (!should_update && !has_pending) return RETURN_OK;
|
||||||
if (const int ret = LockMutex()) {
|
if (const int ret = LockMutex()) {
|
||||||
gl_ErrorState |= uMod_ERROR_TEXTURE;
|
gl_ErrorState |= uMod_ERROR_TEXTURE;
|
||||||
return ret;
|
return ret;
|
||||||
@@ -95,6 +131,8 @@ int TextureClient::MergeUpdate()
|
|||||||
|
|
||||||
Message("MergeUpdate(): %p\n", this);
|
Message("MergeUpdate(): %p\n", this);
|
||||||
|
|
||||||
|
ProcessPendingOps();
|
||||||
|
|
||||||
for (const auto pTexture : OriginalTextures) {
|
for (const auto pTexture : OriginalTextures) {
|
||||||
if (pTexture->CrossRef_D3Dtex == nullptr) {
|
if (pTexture->CrossRef_D3Dtex == nullptr) {
|
||||||
LookUpToMod(pTexture);
|
LookUpToMod(pTexture);
|
||||||
@@ -111,9 +149,69 @@ int TextureClient::MergeUpdate()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
should_update = false;
|
||||||
return UnlockMutex();
|
return UnlockMutex();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TextureClient::EnqueueAdd(HashType hash, std::vector<BYTE> data)
|
||||||
|
{
|
||||||
|
if (!hash) return;
|
||||||
|
std::lock_guard lk(pending_mutex);
|
||||||
|
pending_ops.push_back(PendingOp{PendingOp::Kind::Add, hash, std::move(data)});
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureClient::EnqueueRemove(HashType hash)
|
||||||
|
{
|
||||||
|
if (!hash) return;
|
||||||
|
std::lock_guard lk(pending_mutex);
|
||||||
|
pending_ops.push_back(PendingOp{PendingOp::Kind::Remove, hash, {}});
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureClient::ProcessPendingOps()
|
||||||
|
{
|
||||||
|
std::vector<PendingOp> ops;
|
||||||
|
{
|
||||||
|
std::lock_guard lk(pending_mutex);
|
||||||
|
ops.swap(pending_ops);
|
||||||
|
}
|
||||||
|
for (auto& op : ops) {
|
||||||
|
if (op.kind == PendingOp::Kind::Add) {
|
||||||
|
if (modded_textures.contains(op.hash)) continue;
|
||||||
|
const auto texture_file_struct = new TextureFileStruct();
|
||||||
|
texture_file_struct->crc_hash = op.hash;
|
||||||
|
texture_file_struct->data = std::move(op.data);
|
||||||
|
modded_textures.emplace(op.hash, texture_file_struct);
|
||||||
|
should_update = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
RemoveModdedTexture(op.hash);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureClient::RemoveModdedTexture(HashType hash)
|
||||||
|
{
|
||||||
|
const auto it = modded_textures.find(hash);
|
||||||
|
if (it == modded_textures.end()) return;
|
||||||
|
const auto texture_file_struct = it->second;
|
||||||
|
|
||||||
|
auto unswitch_in = [texture_file_struct](auto& vec) {
|
||||||
|
for (auto* original_texture : vec) {
|
||||||
|
auto* fake_texture = original_texture->CrossRef_D3Dtex;
|
||||||
|
if (fake_texture && fake_texture->Reference == texture_file_struct) {
|
||||||
|
UnswitchTextures(original_texture);
|
||||||
|
fake_texture->Release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
unswitch_in(OriginalTextures);
|
||||||
|
unswitch_in(OriginalVolumeTextures);
|
||||||
|
unswitch_in(OriginalCubeTextures);
|
||||||
|
|
||||||
|
modded_textures.erase(it);
|
||||||
|
delete texture_file_struct;
|
||||||
|
}
|
||||||
|
|
||||||
void TextureClient::SetLastCreatedTexture(uMod_IDirect3DTexture9* texture)
|
void TextureClient::SetLastCreatedTexture(uMod_IDirect3DTexture9* texture)
|
||||||
{
|
{
|
||||||
if (isDirectXExDevice)
|
if (isDirectXExDevice)
|
||||||
@@ -154,7 +252,7 @@ int TextureClient::UnlockMutex()
|
|||||||
return RETURN_OK;
|
return RETURN_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
gsl::owner<TextureFileStruct*> AddFile(TexEntry& entry, const bool compress)
|
gsl::owner<TextureFileStruct*> MakeTextureFileStruct(TexEntry& entry, const bool compress)
|
||||||
{
|
{
|
||||||
const auto texture_file_struct = new TextureFileStruct();
|
const auto texture_file_struct = new TextureFileStruct();
|
||||||
texture_file_struct->crc_hash = entry.crc_hash;
|
texture_file_struct->crc_hash = entry.crc_hash;
|
||||||
@@ -173,6 +271,7 @@ std::vector<gsl::owner<TextureFileStruct*>> ProcessModfile(const std::filesystem
|
|||||||
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");
|
||||||
|
CoUninitialize();
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
Message("%zu textures... ", entries.size());
|
Message("%zu textures... ", entries.size());
|
||||||
@@ -180,9 +279,9 @@ std::vector<gsl::owner<TextureFileStruct*>> ProcessModfile(const std::filesystem
|
|||||||
texture_file_structs.reserve(entries.size());
|
texture_file_structs.reserve(entries.size());
|
||||||
unsigned file_bytes_loaded = 0;
|
unsigned file_bytes_loaded = 0;
|
||||||
for (auto& tpf_entry : entries) {
|
for (auto& tpf_entry : entries) {
|
||||||
const auto tex_file_struct = AddFile(tpf_entry, compress);
|
const auto texture_file_struct = MakeTextureFileStruct(tpf_entry, compress);
|
||||||
texture_file_structs.push_back(tex_file_struct);
|
texture_file_structs.push_back(texture_file_struct);
|
||||||
file_bytes_loaded += texture_file_structs.back()->data.size();
|
file_bytes_loaded += static_cast<unsigned>(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);
|
||||||
@@ -190,65 +289,162 @@ std::vector<gsl::owner<TextureFileStruct*>> ProcessModfile(const std::filesystem
|
|||||||
return texture_file_structs;
|
return texture_file_structs;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextureClient::LoadModsFromModlist(std::pair<std::string, std::string> modfile_tuple)
|
std::vector<std::filesystem::path> TextureClient::GetFiles()
|
||||||
{
|
{
|
||||||
static std::vector<std::filesystem::path> loaded_modfiles{};
|
std::lock_guard lk(global_mutex);
|
||||||
|
std::vector<std::filesystem::path> result;
|
||||||
|
result.reserve(loaded_files.size());
|
||||||
|
for (const auto& key : loaded_files | std::views::keys) {
|
||||||
|
result.push_back(key);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int TextureClient::RemoveFile(const std::filesystem::path& path)
|
||||||
|
{
|
||||||
|
const auto absolute_path = std::filesystem::absolute(path);
|
||||||
|
std::lock_guard lk(global_mutex);
|
||||||
|
const auto it = loaded_files.find(absolute_path);
|
||||||
|
if (it == loaded_files.end()) return RETURN_FILE_NOT_LOADED;
|
||||||
|
if (current_client) {
|
||||||
|
for (const auto hash : it->second) {
|
||||||
|
current_client->EnqueueRemove(hash);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
loaded_files.erase(it);
|
||||||
|
return RETURN_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int TextureClient::AddFile(const std::filesystem::path& path)
|
||||||
|
{
|
||||||
|
const auto absolute_path = std::filesystem::absolute(path);
|
||||||
|
{
|
||||||
|
std::lock_guard lk(global_mutex);
|
||||||
|
if (loaded_files.contains(absolute_path)) return RETURN_EXISTS;
|
||||||
|
}
|
||||||
|
if (!std::filesystem::exists(absolute_path)) return RETURN_FILE_NOT_LOADED;
|
||||||
|
|
||||||
|
const auto file_size = std::filesystem::file_size(absolute_path);
|
||||||
|
const auto texture_file_structs = ProcessModfile(absolute_path, file_size > 400'000'000);
|
||||||
|
if (texture_file_structs.empty()) return RETURN_FILE_NOT_LOADED;
|
||||||
|
|
||||||
|
std::lock_guard lk(global_mutex);
|
||||||
|
// Re-check under lock; another thread may have loaded the same path concurrently.
|
||||||
|
if (loaded_files.contains(absolute_path)) {
|
||||||
|
for (auto* texture_file_struct : texture_file_structs) delete texture_file_struct;
|
||||||
|
return RETURN_EXISTS;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<HashType> hashes;
|
||||||
|
for (auto* texture_file_struct : texture_file_structs) {
|
||||||
|
const auto hash = texture_file_struct->crc_hash;
|
||||||
|
if (hash && std::ranges::find(hashes, hash) == hashes.end()) {
|
||||||
|
hashes.push_back(hash);
|
||||||
|
if (current_client) {
|
||||||
|
current_client->EnqueueAdd(hash, std::move(texture_file_struct->data));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delete texture_file_struct;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hashes.empty()) return RETURN_TEXTURE_NOT_LOADED;
|
||||||
|
loaded_files.emplace(absolute_path, std::move(hashes));
|
||||||
|
return RETURN_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureClient::LoadStartupModlists()
|
||||||
|
{
|
||||||
std::locale::global(std::locale(""));
|
std::locale::global(std::locale(""));
|
||||||
std::istringstream file(modfile_tuple.second);
|
|
||||||
std::string line;
|
|
||||||
std::vector<std::filesystem::path> modfiles;
|
std::vector<std::filesystem::path> modfiles;
|
||||||
while (std::getline(file, line)) {
|
for (const auto& content : modlists_contents | std::views::values) {
|
||||||
if (line.starts_with("//") || line.starts_with("#") || line.empty()) {
|
std::istringstream iss(content);
|
||||||
|
std::string line;
|
||||||
|
while (std::getline(iss, line)) {
|
||||||
|
if (line.starts_with("//") || line.starts_with("#") || line.empty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Remove newline character
|
||||||
|
line.erase(std::ranges::remove(line, '\r').begin(), line.end());
|
||||||
|
line.erase(std::ranges::remove(line, '\n').begin(), line.end());
|
||||||
|
if (line.empty()) continue;
|
||||||
|
|
||||||
|
auto modfile = std::filesystem::absolute(std::filesystem::path(utils::utf8_to_wstring(line)));
|
||||||
|
if (!std::ranges::contains(modfiles, modfile)) {
|
||||||
|
modfiles.push_back(std::move(modfile));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto total_size = 0ull;
|
||||||
|
for (const auto& modfile : modfiles) {
|
||||||
|
if (std::filesystem::exists(modfile)) total_size += std::filesystem::file_size(modfile);
|
||||||
|
}
|
||||||
|
const bool compress = total_size > 400'000'000;
|
||||||
|
|
||||||
|
std::vector<std::future<std::vector<gsl::owner<TextureFileStruct*>>>> futures;
|
||||||
|
futures.reserve(modfiles.size());
|
||||||
|
for (const auto& modfile : modfiles) {
|
||||||
|
futures.emplace_back(std::async(std::launch::async, ProcessModfile, modfile, compress));
|
||||||
|
}
|
||||||
|
auto loaded_size = 0ull;
|
||||||
|
for (size_t i = 0; i < modfiles.size(); ++i) {
|
||||||
|
auto texture_file_structs = futures[i].get();
|
||||||
|
|
||||||
|
std::lock_guard lk(global_mutex);
|
||||||
|
if (loaded_files.contains(modfiles[i])) {
|
||||||
|
for (const auto* texture_file_struct : texture_file_structs) delete texture_file_struct;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// Remove newline character
|
|
||||||
line.erase(std::ranges::remove(line, '\r').begin(), line.end());
|
|
||||||
line.erase(std::ranges::remove(line, '\n').begin(), line.end());
|
|
||||||
|
|
||||||
const auto wline = utils::utf8_to_wstring(line);
|
std::vector<HashType> hashes;
|
||||||
const auto fsline = std::filesystem::path(wline);
|
for (auto* texture_file_struct : texture_file_structs) {
|
||||||
|
const auto hash = texture_file_struct->crc_hash;
|
||||||
if (!std::ranges::contains(loaded_modfiles, fsline)) {
|
if (hash && std::ranges::find(hashes, hash) == hashes.end()) {
|
||||||
modfiles.push_back(fsline);
|
hashes.push_back(hash);
|
||||||
loaded_modfiles.push_back(fsline);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
auto files_size = 0ull;
|
|
||||||
for (const auto modfile : modfiles) {
|
|
||||||
if (std::filesystem::exists(modfile)) {
|
|
||||||
files_size += std::filesystem::file_size(modfile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
std::vector<std::future<std::vector<gsl::owner<TextureFileStruct*>>>> futures;
|
|
||||||
for (const auto modfile : modfiles) {
|
|
||||||
futures.emplace_back(std::async(std::launch::async, ProcessModfile, modfile, files_size > 400'000'000));
|
|
||||||
}
|
|
||||||
auto loaded_size = 0u;
|
|
||||||
for (auto& future : futures) {
|
|
||||||
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();
|
loaded_size += texture_file_struct->data.size();
|
||||||
|
if (current_client) {
|
||||||
|
current_client->EnqueueAdd(hash, std::move(texture_file_struct->data));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
delete texture_file_struct;
|
||||||
delete texture_file_struct;
|
}
|
||||||
}
|
|
||||||
|
if (!hashes.empty()) {
|
||||||
|
loaded_files.emplace(modfiles[i], std::move(hashes));
|
||||||
}
|
}
|
||||||
should_update = true;
|
|
||||||
}
|
}
|
||||||
Message("Finished loading mods from %s: Loaded %u bytes (%u mb)\n", modfile_tuple.first.c_str(), loaded_size, loaded_size / 1024 / 1024);
|
Info("LoadStartupModlists: %llu bytes (%llu MB)\n", loaded_size, loaded_size / 1024 / 1024);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextureClient::Initialize()
|
void TextureClient::Initialize()
|
||||||
{
|
{
|
||||||
const auto t1 = std::chrono::high_resolution_clock::now();
|
const auto t1 = std::chrono::high_resolution_clock::now();
|
||||||
Info("Initialize: begin\n");
|
Info("Initialize: begin\n");
|
||||||
for (const auto& modlists_content : modlists_contents) {
|
|
||||||
LoadModsFromModlist(modlists_content);
|
// now load files that were added by AddFile() before the device existed
|
||||||
|
std::vector<std::filesystem::path> pre_existing_paths;
|
||||||
|
{
|
||||||
|
std::lock_guard lk(global_mutex);
|
||||||
|
pre_existing_paths.reserve(loaded_files.size());
|
||||||
|
for (const auto& key : loaded_files | std::views::keys) pre_existing_paths.push_back(key);
|
||||||
}
|
}
|
||||||
|
for (const auto& path : pre_existing_paths) {
|
||||||
|
if (!std::filesystem::exists(path)) continue;
|
||||||
|
const auto file_size = std::filesystem::file_size(path);
|
||||||
|
auto texture_file_structs = ProcessModfile(path, file_size > 400'000'000);
|
||||||
|
std::vector<HashType> seen;
|
||||||
|
for (auto* texture_file_struct : texture_file_structs) {
|
||||||
|
const auto hash = texture_file_struct->crc_hash;
|
||||||
|
if (hash && std::ranges::find(seen, hash) == seen.end()) {
|
||||||
|
seen.push_back(hash);
|
||||||
|
EnqueueAdd(hash, std::move(texture_file_struct->data));
|
||||||
|
}
|
||||||
|
delete texture_file_struct;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LoadStartupModlists();
|
||||||
|
|
||||||
const auto t2 = std::chrono::high_resolution_clock::now();
|
const auto t2 = std::chrono::high_resolution_clock::now();
|
||||||
const auto ms = duration_cast<std::chrono::milliseconds>(t2 - t1);
|
const auto ms = duration_cast<std::chrono::milliseconds>(t2 - t1);
|
||||||
Info("Initialize: end, took %d ms\n", ms);
|
Info("Initialize: end, took %d ms\n", ms);
|
||||||
@@ -333,18 +529,18 @@ int TextureClient::LookUpToMod(uModTexturePtr auto pTexture)
|
|||||||
|
|
||||||
const auto textureFileStruct = found->second;
|
const auto textureFileStruct = found->second;
|
||||||
|
|
||||||
decltype(pTexture) fake_Texture;
|
decltype(pTexture) fake_texture;
|
||||||
ret = LoadTexture(textureFileStruct, &fake_Texture);
|
ret = LoadTexture(textureFileStruct, &fake_texture);
|
||||||
if (ret != RETURN_OK)
|
if (ret != RETURN_OK)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
ret = SwitchTextures(fake_Texture, pTexture);
|
ret = SwitchTextures(fake_texture, pTexture);
|
||||||
if (ret != RETURN_OK) {
|
if (ret != RETURN_OK) {
|
||||||
Message("TextureClient::LookUpToMod(): textures not switched %#lX\n", textureFileStruct->crc_hash);
|
Message("TextureClient::LookUpToMod(): textures not switched %#lX\n", textureFileStruct->crc_hash);
|
||||||
fake_Texture->Release();
|
fake_texture->Release();
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
fake_Texture->Reference = textureFileStruct;
|
fake_texture->Reference = textureFileStruct;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -252,6 +252,54 @@ void InitInstance(HINSTANCE hModule)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern "C" __declspec(dllexport) int __cdecl AddFile(const wchar_t* path)
|
||||||
|
{
|
||||||
|
if (!path) return RETURN_BAD_ARGUMENT;
|
||||||
|
try {
|
||||||
|
return TextureClient::AddFile(std::filesystem::path(path));
|
||||||
|
}
|
||||||
|
catch (...) {
|
||||||
|
return RETURN_FATAL_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" __declspec(dllexport) int __cdecl RemoveFile(const wchar_t* path)
|
||||||
|
{
|
||||||
|
if (!path) return RETURN_BAD_ARGUMENT;
|
||||||
|
try {
|
||||||
|
return TextureClient::RemoveFile(std::filesystem::path(path));
|
||||||
|
}
|
||||||
|
catch (...) {
|
||||||
|
return RETURN_FATAL_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" __declspec(dllexport) int __cdecl GetFiles(wchar_t* buffer, int buffer_size_chars)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
const auto files = TextureClient::GetFiles();
|
||||||
|
size_t required = 1;
|
||||||
|
for (const auto& path : files) {
|
||||||
|
required += path.native().size() + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buffer && buffer_size_chars > 0 && static_cast<size_t>(buffer_size_chars) >= required) {
|
||||||
|
wchar_t* out = buffer;
|
||||||
|
for (const auto& path : files) {
|
||||||
|
const auto& native = path.native();
|
||||||
|
std::ranges::copy(native, out);
|
||||||
|
out += native.size();
|
||||||
|
*out++ = L'\0';
|
||||||
|
}
|
||||||
|
*out = L'\0';
|
||||||
|
}
|
||||||
|
return static_cast<int>(required);
|
||||||
|
}
|
||||||
|
catch (...) {
|
||||||
|
return RETURN_FATAL_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ExitInstance()
|
void ExitInstance()
|
||||||
{
|
{
|
||||||
DISABLE_HOOK(GetProcAddress_fn);
|
DISABLE_HOOK(GetProcAddress_fn);
|
||||||
|
|||||||
Reference in New Issue
Block a user