From e0947cb2276eed9911dd7e7686340277720287de Mon Sep 17 00:00:00 2001 From: Silent Date: Mon, 27 Apr 2026 16:02:54 +0200 Subject: [PATCH 1/3] std.dmaudio: Support overriding sfx.raw/sfx.sdt sfx.raw is permanently opened by the game, so any attempts to reinstall are pointless. Loose files cannot be loaded at the moment, but they are likely theoretically possible for replacements. Loose addon SFX files are meaningfless, as the game accesses them exclusively through an ID enum, and thus there is no way to make sure new sounds sit where mods expect them to. --- doc/plugins/gta3/std.dmaudio.md | 12 ++ doc/readme/Leia-me.md | 2 +- doc/readme/Readme.md | 2 +- premake5.lua | 1 + src/plugins/gta3/std.dmaudio/dmaudio.cpp | 147 +++++++++++++++++++++++ src/translator/gta3/3/10.hpp | 7 ++ src/translator/gta3/vc/10.hpp | 7 ++ 7 files changed, 176 insertions(+), 2 deletions(-) create mode 100644 doc/plugins/gta3/std.dmaudio.md create mode 100644 src/plugins/gta3/std.dmaudio/dmaudio.cpp diff --git a/doc/plugins/gta3/std.dmaudio.md b/doc/plugins/gta3/std.dmaudio.md new file mode 100644 index 00000000..f182ca9b --- /dev/null +++ b/doc/plugins/gta3/std.dmaudio.md @@ -0,0 +1,12 @@ +gta3.std.dmaudio +========================================================================= + + __Author__: Silent + + __Priority__: 50 + + __Game__: III, Vice City + +************************************************************************* + +__Description__: + This plugin is responsible for DMAudio SFX archives, that is: + + * SFX archives (sfx.raw/sfx.sdt) diff --git a/doc/readme/Leia-me.md b/doc/readme/Leia-me.md index 0bef4732..faec4008 100644 --- a/doc/readme/Leia-me.md +++ b/doc/readme/Leia-me.md @@ -101,6 +101,6 @@ Finalmente, créditos. #### Muito Obrigado à * ArtututuVidor$, Andryo, Junior_Djjr e JNRois12 por alpha-testarem * Gramps e TJGM pelo suporte emocional. - * SilentPL por muitos fixes e ajudas no desenvolvimento do projeto. + * Silent por muitos fixes e ajudas no desenvolvimento do projeto. * ThirteenAG por me fornecer varios ponteiros (literalmente) para a versão do GTA III / Vice City. diff --git a/doc/readme/Readme.md b/doc/readme/Readme.md index 00714b53..9b02a545 100644 --- a/doc/readme/Readme.md +++ b/doc/readme/Readme.md @@ -102,5 +102,5 @@ Finally, let's go to the credits. #### Special Thanks To * ArtututuVidor$, Andryo, Junior_Djjr, JNRois12 for alpha-testing * Gramps and TJGM for emotional support. - * SilentPL for A LOT of fixes and help with the development of Mod Loader. + * Silent for A LOT of fixes and help with the development of Mod Loader. * ThirteenAG for giving me lots of pointers (literally) for the GTA III / Vice City version. diff --git a/premake5.lua b/premake5.lua index d7fd143c..ad102dbe 100644 --- a/premake5.lua +++ b/premake5.lua @@ -280,6 +280,7 @@ solution "modloader" "std.sprites", "std.fx", "std.text", + "std.dmaudio", "std.tracks", "std.bank", "std.stream", diff --git a/src/plugins/gta3/std.dmaudio/dmaudio.cpp b/src/plugins/gta3/std.dmaudio/dmaudio.cpp new file mode 100644 index 00000000..32036178 --- /dev/null +++ b/src/plugins/gta3/std.dmaudio/dmaudio.cpp @@ -0,0 +1,147 @@ +/* + * DMAudio Loader Plugin for Mod Loader + * Copyright (C) 2013-2026 Silent + * Licensed under the MIT License, see LICENSE at top level directory. + * + * std.dmaudio -- Standard DMAudio Loader Plugin for Mod Loader + * + */ +#include +using namespace modloader; + +/* + * The plugin object + */ +class DMAudioPlugin : public modloader::basic_plugin +{ + private: + size_t sfx_raw = 0; // Hash + size_t sfx_sdt = 0; // Hash + + file_overrider sfx_raw_overrider; // sfx.raw overrider + file_overrider sfx_sdt_overrider; // sfx.sdt overrider + + public: + // Standard plugin methods + const info& GetInfo() override; + bool OnStartup() override; + bool OnShutdown() override; + int GetBehaviour(modloader::file&) override; + bool InstallFile(const modloader::file&) override; + bool ReinstallFile(const modloader::file&) override; + bool UninstallFile(const modloader::file&) override; + +} dmaudio_plugin; + +REGISTER_ML_PLUGIN(::dmaudio_plugin); + +/* + * DMAudioPlugin::GetInfo + * Returns information about this plugin + */ +const DMAudioPlugin::info& DMAudioPlugin::GetInfo() +{ + static const char* extable[] = { "raw", "sdt", 0 }; + static const info xinfo = { "std.dmaudio", get_version_by_date(), "Silent", -1, extable }; + return xinfo; +} + +/* + * DMAudioPlugin::OnStartup + * Startups the plugin + */ +bool DMAudioPlugin::OnStartup() +{ + if(gvm.IsIII() || gvm.IsVC()) + { + this->sfx_raw = modloader::hash("sfx.raw"); + this->sfx_sdt = modloader::hash("sfx.sdt"); + + // SFX files are used and loaded constantly, and the game doesn't even support closing sfx.raw. + // They are actually fopen, but the signatures are compatible + auto no_reinstall = file_overrider::params(nullptr); + this->sfx_sdt_overrider.SetParams(no_reinstall).SetFileDetour(OpenFileDetour()); + this->sfx_raw_overrider.SetParams(no_reinstall).SetFileDetour(OpenFileDetour()); + return true; + } + return false; +} + +/* + * DMAudioPlugin::OnShutdown + * Shutdowns the plugin + */ +bool DMAudioPlugin::OnShutdown() +{ + return true; +} + + +/* + * DMAudioPlugin::GetBehaviour + * Gets the relationship between this plugin and the file + */ +int DMAudioPlugin::GetBehaviour(modloader::file& file) +{ + if(!file.is_dir()) + { + if(file.hash == this->sfx_raw || file.hash == this->sfx_sdt) + { + file.behaviour = file.hash; + return MODLOADER_BEHAVIOUR_YES; + } + } + return MODLOADER_BEHAVIOUR_NO; +} + + +/* + * DMAudioPlugin::InstallFile + * Installs a file using this plugin + */ +bool DMAudioPlugin::InstallFile(const modloader::file& file) +{ + if(file.behaviour == this->sfx_raw) + { + return sfx_raw_overrider.InstallFile(file); + } + else if(file.behaviour == this->sfx_sdt) + { + return sfx_sdt_overrider.InstallFile(file); + } + return false; +} + +/* + * DMAudioPlugin::ReinstallFile + * Reinstall a file previosly installed that has been updated + */ +bool DMAudioPlugin::ReinstallFile(const modloader::file& file) +{ + if(file.behaviour == this->sfx_raw) + { + return sfx_raw_overrider.ReinstallFile(); + } + else if(file.behaviour == this->sfx_sdt) + { + return sfx_sdt_overrider.ReinstallFile(); + } + return false; +} + +/* + * DMAudioPlugin::UninstallFile + * Uninstall a previosly installed file + */ +bool DMAudioPlugin::UninstallFile(const modloader::file& file) +{ + if(file.behaviour == this->sfx_raw) + { + return sfx_raw_overrider.UninstallFile(); + } + else if(file.behaviour == this->sfx_sdt) + { + return sfx_sdt_overrider.UninstallFile(); + } + return false; +} diff --git a/src/translator/gta3/3/10.hpp b/src/translator/gta3/3/10.hpp index 33694b47..1fe14642 100644 --- a/src/translator/gta3/3/10.hpp +++ b/src/translator/gta3/3/10.hpp @@ -249,6 +249,13 @@ static void III_10(std::map& map) map[xIII(0x54BB7C)] = 0x54BB7C;// call _ZN6CPlane8LoadPathEPKcRiRfb ; "flight4.dat" } + // std.dmaudio + if(true) + { + map[xVc(0x5D5B7B)] = 0x5682E1; // call fopen ; @cSampleManager::InitialiseSampleBanks (sfx.sdt) + map[xVc(0x5D5BC5)] = 0x568301; // call fopen ; @cSampleManager::InitialiseSampleBanks (sfx.raw) + } + // traits if(true) { diff --git a/src/translator/gta3/vc/10.hpp b/src/translator/gta3/vc/10.hpp index bfeda86b..2eabfdde 100644 --- a/src/translator/gta3/vc/10.hpp +++ b/src/translator/gta3/vc/10.hpp @@ -293,6 +293,13 @@ static void vc_10(std::map& map) map[xVc(0x5B24AE)] = 0x5B24AE; // call _ZN6CPlane8LoadPathEPKcRiRfb ; "flight3.dat" } + // std.dmaudio + if(true) + { + map[xVc(0x5D5B7B)] = 0x5D5B7B; // call fopen ; @cSampleManager::InitialiseSampleBanks (sfx.sdt) + map[xVc(0x5D5BC5)] = 0x5D5BC5; // call fopen ; @cSampleManager::InitialiseSampleBanks (sfx.raw) + } + // traits if(true) { From ca05c58c96ad1938889a0c2719643ad27e9ba485 Mon Sep 17 00:00:00 2001 From: Silent Date: Mon, 27 Apr 2026 23:26:51 +0200 Subject: [PATCH 2/3] std.dmaudio: Support overriding and adding audio samples --- doc/plugins/gta3/std.dmaudio.md | 3 +- src/plugins/gta3/std.dmaudio/dmaudio.cpp | 133 ++++++++++++++++++----- src/translator/gta3/3/10.hpp | 6 + src/translator/gta3/vc/10.hpp | 8 ++ 4 files changed, 120 insertions(+), 30 deletions(-) diff --git a/doc/plugins/gta3/std.dmaudio.md b/doc/plugins/gta3/std.dmaudio.md index f182ca9b..77bd415e 100644 --- a/doc/plugins/gta3/std.dmaudio.md +++ b/doc/plugins/gta3/std.dmaudio.md @@ -7,6 +7,7 @@ gta3.std.dmaudio ************************************************************************* __Description__: - This plugin is responsible for DMAudio SFX archives, that is: + This plugin is responsible for DMAudio SFX archives, streamed samples, cutscenes, and radios, that is: * SFX archives (sfx.raw/sfx.sdt) + * WAV/MP3/ADF files in the Audio directory diff --git a/src/plugins/gta3/std.dmaudio/dmaudio.cpp b/src/plugins/gta3/std.dmaudio/dmaudio.cpp index 32036178..5c79c620 100644 --- a/src/plugins/gta3/std.dmaudio/dmaudio.cpp +++ b/src/plugins/gta3/std.dmaudio/dmaudio.cpp @@ -9,17 +9,42 @@ #include using namespace modloader; +enum class Type +{ + SfxRaw = 0, // sfx.raw + SfxSdt = 1, // sfx.dat + Sample = 2, // Samples (.wav, .mp3, .adf, .vb) + Max = 3, // Max 2 bits +}; + + +// Basic masks +static const uint64_t hash_mask_base = 0xFFFFFFFF; +static const uint64_t type_mask_base = 0x0003; // Mask for type without any shifting +static const uint32_t type_mask_shf = 32; // Takes 2 bits, starts from 33th bit because first 32th bits is a hash + +// Sets the initial value for a behaviour, by using an filename hash and file type +inline uint64_t SetType(uint32_t hash, Type type) +{ + return modloader::file::set_mask(uint64_t(hash), type_mask_base, type_mask_shf, type); +} + +// Gets the behaviour file type +inline Type GetType(uint64_t mask) +{ + return modloader::file::get_mask(mask, type_mask_base, type_mask_shf); +} + + /* * The plugin object */ class DMAudioPlugin : public modloader::basic_plugin { private: - size_t sfx_raw = 0; // Hash - size_t sfx_sdt = 0; // Hash - - file_overrider sfx_raw_overrider; // sfx.raw overrider - file_overrider sfx_sdt_overrider; // sfx.sdt overrider + file_overrider sfx_raw_overrider; // sfx.raw overrider + file_overrider sfx_sdt_overrider; // sfx.sdt overrider + std::map streams; // Stream (hash, file*) map public: // Standard plugin methods @@ -31,6 +56,9 @@ class DMAudioPlugin : public modloader::basic_plugin bool ReinstallFile(const modloader::file&) override; bool UninstallFile(const modloader::file&) override; + private: + static char* PatchedStrcat(char* destination, const char* source); + } dmaudio_plugin; REGISTER_ML_PLUGIN(::dmaudio_plugin); @@ -41,7 +69,7 @@ REGISTER_ML_PLUGIN(::dmaudio_plugin); */ const DMAudioPlugin::info& DMAudioPlugin::GetInfo() { - static const char* extable[] = { "raw", "sdt", 0 }; + static const char* extable[] = { "raw", "sdt", "wav", "mp3", "adf", "vb", 0 }; static const info xinfo = { "std.dmaudio", get_version_by_date(), "Silent", -1, extable }; return xinfo; } @@ -54,14 +82,27 @@ bool DMAudioPlugin::OnStartup() { if(gvm.IsIII() || gvm.IsVC()) { - this->sfx_raw = modloader::hash("sfx.raw"); - this->sfx_sdt = modloader::hash("sfx.sdt"); - // SFX files are used and loaded constantly, and the game doesn't even support closing sfx.raw. // They are actually fopen, but the signatures are compatible auto no_reinstall = file_overrider::params(nullptr); this->sfx_sdt_overrider.SetParams(no_reinstall).SetFileDetour(OpenFileDetour()); this->sfx_raw_overrider.SetParams(no_reinstall).SetFileDetour(OpenFileDetour()); + + // Patch strcat calls in cSampleManager + using namespace injector; + const auto strcatFunc = raw_ptr(&PatchedStrcat); + + MakeCALL(xVc(0x5D6339), strcatFunc); // cSampleManager::StartStreamedFile + MakeCALL(xVc(0x5D64CD), strcatFunc); // cSampleManager::PreloadStreamedFile + MakeCALL(xVc(0x5D799B), strcatFunc); // cSampleManager::Initialise + MakeCALL(xVc(0x5D7B69), strcatFunc); // cSampleManager::Initialise + //MakeCALL(xVc(0x5D71E3), strcatFunc); // cSampleManager::CheckForAnAudioFileOnCD (pointless) + if (gvm.IsVC()) + { + MakeCALL(xVc(0x5D7C1B), strcatFunc); // cSampleManager::Initialise + MakeCALL(xVc(0x5D7A7B), strcatFunc); // cSampleManager::Initialise + } + return true; } return false; @@ -85,11 +126,25 @@ int DMAudioPlugin::GetBehaviour(modloader::file& file) { if(!file.is_dir()) { - if(file.hash == this->sfx_raw || file.hash == this->sfx_sdt) + static const auto sfx_raw = modloader::hash("sfx.raw"); + static const auto sfx_sdt = modloader::hash("sfx.sdt"); + if(file.hash == sfx_raw) + { + file.behaviour = SetType(file.hash, Type::SfxRaw); + } + else if(file.hash == sfx_sdt) + { + file.behaviour = SetType(file.hash, Type::SfxSdt); + } + else if(file.is_ext("wav") || file.is_ext("mp3") || file.is_ext("adf") || file.is_ext("vb")) { - file.behaviour = file.hash; - return MODLOADER_BEHAVIOUR_YES; + file.behaviour = SetType(file.hash, Type::Sample); } + else + { + return MODLOADER_BEHAVIOUR_NO; + } + return MODLOADER_BEHAVIOUR_YES; } return MODLOADER_BEHAVIOUR_NO; } @@ -101,13 +156,11 @@ int DMAudioPlugin::GetBehaviour(modloader::file& file) */ bool DMAudioPlugin::InstallFile(const modloader::file& file) { - if(file.behaviour == this->sfx_raw) + switch(GetType(file.behaviour)) { - return sfx_raw_overrider.InstallFile(file); - } - else if(file.behaviour == this->sfx_sdt) - { - return sfx_sdt_overrider.InstallFile(file); + case Type::SfxRaw: return sfx_raw_overrider.InstallFile(file); + case Type::SfxSdt: return sfx_sdt_overrider.InstallFile(file); + case Type::Sample: this->streams[file.hash] = &file; return true; } return false; } @@ -118,13 +171,11 @@ bool DMAudioPlugin::InstallFile(const modloader::file& file) */ bool DMAudioPlugin::ReinstallFile(const modloader::file& file) { - if(file.behaviour == this->sfx_raw) - { - return sfx_raw_overrider.ReinstallFile(); - } - else if(file.behaviour == this->sfx_sdt) + switch(GetType(file.behaviour)) { - return sfx_sdt_overrider.ReinstallFile(); + case Type::SfxRaw: return sfx_raw_overrider.ReinstallFile(); + case Type::SfxSdt: return sfx_sdt_overrider.ReinstallFile(); + case Type::Sample: this->streams[file.hash] = &file; return true; } return false; } @@ -135,13 +186,37 @@ bool DMAudioPlugin::ReinstallFile(const modloader::file& file) */ bool DMAudioPlugin::UninstallFile(const modloader::file& file) { - if(file.behaviour == this->sfx_raw) + switch(GetType(file.behaviour)) { - return sfx_raw_overrider.UninstallFile(); + case Type::SfxRaw: return sfx_raw_overrider.UninstallFile(); + case Type::SfxSdt: return sfx_sdt_overrider.UninstallFile(); + case Type::Sample: this->streams.erase(file.hash); return true; } - else if(file.behaviour == this->sfx_sdt) + return false; +} + +char* DMAudioPlugin::PatchedStrcat(char* destination, const char* source) +{ + const auto& plugin = plugin_ptr->cast(); + + const char* lookupName; + const char* filename = std::strrchr(source, '\\'); + if (filename != nullptr) { - return sfx_sdt_overrider.UninstallFile(); + lookupName = filename+1; + } + else + { + lookupName = source; + } + auto it = plugin.streams.find(modloader::hash(lookupName, ::tolower)); + if (it != plugin.streams.end()) + { + std::string path; + return strcpy(destination, it->second->fullpath(path).c_str()); + } + else + { + return strcat(destination, source); } - return false; } diff --git a/src/translator/gta3/3/10.hpp b/src/translator/gta3/3/10.hpp index 1fe14642..746ba54c 100644 --- a/src/translator/gta3/3/10.hpp +++ b/src/translator/gta3/3/10.hpp @@ -254,6 +254,12 @@ static void III_10(std::map& map) { map[xVc(0x5D5B7B)] = 0x5682E1; // call fopen ; @cSampleManager::InitialiseSampleBanks (sfx.sdt) map[xVc(0x5D5BC5)] = 0x568301; // call fopen ; @cSampleManager::InitialiseSampleBanks (sfx.raw) + + map[xVc(0x5D6339)] = 0x568066; // call strcat ; cSampleManager::StartStreamedFile + map[xVc(0x5D64CD)] = 0x567CCC; // call strcat ; cSampleManager::PreloadStreamedFile + map[xVc(0x5D799B)] = 0x566A76; // call strcat ; cSampleManager::Initialise + map[xVc(0x5D7B69)] = 0x566AC6; // call strcat ; cSampleManager::Initialise + //map[xVc(0x5D71E3)] = 0x566EE9; // call strcat ; cSampleManager::CheckForAnAudioFileOnCD (pointless) } // traits diff --git a/src/translator/gta3/vc/10.hpp b/src/translator/gta3/vc/10.hpp index 2eabfdde..02223a7e 100644 --- a/src/translator/gta3/vc/10.hpp +++ b/src/translator/gta3/vc/10.hpp @@ -298,6 +298,14 @@ static void vc_10(std::map& map) { map[xVc(0x5D5B7B)] = 0x5D5B7B; // call fopen ; @cSampleManager::InitialiseSampleBanks (sfx.sdt) map[xVc(0x5D5BC5)] = 0x5D5BC5; // call fopen ; @cSampleManager::InitialiseSampleBanks (sfx.raw) + + map[xVc(0x5D6339)] = 0x5D6339; // call strcat ; cSampleManager::StartStreamedFile + map[xVc(0x5D64CD)] = 0x5D64CD; // call strcat ; cSampleManager::PreloadStreamedFile + map[xVc(0x5D799B)] = 0x5D799B; // call strcat ; cSampleManager::Initialise + map[xVc(0x5D7B69)] = 0x5D7B69; // call strcat ; cSampleManager::Initialise + //map[xVc(0x5D71E3)] = 0x5D71E3; // call strcat ; cSampleManager::CheckForAnAudioFileOnCD (pointless) + map[xVc(0x5D7C1B)] = 0x5D7C1B; // call strcat ; cSampleManager::Initialise + map[xVc(0x5D7A7B)] = 0x5D7A7B; // call strcat ; cSampleManager::Initialise } // traits From 599656aecab4476d66c256bd664a1daba657cec3 Mon Sep 17 00:00:00 2001 From: Silent Date: Tue, 28 Apr 2026 15:25:57 +0200 Subject: [PATCH 3/3] std.audio: Match duplicate stream names on the longest path suffix --- src/plugins/gta3/std.dmaudio/dmaudio.cpp | 70 +++++++++++++++++++----- 1 file changed, 56 insertions(+), 14 deletions(-) diff --git a/src/plugins/gta3/std.dmaudio/dmaudio.cpp b/src/plugins/gta3/std.dmaudio/dmaudio.cpp index 5c79c620..3e441b36 100644 --- a/src/plugins/gta3/std.dmaudio/dmaudio.cpp +++ b/src/plugins/gta3/std.dmaudio/dmaudio.cpp @@ -42,9 +42,9 @@ inline Type GetType(uint64_t mask) class DMAudioPlugin : public modloader::basic_plugin { private: - file_overrider sfx_raw_overrider; // sfx.raw overrider - file_overrider sfx_sdt_overrider; // sfx.sdt overrider - std::map streams; // Stream (hash, file*) map + file_overrider sfx_raw_overrider; // sfx.raw overrider + file_overrider sfx_sdt_overrider; // sfx.sdt overrider + std::multimap streams; // Stream (hash, file*) map public: // Standard plugin methods @@ -138,7 +138,8 @@ int DMAudioPlugin::GetBehaviour(modloader::file& file) } else if(file.is_ext("wav") || file.is_ext("mp3") || file.is_ext("adf") || file.is_ext("vb")) { - file.behaviour = SetType(file.hash, Type::Sample); + // Duplicate file names are allowed + file.behaviour = SetType(modloader::hash(file.filepath()), Type::Sample); } else { @@ -160,7 +161,7 @@ bool DMAudioPlugin::InstallFile(const modloader::file& file) { case Type::SfxRaw: return sfx_raw_overrider.InstallFile(file); case Type::SfxSdt: return sfx_sdt_overrider.InstallFile(file); - case Type::Sample: this->streams[file.hash] = &file; return true; + case Type::Sample: this->streams.emplace(file.hash, &file); return true; } return false; } @@ -175,7 +176,7 @@ bool DMAudioPlugin::ReinstallFile(const modloader::file& file) { case Type::SfxRaw: return sfx_raw_overrider.ReinstallFile(); case Type::SfxSdt: return sfx_sdt_overrider.ReinstallFile(); - case Type::Sample: this->streams[file.hash] = &file; return true; + case Type::Sample: return true; // No need to do anything } return false; } @@ -190,7 +191,19 @@ bool DMAudioPlugin::UninstallFile(const modloader::file& file) { case Type::SfxRaw: return sfx_raw_overrider.UninstallFile(); case Type::SfxSdt: return sfx_sdt_overrider.UninstallFile(); - case Type::Sample: this->streams.erase(file.hash); return true; + case Type::Sample: + { + auto range = this->streams.equal_range(file.hash); + for (auto it = range.first; it != range.second; ++it) + { + if (it->second == &file) + { + this->streams.erase(it); + return true; + } + } + break; + } } return false; } @@ -209,14 +222,43 @@ char* DMAudioPlugin::PatchedStrcat(char* destination, const char* source) { lookupName = source; } - auto it = plugin.streams.find(modloader::hash(lookupName, ::tolower)); - if (it != plugin.streams.end()) + auto range = plugin.streams.equal_range(modloader::hash(lookupName, ::tolower)); + if (range.first != range.second) { + // If we have just one match, return it. Else, find the closest common path suffix and use that. + auto bestMatch = range.first; + if (std::distance(range.first, range.second) > 1) + { + size_t bestScore = 1; + const std::string sourcePath = modloader::NormalizePath(source); + for (auto it = range.first; it != range.second; ++it) + { + const std::string modFilepath(it->second->filepath()); + + size_t currentScore = 2; + size_t sourceSuffixPos; + do + { + sourceSuffixPos = GetLastPathComponent(sourcePath, currentScore); + const size_t modSuffixPos = GetLastPathComponent(modFilepath, currentScore); + if (sourcePath.compare(sourceSuffixPos, std::string::npos, modFilepath, modSuffixPos, std::string::npos) != 0) + { + break; + } + currentScore++; + } + while (sourceSuffixPos != 0); + + if (currentScore > bestScore) + { + bestScore = currentScore; + bestMatch = it; + } + } + } + std::string path; - return strcpy(destination, it->second->fullpath(path).c_str()); - } - else - { - return strcat(destination, source); + return strcpy(destination, bestMatch->second->fullpath(path).c_str()); } + return strcat(destination, source); }