From e1389752fc635195f69b2f21a1e36cf6e63f03d2 Mon Sep 17 00:00:00 2001 From: Trsdy <914137150@qq.com> Date: Sat, 1 Aug 2026 21:05:14 +0800 Subject: [PATCH] shit --- src/Misc/SavedGamesInSubdir.cpp | 115 ++++++++++++++++++++++++++++++++ src/Spawner/Spawner.Config.cpp | 1 + src/Spawner/Spawner.Config.h | 2 + src/Spawner/Spawner.Hook.cpp | 10 +++ src/Spawner/Spawner.cpp | 2 +- 5 files changed, 129 insertions(+), 1 deletion(-) diff --git a/src/Misc/SavedGamesInSubdir.cpp b/src/Misc/SavedGamesInSubdir.cpp index 6a9ce5bf..2fe59d4e 100644 --- a/src/Misc/SavedGamesInSubdir.cpp +++ b/src/Misc/SavedGamesInSubdir.cpp @@ -343,3 +343,118 @@ DEFINE_HOOK(0x55DC85, MainLoop_SaveGame_SanitizeFilename, 0x7) return 0x55DC90; } + +#pragma region nosaveload +#include +#include +#include + +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; + + WWMessageBox::Instance.Process( + Fetch_CSF_Text("TXT_HARDCORE_FAILURE", L"GG"), + StringTable::LoadString("TXT_OK"), nullptr, nullptr); + + return 0x6860EE; +} + +// disable load, save and delete buttons on the ingame menu + +void PatchDialog181() +{ + auto h = Game::hInstance; + auto r = FindResourceA(h, MAKEINTRESOURCE(181), RT_DIALOG); + if (!r) return; + + auto sz = SizeofResource(h, r); + auto p = (BYTE*)LockResource(LoadResource(h, r)); + if (!p) return; + + DWORD old; + VirtualProtect(p, sz, PAGE_READWRITE, &old); + using namespace std::literals; + for (DWORD i = 0; i < sz; i += 2) + for (auto [f, t] : { std::pair{L"GUI:SaveGame"sv, L"GUI:CantSave"sv}, + {L"GUI:LoadGame"sv, L"GUI:CantLoad"sv} }) + { + if (!wcsncmp((wchar_t*)(p + i), f.data(), f.size())) + { + memcpy(p + i, t.data(), t.size() * 2); + memset(p + i + t.size() * 2, 0, (f.size() - t.size()) * 2); + } + } + VirtualProtect(p, sz, old, &old); +} + +DEFINE_HOOK(0x4F17F6, sub_4F1720_DisableSaves, 0x6) +{ + if (!Spawner::GetConfig()->DisableSaveLoad) return 0; + GET(HWND, hDlg, EBP); + + enum { LoadGameButton = 1310, DeleteGameButton = 1312 }; + + for (int item = LoadGameButton; item <= DeleteGameButton; ++item) + { + if (HWND hItem = GetDlgItem(hDlg, item)) + EnableWindow(hItem, 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(0x683C14, ScenarioClass_StartScenario_IntroMovie, 0x8) +{ + GET_STACK(DWORD const, caller, 0xFC); + if (caller == 0x686501 || caller == 0x68629B) + return 0x683CF7; + return 0; +} + +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; +} +#pragma endregion diff --git a/src/Spawner/Spawner.Config.cpp b/src/Spawner/Spawner.Config.cpp index 205eca9d..62d61f44 100644 --- a/src/Spawner/Spawner.Config.cpp +++ b/src/Spawner/Spawner.Config.cpp @@ -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 diff --git a/src/Spawner/Spawner.Config.h b/src/Spawner/Spawner.Config.h index fca7b2be..35191153 100644 --- a/src/Spawner/Spawner.Config.h +++ b/src/Spawner/Spawner.Config.h @@ -147,6 +147,7 @@ class SpawnerConfig bool ContinueWithoutHumans; bool DefeatedBecomesObserver; bool Observer_ShowAIOnSidebar; + bool DisableSaveLoad; #ifdef IS_CNCNET_YR_VER bool DisableChat; #endif @@ -245,6 +246,7 @@ class SpawnerConfig , ContinueWithoutHumans { false } , DefeatedBecomesObserver { false } , Observer_ShowAIOnSidebar { false } + , DisableSaveLoad { false } #ifdef IS_CNCNET_YR_VER , DisableChat { false } #endif diff --git a/src/Spawner/Spawner.Hook.cpp b/src/Spawner/Spawner.Hook.cpp index 0659dcad..bdb8e618 100644 --- a/src/Spawner/Spawner.Hook.cpp +++ b/src/Spawner/Spawner.Hook.cpp @@ -28,6 +28,8 @@ #include #include +void PatchDialog181(); + DEFINE_HOOK(0x6BD7C5, WinMain_SpawnerInit, 0x6) { if (Spawner::Enabled) @@ -73,6 +75,14 @@ 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 + PatchDialog181(); + } } return 0; diff --git a/src/Spawner/Spawner.cpp b/src/Spawner/Spawner.cpp index fc15e522..b9801e41 100644 --- a/src/Spawner/Spawner.cpp +++ b/src/Spawner/Spawner.cpp @@ -315,7 +315,7 @@ bool Spawner::StartScenario(const char* pScenarioName) bool result = ScenarioClass::StartScenario(pScenarioName, 1, 0); - if (Spawner::Config->CustomMissionID != 0) // after parsing + if (Spawner::Config->CustomMissionID != 0 || Spawner::Config->DisableSaveLoad) // after parsing ScenarioClass::Instance->EndOfGame = true; return result;