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);