diff --git a/DOCS.md b/DOCS.md new file mode 100644 index 00000000..df097193 --- /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: *PreloadMixes* for preloading, *PostloadMixes* for postloading. +This section it is just a sorted list of filenames. + +Example: + +> [PreloadMixes] \ +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 diff --git a/src/Spawner/CustomMixes.cpp b/src/Spawner/CustomMixes.cpp index 2a73ab63..5dd7182b 100644 --- a/src/Spawner/CustomMixes.cpp +++ b/src/Spawner/CustomMixes.cpp @@ -16,31 +16,56 @@ * 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& mixName : config->PreloadMixes) { - GameDelete(Ra2ModeMIX); - Ra2ModeMIX = nullptr; + 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.mix")); + return 0; } -DEFINE_HOOK(0x5301AC, InitBootstrapMixfiles_CustomMixes, 0x5) +DEFINE_HOOK(0x53044A, InitBootstrapMixfiles_CustomMixes_Postload, 0x10) { - ProgEnd_CustomMixes(R); - - if (Ra2Mode::IsEnabled()) + auto config = Spawner::GetConfig(); + for (auto& mixName : config->PostloadMixes) { - Ra2ModeMIX = GameCreate("ra2mode.mix"); + CustomMixes.push_back(GameCreate(mixName.c_str())); + Debug::Log("%s ", mixName.c_str()); } return 0; diff --git a/src/Spawner/Spawner.Config.cpp b/src/Spawner/Spawner.Config.cpp index e4761557..604cf164 100644 --- a/src/Spawner/Spawner.Config.cpp +++ b/src/Spawner/Spawner.Config.cpp @@ -21,6 +21,24 @@ #include "Spawner.Config.h" #include +#include + +inline void ReadListFromSection(CCINIClass* pINI, const char* pSection, std::list& strings) +{ + if (!pINI->GetSection(pSection)) + return; + + const int itemsCount = pINI->GetKeyCount(pSection); + for (int i = 0; i < itemsCount; ++i) + { + 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 (str == NONE_STR) + strings.remove(str); + } +} void SpawnerConfig::LoadFromINIFile(CCINIClass* pINI) { @@ -108,6 +126,10 @@ void SpawnerConfig::LoadFromINIFile(CCINIClass* pINI) // TODO: // QuickMatch = pINI->ReadBool(pSettingsSection, "QuickMatch", QuickMatch); // RunAutoSS = pINI->ReadBool(pSettingsSection, "RunAutoSS", RunAutoSS); + + // Custom Mixes + 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 8fe10332..77de786a 100644 --- a/src/Spawner/Spawner.Config.h +++ b/src/Spawner/Spawner.Config.h @@ -19,9 +19,12 @@ #pragma once #include +#include class CCINIClass; +constexpr const char* NONE_STR = ""; + class SpawnerConfig { @@ -137,6 +140,12 @@ class SpawnerConfig // bool QuickMatch; // bool RunAutoSS; + // Custom mixes + // 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 : MPModeIndex { 1 } @@ -224,6 +233,10 @@ class SpawnerConfig // TODO: // , QuickMatch { false } // , RunAutoSS { false } + + // Custom Mixes + , PreloadMixes() + , PostloadMixes() { } void LoadFromINIFile(CCINIClass* pINI);