From 73bc004b311ac12226f4843835c383d03d44cd01 Mon Sep 17 00:00:00 2001 From: Multfinite Date: Fri, 8 Sep 2023 20:04:31 +0300 Subject: [PATCH 1/4] Implement mix customizing via spawn.ini: Sections are mixes_preload & mixes_postload --- src/Spawner/CustomMixes.cpp | 44 +++++++++++++++++++++++++++------- src/Spawner/Ra2Mode.h | 2 ++ src/Spawner/Spawner.Config.cpp | 37 ++++++++++++++++++++++++++++ src/Spawner/Spawner.Config.h | 9 +++++++ 4 files changed, 83 insertions(+), 9 deletions(-) diff --git a/src/Spawner/CustomMixes.cpp b/src/Spawner/CustomMixes.cpp index 2a73ab63..45ae1c10 100644 --- a/src/Spawner/CustomMixes.cpp +++ b/src/Spawner/CustomMixes.cpp @@ -16,31 +16,57 @@ * You should have received a copy of the GNU General Public License * along with this program.If not, see . */ +#include "Spawner.h" #include "Ra2Mode.h" +#include #include + #include -MixFileClass *Ra2ModeMIX = nullptr; +#include + +static std::list CustomMixes = { }; + +inline void FreeMixes(std::list& mixes) +{ + for (auto pMix : CustomMixes) + GameDelete(pMix); + CustomMixes.clear(); +} DEFINE_HOOK(0x6BE9BD, ProgEnd_CustomMixes, 0x6) { - if (Ra2ModeMIX) + FreeMixes(CustomMixes); + return 0; +} + +DEFINE_HOOK(0x5301AC, InitBootstrapMixfiles_CustomMixes_Preload, 0x5) +{ + FreeMixes(CustomMixes); + + auto config = Spawner::GetConfig(); + for (auto& pair : config->PreloadMixes) { - GameDelete(Ra2ModeMIX); - Ra2ModeMIX = nullptr; + CustomMixes.push_back(GameCreate(pair.second.c_str())); + Debug::Log(" %s ", pair.second.c_str()); } + // Any 'mode' mixes should be loaded after user custom mixes to prevent overload it. + if (Ra2Mode::IsEnabled()) + CustomMixes.push_back(GameCreate(Ra2Mode::MixFileName)); + return 0; } -DEFINE_HOOK(0x5301AC, InitBootstrapMixfiles_CustomMixes, 0x5) +DEFINE_HOOK_AGAIN(0x5302E4, InitBootstrapMixfiles_CustomMixes_Postload, 0x9) +DEFINE_HOOK(0x53044A, InitBootstrapMixfiles_CustomMixes_Postload, 0x9) { - ProgEnd_CustomMixes(R); - - if (Ra2Mode::IsEnabled()) + auto config = Spawner::GetConfig(); + for (auto& pair : config->PostloadMixes) { - Ra2ModeMIX = GameCreate("ra2mode.mix"); + CustomMixes.push_back(GameCreate(pair.second.c_str())); + Debug::Log(" %s ", pair.second.c_str()); } return 0; diff --git a/src/Spawner/Ra2Mode.h b/src/Spawner/Ra2Mode.h index 474e0cf5..1ae69007 100644 --- a/src/Spawner/Ra2Mode.h +++ b/src/Spawner/Ra2Mode.h @@ -22,6 +22,8 @@ class Ra2Mode { static bool Enabled; public: + static constexpr const char* MixFileName = "ra2mode.mix"; + static bool IsEnabled() { return Ra2Mode::Enabled; diff --git a/src/Spawner/Spawner.Config.cpp b/src/Spawner/Spawner.Config.cpp index e4761557..e12dd90c 100644 --- a/src/Spawner/Spawner.Config.cpp +++ b/src/Spawner/Spawner.Config.cpp @@ -21,6 +21,31 @@ #include "Spawner.Config.h" #include +#include + +constexpr const char* NONE_STR = ""; + +inline void ReadFiles(CCINIClass* pINI, const char* pSection, std::list>& files) +{ + if (!pINI->GetSection(pSection)) + return; + + const int itemsCount = pINI->GetKeyCount(pSection); + for (int i = 0; i < itemsCount; ++i) + { + auto pKey = pINI->GetKeyName(pSection, i); + char* pEnd = nullptr; + auto index = std::strtol(pKey, &pEnd, 10); + if (pEnd == pKey || *pEnd != 0) + continue; + + char fileName[0x80]; + pINI->ReadString(pSection, pKey, "", fileName); + + if (fileName[0] != 0 || _strcmpi(fileName, NONE_STR) != 0) + files.emplace_back(index, fileName); + } +} void SpawnerConfig::LoadFromINIFile(CCINIClass* pINI) { @@ -108,6 +133,18 @@ void SpawnerConfig::LoadFromINIFile(CCINIClass* pINI) // TODO: // QuickMatch = pINI->ReadBool(pSettingsSection, "QuickMatch", QuickMatch); // RunAutoSS = pINI->ReadBool(pSettingsSection, "RunAutoSS", RunAutoSS); + + // Custom Mixes + ReadFiles(pINI, "mixes_preload", PreloadMixes); + ReadFiles(pINI, "mixes_postload", PostloadMixes); + + auto predicate = [](std::pair const& a, std::pair const& b) -> bool + { + return a.first < b.first; + }; + + PreloadMixes.sort(predicate); + PostloadMixes.sort(predicate); } constexpr char* PlayerSectionArray[8] = { diff --git a/src/Spawner/Spawner.Config.h b/src/Spawner/Spawner.Config.h index 8fe10332..f63abcc9 100644 --- a/src/Spawner/Spawner.Config.h +++ b/src/Spawner/Spawner.Config.h @@ -19,6 +19,7 @@ #pragma once #include +#include class CCINIClass; @@ -137,6 +138,10 @@ class SpawnerConfig // bool QuickMatch; // bool RunAutoSS; + // Custom mixes + std::list> PreloadMixes; + std::list> PostloadMixes; + SpawnerConfig() // default values // Game Mode Options : MPModeIndex { 1 } @@ -224,6 +229,10 @@ class SpawnerConfig // TODO: // , QuickMatch { false } // , RunAutoSS { false } + + // Custom Mixes + , PreloadMixes() + , PostloadMixes() { } void LoadFromINIFile(CCINIClass* pINI); From 7ada45185bd57b70f086bc3c72a629c7b7c43231 Mon Sep 17 00:00:00 2001 From: Multfinite Date: Fri, 8 Sep 2023 20:05:50 +0300 Subject: [PATCH 2/4] Add DOCS.md Custom mixes --- DOCS.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 DOCS.md diff --git a/DOCS.md b/DOCS.md new file mode 100644 index 00000000..f2223849 --- /dev/null +++ b/DOCS.md @@ -0,0 +1,18 @@ +### Custom mixes + +Mixes can be preloaded and postloaded. Preload mixes will overwrite all content of original mix files and postloaded mix files. +Postloaded mixes will be overwritten by any other mix files content (include original files). + +> Overwite priority it's loading order:\ +Preload mixes -> Original mixes -> Postload mixes + +Configuration file (spawn.ini) allow to configure mix loading via two sections: *mixes_preload* for preloading, *mixes_postload* for postloading. +This section it is just a sorted list of filenames. + +Example: + +> [mixes_preload] \ +1=HTNK.mix \ +0=HTK.mix + +The first will be HTK.mix and then HTNK.mix - this list will sorted in ascending order. \ No newline at end of file From 34fe46bf7c04f5952ff6e3496821e775d89c788b Mon Sep 17 00:00:00 2001 From: Multfinite Date: Wed, 13 Sep 2023 14:57:10 +0300 Subject: [PATCH 3/4] Edits due to PR #7 into main repository --- DOCS.md | 2 +- src/Spawner/CustomMixes.cpp | 23 +++++++++++------------ src/Spawner/Ra2Mode.h | 2 -- src/Spawner/Spawner.Config.cpp | 31 ++++++++----------------------- src/Spawner/Spawner.Config.h | 8 ++++++-- 5 files changed, 26 insertions(+), 40 deletions(-) diff --git a/DOCS.md b/DOCS.md index f2223849..52936188 100644 --- a/DOCS.md +++ b/DOCS.md @@ -6,7 +6,7 @@ Postloaded mixes will be overwritten by any other mix files content (include ori > Overwite priority it's loading order:\ Preload mixes -> Original mixes -> Postload mixes -Configuration file (spawn.ini) allow to configure mix loading via two sections: *mixes_preload* for preloading, *mixes_postload* for postloading. +Configuration file (spawn.ini) allow to configure mix loading via two sections: *PreloadMixes* for preloading, *PostloadMixes* for postloading. This section it is just a sorted list of filenames. Example: diff --git a/src/Spawner/CustomMixes.cpp b/src/Spawner/CustomMixes.cpp index 45ae1c10..5dd7182b 100644 --- a/src/Spawner/CustomMixes.cpp +++ b/src/Spawner/CustomMixes.cpp @@ -26,7 +26,7 @@ #include -static std::list CustomMixes = { }; +static std::list CustomMixes = { }; inline void FreeMixes(std::list& mixes) { @@ -45,28 +45,27 @@ DEFINE_HOOK(0x5301AC, InitBootstrapMixfiles_CustomMixes_Preload, 0x5) { FreeMixes(CustomMixes); - auto config = Spawner::GetConfig(); - for (auto& pair : config->PreloadMixes) + auto config = Spawner::GetConfig(); + for (auto& mixName : config->PreloadMixes) { - CustomMixes.push_back(GameCreate(pair.second.c_str())); - Debug::Log(" %s ", pair.second.c_str()); + CustomMixes.push_back(GameCreate(mixName.c_str())); + Debug::Log("%s ", mixName.c_str()); } // Any 'mode' mixes should be loaded after user custom mixes to prevent overload it. if (Ra2Mode::IsEnabled()) - CustomMixes.push_back(GameCreate(Ra2Mode::MixFileName)); + CustomMixes.push_back(GameCreate("ra2mode.mix")); return 0; } -DEFINE_HOOK_AGAIN(0x5302E4, InitBootstrapMixfiles_CustomMixes_Postload, 0x9) -DEFINE_HOOK(0x53044A, InitBootstrapMixfiles_CustomMixes_Postload, 0x9) +DEFINE_HOOK(0x53044A, InitBootstrapMixfiles_CustomMixes_Postload, 0x10) { - auto config = Spawner::GetConfig(); - for (auto& pair : config->PostloadMixes) + auto config = Spawner::GetConfig(); + for (auto& mixName : config->PostloadMixes) { - CustomMixes.push_back(GameCreate(pair.second.c_str())); - Debug::Log(" %s ", pair.second.c_str()); + CustomMixes.push_back(GameCreate(mixName.c_str())); + Debug::Log("%s ", mixName.c_str()); } return 0; diff --git a/src/Spawner/Ra2Mode.h b/src/Spawner/Ra2Mode.h index 1ae69007..474e0cf5 100644 --- a/src/Spawner/Ra2Mode.h +++ b/src/Spawner/Ra2Mode.h @@ -22,8 +22,6 @@ class Ra2Mode { static bool Enabled; public: - static constexpr const char* MixFileName = "ra2mode.mix"; - static bool IsEnabled() { return Ra2Mode::Enabled; diff --git a/src/Spawner/Spawner.Config.cpp b/src/Spawner/Spawner.Config.cpp index e12dd90c..604cf164 100644 --- a/src/Spawner/Spawner.Config.cpp +++ b/src/Spawner/Spawner.Config.cpp @@ -23,9 +23,7 @@ #include #include -constexpr const char* NONE_STR = ""; - -inline void ReadFiles(CCINIClass* pINI, const char* pSection, std::list>& files) +inline void ReadListFromSection(CCINIClass* pINI, const char* pSection, std::list& strings) { if (!pINI->GetSection(pSection)) return; @@ -33,17 +31,12 @@ inline void ReadFiles(CCINIClass* pINI, const char* pSection, std::listGetKeyCount(pSection); for (int i = 0; i < itemsCount; ++i) { - auto pKey = pINI->GetKeyName(pSection, i); - char* pEnd = nullptr; - auto index = std::strtol(pKey, &pEnd, 10); - if (pEnd == pKey || *pEnd != 0) - continue; - - char fileName[0x80]; - pINI->ReadString(pSection, pKey, "", fileName); + auto pKey = pINI->GetKeyName(pSection, i); + std::string& str = strings.emplace_back(); str.resize(0x80); + pINI->ReadString(pSection, pKey, NONE_STR, (char*) str.c_str(), str.size()); str.resize(strlen(str.c_str())); - if (fileName[0] != 0 || _strcmpi(fileName, NONE_STR) != 0) - files.emplace_back(index, fileName); + if (str == NONE_STR) + strings.remove(str); } } @@ -135,16 +128,8 @@ void SpawnerConfig::LoadFromINIFile(CCINIClass* pINI) // RunAutoSS = pINI->ReadBool(pSettingsSection, "RunAutoSS", RunAutoSS); // Custom Mixes - ReadFiles(pINI, "mixes_preload", PreloadMixes); - ReadFiles(pINI, "mixes_postload", PostloadMixes); - - auto predicate = [](std::pair const& a, std::pair const& b) -> bool - { - return a.first < b.first; - }; - - PreloadMixes.sort(predicate); - PostloadMixes.sort(predicate); + ReadListFromSection(pINI, "PreloadMixes", PreloadMixes); + ReadListFromSection(pINI, "PostloadMixes", PostloadMixes); } constexpr char* PlayerSectionArray[8] = { diff --git a/src/Spawner/Spawner.Config.h b/src/Spawner/Spawner.Config.h index f63abcc9..77de786a 100644 --- a/src/Spawner/Spawner.Config.h +++ b/src/Spawner/Spawner.Config.h @@ -23,6 +23,8 @@ class CCINIClass; +constexpr const char* NONE_STR = ""; + class SpawnerConfig { @@ -139,8 +141,10 @@ class SpawnerConfig // bool RunAutoSS; // Custom mixes - std::list> PreloadMixes; - std::list> PostloadMixes; + // Note: std::list and std::string will be realised followed to RAII concept. It is pretty save instead of const char*. + + std::list PreloadMixes; + std::list PostloadMixes; SpawnerConfig() // default values // Game Mode Options From d1890e384ffa6530f4e495e86db4a2046bc47053 Mon Sep 17 00:00:00 2001 From: Multfinite Date: Wed, 13 Sep 2023 15:01:09 +0300 Subject: [PATCH 4/4] Update DOCS.md --- DOCS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DOCS.md b/DOCS.md index 52936188..df097193 100644 --- a/DOCS.md +++ b/DOCS.md @@ -11,7 +11,7 @@ This section it is just a sorted list of filenames. Example: -> [mixes_preload] \ +> [PreloadMixes] \ 1=HTNK.mix \ 0=HTK.mix