Skip to content

Commit ba33dd3

Browse files
committed
merge
1 parent 4730aad commit ba33dd3

File tree

4 files changed

+203
-1
lines changed

4 files changed

+203
-1
lines changed

src/Misc/SavedGamesInSubdir.cpp

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

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)