#include #include "loader/component_loader.hpp" #include "game/dvars.hpp" #include "game/engine/scoped_critical_section.hpp" #include #include "command.hpp" #include "scheduler.hpp" namespace debug { namespace { void com_assert_f() { assert("a" && false); } void com_bug_f(const command::params& params) { char new_file_name[MAX_PATH]{}; char to_ospath[MAX_PATH]{}; char from_ospath[MAX_PATH]{}; const char* bug{}; if (!*game::logfile) { game::Com_PrintError(game::CON_CHANNEL_ERROR, "CopyFile failed: logfile wasn't opened\n"); return; } if (params.size() == 2) { bug = params.get(1); } else { assert(dvars::bug_name); bug = dvars::bug_name->current.string; } game::Com_sprintf(new_file_name, sizeof(new_file_name), "%s_%s.log", bug, game::Live_GetLocalClientName(game::CONTROLLER_INDEX_0)); game::engine::scoped_critical_section lock(game::CRITSECT_CONSOLE, game::SCOPED_CRITSECT_NORMAL); if (*game::logfile) { game::FS_FCloseFile(*game::logfile); *game::logfile = 0; } game::FS_BuildOSPath(std::filesystem::current_path().string().c_str(), "", "console.log", from_ospath); game::FS_BuildOSPath(std::filesystem::current_path().string().c_str(), "", new_file_name, to_ospath); const auto result = CopyFileA(from_ospath, to_ospath, 0); game::Com_OpenLogFile(); lock.leave_crit_sect(); if (!result) { game::Com_PrintError(game::CON_CHANNEL_ERROR, "CopyFile failed(%d) %s %s\n", GetLastError(), "console.log", new_file_name); } } void com_bug_name_inc_f() { char buf[MAX_PATH]{}; if (std::strlen(dvars::bug_name->current.string) < 4) { game::Dvar_SetString(dvars::bug_name, "bug0"); return; } if (std::strncmp(dvars::bug_name->current.string, "bug", 3) != 0) { game::Dvar_SetString(dvars::bug_name, "bug0"); return; } const auto n = std::strtol(dvars::bug_name->current.string + 3, nullptr, 10); game::Com_sprintf(buf, sizeof(buf), "bug%d", n + 1); game::Dvar_SetString(dvars::bug_name, buf); } void g_print_fast_file_errors(const char* fastfile) { assert(fastfile); const auto rawfile_buf_large = std::make_unique(0x18000); auto* rawfile_buf = rawfile_buf_large.get(); auto* text = game::DB_ReadRawFile(fastfile, rawfile_buf, 0x18000); assert(text); if (*text) { game::Com_PrintError(game::CON_CHANNEL_ERROR, "There were errors when building fast file '%s'\n", fastfile); game::Com_PrintError(game::CON_CHANNEL_ERROR, "%s", text); } else { game::Com_Printf(game::CON_CHANNEL_DONT_FILTER, "There were no errors when building fast file '%s'\n", fastfile); } } void g_init_game_stub() { utils::hook::invoke(0x4D6410); g_print_fast_file_errors("code_post_gfx"); g_print_fast_file_errors("common"); g_print_fast_file_errors((*dvars::sv_mapname)->current.string); } } // namespace class component final : public component_interface { public: static_assert(sizeof(RTL_CRITICAL_SECTION) == 0x18); void post_load() override { utils::hook::set(0x604203, com_assert_f); scheduler::once( [] { dvars::bug_name = game::Dvar_RegisterString( "bug_name", "bug0", game::DVAR_CHEAT | game::DVAR_CODINFO, "Name appended to the copied console log"); }, scheduler::pipeline::main); command::add("bug", com_bug_f); command::add("bug_name_inc", com_bug_name_inc_f); #ifdef _DEBUG utils::hook(0x4C79DF, g_init_game_stub, HOOK_CALL) .install() // hook* ->quick(); // Scr_FreeEntityList #endif } }; } // namespace debug REGISTER_COMPONENT(debug::component)