Skip to content

Commit a4a6511

Browse files
committed
Implement CustomMissionID for 3rd party mission filtering and extra info on S/L
1 parent 0bb085a commit a4a6511

File tree

4 files changed

+216
-1
lines changed

4 files changed

+216
-1
lines changed

src/Misc/SavedGamesInSubdir.cpp

Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,11 @@
2222
#include <Utilities/Macro.h>
2323
#include <Spawner/Spawner.h>
2424

25+
#include <HouseClass.h>
26+
#include <LoadOptionsClass.h>
27+
2528
#include <filesystem>
29+
#include <optional>
2630

2731
namespace SavedGames
2832
{
@@ -161,3 +165,199 @@ DEFINE_HOOK(0x67FD26, LoadOptionsClass_ReadSaveInfo_SGInSubdir, 0x5)
161165

162166
return 0;
163167
}
168+
169+
170+
//issue #18 : Save game filter for 3rd party campaigns
171+
namespace SavedGames
172+
{
173+
struct CustomMissionID
174+
{
175+
static constexpr wchar_t* SaveName = L"CustomMissionID";
176+
177+
int Number;
178+
179+
CustomMissionID() : Number { Spawner::GetConfig()->CustomMissionID } { }
180+
181+
CustomMissionID(int num) : Number { num } { }
182+
183+
operator int() const { return Number; }
184+
};
185+
186+
// More fun
187+
int HowManyTimesISavedForThisScenario = 0;
188+
189+
struct ExtraMetaInfo
190+
{
191+
static constexpr wchar_t* SaveName = L"Spawner Extra Info";
192+
193+
int CurrentFrame;
194+
int SavedCount;
195+
int TechnoCount;
196+
197+
explicit ExtraMetaInfo()
198+
:CurrentFrame { Unsorted::CurrentFrame }
199+
, SavedCount { HowManyTimesISavedForThisScenario }
200+
, TechnoCount { TechnoClass::Array->Count }
201+
{
202+
}
203+
};
204+
205+
template<typename T>
206+
bool AppendToStorage(IStorage* pStorage)
207+
{
208+
IStream* pStream = nullptr;
209+
bool ret = false;
210+
HRESULT hr = pStorage->CreateStream(
211+
T::SaveName,
212+
STGM_WRITE | STGM_CREATE | STGM_SHARE_EXCLUSIVE,
213+
0,
214+
0,
215+
&pStream
216+
);
217+
218+
if (SUCCEEDED(hr) && pStream != nullptr)
219+
{
220+
T info {};
221+
ULONG written = 0;
222+
hr = pStream->Write(&info, sizeof(info), &written);
223+
ret = SUCCEEDED(hr) && written == sizeof(info);
224+
pStream->Release();
225+
}
226+
227+
return ret;
228+
}
229+
230+
231+
template<typename T>
232+
std::optional<T> ReadFromStorage(IStorage* pStorage)
233+
{
234+
IStream* pStream = nullptr;
235+
bool hasValue = false;
236+
HRESULT hr = pStorage->OpenStream(
237+
T::SaveName,
238+
NULL,
239+
STGM_READ | STGM_SHARE_EXCLUSIVE,
240+
0,
241+
&pStream
242+
);
243+
244+
T info;
245+
246+
if (SUCCEEDED(hr) && pStream != nullptr)
247+
{
248+
ULONG read = 0;
249+
hr = pStream->Read(&info, sizeof(info), &read);
250+
hasValue = SUCCEEDED(hr) && read == sizeof(info);
251+
252+
pStream->Release();
253+
}
254+
255+
return hasValue ? std::make_optional(info) : std::nullopt;
256+
}
257+
258+
}
259+
260+
DEFINE_HOOK(0x559921, LoadOptionsClass_FillList_FilterFiles, 0x6)
261+
{
262+
GET(FileEntryClass*, pEntry, EBP);
263+
enum { NullThisEntry = 0x559959 };
264+
265+
// there was a qsort later and filters out these but we could have just removed them right here
266+
if (pEntry->IsWrongVersion || !pEntry->IsValid)
267+
{
268+
GameDelete(pEntry);
269+
return NullThisEntry;
270+
};
271+
272+
static OLECHAR wNameBuffer[0x100] {};
273+
SavedGames::FormatPath(Main::readBuffer, pEntry->Filename.data());
274+
MultiByteToWideChar(CP_UTF8, 0, Main::readBuffer, -1, wNameBuffer, std::size(wNameBuffer));
275+
IStorage* pStorage = nullptr;
276+
bool shouldDelete = false;
277+
if (SUCCEEDED(StgOpenStorage(wNameBuffer, NULL,
278+
STGM_READWRITE | STGM_SHARE_EXCLUSIVE,
279+
0, 0, &pStorage)
280+
))
281+
{
282+
auto id = SavedGames::ReadFromStorage<SavedGames::CustomMissionID>(pStorage);
283+
284+
if (Spawner::GetConfig()->CustomMissionID != id.value_or(0))
285+
shouldDelete = true;
286+
}
287+
288+
if (pStorage)
289+
pStorage->Release();
290+
291+
if (shouldDelete)
292+
{
293+
GameDelete(pEntry);
294+
return NullThisEntry;
295+
}
296+
297+
return 0;
298+
}
299+
300+
DEFINE_HOOK(0x683AFE, StartNewScenario_ClearCounter, 0x6)
301+
{
302+
SavedGames::HowManyTimesISavedForThisScenario = 0;
303+
return 0;
304+
}
305+
306+
307+
// Write : A la fin
308+
DEFINE_HOOK(0x67D2E3, SaveGame_AdditionalInfoForClient, 0x6)
309+
{
310+
GET_STACK(IStorage*, pStorage, STACK_OFFSET(0x4A0, -0x490));
311+
using namespace SavedGames;
312+
HowManyTimesISavedForThisScenario++;
313+
314+
if (pStorage)
315+
{
316+
if (SessionClass::IsCampaign() && Spawner::GetConfig()->CustomMissionID)
317+
AppendToStorage<CustomMissionID>(pStorage);
318+
if (AppendToStorage<ExtraMetaInfo>(pStorage))
319+
Debug::Log("[Spawner] Extra meta info appended on sav file\n");
320+
}
321+
322+
return 0;
323+
}
324+
325+
// Read : Au debut
326+
DEFINE_HOOK(0x67E4DC, LoadGame_AdditionalInfoForClient, 0x7)
327+
{
328+
LEA_STACK(const wchar_t*, filename, STACK_OFFSET(0x518, -0x4F4));
329+
IStorage* pStorage = nullptr;
330+
using namespace SavedGames;
331+
332+
if (SUCCEEDED(StgOpenStorage(filename, NULL,
333+
STGM_READWRITE | STGM_SHARE_EXCLUSIVE,
334+
0, 0, &pStorage)
335+
))
336+
{
337+
if (auto id = ReadFromStorage<CustomMissionID>(pStorage))
338+
{
339+
int num = id->Number;
340+
Debug::Log("[Spawner] sav file CustomMissionID = %d\n", num);
341+
Spawner::GetConfig()->CustomMissionID = num;
342+
ScenarioClass::Instance->EndOfGame = true;
343+
}
344+
else
345+
{
346+
Spawner::GetConfig()->CustomMissionID = 0;
347+
}
348+
349+
if (auto info = ReadFromStorage<ExtraMetaInfo>(pStorage))
350+
{
351+
Debug::Log("[Spawner] CurrentFrame = %d, TechnoCount = %d, HowManyTimesSaved = %d \n"
352+
, info->CurrentFrame
353+
, info->TechnoCount
354+
, info->SavedCount
355+
);
356+
HowManyTimesISavedForThisScenario = info->SavedCount;
357+
}
358+
}
359+
if (pStorage)
360+
pStorage->Release();
361+
362+
return 0;
363+
}

src/Spawner/Spawner.Config.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ void SpawnerConfig::LoadFromINIFile(CCINIClass* pINI)
5555
LoadSaveGame = pINI->ReadBool(pSettingsSection, "LoadSaveGame", LoadSaveGame);
5656
/* SavedGameDir */ pINI->ReadString(pSettingsSection, "SavedGameDir", SavedGameDir, SavedGameDir, sizeof(SavedGameDir));
5757
/* SaveGameName */ pINI->ReadString(pSettingsSection, "SaveGameName", SaveGameName, SaveGameName, sizeof(SaveGameName));
58+
CustomMissionID = pINI->ReadInteger(pSettingsSection, "CustomMissionID", 0);
5859

5960
{ // Scenario Options
6061
Seed = pINI->ReadInteger(pSettingsSection, "Seed", Seed);

src/Spawner/Spawner.Config.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ class SpawnerConfig
9494
bool LoadSaveGame;
9595
char SavedGameDir[MAX_PATH]; // Nested paths are also supported, e.g. "Saved Games\\Yuri's Revenge"
9696
char SaveGameName[60];
97+
int CustomMissionID;
9798

9899
// Scenario Options
99100
int Seed;
@@ -161,6 +162,7 @@ class SpawnerConfig
161162
, LoadSaveGame { false }
162163
, SavedGameDir { "Saved Games" }
163164
, SaveGameName { "" }
165+
, CustomMissionID { 0 }
164166

165167
// Scenario Options
166168
, Seed { 0 }

src/Spawner/Spawner.cpp

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "ProtocolZero.LatencyLevel.h"
2525
#include <Utilities/Debug.h>
2626
#include <Utilities/DumperTypes.h>
27+
#include <Utilities/Patch.h>
2728

2829
#include <GameOptionsClass.h>
2930
#include <GameStrings.h>
@@ -294,7 +295,18 @@ bool Spawner::StartNewScenario(const char* pScenarioName)
294295
if (SessionClass::IsCampaign())
295296
{
296297
pGameModeOptions->Crates = true;
297-
return ScenarioClass::StartScenario(pScenarioName, 1, 0);
298+
299+
// Rename MISSIONMD.INI to this
300+
// because Ares has LoadScreenText.Color and Phobos has Starkku's PR #1145
301+
if (Spawner::Config->CustomMissionID) // before parsing
302+
Patch::Apply_RAW(0x839724, "Spawn.ini");
303+
304+
bool result = ScenarioClass::StartScenario(pScenarioName, 1, 0);
305+
306+
if (Spawner::Config->CustomMissionID) // after parsing
307+
ScenarioClass::Instance->EndOfGame = true;
308+
309+
return result;
298310
}
299311
else if (SessionClass::IsSkirmish())
300312
{

0 commit comments

Comments
 (0)