diff --git a/include/core/interfaces.hpp b/include/core/interfaces.hpp index 8f68da5d..7a7626d3 100644 --- a/include/core/interfaces.hpp +++ b/include/core/interfaces.hpp @@ -96,3 +96,4 @@ extern IFileSystem *g_IFileSystem; extern IMDLCache *g_IMDLCache; void CreateInterfaces(); +void CreateEarlyInterfaces(); diff --git a/include/core/sharedobj.hpp b/include/core/sharedobj.hpp index 0f06aabc..40ad07ec 100644 --- a/include/core/sharedobj.hpp +++ b/include/core/sharedobj.hpp @@ -54,5 +54,6 @@ SharedObject &studiorender(); SharedObject &libsdl(); #endif +void LoadEarlyObjects(); void LoadAllSharedObjects(); } // namespace sharedobj diff --git a/include/init.hpp b/include/init.hpp index 79b437b3..8d9999d7 100755 --- a/include/init.hpp +++ b/include/init.hpp @@ -10,9 +10,16 @@ #include std::stack &init_stack(); +std::stack &init_stack_early(); class InitRoutine { public: InitRoutine(void (*func)()); }; + +class InitRoutineEarly +{ +public: + InitRoutineEarly(void (*func)()); +}; diff --git a/src/core/init.cpp b/src/core/init.cpp index e805a46b..08a36568 100755 --- a/src/core/init.cpp +++ b/src/core/init.cpp @@ -17,3 +17,14 @@ InitRoutine::InitRoutine(void (*func)()) { init_stack().push(func); } + +std::stack &init_stack_early() +{ + static std::stack stack{}; + return stack; +} + +InitRoutineEarly::InitRoutineEarly(void (*func)()) +{ + init_stack_early().push(func); +} diff --git a/src/core/interfaces.cpp b/src/core/interfaces.cpp index b8480a96..0eee1182 100644 --- a/src/core/interfaces.cpp +++ b/src/core/interfaces.cpp @@ -84,6 +84,11 @@ template T *BruteforceInterface(std::string name, sharedobj::Shared extern "C" typedef HSteamPipe (*GetHSteamPipe_t)(); extern "C" typedef HSteamUser (*GetHSteamUser_t)(); +void CreateEarlyInterfaces() +{ + g_IFileSystem = BruteforceInterface("VFileSystem", sharedobj::filesystem_stdio()); +} + void CreateInterfaces() { g_ICvar = BruteforceInterface("VEngineCvar", sharedobj::vstdlib()); @@ -141,8 +146,7 @@ void CreateInterfaces() g_IModelInfo = BruteforceInterface("VModelInfoClient", sharedobj::engine()); g_IBaseClientState = *(reinterpret_cast(gSignatures.GetEngineSignature("C7 04 24 ? ? ? ? E8 ? ? ? ? C7 04 24 ? ? ? ? 89 44 24 04 E8 ? ? ? ? A1 ? ? ? ?") + 3)); logging::Info("BaseClientState: 0x%08x", g_IBaseClientState); - while (!g_IAchievementMgr) - g_IAchievementMgr = g_IEngine->GetAchievementMgr(); + g_IAchievementMgr = g_IEngine->GetAchievementMgr(); g_ISteamUserStats = g_ISteamClient->GetISteamUserStats(su, sp, "STEAMUSERSTATS_INTERFACE_VERSION011"); IF_GAME(IsTF2()) { @@ -153,7 +157,6 @@ void CreateInterfaces() g_pGameRules = *reinterpret_cast(g_pGameRules_sig + 8); } g_IMaterialSystem = BruteforceInterface("VMaterialSystem", sharedobj::materialsystem()); - g_IFileSystem = BruteforceInterface("VFileSystem", sharedobj::filesystem_stdio()); g_IMDLCache = BruteforceInterface("MDLCache", sharedobj::datacache()); g_IPanel = BruteforceInterface("VGUI_Panel", sharedobj::vgui2()); diff --git a/src/core/sharedobj.cpp b/src/core/sharedobj.cpp index e9bc6b1e..77a6a2a9 100644 --- a/src/core/sharedobj.cpp +++ b/src/core/sharedobj.cpp @@ -84,19 +84,30 @@ void *SharedObject::CreateInterface(const std::string &interface) return (void *) (fptr(interface.c_str(), nullptr)); } +void LoadEarlyObjects() +{ + try + { + engine().Load(); + filesystem_stdio().Load(); + tier0().Load(); + materialsystem().Load(); + } + catch (std::exception &ex) + { + logging::Info("Exception: %s", ex.what()); + } +} + void LoadAllSharedObjects() { try { steamclient().Load(); client().Load(); - engine().Load(); steamapi().Load(); vstdlib().Load(); - tier0().Load(); inputsystem().Load(); - materialsystem().Load(); - filesystem_stdio().Load(); datacache().Load(); vgui2().Load(); #if ENABLE_VISUALS diff --git a/src/hack.cpp b/src/hack.cpp index 3a6f8d32..550b838f 100644 --- a/src/hack.cpp +++ b/src/hack.cpp @@ -301,10 +301,12 @@ static bool FSHook_Precache(const char *pFileName, const char *pPathID) static CatCommand debug_invalidate("invalidate_mdl_cache", "Invalidates MDL cache", []() { g_IBaseClient->InvalidateMdlCache(); }); -static hooks::VMTHook fs_hook, fs_hook2; +static hooks::VMTHook fs_hook{}, fs_hook2{}; static void ReduceRamUsage() { + if (fs_hook.IsHooked(reinterpret_cast(g_IFileSystem))) + return; /* TO DO: Improves load speeds but doesn't reduce memory usage a lot * It seems engine still allocates significant parts without them * being really used @@ -329,7 +331,11 @@ static void ReduceRamUsage() * spam related to mdl not being able to play sequence that it * cannot play on error.mdl */ - static BytePatch playSequence{ gSignatures.GetClientSignature, "55 89 E5 57 56 53 83 EC ? 8B 75 0C 8B 5D 08 85 F6 74 ? 83 BB", 0x00, { 0xC3 } }; + if (g_IBaseClient) + { + static BytePatch playSequence{ gSignatures.GetClientSignature, "55 89 E5 57 56 53 83 EC ? 8B 75 0C 8B 5D 08 85 F6 74 ? 83 BB", 0x00, { 0xC3 } }; + playSequence.Patch(); + } #if 0 /* Same explanation as above, but spams about certain particles not loaded */ static BytePatch particleCreate{ gSignatures.GetClientSignature, @@ -348,15 +354,28 @@ static void ReduceRamUsage() particlePrecache.Patch(); particleCreating.Patch(); #endif - playSequence.Patch(); } static void UnHookFs() { fs_hook.Release(); fs_hook2.Release(); - g_IBaseClient->InvalidateMdlCache(); + if (g_IBaseClient) + g_IBaseClient->InvalidateMdlCache(); } +#if !ENABLE_VISUALS +static InitRoutineEarly nullify_textmode([]() { + ReduceRamUsage(); + static auto addr1 = e8call_direct(gSignatures.GetEngineSignature("E8 ? ? ? ? 8B 93 ? ? ? ? 85 D2 0F 84 ? ? ? ?")) + 0x18; + static auto addr2 = sharedobj::materialsystem().Pointer(0x3EC08); + + static BytePatch patch1(addr1, { 0x81, 0xC4, 0x6C, 0x20, 0x00, 0x00, 0x5B, 0x5E, 0x5F, 0x5D, 0xC3 }); + static BytePatch patch2(addr2, { 0x83, 0xC4, 0x50, 0x5B, 0x5E, 0x5D, 0xC3 }); + + patch1.Patch(); + patch2.Patch(); +}); +#endif static void InitRandom() { @@ -413,15 +432,24 @@ free(logname);*/ #endif /* TEXTMODE */ logging::Info("Initializing..."); InitRandom(); - sharedobj::LoadAllSharedObjects(); - CreateInterfaces(); - CDumper dumper; + sharedobj::LoadEarlyObjects(); + CreateEarlyInterfaces(); + logging::Info("Clearing Early initializer stack"); null_graphics.installChangeCallback([](settings::VariableBase &, bool after) { if (after) ReduceRamUsage(); else UnHookFs(); }); + while (!init_stack_early().empty()) + { + init_stack_early().top()(); + init_stack_early().pop(); + } + logging::Info("Early Initializer stack done"); + sharedobj::LoadAllSharedObjects(); + CreateInterfaces(); + CDumper dumper; dumper.SaveDump(); logging::Info("Is TF2? %d", IsTF2()); logging::Info("Is TF2C? %d", IsTF2C()); diff --git a/src/hacks/Achievement.cpp b/src/hacks/Achievement.cpp index a6ff6c1b..fe887192 100644 --- a/src/hacks/Achievement.cpp +++ b/src/hacks/Achievement.cpp @@ -35,8 +35,20 @@ static settings::Int equip_secondary{ "achievement.equip-secondary", "0" }; static settings::Int equip_melee{ "achievement.equip-melee", "0" }; static settings::Int equip_pda2{ "achievement.equip-pda2", "0" }; +bool checkachmngr() +{ + if (!g_IAchievementMgr) + { + g_IAchievementMgr = g_IEngine->GetAchievementMgr(); + if (!g_IAchievementMgr) + return false; + } + return true; +} void Lock() { + if (!checkachmngr()) + return; if (safety) { ConColorMsg({ 255, 0, 0, 255 }, "Switch `cat set achievement.safety` to false before using any achievement commands!\n"); @@ -53,6 +65,8 @@ void Lock() void Unlock() { + if (!checkachmngr()) + return; /*auto Invmng = re::CTFInventoryManager::GTFInventoryManager(); auto Inv = re::CTFPlayerInventory::GTFPlayerInventory(); auto Item = Inv->GetFirstItemOfItemDef(59); @@ -70,6 +84,8 @@ void Unlock() } CatCommand dump_achievement("achievement_dump", "Dump achievements to file (development)", []() { + if (!checkachmngr()) + return; std::ofstream out("/tmp/cathook_achievements.txt", std::ios::out); if (out.bad()) return; @@ -80,6 +96,8 @@ CatCommand dump_achievement("achievement_dump", "Dump achievements to file (deve out.close(); }); CatCommand unlock_single("achievement_unlock_single", "Unlocks single achievement by ID", [](const CCommand &args) { + if (!checkachmngr()) + return; char *out = nullptr; int id = strtol(args.Arg(1), &out, 10); if (out == args.Arg(1)) @@ -95,6 +113,8 @@ CatCommand unlock_single("achievement_unlock_single", "Unlocks single achievemen }); // For some reason it SEGV's when I try to GetAchievementByID(); CatCommand lock_single("achievement_lock_single", "Locks single achievement by INDEX!", [](const CCommand &args) { + if (!checkachmngr()) + return; if (args.ArgC() < 1) { logging::Info("Actually provide an index"); @@ -143,6 +163,8 @@ std::vector equip_queue; void unlock_achievements_and_accept(std::vector items) { + if (!checkachmngr()) + return; for (auto id : items) { IAchievement *ach = reinterpret_cast(g_IAchievementMgr->GetAchievementByID(id));