Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Spawner.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
<!-- Compiled files -->
<ItemGroup>
<!-- Root -->
<ClCompile Include="src\Misc\NoSaveLoadMode.cpp" />
<ClCompile Include="src\UI\Dialogs.cpp" />
<ClCompile Include="src\UI\Hooks.cpp" />
<ResourceCompile Include="$(ThisDir)\src\version.rc" />
Expand Down
104 changes: 104 additions & 0 deletions src/Misc/NoSaveLoadMode.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
#include <Utilities/Macro.h>
#include <Spawner/Spawner.h>
#include <WWMessageBox.h>
#include <LoadProgressManager.h>

const wchar_t* Fetch_CSF_Text(const char* label, const wchar_t* defaultText)
{
std::wstring_view msg = StringTable::LoadString(label);
if (msg.empty() || msg.starts_with(L"MISSING"))
return defaultText;
return msg.data();
}

DEFINE_HOOK(0x686089, DoLose_RetryDialogForCampaigns, 0x7)
{
if (!Spawner::GetConfig()->DisableSaveLoad) return 0;
enum { OK = 0x6860F6, Cancel = 0x6860EE};
if (WWMessageBox::Instance.Process(
Fetch_CSF_Text("TXT_HARDCORE_FAILURE", L"GG"),
StringTable::LoadString("GUI:Restart"),
StringTable::LoadString("GUI:Leave"), nullptr) == WWMessageBox::Result::OK)
return OK;

return Cancel;
}

// disable load, save and delete buttons on the ingame menu
DEFINE_HOOK(0x4F17F6, sub_4F1720_DisableSaves, 0x6)
{
if (!Spawner::GetConfig()->DisableSaveLoad) return 0;
GET(HWND, hDlg, EBP);

HWND hLoad = GetDlgItem(hDlg, 1310);
SendMessageA(hLoad, 1202, 0, (LPARAM)Fetch_CSF_Text("GUI:CantLoad", L"Cannot Load"));
EnableWindow(hLoad, FALSE);
HWND hSave = GetDlgItem(hDlg, 1311);
SendMessageA(hSave, 1202, 0, (LPARAM)Fetch_CSF_Text("GUI:CantSave", L"Cannot Save"));
EnableWindow(hSave, FALSE);
HWND hDel = GetDlgItem(hDlg, 1312);
SendMessageA(hDel, 1202, 0, (LPARAM)L"----");
EnableWindow(hDel, FALSE);

return 0x4F1834;
}

std::wstring HardCoreText {};
DEFINE_HOOK(0x553076, LoadProgressMgr_Draw_ExtraText, 0x5)
{
GET(LoadProgressManager*, self, EBP);
if (!Spawner::GetConfig()->DisableSaveLoad) return 0;

Point2D pos
{
self->TitleBarRect.X + self->TitleBarRect.Width - 100,
self->TitleBarRect.Y + 10
};
if (HardCoreText.empty())
HardCoreText = Fetch_CSF_Text("TXT_HARDCORE_MODE", L"HardCore");
LEA_STACK(RectangleStruct*, pBnd, STACK_OFFSET(0x1268, -0x1204));
if (auto logo = FileSystem::LoadSHPFile("hardcorelogo.shp"))
{
self->ProgressSurface->DrawSHP(FileSystem::PALETTE_PAL, logo, 0, &pos, pBnd, BlitterFlags::bf_400, 0, 0, ZGradient::Ground, 1000, 0, nullptr, 0, 0, 0);
}
else
{
self->ProgressSurface->DrawText(HardCoreText.c_str(), &pos, COLOR_RED);
}

return 0;
}

DEFINE_HOOK(0x68A107, ScenarioCass_ReadMapINI_eog, 0x6)
{
if (!Spawner::GetConfig()->DisableSaveLoad)
return 0;

GET(ScenarioClass*, sce, ESI);
sce->EndOfGame = true;
return 0x68A125;
}

DEFINE_HOOK(0x6D0E20, TabClass_Draw_ccb, 0x6)
{
if (HardCoreText.empty()) return 0;
auto rect = DSurface::Temp->GetRect();
auto wanted = Drawing::GetTextDimensions(HardCoreText.c_str(), { 0,0 }, 0);
Point2D pt {
rect.Width - wanted.Width - 40,
rect.Height - 24
};
DSurface::Temp->DrawText(HardCoreText.c_str(), &pt, COLOR_WHITE);
return 0;
}

DEFINE_HOOK(0x6C9357, MissionEndDlg_HardCoreExitText, 0x5)
{
if (!Spawner::GetConfig()->DisableSaveLoad) return 0;
GET(HWND, hdlg, EBX);

auto btnCtn = GetDlgItem(hdlg, 1059);
SendMessageA(btnCtn, 1202, 0, (LPARAM)StringTable::LoadString("GUI:ExitGame"));

return 0;
}
1 change: 1 addition & 0 deletions src/Spawner/Spawner.Config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ void SpawnerConfig::LoadFromINIFile(CCINIClass* pINI)
ContinueWithoutHumans = pINI->ReadBool(pSettingsSection, "ContinueWithoutHumans", ContinueWithoutHumans);
DefeatedBecomesObserver = pINI->ReadBool(pSettingsSection, "DefeatedBecomesObserver", DefeatedBecomesObserver);
Observer_ShowAIOnSidebar = pINI->ReadBool(pSettingsSection, "Observer.ShowAIOnSidebar", Observer_ShowAIOnSidebar);
DisableSaveLoad = pINI->ReadBool(pSettingsSection, "DisableSaveLoad", false) && IsCampaign && !LoadSaveGame;
#ifdef IS_CNCNET_YR_VER
DisableChat = pINI->ReadBool(pSettingsSection, "DisableChat", DisableChat);
#endif
Expand Down
2 changes: 2 additions & 0 deletions src/Spawner/Spawner.Config.h
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ class SpawnerConfig
bool ContinueWithoutHumans;
bool DefeatedBecomesObserver;
bool Observer_ShowAIOnSidebar;
bool DisableSaveLoad;
#ifdef IS_CNCNET_YR_VER
bool DisableChat;
#endif
Expand Down Expand Up @@ -245,6 +246,7 @@ class SpawnerConfig
, ContinueWithoutHumans { false }
, DefeatedBecomesObserver { false }
, Observer_ShowAIOnSidebar { false }
, DisableSaveLoad { false }
#ifdef IS_CNCNET_YR_VER
, DisableChat { false }
#endif
Expand Down
7 changes: 7 additions & 0 deletions src/Spawner/Spawner.Hook.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,13 @@ DEFINE_HOOK(0x6BD7C5, WinMain_SpawnerInit, 0x6)

// Skip load *.PKT, *.YRO and *.YRM map files
Patch::Apply_LJMP(0x699AD9, 0x69A1B2); // SessionClass::Read_Scenario_Descriptions

if (Spawner::GetConfig()->DisableSaveLoad)
{
Patch::Apply_LJMP(0x55DBCD, 0x55DC99); // Disable MainLoop_SaveGame (Spawner&Phobos)
Patch::Apply_RAW(0x67CEF0, { 0x33,0xC0,0xC3 }); // Corrupt savegame function
Patch::Apply_TYPED(0x83D560, { (DWORD)std::rand() }); // Corrupt save game magicn
}
}

return 0;
Expand Down
Loading