Merge pull request #77 from h1-mod/1.15

1.15
This commit is contained in:
fed 2022-05-30 19:44:35 +00:00 committed by GitHub
commit da90d37846
99 changed files with 6347 additions and 5372 deletions

View File

@ -4,8 +4,7 @@
<img alig src="https://cdn.discordapp.com/attachments/895680402142941194/938488726391242842/splash.png"/> <img alig src="https://cdn.discordapp.com/attachments/895680402142941194/938488726391242842/splash.png"/>
</p> </p>
An experimental client for Call of Duty Modern Warfare Remastered (version `1.4.814118.0-0.32767`). An client for Call of Duty: Modern Warfare Remastered (version `1.15.1251288.0-0.32767`).
Research for the latest Steam version of the game is on the [1.15](https://github.com/h1-mod/h1-mod/tree/1.15) branch.
[This project is based on S1x.](https://github.com/XLabsProject/s1x-client) [This project is based on S1x.](https://github.com/XLabsProject/s1x-client)

View File

@ -93,6 +93,7 @@ function infoelement(data)
local value = LUI.UIText.new({ local value = LUI.UIText.new({
left = left + 5, left = left + 5,
top = textoffsety, top = textoffsety,
font = labelfont.Font,
height = textheight, height = textheight,
leftAnchor = true, leftAnchor = true,
topAnchor = true, topAnchor = true,
@ -156,3 +157,4 @@ LUI.onmenuopen("mp_hud", function(hud)
hud.static:addElement(infobar) hud.static:addElement(infobar)
end) end)

View File

@ -2,15 +2,13 @@ game:addlocalizedstring("MENU_MODS", "MODS")
game:addlocalizedstring("MENU_MODS_DESC", "Load installed mods.") game:addlocalizedstring("MENU_MODS_DESC", "Load installed mods.")
game:addlocalizedstring("LUA_MENU_MOD_DESC_DEFAULT", "Load &&1.") game:addlocalizedstring("LUA_MENU_MOD_DESC_DEFAULT", "Load &&1.")
game:addlocalizedstring("LUA_MENU_MOD_DESC", "&&1\nAuthor: &&2\nVersion: &&3") game:addlocalizedstring("LUA_MENU_MOD_DESC", "&&1\nAuthor: &&2\nVersion: &&3")
game:addlocalizedstring("LUA_MENU_OPEN_STORE", "Open store")
game:addlocalizedstring("LUA_MENU_OPEN_STORE_DESC", "Download and install mods.")
game:addlocalizedstring("LUA_MENU_LOADED_MOD", "Loaded mod: ^2&&1") game:addlocalizedstring("LUA_MENU_LOADED_MOD", "Loaded mod: ^2&&1")
game:addlocalizedstring("LUA_MENU_AVAILABLE_MODS", "Available mods") game:addlocalizedstring("LUA_MENU_AVAILABLE_MODS", "Available mods")
game:addlocalizedstring("LUA_MENU_UNLOAD", "Unload") game:addlocalizedstring("LUA_MENU_UNLOAD", "Unload")
game:addlocalizedstring("LUA_MENU_UNLOAD_DESC", "Unload the currently loaded mod.") game:addlocalizedstring("LUA_MENU_UNLOAD_DESC", "Unload the currently loaded mod.")
function createdivider(menu, text) function createdivider(menu, text)
local element = LUI.UIElement.new( { local element = LUI.UIElement.new({
leftAnchor = true, leftAnchor = true,
rightAnchor = true, rightAnchor = true,
left = 0, left = 0,
@ -26,7 +24,10 @@ function createdivider(menu, text)
title_bar_text = Engine.ToUpperCase(text) title_bar_text = Engine.ToUpperCase(text)
})) }))
element.text = element:getFirstChild():getFirstChild():getNextSibling()
menu.list:addElement(element) menu.list:addElement(element)
return element
end end
function string:truncate(length) function string:truncate(length)
@ -48,12 +49,17 @@ LUI.addmenubutton("main_campaign", {
function getmodname(path) function getmodname(path)
local name = path local name = path
local desc = Engine.Localize("@LUA_MENU_MOD_DESC_DEFAULT", name) game:addlocalizedstring(name, name)
game:addlocalizedstring("LUA_MENU_MOD_DESC_DEFAULT", "Load &&1.")
local desc = Engine.Localize("LUA_MENU_MOD_DESC_DEFAULT", name)
local infofile = path .. "/info.json" local infofile = path .. "/info.json"
if (io.fileexists(infofile)) then if (io.fileexists(infofile)) then
pcall(function() pcall(function()
local data = json.decode(io.readfile(infofile)) local data = json.decode(io.readfile(infofile))
game:addlocalizedstring(data.description, data.description)
game:addlocalizedstring(data.author, data.author)
game:addlocalizedstring(data.version, data.version)
desc = Engine.Localize("@LUA_MENU_MOD_DESC", desc = Engine.Localize("@LUA_MENU_MOD_DESC",
data.description, data.author, data.version) data.description, data.author, data.version)
name = data.name name = data.name
@ -63,7 +69,7 @@ function getmodname(path)
return name, desc return name, desc
end end
LUI.MenuBuilder.m_types_build["mods_menu"] = function(a1) LUI.MenuBuilder.registerType("mods_menu", function(a1)
local menu = LUI.MenuTemplate.new(a1, { local menu = LUI.MenuTemplate.new(a1, {
menu_title = "@MENU_MODS", menu_title = "@MENU_MODS",
exclusiveController = 0, exclusiveController = 0,
@ -74,7 +80,8 @@ LUI.MenuBuilder.m_types_build["mods_menu"] = function(a1)
local modfolder = game:getloadedmod() local modfolder = game:getloadedmod()
if (modfolder ~= "") then if (modfolder ~= "") then
createdivider(menu, Engine.Localize("@LUA_MENU_LOADED_MOD", getmodname(modfolder):truncate(24))) local name = getmodname(modfolder)
createdivider(menu, Engine.Localize("@LUA_MENU_LOADED_MOD", name:truncate(24)))
menu:AddButton("@LUA_MENU_UNLOAD", function() menu:AddButton("@LUA_MENU_UNLOAD", function()
Engine.Exec("unloadmod") Engine.Exec("unloadmod")
@ -113,4 +120,4 @@ LUI.MenuBuilder.m_types_build["mods_menu"] = function(a1)
menu.optionTextInfo = LUI.Options.AddOptionTextInfo(menu) menu.optionTextInfo = LUI.Options.AddOptionTextInfo(menu)
return menu return menu
end end)

View File

@ -27,13 +27,13 @@ function menu_xboxlive(f16_arg0, f16_arg1)
menu:AddCACButton() menu:AddCACButton()
menu:AddBarracksButton() menu:AddBarracksButton()
menu:AddPersonalizationButton() menu:AddPersonalizationButton()
-- menu:AddDepotButton() menu:AddDepotButton()
end end
serverListButton = menu:AddButton("@MENU_PRIVATE_MATCH", MPLobbyOnline.OnPrivateMatch, local privateMatchButton = menu:AddButton("@MENU_PRIVATE_MATCH", MPLobbyOnline.OnPrivateMatch,
MPLobbyOnline.disablePrivateMatchButton) MPLobbyOnline.disablePrivateMatchButton)
serverListButton:rename("menu_xboxlive_private_match") privateMatchButton:rename("menu_xboxlive_private_match")
serverListButton:setDisabledRefreshRate(500) privateMatchButton:setDisabledRefreshRate(500)
if not Engine.IsCoreMode() then if not Engine.IsCoreMode() then
local leaderboardButton = menu:AddButton("@LUA_MENU_LEADERBOARD", "OpLeaderboardMain") local leaderboardButton = menu:AddButton("@LUA_MENU_LEADERBOARD", "OpLeaderboardMain")
leaderboardButton:rename("OperatorMenu_leaderboard") leaderboardButton:rename("OperatorMenu_leaderboard")

View File

@ -1,5 +1,5 @@
if (game:issingleplayer() or not Engine.InFrontend()) then if (game:issingleplayer() or not Engine.InFrontend()) then
return return
end end
game:addlocalizedstring("LUA_MENU_STATS", "Stats") game:addlocalizedstring("LUA_MENU_STATS", "Stats")
@ -7,11 +7,15 @@ game:addlocalizedstring("LUA_MENU_STATS_DESC", "Edit player stats settings.")
game:addlocalizedstring("LUA_MENU_UNLOCKALL_ITEMS", "Unlock all items") game:addlocalizedstring("LUA_MENU_UNLOCKALL_ITEMS", "Unlock all items")
game:addlocalizedstring("LUA_MENU_UNLOCKALL_ITEMS_DESC", game:addlocalizedstring("LUA_MENU_UNLOCKALL_ITEMS_DESC",
"Whether items should be locked based on the player's stats or always unlocked.") "Whether items should be locked based on the player's stats or always unlocked.")
game:addlocalizedstring("LUA_MENU_UNLOCKALL_LOOT", "Unlock all loot")
game:addlocalizedstring("LUA_MENU_UNLOCKALL_LOOT_DESC",
"Whether loot should be locked based on the player's stats or always unlocked.")
game:addlocalizedstring("LUA_MENU_UNLOCKALL_CLASSES", "Unlock all classes") game:addlocalizedstring("LUA_MENU_UNLOCKALL_CLASSES", "Unlock all classes")
game:addlocalizedstring("LUA_MENU_UNLOCKALL_CLASSES_DESC", game:addlocalizedstring("LUA_MENU_UNLOCKALL_CLASSES_DESC",
"Whether classes should be locked based on the player's stats or always unlocked.") "Whether classes should be locked based on the player's stats or always unlocked.")
game:addlocalizedstring("LUA_MENU_PRESTIGE", "Prestige") game:addlocalizedstring("LUA_MENU_PRESTIGE", "Prestige")
game:addlocalizedstring("LUA_MENU_PRESTIGE_DESC", "Edit prestige level.") game:addlocalizedstring("LUA_MENU_PRESTIGE_DESC", "Edit prestige level.")
@ -25,166 +29,143 @@ game:addlocalizedstring("LUA_MENU_SETTINGS", "Settings")
game:addlocalizedstring("LUA_MENU_EDIT_STATS", "Edit Stats") game:addlocalizedstring("LUA_MENU_EDIT_STATS", "Edit Stats")
function createdivider(menu, text) function createdivider(menu, text)
local element = LUI.UIElement.new({ local element = LUI.UIElement.new({
leftAnchor = true, leftAnchor = true,
rightAnchor = true, rightAnchor = true,
left = 0, left = 0,
right = 0, right = 0,
topAnchor = true, topAnchor = true,
bottomAnchor = false, bottomAnchor = false,
top = 0, top = 0,
bottom = 33.33 bottom = 33.33
}) })
element.scrollingToNext = true element.scrollingToNext = true
element:addElement(LUI.MenuBuilder.BuildRegisteredType("h1_option_menu_titlebar", { element:addElement(LUI.MenuBuilder.BuildRegisteredType("h1_option_menu_titlebar", {
title_bar_text = Engine.ToUpperCase(Engine.Localize(text)) title_bar_text = Engine.ToUpperCase(Engine.Localize(text))
})) }))
menu.list:addElement(element) menu.list:addElement(element)
end end
local personalizationbutton = LUI.MPLobbyBase.AddPersonalizationButton local personalizationbutton = LUI.MPLobbyBase.AddPersonalizationButton
LUI.MPLobbyBase.AddPersonalizationButton = function(menu) LUI.MPLobbyBase.AddPersonalizationButton = function(menu)
personalizationbutton(menu) personalizationbutton(menu)
menu:AddButton("@LUA_MENU_STATS", function() menu:AddButton("@LUA_MENU_STATS", function()
LUI.FlowManager.RequestAddMenu(nil, "stats_menu") LUI.FlowManager.RequestAddMenu(nil, "stats_menu")
end) end)
end end
LUI.MenuBuilder.registerType("stats_menu", function(a1) LUI.MenuBuilder.registerType("stats_menu", function(a1)
local menu = LUI.MenuTemplate.new(a1, { local menu = LUI.MenuTemplate.new(a1, {
menu_title = Engine.ToUpperCase(Engine.Localize("@LUA_MENU_STATS")), menu_title = Engine.ToUpperCase(Engine.Localize("@LUA_MENU_STATS")),
menu_width = luiglobals.GenericMenuDims.OptionMenuWidth menu_width = luiglobals.GenericMenuDims.OptionMenuWidth
}) })
createdivider(menu, "@LUA_MENU_SETTINGS") createdivider(menu, "@LUA_MENU_SETTINGS")
LUI.Options.CreateOptionButton(menu, "cg_unlockall_items", "@LUA_MENU_UNLOCKALL_ITEMS", LUI.Options.CreateOptionButton(menu, "cg_unlockall_items", "@LUA_MENU_UNLOCKALL_ITEMS",
"@LUA_MENU_UNLOCKALL_ITEMS_DESC", {{ "@LUA_MENU_UNLOCKALL_ITEMS_DESC", {{
text = "@LUA_MENU_ENABLED", text = "@LUA_MENU_ENABLED",
value = true value = true
}, { }, {
text = "@LUA_MENU_DISABLED", text = "@LUA_MENU_DISABLED",
value = false value = false
}}, nil, nil) }}, nil, nil)
LUI.Options.CreateOptionButton(menu, "cg_unlockall_classes", "@LUA_MENU_UNLOCKALL_CLASSES", LUI.Options.CreateOptionButton(menu, "cg_unlockall_loot", "@LUA_MENU_UNLOCKALL_LOOT",
"@LUA_MENU_UNLOCKALL_CLASSES_DESC", {{ "@LUA_MENU_UNLOCKALL_LOOT_DESC", {{
text = "@LUA_MENU_ENABLED", text = "@LUA_MENU_ENABLED",
value = true value = true
}, { }, {
text = "@LUA_MENU_DISABLED", text = "@LUA_MENU_DISABLED",
value = false value = false
}}, nil, nil) }}, nil, nil)
createdivider(menu, "@LUA_MENU_EDIT_STATS") LUI.Options.CreateOptionButton(menu, "cg_unlockall_classes", "@LUA_MENU_UNLOCKALL_CLASSES",
"@LUA_MENU_UNLOCKALL_CLASSES_DESC", {{
text = "@LUA_MENU_ENABLED",
value = true
}, {
text = "@LUA_MENU_DISABLED",
value = false
}}, nil, nil)
local prestige = Engine.GetPlayerData(0, CoD.StatsGroup.Ranked, "prestige") or 0 createdivider(menu, "@LUA_MENU_EDIT_STATS")
local experience = Engine.GetPlayerData(0, CoD.StatsGroup.Ranked, "experience") or 0
local rank = luiglobals.Lobby.GetRankForXP(experience, prestige)
local saved = true local prestige = Engine.GetPlayerData(0, CoD.StatsGroup.Ranked, "prestige") or 0
local prestigevalue = prestige local experience = Engine.GetPlayerData(0, CoD.StatsGroup.Ranked, "experience") or 0
local rankvalue = rank local rank = Lobby.GetRankForXP(experience, prestige)
local rankbutton = nil
prestigeeditbutton(menu, function(value) prestigeeditbutton(menu, function(value)
prestigevalue = value Engine.SetPlayerData(0, CoD.StatsGroup.Ranked, "prestige", tonumber(value))
saved = false end)
end)
rankbutton = rankeditbutton(menu, function(value) rankeditbutton(menu, function(value)
rankvalue = value local rank = tonumber(value)
saved = false local prestige = Engine.GetPlayerData(0, CoD.StatsGroup.Ranked, "prestige") or 0
end) local experience = rank == 0 and 0 or Rank.GetRankMaxXP(tonumber(value) - 1, prestige)
local savebutton = menu:AddButton("@LUA_MENU_SAVE", function() Engine.SetPlayerData(0, CoD.StatsGroup.Ranked, "experience", experience)
Engine.SetPlayerData(0, CoD.StatsGroup.Ranked, "prestige", tonumber(prestigevalue)) end)
local rank = tonumber(rankvalue) LUI.Options.InitScrollingList(menu.list, nil)
local prestige = Engine.GetPlayerData(0, CoD.StatsGroup.Ranked, "prestige") or 0 LUI.Options.AddOptionTextInfo(menu)
local experience = rank == 0 and 0 or luiglobals.Rank.GetRankMaxXP(tonumber(rankvalue) - 1, prestige)
Engine.SetPlayerData(0, CoD.StatsGroup.Ranked, "experience", experience) menu:AddBackButton()
saved = true return menu
end, nil, nil, nil, {
desc_text = Engine.Localize("LUA_MENU_SAVE_DESC")
})
LUI.Options.InitScrollingList(menu.list, nil)
LUI.Options.AddOptionTextInfo(menu)
menu:AddBackButton(function()
if (saved) then
LUI.FlowManager.RequestLeaveMenu(menu)
return
end
LUI.yesnopopup({
title = Engine.Localize("@MENU_NOTICE"),
text = Engine.Localize("@LUA_MENU_UNSAVED_CHANGES"),
callback = function(result)
if (result) then
LUI.FlowManager.RequestLeaveMenu(menu)
end
end
})
end)
return menu
end) end)
function prestigeeditbutton(menu, callback) function prestigeeditbutton(menu, callback)
local options = {} local options = {}
local max = luiglobals.Lobby.GetMaxPrestigeLevel() local max = Lobby.GetMaxPrestigeLevel()
local prestige = Engine.GetPlayerData(0, CoD.StatsGroup.Ranked, "prestige") or 0 local prestige = Engine.GetPlayerData(0, CoD.StatsGroup.Ranked, "prestige") or 0
for i = 0, max do for i = 0, max do
game:addlocalizedstring("LUA_MENU_" .. i, i .. "") game:addlocalizedstring("LUA_MENU_" .. i, i .. "")
table.insert(options, { table.insert(options, {
text = "@" .. i, text = "@" .. i,
value = i .. "" value = i .. ""
}) })
end end
Engine.SetDvarFromString("ui_prestige_level", prestige .. "") Engine.SetDvarFromString("ui_prestige_level", prestige .. "")
LUI.Options.CreateOptionButton(menu, "ui_prestige_level", "@LUA_MENU_PRESTIGE", "@LUA_MENU_PRESTIGE_DESC", options, LUI.Options.CreateOptionButton(menu, "ui_prestige_level", "@LUA_MENU_PRESTIGE", "@LUA_MENU_PRESTIGE_DESC", options,
nil, nil, callback) nil, nil, callback)
end end
function rankeditbutton(menu, callback) function rankeditbutton(menu, callback)
local options = {} local options = {}
local prestige = Engine.GetPlayerData(0, CoD.StatsGroup.Ranked, "prestige") or 0 local prestige = Engine.GetPlayerData(0, CoD.StatsGroup.Ranked, "prestige") or 0
local experience = Engine.GetPlayerData(0, CoD.StatsGroup.Ranked, "experience") or 0 local experience = Engine.GetPlayerData(0, CoD.StatsGroup.Ranked, "experience") or 0
local rank = luiglobals.Lobby.GetRankForXP(experience, prestige) local rank = Lobby.GetRankForXP(experience, prestige)
local max = luiglobals.Rank.GetMaxRank(prestige) local max = Rank.GetMaxRank(prestige)
local maxprestige = luiglobals.Lobby.GetMaxPrestigeLevel() local maxprestige = Lobby.GetMaxPrestigeLevel()
for i = 0, max do for i = 0, max do
game:addlocalizedstring("LUA_MENU_" .. i, i .. "") game:addlocalizedstring("LUA_MENU_" .. i, i .. "")
table.insert(options, { table.insert(options, {
text = "@" .. (i + 1), text = "@" .. (i + 1),
value = i .. "" value = i .. ""
}) })
end end
Engine.SetDvarFromString("ui_rank_level_", rank .. "") Engine.SetDvarFromString("ui_rank_level_", rank .. "")
return LUI.Options.CreateOptionButton(menu, "ui_rank_level_", "@LUA_MENU_RANK", "@LUA_MENU_RANK_DESC", options, nil, return LUI.Options.CreateOptionButton(menu, "ui_rank_level_", "@LUA_MENU_RANK", "@LUA_MENU_RANK_DESC", options, nil,
nil, callback) nil, callback)
end end
local isclasslocked = luiglobals.Cac.IsCustomClassLocked local isclasslocked = Cac.IsCustomClassLocked
luiglobals.Cac.IsCustomClassLocked = function(...) Cac.IsCustomClassLocked = function(...)
if (Engine.GetDvarBool("cg_unlockall_classes")) then if (Engine.GetDvarBool("cg_unlockall_classes")) then
return false return false
end end
return isclasslocked(table.unpack({...})) return isclasslocked(...)
end end

View File

@ -303,7 +303,7 @@ targetname "h1-mod"
pchheader "std_include.hpp" pchheader "std_include.hpp"
pchsource "src/client/std_include.cpp" pchsource "src/client/std_include.cpp"
linkoptions {"/IGNORE:4254", "/DYNAMICBASE:NO", "/SAFESEH:NO", "/LARGEADDRESSAWARE", "/LAST:.main", "/PDBCompress"} linkoptions {"/IGNORE:4254", "/DYNAMICBASE:NO", "/SAFESEH:NO", "/LARGEADDRESSAWARE", "/PDBCompress"}
files {"./src/client/**.rc", "./src/client/**.hpp", "./src/client/**.cpp", "./src/client/resources/**.*"} files {"./src/client/**.rc", "./src/client/**.hpp", "./src/client/**.cpp", "./src/client/resources/**.*"}

View File

@ -0,0 +1,145 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "scheduler.hpp"
#include "game/game.hpp"
#include <utils/hook.hpp>
namespace arxan
{
namespace
{
utils::hook::detour nt_close_hook;
utils::hook::detour nt_query_information_process_hook;
NTSTATUS WINAPI nt_query_information_process_stub(const HANDLE handle, const PROCESSINFOCLASS info_class,
const PVOID info,
const ULONG info_length, const PULONG ret_length)
{
auto* orig = static_cast<decltype(NtQueryInformationProcess)*>(nt_query_information_process_hook.
get_original());
const auto status = orig(handle, info_class, info, info_length, ret_length);
if (NT_SUCCESS(status))
{
if (info_class == ProcessBasicInformation)
{
static DWORD explorer_pid = 0;
if (!explorer_pid)
{
auto* const shell_window = GetShellWindow();
GetWindowThreadProcessId(shell_window, &explorer_pid);
}
static_cast<PPROCESS_BASIC_INFORMATION>(info)->Reserved3 = PVOID(DWORD64(explorer_pid));
}
else if (info_class == 30) // ProcessDebugObjectHandle
{
*static_cast<HANDLE*>(info) = nullptr;
return 0xC0000353;
}
else if (info_class == 7) // ProcessDebugPort
{
*static_cast<HANDLE*>(info) = nullptr;
}
else if (info_class == 31)
{
*static_cast<ULONG*>(info) = 1;
}
//https://docs.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntqueryinformationprocess
}
return status;
}
NTSTATUS NTAPI nt_close_stub(const HANDLE handle)
{
char info[16];
if (NtQueryObject(handle, OBJECT_INFORMATION_CLASS(4), &info, 2, nullptr) >= 0 && size_t(handle) != 0x12345)
{
auto* orig = static_cast<decltype(NtClose)*>(nt_close_hook.get_original());
return orig(handle);
}
return STATUS_INVALID_HANDLE;
}
LONG WINAPI exception_filter(const LPEXCEPTION_POINTERS info)
{
if (info->ExceptionRecord->ExceptionCode == STATUS_INVALID_HANDLE)
{
return EXCEPTION_CONTINUE_EXECUTION;
}
return EXCEPTION_CONTINUE_SEARCH;
}
void hide_being_debugged()
{
auto* const peb = PPEB(__readgsqword(0x60));
peb->BeingDebugged = false;
*reinterpret_cast<PDWORD>(LPSTR(peb) + 0xBC) &= ~0x70;
}
void remove_hardware_breakpoints()
{
CONTEXT context;
ZeroMemory(&context, sizeof(context));
context.ContextFlags = CONTEXT_DEBUG_REGISTERS;
auto* const thread = GetCurrentThread();
GetThreadContext(thread, &context);
context.Dr0 = 0;
context.Dr1 = 0;
context.Dr2 = 0;
context.Dr3 = 0;
context.Dr6 = 0;
context.Dr7 = 0;
SetThreadContext(thread, &context);
}
BOOL WINAPI set_thread_context_stub(const HANDLE thread, CONTEXT* context)
{
return SetThreadContext(thread, context);
}
}
class component final : public component_interface
{
public:
void* load_import(const std::string& library, const std::string& function) override
{
if (function == "SetThreadContext")
{
//return set_thread_context_stub;
}
return nullptr;
}
void post_load() override
{
hide_being_debugged();
scheduler::loop(hide_being_debugged, scheduler::pipeline::async);
const utils::nt::library ntdll("ntdll.dll");
nt_close_hook.create(ntdll.get_proc<void*>("NtClose"), nt_close_stub);
nt_query_information_process_hook.create(ntdll.get_proc<void*>("NtQueryInformationProcess"),
nt_query_information_process_stub);
// https://www.geoffchappell.com/studies/windows/win32/ntdll/api/index.htm
AddVectoredExceptionHandler(1, exception_filter);
}
void post_unpack() override
{
// cba to implement sp, not sure if it's even needed
if (game::environment::is_sp()) return;
}
};
}
REGISTER_COMPONENT(arxan::component)

View File

@ -89,7 +89,7 @@ namespace auth
return utils::string::va("0x%lX", value); return utils::string::va("0x%lX", value);
} }
int send_connect_data_stub(game::netsrc_t sock, game::netadr_s* adr, const char* format, const int len) bool send_connect_data(game::netsrc_t sock, game::netadr_s* adr, const char* format, const int len)
{ {
std::string connect_string(format, len); std::string connect_string(format, len);
game::SV_Cmd_TokenizeString(connect_string.data()); game::SV_Cmd_TokenizeString(connect_string.data());
@ -189,7 +189,29 @@ namespace auth
a.call_aligned(direct_connect); a.call_aligned(direct_connect);
a.popad64(); a.popad64();
a.jmp(0x140488CE2); // H1MP64(1.4) a.jmp(0x1CAF64_b);
});
}
void* get_send_connect_data_stub()
{
return utils::hook::assemble([](utils::hook::assembler& a)
{
const auto false_ = a.newLabel();
const auto original = a.newLabel();
a.mov(ecx, eax);
a.lea(r8, qword_ptr(rbp, 0x4C0));
a.mov(r9d, ebx);
a.lea(rdx, qword_ptr(rsp, 0x30));
a.pushad64();
a.call_aligned(send_connect_data);
a.test(al, al);
a.popad64();
a.mov(rbx, qword_ptr(rsp, 0x9F0));
a.jmp(0x12D446_b);
}); });
} }
} }
@ -212,25 +234,20 @@ namespace auth
// Patch steam id bit check // Patch steam id bit check
if (game::environment::is_sp()) if (game::environment::is_sp())
{ {
utils::hook::jump(0x140475C17, 0x140475C6A); // H1(1.4) utils::hook::jump(0x4FA1B3_b, 0x4FA21A_b, true);
utils::hook::jump(0x140476AFF, 0x140476B40); // H1(1.4) utils::hook::jump(0x4FB272_b, 0x4FB2B7_b, true);
utils::hook::jump(0x140476FA4, 0x140476FF2); // H1(1.4) utils::hook::jump(0x4FB781_b, 0x4FB7D3_b, true);
} }
else else
{ {
utils::hook::jump(0x140571E07, 0x140571E5A); // H1(1.4) // kill "disconnected from steam" error
utils::hook::jump(0x14004B223, 0x14004B4F2); // H1(1.4) utils::hook::nop(0x1D61DF_b, 0x11);
utils::hook::jump(0x14004B4AD, 0x14004B4F2); // H1(1.4)
utils::hook::jump(0x140572F6F, 0x140572FB0); // H1(1.4)
utils::hook::jump(0x140573470, 0x1405734B6); // H1(1.4)
utils::hook::jump(0x140488BC1, get_direct_connect_stub(), true); // H1(1.4) utils::hook::jump(0x1CAE70_b, get_direct_connect_stub(), true);
utils::hook::call(0x140250ED2, send_connect_data_stub); // H1(1.4) utils::hook::jump(0x12D426_b, get_send_connect_data_stub(), true);
// Skip checks for sending connect packet
utils::hook::jump(0x1402508FC, 0x140250946);
// Don't instantly timeout the connecting client ? not sure about this // Don't instantly timeout the connecting client ? not sure about this
utils::hook::set(0x14025136B, 0xC3); utils::hook::set(0x12D93C_b, 0xC3);
} }
command::add("guid", []() command::add("guid", []()

View File

@ -15,7 +15,7 @@ namespace binding
int get_num_keys() int get_num_keys()
{ {
return SELECT_VALUE(102, 103); return 110;
} }
int key_write_bindings_to_buffer_stub(int /*localClientNum*/, char* buffer, const int buffer_size) int key_write_bindings_to_buffer_stub(int /*localClientNum*/, char* buffer, const int buffer_size)
@ -83,7 +83,7 @@ namespace binding
int key_get_binding_for_cmd_stub(const char* command) int key_get_binding_for_cmd_stub(const char* command)
{ {
// original binds // original binds
for (auto i = 0; i <= get_num_keys(); i++) for (auto i = 0; i < get_num_keys(); i++)
{ {
if (game::command_whitelist[i] && !strcmp(command, game::command_whitelist[i])) if (game::command_whitelist[i] && !strcmp(command, game::command_whitelist[i]))
{ {
@ -103,7 +103,7 @@ namespace binding
if (static_cast<size_t>(key) < custom_binds.size() && !custom_binds[key].empty()) if (static_cast<size_t>(key) < custom_binds.size() && !custom_binds[key].empty())
{ {
game::Cbuf_AddText(local_client_num, utils::string::va("%s\n", custom_binds[key].data())); game::Cbuf_AddText(local_client_num, 0, utils::string::va("%s\n", custom_binds[key].data()));
} }
return; return;
@ -124,13 +124,13 @@ namespace binding
} }
// write all bindings to config file // write all bindings to config file
utils::hook::call(SELECT_VALUE(0x1401881DB, 0x14025032F), key_write_bindings_to_buffer_stub); // H1(1.4) utils::hook::jump(SELECT_VALUE(0x1AC980_b, 0x199ED0_b), key_write_bindings_to_buffer_stub);
// links a custom command to an index // links a custom command to an index
utils::hook::jump(SELECT_VALUE(0x140343C00, 0x1404041E0), key_get_binding_for_cmd_stub); // H1(1.4) utils::hook::jump(SELECT_VALUE(0x377280_b, 0x1572B0_b), key_get_binding_for_cmd_stub);
// execute custom binds // execute custom binds
cl_execute_key_hook.create(SELECT_VALUE(0x140183C70, 0x14024ACF0), &cl_execute_key_stub); // H1(1.4) cl_execute_key_hook.create(SELECT_VALUE(0x1A8350_b, 0x130610_b), &cl_execute_key_stub);
} }
}; };
} }

View File

@ -95,7 +95,7 @@ namespace bots
num_bots = atoi(params.get(1)); num_bots = atoi(params.get(1));
} }
num_bots = std::min(num_bots, *game::mp::svs_numclients);; num_bots = std::min(num_bots, *game::mp::svs_numclients);
for (auto i = 0; i < num_bots; i++) for (auto i = 0; i < num_bots; i++)
{ {
@ -106,4 +106,4 @@ namespace bots
}; };
} }
REGISTER_COMPONENT(bots::component) REGISTER_COMPONENT(bots::component)

View File

@ -27,14 +27,28 @@ namespace branding
const auto* const build_num = ui_get_formatted_build_number_hook.invoke<const char*>(); const auto* const build_num = ui_get_formatted_build_number_hook.invoke<const char*>();
return utils::string::va("%s (%s)", VERSION, build_num); return utils::string::va("%s (%s)", VERSION, build_num);
} }
void draw_branding()
{
const auto font = game::R_RegisterFont("fonts/fira_mono_bold.ttf", 20);
if (font)
{
game::R_AddCmdDrawText("H1-Mod: " VERSION, 0x7FFFFFFF, font, 10.f,
5.f + static_cast<float>(font->pixelHeight), 1.f, 1.f, 0.0f, color, 0);
}
}
} }
class component final : public component_interface class component final : public component_interface
{ {
public: public:
void post_start() override
{
scheduler::loop(draw_branding, scheduler::pipeline::renderer);
}
void post_unpack() override void post_unpack() override
{ {
if (game::environment::is_dedi()) if (game::environment::is_dedi())
{ {
return; return;
@ -46,20 +60,12 @@ namespace branding
localized_strings::override("MENU_MULTIPLAYER_CAPS", "H1-MOD: MULTIPLAYER"); localized_strings::override("MENU_MULTIPLAYER_CAPS", "H1-MOD: MULTIPLAYER");
} }
dvars::override::set_string("version", utils::string::va("H1-Mod %s", VERSION)); //dvars::override::set_string("version", utils::string::va("H1-Mod %s", VERSION));
ui_get_formatted_build_number_hook.create( ui_get_formatted_build_number_hook.create(
SELECT_VALUE(0x1403B1C40, 0x1404E74C0), ui_get_formatted_build_number_stub); SELECT_VALUE(0x406EC0_b, 0x1DF300_b), ui_get_formatted_build_number_stub);
scheduler::loop([]()
{
const auto font = game::R_RegisterFont("fonts/fira_mono_bold.ttf", 20);
game::R_AddCmdDrawText("H1-Mod: " VERSION, 0x7FFFFFFF, font, 10.f,
5.f + static_cast<float>(font->pixelHeight), 1.f, 1.f, 0.0f, color, 0);
}, scheduler::pipeline::renderer);
} }
}; };
} }
REGISTER_COMPONENT(branding::component) REGISTER_COMPONENT(branding::component)

View File

@ -0,0 +1,46 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "game/game.hpp"
#include "game/dvars.hpp"
#include <utils/hook.hpp>
namespace bullet
{
namespace
{
game::dvar_t* bg_surface_penetration = nullptr;
utils::hook::detour bg_get_surface_penetration_depth_hook;
float bg_get_surface_penetration_depth_stub(game::Weapon weapon, bool is_alternate, int surface_type)
{
if (bg_surface_penetration->current.value > 0.0f)
{
return bg_surface_penetration->current.value;
}
return bg_get_surface_penetration_depth_hook.invoke<float>(weapon, is_alternate, surface_type);
}
}
class component final : public component_interface
{
public:
void post_unpack() override
{
if (game::environment::is_sp())
{
return;
}
bg_surface_penetration = dvars::register_float("bg_surfacePenetration", 0.0f,
0.0f, std::numeric_limits<float>::max(), game::DVAR_FLAG_SAVED,
"Set to a value greater than 0 to override the surface penetration depth");
bg_get_surface_penetration_depth_hook.create(0x2E1110_b, &bg_get_surface_penetration_depth_stub);
}
};
}
REGISTER_COMPONENT(bullet::component)

View File

@ -30,14 +30,14 @@ namespace chat
} }
// use better font // use better font
utils::hook::call(0x1400AA831, ui_get_font_handle_stub); utils::hook::inject(0x0F6F61_b, reinterpret_cast<void*>(0x2E6F588_b));
utils::hook::call(0x14024800C, ui_get_font_handle_stub); utils::hook::inject(0x18A980_b, reinterpret_cast<void*>(0x2E6F588_b));
utils::hook::call(0x14024F573, ui_get_font_handle_stub); utils::hook::call(0x33EDEC_b, ui_get_font_handle_stub);
// set text style to 0 (non-blurry) // set text style to 0 (non-blurry)
utils::hook::set<uint8_t>(0x14024F5DD, 0); utils::hook::set<uint8_t>(0x18A9F2_b, 0);
utils::hook::set<uint8_t>(0x1400AAA1C, 0); utils::hook::set<uint8_t>(0x0F7151_b, 0);
utils::hook::set<uint8_t>(0x14024802E, 0); utils::hook::set<uint8_t>(0x33EE0E_b, 0);
localized_strings::override("EXE_SAY", "^3Match^7"); localized_strings::override("EXE_SAY", "^3Match^7");
localized_strings::override("EXE_SAYTEAM", "^5Team^7"); localized_strings::override("EXE_SAYTEAM", "^5Team^7");

View File

@ -96,7 +96,7 @@ namespace colors
const size_t unk, const size_t unk2) const size_t unk, const size_t unk2)
{ {
// CL_GetClientName (CL_GetClientNameAndClantag?) // CL_GetClientName (CL_GetClientNameAndClantag?)
const auto result = utils::hook::invoke<size_t>(0x14025BAA0, local_client_num, index, buf, size, unk, unk2); const auto result = utils::hook::invoke<size_t>(0x343BA0_b, local_client_num, index, buf, size, unk, unk2);
utils::string::strip(buf, buf, size); utils::string::strip(buf, buf, size);
@ -109,11 +109,11 @@ namespace colors
if (index == '8') if (index == '8')
{ {
*color = *reinterpret_cast<DWORD*>(SELECT_VALUE(0x14F142FF8, 0x14FE70634)); // H1(1.4) *color = *reinterpret_cast<DWORD*>(SELECT_VALUE(0x0, 0xEA749B4_b)); // 1.15
} }
else if (index == '9') else if (index == '9')
{ {
*color = *reinterpret_cast<DWORD*>(SELECT_VALUE(0x14F142FFC, 0x14FE70638)); // H1(1.4) *color = *reinterpret_cast<DWORD*>(SELECT_VALUE(0x0, 0xEA749B8_b)); // 1.15
} }
else if (index == ':') else if (index == ':')
{ {
@ -121,7 +121,7 @@ namespace colors
} }
else if (index == ';') else if (index == ';')
{ {
*color = *reinterpret_cast<DWORD*>(SELECT_VALUE(0x14F143004, 0x14FE70640)); // H1(1.4) *color = *reinterpret_cast<DWORD*>(SELECT_VALUE(0x0, 0xEA749C0_b)); // 1.15
} }
else if (index == '<') else if (index == '<')
{ {
@ -147,17 +147,17 @@ namespace colors
if (!game::environment::is_sp()) if (!game::environment::is_sp())
{ {
// allows colored name in-game // allows colored name in-game
utils::hook::jump(0x140503810, com_clean_name_stub); // H1(1.4) utils::hook::jump(0x5AEDF0_b, com_clean_name_stub, true);
// don't apply colors to overhead names // don't apply colors to overhead names
utils::hook::call(0x1400AB416, get_client_name_stub); // H1(1.4) utils::hook::call(0xF7B85_b, get_client_name_stub);
// patch I_CleanStr // patch I_CleanStr
utils::hook::jump(0x140503D00, i_clean_str_stub); // H1(1.4) utils::hook::jump(0x5AF2E0_b, i_clean_str_stub, true);
} }
// force new colors // force new colors
utils::hook::jump(SELECT_VALUE(0x140524BD0, 0x1406206A0), rb_lookup_color_stub); // H1(1.4) utils::hook::jump(SELECT_VALUE(0x5B17E0_b, 0x6C9460_b), rb_lookup_color_stub, true);
// add colors // add colors
add(0, 0, 0); // 0 - Black add(0, 0, 0); // 0 - Black

View File

@ -6,6 +6,7 @@
#include "game_console.hpp" #include "game_console.hpp"
#include "fastfiles.hpp" #include "fastfiles.hpp"
#include "scheduler.hpp" #include "scheduler.hpp"
#include "logfile.hpp"
#include "game/game.hpp" #include "game/game.hpp"
#include "game/dvars.hpp" #include "game/dvars.hpp"
@ -22,6 +23,7 @@ namespace command
namespace namespace
{ {
utils::hook::detour client_command_hook; utils::hook::detour client_command_hook;
utils::hook::detour parse_commandline_hook;
std::unordered_map<std::string, std::function<void(params&)>> handlers; std::unordered_map<std::string, std::function<void(params&)>> handlers;
std::unordered_map<std::string, std::function<void(int, params_sv&)>> handlers_sv; std::unordered_map<std::string, std::function<void(int, params_sv&)>> handlers_sv;
@ -39,6 +41,11 @@ namespace command
void client_command(const int client_num) void client_command(const int client_num)
{ {
if (!logfile::client_command_stub(client_num))
{
return;
}
params_sv params = {}; params_sv params = {};
const auto command = utils::string::to_lower(params[0]); const auto command = utils::string::to_lower(params[0]);
@ -63,8 +70,8 @@ namespace command
static std::string comand_line_buffer = GetCommandLineA(); static std::string comand_line_buffer = GetCommandLineA();
auto* command_line = comand_line_buffer.data(); auto* command_line = comand_line_buffer.data();
auto& com_num_console_lines = *reinterpret_cast<int*>(0x142623FB4); auto& com_num_console_lines = *reinterpret_cast<int*>(0x35634B8_b);
auto* com_console_lines = reinterpret_cast<char**>(0x142623FC0); auto* com_console_lines = reinterpret_cast<char**>(0x35634C0_b);
auto inq = false; auto inq = false;
com_console_lines[0] = command_line; com_console_lines[0] = command_line;
@ -96,7 +103,7 @@ namespace command
void parse_commandline_stub() void parse_commandline_stub()
{ {
parse_command_line(); parse_command_line();
utils::hook::invoke<void>(0x1400D8210); parse_commandline_hook.invoke<void>();
} }
game::dvar_t* dvar_command_stub() game::dvar_t* dvar_command_stub()
@ -114,11 +121,11 @@ namespace command
{ {
if (args.size() == 1) if (args.size() == 1)
{ {
const auto current = game::Dvar_ValueToString(dvar, dvar->current); const auto current = game::Dvar_ValueToString(dvar, true, dvar->current);
const auto reset = game::Dvar_ValueToString(dvar, dvar->reset); const auto reset = game::Dvar_ValueToString(dvar, true, dvar->reset);
console::info("\"%s\" is: \"%s\" default: \"%s\" hash: 0x%08lX\n", console::info("\"%s\" is: \"%s\" default: \"%s\" hash: 0x%08lX type: %i\n",
args[0], current, reset, dvar->hash); args[0], current, reset, dvar->hash, dvar->type);
const auto dvar_info = dvars::dvar_get_description(args[0]); const auto dvar_info = dvars::dvar_get_description(args[0]);
@ -127,7 +134,7 @@ namespace command
} }
else else
{ {
char command[0x1000] = { 0 }; char command[0x1000] = {0};
game::Dvar_GetCombinedString(command, 1); game::Dvar_GetCombinedString(command, 1);
game::Dvar_SetCommand(dvar->hash, "", command); game::Dvar_SetCommand(dvar->hash, "", command);
} }
@ -340,8 +347,8 @@ namespace command
// parse the commandline if it's not parsed // parse the commandline if it's not parsed
parse_command_line(); parse_command_line();
auto& com_num_console_lines = *reinterpret_cast<int*>(0x142623FB4); auto& com_num_console_lines = *reinterpret_cast<int*>(0x35634B8_b);
auto* com_console_lines = reinterpret_cast<char**>(0x142623FC0); auto* com_console_lines = reinterpret_cast<char**>(0x35634C0_b);
for (int i = 0; i < com_num_console_lines; i++) for (int i = 0; i < com_num_console_lines; i++)
{ {
@ -454,8 +461,6 @@ namespace command
void add(const char* name, const std::function<void(const params&)>& callback) void add(const char* name, const std::function<void(const params&)>& callback)
{ {
static game::cmd_function_s cmd_test;
const auto command = utils::string::to_lower(name); const auto command = utils::string::to_lower(name);
if (handlers.find(command) == handlers.end()) if (handlers.find(command) == handlers.end())
@ -493,7 +498,7 @@ namespace command
} }
else else
{ {
game::Cbuf_AddText(0, command.data()); game::Cbuf_AddText(0, 0, command.data());
} }
} }
@ -508,12 +513,12 @@ namespace command
} }
else else
{ {
utils::hook::call(0x1400D728F, parse_commandline_stub); parse_commandline_hook.create(0x157D50_b, parse_commandline_stub);
utils::hook::jump(0x14041D750, dvar_command_stub);
add_commands_mp(); add_commands_mp();
} }
utils::hook::jump(SELECT_VALUE(0x3A7C80_b, 0x4E9F40_b), dvar_command_stub, true);
add_commands_generic(); add_commands_generic();
} }
@ -620,7 +625,7 @@ namespace command
if (dvar->type != game::dvar_type::string if (dvar->type != game::dvar_type::string
&& dvar->type != game::dvar_type::enumeration) && dvar->type != game::dvar_type::enumeration)
{ {
console::info("%s is not a string-based dvar\n", dvar->hash); console::info("%s is not a string-based dvar\n", name);
return; return;
} }
@ -680,16 +685,6 @@ namespace command
toggle_client_flag(2, "ufo"); toggle_client_flag(2, "ufo");
}); });
add("give", [](const params& params)
{
if (!game::SV_Loaded())
{
return;
}
cmd_give_weapon(0, params.get_all());
});
add("dropweapon", [](const params& params) add("dropweapon", [](const params& params)
{ {
if (!game::SV_Loaded()) if (!game::SV_Loaded())
@ -719,11 +714,21 @@ namespace command
cmd_kill(0); cmd_kill(0);
}); });
add("give", [](const params& params)
{
if (!game::SV_Loaded())
{
return;
}
cmd_give_weapon(0, params.get_all());
});
} }
static void add_commands_mp() static void add_commands_mp()
{ {
client_command_hook.create(0x140336000, &client_command); client_command_hook.create(0x4132E0_b, &client_command);
add_sv("god", [](const int client_num, const params_sv&) add_sv("god", [](const int client_num, const params_sv&)
{ {
@ -772,7 +777,7 @@ namespace command
return; return;
} }
toggle_client_flag(client_num, 2, "noclip"); toggle_client_flag(client_num, 2, "ufo");
}); });
add_sv("give", [](const int client_num, const params_sv& params) add_sv("give", [](const int client_num, const params_sv& params)

View File

@ -1,14 +1,17 @@
#include <std_include.hpp> #include <std_include.hpp>
#include "console.hpp" #include "console.hpp"
#include "loader/component_loader.hpp" #include "loader/component_loader.hpp"
#include "game/game.hpp" #include "game/game.hpp"
#include "command.hpp" #include "command.hpp"
#include "rcon.hpp"
#include <utils/thread.hpp> #include <utils/thread.hpp>
#include <utils/flags.hpp>
#include <utils/concurrency.hpp>
#include <utils/hook.hpp> #include <utils/hook.hpp>
#define OUTPUT_HANDLE GetStdHandle(STD_OUTPUT_HANDLE)
namespace game_console namespace game_console
{ {
void print(int type, const std::string& data); void print(int type, const std::string& data);
@ -18,26 +21,40 @@ namespace console
{ {
namespace namespace
{ {
using message_queue = std::queue<std::string>; utils::hook::detour printf_hook;
utils::concurrency::container<message_queue> messages; std::recursive_mutex print_mutex;
bool native_console() struct
{ {
static const auto flag = utils::flags::has_flag("nativeconsole"); bool kill;
return flag; std::thread thread;
HANDLE kill_event;
char buffer[512]{};
int cursor;
std::int32_t history_index = -1;
std::deque<std::string> history{};
} con{};
void set_cursor_pos(int x)
{
CONSOLE_SCREEN_BUFFER_INFO info{};
GetConsoleScreenBufferInfo(OUTPUT_HANDLE, &info);
info.dwCursorPosition.X = static_cast<short>(x);
SetConsoleCursorPosition(OUTPUT_HANDLE, info.dwCursorPosition);
} }
void hide_console() void show_cursor(const bool show)
{ {
auto* const con_window = GetConsoleWindow(); CONSOLE_CURSOR_INFO info{};
GetConsoleCursorInfo(OUTPUT_HANDLE, &info);
info.bVisible = show;
SetConsoleCursorInfo(OUTPUT_HANDLE, &info);
}
DWORD process; template <typename... Args>
GetWindowThreadProcessId(con_window, &process); int invoke_printf(const char* fmt, Args&&... args)
{
if (!native_console() && (process == GetCurrentProcessId() || IsDebuggerPresent())) return printf_hook.invoke<int>(fmt, std::forward<Args>(args)...);
{
ShowWindow(con_window, SW_HIDE);
}
} }
std::string format(va_list* ap, const char* message) std::string format(va_list* ap, const char* message)
@ -45,244 +62,266 @@ namespace console
static thread_local char buffer[0x1000]; static thread_local char buffer[0x1000];
const auto count = _vsnprintf_s(buffer, sizeof(buffer), sizeof(buffer), message, *ap); const auto count = _vsnprintf_s(buffer, sizeof(buffer), sizeof(buffer), message, *ap);
if (count < 0)
{
return {};
}
if (count < 0) return {};
return {buffer, static_cast<size_t>(count)}; return {buffer, static_cast<size_t>(count)};
} }
void dispatch_message(const int type, const std::string& message) uint8_t get_attribute(const int type)
{ {
if (native_console()) switch (type)
{ {
printf("%s\n", message.data()); case con_type_info:
return 7; // white
case con_type_warning:
return 6; // yellow
case con_type_error:
return 4; // red
case con_type_debug:
return 3; // cyan
}
return 7;
}
void update()
{
std::lock_guard _0(print_mutex);
show_cursor(false);
set_cursor_pos(0);
invoke_printf("%s", con.buffer);
set_cursor_pos(con.cursor);
show_cursor(true);
}
void clear_output()
{
std::lock_guard _0(print_mutex);
show_cursor(false);
set_cursor_pos(0);
for (auto i = 0; i < std::strlen(con.buffer); i++)
{
invoke_printf(" ");
}
set_cursor_pos(con.cursor);
show_cursor(true);
}
int dispatch_message(const int type, const std::string& message)
{
if (rcon::message_redirect(message))
{
return 0;
}
std::lock_guard _0(print_mutex);
clear_output();
set_cursor_pos(0);
SetConsoleTextAttribute(OUTPUT_HANDLE, get_attribute(type));
const auto res = invoke_printf("%s", message.data());
SetConsoleTextAttribute(OUTPUT_HANDLE, get_attribute(con_type_info));
game_console::print(type, message);
if (message.size() <= 0 || message[message.size() - 1] != '\n')
{
invoke_printf("\n");
}
update();
return res;
}
void clear()
{
std::lock_guard _0(print_mutex);
clear_output();
strncpy_s(con.buffer, "", sizeof(con.buffer));
con.cursor = 0;
set_cursor_pos(0);
}
size_t get_max_input_length()
{
CONSOLE_SCREEN_BUFFER_INFO info{};
GetConsoleScreenBufferInfo(OUTPUT_HANDLE, &info);
const auto columns = static_cast<size_t>(info.srWindow.Right - info.srWindow.Left - 1);
return std::max(size_t(0), std::min(columns, sizeof(con.buffer)));
}
void handle_resize()
{
clear();
update();
}
void handle_input(const INPUT_RECORD record)
{
if (record.EventType == WINDOW_BUFFER_SIZE_EVENT)
{
handle_resize();
return; return;
} }
game_console::print(type, message); if (record.EventType != KEY_EVENT || !record.Event.KeyEvent.bKeyDown)
messages.access([&message](message_queue& msgs)
{ {
msgs.emplace(message); return;
});
}
void append_text(const char* text)
{
dispatch_message(con_type_info, text);
}
}
class component final : public component_interface
{
public:
component()
{
hide_console();
if (native_console())
{
setvbuf(stdout, nullptr, _IONBF, 0);
setvbuf(stderr, nullptr, _IONBF, 0);
}
else
{
(void)_pipe(this->handles_, 1024, _O_TEXT);
(void)_dup2(this->handles_[1], 1);
(void)_dup2(this->handles_[1], 2);
}
}
void post_start() override
{
this->terminate_runner_ = false;
this->console_runner_ = utils::thread::create_named_thread("Console IO", [this]
{
if (native_console())
{
this->native_input();
}
else
{
this->runner();
}
});
}
void pre_destroy() override
{
this->terminate_runner_ = true;
printf("\r\n");
_flushall();
if (this->console_runner_.joinable())
{
this->console_runner_.join();
} }
if (this->console_thread_.joinable()) std::lock_guard _0(print_mutex);
const auto key = record.Event.KeyEvent.wVirtualKeyCode;
switch (key)
{ {
this->console_thread_.join(); case VK_UP:
{
if (++con.history_index >= con.history.size())
{
con.history_index = static_cast<int>(con.history.size()) - 1;
}
clear();
if (con.history_index != -1)
{
strncpy_s(con.buffer, con.history.at(con.history_index).data(), sizeof(con.buffer));
con.cursor = static_cast<int>(strlen(con.buffer));
}
update();
break;
} }
case VK_DOWN:
#ifndef NATIVE_CONSOLE
_close(this->handles_[0]);
_close(this->handles_[1]);
#endif
messages.access([&](message_queue& msgs)
{ {
msgs = {}; if (--con.history_index < -1)
});
}
void post_unpack() override
{
// Redirect input (]command)
utils::hook::jump(SELECT_VALUE(0x1403E34C0, 0x1405141E0), append_text); // H1(1.4)
this->initialize();
}
private:
volatile bool console_initialized_ = false;
volatile bool terminate_runner_ = false;
std::thread console_runner_;
std::thread console_thread_;
int handles_[2]{};
void initialize()
{
this->console_thread_ = utils::thread::create_named_thread("Console", [this]()
{
if (!native_console() && (game::environment::is_dedi() || !utils::flags::has_flag("noconsole")))
{ {
game::Sys_ShowConsole(); con.history_index = -1;
} }
if (!game::environment::is_dedi()) clear();
if (con.history_index != -1)
{ {
// Hide that shit strncpy_s(con.buffer, con.history.at(con.history_index).data(), sizeof(con.buffer));
ShowWindow(console::get_window(), SW_MINIMIZE); con.cursor = static_cast<int>(strlen(con.buffer));
} }
update();
break;
}
case VK_LEFT:
{
if (con.cursor > 0)
{ {
messages.access([&](message_queue&) con.cursor--;
set_cursor_pos(con.cursor);
}
break;
}
case VK_RIGHT:
{
if (con.cursor < std::strlen(con.buffer))
{
con.cursor++;
set_cursor_pos(con.cursor);
}
break;
}
case VK_RETURN:
{
if (con.history_index != -1)
{
const auto itr = con.history.begin() + con.history_index;
if (*itr == con.buffer)
{ {
this->console_initialized_ = true; con.history.erase(con.history.begin() + con.history_index);
});
}
MSG msg;
while (!this->terminate_runner_)
{
if (PeekMessageA(&msg, nullptr, NULL, NULL, PM_REMOVE))
{
if (msg.message == WM_QUIT)
{
command::execute("quit", false);
break;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
this->log_messages();
std::this_thread::sleep_for(1ms);
} }
} }
});
}
void log_messages()
{
/*while*/
if (this->console_initialized_ && !messages.get_raw().empty())
{
std::queue<std::string> message_queue_copy;
if (con.buffer[0])
{ {
messages.access([&](message_queue& msgs) con.history.push_front(con.buffer);
{
message_queue_copy = std::move(msgs);
msgs = {};
});
} }
while (!message_queue_copy.empty()) if (con.history.size() > 10)
{ {
log_message(message_queue_copy.front()); con.history.erase(con.history.begin() + 10);
message_queue_copy.pop();
} }
con.history_index = -1;
game::Cbuf_AddText(0, 0, con.buffer);
con.cursor = 0;
clear_output();
invoke_printf("]%s\r\n", con.buffer);
strncpy_s(con.buffer, "", sizeof(con.buffer));
break;
} }
case VK_BACK:
fflush(stdout);
fflush(stderr);
}
static void log_message(const std::string& message)
{
OutputDebugStringA(message.data());
game::Conbuf_AppendText(message.data());
}
void runner()
{
char buffer[1024];
while (!this->terminate_runner_ && this->handles_[0])
{ {
const auto len = _read(this->handles_[0], buffer, sizeof(buffer)); if (con.cursor <= 0)
if (len > 0)
{ {
dispatch_message(con_type_info, std::string(buffer, len)); break;
}
else
{
std::this_thread::sleep_for(1ms);
} }
clear_output();
std::memmove(con.buffer + con.cursor - 1, con.buffer + con.cursor,
strlen(con.buffer) + 1 - con.cursor);
con.cursor--;
update();
break;
} }
default:
std::this_thread::yield();
}
void native_input()
{
std::string cmd;
while (!this->terminate_runner_)
{ {
std::getline(std::cin, cmd); const auto c = record.Event.KeyEvent.uChar.AsciiChar;
command::execute(cmd); if (!c)
{
break;
}
if (std::strlen(con.buffer) + 1 >= get_max_input_length())
{
break;
}
std::memmove(con.buffer + con.cursor + 1,
con.buffer + con.cursor, std::strlen(con.buffer) + 1 - con.cursor);
con.buffer[con.cursor] = c;
con.cursor++;
update();
break;
}
} }
std::this_thread::yield();
} }
};
HWND get_window() int __cdecl printf_stub(const char* fmt, ...)
{ {
return *reinterpret_cast<HWND*>((SELECT_VALUE(0x14CF56C00, 0x14DDFC2D0))); // H1(1.4) va_list ap;
} va_start(ap, fmt);
const auto result = format(&ap, fmt);
va_end(ap);
void set_title(std::string title) return dispatch_message(con_type_info, result);
{ }
SetWindowText(get_window(), title.data());
}
void set_size(const int width, const int height)
{
RECT rect;
GetWindowRect(get_window(), &rect);
SetWindowPos(get_window(), nullptr, rect.left, rect.top, width, height, 0);
auto* const logo_window = *reinterpret_cast<HWND*>(SELECT_VALUE(0x14CF56C10, 0x14DDFC2E0)); // H1(1.4)
SetWindowPos(logo_window, nullptr, 5, 5, width - 25, 60, 0);
} }
void print(const int type, const char* fmt, ...) void print(const int type, const char* fmt, ...)
@ -294,6 +333,86 @@ namespace console
dispatch_message(type, result); dispatch_message(type, result);
} }
class component final : public component_interface
{
public:
component()
{
ShowWindow(GetConsoleWindow(), SW_HIDE);
}
void post_unpack() override
{
printf_hook.create(printf, printf_stub);
ShowWindow(GetConsoleWindow(), SW_SHOW);
SetConsoleTitle("H1-Mod");
con.kill_event = CreateEvent(NULL, TRUE, FALSE, NULL);
con.thread = utils::thread::create_named_thread("Console", []()
{
const auto handle = GetStdHandle(STD_INPUT_HANDLE);
HANDLE handles[2] = {handle, con.kill_event};
MSG msg{};
INPUT_RECORD record{};
DWORD num_events{};
while (!con.kill)
{
const auto result = MsgWaitForMultipleObjects(2, handles, FALSE, INFINITE, QS_ALLINPUT);
if (con.kill)
{
return;
}
switch (result)
{
case WAIT_OBJECT_0:
{
if (!ReadConsoleInput(handle, &record, 1, &num_events) || num_events == 0)
{
break;
}
handle_input(record);
break;
}
case WAIT_OBJECT_0 + 1:
{
if (!PeekMessageA(&msg, GetConsoleWindow(), NULL, NULL, PM_REMOVE))
{
break;
}
if (msg.message == WM_QUIT)
{
command::execute("quit", false);
break;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
break;
}
}
}
});
}
void pre_destroy() override
{
con.kill = true;
SetEvent(con.kill_event);
if (con.thread.joinable())
{
con.thread.join();
}
}
};
} }
REGISTER_COMPONENT(console::component) REGISTER_COMPONENT(console::component)

View File

@ -9,6 +9,7 @@ namespace console
enum console_type enum console_type
{ {
con_type_error = 1, con_type_error = 1,
con_type_debug = 2,
con_type_warning = 3, con_type_warning = 3,
con_type_info = 7 con_type_info = 7
}; };
@ -21,6 +22,14 @@ namespace console
print(con_type_error, fmt, std::forward<Args>(args)...); print(con_type_error, fmt, std::forward<Args>(args)...);
} }
template <typename... Args>
void debug(const char* fmt, Args&&... args)
{
#ifdef DEBUG
print(con_type_debug, fmt, std::forward<Args>(args)...);
#endif
}
template <typename... Args> template <typename... Args>
void warn(const char* fmt, Args&&... args) void warn(const char* fmt, Args&&... args)
{ {

View File

@ -26,7 +26,7 @@ namespace dedicated
initialized = true; initialized = true;
// R_LoadGraphicsAssets // R_LoadGraphicsAssets
utils::hook::invoke<void>(0x1405DF4B0); utils::hook::invoke<void>(0x686310_b);
} }
void send_heartbeat() void send_heartbeat()
@ -83,8 +83,8 @@ namespace dedicated
{ {
if (game::Live_SyncOnlineDataFlags(0) == 0) if (game::Live_SyncOnlineDataFlags(0) == 0)
{ {
game::Cbuf_AddText(client, command); game::Cbuf_AddText(client, 0, command);
game::Cbuf_AddText(client, "\n"); game::Cbuf_AddText(client, 0, "\n");
} }
else else
{ {
@ -99,8 +99,8 @@ namespace dedicated
for (const auto& command : queue) for (const auto& command : queue)
{ {
game::Cbuf_AddText(0, command.data()); game::Cbuf_AddText(0, 0, command.data());
game::Cbuf_AddText(0, "\n"); game::Cbuf_AddText(0, 0, "\n");
} }
} }
@ -126,12 +126,16 @@ namespace dedicated
void kill_server() void kill_server()
{ {
for (auto i = 0; i < *game::mp::svs_numclients; ++i) const auto* svs_clients = *game::mp::svs_clients;
if (svs_clients != nullptr)
{ {
if (game::mp::svs_clients[i].header.state >= 3) for (auto i = 0; i < *game::mp::svs_numclients; ++i)
{ {
game::SV_GameSendServerCommand(i, game::SV_CMD_CAN_IGNORE, if (svs_clients[i].header.state >= 3)
utils::string::va("r \"%s\"", "EXE_ENDOFGAME")); {
game::SV_GameSendServerCommand(i, game::SV_CMD_CAN_IGNORE,
utils::string::va("r \"%s\"", "EXE_ENDOFGAME"));
}
} }
} }
@ -156,6 +160,23 @@ namespace dedicated
game::Com_Error(game::ERR_DROP, "%s", buffer); game::Com_Error(game::ERR_DROP, "%s", buffer);
} }
utils::hook::detour ui_set_active_menu_hook;
void ui_set_active_menu_stub(void* localClientNum, int menu)
{
static auto done = false;
if (done && (menu == 6 || menu == 7))
{
return;
}
if (menu == 6 || menu == 7)
{
done = true;
}
ui_set_active_menu_hook.invoke<void>(localClientNum, menu);
}
} }
void initialize() void initialize()
@ -197,96 +218,113 @@ namespace dedicated
dvars::override::register_bool("r_preloadShaders", false, game::DVAR_FLAG_READ); dvars::override::register_bool("r_preloadShaders", false, game::DVAR_FLAG_READ);
// Stop crashing from sys_errors // Stop crashing from sys_errors
utils::hook::jump(0x140511520, sys_error_stub); utils::hook::jump(0x1D8710_b, sys_error_stub, true);
// Hook R_SyncGpu // Hook R_SyncGpu
utils::hook::jump(0x1405E12F0, sync_gpu_stub); utils::hook::jump(0x688620_b, sync_gpu_stub, true);
utils::hook::jump(0x140254800, init_dedicated_server); utils::hook::jump(0x135600_b, init_dedicated_server, true);
// delay startup commands until the initialization is done // delay startup commands until the initialization is done
utils::hook::call(0x1400D72D6, execute_startup_command); utils::hook::jump(0x157DD3_b, utils::hook::assemble([](utils::hook::assembler& a)
{
a.lea(r8, qword_ptr(rsp, 0x20));
a.xor_(ecx, ecx);
// delay console commands until the initialization is done a.pushad64();
utils::hook::call(0x1400D808C, execute_console_command); a.call_aligned(execute_startup_command);
utils::hook::nop(0x1400D80A4, 5); a.popad64();
a.jmp(0x157DDF_b);
}), true);//
// delay console commands until the initialization is done // COULDN'T FOUND
// utils::hook::call(0x1400D808C, execute_console_command);
// utils::hook::nop(0x1400D80A4, 5);
// patch GScr_SetDynamicDvar to behave better // patch GScr_SetDynamicDvar to behave better
gscr_set_dynamic_dvar_hook.create(0x14036B600, &gscr_set_dynamic_dvar); gscr_set_dynamic_dvar_hook.create(0x43CF60_b, &gscr_set_dynamic_dvar);
utils::hook::nop(0x1404ED90E, 5); // don't load config file utils::hook::nop(0x189514_b, 248); // don't load config file
utils::hook::nop(0x140403D92, 5); // ^ utils::hook::nop(0x156C46_b, 5); // ^
utils::hook::set<uint8_t>(0x1400DC1D0, 0xC3); // don't save config file utils::hook::set<uint8_t>(0x17F470_b, 0xC3); // don't save config file
utils::hook::set<uint8_t>(0x140274710, 0xC3); // disable self-registration utils::hook::set<uint8_t>(0x351AA0_b, 0xC3); // disable self-registration
utils::hook::set<uint8_t>(0x140515890, 0xC3); // init sound system (1) utils::hook::set<uint8_t>(0x5BF4E0_b, 0xC3); // init sound system (1)
utils::hook::set<uint8_t>(0x1406574F0, 0xC3); // init sound system (2) utils::hook::set<uint8_t>(0x701820_b, 0xC3); // init sound system (2)
utils::hook::set<uint8_t>(0x140620D10, 0xC3); // render thread utils::hook::set<uint8_t>(0x701850_b, 0xC3); // init sound system (3)
utils::hook::set<uint8_t>(0x14025B850, 0xC3); // called from Com_Frame, seems to do renderer stuff utils::hook::set<uint8_t>(0x6C9B10_b, 0xC3); // render thread
utils::hook::set<uint8_t>(0x1402507B0, 0xC3); // CL_CheckForResend, which tries to connect to the local server constantly utils::hook::set<uint8_t>(0x343950_b, 0xC3); // called from Com_Frame, seems to do renderer stuff
utils::hook::set<uint8_t>(0x1405D5178, 0x00); // r_loadForRenderer default to 0 utils::hook::set<uint8_t>(0x12CCA0_b, 0xC3); // CL_CheckForResend, which tries to connect to the local server constantly
utils::hook::set<uint8_t>(0x14050C2D0, 0xC3); // recommended settings check - TODO: Check hook utils::hook::set<uint8_t>(0x67ADCE_b, 0x00); // r_loadForRenderer default to 0
utils::hook::set<uint8_t>(0x140514C00, 0xC3); // some mixer-related function called on shutdown utils::hook::set<uint8_t>(0x5B7AF0_b, 0xC3); // recommended settings check
utils::hook::set<uint8_t>(0x140409830, 0xC3); // dont load ui gametype stuff utils::hook::set<uint8_t>(0x5BE850_b, 0xC3); // some mixer-related function called on shutdown
utils::hook::set<uint8_t>(0x4DEA50_b, 0xC3); // dont load ui gametype stuff
utils::hook::nop(0x140481B06, 6); // unknown check in SV_ExecuteClientMessage utils::hook::nop(0x54ED81_b, 6); // unknown check in SV_ExecuteClientMessage
utils::hook::nop(0x140480FAC, 4); // allow first slot to be occupied utils::hook::nop(0x54E337_b, 4); // allow first slot to be occupied
utils::hook::nop(0x14025619B, 2); // properly shut down dedicated servers utils::hook::nop(0x13ABCB_b, 2); // properly shut down dedicated servers
utils::hook::nop(0x14025615E, 2); // ^ utils::hook::nop(0x13AB8E_b, 2); // ^
utils::hook::nop(0x1402561C0, 5); // don't shutdown renderer utils::hook::nop(0x13ABF0_b, 5); // don't shutdown renderer
utils::hook::set<uint8_t>(0x140091840, 0xC3); // something to do with blendShapeVertsView utils::hook::set<uint8_t>(0xAA290_b, 0xC3); // something to do with blendShapeVertsView
utils::hook::nop(0x140659A0D, 8); // sound thing utils::hook::nop(0x70465D_b, 8); // sound thing
// (COULD NOT FIND IN H1) // (COULD NOT FIND IN H1)
// utils::hook::set<uint8_t>(0x1404D6960, 0xC3); // cpu detection stuff? // utils::hook::set<uint8_t>(0x1404D6960, 0xC3); // cpu detection stuff?
utils::hook::set<uint8_t>(0x1405E97F0, 0xC3); // gfx stuff during fastfile loading utils::hook::set<uint8_t>(0x690F30_b, 0xC3); // gfx stuff during fastfile loading
utils::hook::set<uint8_t>(0x1405E9700, 0xC3); // ^ utils::hook::set<uint8_t>(0x690E00_b, 0xC3); // ^
utils::hook::set<uint8_t>(0x1405E9790, 0xC3); // ^ utils::hook::set<uint8_t>(0x690ED0_b, 0xC3); // ^
utils::hook::set<uint8_t>(0x1402C1180, 0xC3); // ^ utils::hook::set<uint8_t>(0x39B980_b, 0xC3); // ^
utils::hook::set<uint8_t>(0x1405E9750, 0xC3); // ^ utils::hook::set<uint8_t>(0x690E50_b, 0xC3); // ^
utils::hook::set<uint8_t>(0x1405AD5B0, 0xC3); // directx stuff utils::hook::set<uint8_t>(0x651BA0_b, 0xC3); // directx stuff
utils::hook::set<uint8_t>(0x1405DB150, 0xC3); // ^ utils::hook::set<uint8_t>(0x681950_b, 0xC3); // ^
utils::hook::set<uint8_t>(0x140625220, 0xC3); // ^ - mutex utils::hook::set<uint8_t>(0x6CE390_b, 0xC3); // ^ - mutex
utils::hook::set<uint8_t>(0x1405DB650, 0xC3); // ^ utils::hook::set<uint8_t>(0x681ED0_b, 0xC3); // ^
utils::hook::set<uint8_t>(0x14008B5F0, 0xC3); // rendering stuff utils::hook::set<uint8_t>(0x0A3CD0_b, 0xC3); // rendering stuff
utils::hook::set<uint8_t>(0x1405DB8B0, 0xC3); // ^ utils::hook::set<uint8_t>(0x682150_b, 0xC3); // ^
utils::hook::set<uint8_t>(0x1405DB9C0, 0xC3); // ^ utils::hook::set<uint8_t>(0x682260_b, 0xC3); // ^
utils::hook::set<uint8_t>(0x1405DC050, 0xC3); // ^ utils::hook::set<uint8_t>(0x6829C0_b, 0xC3); // ^
utils::hook::set<uint8_t>(0x1405DCBA0, 0xC3); // ^ utils::hook::set<uint8_t>(0x6834A0_b, 0xC3); // ^
utils::hook::set<uint8_t>(0x1405DD240, 0xC3); // ^ utils::hook::set<uint8_t>(0x683B40_b, 0xC3); // ^
// shaders // shaders
utils::hook::set<uint8_t>(0x1400916A0, 0xC3); // ^ utils::hook::set<uint8_t>(0x0AA090_b, 0xC3); // ^
utils::hook::set<uint8_t>(0x140091610, 0xC3); // ^ utils::hook::set<uint8_t>(0x0A9FE0_b, 0xC3); // ^
utils::hook::set<uint8_t>(0x14061ACC0, 0xC3); // ^ - mutex utils::hook::set<uint8_t>(0x6C38D0_b, 0xC3); // ^ - mutex
utils::hook::set<uint8_t>(0x140516080, 0xC3); // idk utils::hook::set<uint8_t>(0x5BFD10_b, 0xC3); // idk
utils::hook::set<uint8_t>(0x1405AE5F0, 0xC3); // ^ utils::hook::set<uint8_t>(0x652E10_b, 0xC3); // ^
utils::hook::set<uint8_t>(0x1405E0B30, 0xC3); // R_Shutdown utils::hook::set<uint8_t>(0x687D20_b, 0xC3); // R_Shutdown
utils::hook::set<uint8_t>(0x1405AE400, 0xC3); // shutdown stuff utils::hook::set<uint8_t>(0x652BA0_b, 0xC3); // shutdown stuff
utils::hook::set<uint8_t>(0x1405E0C00, 0xC3); // ^ utils::hook::set<uint8_t>(0x687DF0_b, 0xC3); // ^
utils::hook::set<uint8_t>(0x1405DFE50, 0xC3); // ^ utils::hook::set<uint8_t>(0x686DE0_b, 0xC3); // ^
// utils::hook::set<uint8_t>(0x1404B67E0, 0xC3); // sound crashes (H1 - questionable, function looks way different) // utils::hook::set<uint8_t>(0x1404B67E0, 0xC3); // sound crashes (H1 - questionable, function looks way different)
utils::hook::set<uint8_t>(0x14048B660, 0xC3); // disable host migration utils::hook::set<uint8_t>(0x556250_b, 0xC3); // disable host migration
utils::hook::set<uint8_t>(0x14042B2E0, 0xC3); // render synchronization lock utils::hook::set<uint8_t>(0x4F7C10_b, 0xC3); // render synchronization lock
utils::hook::set<uint8_t>(0x14042B210, 0xC3); // render synchronization unlock utils::hook::set<uint8_t>(0x4F7B40_b, 0xC3); // render synchronization unlock
utils::hook::set<uint8_t>(0x140176D2D, 0xEB); // LUI: Unable to start the LUI system due to errors in main.lua utils::hook::set<uint8_t>(0x27AA9D_b, 0xEB); // LUI: Unable to start the LUI system due to errors in main.lua
utils::hook::set<uint8_t>(0x27AAC5_b, 0xEB); // LUI: Unable to start the LUI system due to errors in depot.lua
utils::hook::set<uint8_t>(0x27AADC_b, 0xEB); // ^
utils::hook::nop(0x140506ECE, 5); // Disable sound pak file loading utils::hook::nop(0x5B25BE_b, 5); // Disable sound pak file loading
utils::hook::nop(0x140506ED6, 2); // ^ utils::hook::nop(0x5B25C6_b, 2); // ^
utils::hook::set<uint8_t>(0x1402C5910, 0xC3); // Disable image pak file loading utils::hook::set<uint8_t>(0x3A0BA0_b, 0xC3); // Disable image pak file loading
// Reduce min required memory // Reduce min required memory
utils::hook::set<uint64_t>(0x14050C717, 0x80000000); utils::hook::set<uint64_t>(0x5B7F37_b, 0x80000000);
utils::hook::set(0x1402BF7F0, 0xC3); // some loop utils::hook::set<uint8_t>(0x399E10_b, 0xC3); // some loop
utils::hook::set(0x14007E150, 0xC3); // related to shader caching / techsets / fastfiles utils::hook::set<uint8_t>(0x1D48B0_b, 0xC3); // related to shader caching / techsets / fastfilesc
utils::hook::set<uint8_t>(0x3A1940_b, 0xC3); // DB_ReadPackedLoadedSounds
// Workaround for server spamming 'exec default_xboxlive.cfg' when not running
ui_set_active_menu_hook.create(0x1E4D80_b, ui_set_active_menu_stub);
// initialize the game after onlinedataflags is 32 (workaround) // initialize the game after onlinedataflags is 32 (workaround)
scheduler::schedule([=]() scheduler::schedule([=]()
@ -318,14 +356,14 @@ namespace dedicated
execute_startup_command_queue(); execute_startup_command_queue();
execute_console_command_queue(); execute_console_command_queue();
// Send heartbeat to dpmaster // Send heartbeat to master
scheduler::once(send_heartbeat, scheduler::pipeline::server); scheduler::once(send_heartbeat, scheduler::pipeline::server);
scheduler::loop(send_heartbeat, scheduler::pipeline::server, 10min); scheduler::loop(send_heartbeat, scheduler::pipeline::server, 10min);
command::add("heartbeat", send_heartbeat); command::add("heartbeat", send_heartbeat);
}, scheduler::pipeline::main, 1s); }, scheduler::pipeline::main, 1s);
command::add("killserver", kill_server); command::add("killserver", kill_server);
com_quit_f_hook.create(0x1400DA640, &kill_server); com_quit_f_hook.create(0x17CD00_b, &kill_server);
} }
}; };
} }

View File

@ -1,5 +1,4 @@
#include <std_include.hpp> #include <std_include.hpp>
#include "console.hpp"
#include "loader/component_loader.hpp" #include "loader/component_loader.hpp"
#include "game/game.hpp" #include "game/game.hpp"
#include "scheduler.hpp" #include "scheduler.hpp"
@ -20,9 +19,9 @@ namespace dedicated_info
scheduler::loop([]() scheduler::loop([]()
{ {
auto* sv_running = game::Dvar_FindVar("sv_running"); auto* sv_running = game::Dvar_FindVar("sv_running");
if (!sv_running || !sv_running->current.enabled) if (!sv_running || !sv_running->current.enabled || (*game::mp::svs_clients) == nullptr)
{ {
console::set_title("H1-Mod Dedicated Server"); SetConsoleTitle("H1-Mod Dedicated Server");
return; return;
} }
@ -33,12 +32,14 @@ namespace dedicated_info
auto bot_count = 0; auto bot_count = 0;
auto client_count = 0; auto client_count = 0;
for (auto i = 0; i < sv_maxclients->current.integer; i++) const auto svs_clients = *game::mp::svs_clients;
for (auto i = 0; i < *game::mp::svs_numclients; i++)
{ {
auto* client = &game::mp::svs_clients[i]; const auto client = svs_clients[i];
auto* self = &game::mp::g_entities[i]; auto* self = &game::mp::g_entities[i];
if (client->header.state >= 1 && self && self->client) if (client.header.state >= 1 && self && self->client)
{ {
client_count++; client_count++;
if (game::SV_BotIsBot(i)) if (game::SV_BotIsBot(i))
@ -54,9 +55,10 @@ namespace dedicated_info
utils::string::strip(sv_hostname->current.string, cleaned_hostname.data(), utils::string::strip(sv_hostname->current.string, cleaned_hostname.data(),
static_cast<int>(strlen(sv_hostname->current.string)) + 1); static_cast<int>(strlen(sv_hostname->current.string)) + 1);
console::set_title(utils::string::va("%s on %s [%d/%d] (%d)", cleaned_hostname.data(), SetConsoleTitle(utils::string::va("%s on %s [%d/%d] (%d)", cleaned_hostname.data(),
mapname->current.string, client_count, mapname->current.string, client_count,
sv_maxclients->current.integer, bot_count)); sv_maxclients->current.integer, bot_count)
);
}, scheduler::pipeline::main, 1s); }, scheduler::pipeline::main, 1s);
} }
}; };

View File

@ -297,7 +297,7 @@ namespace demonware
if (server) if (server)
{ {
server->handle_input(buf, len, {s, to, tolen}); server->handle_input(buf, len, { s, to, tolen });
return len; return len;
} }
@ -425,9 +425,9 @@ namespace demonware
} }
} }
void bd_logger_stub(char* a1, void* a2, void* a3, void* a4, const char* function, ...) void bd_logger_stub()
{ {
//printf("logged\n");
} }
#ifdef DEBUG #ifdef DEBUG
@ -481,6 +481,39 @@ namespace demonware
printf("bdAuth: Unknown error\n"); printf("bdAuth: Unknown error\n");
} }
#endif #endif
utils::hook::detour handle_auth_reply_hook;
bool handle_auth_reply_stub(void* a1, void* a2, void* a3)
{
// Skip bdAuth::validateResponseSignature
utils::hook::set(0x7D4AB0_b, 0xC301B0);
// Skip bdAuth::processPlatformData
utils::hook::set(0x7D55C0_b, 0xC301B0);
return handle_auth_reply_hook.invoke<bool>(a1, a2, a3);
}
void* allocate_somewhere_near(uint8_t* base_address)
{
const size_t PAGE_SIZE = 0x1000;
size_t offset = 0;
while (true)
{
offset += PAGE_SIZE;
auto res = VirtualAlloc(base_address - offset, PAGE_SIZE, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
if (res)
{
std::memset(res, 0, PAGE_SIZE);
return res;
}
}
}
void request_start_match_stub()
{
const auto* args = "StartServer";
game::UI_RunMenuScript(0, &args);
}
} }
class component final : public component_interface class component final : public component_interface
@ -505,6 +538,11 @@ namespace demonware
void post_load() override void post_load() override
{ {
if (game::environment::is_sp())
{
return;
}
server_thread = utils::thread::create_named_thread("Demonware", server_main); server_thread = utils::thread::create_named_thread("Demonware", server_main);
} }
@ -537,52 +575,44 @@ namespace demonware
void post_unpack() override void post_unpack() override
{ {
utils::hook::jump(SELECT_VALUE(0x140610320, 0x1407400B0), bd_logger_stub);
if (game::environment::is_sp()) if (game::environment::is_sp())
{ {
utils::hook::set<uint8_t>(0x1405FCA00, 0xC3); // bdAuthSteam utils::hook::set<uint8_t>(0x68DDA0_b, 0xC3); // bdAuthSteam
utils::hook::set<uint8_t>(0x140333A00, 0xC3); // dwNet utils::hook::set<uint8_t>(0x366600_b, 0xC3); // dwNet
return; return;
} }
utils::hook::set<uint8_t>(0x140715039, 0x0); // CURLOPT_SSL_VERIFYPEER utils::hook::set<uint8_t>(0x7C0AD9_b, 0x0); // CURLOPT_SSL_VERIFYPEER
utils::hook::set<uint8_t>(0x140715025, 0xAF); // CURLOPT_SSL_VERIFYHOST utils::hook::set<uint8_t>(0x7C0AC5_b, 0xAF); // CURLOPT_SSL_VERIFYHOST
utils::hook::set<uint8_t>(0x14095433C, 0x0); // HTTPS -> HTTP utils::hook::set<uint8_t>(0xA1327C_b, 0x0); // HTTPS -> HTTP
//HTTPS -> HTTP std::memcpy(reinterpret_cast<void*>(0x8D0298_b),
utils::hook::inject(0x14006DDA9, "http://prod.umbrella.demonware.net/v1.0/"); "http://prod.umbrella.demonware.net/v1.0/", sizeof("http://prod.umbrella.demonware.net/v1.0/"));
utils::hook::inject(0x14006E11C, "http://prod.umbrella.demonware.net/v1.0/"); std::memcpy(reinterpret_cast<void*>(0x8D05A8_b),
utils::hook::inject(0x14006E2FB, "http://prod.umbrella.demonware.net/v1.0/"); "http://prod.uno.demonware.net/v1.0/", sizeof("http://prod.uno.demonware.net/v1.0/"));
utils::hook::inject(0x14006E9A9, "http://prod.uno.demonware.net/v1.0/"); std::memcpy(reinterpret_cast<void*>(0x9EDB08_b), "http://%s:%d/auth/", sizeof("http://%s:%d/auth/"));
utils::hook::inject(0x14006ED49, "http://prod.uno.demonware.net/v1.0/");
utils::hook::inject(0x140728170, "http://%s:%d/auth/");
utils::hook::set<uint8_t>(0x14047F290, 0xC3); // SV_SendMatchData // utils::hook::set<uint8_t>(0x19F8C0_b, 0xC3); // SV_SendMatchData, not sure
utils::hook::set<uint8_t>(0x140598990, 0xC3); // Live_CheckForFullDisconnect utils::hook::nop(0x19BB67_b, 5); // LiveStorage_SendMatchDataComplete
utils::hook::nop(0x19BC3F_b, 5); // LiveStorage_GettingStoreConfigComplete probably
utils::hook::set<uint8_t>(0x1A3340_b, 0xC3); // Live_CheckForFullDisconnect
#ifdef DEBUG // Remove some while loop that freezes the rendering for a few secs while connecting
// yes utils::hook::nop(0x625555_b, 5);
utils::hook::call(0x140727BEB, l);
utils::hook::call(0x140727AFC, i); handle_auth_reply_hook.create(0x7AC600_b, handle_auth_reply_stub);
utils::hook::call(0x140727E49, h);
utils::hook::call(0x140727E30, g); // Skip update check in Live_SyncOnlineDataFlags
utils::hook::call(0x140727E37, f); utils::hook::set(0x47A6D0_b, 0xC301B0);
utils::hook::call(0x140727DF2, e); // Remove update failed popup
utils::hook::call(0x140727DF9, d); utils::hook::set(0x47B2B0_b, 0xC301B0);
utils::hook::call(0x140727CFC, c);
utils::hook::call(0x140727C82, b); // xpartygo -> just start the match
utils::hook::call(0x140727E6A, a); utils::hook::jump(0x355B80_b, request_start_match_stub, true);
#endif
// Checks X-Signature header or something utils::hook::set(0x396AD0_b, 0xC301B0); // DB_IsZoneLoaded("ffotd")
utils::hook::set(0x140728380, 0xC301B0); utils::hook::set(0x4DD600_b, 0xC300B0); // dont use ffotd
// Checks extended_data and extra_data in json object utils::hook::set(0x4DD5B0_b, 0xC300B0); // dont dl ffotd
utils::hook::set(0x140728E90, 0xC301B0);
// Update check
utils::hook::set(0x1403A5390, 0xC301B0);
// Remove some while loop in demonware that freezes the rendering for a few secs at launch
utils::hook::nop(0x14057DBC5, 5);
} }
void pre_destroy() override void pre_destroy() override

View File

@ -10,7 +10,6 @@
#include "materials.hpp" #include "materials.hpp"
#include "discord.hpp" #include "discord.hpp"
#include "ui_scripting.hpp"
#include "game/ui_scripting/execution.hpp" #include "game/ui_scripting/execution.hpp"
#include <utils/string.hpp> #include <utils/string.hpp>
@ -53,7 +52,6 @@ namespace discord
discord_presence.matchSecret = ""; discord_presence.matchSecret = "";
discord_presence.joinSecret = ""; discord_presence.joinSecret = "";
discord_presence.partyId = ""; discord_presence.partyId = "";
discord_presence.state = "";
} }
else else
{ {
@ -103,7 +101,16 @@ namespace discord
discord_presence.partyPrivacy = DISCORD_PARTY_PUBLIC; discord_presence.partyPrivacy = DISCORD_PARTY_PUBLIC;
} }
discord_presence.partySize = *reinterpret_cast<int*>(0x1429864C4); const auto client_state = *game::mp::client_state;
if (client_state != nullptr)
{
discord_presence.partySize = client_state->num_players;
}
else
{
discord_presence.partySize = 0;
}
discord_presence.partyMax = max_clients; discord_presence.partyMax = max_clients;
discord_presence.state = clean_hostname; discord_presence.state = clean_hostname;
discord_presence.largeImageKey = map; discord_presence.largeImageKey = map;
@ -188,9 +195,13 @@ namespace discord
handlers.ready = ready; handlers.ready = ready;
handlers.errored = errored; handlers.errored = errored;
handlers.disconnected = errored; handlers.disconnected = errored;
handlers.joinGame = join_game;
handlers.spectateGame = nullptr; handlers.spectateGame = nullptr;
handlers.joinRequest = join_request;
if (game::environment::is_mp())
{
handlers.joinGame = join_game;
handlers.joinRequest = join_request;
}
Discord_Initialize("947125042930667530", &handlers, 1, nullptr); Discord_Initialize("947125042930667530", &handlers, 1, nullptr);
@ -285,4 +296,4 @@ namespace discord
}; };
} }
REGISTER_COMPONENT(discord::component) REGISTER_COMPONENT(discord::component)

View File

@ -83,92 +83,45 @@ namespace dvar_cheats
return true; return true;
} }
const auto dvar_flag_checks_stub = utils::hook::assemble([](utils::hook::assembler& a) void* get_dvar_flag_checks_stub()
{ {
const auto can_set_value = a.newLabel(); return utils::hook::assemble([](utils::hook::assembler& a)
const auto zero_source = a.newLabel();
a.pushad64();
a.mov(r8, rdi);
a.mov(edx, esi);
a.mov(rcx, rbx);
a.call_aligned(apply_sv_cheats); //check if we are setting sv_cheats
a.popad64();
a.cmp(esi, 0);
a.jz(zero_source); //if the SetSource is 0 (INTERNAL) ignore flag checks
a.pushad64();
a.mov(edx, esi); //source
a.mov(rcx, rbx); //dvar
a.call_aligned(dvar_flag_checks); //protect read/write/cheat/replicated dvars
a.cmp(al, 1);
a.jz(can_set_value);
// if we get here, we are non-zero source and CANNOT set values
a.popad64(); // if I do this before the jz it won't work. for some reason the popad64 is affecting the ZR flag
a.jmp(0x1404FDCAB);
// if we get here, we are non-zero source and CAN set values
a.bind(can_set_value);
a.popad64(); // if I do this before the jz it won't work. for some reason the popad64 is affecting the ZR flag
a.cmp(esi, 1);
a.jmp(0x1404FDA22);
// if we get here, we are zero source and ignore flags
a.bind(zero_source);
a.jmp(0x1404FDA62);
});
void cg_set_client_dvar_from_server(const int local_client_num, void* cg, const char* dvar_id, const char* value)
{
const auto* dvar = game::Dvar_FindVar(dvar_id);
if (dvar)
{ {
// If we send as string, it can't be set with source SERVERCMD because the game only allows that source on real server cmd dvars. const auto can_set_value = a.newLabel();
// Just use external instead as if it was being set by the console const auto zero_source = a.newLabel();
game::Dvar_SetFromStringByNameFromSource(dvar_id, value, game::DvarSetSource::DVAR_SOURCE_EXTERNAL);
} a.pushad64();
else a.mov(r8, rdi);
{ a.mov(edx, esi);
// Not a dvar name, assume it is an id and the game will handle normally a.mov(rcx, rbx);
game::CG_SetClientDvarFromServer(local_client_num, cg, dvar_id, value); a.call_aligned(apply_sv_cheats); // check if we are setting sv_cheats
} a.popad64();
a.cmp(esi, 0);
a.jz(zero_source); // if the SetSource is 0 (INTERNAL) ignore flag checks
a.pushad64();
a.mov(edx, esi); // source
a.mov(rcx, rbx); // dvar
a.call_aligned(dvar_flag_checks); // protect read/write/cheat/replicated dvars
a.cmp(al, 1);
a.jz(can_set_value);
// if we get here, we are non-zero source and CANNOT set values
a.popad64(); // if I do this before the jz it won't work. for some reason the popad64 is affecting the ZR flag
a.jmp(0x18655C_b);
// if we get here, we are non-zero source and CAN set values
a.bind(can_set_value);
a.popad64(); // if I do this before the jz it won't work. for some reason the popad64 is affecting the ZR flag
a.cmp(esi, 1);
a.jmp(0x1861EE_b);
// if we get here, we are zero source and ignore flags
a.bind(zero_source);
a.jmp(0x18628F_b);
});
} }
void set_client_dvar_by_string(const int entity_num, const char* value)
{
const auto* dvar = game::Scr_GetString(0); // grab the original dvar again since it's never stored on stack
const auto* command = utils::string::va("q %s \"%s\"", dvar, value);
game::SV_GameSendServerCommand(entity_num, game::SV_CMD_RELIABLE, command);
}
const auto player_cmd_set_client_dvar = utils::hook::assemble([](utils::hook::assembler& a)
{
const auto set_by_string = a.newLabel();
a.pushad64();
// check if we didn't find a network dvar index
a.mov(ecx, dword_ptr(rsp, 0x8C8));
a.cmp(ecx, 0);
a.je(set_by_string);
// we found an index, handle normally
a.popad64();
a.mov(r8d, ptr(rsp, 0x848));
a.lea(r9, ptr(rsp, 0x30));
a.jmp(0x1402E2E57);
// no index, let's send the dvar as a string
a.bind(set_by_string);
a.movzx(ecx, word_ptr(rsp, 0x8C0)); //entity_num
a.lea(rdx, ptr(rsp, 0xB0)); //value
a.call_aligned(set_client_dvar_by_string);
a.popad64();
a.jmp(0x1402E2E7D);
});
class component final : public component_interface class component final : public component_interface
{ {
public: public:
@ -179,20 +132,13 @@ namespace dvar_cheats
return; return;
} }
utils::hook::nop(0x1404FDA0D, 4); // let our stub handle zero-source sets utils::hook::nop(0x1861D4_b, 8); // let our stub handle zero-source sets
utils::hook::jump(0x1404FDA14, dvar_flag_checks_stub, true); // check extra dvar flags when setting values utils::hook::jump(0x1861DF_b, get_dvar_flag_checks_stub(), true); // check extra dvar flags when setting values
// utils::hook::nop(0x14032AACC, 5); // remove error in PlayerCmd_SetClientDvar if setting a non-network dvar
// utils::hook::set<uint8_t>(0x14032AA9B, 0xEB);
// don't check flags on the dvars, send any existing dvar instead
// utils::hook::jump(0x14032AB14, player_cmd_set_client_dvar, true); // send non-network dvars as string
// utils::hook::call(0x1401BB782, cg_set_client_dvar_from_server);
// check for dvars being sent as string before parsing ids
scheduler::once([]() scheduler::once([]()
{ {
dvars::register_bool("sv_cheats", false, game::DvarFlags::DVAR_FLAG_REPLICATED, dvars::register_bool("sv_cheats", false, game::DvarFlags::DVAR_FLAG_REPLICATED,
"Allow cheat commands and dvars on this server"); "Allow cheat commands and dvars on this server");
}, scheduler::pipeline::main); }, scheduler::pipeline::main);
} }
}; };

View File

@ -35,7 +35,6 @@ namespace dvars
struct dvar_vector3 : dvar_base struct dvar_vector3 : dvar_base
{ {
float x{}; float x{};
float y{}; float y{};
float z{}; float z{};
@ -43,6 +42,12 @@ namespace dvars
float max{}; float max{};
}; };
struct dvar_enum : dvar_base
{
const char* const* value_list{};
int default_index{};
};
struct dvar_int : dvar_base struct dvar_int : dvar_base
{ {
int value{}; int value{};
@ -139,6 +144,7 @@ namespace dvars
static std::unordered_map<std::string, dvar_string> register_string_overrides; static std::unordered_map<std::string, dvar_string> register_string_overrides;
static std::unordered_map<std::string, dvar_vector2> register_vector2_overrides; static std::unordered_map<std::string, dvar_vector2> register_vector2_overrides;
static std::unordered_map<std::string, dvar_vector3> register_vector3_overrides; static std::unordered_map<std::string, dvar_vector3> register_vector3_overrides;
static std::unordered_map<std::string, dvar_enum> register_enum_overrides;
static std::unordered_map<std::string, bool> set_bool_overrides; static std::unordered_map<std::string, bool> set_bool_overrides;
static std::unordered_map<std::string, float> set_float_overrides; static std::unordered_map<std::string, float> set_float_overrides;
@ -210,6 +216,16 @@ namespace dvars
register_vector3_overrides[name] = std::move(values); register_vector3_overrides[name] = std::move(values);
} }
void register_enum(const std::string& name, /*const char* const* value_list, int default_index,*/
const unsigned int flags)
{
dvar_enum values;
//values.value_list = value_list;
//values.default_index = default_index;
values.flags = flags;
register_enum_overrides[name] = std::move(values);
}
void set_bool(const std::string& name, const bool value) void set_bool(const std::string& name, const bool value)
{ {
set_bool_overrides[name] = value; set_bool_overrides[name] = value;
@ -237,11 +253,15 @@ namespace dvars
} }
utils::hook::detour dvar_register_bool_hook; utils::hook::detour dvar_register_bool_hook;
utils::hook::detour dvar_register_bool_hashed_hook;
utils::hook::detour dvar_register_float_hook; utils::hook::detour dvar_register_float_hook;
utils::hook::detour dvar_register_float_hashed_hook;
utils::hook::detour dvar_register_int_hook; utils::hook::detour dvar_register_int_hook;
utils::hook::detour dvar_register_int_hashed_hook;
utils::hook::detour dvar_register_string_hook; utils::hook::detour dvar_register_string_hook;
utils::hook::detour dvar_register_vector2_hook; utils::hook::detour dvar_register_vector2_hook;
utils::hook::detour dvar_register_vector3_hook; utils::hook::detour dvar_register_vector3_hook;
utils::hook::detour dvar_register_enum_hook;
utils::hook::detour dvar_set_bool_hook; utils::hook::detour dvar_set_bool_hook;
utils::hook::detour dvar_set_float_hook; utils::hook::detour dvar_set_float_hook;
@ -261,6 +281,18 @@ namespace dvars
return dvar_register_bool_hook.invoke<game::dvar_t*>(hash, name, value, flags); return dvar_register_bool_hook.invoke<game::dvar_t*>(hash, name, value, flags);
} }
game::dvar_t* dvar_register_bool_hashed(const int hash, const char* name, bool value, unsigned int flags)
{
auto* var = find_dvar(override::register_bool_overrides, hash);
if (var)
{
value = var->value;
flags = var->flags;
}
return dvar_register_bool_hashed_hook.invoke<game::dvar_t*>(hash, name, value, flags);
}
game::dvar_t* dvar_register_float(const int hash, const char* name, float value, float min, float max, unsigned int flags) game::dvar_t* dvar_register_float(const int hash, const char* name, float value, float min, float max, unsigned int flags)
{ {
auto* var = find_dvar(override::register_float_overrides, hash); auto* var = find_dvar(override::register_float_overrides, hash);
@ -275,6 +307,20 @@ namespace dvars
return dvar_register_float_hook.invoke<game::dvar_t*>(hash, name, value, min, max, flags); return dvar_register_float_hook.invoke<game::dvar_t*>(hash, name, value, min, max, flags);
} }
game::dvar_t* dvar_register_float_hashed(const int hash, const char* name, float value, float min, float max, unsigned int flags)
{
auto* var = find_dvar(override::register_float_overrides, hash);
if (var)
{
value = var->value;
min = var->min;
max = var->max;
flags = var->flags;
}
return dvar_register_float_hashed_hook.invoke<game::dvar_t*>(hash, name, value, min, max, flags);
}
game::dvar_t* dvar_register_int(const int hash, const char* name, int value, int min, int max, unsigned int flags) game::dvar_t* dvar_register_int(const int hash, const char* name, int value, int min, int max, unsigned int flags)
{ {
auto* var = find_dvar(override::register_int_overrides, hash); auto* var = find_dvar(override::register_int_overrides, hash);
@ -289,6 +335,20 @@ namespace dvars
return dvar_register_int_hook.invoke<game::dvar_t*>(hash, name, value, min, max, flags); return dvar_register_int_hook.invoke<game::dvar_t*>(hash, name, value, min, max, flags);
} }
game::dvar_t* dvar_register_int_hashed(const int hash, const char* name, int value, int min, int max, unsigned int flags)
{
auto* var = find_dvar(override::register_int_overrides, hash);
if (var)
{
value = var->value;
min = var->min;
max = var->max;
flags = var->flags;
}
return dvar_register_int_hashed_hook.invoke<game::dvar_t*>(hash, name, value, min, max, flags);
}
game::dvar_t* dvar_register_string(const int hash, const char* name, const char* value, unsigned int flags) game::dvar_t* dvar_register_string(const int hash, const char* name, const char* value, unsigned int flags)
{ {
auto* var = find_dvar(override::register_string_overrides, hash); auto* var = find_dvar(override::register_string_overrides, hash);
@ -334,6 +394,19 @@ namespace dvars
return dvar_register_vector3_hook.invoke<game::dvar_t*>(hash, name, x, y, z, min, max, flags); return dvar_register_vector3_hook.invoke<game::dvar_t*>(hash, name, x, y, z, min, max, flags);
} }
game::dvar_t* dvar_register_enum(const int hash, const char* name, const char* const value_list, int default_index, unsigned int flags)
{
auto* var = find_dvar(override::register_enum_overrides, hash);
if (var)
{
//value_list = var->value_list;
//default_index = var->default_index;
flags = var->flags;
}
return dvar_register_enum_hook.invoke<game::dvar_t*>(hash, name, value_list, default_index, flags);
}
void dvar_set_bool(game::dvar_t* dvar, bool boolean) void dvar_set_bool(game::dvar_t* dvar, bool boolean)
{ {
const auto disabled = find_dvar(disable::set_bool_disables, dvar->hash); const auto disabled = find_dvar(disable::set_bool_disables, dvar->hash);
@ -424,18 +497,26 @@ namespace dvars
public: public:
void post_unpack() override void post_unpack() override
{ {
dvar_register_bool_hook.create(SELECT_VALUE(0x1403C47E0, 0x1404FA540), &dvar_register_bool); dvar_register_bool_hook.create(SELECT_VALUE(0x419220_b, 0x182340_b), &dvar_register_bool);
dvar_register_float_hook.create(SELECT_VALUE(0x1403C4BB0, 0x1404FA910), &dvar_register_float); dvar_register_float_hook.create(SELECT_VALUE(0x4195F0_b, 0x1827F0_b), &dvar_register_float);
dvar_register_int_hook.create(SELECT_VALUE(0x1403C4CC0, 0x1404FAA20), &dvar_register_int); dvar_register_int_hook.create(SELECT_VALUE(0x419700_b, 0x182A10_b), &dvar_register_int);
dvar_register_string_hook.create(SELECT_VALUE(0x1403C4DA0, 0x1404FAB00), &dvar_register_string); dvar_register_string_hook.create(SELECT_VALUE(0x4197E0_b, 0x182BD0_b), &dvar_register_string);
dvar_register_vector2_hook.create(SELECT_VALUE(0x1403C4E80, 0x1404FABE0), &dvar_register_vector2); dvar_register_vector2_hook.create(SELECT_VALUE(0x4198C0_b, 0x182CB0_b), &dvar_register_vector2);
dvar_register_vector3_hook.create(SELECT_VALUE(0x1403C4FC0, 0x1404FACE0), &dvar_register_vector3); dvar_register_vector3_hook.create(SELECT_VALUE(0x419A00_b, 0x182DB0_b), &dvar_register_vector3);
dvar_register_enum_hook.create(SELECT_VALUE(0x419500_b, 0x182700_b), &dvar_register_enum);
dvar_set_bool_hook.create(SELECT_VALUE(0x1403C7020, 0x1404FCDF0), &dvar_set_bool); if (!game::environment::is_sp())
dvar_set_float_hook.create(SELECT_VALUE(0x1403C7420, 0x1404FD360), &dvar_set_float); {
dvar_set_int_hook.create(SELECT_VALUE(0x1403C76C0, 0x1404FD5E0), &dvar_set_int); dvar_register_bool_hashed_hook.create(SELECT_VALUE(0x0, 0x182420_b), &dvar_register_bool_hashed);
dvar_set_string_hook.create(SELECT_VALUE(0x1403C7900, 0x1404FD8D0), &dvar_set_string); dvar_register_float_hashed_hook.create(SELECT_VALUE(0x0, 0x182900_b), &dvar_register_float_hashed);
dvar_set_from_string_hook.create(SELECT_VALUE(0x1403C7620, 0x1404FD520), &dvar_set_from_string); dvar_register_int_hashed_hook.create(SELECT_VALUE(0x0, 0x182AF0_b), &dvar_register_int_hashed);
}
dvar_set_bool_hook.create(SELECT_VALUE(0x41B820_b, 0x185520_b), &dvar_set_bool);
dvar_set_float_hook.create(SELECT_VALUE(0x41BC60_b, 0x185AA0_b), &dvar_set_float);
dvar_set_int_hook.create(SELECT_VALUE(0x41BEE0_b, 0x185D10_b), &dvar_set_int);
dvar_set_string_hook.create(SELECT_VALUE(0x41C0F0_b, 0x186080_b), &dvar_set_string);
dvar_set_from_string_hook.create(SELECT_VALUE(0x41BE20_b, 0x185C60_b), &dvar_set_from_string);
} }
}; };
} }

View File

@ -18,6 +18,7 @@ namespace dvars
void register_string(const std::string& name, const std::string& value, const unsigned int flags); void register_string(const std::string& name, const std::string& value, const unsigned int flags);
void register_vec2(const std::string& name, float x, float y, float min, float max, const unsigned int flags); void register_vec2(const std::string& name, float x, float y, float min, float max, const unsigned int flags);
void register_vec3(const std::string& name, float x, float y, float z, float min, float max, const unsigned int flags); void register_vec3(const std::string& name, float x, float y, float z, float min, float max, const unsigned int flags);
void register_enum(const std::string& name, /*const char* const* value_list, int default_index,*/ const unsigned int flags);
void set_bool(const std::string& name, bool boolean); void set_bool(const std::string& name, bool boolean);
void set_float(const std::string& name, float fl); void set_float(const std::string& name, float fl);

View File

@ -84,9 +84,10 @@ namespace exception
void display_error_dialog() void display_error_dialog()
{ {
std::string error_str = utils::string::va("Fatal error (0x%08X) at 0x%p.\n" std::string error_str = utils::string::va("Fatal error (0x%08X) at 0x%p (0x%p).\n"
"A minidump has been written.\n\n", "A minidump has been written.\n\n",
exception_data.code, exception_data.address); exception_data.code, exception_data.address,
reinterpret_cast<uint64_t>(exception_data.address) - game::base_address);
if (!system_check::is_valid()) if (!system_check::is_valid())
{ {
@ -176,6 +177,7 @@ namespace exception
line("Clean game: "s + (system_check::is_valid() ? "Yes" : "No")); line("Clean game: "s + (system_check::is_valid() ? "Yes" : "No"));
line(utils::string::va("Exception: 0x%08X", exceptioninfo->ExceptionRecord->ExceptionCode)); line(utils::string::va("Exception: 0x%08X", exceptioninfo->ExceptionRecord->ExceptionCode));
line(utils::string::va("Address: 0x%llX", exceptioninfo->ExceptionRecord->ExceptionAddress)); line(utils::string::va("Address: 0x%llX", exceptioninfo->ExceptionRecord->ExceptionAddress));
line(utils::string::va("Base: 0x%llX", game::base_address));
#pragma warning(push) #pragma warning(push)
#pragma warning(disable: 4996) #pragma warning(disable: 4996)

View File

@ -1,12 +1,16 @@
#include <std_include.hpp> #include <std_include.hpp>
#include "loader/component_loader.hpp" #include "loader/component_loader.hpp"
#include "fastfiles.hpp"
#include "game/dvars.hpp"
#include "fastfiles.hpp"
#include "command.hpp" #include "command.hpp"
#include "console.hpp" #include "console.hpp"
#include <utils/hook.hpp> #include <utils/hook.hpp>
#include <utils/concurrency.hpp> #include <utils/concurrency.hpp>
#include <utils/io.hpp>
#include <utils/string.hpp>
namespace fastfiles namespace fastfiles
{ {
@ -15,16 +19,69 @@ namespace fastfiles
namespace namespace
{ {
utils::hook::detour db_try_load_x_file_internal_hook; utils::hook::detour db_try_load_x_file_internal_hook;
utils::hook::detour db_find_xasset_header_hook;
game::dvar_t* g_dump_scripts;
void db_try_load_x_file_internal(const char* zone_name, const int flags) void db_try_load_x_file_internal(const char* zone_name, const int flags)
{ {
printf("Loading fastfile %s\n", zone_name); console::info("Loading fastfile %s\n", zone_name);
current_fastfile.access([&](std::string& fastfile) current_fastfile.access([&](std::string& fastfile)
{ {
fastfile = zone_name; fastfile = zone_name;
}); });
db_try_load_x_file_internal_hook.invoke<void>(zone_name, flags); db_try_load_x_file_internal_hook.invoke<void>(zone_name, flags);
} }
void dump_gsc_script(const std::string& name, game::XAssetHeader header)
{
if (!g_dump_scripts->current.enabled)
{
return;
}
std::string buffer;
buffer.append(header.scriptfile->name, strlen(header.scriptfile->name) + 1);
buffer.append(reinterpret_cast<char*>(&header.scriptfile->compressedLen), 4);
buffer.append(reinterpret_cast<char*>(&header.scriptfile->len), 4);
buffer.append(reinterpret_cast<char*>(&header.scriptfile->bytecodeLen), 4);
buffer.append(header.scriptfile->buffer, header.scriptfile->compressedLen);
buffer.append(header.scriptfile->bytecode, header.scriptfile->bytecodeLen);
const auto out_name = utils::string::va("gsc_dump/%s.gscbin", name.data());
utils::io::write_file(out_name, buffer);
console::info("Dumped %s\n", out_name);
}
game::XAssetHeader db_find_xasset_header_stub(game::XAssetType type, const char* name, int allow_create_default)
{
const auto start = game::Sys_Milliseconds();
const auto result = db_find_xasset_header_hook.invoke<game::XAssetHeader>(type, name, allow_create_default);
const auto diff = game::Sys_Milliseconds() - start;
if (type == game::XAssetType::ASSET_TYPE_SCRIPTFILE)
{
dump_gsc_script(name, result);
}
if (diff > 100)
{
console::print(
result.data == nullptr
? console::con_type_error
: console::con_type_warning,
"Waited %i msec for %sasset \"%s\", of type \"%s\"\n",
diff,
result.data == nullptr
? "missing "
: "",
name,
game::g_assetNames[type]
);
}
return result;
}
} }
std::string get_current_fastfile() std::string get_current_fastfile()
@ -51,7 +108,11 @@ namespace fastfiles
void post_unpack() override void post_unpack() override
{ {
db_try_load_x_file_internal_hook.create( db_try_load_x_file_internal_hook.create(
SELECT_VALUE(0x1401CDDD0, 0x1402BFFE0), &db_try_load_x_file_internal); SELECT_VALUE(0x1F5700_b, 0x39A620_b), &db_try_load_x_file_internal);
db_find_xasset_header_hook.create(game::DB_FindXAssetHeader, db_find_xasset_header_stub);
g_dump_scripts = dvars::register_bool("g_dumpScripts", false, game::DVAR_FLAG_NONE, "Dump GSC scripts");
} }
}; };
} }

View File

@ -12,36 +12,6 @@
namespace filesystem namespace filesystem
{ {
namespace
{
bool custom_path_registered = false;
std::string get_binary_directory()
{
const auto dir = game_module::get_host_module().get_folder();
return utils::string::replace(dir, "/", "\\");
}
void register_custom_path_stub(const char* path, const char* dir)
{
if (!custom_path_registered)
{
custom_path_registered = true;
const auto launcher_dir = get_binary_directory();
game::FS_AddLocalizedGameDirectory(launcher_dir.data(), "data");
}
game::FS_AddLocalizedGameDirectory(path, dir);
}
void fs_startup_stub(const char* gamename)
{
custom_path_registered = false;
game::FS_Startup(gamename);
}
}
file::file(std::string name) file::file(std::string name)
: name_(std::move(name)) : name_(std::move(name))
{ {
@ -110,19 +80,6 @@ namespace filesystem
public: public:
void post_unpack() override void post_unpack() override
{ {
// Set fs_basegame
dvars::override::register_string("fs_basegame", "h1-mod", game::DVAR_FLAG_WRITE);
utils::hook::call(SELECT_VALUE(0x1403B76E2, 0x1404ED3E2), fs_startup_stub);
if (game::environment::is_mp())
{
utils::hook::call(0x1404ED823, fs_startup_stub);
}
utils::hook::call(SELECT_VALUE(0x1403B8D31, 0x1404EE3D0), register_custom_path_stub);
utils::hook::call(SELECT_VALUE(0x1403B8D51, 0x1404EE3F0), register_custom_path_stub);
utils::hook::call(SELECT_VALUE(0x1403B8D90, 0x1404EE42F), register_custom_path_stub);
get_search_paths().insert("."); get_search_paths().insert(".");
get_search_paths().insert("h1-mod"); get_search_paths().insert("h1-mod");
get_search_paths().insert("data"); get_search_paths().insert("data");

View File

@ -114,7 +114,7 @@ namespace fonts
} }
data_.fonts.clear(); data_.fonts.clear();
utils::hook::set<int>(SELECT_VALUE(0x14F09DBB8, 0x14FD61EE8), 0); // reset registered font count utils::hook::set<int>(SELECT_VALUE(0xF793E38_b, 0xE962188_b), 0); // reset registered font count
}); });
} }
@ -128,7 +128,7 @@ namespace fonts
return; return;
} }
utils::hook::call(SELECT_VALUE(0x1404D41B6, 0x1405D9296), db_find_xasset_header_stub); utils::hook::call(SELECT_VALUE(0x55C596_b, 0x67F6E6_b), db_find_xasset_header_stub);
} }
}; };
} }

View File

@ -5,6 +5,7 @@
#include "game/game.hpp" #include "game/game.hpp"
#include "game/dvars.hpp" #include "game/dvars.hpp"
#include "dvars.hpp"
#include <utils/hook.hpp> #include <utils/hook.hpp>
#include <utils/string.hpp> #include <utils/string.hpp>
@ -14,6 +15,8 @@ namespace fps
{ {
namespace namespace
{ {
utils::hook::detour sub_5D6810_hook;
game::dvar_t* cg_drawfps; game::dvar_t* cg_drawfps;
game::dvar_t* cg_drawping; game::dvar_t* cg_drawping;
@ -87,8 +90,6 @@ namespace fps
cg_perf.previous_ms = cg_perf.current_ms; cg_perf.previous_ms = cg_perf.current_ms;
perf_calc_fps(&cg_perf, cg_perf.frame_ms); perf_calc_fps(&cg_perf, cg_perf.frame_ms);
utils::hook::invoke<void>(SELECT_VALUE(0x1405487A0, 0x1406575A0));
} }
void cg_draw_fps() void cg_draw_fps()
@ -98,23 +99,26 @@ namespace fps
const auto fps = fps::get_fps(); const auto fps = fps::get_fps();
const auto font = game::R_RegisterFont("fonts/fira_mono_regular.ttf", 25); const auto font = game::R_RegisterFont("fonts/fira_mono_regular.ttf", 25);
const auto fps_string = utils::string::va("%i", fps); if (font)
{
const auto fps_string = utils::string::va("%i", fps);
const auto x = (game::ScrPlace_GetViewPlacement()->realViewportSize[0] - 15.0f) - game::R_TextWidth( const auto x = (game::ScrPlace_GetViewPlacement()->realViewportSize[0] - 15.0f) -
fps_string, 0x7FFFFFFF, font); game::R_TextWidth(fps_string, 0x7FFFFFFF, font);
const auto y = font->pixelHeight + 10.f; const auto y = font->pixelHeight + 10.f;
const auto fps_color = fps >= 60 ? fps_color_good : (fps >= 30 ? fps_color_ok : fps_color_bad); const auto fps_color = fps >= 60 ? fps_color_good : (fps >= 30 ? fps_color_ok : fps_color_bad);
game::R_AddCmdDrawText(fps_string, 0x7FFFFFFF, font, x, y, 1.f, 1.f, 0.0f, fps_color, 6); game::R_AddCmdDrawText(fps_string, 0x7FFFFFFF, font, x, y, 1.f, 1.f, 0.0f, fps_color, 6);
}
} }
} }
void cg_draw_ping() void cg_draw_ping()
{ {
if (cg_drawping->current.integer > 0 && game::CL_IsCgameInitialized() && !game::VirtualLobby_Loaded()) if (cg_drawping->current.integer > 0 && game::CL_IsCgameInitialized() && !game::VirtualLobby_Loaded() && *game::mp::client_state)
{ {
const auto font = game::R_RegisterFont("fonts/consolefont", 20); const auto font = game::R_RegisterFont("fonts/consolefont", 20);
const auto ping_string = utils::string::va("Ping: %i", *game::mp::ping); const auto ping_string = utils::string::va("Ping: %i", (*game::mp::client_state)->ping);
const auto x = (game::ScrPlace_GetViewPlacement()->realViewportSize[0] - 375.0f) - game::R_TextWidth( const auto x = (game::ScrPlace_GetViewPlacement()->realViewportSize[0] - 375.0f) - game::R_TextWidth(
ping_string, 0x7FFFFFFF, font); ping_string, 0x7FFFFFFF, font);
@ -124,12 +128,17 @@ namespace fps
} }
} }
game::dvar_t* cg_draw_fps_register_stub(const char* name, const char** _enum, const int value, unsigned int /*flags*/, game::dvar_t* cg_draw_fps_register_stub()
const char* desc)
{ {
cg_drawfps = dvars::register_int("cg_drawFps", 0, 0, 2, game::DVAR_FLAG_SAVED, "Draw frames per second"); cg_drawfps = dvars::register_int("cg_drawFps", 0, 0, 2, game::DVAR_FLAG_SAVED, "Draw frames per second");
return cg_drawfps; return cg_drawfps;
} }
void sub_5D6810_stub()
{
perf_update();
sub_5D6810_hook.invoke<void>();
}
} }
int get_fps() int get_fps()
@ -151,22 +160,42 @@ namespace fps
// fps setup // fps setup
cg_perf.perf_start = std::chrono::high_resolution_clock::now(); cg_perf.perf_start = std::chrono::high_resolution_clock::now();
utils::hook::call(SELECT_VALUE(0x14018D261, 0x14025B747), &perf_update);
// change cg_drawfps flags to saved if (game::environment::is_mp())
utils::hook::call(SELECT_VALUE(0x140139F48, 0x140222A46), &cg_draw_fps_register_stub); {
utils::hook::jump(SELECT_VALUE(0, 0x343847_b), utils::hook::assemble([](utils::hook::assembler& a)
{
a.pushad64();
a.call_aligned(perf_update);
a.popad64();
a.call(0x702250_b);
a.mov(edx, 3);
a.xor_(ecx, ecx);
a.jmp(0x343853_b);
}), true);
// Don't register cg_drawfps
utils::hook::nop(0x31D74F_b, 0x1C);
utils::hook::nop(0x31D76F_b, 0x7);
}
else
{
sub_5D6810_hook.create(0x5D6810_b, sub_5D6810_stub);
// Don't register cg_drawfps
utils::hook::nop(0x15C97D_b, 0x20);
utils::hook::nop(0x15C9A1_b, 0x7);
}
scheduler::loop(cg_draw_fps, scheduler::pipeline::renderer); scheduler::loop(cg_draw_fps, scheduler::pipeline::renderer);
if (game::environment::is_sp()) cg_drawfps = dvars::register_int("cg_drawFps", 0, 0, 2, game::DVAR_FLAG_SAVED, "Draw frames per second");
{
cg_drawfps = dvars::register_int("cg_drawFps", 0, 0, 2, game::DVAR_FLAG_SAVED, "Draw frames per second");
}
if (game::environment::is_mp()) if (game::environment::is_mp())
{ {
// fix ping value // fix ping value
utils::hook::nop(0x14025AC41, 2); utils::hook::nop(0x342C6C_b, 2);
cg_drawping = dvars::register_int("cg_drawPing", 0, 0, 1, game::DVAR_FLAG_SAVED, "Choose to draw ping"); cg_drawping = dvars::register_int("cg_drawPing", 0, 0, 1, game::DVAR_FLAG_SAVED, "Choose to draw ping");

View File

@ -286,10 +286,10 @@ namespace game_console
{ {
const auto offset = (con.screen_max[0] - con.globals.x) / 4.f; const auto offset = (con.screen_max[0] - con.globals.x) / 4.f;
draw_hint_text(0, game::Dvar_ValueToString(dvar, dvar->current), draw_hint_text(0, game::Dvar_ValueToString(dvar, true, dvar->current),
dvars::con_inputDvarValueColor->current.vector, offset); dvars::con_inputDvarValueColor->current.vector, offset);
draw_hint_text(1, " default", dvars::con_inputDvarInactiveValueColor->current.vector); draw_hint_text(1, " default", dvars::con_inputDvarInactiveValueColor->current.vector);
draw_hint_text(1, game::Dvar_ValueToString(dvar, dvar->reset), draw_hint_text(1, game::Dvar_ValueToString(dvar, true, dvar->reset),
dvars::con_inputDvarInactiveValueColor->current.vector, offset); dvars::con_inputDvarInactiveValueColor->current.vector, offset);
draw_hint_text(2, matches[0].description.data(), draw_hint_text(2, matches[0].description.data(),
color_white, 0); color_white, 0);
@ -324,7 +324,7 @@ namespace game_console
if (dvar) if (dvar)
{ {
draw_hint_text(static_cast<int>(i), game::Dvar_ValueToString(dvar, dvar->current), draw_hint_text(static_cast<int>(i), game::Dvar_ValueToString(dvar, true, dvar->current),
dvars::con_inputDvarValueColor->current.vector, offset); dvars::con_inputDvarValueColor->current.vector, offset);
draw_hint_text(static_cast<int>(i), matches[i].description.data(), draw_hint_text(static_cast<int>(i), matches[i].description.data(),
@ -391,7 +391,7 @@ namespace game_console
const auto width = (con.screen_max[0] - con.screen_min[0]) - 12.0f; const auto width = (con.screen_max[0] - con.screen_min[0]) - 12.0f;
const auto height = ((con.screen_max[1] - con.screen_min[1]) - 32.0f) - 12.0f; const auto height = ((con.screen_max[1] - con.screen_min[1]) - 32.0f) - 12.0f;
game::R_AddCmdDrawText("H1-Mod 1.4", 0x7FFFFFFF, console_font, x, game::R_AddCmdDrawText("H1-Mod 1.15", 0x7FFFFFFF, console_font, x,
((height - 16.0f) + y) + console_font->pixelHeight, 1.0f, 1.0f, 0.0f, color_title, 0); ((height - 16.0f) + y) + console_font->pixelHeight, 1.0f, 1.0f, 0.0f, color_title, 0);
draw_output_scrollbar(x, y, width, height, output); draw_output_scrollbar(x, y, width, height, output);
@ -586,10 +586,13 @@ namespace game_console
return false; return false;
} }
if (game::playerKeys[local_client_num].keys[game::keyNum_t::K_SHIFT].down) const auto shift_down = game::playerKeys[local_client_num].keys[game::keyNum_t::K_SHIFT].down;
if (shift_down)
{ {
if (!(*game::keyCatchers & 1)) if (!(*game::keyCatchers & 1))
{
toggle_console(); toggle_console();
}
toggle_console_output(); toggle_console_output();
return false; return false;
@ -680,7 +683,7 @@ namespace game_console
if (key == game::keyNum_t::K_ENTER) if (key == game::keyNum_t::K_ENTER)
{ {
game::Cbuf_AddText(0, utils::string::va("%s \n", fixed_input.data())); game::Cbuf_AddText(0, 0, utils::string::va("%s \n", fixed_input.data()));
if (history_index != -1) if (history_index != -1)
{ {
@ -720,20 +723,17 @@ namespace game_console
{ {
return; return;
} }
//scheduler::loop(draw_console, scheduler::pipeline::renderer);
} }
void post_unpack() override void post_unpack() override
{ {
scheduler::loop(draw_console, scheduler::pipeline::renderer);
if (game::environment::is_dedi()) if (game::environment::is_dedi())
{ {
return; return;
} }
scheduler::loop(draw_console, scheduler::pipeline::renderer);
// initialize our structs // initialize our structs
con.cursor = 0; con.cursor = 0;
con.visible_line_count = 0; con.visible_line_count = 0;

View File

@ -3,6 +3,7 @@
#include "game_module.hpp" #include "game_module.hpp"
#include <utils/hook.hpp> #include <utils/hook.hpp>
#include <game/game.hpp>
namespace game_module namespace game_module
{ {
@ -59,9 +60,9 @@ namespace game_module
DWORD __stdcall get_module_file_name_a(HMODULE hmodule, const LPSTR filename, const DWORD size) DWORD __stdcall get_module_file_name_a(HMODULE hmodule, const LPSTR filename, const DWORD size)
{ {
if (!hmodule) if (!hmodule || utils::nt::library(hmodule) == get_game_module())
{ {
hmodule = get_game_module(); hmodule = get_host_module();
} }
return file_name_a_hook.invoke<DWORD>(hmodule, filename, size); return file_name_a_hook.invoke<DWORD>(hmodule, filename, size);
@ -69,9 +70,9 @@ namespace game_module
DWORD __stdcall get_module_file_name_w(HMODULE hmodule, const LPWSTR filename, const DWORD size) DWORD __stdcall get_module_file_name_w(HMODULE hmodule, const LPWSTR filename, const DWORD size)
{ {
if (!hmodule) if (!hmodule || utils::nt::library(hmodule) == get_game_module())
{ {
hmodule = get_game_module(); hmodule = get_host_module();
} }
return file_name_w_hook.invoke<DWORD>(hmodule, filename, size); return file_name_w_hook.invoke<DWORD>(hmodule, filename, size);
@ -90,7 +91,7 @@ namespace game_module
utils::nt::library get_game_module() utils::nt::library get_game_module()
{ {
static utils::nt::library game{HMODULE(0x140000000)}; static utils::nt::library game{HMODULE(game::base_address)};
return game; return game;
} }
@ -110,7 +111,11 @@ namespace game_module
void post_load() override void post_load() override
{ {
#ifdef INJECT_HOST_AS_LIB
hook_module_resolving(); hook_module_resolving();
#else
assert(get_host_module() == get_game_module());
#endif
} }
}; };
} }

View File

@ -16,6 +16,14 @@ namespace gameplay
{ {
utils::hook::detour pm_weapon_use_ammo_hook; utils::hook::detour pm_weapon_use_ammo_hook;
utils::hook::detour pm_player_trace_hook; utils::hook::detour pm_player_trace_hook;
utils::hook::detour pm_crashland_hook;
utils::hook::detour jump_apply_slowdown_hook;
utils::hook::detour stuck_in_client_hook;
utils::hook::detour cm_transformed_capsule_trace_hook;
utils::hook::detour client_end_frame_hook;
utils::hook::detour g_damage_client_hook;
utils::hook::detour g_damage_hook;
game::dvar_t* jump_slowDownEnable; game::dvar_t* jump_slowDownEnable;
game::dvar_t* jump_enableFallDamage; game::dvar_t* jump_enableFallDamage;
@ -24,18 +32,16 @@ namespace gameplay
{ {
if (jump_slowDownEnable->current.enabled) if (jump_slowDownEnable->current.enabled)
{ {
utils::hook::invoke<void>(0x1401D5360, ps); jump_apply_slowdown_hook.invoke<void>(ps);
} }
} }
int stuck_in_client_stub(void* entity) void stuck_in_client_stub(void* entity)
{ {
if (dvars::g_playerEjection->current.enabled) if (dvars::g_playerEjection->current.enabled)
{ {
return utils::hook::invoke<int>(0x140326CE0, entity); // StuckInClient stuck_in_client_hook.invoke<void>(entity);
} }
return 0;
} }
void cm_transformed_capsule_trace_stub(game::trace_t* results, const float* start, const float* end, void cm_transformed_capsule_trace_stub(game::trace_t* results, const float* start, const float* end,
@ -43,8 +49,8 @@ namespace gameplay
{ {
if (dvars::g_playerCollision->current.enabled) if (dvars::g_playerCollision->current.enabled)
{ {
utils::hook::invoke<void>(0x1403FF860, cm_transformed_capsule_trace_hook.invoke<void>(results, start, end,
results, start, end, bounds, capsule, contents, origin, angles); // CM_TransformedCapsuleTrace bounds, capsule, contents, origin, angles);
} }
} }
@ -52,7 +58,7 @@ namespace gameplay
{ {
if (jump_enableFallDamage->current.enabled) if (jump_enableFallDamage->current.enabled)
{ {
utils::hook::invoke<void>(0x1401E2D00, ps, pml); pm_crashland_hook.invoke<void>(ps, pml);
} }
} }
@ -65,58 +71,67 @@ namespace gameplay
} }
} }
const auto pm_bouncing_stub_mp = utils::hook::assemble([](utils::hook::assembler& a) void* pm_bouncing_stub_mp()
{ {
const auto no_bounce = a.newLabel(); return utils::hook::assemble([](utils::hook::assembler& a)
const auto loc_1401EAF9D = a.newLabel(); {
const auto no_bounce = a.newLabel();
const auto loc_2D395D = a.newLabel();
a.push(rax); a.push(rax);
a.mov(rax, qword_ptr(reinterpret_cast<int64_t>(&dvars::pm_bouncing))); a.mov(rax, qword_ptr(reinterpret_cast<int64_t>(&dvars::pm_bouncing)));
a.mov(al, byte_ptr(rax, 0x10)); a.mov(al, byte_ptr(rax, 0x10));
a.cmp(byte_ptr(rbp, -0x2D), al); a.cmp(byte_ptr(rbp, -0x7D), al);
a.pop(rax); a.pop(rax);
a.jz(no_bounce); a.jz(no_bounce);
a.jmp(0x1401EB000); a.jmp(0x2D39C0_b);
a.bind(no_bounce); a.bind(no_bounce);
a.cmp(dword_ptr(rsp, 0x70), 0); a.cmp(dword_ptr(rsp, 0x44), 0);
a.jnz(loc_1401EAF9D); a.jnz(loc_2D395D);
a.jmp(0x1401EAFF1); a.jmp(0x2D39B1_b);
a.bind(loc_1401EAF9D); a.bind(loc_2D395D);
a.jmp(0x1401EAF9D); a.jmp(0x2D395D_b);
}); });
}
const auto g_speed_stub = utils::hook::assemble([](utils::hook::assembler& a) void* g_speed_stub()
{ {
a.mov(rax, qword_ptr(reinterpret_cast<int64_t>(&dvars::g_speed))); return utils::hook::assemble([](utils::hook::assembler& a)
a.mov(eax, dword_ptr(rax, 0x10)); {
a.mov(rax, qword_ptr(reinterpret_cast<int64_t>(&dvars::g_speed)));
a.mov(eax, dword_ptr(rax, 0x10));
// original code // original code
a.mov(dword_ptr(r14, 0x36), ax); a.mov(dword_ptr(r14, 0x36), ax);
a.movzx(eax, word_ptr(r14, 0x3A)); a.movzx(eax, word_ptr(r14, 0x3A));
a.jmp(0x140323DBC); a.jmp(0x4006BC_b);
}); });
}
const auto client_end_frame_stub = utils::hook::assemble([](utils::hook::assembler& a) void* client_end_frame_stub()
{ {
a.push(rax); return utils::hook::assemble([](utils::hook::assembler& a)
{
a.push(rax);
a.mov(rax, qword_ptr(reinterpret_cast<int64_t>(&dvars::g_gravity))); a.mov(rax, qword_ptr(reinterpret_cast<int64_t>(&dvars::g_gravity)));
a.mov(eax, dword_ptr(rax, 0x10)); a.mov(eax, dword_ptr(rax, 0x10));
a.mov(word_ptr(rbx, 0x34), ax); a.mov(word_ptr(rbx, 0x34), ax);
a.pop(rax); a.pop(rax);
// Game code hook skipped // Game code hook skipped
a.mov(eax, dword_ptr(rbx, 0x494C)); a.mov(eax, dword_ptr(rbx, 0x495C));
a.mov(rdi, rcx); a.mov(rdi, rcx);
a.jmp(0x140322F82); a.jmp(0x3FF822_b);
}); });
}
void pm_player_trace_stub(game::pmove_t* pm, game::trace_t* trace, const float* f3, void pm_player_trace_stub(game::pmove_t* pm, game::trace_t* trace, const float* f3,
const float* f4, const game::Bounds* bounds, int a6, int a7) const float* f4, const game::Bounds* bounds, int a6, int a7)
@ -135,7 +150,7 @@ namespace gameplay
const auto stand = a.newLabel(); const auto stand = a.newLabel();
const auto allsolid = a.newLabel(); const auto allsolid = a.newLabel();
a.call(qword_ptr(r10, r15)); // Game code a.call(rsi); // Game code
a.push(rax); a.push(rax);
@ -151,11 +166,57 @@ namespace gameplay
a.jnz(allsolid); a.jnz(allsolid);
a.bind(stand); a.bind(stand);
a.and_(dword_ptr(rbx, 0x54), 0xFFFFFFFD); a.and_(dword_ptr(r15, 0x54), 0xFFFFFFFD);
a.jmp(0x1401E1CDF); a.jmp(0x2C9F9D_b);
a.bind(allsolid); a.bind(allsolid);
a.jmp(0x1401E1CE1); a.jmp(0x2C9F9F_b);
};
void client_end_frame_stub2(game::mp::gentity_s* entity)
{
client_end_frame_hook.invoke<void>(entity);
if ((entity->client->flags & 1)) // noclip
{
entity->client->pm_type = 2;
}
else if ((entity->client->flags & 2)) // ufo
{
entity->client->pm_type = 3;
}
}
void g_damage_client_stub(game::mp::gentity_s* targ, const game::mp::gentity_s* inflictor, game::mp::gentity_s* attacker,
const float* dir, const float* point, int damage, int dflags, int mod,
const unsigned int weapon, bool is_alternate, unsigned int hit_loc, int time_offset)
{
if ((targ->client->flags & 1) || (targ->client->flags & 2)) // noclip, ufo
{
return;
}
g_damage_client_hook.invoke<void>(targ, inflictor, attacker, dir, point, damage, dflags, mod,
weapon, is_alternate, hit_loc, time_offset);
}
void g_damage_stub(game::mp::gentity_s* targ, const game::mp::gentity_s* inflictor, game::mp::gentity_s* attacker,
const float* dir, const float* point, int damage, int dflags, int mod,
const unsigned int weapon, bool is_alternate, unsigned int hit_loc,
unsigned int model_index, unsigned int part_name, int time_offset)
{
if (targ->flags & 1) // godmode
{
return;
}
if (targ->flags & 2) // demigod
{
damage = 1;
}
g_damage_hook.invoke<void>(targ, inflictor, attacker, dir, point, damage, dflags, mod, weapon,
is_alternate, hit_loc, model_index, part_name, time_offset);
} }
} }
@ -164,65 +225,66 @@ namespace gameplay
public: public:
void post_unpack() override void post_unpack() override
{ {
dvars::player_sustainAmmo = dvars::register_bool("player_sustainAmmo", false,
game::DVAR_FLAG_REPLICATED, "Firing weapon will not decrease clip ammo");
pm_weapon_use_ammo_hook.create(SELECT_VALUE(0x14042E380, 0x1401F6B90), &pm_weapon_use_ammo_stub);
if (game::environment::is_sp()) if (game::environment::is_sp())
{ {
return; return;
} }
#ifdef DEBUG dvars::player_sustainAmmo = dvars::register_bool("player_sustainAmmo", false,
// Influence PM_JitterPoint code flow so the trace->startsolid checks are 'ignored' game::DVAR_FLAG_REPLICATED, "Firing weapon will not decrease clip ammo");
pm_player_trace_hook.create(0x1401E8BE0, &pm_player_trace_stub); pm_weapon_use_ammo_hook.create(0x2DF830_b, &pm_weapon_use_ammo_stub);
utils::hook::nop(0x4006AD_b, 15);
utils::hook::jump(0x4006AD_b, g_speed_stub(), true);
dvars::g_speed = dvars::register_int("g_speed", 190, 0, 1000,
game::DVAR_FLAG_REPLICATED, "changes the speed of the player");
dvars::pm_bouncing = dvars::register_bool("pm_bouncing", false,
game::DVAR_FLAG_REPLICATED, "Enable bouncing");
utils::hook::jump(0x2D39A4_b, pm_bouncing_stub_mp(), true);
dvars::g_gravity = dvars::register_int("g_gravity", 800, 0, 1000, game::DVAR_FLAG_REPLICATED,
"Game gravity in inches per second squared");
utils::hook::jump(0x3FF812_b, client_end_frame_stub(), true);
utils::hook::nop(0x3FF808_b, 1);
/*
// Influence PM_JitterPoint code flow so the trace->startsolid checks are 'ignored'
pm_player_trace_hook.create(0x2D14C0_b, &pm_player_trace_stub);
// If g_enableElevators is 1 the 'ducked' flag will always be removed from the player state // If g_enableElevators is 1 the 'ducked' flag will always be removed from the player state
utils::hook::jump(0x1401E1CD1, utils::hook::assemble(pm_trace_stub), true); utils::hook::jump(0x2C9F90_b, utils::hook::assemble(pm_trace_stub), true);
dvars::g_enableElevators = dvars::register_bool("g_enableElevators", false, game::DvarFlags::DVAR_FLAG_NONE, "Enables Elevators"); dvars::g_enableElevators = dvars::register_bool("g_enableElevators", false, game::DvarFlags::DVAR_FLAG_NONE, "Enables Elevators");
#endif
auto* timescale = dvars::register_float("timescale", 1.0f, 0.1f, 50.0f, game::DVAR_FLAG_REPLICATED, "Changes Timescale of the game"); auto* timescale = dvars::register_float("timescale", 1.0f, 0.1f, 50.0f, game::DVAR_FLAG_REPLICATED, "Changes Timescale of the game");
utils::hook::inject(0x1400D89A4, &timescale->current.value); utils::hook::inject(0x15B204_b, &timescale->current.value); // Com_GetTimeScale
utils::hook::inject(0x1400DA9D1, &timescale->current.value); utils::hook::inject(0x17D241_b, &timescale->current.value); // Com_Restart
utils::hook::inject(0x1400DB7A9, &timescale->current.value); utils::hook::inject(0x17E609_b, &timescale->current.value); // Com_SetSlowMotion
utils::hook::inject(0x1400DB7C6, &timescale->current.value); utils::hook::inject(0x17E626_b, &timescale->current.value); // Com_SetSlowMotion
utils::hook::inject(0x1400DB83C, &timescale->current.value); utils::hook::inject(0x17E69C_b, &timescale->current.value); // Com_SetSlowMotion
utils::hook::inject(0x1400DB9CC, &timescale->current.value); // utils::hook::inject(0x1400DB9CC, &timescale->current.value); // Com_ErrorCleanup_Shutdown (Inlined)
utils::hook::inject(0x1400DBAF0, &timescale->current.value); utils::hook::inject(0x17EAD0_b, &timescale->current.value); // Com_TimeScaleMsec (Crash)
utils::hook::inject(0x1400DBE72, &timescale->current.value); utils::hook::inject(0x17EFE2_b, &timescale->current.value); // Com_UpdateSlowMotion
utils::hook::inject(0x1400DBE9C, &timescale->current.value); utils::hook::inject(0x17F00C_b, &timescale->current.value); // Com_UpdateSlowMotion
*/
utils::hook::call(0x1401E8830, jump_apply_slowdown_stub); jump_apply_slowdown_hook.create(0x2BD0B0_b, jump_apply_slowdown_stub);
jump_slowDownEnable = dvars::register_bool("jump_slowDownEnable", true, game::DVAR_FLAG_REPLICATED, "Slow player movement after jumping"); jump_slowDownEnable = dvars::register_bool("jump_slowDownEnable", true, game::DVAR_FLAG_REPLICATED, "Slow player movement after jumping");
utils::hook::call(0x1401E490F, pm_crashland_stub); pm_crashland_hook.create(0x2CB070_b, pm_crashland_stub);
jump_enableFallDamage = dvars::register_bool("jump_enableFallDamage", true, game::DVAR_FLAG_REPLICATED, "Enable fall damage"); jump_enableFallDamage = dvars::register_bool("jump_enableFallDamage", true, game::DVAR_FLAG_REPLICATED, "Enable fall damage");
dvars::g_playerEjection = dvars::register_bool("g_playerEjection", true, game::DVAR_FLAG_REPLICATED, dvars::g_playerEjection = dvars::register_bool("g_playerEjection", true, game::DVAR_FLAG_REPLICATED,
"Flag whether player ejection is on or off"); "Flag whether player ejection is on or off");
utils::hook::call(0x140323333, stuck_in_client_stub); stuck_in_client_hook.create(0x4035F0_b, stuck_in_client_stub);
utils::hook::nop(0x140323DAD, 15);
utils::hook::jump(0x140323DAD, g_speed_stub, true);
dvars::g_speed = dvars::register_int("g_speed", 190, std::numeric_limits<int>::min(), std::numeric_limits<int>::max(),
game::DVAR_FLAG_REPLICATED, "changes the speed of the player");
// Implement player collision dvar
dvars::g_playerCollision = dvars::register_bool("g_playerCollision", true, game::DVAR_FLAG_REPLICATED, dvars::g_playerCollision = dvars::register_bool("g_playerCollision", true, game::DVAR_FLAG_REPLICATED,
"Flag whether player collision is on or off"); "Flag whether player collision is on or off");
utils::hook::call(0x14049D7CF, cm_transformed_capsule_trace_stub); // SV_ClipMoveToEntity cm_transformed_capsule_trace_hook.create(0x4D63C0_b, cm_transformed_capsule_trace_stub);
utils::hook::call(0x140240BC3, cm_transformed_capsule_trace_stub); // CG_ClipMoveToEntity
// Implement bouncing dvar // Make noclip work
dvars::pm_bouncing = dvars::register_bool("pm_bouncing", false, client_end_frame_hook.create(0x3FF7D0_b, client_end_frame_stub2);
game::DVAR_FLAG_REPLICATED, "Enable bouncing"); g_damage_client_hook.create(0x414F10_b, g_damage_client_stub);
utils::hook::jump(0x1401EAFE4, pm_bouncing_stub_mp, true); g_damage_hook.create(0x414A10_b, g_damage_stub);
dvars::g_gravity = dvars::register_int("g_gravity", 800, std::numeric_limits<short>::min(),
std::numeric_limits<short>::max(), game::DVAR_FLAG_REPLICATED, "Game gravity in inches per second squared");
utils::hook::jump(0x140322F72, client_end_frame_stub, true);
utils::hook::nop(0x140322F68, 1); // Nop skipped opcode
} }
}; };
} }

View File

@ -4,7 +4,6 @@
#include "game/game.hpp" #include "game/game.hpp"
#include "game_console.hpp" #include "game_console.hpp"
#include "ui_scripting.hpp"
#include "game/ui_scripting/execution.hpp" #include "game/ui_scripting/execution.hpp"
#include <utils/hook.hpp> #include <utils/hook.hpp>
@ -16,7 +15,6 @@ namespace input
utils::hook::detour cl_char_event_hook; utils::hook::detour cl_char_event_hook;
utils::hook::detour cl_key_event_hook; utils::hook::detour cl_key_event_hook;
void cl_char_event_stub(const int local_client_num, const int key) void cl_char_event_stub(const int local_client_num, const int key)
{ {
if (ui_scripting::lui_running()) if (ui_scripting::lui_running())
@ -66,8 +64,8 @@ namespace input
return; return;
} }
cl_char_event_hook.create(SELECT_VALUE(0x1401871A0, 0x14024E810), cl_char_event_stub); cl_char_event_hook.create(SELECT_VALUE(0x1AB8F0_b, 0x12C8F0_b), cl_char_event_stub);
cl_key_event_hook.create(SELECT_VALUE(0x1401874D0, 0x14024EA60), cl_key_event_stub); cl_key_event_hook.create(SELECT_VALUE(0x1ABC20_b, 0x135A70_b), cl_key_event_stub);
} }
}; };
} }

View File

@ -44,9 +44,9 @@ namespace localized_strings
void post_unpack() override void post_unpack() override
{ {
// Change some localized strings // Change some localized strings
seh_string_ed_get_string_hook.create(SELECT_VALUE(0x1403924A0, 0x1404BB2A0), &seh_string_ed_get_string); seh_string_ed_get_string_hook.create(SELECT_VALUE(0x3E6CE0_b, 0x585DA0_b), &seh_string_ed_get_string);
} }
}; };
} }
REGISTER_COMPONENT(localized_strings::component) REGISTER_COMPONENT(localized_strings::component)

View File

@ -2,15 +2,10 @@
#include "loader/component_loader.hpp" #include "loader/component_loader.hpp"
#include "scheduler.hpp" #include "scheduler.hpp"
#include "game/scripting/entity.hpp" #include "logfile.hpp"
#include "game/scripting/execution.hpp"
#include "game/scripting/lua/value_conversion.hpp"
#include "game/scripting/lua/error.hpp"
#include <utils/hook.hpp> #include <utils/hook.hpp>
#include "logfile.hpp"
namespace logfile namespace logfile
{ {
std::unordered_map<const char*, sol::protected_function> vm_execute_hooks; std::unordered_map<const char*, sol::protected_function> vm_execute_hooks;
@ -20,6 +15,9 @@ namespace logfile
utils::hook::detour scr_player_killed_hook; utils::hook::detour scr_player_killed_hook;
utils::hook::detour scr_player_damage_hook; utils::hook::detour scr_player_damage_hook;
utils::hook::detour client_command_hook;
utils::hook::detour g_shutdown_game_hook;
std::vector<sol::protected_function> player_killed_callbacks; std::vector<sol::protected_function> player_killed_callbacks;
std::vector<sol::protected_function> player_damage_callbacks; std::vector<sol::protected_function> player_damage_callbacks;
@ -58,7 +56,7 @@ namespace logfile
std::string convert_mod(const int meansOfDeath) std::string convert_mod(const int meansOfDeath)
{ {
const auto value = reinterpret_cast<game::scr_string_t**>(0x140FEC3F0)[meansOfDeath]; const auto value = reinterpret_cast<game::scr_string_t**>(0x10B5290_b)[meansOfDeath];
const auto string = game::SL_ConvertToString(*value); const auto string = game::SL_ConvertToString(*value);
return string; return string;
} }
@ -68,7 +66,7 @@ namespace logfile
const bool isAlternate, const float* vDir, const unsigned int hitLoc, int psTimeOffset, int deathAnimDuration) const bool isAlternate, const float* vDir, const unsigned int hitLoc, int psTimeOffset, int deathAnimDuration)
{ {
{ {
const std::string hitloc = reinterpret_cast<const char**>(0x140FEC4D0)[hitLoc]; const std::string hitloc = reinterpret_cast<const char**>(0x10B5370_b)[hitLoc];
const auto mod_ = convert_mod(meansOfDeath); const auto mod_ = convert_mod(meansOfDeath);
const auto weapon_ = get_weapon_name(weapon, isAlternate); const auto weapon_ = get_weapon_name(weapon, isAlternate);
@ -110,7 +108,7 @@ namespace logfile
const float* vDir, const unsigned int hitLoc, const int timeOffset) const float* vDir, const unsigned int hitLoc, const int timeOffset)
{ {
{ {
const std::string hitloc = reinterpret_cast<const char**>(0x140FEC4D0)[hitLoc]; const std::string hitloc = reinterpret_cast<const char**>(0x10B5370_b)[hitLoc];
const auto mod_ = convert_mod(meansOfDeath); const auto mod_ = convert_mod(meansOfDeath);
const auto weapon_ = get_weapon_name(weapon, isAlternate); const auto weapon_ = get_weapon_name(weapon, isAlternate);
@ -147,51 +145,6 @@ namespace logfile
meansOfDeath, weapon, isAlternate, vPoint, vDir, hitLoc, timeOffset); meansOfDeath, weapon, isAlternate, vPoint, vDir, hitLoc, timeOffset);
} }
void client_command_stub(const int clientNum)
{
auto self = &game::mp::g_entities[clientNum];
char cmd[1024] = {0};
game::SV_Cmd_ArgvBuffer(0, cmd, 1024);
if (cmd == "say"s || cmd == "say_team"s)
{
auto hidden = false;
std::string message(game::ConcatArgs(1));
hidden = message[1] == '/';
message.erase(0, hidden ? 2 : 1);
scheduler::once([cmd, message, self]()
{
const scripting::entity level{*game::levelEntityId};
const scripting::entity player{game::Scr_GetEntityId(self->s.entityNum, 0)};
scripting::notify(level, cmd, {player, message});
scripting::notify(player, cmd, {message});
}, scheduler::pipeline::server);
if (hidden)
{
return;
}
}
// ClientCommand
return utils::hook::invoke<void>(0x140336000, clientNum);
}
void g_shutdown_game_stub(const int freeScripts)
{
{
const scripting::entity level{*game::levelEntityId};
scripting::notify(level, "shutdownGame_called", {1});
}
// G_ShutdownGame
return utils::hook::invoke<void>(0x140345A60, freeScripts);
}
unsigned int local_id_to_entity(unsigned int local_id) unsigned int local_id_to_entity(unsigned int local_id)
{ {
const auto variable = game::scr_VarGlob->objectVariableValue[local_id]; const auto variable = game::scr_VarGlob->objectVariableValue[local_id];
@ -254,7 +207,7 @@ namespace logfile
a.inc(r14); a.inc(r14);
a.mov(dword_ptr(rbp, 0xA4), r15d); a.mov(dword_ptr(rbp, 0xA4), r15d);
a.jmp(SELECT_VALUE(0x140376663, 0x140444653)); a.jmp(SELECT_VALUE(0x3CA153_b, 0x5111B3_b));
a.bind(replace); a.bind(replace);
@ -291,25 +244,53 @@ namespace logfile
hook_enabled = false; hook_enabled = false;
} }
bool client_command_stub(const int client_num)
{
auto self = &game::mp::g_entities[client_num];
char cmd[1024] = {0};
game::SV_Cmd_ArgvBuffer(0, cmd, 1024);
if (cmd == "say"s || cmd == "say_team"s)
{
auto hidden = false;
std::string message(game::ConcatArgs(1));
hidden = message[1] == '/';
message.erase(0, hidden ? 2 : 1);
scheduler::once([cmd, message, self, hidden]()
{
const scripting::entity level{*game::levelEntityId};
const scripting::entity player{game::Scr_GetEntityId(self->s.entityNum, 0)};
scripting::notify(level, cmd, {player, message, hidden});
scripting::notify(player, cmd, {message, hidden});
}, scheduler::pipeline::server);
if (hidden)
{
return false;
}
}
return true;
}
class component final : public component_interface class component final : public component_interface
{ {
public: public:
void post_unpack() override void post_unpack() override
{ {
utils::hook::jump(SELECT_VALUE(0x140376655, 0x140444645), utils::hook::assemble(vm_execute_stub), true); utils::hook::jump(SELECT_VALUE(0x3CA145_b, 0x5111A5_b), utils::hook::assemble(vm_execute_stub), true);
if (game::environment::is_sp()) if (game::environment::is_sp())
{ {
return; return;
} }
utils::hook::call(0x14048191D, client_command_stub); scr_player_damage_hook.create(0x1CE780_b, scr_player_damage_stub);
scr_player_killed_hook.create(0x1CEA60_b, scr_player_killed_stub);
scr_player_damage_hook.create(0x14037DC50, scr_player_damage_stub);
scr_player_killed_hook.create(0x14037DF30, scr_player_killed_stub);
utils::hook::call(0x140484EC0, g_shutdown_game_stub);
utils::hook::call(0x1404853C1, g_shutdown_game_stub);
} }
}; };
} }

View File

@ -1,5 +1,10 @@
#pragma once #pragma once
#include "game/scripting/entity.hpp"
#include "game/scripting/execution.hpp"
#include "game/scripting/lua/value_conversion.hpp"
#include "game/scripting/lua/error.hpp"
namespace logfile namespace logfile
{ {
extern std::unordered_map<const char*, sol::protected_function> vm_execute_hooks; extern std::unordered_map<const char*, sol::protected_function> vm_execute_hooks;
@ -10,4 +15,6 @@ namespace logfile
void enable_vm_execute_hook(); void enable_vm_execute_hook();
void disable_vm_execute_hook(); void disable_vm_execute_hook();
bool client_command_stub(const int client_num);
} }

View File

@ -110,50 +110,6 @@ namespace logger
console::info(buffer); console::info(buffer);
} }
void lui_error()
{
utils::hook::call(0x140162809, print_warning);
utils::hook::call(0x140162815, print_warning);
utils::hook::call(0x14016281D, print_warning);
utils::hook::call(0x140162829, print_warning);
utils::hook::call(0x140162E32, print_warning);
utils::hook::call(0x140162E3E, print_warning);
utils::hook::call(0x140162E46, print_warning);
utils::hook::call(0x140162E52, print_warning);
utils::hook::call(0x140168435, print_warning);
utils::hook::call(0x140168441, print_warning);
utils::hook::call(0x140168449, print_warning);
utils::hook::call(0x140168455, print_warning);
utils::hook::call(0x14016914D, print_warning);
utils::hook::call(0x140169161, print_warning);
utils::hook::call(0x140169C04, print_warning);
utils::hook::call(0x140169C0C, print_warning);
utils::hook::call(0x140169C18, print_warning);
utils::hook::call(0x140169CB7, print_warning);
utils::hook::call(0x140169CDE, print_warning);
utils::hook::call(0x140169CEA, print_warning);
utils::hook::call(0x140169D03, print_warning);
utils::hook::call(0x14016BE72, print_warning);
utils::hook::call(0x14016C020, print_warning);
}
void lui_interface_debug_print()
{
utils::hook::call(0x14015C0B2, print_warning);
utils::hook::call(0x140162453, print_warning);
utils::hook::call(0x1401625DF, print_warning);
utils::hook::call(0x14016713C, print_dev);
utils::hook::call(0x1401687CD, print_dev);
utils::hook::call(0x14016BB8A, print_dev);
utils::hook::call(0x14016CA9C, print_dev);
}
} }
class component final : public component_interface class component final : public component_interface
@ -161,15 +117,12 @@ namespace logger
public: public:
void post_unpack() override void post_unpack() override
{ {
if (game::environment::is_mp()) if (!game::environment::is_dedi())
{ {
lui_error(); // lua stuff
lui_interface_debug_print(); utils::hook::jump(SELECT_VALUE(0x106010_b, 0x27CBB0_b), print_dev); // debug
} utils::hook::jump(SELECT_VALUE(0x107680_b, 0x27E210_b), print_error); // error
utils::hook::jump(SELECT_VALUE(0x0E6E30_b, 0x1F6140_b), printf); // print
if (!game::environment::is_sp())
{
utils::hook::call(0x14051347F, print_com_error);
} }
com_error_hook.create(game::Com_Error, com_error_stub); com_error_hook.create(game::Com_Error, com_error_stub);

View File

@ -15,14 +15,6 @@ namespace lui
public: public:
void post_unpack() override void post_unpack() override
{ {
// Don't show create cod account popup
//utils::hook::set<uint32_t>(0x14017C957, 0);
//#ifdef _DEBUG
// Enable development menus (causes issues in sp)
//utils::hook::set<uint32_t>(SELECT_VALUE(0x1400B4ABC, 0x1401AB779), 1);
//#endif
command::add("lui_open", [](const command::params& params) command::add("lui_open", [](const command::params& params)
{ {
if (params.size() <= 1) if (params.size() <= 1)

View File

@ -9,9 +9,10 @@
namespace map_rotation namespace map_rotation
{ {
DWORD previousPriority;
namespace namespace
{ {
DWORD previous_priority{};
void set_dvar(const std::string& dvar, const std::string& value) void set_dvar(const std::string& dvar, const std::string& value)
{ {
command::execute(utils::string::va("%s \"%s\"", dvar.data(), value.data()), true); command::execute(utils::string::va("%s \"%s\"", dvar.data(), value.data()), true);
@ -84,10 +85,10 @@ namespace map_rotation
scheduler::on_game_initialized([]() scheduler::on_game_initialized([]()
{ {
//printf("=======================setting OLD priority=======================\n"); //printf("=======================setting OLD priority=======================\n");
SetPriorityClass(GetCurrentProcess(), previousPriority); SetPriorityClass(GetCurrentProcess(), previous_priority);
}, scheduler::pipeline::main, 1s); }, scheduler::pipeline::main, 1s);
previousPriority = GetPriorityClass(GetCurrentProcess()); previous_priority = GetPriorityClass(GetCurrentProcess());
//printf("=======================setting NEW priority=======================\n"); //printf("=======================setting NEW priority=======================\n");
SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS); SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS);
} }
@ -147,7 +148,6 @@ namespace map_rotation
return scheduler::cond_end; return scheduler::cond_end;
}, scheduler::pipeline::main, 1s); }, scheduler::pipeline::main, 1s);
} }
} }
class component final : public component_interface class component final : public component_interface
@ -170,9 +170,9 @@ namespace map_rotation
command::add("map_rotate", &perform_map_rotation); command::add("map_rotate", &perform_map_rotation);
// Hook GScr_ExitLevel // Hook GScr_ExitLevel
utils::hook::jump(0x140376630, &trigger_map_rotation); // not sure if working utils::hook::jump(0xE2670_b, &trigger_map_rotation, true); // not sure if working
previousPriority = GetPriorityClass(GetCurrentProcess()); previous_priority = GetPriorityClass(GetCurrentProcess());
} }
}; };
} }

View File

@ -203,8 +203,8 @@ namespace materials
} }
material_register_handle_hook.create(game::Material_RegisterHandle, material_register_handle_stub); material_register_handle_hook.create(game::Material_RegisterHandle, material_register_handle_stub);
db_material_streaming_fail_hook.create(SELECT_VALUE(0x1401D3180, 0x1402C6260), db_material_streaming_fail_stub); db_material_streaming_fail_hook.create(SELECT_VALUE(0x1FB400_b, 0x3A1600_b), db_material_streaming_fail_stub);
db_get_material_index_hook.create(SELECT_VALUE(0x1401CAD00, 0x1402BBB20), db_get_material_index_stub); db_get_material_index_hook.create(SELECT_VALUE(0x1F1D80_b, 0x396000_b), db_get_material_index_stub);
} }
}; };
} }

View File

@ -61,7 +61,7 @@ namespace mods
utils::io::create_directory("mods"); utils::io::create_directory("mods");
} }
db_release_xassets_hook.create(SELECT_VALUE(0x1401CD560, 0x1402BF160), db_release_xassets_stub); db_release_xassets_hook.create(SELECT_VALUE(0x1F4DB0_b, 0x399740_b), db_release_xassets_stub);
command::add("loadmod", [](const command::params& params) command::add("loadmod", [](const command::params& params)
{ {

View File

@ -60,25 +60,25 @@ namespace network
// Command handled // Command handled
a.popad64(); a.popad64();
a.mov(al, 1); a.mov(al, 1);
a.jmp(0x140252AF8); a.jmp(0x12FCAA_b);
a.bind(return_unhandled); a.bind(return_unhandled);
a.popad64(); a.popad64();
a.jmp(0x14025234C); a.jmp(0x12F3AC_b);
} }
int net_compare_base_address(const game::netadr_s* a1, const game::netadr_s* a2) int net_compare_base_address(const game::netadr_s* a, const game::netadr_s* b)
{ {
if (a1->type == a2->type) if (a->type == b->type)
{ {
switch (a1->type) switch (a->type)
{ {
case game::netadrtype_t::NA_BOT: case game::netadrtype_t::NA_BOT:
case game::netadrtype_t::NA_LOOPBACK: case game::netadrtype_t::NA_LOOPBACK:
return a1->port == a2->port; return a->port == b->port;
case game::netadrtype_t::NA_IP: case game::netadrtype_t::NA_IP:
return !memcmp(a1->ip, a2->ip, 4); return !memcmp(a->ip, b->ip, 4);
case game::netadrtype_t::NA_BROADCAST: case game::netadrtype_t::NA_BROADCAST:
return true; return true;
default: default:
@ -89,9 +89,9 @@ namespace network
return false; return false;
} }
int net_compare_address(const game::netadr_s* a1, const game::netadr_s* a2) int net_compare_address(const game::netadr_s* a, const game::netadr_s* b)
{ {
return net_compare_base_address(a1, a2) && a1->port == a2->port; return net_compare_base_address(a, b) && a->port == b->port;
} }
void reconnect_migratated_client(void*, game::netadr_s* from, const int, const int, const char*, void reconnect_migratated_client(void*, game::netadr_s* from, const int, const int, const char*,
@ -110,17 +110,17 @@ namespace network
if (net_interface && net_interface != "localhost"s) if (net_interface && net_interface != "localhost"s)
{ {
// Sys_StringToSockaddr // Sys_StringToSockaddr
utils::hook::invoke<void>(0x1404F6580, net_interface, &address); utils::hook::invoke<void>(0x59E810_b, net_interface, &address);
} }
address.sin_family = AF_INET; address.sin_family = AF_INET;
address.sin_port = ntohs(static_cast<short>(port)); address.sin_port = ntohs(static_cast<short>(port));
const auto sock = ::socket(AF_INET, SOCK_DGRAM, protocol); const auto sock = socket(AF_INET, SOCK_DGRAM, protocol);
u_long arg = 1; u_long arg = 1;
ioctlsocket(sock, FIONBIO, &arg); ioctlsocket(sock, FIONBIO, &arg);
char optval[4] = { 1 }; char optval[4] = {1};
setsockopt(sock, 0xFFFF, 32, optval, 4); setsockopt(sock, 0xFFFF, 32, optval, 4);
if (bind(sock, reinterpret_cast<sockaddr*>(&address), sizeof(address)) != -1) if (bind(sock, reinterpret_cast<sockaddr*>(&address), sizeof(address)) != -1)
@ -138,10 +138,10 @@ namespace network
get_callbacks()[utils::string::to_lower(command)] = callback; get_callbacks()[utils::string::to_lower(command)] = callback;
} }
int dw_send_to_stub(const int size, const char* src, game::netadr_s* a3) int dw_send_to_stub(const int size, const char* src, game::netadr_s* to)
{ {
sockaddr s = {}; sockaddr s = {};
game::NetadrToSockadr(a3, &s); game::NetadrToSockadr(to, &s);
return sendto(*game::query_socket, src, size, 0, &s, 16) >= 0; return sendto(*game::query_socket, src, size, 0, &s, 16) >= 0;
} }
@ -229,53 +229,47 @@ namespace network
} }
// redirect dw_sendto to raw socket // redirect dw_sendto to raw socket
//utils::hook::jump(0x1404D850A, reinterpret_cast<void*>(0x1404D849A)); utils::hook::jump(0x5EEC90_b, dw_send_to_stub);
utils::hook::call(0x140513467, dw_send_to_stub);
utils::hook::jump(game::Sys_SendPacket, dw_send_to_stub); utils::hook::jump(game::Sys_SendPacket, dw_send_to_stub);
// intercept command handling // intercept command handling
utils::hook::jump(0x140252327, utils::hook::assemble(handle_command_stub), true); utils::hook::jump(0x12F387_b, utils::hook::assemble(handle_command_stub), true);
// handle xuid without secure connection // handle xuid without secure connection
utils::hook::nop(0x140486AAF, 6); utils::hook::nop(0x554222_b, 6);
utils::hook::jump(0x140424F20, net_compare_address); utils::hook::jump(0x4F1800_b, net_compare_address);
utils::hook::jump(0x140424F70, net_compare_base_address); utils::hook::jump(0x4F1850_b, net_compare_base_address);
// don't establish secure conenction // don't establish secure conenction
utils::hook::set<uint8_t>(0x14027EA4D, 0xEB); utils::hook::set<uint8_t>(0x358C8D_b, 0xEB);
utils::hook::set<uint8_t>(0x14027EB1E, 0xEB); utils::hook::set<uint8_t>(0x358D5E_b, 0xEB);
utils::hook::set<uint8_t>(0x14027EF8D, 0xEB); utils::hook::set<uint8_t>(0x3591CD_b, 0xEB);
utils::hook::set<uint8_t>(0x14025081F, 0xEB); utils::hook::set<uint8_t>(0x12CD0F_b, 0xEB);
// ignore unregistered connection // ignore unregistered connection
utils::hook::jump(0x140480F46, 0x140480EE5); utils::hook::jump(0x54E2D1_b, 0x54E270_b, true);
utils::hook::set<uint8_t>(0x140480F3B, 0xEB); utils::hook::set<uint8_t>(0x54E2C6_b, 0xEB);
// disable xuid verification // disable xuid verification
utils::hook::set<uint8_t>(0x14005B62D, 0xEB); utils::hook::set<uint8_t>(0x728BF_b, 0xEB);
utils::hook::set<uint8_t>(0x14005B649, 0xEB);
// disable xuid verification // disable xuid verification
utils::hook::nop(0x14048382C, 2); utils::hook::nop(0x5509D9_b, 2);
utils::hook::set<uint8_t>(0x140483889, 0xEB); utils::hook::set<uint8_t>(0x550A36_b, 0xEB);
// ignore configstring mismatch // ignore configstring mismatch
utils::hook::set<uint8_t>(0x1402591C9, 0xEB); utils::hook::set<uint8_t>(0x341261_b, 0xEB);
// ignore dw handle in SV_PacketEvent // ignore dw handle in SV_PacketEvent
utils::hook::set<uint8_t>(0x1404898E2, 0xEB); utils::hook::set<uint8_t>(0x1CBC22_b, 0xEB);
utils::hook::call(0x1404898D6, &net_compare_address);
// ignore dw handle in SV_FindClientByAddress // ignore dw handle in SV_FindClientByAddress
utils::hook::set<uint8_t>(0x140488EFD, 0xEB); utils::hook::set<uint8_t>(0x1CB24D_b, 0xEB);
utils::hook::call(0x140488EF1, &net_compare_address);
// ignore dw handle in SV_DirectConnect // ignore dw handle in SV_DirectConnect
utils::hook::set<uint8_t>(0x140480C58, 0xEB); utils::hook::set<uint8_t>(0x54DFE8_b, 0xEB);
utils::hook::set<uint8_t>(0x140480E6F, 0xEB); utils::hook::set<uint8_t>(0x54E1FD_b, 0xEB);
utils::hook::call(0x140480C4B, &net_compare_address);
utils::hook::call(0x140480E62, &net_compare_address);
// increase cl_maxpackets // increase cl_maxpackets
dvars::override::register_int("cl_maxpackets", 1000, 1, 1000, game::DVAR_FLAG_SAVED); dvars::override::register_int("cl_maxpackets", 1000, 1, 1000, game::DVAR_FLAG_SAVED);
@ -284,31 +278,31 @@ namespace network
dvars::override::register_int("sv_remote_client_snapshot_msec", 33, 33, 100, game::DVAR_FLAG_NONE); dvars::override::register_int("sv_remote_client_snapshot_msec", 33, 33, 100, game::DVAR_FLAG_NONE);
// ignore impure client // ignore impure client
utils::hook::jump(0x140481B58, 0x140481BEE); utils::hook::jump(0x54EDD3_b, 0x54EE69_b);
// don't send checksum // don't send checksum
utils::hook::set<uint8_t>(0x1404F6398, 0); utils::hook::set<uint8_t>(0x59E628_b, 0);
// don't read checksum // don't read checksum
utils::hook::set(0x1404F6620, 0xC301B0); utils::hook::set(0x59E8B0_b, 0xC301B0);
// don't try to reconnect client // don't try to reconnect client
utils::hook::call(0x140480DFF, reconnect_migratated_client); utils::hook::jump(0x54D220_b, reconnect_migratated_client);
utils::hook::nop(0x140480DDB, 4); // this crashes when reconnecting for some reason utils::hook::nop(0x54E168_b, 4); // this crashes when reconnecting for some reason
// allow server owner to modify net_port before the socket bind // allow server owner to modify net_port before the socket bind
utils::hook::call(0x140512BE5, register_netport_stub); utils::hook::call(0x5BD032_b, register_netport_stub);
utils::hook::call(0x140512D20, register_netport_stub); utils::hook::call(0x5BD3F0_b, register_netport_stub);
// increase allowed packet size // increase allowed packet size
const auto max_packet_size = 0x20000; const auto max_packet_size = 0x20000;
utils::hook::set<int>(0x1404255F1, max_packet_size); utils::hook::set<int>(0x4F1ED1_b, max_packet_size);
utils::hook::set<int>(0x140425630, max_packet_size); utils::hook::set<int>(0x4F1F10_b, max_packet_size);
utils::hook::set<int>(0x140425522, max_packet_size); utils::hook::set<int>(0x4F1E02_b, max_packet_size);
utils::hook::set<int>(0x140425545, max_packet_size); utils::hook::set<int>(0x4F1E25_b, max_packet_size);
// ignore built in "print" oob command and add in our own // ignore built in "print" oob command and add in our own
utils::hook::set<uint8_t>(0x14025280E, 0xEB); utils::hook::set<uint8_t>(0x12F817_b, 0xEB);
on("print", [](const game::netadr_s&, const std::string_view& data) on("print", [](const game::netadr_s&, const std::string_view& data)
{ {
const std::string message{data}; const std::string message{data};
@ -317,7 +311,7 @@ namespace network
// Use our own socket since the game's socket doesn't work with non localhost addresses // Use our own socket since the game's socket doesn't work with non localhost addresses
// why? no idea // why? no idea
utils::hook::jump(0x140512B40, create_socket); utils::hook::jump(0x5BD210_b, create_socket);
} }
} }
}; };

View File

@ -71,11 +71,11 @@ namespace party
perform_game_initialization(); perform_game_initialization();
// exit from virtuallobby // exit from virtuallobby
utils::hook::invoke<void>(0x140256D40, 1); utils::hook::invoke<void>(0x13C9C0_b, 1);
// CL_ConnectFromParty // CL_ConnectFromParty
char session_info[0x100] = {}; char session_info[0x100] = {};
utils::hook::invoke<void>(0x140251560, 0, session_info, &target, mapname.data(), gametype.data()); utils::hook::invoke<void>(0x12DFF0_b, 0, session_info, &target, mapname.data(), gametype.data());
} }
std::string get_dvar_string(const std::string& dvar) std::string get_dvar_string(const std::string& dvar)
@ -111,52 +111,43 @@ namespace party
return false; return false;
} }
void didyouknow_stub(const char* dvar_name, const char* string) const char* get_didyouknow_stub(void* table, int row, int column)
{ {
if (!party::sv_motd.empty()) if (party::sv_motd.empty())
{ {
string = party::sv_motd.data(); return utils::hook::invoke<const char*>(0x5A0AC0_b, table, row, column);
} }
return utils::string::va("%s", party::sv_motd.data());
// This function either does Dvar_SetString or Dvar_RegisterString for the given dvar
utils::hook::invoke<void>(0x1404FB210, dvar_name, string);
} }
void disconnect_stub() void disconnect()
{ {
if (!game::VirtualLobby_Loaded()) if (!game::VirtualLobby_Loaded())
{ {
if (game::CL_IsCgameInitialized()) if (game::CL_IsCgameInitialized())
{ {
// CL_ForwardCommandToServer // CL_AddReliableCommand
utils::hook::invoke<void>(0x140253480, 0, "disconnect"); utils::hook::invoke<void>(0x12B810_b, 0, "disconnect");
// CL_WritePacket // CL_WritePacket
utils::hook::invoke<void>(0x14024DB10, 0); utils::hook::invoke<void>(0x13D490_b, 0);
} }
// CL_Disconnect // CL_Disconnect
utils::hook::invoke<void>(0x140252060, 0); utils::hook::invoke<void>(0x12F080_b, 0);
} }
} }
utils::hook::detour cldisconnect_hook; utils::hook::detour cl_disconnect_hook;
void cl_disconnect_stub(int a1) void cl_disconnect_stub(int showMainMenu) // possibly bool
{ {
party::clear_sv_motd(); party::clear_sv_motd();
cldisconnect_hook.invoke<void>(a1); cl_disconnect_hook.invoke<void>(showMainMenu);
} }
const auto drop_reason_stub = utils::hook::assemble([](utils::hook::assembler& a)
{
a.mov(rdx, rdi);
a.mov(ecx, 2);
a.jmp(0x140251F78);
});
void menu_error(const std::string& error) void menu_error(const std::string& error)
{ {
utils::hook::invoke<void>(0x1400DACC0, error.data(), "MENU_NOTICE"); utils::hook::invoke<void>(0x17D770_b, error.data(), "MENU_NOTICE");
utils::hook::set(0x142C1DA98, 1); utils::hook::set(0x2ED2F78_b, 1);
} }
} }
@ -192,9 +183,15 @@ namespace party
int get_client_count() int get_client_count()
{ {
auto count = 0; auto count = 0;
const auto* svs_clients = *game::mp::svs_clients;
if (svs_clients == nullptr)
{
return count;
}
for (auto i = 0; i < *game::mp::svs_numclients; ++i) for (auto i = 0; i < *game::mp::svs_numclients; ++i)
{ {
if (game::mp::svs_clients[i].header.state >= 1) if (svs_clients[i].header.state >= 1)
{ {
++count; ++count;
} }
@ -206,9 +203,15 @@ namespace party
int get_bot_count() int get_bot_count()
{ {
auto count = 0; auto count = 0;
const auto* svs_clients = *game::mp::svs_clients;
if (svs_clients == nullptr)
{
return count;
}
for (auto i = 0; i < *game::mp::svs_numclients; ++i) for (auto i = 0; i < *game::mp::svs_numclients; ++i)
{ {
if (game::mp::svs_clients[i].header.state >= 1 && if (svs_clients[i].header.state >= 1 &&
game::SV_BotIsBot(i)) game::SV_BotIsBot(i))
{ {
++count; ++count;
@ -282,7 +285,6 @@ namespace party
} }
console::info("Starting map: %s\n", mapname.data()); console::info("Starting map: %s\n", mapname.data());
auto* gametype = game::Dvar_FindVar("g_gametype"); auto* gametype = game::Dvar_FindVar("g_gametype");
if (gametype && gametype->current.string) if (gametype && gametype->current.string)
{ {
@ -317,21 +319,28 @@ namespace party
return; return;
} }
// hook disconnect command function
utils::hook::jump(0x1402521C7, disconnect_stub);
// detour CL_Disconnect to clear motd // detour CL_Disconnect to clear motd
cldisconnect_hook.create(0x140252060, cl_disconnect_stub); cl_disconnect_hook.create(0x12F080_b, cl_disconnect_stub);
if (game::environment::is_mp()) if (game::environment::is_mp())
{ {
// show custom drop reason // show custom drop reason
utils::hook::nop(0x140251EFB, 13); utils::hook::nop(0x12EF4E_b, 13);
utils::hook::jump(0x140251EFB, drop_reason_stub, true); utils::hook::jump(0x12EF4E_b, utils::hook::assemble([](utils::hook::assembler& a)
{
a.mov(rdx, rsi);
a.mov(ecx, 2);
a.jmp(0x12EF27_b);
}), true);
command::add("disconnect", disconnect);
} }
// enable custom kick reason in GScr_KickPlayer // enable custom kick reason in GScr_KickPlayer
utils::hook::set<uint8_t>(0x140376A1D, 0xEB); utils::hook::set<uint8_t>(0xE423D_b, 0xEB);
// allow custom didyouknow based on sv_motd
utils::hook::call(0x1A8A3A_b, get_didyouknow_stub);
command::add("map", [](const command::params& argument) command::add("map", [](const command::params& argument)
{ {
@ -349,11 +358,12 @@ namespace party
{ {
return; return;
} }
*reinterpret_cast<int*>(0x14A3A91D0) = 1; // sv_map_restart
*reinterpret_cast<int*>(0x14A3A91D4) = 1; // sv_loadScripts
*reinterpret_cast<int*>(0x14A3A91D8) = 0; // sv_migrate
utils::hook::invoke<void>(0x14047E7F0); // SV_CheckLoadGame *reinterpret_cast<int*>(0xB7B8E60_b) = 1; // sv_map_restart
*reinterpret_cast<int*>(0xB7B8E64_b) = 1; // sv_loadScripts
*reinterpret_cast<int*>(0xB7B8E68_b) = 0; // sv_migrate
utils::hook::invoke<void>(0x54BD50_b); // SV_CheckLoadGame
}); });
command::add("fast_restart", []() command::add("fast_restart", []()
@ -546,8 +556,6 @@ namespace party
printf("%s\n", message.data()); printf("%s\n", message.data());
}); });
utils::hook::call(0x1404C6E8D, didyouknow_stub); // allow custom didyouknow based on sv_motd
network::on("getInfo", [](const game::netadr_s& target, const std::string_view& data) network::on("getInfo", [](const game::netadr_s& target, const std::string_view& data)
{ {
utils::info_string info{}; utils::info_string info{};

View File

@ -46,7 +46,7 @@ namespace patches
return "Unknown Soldier"; return "Unknown Soldier";
} }
return std::string{ username, username_len - 1 }; return std::string{username, username_len - 1};
} }
utils::hook::detour com_register_dvars_hook; utils::hook::detour com_register_dvars_hook;
@ -57,23 +57,21 @@ namespace patches
{ {
// Make name save // Make name save
dvars::register_string("name", get_login_username().data(), game::DVAR_FLAG_SAVED, "Player name."); dvars::register_string("name", get_login_username().data(), game::DVAR_FLAG_SAVED, "Player name.");
// Disable data validation error popup
dvars::register_int("data_validation_allow_drop", 0, 0, 0, game::DVAR_FLAG_NONE, "");
} }
return com_register_dvars_hook.invoke<void>(); return com_register_dvars_hook.invoke<void>();
} }
void set_client_dvar_from_server_stub(void* a1, void* a2, const char* dvar, const char* value) utils::hook::detour set_client_dvar_from_server_hook;
void set_client_dvar_from_server_stub(void* clientNum, void* cgameGlob, const char* dvar, const char* value)
{ {
if (dvar == "cg_fov"s || dvar == "cg_fovMin"s) if (dvar == "cg_fov"s || dvar == "cg_fovMin"s)
{ {
return; return;
} }
// CG_SetClientDvarFromServer set_client_dvar_from_server_hook.invoke<void>(0x11AA90_b, clientNum, cgameGlob, dvar, value);
utils::hook::invoke<void>(0x140236120, a1, a2, dvar, value);
} }
const char* db_read_raw_file_stub(const char* filename, char* buf, const int size) const char* db_read_raw_file_stub(const char* filename, char* buf, const int size)
@ -92,7 +90,7 @@ namespace patches
} }
// DB_ReadRawFile // DB_ReadRawFile
return utils::hook::invoke<const char*>(SELECT_VALUE(0x1401CD4F0, 0x1402BEF10), filename, buf, size); return utils::hook::invoke<const char*>(SELECT_VALUE(0x1F4D00_b, 0x3994B0_b), filename, buf, size);
} }
void bsp_sys_error_stub(const char* error, const char* arg1) void bsp_sys_error_stub(const char* error, const char* arg1)
@ -114,9 +112,15 @@ namespace patches
utils::hook::detour cmd_lui_notify_server_hook; utils::hook::detour cmd_lui_notify_server_hook;
void cmd_lui_notify_server_stub(game::mp::gentity_s* ent) void cmd_lui_notify_server_stub(game::mp::gentity_s* ent)
{ {
const auto svs_clients = *game::mp::svs_clients;
if (svs_clients == nullptr)
{
return;
}
command::params_sv params{}; command::params_sv params{};
const auto menu_id = atoi(params.get(1)); const auto menu_id = atoi(params.get(1));
const auto client = &game::mp::svs_clients[ent->s.entityNum]; const auto client = &svs_clients[ent->s.entityNum];
// 22 => "end_game" // 22 => "end_game"
if (menu_id == 22 && client->header.remoteAddress.type != game::NA_LOOPBACK) if (menu_id == 22 && client->header.remoteAddress.type != game::NA_LOOPBACK)
@ -138,15 +142,17 @@ namespace patches
return; return;
} }
utils::hook::invoke<void>(0x140481A00, client, msg); utils::hook::invoke<void>(0x54EC50_b, client, msg);
} }
void aim_assist_add_to_target_list(void* a1, void* a2) void aim_assist_add_to_target_list(void* aaGlob, void* screenTarget)
{ {
if (!dvars::aimassist_enabled->current.enabled) if (!dvars::aimassist_enabled->current.enabled)
{
return; return;
}
game::AimAssist_AddToTargetList(a1, a2); game::AimAssist_AddToTargetList(aaGlob, screenTarget);
} }
} }
@ -156,15 +162,15 @@ namespace patches
void post_unpack() override void post_unpack() override
{ {
// Register dvars // Register dvars
com_register_dvars_hook.create(SELECT_VALUE(0x140351B80, 0x1400D9320), &com_register_dvars_stub); com_register_dvars_hook.create(SELECT_VALUE(0x385BE0_b, 0x15BB60_b), &com_register_dvars_stub);
// Unlock fps in main menu // Unlock fps in main menu
utils::hook::set<BYTE>(SELECT_VALUE(0x14018D47B, 0x14025B86B), 0xEB); utils::hook::set<BYTE>(SELECT_VALUE(0x1B1EAB_b, 0x34396B_b), 0xEB);
if (!game::environment::is_dedi()) if (!game::environment::is_dedi())
{ {
// Fix mouse lag // Fix mouse lag
utils::hook::nop(SELECT_VALUE(0x1403E3C05, 0x1404DB1AF), 6); utils::hook::nop(SELECT_VALUE(0x4631F9_b, 0x5BFF89_b), 6);
scheduler::loop([]() scheduler::loop([]()
{ {
SetThreadExecutionState(ES_DISPLAY_REQUIRED); SetThreadExecutionState(ES_DISPLAY_REQUIRED);
@ -177,11 +183,11 @@ namespace patches
dvars::override::register_float("cg_fovMin", 1.f, 1.0f, 90.f, game::DvarFlags::DVAR_FLAG_SAVED); dvars::override::register_float("cg_fovMin", 1.f, 1.0f, 90.f, game::DvarFlags::DVAR_FLAG_SAVED);
// Allow kbam input when gamepad is enabled // Allow kbam input when gamepad is enabled
utils::hook::nop(SELECT_VALUE(0x14018797E, 0x14024EF60), 2); utils::hook::nop(SELECT_VALUE(0x1AC0CE_b, 0x135EFB_b), 2);
utils::hook::nop(SELECT_VALUE(0x1401856DC, 0x14024C6B0), 6); utils::hook::nop(SELECT_VALUE(0x1A9DDC_b, 0x13388F_b), 6);
// Allow executing custom cfg files with the "exec" command // Allow executing custom cfg files with the "exec" command
utils::hook::call(SELECT_VALUE(0x140343855, 0x140403E28), db_read_raw_file_stub); utils::hook::call(SELECT_VALUE(0x376EB5_b, 0x156D41_b), db_read_raw_file_stub);
if (!game::environment::is_sp()) if (!game::environment::is_sp())
{ {
@ -191,56 +197,54 @@ namespace patches
static void patch_mp() static void patch_mp()
{ {
// Use name dvar utils::hook::jump(0x5BB9C0_b, &live_get_local_client_name);
utils::hook::jump(0x14050FF90, &live_get_local_client_name);
// Disable data validation error popup
dvars::override::register_int("data_validation_allow_drop", 0, 0, 0, game::DVAR_FLAG_NONE);
// Patch SV_KickClientNum // Patch SV_KickClientNum
sv_kick_client_num_hook.create(0x14047ED00, &sv_kick_client_num); sv_kick_client_num_hook.create(game::SV_KickClientNum, &sv_kick_client_num);
// block changing name in-game // block changing name in-game
utils::hook::set<uint8_t>(0x14047FC90, 0xC3); utils::hook::set<uint8_t>(0x54CFF0_b, 0xC3);
// patch "Couldn't find the bsp for this map." error to not be fatal in mp
utils::hook::call(0x1402BA26B, bsp_sys_error_stub);
// client side aim assist dvar // client side aim assist dvar
dvars::aimassist_enabled = dvars::register_bool("aimassist_enabled", true, dvars::aimassist_enabled = dvars::register_bool("aimassist_enabled", true,
game::DvarFlags::DVAR_FLAG_SAVED, game::DvarFlags::DVAR_FLAG_SAVED,
"Enables aim assist for controllers"); "Enables aim assist for controllers");
utils::hook::call(0x14009EE9E, aim_assist_add_to_target_list); utils::hook::call(0xE857F_b, aim_assist_add_to_target_list);
// patch "Couldn't find the bsp for this map." error to not be fatal in mp
utils::hook::call(0x39465B_b, bsp_sys_error_stub);
// isProfanity // isProfanity
utils::hook::set(0x1402877D0, 0xC3C033); utils::hook::set(0x361AA0_b, 0xC3C033);
// disable emblems
dvars::override::register_int("emblems_active", 0, 0, 0, game::DVAR_FLAG_NONE);
utils::hook::set<uint8_t>(0x140479590, 0xC3); // don't register commands
// disable elite_clan // disable elite_clan
dvars::override::register_int("elite_clan_active", 0, 0, 0, game::DVAR_FLAG_NONE); dvars::override::register_int("elite_clan_active", 0, 0, 0, game::DVAR_FLAG_NONE);
utils::hook::set<uint8_t>(0x140585680, 0xC3); // don't register commands utils::hook::set<uint8_t>(0x62D2F0_b, 0xC3); // don't register commands
// disable codPointStore // disable codPointStore
dvars::override::register_int("codPointStore_enabled", 0, 0, 0, game::DVAR_FLAG_NONE); dvars::override::register_int("codPointStore_enabled", 0, 0, 0, game::DVAR_FLAG_NONE);
// don't register every replicated dvar as a network dvar // don't register every replicated dvar as a network dvar
utils::hook::nop(0x14039E58E, 5); // dvar_foreach utils::hook::nop(0x47408E_b, 5); // dvar_foreach
// patch "Server is different version" to show the server client version // patch "Server is different version" to show the server client version
utils::hook::inject(0x140480955, VERSION); utils::hook::inject(0x54DCE5_b, VERSION);
// prevent servers overriding our fov // prevent servers overriding our fov
utils::hook::call(0x14023279E, set_client_dvar_from_server_stub); set_client_dvar_from_server_hook.create(0x11AA90_b, set_client_dvar_from_server_stub);
utils::hook::nop(0x1400DAF69, 5); utils::hook::nop(0x17DA96_b, 0x16);
utils::hook::nop(0x140190C16, 5); utils::hook::nop(0xE00BE_b, 0x17);
utils::hook::set<uint8_t>(0x14021D22A, 0xEB); utils::hook::set<uint8_t>(0x307F39_b, 0xEB);
// some anti tamper thing that kills performance // some [data validation] anti tamper thing that kills performance
dvars::override::register_int("dvl", 0, 0, 0, game::DVAR_FLAG_READ); dvars::override::register_int("dvl", 0, 0, 0, game::DVAR_FLAG_READ);
// unlock safeArea_* // unlock safeArea_*
utils::hook::jump(0x1402624F5, 0x140262503); utils::hook::jump(0x347BC5_b, 0x347BD3_b);
utils::hook::jump(0x14026251C, 0x140262547); utils::hook::jump(0x347BEC_b, 0x347C17_b);
dvars::override::register_float("safeArea_adjusted_horizontal", 1, 0, 1, game::DVAR_FLAG_SAVED); dvars::override::register_float("safeArea_adjusted_horizontal", 1, 0, 1, game::DVAR_FLAG_SAVED);
dvars::override::register_float("safeArea_adjusted_vertical", 1, 0, 1, game::DVAR_FLAG_SAVED); dvars::override::register_float("safeArea_adjusted_vertical", 1, 0, 1, game::DVAR_FLAG_SAVED);
dvars::override::register_float("safeArea_horizontal", 1, 0, 1, game::DVAR_FLAG_SAVED); dvars::override::register_float("safeArea_horizontal", 1, 0, 1, game::DVAR_FLAG_SAVED);
@ -262,23 +266,26 @@ namespace patches
dvars::override::register_int("com_maxfps", 0, 0, 1000, game::DVAR_FLAG_SAVED); dvars::override::register_int("com_maxfps", 0, 0, 1000, game::DVAR_FLAG_SAVED);
// Prevent clients from ending the game as non host by sending 'end_game' lui notification // Prevent clients from ending the game as non host by sending 'end_game' lui notification
cmd_lui_notify_server_hook.create(0x140335A70, cmd_lui_notify_server_stub); cmd_lui_notify_server_hook.create(0x412D50_b, cmd_lui_notify_server_stub);
// Prevent clients from sending invalid reliableAcknowledge // Prevent clients from sending invalid reliableAcknowledge
utils::hook::call(0x1404899C6, sv_execute_client_message_stub); utils::hook::call(0x1CBD06_b, sv_execute_client_message_stub);
// "fix" for rare 'Out of memory error' error // "fix" for rare 'Out of memory error' error
if (utils::flags::has_flag("memoryfix")) if (utils::flags::has_flag("memoryfix"))
{ {
utils::hook::jump(0x140578BE0, malloc); utils::hook::jump(0x6200C0_b, malloc);
utils::hook::jump(0x140578B00, _aligned_malloc); utils::hook::jump(0x61FFE0_b, _aligned_malloc);
utils::hook::jump(0x140578C40, free); utils::hook::jump(0x620120_b, free);
utils::hook::jump(0x140578D30, realloc); utils::hook::jump(0x620210_b, realloc);
utils::hook::jump(0x140578B60, _aligned_realloc); utils::hook::jump(0x620040_b, _aligned_realloc);
} }
// Change default hostname and make it replicated // Change default hostname and make it replicated
dvars::override::register_string("sv_hostname", "^2H1-Mod^7 Default Server", game::DVAR_FLAG_REPLICATED); dvars::override::register_string("sv_hostname", "^2H1-Mod^7 Default Server", game::DVAR_FLAG_REPLICATED);
// Dont free server/client memory on asset loading (fixes crashing on map rotation)
utils::hook::nop(0x132474_b, 5);
} }
}; };
} }

View File

@ -27,19 +27,17 @@ namespace ranked
dvars::override::register_bool("xblive_privatematch", false, game::DVAR_FLAG_REPLICATED); dvars::override::register_bool("xblive_privatematch", false, game::DVAR_FLAG_REPLICATED);
} }
if (game::environment::is_dedi() && !utils::flags::has_flag("unranked")) if (game::environment::is_dedi())
{ {
dvars::override::register_bool("xblive_privatematch", false, game::DVAR_FLAG_REPLICATED | game::DVAR_FLAG_WRITE); dvars::override::register_bool("xblive_privatematch", false, game::DVAR_FLAG_REPLICATED | game::DVAR_FLAG_WRITE);
dvars::register_bool("force_ranking", true, game::DVAR_FLAG_WRITE, ""); // Skip some check in _menus.gsc
// Skip some check in _menus.gsc
dvars::register_bool("force_ranking", true, game::DVAR_FLAG_WRITE, "");
} }
// Always run bots, even if xblive_privatematch is 0 // Always run bots, even if xblive_privatematch is 0
utils::hook::set(0x1401D9300, 0xC301B0); // BG_BotSystemEnabled utils::hook::set(0x2C10B0_b, 0xC301B0); // BG_BotSystemEnabled
utils::hook::set(0x1401D90D0, 0xC301B0); // BG_AISystemEnabled utils::hook::set(0x2C0E60_b, 0xC301B0); // BG_AISystemEnabled
utils::hook::set(0x1401D92A0, 0xC301B0); // BG_BotFastFileEnabled utils::hook::set(0x2C1040_b, 0xC301B0); // BG_BotFastFileEnabled
utils::hook::set(0x1401D9400, 0xC301B0); // BG_BotsUsingTeamDifficulty utils::hook::set(0x2C11B0_b, 0xC301B0); // BG_BotsUsingTeamDifficulty
} }
}; };
} }

View File

@ -0,0 +1,232 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "game/game.hpp"
#include "game/dvars.hpp"
#include "command.hpp"
#include "console.hpp"
#include "network.hpp"
#include "scheduler.hpp"
#include "rcon.hpp"
#include <utils/hook.hpp>
#include <utils/string.hpp>
namespace rcon
{
namespace
{
bool is_redirecting_ = false;
bool has_redirected_ = false;
game::netadr_s redirect_target_ = {};
std::recursive_mutex redirect_lock;
void setup_redirect(const game::netadr_s& target)
{
std::lock_guard<std::recursive_mutex> $(redirect_lock);
has_redirected_ = false;
is_redirecting_ = true;
redirect_target_ = target;
}
void clear_redirect()
{
std::lock_guard<std::recursive_mutex> $(redirect_lock);
has_redirected_ = false;
is_redirecting_ = false;
redirect_target_ = {};
}
void send_rcon_command(const std::string& password, const std::string& data)
{
// If you are the server, don't bother with rcon and just execute the command
if (game::Dvar_FindVar("sv_running")->current.enabled)
{
game::Cbuf_AddText(0, 0, data.data());
return;
}
if (password.empty())
{
console::info("You must login first to use RCON\n");
return;
}
if (*game::mp::connect_state != nullptr && *game::connectionState >= game::CA_CONNECTED)
{
const auto target = (*game::mp::connect_state)->address;
const auto buffer = password + " " + data;
network::send(target, "rcon", buffer);
}
else
{
console::warn("You need to be connected to a server!\n");
}
}
std::string build_status_buffer()
{
const auto sv_maxclients = game::Dvar_FindVar("sv_maxclients");
const auto mapname = game::Dvar_FindVar("mapname");
std::string buffer{};
buffer.append(utils::string::va("map: %s\n", mapname->current.string));
buffer.append(
"num score bot ping guid name address qport\n");
buffer.append(
"--- ----- --- ---- -------------------------------- ---------------- --------------------- -----\n");
const auto svs_clients = *game::mp::svs_clients;
if (svs_clients == nullptr)
{
return buffer;
}
for (int i = 0; i < sv_maxclients->current.integer; i++)
{
const auto client = &svs_clients[i];
char clean_name[32] = {0};
strncpy_s(clean_name, client->name, sizeof(clean_name));
game::I_CleanStr(clean_name);
if (client->header.state >= 1)
{
buffer.append(utils::string::va("%3i %5i %3s %s %32s %16s %21s %5i\n",
i,
game::G_GetClientScore(i),
game::SV_BotIsBot(i) ? "Yes" : "No",
(client->header.state == 2)
? "CNCT"
: (client->header.state == 1)
? "ZMBI"
: utils::string::va("%4i", game::SV_GetClientPing(i)),
game::SV_GetGuid(i),
clean_name,
network::net_adr_to_string(client->header.remoteAddress),
client->header.remoteAddress.port)
);
}
}
return buffer;
}
}
bool message_redirect(const std::string& message)
{
std::lock_guard<std::recursive_mutex> $(redirect_lock);
if (is_redirecting_)
{
has_redirected_ = true;
network::send(redirect_target_, "print", message, '\n');
return true;
}
return false;
}
class component final : public component_interface
{
public:
void post_unpack() override
{
if (game::environment::is_sp())
{
return;
}
scheduler::once([]()
{
dvars::register_string("rcon_password", "", game::DvarFlags::DVAR_FLAG_NONE,
"The password for remote console");
}, scheduler::pipeline::main);
command::add("status", []()
{
const auto sv_running = game::Dvar_FindVar("sv_running");
if (game::VirtualLobby_Loaded() || !sv_running || !sv_running->current.enabled)
{
console::error("Server is not running\n");
return;
}
auto status_buffer = build_status_buffer();
console::info(status_buffer.data());
});
if (!game::environment::is_dedi())
{
command::add("rcon", [&](const command::params& params)
{
static std::string rcon_password{};
if (params.size() < 2) return;
const auto operation = params.get(1);
if (operation == "login"s)
{
if (params.size() < 3) return;
rcon_password = params.get(2);
}
else if (operation == "logout"s)
{
rcon_password.clear();
}
else
{
send_rcon_command(rcon_password, params.join(1));
}
});
}
else
{
network::on("rcon", [](const game::netadr_s& addr, const std::string_view& data)
{
const auto message = std::string{data};
const auto pos = message.find_first_of(" ");
if (pos == std::string::npos)
{
network::send(addr, "print", "Invalid RCon request", '\n');
console::info("Invalid RCon request from %s\n", network::net_adr_to_string(addr));
return;
}
const auto password = message.substr(0, pos);
const auto command = message.substr(pos + 1);
const auto rcon_password = game::Dvar_FindVar("rcon_password");
if (command.empty() || !rcon_password || !rcon_password->current.string || !strlen(
rcon_password->current.string))
{
return;
}
setup_redirect(addr);
if (password != rcon_password->current.string)
{
network::send(redirect_target_, "print", "Invalid rcon password", '\n');
console::error("Invalid rcon password\n");
}
else
{
command::execute(command, true);
}
if (!has_redirected_)
{
network::send(redirect_target_, "print", "", '\n');
}
clear_redirect();
});
}
}
};
}
REGISTER_COMPONENT(rcon::component)

View File

@ -0,0 +1,6 @@
#pragma once
namespace rcon
{
bool message_redirect(const std::string& message);
}

View File

@ -1,8 +1,10 @@
#include <std_include.hpp> #include <std_include.hpp>
#include "loader/component_loader.hpp" #include "loader/component_loader.hpp"
#include "game_module.hpp"
#include <utils/nt.hpp> #include <utils/nt.hpp>
#include <utils/string.hpp> #include <utils/string.hpp>
#include "game_module.hpp"
namespace redirect namespace redirect
{ {

View File

@ -1,5 +1,8 @@
#include <std_include.hpp> #include <std_include.hpp>
#include "loader/component_loader.hpp" #include "loader/component_loader.hpp"
#include "dvars.hpp"
#include "game/game.hpp" #include "game/game.hpp"
#include "game/dvars.hpp" #include "game/dvars.hpp"
@ -30,7 +33,6 @@ namespace renderer
void gfxdrawmethod() void gfxdrawmethod()
{ {
game::gfxDrawMethod->drawScene = game::GFX_DRAW_SCENE_STANDARD; game::gfxDrawMethod->drawScene = game::GFX_DRAW_SCENE_STANDARD;
game::gfxDrawMethod->baseTechType = dvars::r_fullbright->current.enabled ? get_fullbright_technique() : game::TECHNIQUE_LIT; game::gfxDrawMethod->baseTechType = dvars::r_fullbright->current.enabled ? get_fullbright_technique() : game::TECHNIQUE_LIT;
game::gfxDrawMethod->emissiveTechType = dvars::r_fullbright->current.enabled ? get_fullbright_technique() : game::TECHNIQUE_EMISSIVE; game::gfxDrawMethod->emissiveTechType = dvars::r_fullbright->current.enabled ? get_fullbright_technique() : game::TECHNIQUE_EMISSIVE;
game::gfxDrawMethod->forceTechType = dvars::r_fullbright->current.enabled ? get_fullbright_technique() : 242; game::gfxDrawMethod->forceTechType = dvars::r_fullbright->current.enabled ? get_fullbright_technique() : 242;
@ -67,19 +69,15 @@ namespace renderer
dvars::r_fullbright = dvars::register_int("r_fullbright", 0, 0, 4, game::DVAR_FLAG_SAVED, "Toggles rendering without lighting"); dvars::r_fullbright = dvars::register_int("r_fullbright", 0, 0, 4, game::DVAR_FLAG_SAVED, "Toggles rendering without lighting");
r_init_draw_method_hook.create(SELECT_VALUE(0x1404BD140, 0x1405C46E0), &r_init_draw_method_stub); r_init_draw_method_hook.create(SELECT_VALUE(0x5467E0_b, 0x669580_b), &r_init_draw_method_stub);
r_update_front_end_dvar_options_hook.create(SELECT_VALUE(0x1404F8870, 0x1405FF9E0), &r_update_front_end_dvar_options_stub); r_update_front_end_dvar_options_hook.create(SELECT_VALUE(0x583560_b, 0x6A78C0_b), &r_update_front_end_dvar_options_stub);
// use "saved" flags for "r_normalMap" // use "saved" flags
utils::hook::set<uint8_t>(SELECT_VALUE(0x1404CF5CA, 0x1405D460E), game::DVAR_FLAG_SAVED); dvars::override::register_enum("r_normalMap", game::DVAR_FLAG_SAVED);
dvars::override::register_enum("r_specularMap", game::DVAR_FLAG_SAVED);
// use "saved" flags for "r_specularMap" dvars::override::register_enum("r_specOccMap", game::DVAR_FLAG_SAVED);
utils::hook::set<uint8_t>(SELECT_VALUE(0x1404CF5F5, 0x1405D4639), game::DVAR_FLAG_SAVED);
// use "saved" flags for "r_specOccMap"
utils::hook::set<uint8_t>(SELECT_VALUE(0x1404CF620, 0x1405D4664), game::DVAR_FLAG_SAVED);
} }
}; };
} }
REGISTER_COMPONENT(renderer::component) REGISTER_COMPONENT(renderer::component)

View File

@ -75,7 +75,8 @@ namespace scheduler
{ {
new_callbacks_.access([&](task_list& new_tasks) new_callbacks_.access([&](task_list& new_tasks)
{ {
tasks.insert(tasks.end(), std::move_iterator<task_list::iterator>(new_tasks.begin()), std::move_iterator<task_list::iterator>(new_tasks.end())); tasks.insert(tasks.end(), std::move_iterator<task_list::iterator>(new_tasks.begin()),
std::move_iterator<task_list::iterator>(new_tasks.end()));
new_tasks = {}; new_tasks = {};
}); });
}); });
@ -99,7 +100,6 @@ namespace scheduler
void r_end_frame_stub() void r_end_frame_stub()
{ {
execute(pipeline::renderer); execute(pipeline::renderer);
r_end_frame_hook.invoke<void>();
} }
void server_frame_stub() void server_frame_stub()
@ -108,10 +108,14 @@ namespace scheduler
execute(pipeline::server); execute(pipeline::server);
} }
void main_frame_stub() void* main_frame_stub()
{ {
main_frame_hook.invoke<void>(); const auto _0 = gsl::finally([]()
execute(pipeline::main); {
execute(pipeline::main);
});
return main_frame_hook.invoke<void*>();
} }
void hks_frame_stub() void hks_frame_stub()
@ -121,11 +125,12 @@ namespace scheduler
{ {
execute(pipeline::lui); execute(pipeline::lui);
} }
hks_frame_hook.invoke<bool>();
} }
} }
void schedule(const std::function<bool()>& callback, const pipeline type, void schedule(const std::function<bool()>& callback, const pipeline type,
const std::chrono::milliseconds delay) const std::chrono::milliseconds delay)
{ {
assert(type >= 0 && type < pipeline::count); assert(type >= 0 && type < pipeline::count);
@ -138,7 +143,7 @@ namespace scheduler
} }
void loop(const std::function<void()>& callback, const pipeline type, void loop(const std::function<void()>& callback, const pipeline type,
const std::chrono::milliseconds delay) const std::chrono::milliseconds delay)
{ {
schedule([callback]() schedule([callback]()
{ {
@ -148,7 +153,7 @@ namespace scheduler
} }
void once(const std::function<void()>& callback, const pipeline type, void once(const std::function<void()>& callback, const pipeline type,
const std::chrono::milliseconds delay) const std::chrono::milliseconds delay)
{ {
schedule([callback]() schedule([callback]()
{ {
@ -158,7 +163,7 @@ namespace scheduler
} }
void on_game_initialized(const std::function<void()>& callback, const pipeline type, void on_game_initialized(const std::function<void()>& callback, const pipeline type,
const std::chrono::milliseconds delay) const std::chrono::milliseconds delay)
{ {
schedule([=]() schedule([=]()
{ {
@ -190,10 +195,22 @@ namespace scheduler
void post_unpack() override void post_unpack() override
{ {
r_end_frame_hook.create(SELECT_VALUE(0x1404F7310, 0x1405FE470), scheduler::r_end_frame_stub); utils::hook::jump(SELECT_VALUE(0x581FB0_b, 0x6A6300_b), utils::hook::assemble([](utils::hook::assembler& a)
g_run_frame_hook.create(SELECT_VALUE(0x1402772D0, 0x14033A640), scheduler::server_frame_stub); {
main_frame_hook.create(SELECT_VALUE(0x1401CE8D0, 0x1400D8310), scheduler::main_frame_stub); a.pushad64();
hks_frame_hook.create(SELECT_VALUE(0x1400E37F0, 0x1401755B0), scheduler::hks_frame_stub); a.call_aligned(r_end_frame_stub);
a.popad64();
a.sub(rsp, 0x28);
a.call(SELECT_VALUE(0x581840_b, 0x6A5C20_b));
a.mov(rax, SELECT_VALUE(0x1182A680_b, 0xEAB4308_b));
a.mov(rax, qword_ptr(rax));
a.jmp(SELECT_VALUE(0x581FC0_b, 0x6A6310_b));
}), true);
g_run_frame_hook.create(SELECT_VALUE(0x2992E0_b, 0x417940_b), scheduler::server_frame_stub);
main_frame_hook.create(SELECT_VALUE(0x1B1DF0_b, 0x3438B0_b), scheduler::main_frame_stub);
hks_frame_hook.create(SELECT_VALUE(0x1028D0_b, 0x2792E0_b), scheduler::hks_frame_stub);
} }
void pre_destroy() override void pre_destroy() override
@ -207,4 +224,4 @@ namespace scheduler
}; };
} }
REGISTER_COMPONENT(scheduler::component) REGISTER_COMPONENT(scheduler::component)

View File

@ -94,6 +94,7 @@ namespace scripting
script_function_table.clear(); script_function_table.clear();
} }
scripting::notify(*game::levelEntityId, "shutdownGame_called", {1});
lua::engine::stop(); lua::engine::stop();
return g_shutdown_game_hook.invoke<void>(free_scripts); return g_shutdown_game_hook.invoke<void>(free_scripts);
} }
@ -159,26 +160,6 @@ namespace scripting
scripting::token_map[str] = result; scripting::token_map[str] = result;
return result; return result;
} }
game::XAssetHeader db_find_xasset_header_stub(game::XAssetType type, const char* name, int allow_create_default)
{
const auto result = db_find_xasset_header_hook.invoke<game::XAssetHeader>(type, name, allow_create_default);
if (!g_dump_scripts->current.enabled || type != game::XAssetType::ASSET_TYPE_SCRIPTFILE)
{
return result;
}
std::string buffer;
buffer.append(result.scriptfile->name, strlen(result.scriptfile->name) + 1);
buffer.append(reinterpret_cast<char*>(&result.scriptfile->compressedLen), 4);
buffer.append(reinterpret_cast<char*>(&result.scriptfile->len), 4);
buffer.append(reinterpret_cast<char*>(&result.scriptfile->bytecodeLen), 4);
buffer.append(result.scriptfile->buffer, result.scriptfile->compressedLen);
buffer.append(result.scriptfile->bytecode, result.scriptfile->bytecodeLen);
utils::io::write_file(utils::string::va("gsc_dump/%s.gscbin", name), buffer);
return result;
}
} }
class component final : public component_interface class component final : public component_interface
@ -186,27 +167,24 @@ namespace scripting
public: public:
void post_unpack() override void post_unpack() override
{ {
vm_notify_hook.create(SELECT_VALUE(0x140379A00, 0x1404479F0), vm_notify_stub); vm_notify_hook.create(SELECT_VALUE(0x3CD500_b, 0x514560_b), vm_notify_stub);
scr_add_class_field_hook.create(SELECT_VALUE(0x140370370, 0x14043E2C0), scr_add_class_field_stub); scr_add_class_field_hook.create(SELECT_VALUE(0x3C3CE0_b, 0x50AE20_b), scr_add_class_field_stub);
scr_set_thread_position_hook.create(SELECT_VALUE(0x14036A180, 0x140437D10), scr_set_thread_position_stub); scr_set_thread_position_hook.create(SELECT_VALUE(0x3BD890_b, 0x504870_b), scr_set_thread_position_stub);
process_script_hook.create(SELECT_VALUE(0x1403737E0, 0x1404417E0), process_script_stub); process_script_hook.create(SELECT_VALUE(0x3C7200_b, 0x50E340_b), process_script_stub);
sl_get_canonical_string_hook.create(game::SL_GetCanonicalString, sl_get_canonical_string_stub); sl_get_canonical_string_hook.create(game::SL_GetCanonicalString, sl_get_canonical_string_stub);
if (!game::environment::is_sp()) if (!game::environment::is_sp())
{ {
scr_load_level_hook.create(SELECT_VALUE(0x1402A5BE0, 0x1403727C0), scr_load_level_stub); scr_load_level_hook.create(0x450FC0_b, scr_load_level_stub);
} }
else else
{ {
vm_execute_hook.create(SELECT_VALUE(0x140376590, 0x140444580), vm_execute_stub); vm_execute_hook.create(0x3CA080_b, vm_execute_stub);
} }
g_shutdown_game_hook.create(SELECT_VALUE(0x140277D40, 0x140345A60), g_shutdown_game_stub); g_shutdown_game_hook.create(SELECT_VALUE(0x2A5130_b, 0x422F30_b), g_shutdown_game_stub);
db_find_xasset_header_hook.create(game::DB_FindXAssetHeader, db_find_xasset_header_stub);
g_dump_scripts = dvars::register_bool("g_dumpScripts", false, game::DVAR_FLAG_NONE, "Dump GSC scripts");
scheduler::loop([]() scheduler::loop([]()
{ {

View File

@ -118,8 +118,7 @@ namespace server_list
return diff > server_limit ? server_limit : static_cast<int>(diff); return diff > server_limit ? server_limit : static_cast<int>(diff);
} }
const char* ui_feeder_item_text(int /*localClientNum*/, void* /*a2*/, void* /*a3*/, const int index, const char* ui_feeder_item_text(const int index, const int column)
const int column)
{ {
std::lock_guard<std::mutex> _(mutex); std::lock_guard<std::mutex> _(mutex);
@ -260,18 +259,18 @@ namespace server_list
utils::hook::detour lui_open_menu_hook; utils::hook::detour lui_open_menu_hook;
void lui_open_menu_stub(int controllerIndex, const char* menu, int a3, int a4, unsigned int a5) void lui_open_menu_stub(int controllerIndex, const char* menuName, int isPopup, int isModal, unsigned int isExclusive)
{ {
#ifdef DEBUG #ifdef DEBUG
console::info("[LUI] %s\n", menu); console::info("[LUI] %s\n", menuName);
#endif #endif
if (!strcmp(menu, "menu_systemlink_join")) if (!strcmp(menuName, "menu_systemlink_join"))
{ {
refresh_server_list(); refresh_server_list();
} }
lui_open_menu_hook.invoke<void>(controllerIndex, menu, a3, a4, a5); lui_open_menu_hook.invoke<void>(controllerIndex, menuName, isPopup, isModal, isExclusive);
} }
} }
@ -321,6 +320,11 @@ namespace server_list
return; return;
} }
if (info.get("gamename") != "H1")
{
return;
}
int start_time{}; int start_time{};
const auto now = game::Sys_Milliseconds(); const auto now = game::Sys_Milliseconds();
@ -366,13 +370,64 @@ namespace server_list
lui_open_menu_hook.create(game::LUI_OpenMenu, lui_open_menu_stub); lui_open_menu_hook.create(game::LUI_OpenMenu, lui_open_menu_stub);
// replace UI_RunMenuScript call in LUI_CoD_LuaCall_RefreshServerList to our refresh_servers // replace UI_RunMenuScript call in LUI_CoD_LuaCall_RefreshServerList to our refresh_servers
utils::hook::call(0x14018A0C9, &refresh_server_list); utils::hook::jump(0x28E049_b, utils::hook::assemble([](utils::hook::assembler& a)
utils::hook::call(0x14018A5DE, &join_server); {
utils::hook::nop(0x14018A5FD, 5); a.pushad64();
a.call_aligned(refresh_server_list);
a.popad64();
a.xor_(eax, eax);
a.mov(rbx, qword_ptr(rsp, 0x38));
a.add(rsp, 0x20);
a.pop(rdi);
a.ret();
}), true);
utils::hook::jump(0x28E557_b, utils::hook::assemble([](utils::hook::assembler& a)
{
a.mov(r8d, edi);
a.mov(ecx, eax);
a.mov(ebx, eax);
a.pushad64();
a.call_aligned(join_server);
a.popad64();
a.jmp(0x28E563_b);
}), true);
utils::hook::nop(0x28E57D_b, 5);
// do feeder stuff // do feeder stuff
utils::hook::call(0x14018A199, &ui_feeder_count); utils::hook::jump(0x28E117_b, utils::hook::assemble([](utils::hook::assembler& a)
utils::hook::call(0x14018A3B1, &ui_feeder_item_text); {
a.mov(ecx, eax);
a.pushad64();
a.call_aligned(ui_feeder_count);
a.movd(xmm0, eax);
a.popad64();
a.mov(rax, qword_ptr(rbx, 0x48));
a.cvtdq2ps(xmm0, xmm0);
a.jmp(0x28E12B_b);
}), true);
utils::hook::jump(0x28E331_b, utils::hook::assemble([](utils::hook::assembler& a)
{
a.push(rax);
a.pushad64();
a.mov(rcx, r9); // index
a.mov(rdx, qword_ptr(rsp, 0x88 + 0x20)); // column
a.call_aligned(ui_feeder_item_text);
a.mov(qword_ptr(rsp, 0x80), rax);
a.popad64();
a.pop(rax);
a.mov(rsi, qword_ptr(rsp, 0x90));
a.mov(rdi, rax);
a.jmp(0x28E341_b);
}), true);
scheduler::loop(do_frame_work, scheduler::pipeline::main); scheduler::loop(do_frame_work, scheduler::pipeline::main);

View File

@ -1,51 +0,0 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "scheduler.hpp"
#include "dvars.hpp"
#include "game/game.hpp"
#include "game/dvars.hpp"
#include <utils/nt.hpp>
#include <utils/hook.hpp>
#include <utils/flags.hpp>
namespace shaders
{
namespace
{
game::dvar_t* disable_shader_caching = nullptr;
bool shader_should_show_dialog_stub()
{
return !disable_shader_caching->current.enabled;
}
}
class component final : public component_interface
{
public:
void post_unpack() override
{
if (!game::environment::is_mp())
{
return;
}
const auto has_flag = utils::flags::has_flag("noshadercaching");
disable_shader_caching = dvars::register_bool("disable_shader_caching", has_flag,
game::DVAR_FLAG_SAVED, "Disable shader caching");
if (has_flag)
{
dvars::override::set_bool("disable_shader_caching", 1);
dvars::override::set_from_string("disable_shader_caching", "1");
}
utils::hook::jump(0x14007E710, shader_should_show_dialog_stub);
}
};
}
REGISTER_COMPONENT(shaders::component)

View File

@ -45,7 +45,7 @@ namespace slowmotion
return; return;
} }
utils::hook::jump(0x140365480, scr_cmd_set_slow_motion); utils::hook::jump(0x43D2E0_b, scr_cmd_set_slow_motion);
} }
}; };
} }

View File

@ -30,9 +30,9 @@ namespace splash
void post_unpack() override void post_unpack() override
{ {
// Disable native splash screen // Disable native splash screen
utils::hook::nop(SELECT_VALUE(0x1403E192E, 0x1405123E2), 5); utils::hook::set<uint8_t>(SELECT_VALUE(0x462B90_b, 0x5BDF20_b), 0xC3);
utils::hook::jump(SELECT_VALUE(0x1403E2E70, 0x140513AF0), destroy_stub); utils::hook::jump(SELECT_VALUE(0x462E40_b, 0x5BE1D0_b), destroy_stub, true);
utils::hook::jump(SELECT_VALUE(0x1403E2EB0, 0x140513B30), destroy_stub); utils::hook::jump(SELECT_VALUE(0x462E80_b, 0x5BE210_b), destroy_stub, true);
} }
void pre_destroy() override void pre_destroy() override

View File

@ -16,45 +16,35 @@ namespace stats
namespace namespace
{ {
game::dvar_t* cg_unlock_all_items; game::dvar_t* cg_unlock_all_items;
game::dvar_t* cg_unlock_all_loot;
utils::hook::detour is_item_unlocked_hook; utils::hook::detour is_item_unlocked_hook;
utils::hook::detour is_item_unlocked_hook2; utils::hook::detour is_item_unlocked_hook2;
utils::hook::detour is_item_unlocked_hook3;
int is_item_unlocked_stub(int a1, void* a2, int a3) int is_item_unlocked_stub(int a1, void* a2, void* a3, void* a4, int a5, void* a6)
{ {
if (cg_unlock_all_items->current.enabled) if (cg_unlock_all_items->current.enabled)
{ {
return 0; return 0;
} }
return is_item_unlocked_hook.invoke<int>(a1, a2, a3); return is_item_unlocked_hook.invoke<int>(a1, a2, a3, a4, a5, a6);
}
int is_item_unlocked_stub2(int a1, void* a2, void* a3, void* a4, int a5, void* a6)
{
if (cg_unlock_all_items->current.enabled)
{
return 0;
}
return is_item_unlocked_hook2.invoke<int>(a1, a2, a3, a4, a5, a6);
}
int is_item_unlocked_stub3(int a1)
{
if (cg_unlock_all_items->current.enabled)
{
return 0;
}
return is_item_unlocked_hook3.invoke<int>(a1);
} }
int is_item_unlocked() int is_item_unlocked()
{ {
return 0; return 0;
} }
int is_item_unlocked_stub2(void* a1, void* a2)
{
const auto state = is_item_unlocked_hook2.invoke<int>(a1, a2);
if (state == 15 /*Not In Inventory*/ && cg_unlock_all_loot->current.enabled)
{
return 0;
}
return state;
}
} }
class component final : public component_interface class component final : public component_interface
@ -67,22 +57,25 @@ namespace stats
return; return;
} }
utils::hook::jump(0x19E6E0_b, is_item_unlocked, true);
if (game::environment::is_dedi()) if (game::environment::is_dedi())
{ {
utils::hook::jump(0x140413E60, is_item_unlocked); utils::hook::jump(0x19E070_b, is_item_unlocked, true);
utils::hook::jump(0x140413860, is_item_unlocked); utils::hook::jump(0x19D390_b, is_item_unlocked, true);
utils::hook::jump(0x140412B70, is_item_unlocked); utils::hook::jump(0x19D140_b, is_item_unlocked, true);
} }
else else
{ {
is_item_unlocked_hook.create(0x19E070_b, is_item_unlocked_stub);
is_item_unlocked_hook2.create(0x19D140_b, is_item_unlocked_stub2);
cg_unlock_all_items = dvars::register_bool("cg_unlockall_items", false, game::DVAR_FLAG_SAVED, cg_unlock_all_items = dvars::register_bool("cg_unlockall_items", false, game::DVAR_FLAG_SAVED,
"Whether items should be locked based on the player's stats or always unlocked."); "Whether items should be locked based on the player's stats or always unlocked.");
dvars::register_bool("cg_unlockall_classes", false, game::DVAR_FLAG_SAVED, dvars::register_bool("cg_unlockall_classes", false, game::DVAR_FLAG_SAVED,
"Whether classes should be locked based on the player's stats or always unlocked."); "Whether classes should be locked based on the player's stats or always unlocked.");
cg_unlock_all_loot = dvars::register_bool("cg_unlockall_loot", false, game::DVAR_FLAG_SAVED,
is_item_unlocked_hook.create(0x140413E60, is_item_unlocked_stub); "Whether loot should be locked based on the player's stats or always unlocked.");
is_item_unlocked_hook2.create(0x140413860, is_item_unlocked_stub2);
is_item_unlocked_hook3.create(0x140412B70, is_item_unlocked_stub3);
} }
} }
}; };

View File

@ -42,7 +42,7 @@ namespace steam_proxy
#ifndef DEV_BUILD #ifndef DEV_BUILD
try try
{ {
this->start_mod("\xF0\x9F\x90\xA4" " H1-Mod: "s + (game::environment::is_sp() ? "Singleplayer" : "Multiplayer"), game::environment::is_sp() ? 393080 : 393100); this->start_mod("\xF0\x9F\x8E\xAE" " H1-Mod: "s + (game::environment::is_sp() ? "Singleplayer" : "Multiplayer"), game::environment::is_sp() ? 393080 : 393100);
} }
catch (std::exception& e) catch (std::exception& e)
{ {

View File

@ -51,25 +51,26 @@ namespace system_check
{ {
static std::unordered_map<std::string, std::string> mp_zone_hashes = static std::unordered_map<std::string, std::string> mp_zone_hashes =
{ {
{"patch_common_mp.ff", "3F44B0CFB0B8E0FBD9687C2942204AB7F11E66E6E15C73B8B4A5EB5920115A31"}, {"patch_common_mp.ff", "E45EF5F29D12A5A47F405F89FBBEE479C0A90D02141ABF852D481689514134A1"},
}; };
static std::unordered_map<std::string, std::string> sp_zone_hashes = static std::unordered_map<std::string, std::string> sp_zone_hashes =
{ {
// Steam doesn't necessarily deliver this file :( // Steam doesn't necessarily deliver this file :(
{"patch_common.ff", "BB0617DD94AF2F511571E7184BBEDE76E64D97E5D0DAFDB457F00717F035EBF0"}, {"patch_common.ff", "1D32A9770F90ED022AA76F4859B4AB178E194A703383E61AC2CE83B1E828B18F"},
}; };
return verify_hashes(mp_zone_hashes) && (game::environment::is_dedi() || verify_hashes(sp_zone_hashes)); return verify_hashes(mp_zone_hashes) && (game::environment::is_dedi() || verify_hashes(sp_zone_hashes));
} }
// need to update these values
void verify_binary_version() void verify_binary_version()
{ {
const auto value = *reinterpret_cast<DWORD*>(0x140001337); const auto value = *reinterpret_cast<DWORD*>(0x1337_b);
if (value != 0xFFB8006D && value != 0xFFB80080) if (value != 0x60202B6A && value != 0xBC0E9FE)
{ {
throw std::runtime_error("Unsupported Call of Duty: Modern Warfare Remastered version(1.4)"); throw std::runtime_error("Unsupported Call of Duty: Modern Warfare Remastered version (1.15)");
} }
} }
} }
@ -90,7 +91,7 @@ namespace system_check
if (!is_valid()) if (!is_valid())
{ {
MessageBoxA(nullptr, "Your game files are outdated or unsupported.\n" MessageBoxA(nullptr, "Your game files are outdated or unsupported.\n"
"Please get the latest officially supported Call of Duty: Modern Warfare Remastered 1.4 files, or you will get random crashes and issues.", "Please get the latest officially supported Call of Duty: Modern Warfare Remastered files, or you will get random crashes and issues.",
"Invalid game files!", MB_ICONINFORMATION); "Invalid game files!", MB_ICONINFORMATION);
} }
} }

View File

@ -7,164 +7,481 @@
#include "scheduler.hpp" #include "scheduler.hpp"
#include "command.hpp" #include "command.hpp"
#include "ui_scripting.hpp" #include "localized_strings.hpp"
#include "console.hpp"
#include "game_module.hpp"
#include "fps.hpp"
#include "server_list.hpp"
#include "filesystem.hpp"
#include "mods.hpp"
#include "fastfiles.hpp"
#include "scripting.hpp"
#include "game/ui_scripting/lua/engine.hpp"
#include "game/ui_scripting/execution.hpp" #include "game/ui_scripting/execution.hpp"
#include "game/ui_scripting/lua/error.hpp" #include "game/scripting/execution.hpp"
#include "ui_scripting.hpp"
#include <utils/string.hpp> #include <utils/string.hpp>
#include <utils/hook.hpp> #include <utils/hook.hpp>
#include <utils/io.hpp>
#include <utils/binary_resource.hpp>
namespace ui_scripting namespace ui_scripting
{ {
namespace namespace
{ {
std::unordered_map<game::hks::cclosure*, sol::protected_function> converted_functions; std::unordered_map<game::hks::cclosure*, std::function<arguments(const function_arguments& args)>> converted_functions;
utils::hook::detour hksi_lual_error_hook;
utils::hook::detour hksi_lual_error_hook2;
utils::hook::detour hks_start_hook; utils::hook::detour hks_start_hook;
utils::hook::detour hks_shutdown_hook; utils::hook::detour hks_shutdown_hook;
utils::hook::detour hks_allocator_hook; utils::hook::detour hks_package_require_hook;
utils::hook::detour hks_frame_hook;
utils::hook::detour lui_error_hook;
utils::hook::detour hksi_hks_error_hook;
bool error_hook_enabled = false; utils::hook::detour hks_load_hook;
void hksi_lual_error_stub(game::hks::lua_State* s, const char* fmt, ...) const auto lui_common = utils::nt::load_resource(LUI_COMMON);
const auto lua_json = utils::nt::load_resource(LUA_JSON);
struct script
{ {
char va_buffer[2048] = {0}; std::string name;
std::string root;
};
va_list ap; struct globals_t
va_start(ap, fmt); {
vsprintf_s(va_buffer, fmt, ap); std::string in_require_script;
va_end(ap); std::vector<script> loaded_scripts;
bool load_raw_script{};
std::string raw_script_name{};
};
const auto formatted = std::string(va_buffer); globals_t globals{};
if (!error_hook_enabled) bool is_loaded_script(const std::string& name)
{
for (auto i = globals.loaded_scripts.begin(); i != globals.loaded_scripts.end(); ++i)
{ {
return hksi_lual_error_hook.invoke<void>(s, formatted.data()); if (i->name == name)
{
return true;
}
} }
throw std::runtime_error(formatted); return false;
} }
int hksi_hks_error_stub(game::hks::lua_State* s, int a2) std::string get_root_script(const std::string& name)
{ {
if (!error_hook_enabled) for (auto i = globals.loaded_scripts.begin(); i != globals.loaded_scripts.end(); ++i)
{ {
return hksi_hks_error_hook.invoke<int>(s, a2); if (i->name == name)
{
return i->root;
}
} }
throw std::runtime_error("unknown error"); return {};
} }
void lui_error_stub(game::hks::lua_State* s) table get_globals()
{ {
if (!error_hook_enabled) const auto state = *game::hks::lua_state;
return state->globals.v.table;
}
void print_error(const std::string& error)
{
console::error("************** LUI script execution error **************\n");
console::error("%s\n", error.data());
console::error("********************************************************\n");
}
void print_loading_script(const std::string& name)
{
console::info("Loading LUI script '%s'\n", name.data());
}
std::string get_current_script()
{
const auto state = *game::hks::lua_state;
game::hks::lua_Debug info{};
game::hks::hksi_lua_getstack(state, 1, &info);
game::hks::hksi_lua_getinfo(state, "nSl", &info);
return info.short_src;
}
int load_buffer(const std::string& name, const std::string& data)
{
const auto state = *game::hks::lua_state;
const auto sharing_mode = state->m_global->m_bytecodeSharingMode;
state->m_global->m_bytecodeSharingMode = game::hks::HKS_BYTECODE_SHARING_ON;
const auto _0 = gsl::finally([&]()
{ {
lui_error_hook.invoke<void>(s); state->m_global->m_bytecodeSharingMode = sharing_mode;
});
game::hks::HksCompilerSettings compiler_settings{};
return game::hks::hksi_hksL_loadbuffer(state, &compiler_settings, data.data(), data.size(), name.data());
}
void load_script(const std::string& name, const std::string& data)
{
globals.loaded_scripts.push_back({name, name});
const auto lua = get_globals();
const auto load_results = lua["loadstring"](data, name);
if (load_results[0].is<function>())
{
const auto results = lua["pcall"](load_results);
if (!results[0].as<bool>())
{
print_error(results[1].as<std::string>());
}
}
else if (load_results[1].is<std::string>())
{
print_error(load_results[1].as<std::string>());
}
}
void load_scripts(const std::string& script_dir)
{
if (!utils::io::directory_exists(script_dir))
{
return;
} }
const auto count = static_cast<int>(s->m_apistack.top - s->m_apistack.base); const auto scripts = utils::io::list_files(script_dir);
const auto arguments = get_return_values(count);
std::string error_str = "LUI Error"; for (const auto& script : scripts)
if (count && arguments[0].is<std::string>())
{ {
error_str = arguments[0].as<std::string>(); std::string data{};
if (std::filesystem::is_directory(script) && utils::io::read_file(script + "/__init__.lua", &data))
{
print_loading_script(script);
load_script(script + "/__init__.lua", data);
}
}
}
void setup_functions()
{
const auto lua = get_globals();
lua["io"]["fileexists"] = utils::io::file_exists;
lua["io"]["writefile"] = utils::io::write_file;
lua["io"]["movefile"] = utils::io::move_file;
lua["io"]["filesize"] = utils::io::file_size;
lua["io"]["createdirectory"] = utils::io::create_directory;
lua["io"]["directoryexists"] = utils::io::directory_exists;
lua["io"]["directoryisempty"] = utils::io::directory_is_empty;
lua["io"]["listfiles"] = utils::io::list_files;
lua["io"]["removefile"] = utils::io::remove_file;
lua["io"]["readfile"] = static_cast<std::string(*)(const std::string&)>(utils::io::read_file);
using game = table;
auto game_type = game();
lua["game"] = game_type;
game_type["getfps"] = [](const game&)
{
return fps::get_fps();
};
game_type["getping"] = [](const game&)
{
if ((*::game::mp::client_state) == nullptr)
{
return 0;
}
return (*::game::mp::client_state)->ping;
};
game_type["issingleplayer"] = [](const game&)
{
return ::game::environment::is_sp();
};
game_type["ismultiplayer"] = [](const game&)
{
return ::game::environment::is_mp();
};
game_type["addlocalizedstring"] = [](const game&, const std::string& string,
const std::string& value)
{
localized_strings::override(string, value);
};
game_type["sharedset"] = [](const game&, const std::string& key, const std::string& value)
{
scripting::shared_table.access([key, value](scripting::shared_table_t& table)
{
table[key] = value;
});
};
game_type["sharedget"] = [](const game&, const std::string& key)
{
std::string result;
scripting::shared_table.access([key, &result](scripting::shared_table_t& table)
{
result = table[key];
});
return result;
};
game_type["sharedclear"] = [](const game&)
{
scripting::shared_table.access([](scripting::shared_table_t& table)
{
table.clear();
});
};
game_type["assetlist"] = [](const game&, const std::string& type_string)
{
auto table_ = table();
auto index = 1;
auto type_index = -1;
for (auto i = 0; i < ::game::XAssetType::ASSET_TYPE_COUNT; i++)
{
if (type_string == ::game::g_assetNames[i])
{
type_index = i;
}
}
if (type_index == -1)
{
throw std::runtime_error("Asset type does not exist");
}
const auto type = static_cast<::game::XAssetType>(type_index);
fastfiles::enum_assets(type, [type, &table_, &index](const ::game::XAssetHeader header)
{
const auto asset = ::game::XAsset{type, header};
const std::string asset_name = ::game::DB_GetXAssetName(&asset);
table_[index++] = asset_name;
}, true);
return table_;
};
game_type["getweapondisplayname"] = [](const game&, const std::string& name)
{
const auto alternate = name.starts_with("alt_");
const auto weapon = ::game::G_GetWeaponForName(name.data());
char buffer[0x400] = {0};
::game::CG_GetWeaponDisplayName(weapon, alternate, buffer, 0x400);
return std::string(buffer);
};
game_type["getloadedmod"] = [](const game&)
{
return mods::mod_path;
};
if (::game::environment::is_sp())
{
using player = table;
auto player_type = player();
lua["player"] = player_type;
player_type["notify"] = [](const player&, const std::string& name, const variadic_args& va)
{
if (!::game::CL_IsCgameInitialized() || !::game::SV_Loaded())
{
throw std::runtime_error("Not in game");
}
const auto to_string = get_globals()["tostring"];
const auto arguments = get_return_values();
std::vector<std::string> args{};
for (const auto& value : va)
{
const auto value_str = to_string(value);
args.push_back(value_str[0].as<std::string>());
}
::scheduler::once([name, args]()
{
try
{
std::vector<scripting::script_value> arguments{};
for (const auto& arg : args)
{
arguments.push_back(arg);
}
const auto player = scripting::call("getentbynum", {0}).as<scripting::entity>();
scripting::notify(player, name, arguments);
}
catch (...)
{
}
}, ::scheduler::pipeline::server);
};
}
}
void start()
{
globals = {};
const auto lua = get_globals();
lua["EnableGlobals"]();
setup_functions();
lua["print"] = function(reinterpret_cast<game::hks::lua_function>(SELECT_VALUE(0x93490_b, 0x209EB0_b)));
lua["table"]["unpack"] = lua["unpack"];
lua["luiglobals"] = lua;
load_script("lui_common", lui_common);
load_script("lua_json", lua_json);
for (const auto& path : filesystem::get_search_paths())
{
load_scripts(path + "/ui_scripts/");
if (game::environment::is_sp())
{
load_scripts(path + "/ui_scripts/sp/");
}
else
{
load_scripts(path + "/ui_scripts/mp/");
}
} }
throw std::runtime_error(error_str); }
void try_start()
{
try
{
start();
}
catch (const std::exception& e)
{
console::error("Failed to load LUI scripts: %s\n", e.what());
}
} }
void* hks_start_stub(char a1) void* hks_start_stub(char a1)
{ {
const auto _1 = gsl::finally([]() const auto _0 = gsl::finally(&try_start);
{
ui_scripting::lua::engine::start();
});
return hks_start_hook.invoke<void*>(a1); return hks_start_hook.invoke<void*>(a1);
} }
void hks_shutdown_stub() void hks_shutdown_stub()
{ {
converted_functions.clear(); converted_functions.clear();
ui_scripting::lua::engine::stop(); globals = {};
hks_shutdown_hook.invoke<void*>(); return hks_shutdown_hook.invoke<void>();
} }
void* hks_allocator_stub(void* userData, void* oldMemory, unsigned __int64 oldSize, unsigned __int64 newSize) void* hks_package_require_stub(game::hks::lua_State* state)
{ {
const auto closure = reinterpret_cast<game::hks::cclosure*>(oldMemory); const auto script = get_current_script();
if (converted_functions.find(closure) != converted_functions.end()) const auto root = get_root_script(script);
globals.in_require_script = root;
return hks_package_require_hook.invoke<void*>(state);
}
game::XAssetHeader db_find_xasset_header_stub(game::XAssetType type, const char* name, int allow_create_default)
{
if (!is_loaded_script(globals.in_require_script))
{ {
converted_functions.erase(closure); return game::DB_FindXAssetHeader(type, name, allow_create_default);
} }
return hks_allocator_hook.invoke<void*>(userData, oldMemory, oldSize, newSize); const auto folder = globals.in_require_script.substr(0, globals.in_require_script.find_last_of("/\\"));
} const std::string name_ = name;
} const std::string target_script = folder + "/" + name_ + ".lua";
int main_function_handler(game::hks::lua_State* state) if (utils::io::file_exists(target_script))
{ {
const auto value = state->m_apistack.base[-1]; globals.load_raw_script = true;
if (value.t != game::hks::TCFUNCTION) globals.raw_script_name = target_script;
return static_cast<game::XAssetHeader>(reinterpret_cast<game::LuaFile*>(1));
}
else if (name_.starts_with("ui/LUI/"))
{
return game::DB_FindXAssetHeader(type, name, allow_create_default);
}
return static_cast<game::XAssetHeader>(nullptr);
}
int hks_load_stub(game::hks::lua_State* state, void* compiler_options,
void* reader, void* reader_data, const char* chunk_name)
{ {
if (globals.load_raw_script)
{
globals.load_raw_script = false;
globals.loaded_scripts.push_back({globals.raw_script_name, globals.in_require_script});
return load_buffer(globals.raw_script_name, utils::io::read_file(globals.raw_script_name));
}
else
{
return hks_load_hook.invoke<int>(state, compiler_options, reader,
reader_data, chunk_name);
}
}
int main_handler(game::hks::lua_State* state)
{
const auto value = state->m_apistack.base[-1];
if (value.t != game::hks::TCFUNCTION)
{
return 0;
}
const auto closure = value.v.cClosure;
if (converted_functions.find(closure) == converted_functions.end())
{
return 0;
}
const auto& function = converted_functions[closure];
try
{
const auto args = get_return_values();
const auto results = function(args);
for (const auto& result : results)
{
push_value(result);
}
return static_cast<int>(results.size());
}
catch (const std::exception& e)
{
game::hks::hksi_luaL_error(state, e.what());
}
return 0; return 0;
} }
const auto closure = reinterpret_cast<game::hks::cclosure*>(value.v.cClosure);
if (converted_functions.find(closure) == converted_functions.end())
{
return 0;
}
const auto& function = converted_functions[closure];
const auto count = static_cast<int>(state->m_apistack.top - state->m_apistack.base);
const auto arguments = get_return_values(count);
const auto s = function.lua_state();
std::vector<sol::lua_value> converted_args;
for (const auto& argument : arguments)
{
converted_args.push_back(lua::convert(s, argument));
}
const auto results = function(sol::as_args(converted_args));
lua::handle_error(results);
for (const auto& result : results)
{
push_value(lua::convert({s, result}));
}
return results.return_count();
} }
void add_converted_function(game::hks::cclosure* closure, const sol::protected_function& function) template <typename F>
game::hks::cclosure* convert_function(F f)
{ {
converted_functions[closure] = function; const auto state = *game::hks::lua_state;
} const auto closure = game::hks::cclosure_Create(state, main_handler, 0, 0, 0);
converted_functions[closure] = wrap_function(f);
void clear_converted_functions() return closure;
{
converted_functions.clear();
}
void enable_error_hook()
{
error_hook_enabled = true;
}
void disable_error_hook()
{
error_hook_enabled = false;
} }
bool lui_running() bool lui_running()
@ -183,27 +500,22 @@ namespace ui_scripting
return; return;
} }
scheduler::loop(ui_scripting::lua::engine::run_frame, scheduler::pipeline::lui); utils::hook::call(SELECT_VALUE(0xE7419_b, 0x25E809_b), db_find_xasset_header_stub);
utils::hook::call(SELECT_VALUE(0xE72CB_b, 0x25E6BB_b), db_find_xasset_header_stub);
hks_start_hook.create(SELECT_VALUE(0x1400E4B40, 0x140176A40), hks_start_stub); hks_load_hook.create(SELECT_VALUE(0xB46F0_b, 0x22C180_b), hks_load_stub);
hks_shutdown_hook.create(SELECT_VALUE(0x1400DD3D0, 0x14016CA80), hks_shutdown_stub);
hksi_lual_error_hook.create(SELECT_VALUE(0x1400A5EA0, 0x14012F300), hksi_lual_error_stub);
hks_allocator_hook.create(SELECT_VALUE(0x14009B570, 0x14012BAC0), hks_allocator_stub);
lui_error_hook.create(SELECT_VALUE(0x14007D7D0, 0x14010C9E0), lui_error_stub);
hksi_hks_error_hook.create(SELECT_VALUE(0x14009DD80, 0x14012E390), hksi_hks_error_stub);
if (game::environment::is_mp()) hks_package_require_hook.create(SELECT_VALUE(0x90070_b, 0x214040_b), hks_package_require_stub);
{ hks_start_hook.create(SELECT_VALUE(0x103C50_b, 0x27A790_b), hks_start_stub);
hksi_lual_error_hook2.create(0x1401366B0, hksi_lual_error_stub); hks_shutdown_hook.create(SELECT_VALUE(0xFB370_b, 0x2707C0_b), hks_shutdown_stub);
}
command::add("lui_restart", []() command::add("lui_restart", []()
{ {
utils::hook::invoke<void>(SELECT_VALUE(0x1400DD3D0, 0x14016CA80)); utils::hook::invoke<void>(SELECT_VALUE(0xFB370_b, 0x2707C0_b));
utils::hook::invoke<void>(SELECT_VALUE(0x1400E6170, 0x1401780D0)); utils::hook::invoke<void>(SELECT_VALUE(0x1052C0_b, 0x27BEC0_b));
}); });
} }
}; };
} }
REGISTER_COMPONENT(ui_scripting::component) REGISTER_COMPONENT(ui_scripting::component)

View File

@ -1,14 +1,49 @@
#pragma once #pragma once
#include "game/ui_scripting/lua/value_conversion.hpp"
namespace ui_scripting namespace ui_scripting
{ {
int main_function_handler(game::hks::lua_State* state); template <class... Args, std::size_t... I>
void add_converted_function(game::hks::cclosure* closure, const sol::protected_function& function); auto wrap_function(const std::function<void(Args...)>& f, std::index_sequence<I...>)
void clear_converted_functions(); {
return [f](const function_arguments& args)
{
f(args[I]...);
return arguments{{}};
};
}
void enable_error_hook(); template <class... Args, std::size_t... I>
void disable_error_hook(); auto wrap_function(const std::function<arguments(Args...)>& f, std::index_sequence<I...>)
{
return [f](const function_arguments& args)
{
return f(args[I]...);
};
}
template <typename R, class... Args, std::size_t... I>
auto wrap_function(const std::function<R(Args...)>& f, std::index_sequence<I...>)
{
return [f](const function_arguments& args)
{
return arguments{f(args[I]...)};
};
}
template <typename R, class... Args>
auto wrap_function(const std::function<R(Args...)>& f)
{
return wrap_function(f, std::index_sequence_for<Args...>{});
}
template <class F>
auto wrap_function(F f)
{
return wrap_function(std::function(f));
}
template <typename F>
game::hks::cclosure* convert_function(F f);
bool lui_running(); bool lui_running();
} }

View File

@ -468,4 +468,4 @@ namespace updater
}; };
} }
REGISTER_COMPONENT(updater::component) //REGISTER_COMPONENT(updater::component)

View File

@ -52,4 +52,4 @@ namespace videos
}; };
} }
REGISTER_COMPONENT(videos::component) //REGISTER_COMPONENT(videos::component)

View File

@ -10,36 +10,40 @@ namespace virtuallobby
{ {
namespace namespace
{ {
game::dvar_t* virtualLobby_fovscale; game::dvar_t* virtual_lobby_fovscale;
const auto get_fovscale_stub = utils::hook::assemble([](utils::hook::assembler& a) void* get_get_fovscale_stub()
{ {
const auto ret = a.newLabel(); return utils::hook::assemble([](utils::hook::assembler& a)
const auto original = a.newLabel(); {
const auto ret = a.newLabel();
const auto original = a.newLabel();
a.pushad64(); a.pushad64();
a.mov(rax, qword_ptr(0x1425F7210)); // virtualLobbyInFiringRange a.mov(rax, qword_ptr(0x2999CE8_b)); // virtualLobbyInFiringRange
a.cmp(byte_ptr(rax, 0x10), 1); a.cmp(byte_ptr(rax, 0x10), 1);
a.je(original); a.je(original);
a.call_aligned(game::VirtualLobby_Loaded); a.call_aligned(game::VirtualLobby_Loaded);
a.cmp(al, 0); a.cmp(al, 0);
a.je(original); a.je(original);
// virtuallobby // virtuallobby
a.popad64(); a.popad64();
a.mov(rax, ptr(reinterpret_cast<int64_t>(&virtualLobby_fovscale))); a.mov(rax, ptr(reinterpret_cast<int64_t>(&virtual_lobby_fovscale)));
a.jmp(ret); a.jmp(ret);
// original // original
a.bind(original); a.bind(original);
a.popad64(); a.popad64();
a.mov(rax, qword_ptr(0x1413A8580)); a.mov(rax, qword_ptr(0x14C4EC8_b));
a.jmp(ret); a.jmp(ret);
a.bind(ret); a.bind(ret);
a.mov(rcx, 0x142935000); a.mov(rdi, rax);
a.jmp(0x1400B556A); a.mov(ecx, 8);
}); a.jmp(0x104545_b);
});
}
} }
class component final : public component_interface class component final : public component_interface
@ -52,11 +56,10 @@ namespace virtuallobby
return; return;
} }
virtualLobby_fovscale = dvars::register_float("virtualLobby_fovScale", 0.7f, 0.0f, 2.0f, virtual_lobby_fovscale = dvars::register_float_hashed("virtualLobby_fovScale", 0.7f, 0.0f, 2.0f,
game::DVAR_FLAG_SAVED, "Field of view scaled for the virtual lobby"); game::DVAR_FLAG_SAVED, "Field of view scaled for the virtual lobby");
utils::hook::nop(0x1400B555C, 14); utils::hook::jump(0x104539_b, get_get_fovscale_stub(), true);
utils::hook::jump(0x1400B555C, get_fovscale_stub, true);
} }
}; };
} }

View File

@ -170,4 +170,41 @@ namespace demonware
buffer->read_blob(&this->data); buffer->read_blob(&this->data);
} }
}; };
class bdFile2 final : public bdTaskResult
{
public:
uint32_t unk1;
uint32_t unk2;
uint32_t unk3;
bool priv;
uint64_t owner_id;
std::string platform;
std::string filename;
std::string data;
void serialize(byte_buffer* buffer) override
{
buffer->write_uint32(this->unk1);
buffer->write_uint32(this->unk2);
buffer->write_uint32(this->unk3);
buffer->write_bool(this->priv);
buffer->write_uint64(this->owner_id);
buffer->write_string(this->platform);
buffer->write_string(this->filename);
buffer->write_blob(this->data);
}
void deserialize(byte_buffer* buffer) override
{
buffer->read_uint32(&this->unk1);
buffer->read_uint32(&this->unk2);
buffer->read_uint32(&this->unk3);
buffer->read_bool(&this->priv);
buffer->read_uint64(&this->owner_id);
buffer->read_string(&this->platform);
buffer->read_string(&this->filename);
buffer->read_blob(&this->data);
}
};
} }

View File

@ -18,7 +18,7 @@ namespace demonware
this->register_task(12, &bdStorage::unk12); this->register_task(12, &bdStorage::unk12);
this->map_publisher_resource("motd-.*\\.txt", DW_MOTD); this->map_publisher_resource("motd-.*\\.txt", DW_MOTD);
this->map_publisher_resource("ffotd-.*\\.ff", DW_FASTFILE); // this->map_publisher_resource("ffotd-.*\\.ff", DW_FASTFILE);
this->map_publisher_resource("playlists(_.+)?\\.aggr", DW_PLAYLISTS); this->map_publisher_resource("playlists(_.+)?\\.aggr", DW_PLAYLISTS);
} }
@ -172,15 +172,16 @@ namespace demonware
const auto path = get_user_file_path(filename); const auto path = get_user_file_path(filename);
utils::io::write_file(path, data); utils::io::write_file(path, data);
auto* info = new bdFileInfo; auto* info = new bdFile2;
info->file_id = *reinterpret_cast<const uint64_t*>(utils::cryptography::sha1::compute(filename).data()); info->unk1 = 0;
info->unk2 = 0;
info->unk3 = 0;
info->priv = false;
info->owner_id = owner;
info->platform = platform;
info->filename = filename; info->filename = filename;
info->create_time = uint32_t(time(nullptr)); info->data = data;
info->modified_time = info->create_time;
info->file_size = uint32_t(data.size());
info->owner_id = uint64_t(owner);
info->priv = priv;
#ifdef DEBUG #ifdef DEBUG
printf("[DW]: [bdStorage]: set user file: %s\n", filename.data()); printf("[DW]: [bdStorage]: set user file: %s\n", filename.data());
@ -221,6 +222,9 @@ namespace demonware
const auto path = get_user_file_path(filename); const auto path = get_user_file_path(filename);
if (!utils::io::read_file(path, &data)) if (!utils::io::read_file(path, &data))
{ {
#ifdef DEBUG
printf("[DW]: [bdStorage]: get user file: missing file: %s, %s, %s\n", game.data(), filename.data(), platform.data());
#endif
continue; continue;
} }

View File

@ -5,7 +5,8 @@ namespace demonware
{ {
bdUNK80::bdUNK80() : service(80, "bdUNK80") bdUNK80::bdUNK80() : service(80, "bdUNK80")
{ {
this->register_task(42, &bdUNK80::unk42); this->register_task(42, &bdUNK80::unk42); // COD POINTS purchase ?
// this->register_task(43, &bdUNK80::unk43); COD POINTS purchase ?
this->register_task(49, &bdUNK80::unk49); this->register_task(49, &bdUNK80::unk49);
this->register_task(60, &bdUNK80::unk60); this->register_task(60, &bdUNK80::unk60);
this->register_task(130, &bdUNK80::unk130); this->register_task(130, &bdUNK80::unk130);

View File

@ -72,9 +72,11 @@ namespace dvars
switch (type) switch (type)
{ {
case game::dvar_type::boolean: case game::dvar_type::boolean:
case game::dvar_type::boolean_hashed:
return "Domain is 0 or 1"s; return "Domain is 0 or 1"s;
case game::dvar_type::value: case game::dvar_type::value:
case game::dvar_type::value_hashed:
if (domain.value.min == -FLT_MAX) if (domain.value.min == -FLT_MAX)
{ {
if (domain.value.max == FLT_MAX) if (domain.value.max == FLT_MAX)
@ -106,6 +108,7 @@ namespace dvars
return dvar_get_vector_domain(4, domain); return dvar_get_vector_domain(4, domain);
case game::dvar_type::integer: case game::dvar_type::integer:
case game::dvar_type::integer_hashed:
if (domain.enumeration.stringCount == INT_MIN) if (domain.enumeration.stringCount == INT_MIN)
{ {
if (domain.integer.max == INT_MAX) if (domain.integer.max == INT_MAX)
@ -11836,7 +11839,7 @@ namespace dvars
} }
game::dvar_t* register_int(const std::string& name, int value, int min, int max, game::dvar_t* register_int(const std::string& name, int value, int min, int max,
game::DvarFlags flags, const std::string& description) unsigned int flags, const std::string& description)
{ {
const auto hash = game::generateHashValue(name.data()); const auto hash = game::generateHashValue(name.data());
@ -11849,7 +11852,7 @@ namespace dvars
} }
game::dvar_t* register_bool(const std::string& name, bool value, game::dvar_t* register_bool(const std::string& name, bool value,
game::DvarFlags flags, const std::string& description) unsigned int flags, const std::string& description)
{ {
const auto hash = game::generateHashValue(name.data()); const auto hash = game::generateHashValue(name.data());
@ -11862,7 +11865,7 @@ namespace dvars
} }
game::dvar_t* register_string(const std::string& name, const char* value, game::dvar_t* register_string(const std::string& name, const char* value,
game::DvarFlags flags, const std::string& description) unsigned int flags, const std::string& description)
{ {
const auto hash = game::generateHashValue(name.data()); const auto hash = game::generateHashValue(name.data());
@ -11875,7 +11878,7 @@ namespace dvars
} }
game::dvar_t* register_float(const std::string& name, float value, float min, game::dvar_t* register_float(const std::string& name, float value, float min,
float max, game::DvarFlags flags, const std::string& description) float max, unsigned int flags, const std::string& description)
{ {
const auto hash = game::generateHashValue(name.data()); const auto hash = game::generateHashValue(name.data());
@ -11887,8 +11890,21 @@ namespace dvars
return game::Dvar_RegisterFloat(hash, "", value, min, max, flags); return game::Dvar_RegisterFloat(hash, "", value, min, max, flags);
} }
game::dvar_t* register_float_hashed(const std::string& name, float value, float min,
float max, unsigned int flags, const std::string& description)
{
const auto hash = game::generateHashValue(name.data());
if (can_add_dvar_to_list(name))
{
dvar_list.push_back({ name, description });
}
return game::Dvar_RegisterFloatHashed(hash, "", value, min, max, flags);
}
game::dvar_t* register_vec4(const std::string& name, float x, float y, float z, game::dvar_t* register_vec4(const std::string& name, float x, float y, float z,
float w, float min, float max, game::DvarFlags flags, const std::string& description) float w, float min, float max, unsigned int flags, const std::string& description)
{ {
const auto hash = game::generateHashValue(name.data()); const auto hash = game::generateHashValue(name.data());

View File

@ -50,13 +50,15 @@ namespace dvars
std::optional<dvar_info> get_dvar_info_from_hash(const int hash); std::optional<dvar_info> get_dvar_info_from_hash(const int hash);
game::dvar_t* register_int(const std::string& name, int value, int min, int max, game::dvar_t* register_int(const std::string& name, int value, int min, int max,
game::DvarFlags flags, const std::string& description); unsigned int flags, const std::string& description);
game::dvar_t* register_bool(const std::string& name, bool value, game::dvar_t* register_bool(const std::string& name, bool value,
game::DvarFlags flags, const std::string& description); unsigned int flags, const std::string& description);
game::dvar_t* register_string(const std::string& name, const char* value, game::dvar_t* register_string(const std::string& name, const char* value,
game::DvarFlags flags, const std::string& description); unsigned int flags, const std::string& description);
game::dvar_t* register_float(const std::string& name, float value, float min, float max, game::dvar_t* register_float(const std::string& name, float value, float min, float max,
game::DvarFlags flags, const std::string& description); unsigned int flags, const std::string& description);
game::dvar_t* register_float_hashed(const std::string& name, float value, float min, float max,
unsigned int flags, const std::string& description);
game::dvar_t* register_vec4(const std::string& name, float x, float y, float z, float w, float min, game::dvar_t* register_vec4(const std::string& name, float x, float y, float z, float w, float min,
float max, game::DvarFlags flags, const std::string& description); float max, unsigned int flags, const std::string& description);
} }

View File

@ -3,6 +3,14 @@
namespace game namespace game
{ {
uint64_t base_address;
void load_base_address()
{
const auto module = GetModuleHandle(NULL);
base_address = uint64_t(module);
}
int Cmd_Argc() int Cmd_Argc()
{ {
return cmd_args->argc[cmd_args->nesting]; return cmd_args->argc[cmd_args->nesting];
@ -28,6 +36,62 @@ namespace game
return !game::environment::is_sp() && *mp::virtualLobby_loaded == 1; return !game::environment::is_sp() && *mp::virtualLobby_loaded == 1;
} }
void SV_GameSendServerCommand(int client_num, svscmd_type type, const char* text)
{
if (*mp::svs_clients == nullptr)
{
return;
}
if (client_num == -1)
{
SV_SendServerCommand(0, type, "%s", text);
}
else
{
SV_SendServerCommand(mp::svs_clients[client_num], type, "%s", text);
}
}
void Cbuf_AddText(int local_client_num, int controller_index, const char* cmd)
{
if (game::environment::is_sp())
{
sp::Cbuf_AddText(local_client_num, cmd);
}
else
{
mp::Cbuf_AddText(local_client_num, controller_index, cmd);
}
}
void Cmd_TokenizeString(const char* text)
{
if (game::environment::is_sp())
{
sp::Cmd_TokenizeString(text);
}
else
{
const auto a2 = 512 - *reinterpret_cast<int*>(0x3516F40_b);
mp::Cmd_TokenizeStringWithLimit(text, a2);
}
}
void Cmd_EndTokenizeString()
{
if (game::environment::is_sp())
{
return sp::Cmd_EndTokenizeString();
}
const auto nesting = cmd_args->nesting;
const auto argc = cmd_args->argc[nesting];
--cmd_args->nesting;
cmd_argsPrivate->totalUsedArgvPool -= argc;
cmd_argsPrivate->totalUsedTextPool -= cmd_argsPrivate->usedTextPool[nesting];
}
namespace environment namespace environment
{ {
launcher::mode mode = launcher::mode::none; launcher::mode mode = launcher::mode::none;
@ -102,3 +166,8 @@ namespace game
} }
} }
} }
uintptr_t operator"" _b(const uintptr_t ptr)
{
return game::base_address + ptr;
}

View File

@ -5,10 +5,13 @@
#define SELECT_VALUE(sp, mp) (game::environment::is_sp() ? (sp) : (mp)) #define SELECT_VALUE(sp, mp) (game::environment::is_sp() ? (sp) : (mp))
#define SERVER_CD_KEY "S1X-CD-Key" #define SERVER_CD_KEY "H1MOD-CD-Key"
namespace game namespace game
{ {
extern uint64_t base_address;
void load_base_address();
namespace environment namespace environment
{ {
launcher::mode get_mode(); launcher::mode get_mode();
@ -37,10 +40,10 @@ namespace game
{ {
if (environment::is_sp()) if (environment::is_sp())
{ {
return sp_object_; return reinterpret_cast<T*>((uint64_t)sp_object_ + base_address);
} }
return mp_object_; return reinterpret_cast<T*>((uint64_t)mp_object_ + base_address);
} }
operator T* () const operator T* () const
@ -65,6 +68,15 @@ namespace game
const char* SV_Cmd_Argv(int index); const char* SV_Cmd_Argv(int index);
bool VirtualLobby_Loaded(); bool VirtualLobby_Loaded();
void SV_GameSendServerCommand(int clientNum, svscmd_type type, const char* text);
void Cbuf_AddText(int local_client_num, int controller_index, const char* cmd);
void Cmd_TokenizeString(const char* text);
void Cmd_EndTokenizeString();
} }
uintptr_t operator"" _b(const uintptr_t ptr);
#include "symbols.hpp" #include "symbols.hpp"

File diff suppressed because it is too large Load Diff

View File

@ -59,10 +59,10 @@ namespace scripting
script_function get_function_by_index(const unsigned index) script_function get_function_by_index(const unsigned index)
{ {
static const auto function_table = SELECT_VALUE(0x14B1D1B90, 0x149813EF0); static const auto function_table = SELECT_VALUE(0xB8CC510_b, 0xAC83820_b);
static const auto method_table = SELECT_VALUE(0x14B1D33A0, 0x149815700); static const auto method_table = SELECT_VALUE(0xB8CDD60_b, 0xAC85070_b);
if (index < 0x301) if (index < 0x30A)
{ {
return reinterpret_cast<script_function*>(function_table)[index - 1]; return reinterpret_cast<script_function*>(function_table)[index - 1];
} }

View File

@ -850,6 +850,15 @@ namespace game
const char** argv[8]; const char** argv[8];
}; };
struct CmdArgsPrivate
{
char textPool[8192];
const char* argvPool[512];
int usedTextPool[8];
int totalUsedArgvPool;
int totalUsedTextPool;
};
struct cmd_function_s struct cmd_function_s
{ {
cmd_function_s* next; cmd_function_s* next;
@ -881,11 +890,14 @@ namespace game
enum dvar_type : std::int8_t enum dvar_type : std::int8_t
{ {
boolean = 0, boolean = 0,
boolean_hashed = 10,
value = 1, value = 1,
value_hashed = 11,
vec2 = 2, vec2 = 2,
vec3 = 3, vec3 = 3,
vec4 = 4, vec4 = 4,
integer = 5, integer = 5,
integer_hashed = 12,
enumeration = 6, enumeration = 6,
string = 7, string = 7,
color = 8, color = 8,
@ -1445,6 +1457,25 @@ namespace game
{ {
}; };
// made up
struct client_state_t
{
char __pad0[0x4A50];
int ping;
char __pad1[0x8];
int num_players;
};
// made up
struct connect_state_t
{
char __pad0[0xC];
netadr_s address;
};
static_assert(offsetof(client_state_t, ping) == 0x4A50);
static_assert(offsetof(client_state_t, num_players) == 0x4A5C);
enum PlayerHandIndex enum PlayerHandIndex
{ {
WEAPON_HAND_DEFAULT = 0x0, WEAPON_HAND_DEFAULT = 0x0,
@ -1477,12 +1508,17 @@ namespace game
struct gclient_s struct gclient_s
{ {
char __pad0[18720]; char __pad0[2];
char name[32]; // 18720 char pm_type; // 2
char __pad1[752]; char __pad1[18831];
int flags; // 19504 char name[32]; // 18834
char __pad2[622];
int flags; // 19488
}; // size = ? }; // size = ?
static_assert(offsetof(gclient_s, name) == 18834);
static_assert(offsetof(gclient_s, flags) == 19488);
struct EntityState struct EntityState
{ {
uint16_t entityNum; uint16_t entityNum;
@ -1519,19 +1555,13 @@ namespace game
char __pad0[265164]; char __pad0[265164];
int reliableSequence; int reliableSequence;
int reliableAcknowledge; int reliableAcknowledge;
char __pad1[265832]; char __pad1[397928];
gentity_s* gentity; // 268976 gentity_s* gentity;
char name[32]; // 268984 char name[32];
char __pad2[8]; char __pad5[348752];
int nextSnapshotTime; // 269024 }; // size = 1011960
char __pad3[544];
LiveClientDropType liveDropRequest; //269572
char __pad4[24];
TestClientType testClient; // 269600
char __pad5[347912];
}; // size = 879616
static_assert(sizeof(client_t) == 879616); static_assert(sizeof(client_t) == 1011960);
} }
namespace sp namespace sp
@ -1563,6 +1593,10 @@ namespace game
namespace hks namespace hks
{ {
struct lua_State;
struct HashTable;
struct cclosure;
struct GenericChunkHeader struct GenericChunkHeader
{ {
unsigned __int64 m_flags; unsigned __int64 m_flags;
@ -1588,9 +1622,6 @@ namespace game
char m_data[30]; char m_data[30];
}; };
struct HashTable;
struct cclosure;
union HksValue union HksValue
{ {
cclosure* cClosure; cclosure* cClosure;
@ -1602,6 +1633,8 @@ namespace game
void* thread; void* thread;
void* ptr; void* ptr;
float number; float number;
long long i64;
unsigned long long ui64;
unsigned int native; unsigned int native;
bool boolean; bool boolean;
}; };
@ -1687,6 +1720,14 @@ namespace game
enum HksError enum HksError
{ {
HKS_NO_ERROR = 0x0,
HKS_ERRSYNTAX = 0xFFFFFFFC,
HKS_ERRFILE = 0xFFFFFFFB,
HKS_ERRRUN = 0xFFFFFF9C,
HKS_ERRMEM = 0xFFFFFF38,
HKS_ERRERR = 0xFFFFFED4,
HKS_THROWING_ERROR = 0xFFFFFE0C,
HKS_GC_YIELD = 0x1,
}; };
struct lua_Debug struct lua_Debug
@ -1707,24 +1748,6 @@ namespace game
int is_tail_call; int is_tail_call;
}; };
struct lua_State : ChunkHeader
{
void* m_global;
CallStack m_callStack;
ApiStack m_apistack;
UpValue* pending;
HksObject globals;
HksObject m_cEnv;
CallSite* m_callsites;
int m_numberOfCCalls;
void* m_context;
InternString* m_name;
lua_State* m_nextState;
lua_State* m_nextStateStack;
Status m_status;
HksError m_error;
};
using lua_function = int(__fastcall*)(lua_State*); using lua_function = int(__fastcall*)(lua_State*);
struct luaL_Reg struct luaL_Reg
@ -1763,5 +1786,231 @@ namespace game
InternString* m_name; InternString* m_name;
HksObject m_upvalues[1]; HksObject m_upvalues[1];
}; };
enum HksCompilerSettings_BytecodeSharingFormat
{
BYTECODE_DEFAULT = 0x0,
BYTECODE_INPLACE = 0x1,
BYTECODE_REFERENCED = 0x2,
};
enum HksCompilerSettings_IntLiteralOptions
{
INT_LITERALS_NONE = 0x0,
INT_LITERALS_LUD = 0x1,
INT_LITERALS_32BIT = 0x1,
INT_LITERALS_UI64 = 0x2,
INT_LITERALS_64BIT = 0x2,
INT_LITERALS_ALL = 0x3,
};
struct HksCompilerSettings
{
int m_emitStructCode;
const char** m_stripNames;
int m_emitGlobalMemoization;
int _m_isHksGlobalMemoTestingMode;
HksCompilerSettings_BytecodeSharingFormat m_bytecodeSharingFormat;
HksCompilerSettings_IntLiteralOptions m_enableIntLiterals;
int(__fastcall* m_debugMap)(const char*, int);
};
enum HksBytecodeSharingMode
{
HKS_BYTECODE_SHARING_OFF = 0x0,
HKS_BYTECODE_SHARING_ON = 0x1,
HKS_BYTECODE_SHARING_SECURE = 0x2,
};
struct HksGcWeights
{
int m_removeString;
int m_finalizeUserdataNoMM;
int m_finalizeUserdataGcMM;
int m_cleanCoroutine;
int m_removeWeak;
int m_markObject;
int m_traverseString;
int m_traverseUserdata;
int m_traverseCoroutine;
int m_traverseWeakTable;
int m_freeChunk;
int m_sweepTraverse;
};
struct GarbageCollector_Stack
{
void* m_storage;
unsigned int m_numEntries;
unsigned int m_numAllocated;
};
struct ProtoList
{
void** m_protoList;
unsigned __int16 m_protoSize;
unsigned __int16 m_protoAllocSize;
};
struct GarbageCollector
{
int m_target;
int m_stepsLeft;
int m_stepLimit;
HksGcWeights m_costs;
int m_unit;
_SETJMP_FLOAT128(*m_jumpPoint)[16];
lua_State* m_mainState;
lua_State* m_finalizerState;
void* m_memory;
int m_phase;
GarbageCollector_Stack m_resumeStack;
GarbageCollector_Stack m_greyStack;
GarbageCollector_Stack m_remarkStack;
GarbageCollector_Stack m_weakStack;
int m_finalizing;
HksObject m_safeTableValue;
lua_State* m_startOfStateStackList;
lua_State* m_endOfStateStackList;
lua_State* m_currentState;
HksObject m_safeValue;
void* m_compiler;
void* m_bytecodeReader;
void* m_bytecodeWriter;
int m_pauseMultiplier;
int m_stepMultiplier;
bool m_stopped;
int(__fastcall* m_gcPolicy)(lua_State*);
unsigned __int64 m_pauseTriggerMemoryUsage;
int m_stepTriggerCountdown;
unsigned int m_stringTableIndex;
unsigned int m_stringTableSize;
UserData* m_lastBlackUD;
UserData* m_activeUD;
};
enum MemoryManager_ChunkColor
{
RED = 0x0,
BLACK = 0x1,
};
struct ChunkList
{
ChunkHeader m_prevToStart;
};
enum Hks_DeleteCheckingMode
{
HKS_DELETE_CHECKING_OFF = 0x0,
HKS_DELETE_CHECKING_ACCURATE = 0x1,
HKS_DELETE_CHECKING_SAFE = 0x2,
};
struct MemoryManager
{
void* (__fastcall* m_allocator)(void*, void*, unsigned __int64, unsigned __int64);
void* m_allocatorUd;
MemoryManager_ChunkColor m_chunkColor;
unsigned __int64 m_used;
unsigned __int64 m_highwatermark;
ChunkList m_allocationList;
ChunkList m_sweepList;
ChunkHeader* m_lastKeptChunk;
lua_State* m_state;
ChunkList m_deletedList;
int m_deleteMode;
Hks_DeleteCheckingMode m_deleteCheckingMode;
};
struct StaticStringCache
{
HksObject m_objects[41];
};
enum HksBytecodeEndianness
{
HKS_BYTECODE_DEFAULT_ENDIAN = 0x0,
HKS_BYTECODE_BIG_ENDIAN = 0x1,
HKS_BYTECODE_LITTLE_ENDIAN = 0x2,
};
struct RuntimeProfileData_Stats
{
unsigned __int64 hksTime;
unsigned __int64 callbackTime;
unsigned __int64 gcTime;
unsigned __int64 cFinalizerTime;
unsigned __int64 compilerTime;
unsigned int hkssTimeSamples;
unsigned int callbackTimeSamples;
unsigned int gcTimeSamples;
unsigned int compilerTimeSamples;
unsigned int num_newuserdata;
unsigned int num_tablerehash;
unsigned int num_pushstring;
unsigned int num_pushcfunction;
unsigned int num_newtables;
};
struct RuntimeProfileData
{
__int64 stackDepth;
__int64 callbackDepth;
unsigned __int64 lastTimer;
RuntimeProfileData_Stats frameStats;
unsigned __int64 gcStartTime;
unsigned __int64 finalizerStartTime;
unsigned __int64 compilerStartTime;
unsigned __int64 compilerStartGCTime;
unsigned __int64 compilerStartGCFinalizerTime;
unsigned __int64 compilerCallbackStartTime;
__int64 compilerDepth;
void* outFile;
lua_State* rootState;
};
struct HksGlobal
{
MemoryManager m_memory;
GarbageCollector m_collector;
StringTable m_stringTable;
HksBytecodeSharingMode m_bytecodeSharingMode;
unsigned int m_tableVersionInitializer;
HksObject m_registry;
ProtoList m_protoList;
HashTable* m_structProtoByName;
ChunkList m_userDataList;
lua_State* m_root;
StaticStringCache m_staticStringCache;
void* m_debugger;
void* m_profiler;
RuntimeProfileData m_runProfilerData;
HksCompilerSettings m_compilerSettings;
int(__fastcall* m_panicFunction)(lua_State*);
void* m_luaplusObjectList;
int m_heapAssertionFrequency;
int m_heapAssertionCount;
void (*m_logFunction)(lua_State*, const char*, ...);
HksBytecodeEndianness m_bytecodeDumpEndianness;
};
struct lua_State : ChunkHeader
{
HksGlobal* m_global;
CallStack m_callStack;
ApiStack m_apistack;
UpValue* pending;
HksObject globals;
HksObject m_cEnv;
CallSite* m_callsites;
int m_numberOfCCalls;
void* m_context;
InternString* m_name;
lua_State* m_nextState;
lua_State* m_nextStateStack;
Status m_status;
HksError m_error;
};
} }
} }

View File

@ -8,277 +8,300 @@ namespace game
* Functions * Functions
**************************************************************/ **************************************************************/
WEAK symbol<void(int type, VariableUnion u)> AddRefToValue{0x14036E600, 0x14043C580}; WEAK symbol<void(int type, VariableUnion u)> AddRefToValue{0x3C1F50, 0x5090E0};
WEAK symbol<void(int type, VariableUnion u)> RemoveRefToValue{0x1403700F0, 0x14043E090}; WEAK symbol<void(int type, VariableUnion u)> RemoveRefToValue{0x3C3A60, 0x50ABF0};
WEAK symbol<void(unsigned int id)> AddRefToObject{0x14036E5F0, 0x14043C570}; WEAK symbol<void(unsigned int id)> AddRefToObject{0x3C1F40, 0x5090D0};
WEAK symbol<void(unsigned int id)> RemoveRefToObject{0x14036FFE0, 0x14043DF80}; WEAK symbol<void(unsigned int id)> RemoveRefToObject{0x3C3950, 0x50AAE0};
WEAK symbol<unsigned int(unsigned int id)> AllocThread{0x14036E960, 0x14043C8E0}; WEAK symbol<unsigned int(unsigned int id)> AllocThread{0x3C22B0, 0x509440};
WEAK symbol<ObjectVariableValue*(unsigned int* id)> AllocVariable{0x14036E9C0, 0x14043C940}; WEAK symbol<ObjectVariableValue*(unsigned int* id)> AllocVariable{0x3C2310, 0x5094A0};
namespace sp
{
WEAK symbol<void(int localClientNum, const char* text)> Cbuf_AddText{0x3764A0, 0x0};
}
namespace mp
{
WEAK symbol<void(int localClientNum, int controllerIndex, const char* text)> Cbuf_AddText{0x0, 0x1CF480};
}
WEAK symbol<void(int localClientNum, const char* text)> Cbuf_AddText{0x140342EB0, 0x1404033B0};
WEAK symbol<void(int localClientNum, int controllerIndex, const char* buffer, WEAK symbol<void(int localClientNum, int controllerIndex, const char* buffer,
void (int, int, const char*))> Cbuf_ExecuteBufferInternal{0x140342FC0, 0x1404034C0}; void (int, int, const char*))> Cbuf_ExecuteBufferInternal{0x3765B0, 0x155BC0};
WEAK symbol<void(const char* message)> Conbuf_AppendText{0x1403E3300, 0x140513FF0}; WEAK symbol<void(const char* message)> Conbuf_AppendText{0x0, 0x0};
WEAK symbol<char*(int start)> ConcatArgs{0x1402697F0, 0x140335D70}; WEAK symbol<char*(int start)> ConcatArgs{0x296420, 0x413050};
WEAK symbol<void(int localClientNum, int controllerIndex, const char* text)> Cmd_ExecuteSingleCommand{0x140343980, 0x140403F60}; WEAK symbol<void(int localClientNum, int controllerIndex, const char* text)> Cmd_ExecuteSingleCommand{0x376FF0, 0x156E90};
WEAK symbol<void(const char* cmdName, void(), cmd_function_s* allocedCmd)> Cmd_AddCommandInternal{0x1403433E0, 0x140403950}; WEAK symbol<void(const char* cmdName, void(), cmd_function_s* allocedCmd)> Cmd_AddCommandInternal{0x376A40, 0x156880};
WEAK symbol<void(const char*)> Cmd_RemoveCommand{0x140343FF0, 0x1404045D0}; WEAK symbol<void(const char*)> Cmd_RemoveCommand{0x377670, 0x157690};
WEAK symbol<void(const char* text_in)> Cmd_TokenizeString{0x140344110, 0x1404046F0};
WEAK symbol<void()> Cmd_EndTokenizeString{0x140343630, 0x140403C20};
WEAK symbol<void(void*, void*)> AimAssist_AddToTargetList{0, 0x14009D0F0}; namespace sp
{
WEAK symbol<void(const char* text_in)> Cmd_TokenizeString{0x377790, 0x0};
WEAK symbol<void()> Cmd_EndTokenizeString{0x376C90, 0x0};
}
namespace mp
{
WEAK symbol<void(const char* text_in, int limit)> Cmd_TokenizeStringWithLimit{0x0, 0x157A40};
}
WEAK symbol<void(void*, void*)> AimAssist_AddToTargetList{0x0, 0xE66C0};
WEAK symbol<void(unsigned int weapon, bool isAlternate, WEAK symbol<void(unsigned int weapon, bool isAlternate,
char* output, unsigned int maxStringLen)> BG_GetWeaponNameComplete{0x140430550, 0x1401F9670}; char* output, unsigned int maxStringLen)> BG_GetWeaponNameComplete{0x4B19C0, 0x2E2500};
WEAK symbol<void()> Com_Frame_Try_Block_Function{0x1401CE8D0, 0x1400D8310}; WEAK symbol<void()> Com_Frame_Try_Block_Function{0x385280, 0x0};
WEAK symbol<CodPlayMode()> Com_GetCurrentCoDPlayMode{0, 0x1405039A0}; WEAK symbol<CodPlayMode()> Com_GetCurrentCoDPlayMode{0x0, 0x5AEF80};
WEAK symbol<bool()> Com_InFrontend{0x1400E4B30, 0x140176A30}; WEAK symbol<bool()> Com_InFrontend{0xF6430, 0x76A40};
WEAK symbol<void(float, float, int)> Com_SetSlowMotion{0, 0x1400DB790}; WEAK symbol<void(float, float, int)> Com_SetSlowMotion{0x0, 0x17E5F0};
WEAK symbol<void(errorParm code, const char* message, ...)> Com_Error{0x1403509C0, 0x1400D78A0}; WEAK symbol<void(errorParm code, const char* message, ...)> Com_Error{0x384820, 0x159860};
WEAK symbol<void()> Com_Quit_f{0x140352BE0, 0x1400DA830}; WEAK symbol<void()> Com_Quit_f{0x0, 0x1F9280};
WEAK symbol<void(char const* finalMessage)> Com_Shutdown{0x140353B70, 0x1400DB8A0}; WEAK symbol<void(char const* finalMessage)> Com_Shutdown{0x3A6A50, 0x0};
WEAK symbol<void()> Quit{0x140352D90, 0x1400DA830}; WEAK symbol<void()> Quit{0x3A5A20, 0x17CF50};
WEAK symbol<void(int localClientNum, const char* message)> CG_GameMessage{0x1401389A0, 0x140220CC0}; WEAK symbol<void(int localClientNum, const char* message)> CG_GameMessage{0x15B3B0, 0x316210};
WEAK symbol<void(int localClientNum, const char* message)> CG_GameMessageBold{0x140138750, 0x140220620}; WEAK symbol<void(int localClientNum, const char* message)> CG_GameMessageBold{0x15B110, 0x3122F0};
WEAK symbol<void(int localClientNum, /*mp::cg_s**/void* cg, WEAK symbol<void(int localClientNum, /*mp::cg_s**/void* cg,
const char* dvar, const char* value)> CG_SetClientDvarFromServer{0, 0x140236120}; const char* dvar, const char* value)> CG_SetClientDvarFromServer{0x0, 0x0};
WEAK symbol<char*(const unsigned int weapon, WEAK symbol<char*(const unsigned int weapon,
bool isAlternate, char* outputBuffer, int bufferLen)> CG_GetWeaponDisplayName{0x14016EC30, 0x1400B5840}; bool isAlternate, char* outputBuffer, int bufferLen)> CG_GetWeaponDisplayName{0x192B80, 0x104B70};
WEAK symbol<bool()> CL_IsCgameInitialized{0x14017EE30, 0x140245650}; WEAK symbol<bool()> CL_IsCgameInitialized{0x1A3210, 0x33C640};
WEAK symbol<void(int a1)> CL_VirtualLobbyShutdown{0, 0x140256D40}; WEAK symbol<void(int a1)> CL_VirtualLobbyShutdown{0x0, 0x0};
WEAK symbol<void(int hash, const char* name, const char* buffer)> Dvar_SetCommand{0x1403C72B0, 0x1404FD0A0}; WEAK symbol<void(int hash, const char* name, const char* buffer)> Dvar_SetCommand{0x41BAD0, 0x1857D0};
WEAK symbol<dvar_t*(const char* name)> Dvar_FindVar{0x1403C5D50, 0x1404FBB00}; WEAK symbol<dvar_t*(const char* name)> Dvar_FindVar{0x41A600, 0x183EB0};
WEAK symbol<void(const dvar_t* dvar)> Dvar_ClearModified{0x1403C5C20, 0x1404FB930}; WEAK symbol<void(const dvar_t* dvar)> Dvar_ClearModified{0x41A4F0, 0x183D50};
WEAK symbol<void(char* buffer, int index)> Dvar_GetCombinedString{0x140354DF0, 0x14041D830}; WEAK symbol<void(char* buffer, int index)> Dvar_GetCombinedString{0x3A7D60, 0x4EA020};
WEAK symbol<const char*(dvar_t* dvar, dvar_value value)> Dvar_ValueToString{0x1403C8560, 0x1404FE660}; WEAK symbol<const char*(dvar_t* dvar, bool is_hashed, dvar_value value)> Dvar_ValueToString{0x41CE00, 0x187000};
WEAK symbol<void(dvar_t* dvar, DvarSetSource source)> Dvar_Reset{0, 0x1404FCC40}; WEAK symbol<void(dvar_t* dvar, DvarSetSource source)> Dvar_Reset{0x41B5F0, 0x185390};
WEAK symbol<void(const char*, const char*, WEAK symbol<void(const char*, const char*,
DvarSetSource)> Dvar_SetFromStringByNameFromSource{0, 0x1404FD490}; DvarSetSource)> Dvar_SetFromStringByNameFromSource{0x41BD90, 0x185BD0};
WEAK symbol<dvar_t*(int hash, const char* name, bool value, WEAK symbol<dvar_t*(int hash, const char* name, bool value,
unsigned int flags)> Dvar_RegisterBool{0x1403C47E0, 0x1404FA540}; unsigned int flags)> Dvar_RegisterBool{0x419220, 0x182340};
WEAK symbol<dvar_t*(int hash, const char* name, int value, int min, int max, WEAK symbol<dvar_t*(int hash, const char* name, int value, int min, int max,
unsigned int flags)> Dvar_RegisterInt{0x1403C4CC0, 0x1404FAA20}; unsigned int flags)> Dvar_RegisterInt{0x419700, 0x182A10};
WEAK symbol<dvar_t*(int hash, const char* dvarName, float value, float min, WEAK symbol<dvar_t*(int hash, const char* dvarName, float value, float min,
float max, unsigned int flags)> Dvar_RegisterFloat{0x1403C4BB0, 0x1404FA910}; float max, unsigned int flags)> Dvar_RegisterFloat{0x4195F0, 0x1827F0};
WEAK symbol<dvar_t*(int hash, const char* dvarName, float value, float min,
float max, unsigned int flags)> Dvar_RegisterFloatHashed{0x0, 0x182900};
WEAK symbol<dvar_t*(int hash, const char* dvarName, const char* value, WEAK symbol<dvar_t*(int hash, const char* dvarName, const char* value,
unsigned int flags)> Dvar_RegisterString{0x1403C4DA0, 0x1404FAB00}; unsigned int flags)> Dvar_RegisterString{0x4197E0, 0x182BD0};
WEAK symbol<dvar_t*(int dvarName, const char* a2, float x, float y, float z, WEAK symbol<dvar_t*(int dvarName, const char* a2, float x, float y, float z,
float w, float min, float max, unsigned int flags)> Dvar_RegisterVec4{0x1403C5220, 0x1404FAF40}; float w, float min, float max, unsigned int flags)> Dvar_RegisterVec4{0x419C60, 0x183010};
WEAK symbol<long long(const char* qpath, char** buffer)> FS_ReadFile{0x1403B9020, 0x1404EE720}; WEAK symbol<long long(const char* qpath, char** buffer)> FS_ReadFile{0x40E280, 0x1EC690};
WEAK symbol<void(void* buffer)> FS_FreeFile{0x1403B9010, 0x1404EE710}; WEAK symbol<void(void* buffer)> FS_FreeFile{0x40E270, 0x59E2F0};
WEAK symbol<void(const char* gameName)> FS_Startup{0x1403B85D0, 0x1404EDD30}; WEAK symbol<void(const char* gameName)> FS_Startup{0x40D890, 0x0};
WEAK symbol<void(const char* path, const char* dir)> FS_AddLocalizedGameDirectory{0x1403B6030, 0x1404EBE20}; WEAK symbol<void(const char* path, const char* dir)> FS_AddLocalizedGameDirectory{0x40B1E0, 0x1878F0};
WEAK symbol<unsigned int(unsigned int, unsigned int)> GetVariable{0x14036FDD0, 0x14043DD70}; WEAK symbol<unsigned int(unsigned int, unsigned int)> GetVariable{0x3C3740, 0x50A8D0};
WEAK symbol<unsigned int(unsigned int parentId, unsigned int unsignedValue)> GetNewVariable{0x14036FA00, 0x14043D990}; WEAK symbol<unsigned int(unsigned int parentId, unsigned int unsignedValue)> GetNewVariable{0x3C3360, 0x50A4F0};
WEAK symbol<unsigned int(unsigned int parentId, unsigned int unsignedValue)> GetNewArrayVariable{0x14036F880, 0x14043D810}; WEAK symbol<unsigned int(unsigned int parentId, unsigned int unsignedValue)> GetNewArrayVariable{0x3C31E0, 0x50A370};
WEAK symbol<void()> GScr_LoadConsts{0x1402D13E0, 0x140393810}; WEAK symbol<unsigned int(unsigned int parentId, unsigned int name)> FindVariable{0x3C2E00, 0x509F90};
WEAK symbol<unsigned int(unsigned int parentId, unsigned int name)> FindVariable{0x14036F4B0, 0x14043D430}; WEAK symbol<unsigned int(int entnum, unsigned int classnum)> FindEntityId{0x3C2D00, 0x509E90};
WEAK symbol<unsigned int(int entnum, unsigned int classnum)> FindEntityId{0x14036F3B0, 0x14043D330}; WEAK symbol<void(unsigned int parentId, unsigned int index)> RemoveVariableValue{0x3C3B00, 0x50AC90};
WEAK symbol<void(unsigned int parentId, unsigned int index)> RemoveVariableValue{0x140370190, 0x14043E130};
WEAK symbol<void(VariableValue* result, unsigned int classnum, WEAK symbol<void(VariableValue* result, unsigned int classnum,
int entnum, int offset)> GetEntityFieldValue{0x140373780, 0x140441780}; int entnum, int offset)> GetEntityFieldValue{0x3C71A0, 0x50E2E0};
WEAK symbol<int(const char* fname)> generateHashValue{0x1400FE8A0, 0x1401B1010}; WEAK symbol<int(const char* fname)> generateHashValue{0x11FEA0, 0x183F80};
WEAK symbol<void()> G_Glass_Update{0x14026C570, 0x14033A640}; WEAK symbol<void()> G_Glass_Update{0x2992E0, 0x417940};
WEAK symbol<int(int clientNum)> G_GetClientScore{0, 0x140342F90}; WEAK symbol<int(int clientNum)> G_GetClientScore{0x0, 0x420420};
WEAK symbol<unsigned int(const char* name)> G_GetWeaponForName{0x1402C2A90, 0x14038C300}; WEAK symbol<unsigned int(const char* name)> G_GetWeaponForName{0x2F20F0, 0x461180};
WEAK symbol<int(playerState_s* ps, unsigned int weapon, int dualWield, WEAK symbol<int(playerState_s* ps, unsigned int weapon, int dualWield,
int startInAltMode, int, int, int, char, ...)> G_GivePlayerWeapon{0x1402C2DF0, 0x14038C750}; int startInAltMode, int, int, int, char, ...)> G_GivePlayerWeapon{0x2F24F0, 0x461600};
WEAK symbol<void(playerState_s* ps, unsigned int weapon, int hadWeapon)> G_InitializeAmmo{0x140270980, 0x14033EDD0}; WEAK symbol<void(playerState_s* ps, unsigned int weapon, int hadWeapon)> G_InitializeAmmo{0x29D9E0, 0x41C170};
WEAK symbol<void(int clientNum, unsigned int weapon)> G_SelectWeapon{0x1402C3750, 0x14038D1B0}; WEAK symbol<void(int clientNum, unsigned int weapon)> G_SelectWeapon{0x2F2EA0, 0x462560};
WEAK symbol<int(playerState_s* ps, unsigned int weapon)> G_TakePlayerWeapon{0x1402C3900, 0x14038D370}; WEAK symbol<int(playerState_s* ps, unsigned int weapon)> G_TakePlayerWeapon{0x2F3050, 0x462770};
WEAK symbol<char*(char* string)> I_CleanStr{0x1403CD230, 0x140503D00}; WEAK symbol<char*(char* string)> I_CleanStr{0x4293E0, 0x5AF2E0};
WEAK symbol<const char* (int, int, int)> Key_KeynumToString{0x140187CC0, 0x14024FE10}; WEAK symbol<const char*(int, int, int)> Key_KeynumToString{0x1AC410, 0x199990};
WEAK symbol<unsigned int(int)> Live_SyncOnlineDataFlags{0, 0x14059A700}; WEAK symbol<unsigned int(int)> Live_SyncOnlineDataFlags{0x0, 0x1A5C10};
WEAK symbol<Material* (const char* material)> Material_RegisterHandle{0x1404E48B0, 0x1405EAB30}; WEAK symbol<Material*(const char* material)> Material_RegisterHandle{0x56EA20, 0x692360};
WEAK symbol<void(netadr_s*, sockaddr*)> NetadrToSockadr{0x1403C11C0, 0x1404F62F0}; WEAK symbol<void(netadr_s*, sockaddr*)> NetadrToSockadr{0x416580, 0x59E580};
WEAK symbol<void(netsrc_t, netadr_s*, const char*)> NET_OutOfBandPrint{0x140357560, 0x1404255D0}; WEAK symbol<void(netsrc_t, netadr_s*, const char*)> NET_OutOfBandPrint{0x3AA550, 0x4F1EB0};
WEAK symbol<void(netsrc_t sock, int length, const void* data, const netadr_s* to)> NET_SendLoopPacket{0, 0x140425790}; WEAK symbol<void(netsrc_t sock, int length, const void* data, const netadr_s* to)> NET_SendLoopPacket{0x0, 0x4F2070};
WEAK symbol<bool(const char* s, netadr_s* a)> NET_StringToAdr{0, 0x140425870}; WEAK symbol<bool(const char* s, netadr_s* a)> NET_StringToAdr{0x0, 0x4F2150};
WEAK symbol<void(float x, float y, float width, float height, float s0, float t0, float s1, float t1, WEAK symbol<void(float x, float y, float width, float height, float s0, float t0, float s1, float t1,
float* color, Material* material)> R_AddCmdDrawStretchPic{0x14017E5C0, 0x1402443A0}; float* color, Material* material)> R_AddCmdDrawStretchPic{0x1A29A0, 0x33B2B0};
WEAK symbol<Font_s* (const char* font, int size)> R_RegisterFont{0x1404D4100, 0x1405D91E0}; WEAK symbol<Font_s*(const char* font, int size)> R_RegisterFont{0x55C4E0, 0x67F630};
WEAK symbol<int(const char* text, int maxChars, Font_s* font)> R_TextWidth{0x1404D43B0, 0x1405D94A0}; WEAK symbol<int(const char* text, int maxChars, Font_s* font)> R_TextWidth{0x55C7A0, 0x67F8F0};
WEAK symbol<int(void* font)> R_GetFontHeight{0x1405EA360, 0x1405D92C0}; WEAK symbol<int(void* font)> R_GetFontHeight{0x55C5C0, 0x67F710};
WEAK symbol<void* (int a1)> R_DrawSomething{0x1404D37B0, 0x1405D8890}; WEAK symbol<void*(int a1)> R_GetSomething{0x55BB90, 0x67ECE0};
WEAK symbol<void()> R_SyncRenderThread{0x1404F8240, 0x1405FF3A0}; WEAK symbol<void()> R_SyncRenderThread{0x582F30, 0x6A78C0};
WEAK symbol<void* (const char* text, int maxChars, void* font, int fontHeight, float x, WEAK symbol<void*(const char* text, int maxChars, void* font, int fontHeight, float x,
float y, float xScale, float yScale, float rotation, float* color, float y, float xScale, float yScale, float rotation, float* color,
int style, int cursor_pos, char cursor_char, int style, int cursor_pos, char cursor_char,
void* style_unk)> H1_AddBaseDrawTextCmd{0x1404F3DC0, 0x1405FB1F0}; void* style_unk)> H1_AddBaseDrawTextCmd{0x57EA60, 0x6A3080};
#define R_AddCmdDrawText(TXT, MC, F, X, Y, XS, YS, R, C, S) \ #define R_AddCmdDrawText(TXT, MC, F, X, Y, XS, YS, R, C, S) \
H1_AddBaseDrawTextCmd(TXT, MC, F, game::R_GetFontHeight(F), X, Y, XS, YS, R, C, S,-1, 0, game::R_DrawSomething(S)) H1_AddBaseDrawTextCmd(TXT, MC, F, game::R_GetFontHeight(F), X, Y, XS, YS, R, C, S, -1, 0, game::R_GetSomething(S))
#define R_AddCmdDrawTextWithCursor(TXT, MC, F, UNK, X, Y, XS, YS, R, C, S, CP, CC) \ #define R_AddCmdDrawTextWithCursor(TXT, MC, F, UNK, X, Y, XS, YS, R, C, S, CP, CC) \
H1_AddBaseDrawTextCmd(TXT, MC, F, game::R_GetFontHeight(F), X, Y, XS, YS, R, C, S, CP, CC, game::R_DrawSomething(S)) H1_AddBaseDrawTextCmd(TXT, MC, F, game::R_GetFontHeight(F), X, Y, XS, YS, R, C, S, CP, CC, game::R_GetSomething(S))
WEAK symbol<char* (GfxImage* image, uint32_t width, uint32_t height, uint32_t depth, uint32_t mipCount, WEAK symbol<char* (GfxImage* image, uint32_t width, uint32_t height, uint32_t depth, uint32_t mipCount,
uint32_t imageFlags, DXGI_FORMAT imageFormat, const char* name, const D3D11_SUBRESOURCE_DATA* initData)> Image_Setup{0x1404D7D50, 0x1405DCF90}; uint32_t imageFlags, DXGI_FORMAT imageFormat, const char* name, const D3D11_SUBRESOURCE_DATA* initData)> Image_Setup{0x560740, 0x683890};
WEAK symbol<unsigned int(unsigned int localId, const char* pos, WEAK symbol<unsigned int(unsigned int localId, const char* pos,
unsigned int paramcount)> VM_Execute{0x140376360, 0x140444350}; unsigned int paramcount)> VM_Execute{0x3C9E50, 0x510EB0};
WEAK symbol<void(unsigned int id, scr_string_t stringValue, WEAK symbol<void(unsigned int id, scr_string_t stringValue,
unsigned int paramcount)> Scr_NotifyId{0x140375800, 0x1404437E0}; unsigned int paramcount)> Scr_NotifyId{0x3C92E0, 0x510340};
WEAK symbol<const float*(const float* v)> Scr_AllocVector{0x140370930, 0x14043E7D0}; WEAK symbol<const float*(const float* v)> Scr_AllocVector{0x3C42D0, 0x50B330};
WEAK symbol<float(int index)> Scr_GetFloat{0x140374D20, 0x140442D10}; WEAK symbol<float(int index)> Scr_GetFloat{0x3C87D0, 0x50F870};
WEAK symbol<const char*(int index)> Scr_GetString{0x140375210, 0x140443150}; WEAK symbol<const char*(int index)> Scr_GetString{0x3C8CC0, 0x50FCB0};
WEAK symbol<int()> Scr_GetNumParam{0x140374F30, 0x140442E70}; WEAK symbol<int()> Scr_GetNumParam{0x0, 0x50F9D0};
WEAK symbol<void()> Scr_ClearOutParams{0x140374460, 0x140442510}; WEAK symbol<void()> Scr_ClearOutParams{0x3C7EF0, 0x50F070};
WEAK symbol<scr_entref_t(unsigned int entId)> Scr_GetEntityIdRef{0x140372D50, 0x140440D80}; WEAK symbol<scr_entref_t(unsigned int entId)> Scr_GetEntityIdRef{0x3C6760, 0x50D8E0};
WEAK symbol<unsigned int(int classnum, unsigned int entnum)> Scr_GetEntityId{0x140372CA0, 0x140440CD0}; WEAK symbol<unsigned int(int classnum, unsigned int entnum)> Scr_GetEntityId{0x3C66B0, 0x50D830};
WEAK symbol<int(unsigned int classnum, int entnum, int offset)> Scr_SetObjectField{0x1402B9F60, 0x140385330}; WEAK symbol<int(unsigned int classnum, int entnum, int offset)> Scr_SetObjectField{0x2E8FC0, 0x459CD0};
WEAK symbol<ScreenPlacement* ()> ScrPlace_GetViewPlacement{0x1401981F0, 0x140288550}; WEAK symbol<ScreenPlacement*()> ScrPlace_GetViewPlacement{0x1BCED0, 0x362840};
WEAK symbol<void(XAssetType type, void(__cdecl* func)(XAssetHeader, void*), const void* inData, bool includeOverride)> WEAK symbol<void(XAssetType type, void(__cdecl* func)(XAssetHeader, void*), const void* inData, bool includeOverride)>
DB_EnumXAssets_Internal{0x1401C9C10, 0x1402BA830}; DB_EnumXAssets_Internal{0x1F0BF0, 0x394C60};
WEAK symbol<const char*(const XAsset* asset)> DB_GetXAssetName{0x14019A390, 0x14028BE50}; WEAK symbol<const char*(const XAsset* asset)> DB_GetXAssetName{0x1BF890, 0x366140};
WEAK symbol<int(XAssetType type)> DB_GetXAssetTypeSize{0x14019A3B0, 0x14028BE70}; WEAK symbol<int(XAssetType type)> DB_GetXAssetTypeSize{0x0, 0x0};
WEAK symbol<XAssetHeader(XAssetType type, const char* name, WEAK symbol<XAssetHeader(XAssetType type, const char* name,
int createDefault)> DB_FindXAssetHeader{0x1401CA150, 0x1402BAC70}; int createDefault)> DB_FindXAssetHeader{0x1F1120, 0x3950C0};
WEAK symbol<void(int clientNum, const char* menu, WEAK symbol<void(int clientNum, const char* menu,
int a3, int a4, unsigned int a5)> LUI_OpenMenu{0x14039D5F0, 0x1404CD210}; int a3, int a4, unsigned int a5)> LUI_OpenMenu{0x3F20A0, 0x1E1210};
WEAK symbol<bool(int clientNum, const char* name, hks::lua_State* s)> LUI_BeginEvent{0x1400D27F0, 0x140161A00}; WEAK symbol<void()> LUI_EnterCriticalSection{0xF19A0, 0x2669B0};
WEAK symbol<void(hks::lua_State* s)> LUI_EndEvent{0x1400D3A80, 0x140162CD0}; WEAK symbol<void()> LUI_LeaveCriticalSection{0xF6C40, 0x26BDC0};
WEAK symbol<void()> LUI_EnterCriticalSection{0x1400D3B70, 0x140162DC0};
WEAK symbol<void()> LUI_LeaveCriticalSection{0x1400D8DB0, 0x140168150};
WEAK symbol<bool(int clientNum, const char* menu)> Menu_IsMenuOpenAndVisible{0x1404709C0, 0x1404C7320}; WEAK symbol<bool(int clientNum, const char* menu)> Menu_IsMenuOpenAndVisible{0x4F43C0, 0x389F70};
WEAK symbol<scr_string_t(const char* str)> SL_FindString{0x14036D700, 0x14043B470}; WEAK symbol<scr_string_t(const char* str)> SL_FindString{0x3C0F50, 0x507FD0};
WEAK symbol<scr_string_t(const char* str, unsigned int user)> SL_GetString{0x14036D9A0, 0x14043B840}; WEAK symbol<scr_string_t(const char* str, unsigned int user)> SL_GetString{0x3C1210, 0x5083A0};
WEAK symbol<const char*(scr_string_t stringValue)> SL_ConvertToString{0x14036D420, 0x14043B170}; WEAK symbol<const char*(scr_string_t stringValue)> SL_ConvertToString{0x3C0C50, 0x507CD0};
WEAK symbol<unsigned int(const char* str)> SL_GetCanonicalString{0x14036A310, 0x140437EA0}; WEAK symbol<unsigned int(const char* str)> SL_GetCanonicalString{0x3BDA20, 0x504A00};
WEAK symbol<void(netadr_s* from)> SV_DirectConnect{0, 0x140480860}; WEAK symbol<void(netadr_s* from)> SV_DirectConnect{0x0, 0x54DBF0};
WEAK symbol<void(int arg, char* buffer, int bufferLength)> SV_Cmd_ArgvBuffer{0x1403446C0, 0x140404CA0}; WEAK symbol<void(int arg, char* buffer, int bufferLength)> SV_Cmd_ArgvBuffer{0x377D40, 0x1CAC60};
WEAK symbol<void(const char* text_in)> SV_Cmd_TokenizeString{0x140344740, 0x140404D20}; WEAK symbol<void(const char* text_in)> SV_Cmd_TokenizeString{0x377DC0, 0x1CACE0};
WEAK symbol<void()> SV_Cmd_EndTokenizedString{0x140344700, 0x140404CE0}; WEAK symbol<void()> SV_Cmd_EndTokenizedString{0x377D80, 0x1CACA0};
WEAK symbol<mp::gentity_s*(const char* name)> SV_AddBot{0, 0x140480190}; WEAK symbol<mp::gentity_s*(const char* name)> SV_AddBot{0x0, 0x54D4F0};
WEAK symbol<bool(int clientNum)> SV_BotIsBot{0, 0x14046E6C0}; WEAK symbol<bool(int clientNum)> SV_BotIsBot{0x0, 0x53B6D0};
WEAK symbol<const char* ()> SV_BotGetRandomName{0, 0x14046DBA0}; WEAK symbol<const char*()> SV_BotGetRandomName{0x0, 0x53ABD0};
WEAK symbol<int(mp::gentity_s* ent)> SV_SpawnTestClient{0, 0x1404832A0}; WEAK symbol<int(mp::gentity_s* ent)> SV_SpawnTestClient{0x0, 0x550580};
WEAK symbol<const char* (int clientNum)> SV_GetGuid{0, 0x140484B90}; WEAK symbol<const char*(int clientNum)> SV_GetGuid{0x0, 0x551D90};
WEAK symbol<int(int clientNum)> SV_GetClientPing{0, 0x140484B70}; WEAK symbol<int(int clientNum)> SV_GetClientPing{0x0, 0x551D70};
WEAK symbol<playerState_s* (int num)> SV_GetPlayerstateForClientNum{0x1404426D0, 0x140484C10}; WEAK symbol<playerState_s* (int num)> SV_GetPlayerstateForClientNum{0x4C3F10, 0x551E10};
WEAK symbol<void(int index, const char* string)> SV_SetConfigstring{0, 0x140486720}; WEAK symbol<void(int index, const char* string)> SV_SetConfigstring{0x0, 0x553E60};
WEAK symbol<bool()> SV_Loaded{0x140442F60, 0x1404864A0}; WEAK symbol<bool()> SV_Loaded{0x4C4810, 0x553970};
WEAK symbol<void(int clientNum, const char* reason)> SV_KickClientNum{0, 0x14047ED00}; WEAK symbol<void(int clientNum, const char* reason)> SV_KickClientNum{0x0, 0x54C060};
WEAK symbol<bool(const char* map)> SV_MapExists{0, 0x14047ED60}; WEAK symbol<bool(const char* map)> SV_MapExists{0x0, 0x54C0C0};
WEAK symbol<void(mp::client_t*, const char*, int)> SV_ExecuteClientCommand{0, 0x140481870}; WEAK symbol<void(mp::client_t*, const char*, int)> SV_ExecuteClientCommand{0x0, 0x0};
WEAK symbol<void(int localClientNum)> SV_FastRestart{0, 0x14047E990}; WEAK symbol<void(int localClientNum)> SV_FastRestart{0x0, 0x54BE00};
WEAK symbol<void(int clientNum, svscmd_type type, const char* text)> SV_GameSendServerCommand{ WEAK symbol<void(void* cl, int type, const char* fmt, ...)> SV_SendServerCommand{0x0, 0x1CC040};
0x1403F3A70, 0x140484AD0
};
WEAK symbol<void()> Sys_ShowConsole{0x1403E3B90, 0x140514910}; WEAK symbol<void()> Sys_ShowConsole{0x0, 0x0};
WEAK symbol<void(const char* error, ...)> Sys_Error{0x1403E0C40, 0x140511520}; WEAK symbol<void(const char* error, ...)> Sys_Error{0x0, 0x1D8710};
WEAK symbol<void(char* path, int pathSize, Sys_Folder folder, const char* filename, const char* ext)> WEAK symbol<void(char* path, int pathSize, Sys_Folder folder, const char* filename, const char* ext)>
Sys_BuildAbsPath{0x1403CFF90, 0x140507010}; Sys_BuildAbsPath{0x42C330, 0x0};
WEAK symbol<int()> Sys_Milliseconds{0x1403E2B10, 0x140513710}; WEAK symbol<int()> Sys_Milliseconds{0x462B30, 0x5BDDF0};
WEAK symbol<bool()> Sys_IsDatabaseReady2{0x1403580B0, 0x14042B090}; WEAK symbol<bool()> Sys_IsDatabaseReady2{0x3AB100, 0x4F79C0};
WEAK symbol<bool(int, void const*, const netadr_s*)> Sys_SendPacket{0x1403E2820, 0x1405133B0}; WEAK symbol<bool(int, void const*, const netadr_s*)> Sys_SendPacket{0x0, 0x5BDA90};
WEAK symbol<bool(const char* path)> Sys_FileExists{0x1403E0CE0, 0x1405115E0}; WEAK symbol<bool(const char* path)> Sys_FileExists{0x0, 0x0};
WEAK symbol<const char* (const char*)> UI_GetMapDisplayName{0, 0x140408CC0}; WEAK symbol<const char*(const char*)> UI_GetMapDisplayName{0x0, 0x4DDEE0};
WEAK symbol<const char* (const char*)> UI_GetGameTypeDisplayName{0, 0x1404086A0}; WEAK symbol<const char*(const char*)> UI_GetGameTypeDisplayName{0x0, 0x4DD8C0};
WEAK symbol<void(unsigned int localClientNum, const char** args)> UI_RunMenuScript{0x14039EFF0, 0x1404CFE60}; WEAK symbol<void(unsigned int localClientNum, const char** args)> UI_RunMenuScript{0x3F3AA0, 0x1E35B0};
WEAK symbol<int(const char* text, int maxChars, Font_s* font, float scale)> UI_TextWidth{0x1403A0F20, 0x1404D21A0}; WEAK symbol<int(const char* text, int maxChars, Font_s* font, float scale)> UI_TextWidth{0x3F5D90, 0x0};
WEAK symbol<const char*(const char* string)> UI_SafeTranslateString{0x140350430, 0x14041C580}; WEAK symbol<const char*(const char* string)> UI_SafeTranslateString{0x3840A0, 0x4E8BC0};
WEAK symbol<void* (jmp_buf* Buf, int Value)> longjmp{0x140648FD4, 0x140779F64}; WEAK symbol<void*(jmp_buf* Buf, int Value)> longjmp{0x6DCA90, 0x826710};
WEAK symbol<int(jmp_buf* Buf)> _setjmp{0x1406BFD30, 0x1407F5F90}; WEAK symbol<int(jmp_buf* Buf)> _setjmp{0x758980, 0x8A3190};
/*************************************************************** /***************************************************************
* Variables * Variables
**************************************************************/ **************************************************************/
WEAK symbol<CmdArgs> sv_cmd_args{0x14AD99A10, 0x14946BA20}; WEAK symbol<CmdArgs> sv_cmd_args{0xB48FF90, 0x2ED1EB0};
WEAK symbol<int> g_script_error_level{0x14BCFA4A4, 0x14A33C824}; WEAK symbol<int> g_script_error_level{0xC3FD358, 0xB7AC1A4};
WEAK symbol<jmp_buf> g_script_error{0x14BCFA5C0, 0x14A33C940}; WEAK symbol<jmp_buf> g_script_error{0xC3FD470, 0xB7AC2C0};
WEAK symbol<unsigned int> levelEntityId{0x14B65E3B0, 0x149CA0730}; WEAK symbol<unsigned int> levelEntityId{0xBD58DA0, 0xB1100B0};
WEAK symbol<unsigned int> gameEntityId{0x14B65E3B4, 0x149CA0734}; WEAK symbol<unsigned int> gameEntityId{0xBD58DA4, 0xB1100B4};
WEAK symbol<const char*> command_whitelist{0x141079A60, 0x14120C360}; WEAK symbol<const char*> command_whitelist{0x115ADF0, 0x10ACB70};
WEAK symbol<cmd_function_s*> cmd_functions{0x14AD99AB8, 0x14946BAC8}; WEAK symbol<cmd_function_s*> cmd_functions{0xB490038, 0x344DF18};
WEAK symbol<CmdArgs> cmd_args{0x14AD99960, 0x14946B970}; WEAK symbol<CmdArgs> cmd_args{0xB48FEE0, 0x2ED1E00};
WEAK symbol<CmdArgsPrivate> cmd_argsPrivate{0, 0x3513F20};
WEAK symbol<int> g_poolSize{0x140DE84A0, 0x140FEADF0}; WEAK symbol<int> connectionState{0x0, 0x2EC82C8};
WEAK symbol<scr_classStruct_t> g_classMap{0x14107AAF0, 0x1412106B0};
WEAK symbol<scrVarGlob_t> scr_VarGlob{0x14B686480, 0x149CC8800}; WEAK symbol<int> g_poolSize{0x0, 0x0};
WEAK symbol<scrVmPub_t> scr_VmPub{0x14BCFC6C0, 0x14A33EA40};
WEAK symbol<function_stack_t> scr_function_stack{0x14BD06C40, 0x14A348FC0};
WEAK symbol<GfxDrawMethod_s> gfxDrawMethod{0x14F05CE50, 0x14FD21180}; WEAK symbol<scrVarGlob_t> scr_VarGlob{0xBD80E00, 0xB138180};
WEAK symbol<scrVmPub_t> scr_VmPub{0xC3F4E20, 0xB7AE3C0};
WEAK symbol<function_stack_t> scr_function_stack{0xC4015C0, 0xB7B8940};
WEAK symbol<int> dvarCount{0x14C217D10, 0x14D064CF4}; WEAK symbol<GfxDrawMethod_s> gfxDrawMethod{0xF7530B0, 0xE9213F0};
WEAK symbol<dvar_t> dvarPool{0x14C217D20, 0x14D064D00};
WEAK symbol<void*> DB_XAssetPool{0x140DE8C80, 0x140FEB5D0}; WEAK symbol<int> dvarCount{0xC90E550, 0x2999C34};
WEAK symbol<const char*> g_assetNames{0x1408B0870, 0x140FEA240}; WEAK symbol<dvar_t> dvarPool{0xC90E560, 0x344DF20};
WEAK symbol<int> keyCatchers{0x14243DAF0, 0x142D0BA9C}; WEAK symbol<void*> DB_XAssetPool{0xEC9FB0, 0x10B4460};
WEAK symbol<PlayerKeyState> playerKeys{0x1422A873C, 0x142C19AFC}; WEAK symbol<const char*> g_assetNames{0x991BA0, 0x10B30D0};
WEAK symbol<SOCKET> query_socket{0, 0x14DDFBF98}; WEAK symbol<int> keyCatchers{0x252AF70, 0x2EC82C4};
WEAK symbol<PlayerKeyState> playerKeys{0x2395B0C, 0x2999E1C};
WEAK symbol<DWORD> threadIds{0x14B19B880, 0x149810E00}; WEAK symbol<SOCKET> query_socket{0xD64D3F8, 0xC9DCD38};
WEAK symbol<DWORD> threadIds{0xB896210, 0xAC80740};
namespace mp namespace mp
{ {
WEAK symbol<gentity_s> g_entities{0, 0x14621E530}; WEAK symbol<gentity_s> g_entities{0x0, 0x71F19E0};
WEAK symbol<client_t> svs_clients{0, 0x14B204A10}; WEAK symbol<client_t*> svs_clients{0x0, 0x2DC3390};
WEAK symbol<int> svs_numclients{0, 0x14B204A0C}; WEAK symbol<int> svs_numclients{0x0, 0x2DC338C};
WEAK symbol<int> gameTime{0, 0x14621BDBC}; WEAK symbol<int> gameTime{0x0, 0x0};
WEAK symbol<int> ping{0, 0x142D106F0}; WEAK symbol<int> sv_serverId_value{0x0, 0x0};
WEAK symbol<int> sv_serverId_value{0, 0x14A3E99B8}; WEAK symbol<bool> virtualLobby_loaded{0x0, 0x2E6EC9D};
WEAK symbol<bool> virtualLobby_loaded{0, 0x142D077FD}; WEAK symbol<client_state_t*> client_state{0x0, 0x2EC84F0};
WEAK symbol<connect_state_t*> connect_state{0x0, 0x2EC8510};
} }
namespace sp namespace sp
{ {
WEAK symbol<gentity_s> g_entities{0x14550DD90, 0}; WEAK symbol<gentity_s> g_entities{0x56E74D0, 0x0};
} }
namespace hks namespace hks
{ {
WEAK symbol<lua_State*> lua_state{0x141E2C2F8, 0x1426D3D08}; WEAK symbol<lua_State*> lua_state{0x1F0E408, 0x36647F8};
WEAK symbol<void(lua_State* s, const char* str, unsigned int l)> hksi_lua_pushlstring{0x14004DA90, 0x1400624F0}; WEAK symbol<void(lua_State* s, const char* str, unsigned int l)> hksi_lua_pushlstring{0x62E60, 0x79800};
WEAK symbol<HksObject*(HksObject* result, lua_State* s, const HksObject* table, const HksObject* key)> hks_obj_getfield{0x14009C0A0, 0x14012C600}; WEAK symbol<HksObject*(HksObject* result, lua_State* s, const HksObject* table, const HksObject* key)> hks_obj_getfield{0xB5100, 0x22CB90};
WEAK symbol<void(lua_State* s, const HksObject* tbl, const HksObject* key, const HksObject* val)> hks_obj_settable{0x14009D240, 0x14012D820}; WEAK symbol<void(lua_State* s, const HksObject* tbl, const HksObject* key, const HksObject* val)> hks_obj_settable{0xB6320, 0x22DDB0};
WEAK symbol<HksObject* (HksObject* result, lua_State* s, const HksObject* table, const HksObject* key)> hks_obj_gettable{0x14009C580, 0x14012CAE0}; WEAK symbol<HksObject* (HksObject* result, lua_State* s, const HksObject* table, const HksObject* key)> hks_obj_gettable{0xB55E0, 0x22D070};
WEAK symbol<void(lua_State* s, int nargs, int nresults, const unsigned int* pc)> vm_call_internal{0x1400C87A0, 0x140159EB0}; WEAK symbol<void(lua_State* s, int nargs, int nresults, const unsigned int* pc)> vm_call_internal{0xE5E40, 0x25D2F0};
WEAK symbol<HashTable*(lua_State* s, unsigned int arraySize, unsigned int hashSize)> Hashtable_Create{0x14008B3B0, 0x14011B320}; WEAK symbol<HashTable*(lua_State* s, unsigned int arraySize, unsigned int hashSize)> Hashtable_Create{0xA3570, 0x21B030};
WEAK symbol<cclosure*(lua_State* s, lua_function function, int num_upvalues, WEAK symbol<cclosure*(lua_State* s, lua_function function, int num_upvalues,
int internal_, int profilerTreatClosureAsFunc)> cclosure_Create{0x14008B5D0, 0x14011B540}; int internal_, int profilerTreatClosureAsFunc)> cclosure_Create{0xA3790, 0x21B250};
WEAK symbol<int(lua_State* s, int t)> hksi_luaL_ref{0x1400A64D0, 0x140136D30}; WEAK symbol<int(lua_State* s, int t)> hksi_luaL_ref{0xB7F90, 0x236CC0};
WEAK symbol<void(lua_State* s, int t, int ref)> hksi_luaL_unref{0x14009EF10, 0x14012F610}; WEAK symbol<void(lua_State* s, int t, int ref)> hksi_luaL_unref{0xB8130, 0x22FC40};
WEAK symbol<void(lua_State* s, HksObject* lfp)> closePendingUpvalues{0x14008EA00, 0x14011E970}; WEAK symbol<int(lua_State* s, const HksCompilerSettings* options, const char* buff,
unsigned __int64 sz, const char* name)> hksi_hksL_loadbuffer{0xB6B90, 0x22E620};
WEAK symbol<int(lua_State* s, const char* what, lua_Debug* ar)> hksi_lua_getinfo{0xB84D0, 0x22FFE0};
WEAK symbol<int(lua_State* s, int level, lua_Debug* ar)> hksi_lua_getstack{0xB87A0, 0x2302B0};
WEAK symbol<void(lua_State* s, const char* fmt, ...)> hksi_luaL_error{0xBF120, 0x22F930};
WEAK symbol<const char*> typenames{0x98CD20, 0x10AD750};
} }
} }

View File

@ -1,16 +1,54 @@
#include <std_include.hpp> #include <std_include.hpp>
#include "execution.hpp" #include "execution.hpp"
#include "component/ui_scripting.hpp" #include "component/ui_scripting.hpp"
#include "component/console.hpp"
#include <utils/string.hpp> #include <utils/string.hpp>
namespace ui_scripting namespace ui_scripting
{ {
namespace
{
script_value get_field(void* ptr, game::hks::HksObjectType type, const script_value& key)
{
const auto state = *game::hks::lua_state;
const auto top = state->m_apistack.top;
push_value(key);
game::hks::HksObject value{};
game::hks::HksObject obj{};
obj.t = type;
obj.v.ptr = ptr;
game::hks::hks_obj_gettable(&value, state, &obj, &state->m_apistack.top[-1]);
state->m_apistack.top = top;
return value;
}
void set_field(void* ptr, game::hks::HksObjectType type, const script_value& key, const script_value& value)
{
const auto state = *game::hks::lua_state;
game::hks::HksObject obj{};
obj.t = type;
obj.v.ptr = ptr;
game::hks::hks_obj_settable(state, &obj, &key.get_raw(), &value.get_raw());
}
}
void push_value(const script_value& value) void push_value(const script_value& value)
{ {
const auto state = *game::hks::lua_state; const auto state = *game::hks::lua_state;
const auto value_ = value.get_raw(); *state->m_apistack.top = value.get_raw();
*state->m_apistack.top = value_; state->m_apistack.top++;
}
void push_value(const game::hks::HksObject& value)
{
const auto state = *game::hks::lua_state;
*state->m_apistack.top = value;
state->m_apistack.top++; state->m_apistack.top++;
} }
@ -20,13 +58,36 @@ namespace ui_scripting
return state->m_apistack.top[-1 - offset]; return state->m_apistack.top[-1 - offset];
} }
arguments get_return_values(int count) arguments get_return_values()
{ {
const auto state = *game::hks::lua_state;
const auto count = static_cast<int>(state->m_apistack.top - state->m_apistack.base);
arguments values; arguments values;
for (auto i = count - 1; i >= 0; i--) for (auto i = count - 1; i >= 0; i--)
{ {
values.push_back(get_return_value(i)); const auto v = get_return_value(i);
values.push_back(v);
}
if (values.size() == 0)
{
values.push_back({});
}
return values;
}
arguments get_return_values(game::hks::HksObject* base)
{
const auto state = *game::hks::lua_state;
const auto count = static_cast<int>(state->m_apistack.top - base);
arguments values;
for (auto i = count - 1; i >= 0; i--)
{
const auto v = get_return_value(i);
values.push_back(v);
} }
if (values.size() == 0) if (values.size() == 0)
@ -40,7 +101,7 @@ namespace ui_scripting
bool notify(const std::string& name, const event_arguments& arguments) bool notify(const std::string& name, const event_arguments& arguments)
{ {
const auto state = *game::hks::lua_state; const auto state = *game::hks::lua_state;
if (!state) if (state == nullptr)
{ {
return false; return false;
} }
@ -52,160 +113,63 @@ namespace ui_scripting
{ {
const auto globals = table((*::game::hks::lua_state)->globals.v.table); const auto globals = table((*::game::hks::lua_state)->globals.v.table);
const auto engine = globals.get("Engine").as<table>(); const auto engine = globals.get("Engine").as<table>();
const auto root = engine.get("GetLuiRoot").as<function>().call({})[0].as<userdata>(); const auto root = engine.get("GetLuiRoot")()[0].as<userdata>();
const auto process_event = root.get("processEvent").as<function>(); const auto process_event = root.get("processEvent");
table event{}; table event{};
event.set("name", name); event.set("name", name);
event.set("dispatchChildren", true);
for (const auto& arg : arguments) for (const auto& arg : arguments)
{ {
event.set(arg.first, arg.second); event.set(arg.first, arg.second);
} }
process_event.call({root, event}); process_event(root, event);
return true; return true;
} }
catch (const std::exception& e) catch (const std::exception& e)
{ {
printf("Error processing event '%s' %s\n", name.data(), e.what()); console::error("Error processing event '%s' %s\n", name.data(), e.what());
return false;
} }
return false;
} }
arguments call_script_function(const function& function, const arguments& arguments) arguments call_script_function(const function& function, const arguments& arguments)
{ {
const auto state = *game::hks::lua_state; const auto state = *game::hks::lua_state;
const auto top = state->m_apistack.top;
stack stack;
push_value(function); push_value(function);
for (auto i = arguments.begin(); i != arguments.end(); ++i) for (auto i = arguments.begin(); i != arguments.end(); ++i)
{ {
push_value(*i); push_value(*i);
} }
const auto num_args = static_cast<int>(arguments.size()); game::hks::vm_call_internal(state, static_cast<int>(arguments.size()), -1, 0);
stack.save(num_args + 1); const auto args = get_return_values(top);
state->m_apistack.top = top;
const auto _1 = gsl::finally(&disable_error_hook); return args;
enable_error_hook();
try
{
game::hks::vm_call_internal(state, static_cast<int>(arguments.size()), -1, 0);
const auto count = static_cast<int>(state->m_apistack.top - state->m_apistack.base);
return get_return_values(count);
}
catch (const std::exception& e)
{
stack.fix();
throw std::runtime_error(std::string("Error executing script function: ") + e.what());
}
} }
script_value get_field(const userdata& self, const script_value& key) script_value get_field(const userdata& self, const script_value& key)
{ {
const auto state = *game::hks::lua_state; return get_field(self.ptr, game::hks::TUSERDATA, key);
stack stack;
push_value(key);
stack.save(1);
const auto _1 = gsl::finally(&disable_error_hook);
enable_error_hook();
game::hks::HksObject value{};
game::hks::HksObject userdata{};
userdata.t = game::hks::TUSERDATA;
userdata.v.ptr = self.ptr;
try
{
game::hks::hks_obj_gettable(&value, state, &userdata, &state->m_apistack.top[-1]);
return value;
}
catch (const std::exception& e)
{
stack.fix();
throw std::runtime_error("Error getting userdata field: "s + e.what());
}
} }
script_value get_field(const table& self, const script_value& key) script_value get_field(const table& self, const script_value& key)
{ {
const auto state = *game::hks::lua_state; return get_field(self.ptr, game::hks::TTABLE, key);
stack stack;
push_value(key);
stack.save(1);
const auto _1 = gsl::finally(&disable_error_hook);
enable_error_hook();
game::hks::HksObject value{};
game::hks::HksObject userdata{};
userdata.t = game::hks::TTABLE;
userdata.v.ptr = self.ptr;
try
{
game::hks::hks_obj_gettable(&value, state, &userdata, &state->m_apistack.top[-1]);
return value;
}
catch (const std::exception& e)
{
stack.fix();
throw std::runtime_error("Error getting table field: "s + e.what());
}
} }
void set_field(const userdata& self, const script_value& key, const script_value& value) void set_field(const userdata& self, const script_value& key, const script_value& value)
{ {
const auto state = *game::hks::lua_state; set_field(self.ptr, game::hks::TUSERDATA, key, value);
stack stack;
stack.save(0);
const auto _1 = gsl::finally(&disable_error_hook);
enable_error_hook();
game::hks::HksObject userdata{};
userdata.t = game::hks::TUSERDATA;
userdata.v.ptr = self.ptr;
try
{
game::hks::hks_obj_settable(state, &userdata, &key.get_raw(), &value.get_raw());
}
catch (const std::exception& e)
{
stack.fix();
throw std::runtime_error("Error setting userdata field: "s + e.what());
}
} }
void set_field(const table& self, const script_value& key, const script_value& value) void set_field(const table& self, const script_value& key, const script_value& value)
{ {
const auto state = *game::hks::lua_state; set_field(self.ptr, game::hks::TTABLE, key, value);
stack stack;
stack.save(0);
const auto _1 = gsl::finally(&disable_error_hook);
enable_error_hook();
game::hks::HksObject userdata{};
userdata.t = game::hks::TTABLE;
userdata.v.ptr = self.ptr;
try
{
game::hks::hks_obj_settable(state, &userdata, &key.get_raw(), &value.get_raw());
}
catch (const std::exception& e)
{
stack.fix();
throw std::runtime_error("Error setting table field: "s + e.what());
}
} }
} }

View File

@ -6,8 +6,11 @@
namespace ui_scripting namespace ui_scripting
{ {
void push_value(const script_value& value); void push_value(const script_value& value);
void push_value(const game::hks::HksObject& value);
script_value get_return_value(int offset); script_value get_return_value(int offset);
arguments get_return_values(int count); arguments get_return_values();
arguments get_return_values(game::hks::HksObject* base);
bool notify(const std::string& name, const event_arguments& arguments); bool notify(const std::string& name, const event_arguments& arguments);

View File

@ -1,428 +0,0 @@
#include <std_include.hpp>
#include "context.hpp"
#include "error.hpp"
#include "value_conversion.hpp"
#include "../../scripting/execution.hpp"
#include "../script_value.hpp"
#include "../execution.hpp"
#include "../../../component/ui_scripting.hpp"
#include "../../../component/command.hpp"
#include "../../../component/updater.hpp"
#include "../../../component/fps.hpp"
#include "../../../component/localized_strings.hpp"
#include "../../../component/fastfiles.hpp"
#include "../../../component/scripting.hpp"
#include "../../../component/mods.hpp"
#include "../../../component/discord.hpp"
#include "component/game_console.hpp"
#include "component/scheduler.hpp"
#include <discord_rpc.h>
#include <utils/string.hpp>
#include <utils/nt.hpp>
#include <utils/io.hpp>
namespace ui_scripting::lua
{
namespace
{
const auto json_script = utils::nt::load_resource(LUA_JSON);
void setup_json(sol::state& state)
{
const auto json = state.safe_script(json_script, &sol::script_pass_on_error);
handle_error(json);
state["json"] = json;
}
void setup_io(sol::state& state)
{
state["io"]["fileexists"] = utils::io::file_exists;
state["io"]["writefile"] = utils::io::write_file;
state["io"]["movefile"] = utils::io::move_file;
state["io"]["filesize"] = utils::io::file_size;
state["io"]["createdirectory"] = utils::io::create_directory;
state["io"]["directoryexists"] = utils::io::directory_exists;
state["io"]["directoryisempty"] = utils::io::directory_is_empty;
state["io"]["listfiles"] = utils::io::list_files;
state["io"]["copyfolder"] = utils::io::copy_folder;
state["io"]["removefile"] = utils::io::remove_file;
state["io"]["removedirectory"] = utils::io::remove_directory;
state["io"]["readfile"] = static_cast<std::string(*)(const std::string&)>(utils::io::read_file);
}
void setup_types(sol::state& state, scheduler& scheduler)
{
struct game
{
};
auto game_type = state.new_usertype<game>("game_");
state["game"] = game();
game_type["ontimeout"] = [&scheduler](const game&, const sol::protected_function& callback,
const long long milliseconds)
{
return scheduler.add(callback, milliseconds, true);
};
game_type["oninterval"] = [&scheduler](const game&, const sol::protected_function& callback,
const long long milliseconds)
{
return scheduler.add(callback, milliseconds, false);
};
game_type["getfps"] = [](const game&)
{
return fps::get_fps();
};
game_type["getping"] = [](const game&)
{
return *::game::mp::ping;
};
game_type["issingleplayer"] = [](const game&)
{
return ::game::environment::is_sp();
};
game_type["ismultiplayer"] = [](const game&)
{
return ::game::environment::is_mp();
};
game_type["addlocalizedstring"] = [](const game&, const std::string& string,
const std::string& value)
{
localized_strings::override(string, value);
};
game_type["sharedset"] = [](const game&, const std::string& key, const std::string& value)
{
scripting::shared_table.access([key, value](scripting::shared_table_t& table)
{
table[key] = value;
});
};
game_type["sharedget"] = [](const game&, const std::string& key)
{
std::string result;
scripting::shared_table.access([key, &result](scripting::shared_table_t& table)
{
result = table[key];
});
return result;
};
game_type["sharedclear"] = [](const game&)
{
scripting::shared_table.access([](scripting::shared_table_t& table)
{
table.clear();
});
};
game_type["assetlist"] = [](const game&, const sol::this_state s, const std::string& type_string)
{
auto table = sol::table::create(s.lua_state());
auto index = 1;
auto type_index = -1;
for (auto i = 0; i < ::game::XAssetType::ASSET_TYPE_COUNT; i++)
{
if (type_string == ::game::g_assetNames[i])
{
type_index = i;
}
}
if (type_index == -1)
{
throw std::runtime_error("Asset type does not exist");
}
const auto type = static_cast<::game::XAssetType>(type_index);
fastfiles::enum_assets(type, [type, &table, &index](const ::game::XAssetHeader header)
{
const auto asset = ::game::XAsset{type, header};
const std::string asset_name = ::game::DB_GetXAssetName(&asset);
table[index++] = asset_name;
}, true);
return table;
};
game_type["getweapondisplayname"] = [](const game&, const std::string& name)
{
const auto alternate = name.starts_with("alt_");
const auto weapon = ::game::G_GetWeaponForName(name.data());
char buffer[0x400] = {0};
::game::CG_GetWeaponDisplayName(weapon, alternate, buffer, 0x400);
return std::string(buffer);
};
game_type["getloadedmod"] = [](const game&)
{
return mods::mod_path;
};
auto userdata_type = state.new_usertype<userdata>("userdata_");
userdata_type["new"] = sol::property(
[](const userdata& userdata, const sol::this_state s)
{
return convert(s, userdata.get("new"));
},
[](const userdata& userdata, const sol::this_state s, const sol::lua_value& value)
{
userdata.set("new", convert({s, value}));
}
);
userdata_type["get"] = [](const userdata& userdata, const sol::this_state s,
const sol::lua_value& key)
{
return convert(s, userdata.get(convert({s, key})));
};
userdata_type["set"] = [](const userdata& userdata, const sol::this_state s,
const sol::lua_value& key, const sol::lua_value& value)
{
userdata.set(convert({s, key}), convert({s, value}));
};
userdata_type[sol::meta_function::index] = [](const userdata& userdata, const sol::this_state s,
const sol::lua_value& key)
{
return convert(s, userdata.get(convert({s, key})));
};
userdata_type[sol::meta_function::new_index] = [](const userdata& userdata, const sol::this_state s,
const sol::lua_value& key, const sol::lua_value& value)
{
userdata.set(convert({s, key}), convert({s, value}));
};
auto table_type = state.new_usertype<table>("table_");
table_type["new"] = sol::property(
[](const table& table, const sol::this_state s)
{
return convert(s, table.get("new"));
},
[](const table& table, const sol::this_state s, const sol::lua_value& value)
{
table.set("new", convert({s, value}));
}
);
table_type["get"] = [](const table& table, const sol::this_state s,
const sol::lua_value& key)
{
return convert(s, table.get(convert({s, key})));
};
table_type["set"] = [](const table& table, const sol::this_state s,
const sol::lua_value& key, const sol::lua_value& value)
{
table.set(convert({s, key}), convert({s, value}));
};
table_type[sol::meta_function::index] = [](const table& table, const sol::this_state s,
const sol::lua_value& key)
{
return convert(s, table.get(convert({s, key})));
};
table_type[sol::meta_function::new_index] = [](const table& table, const sol::this_state s,
const sol::lua_value& key, const sol::lua_value& value)
{
table.set(convert({s, key}), convert({s, value}));
};
auto function_type = state.new_usertype<function>("function_");
function_type[sol::meta_function::call] = [](const function& function, const sol::this_state s, sol::variadic_args va)
{
arguments arguments{};
for (auto arg : va)
{
arguments.push_back(convert({s, arg}));
}
const auto values = function.call(arguments);
std::vector<sol::lua_value> returns;
for (const auto& value : values)
{
returns.push_back(convert(s, value));
}
return sol::as_returns(returns);
};
if (::game::environment::is_sp())
{
struct player
{
};
auto player_type = state.new_usertype<player>("player_");
state["player"] = player();
player_type["notify"] = [](const player&, const sol::this_state s, const std::string& name, sol::variadic_args va)
{
if (!::game::CL_IsCgameInitialized() || !::game::sp::g_entities[0].client)
{
throw std::runtime_error("Not in game");
}
const sol::state_view view{s};
const auto to_string = view["tostring"].get<sol::protected_function>();
std::vector<std::string> args{};
for (auto arg : va)
{
args.push_back(to_string.call(arg).get<std::string>());
}
::scheduler::once([s, name, args]()
{
try
{
std::vector<scripting::script_value> arguments{};
for (const auto& arg : args)
{
arguments.push_back(arg);
}
const auto player = scripting::call("getentbynum", {0}).as<scripting::entity>();
scripting::notify(player, name, arguments);
}
catch (...)
{
}
}, ::scheduler::pipeline::server);
};
}
state["luiglobals"] = table((*::game::hks::lua_state)->globals.v.table);
state["CoD"] = state["luiglobals"]["CoD"];
state["LUI"] = state["luiglobals"]["LUI"];
state["Engine"] = state["luiglobals"]["Engine"];
state["Game"] = state["luiglobals"]["Game"];
auto updater_table = sol::table::create(state.lua_state());
updater_table["relaunch"] = updater::relaunch;
updater_table["sethastriedupdate"] = updater::set_has_tried_update;
updater_table["gethastriedupdate"] = updater::get_has_tried_update;
updater_table["autoupdatesenabled"] = updater::auto_updates_enabled;
updater_table["startupdatecheck"] = updater::start_update_check;
updater_table["isupdatecheckdone"] = updater::is_update_check_done;
updater_table["getupdatecheckstatus"] = updater::get_update_check_status;
updater_table["isupdateavailable"] = updater::is_update_available;
updater_table["startupdatedownload"] = updater::start_update_download;
updater_table["isupdatedownloaddone"] = updater::is_update_download_done;
updater_table["getupdatedownloadstatus"] = updater::get_update_download_status;
updater_table["cancelupdate"] = updater::cancel_update;
updater_table["isrestartrequired"] = updater::is_restart_required;
updater_table["getlasterror"] = updater::get_last_error;
updater_table["getcurrentfile"] = updater::get_current_file;
state["updater"] = updater_table;
auto discord_table = sol::table::create(state.lua_state());
discord_table["respond"] = discord::respond;
discord_table["getavatarmaterial"] = discord::get_avatar_material;
discord_table["reply"] = sol::table::create(state.lua_state());
discord_table["reply"]["yes"] = DISCORD_REPLY_YES;
discord_table["reply"]["ignore"] = DISCORD_REPLY_IGNORE;
discord_table["reply"]["no"] = DISCORD_REPLY_NO;
state["discord"] = discord_table;
}
}
context::context(std::string data, script_type type)
: scheduler_(state_)
{
this->state_.open_libraries(sol::lib::base,
sol::lib::package,
sol::lib::io,
sol::lib::string,
sol::lib::os,
sol::lib::math,
sol::lib::table);
setup_json(this->state_);
setup_io(this->state_);
setup_types(this->state_, this->scheduler_);
if (type == script_type::file)
{
this->folder_ = data;
this->state_["include"] = [this](const std::string& file)
{
this->load_script(file);
};
sol::function old_require = this->state_["require"];
auto base_path = utils::string::replace(this->folder_, "/", ".") + ".";
this->state_["require"] = [base_path, old_require](const std::string& path)
{
return old_require(base_path + path);
};
this->state_["scriptdir"] = [this]()
{
return this->folder_;
};
printf("Loading ui script '%s'\n", this->folder_.data());
this->load_script("__init__");
}
if (type == script_type::code)
{
handle_error(this->state_.safe_script(data, &sol::script_pass_on_error));
}
}
context::~context()
{
this->state_.collect_garbage();
this->scheduler_.clear();
this->state_ = {};
}
void context::run_frame()
{
this->scheduler_.run_frame();
this->state_.collect_garbage();
}
void context::load_script(const std::string& script)
{
if (!this->loaded_scripts_.emplace(script).second)
{
return;
}
const auto file = (std::filesystem::path{this->folder_} / (script + ".lua")).generic_string();
handle_error(this->state_.safe_script_file(file, &sol::script_pass_on_error));
}
}

View File

@ -1,42 +0,0 @@
#pragma once
#pragma warning(push)
#pragma warning(disable: 4702)
#define SOL_ALL_SAFETIES_ON 1
#define SOL_PRINT_ERRORS 0
#include <sol/sol.hpp>
#include "scheduler.hpp"
namespace ui_scripting::lua
{
enum script_type
{
file,
code
};
class context
{
public:
context(std::string folder, script_type type);
~context();
context(context&&) noexcept = delete;
context& operator=(context&&) noexcept = delete;
context(const context&) = delete;
context& operator=(const context&) = delete;
void run_frame();
private:
sol::state state_{};
std::string folder_;
std::unordered_set<std::string> loaded_scripts_;
scheduler scheduler_;
void load_script(const std::string& script);
};
}

View File

@ -1,83 +0,0 @@
#include <std_include.hpp>
#include "engine.hpp"
#include "context.hpp"
#include "../../../component/ui_scripting.hpp"
#include "../../../component/filesystem.hpp"
#include <utils/io.hpp>
#include <utils/nt.hpp>
namespace ui_scripting::lua::engine
{
namespace
{
const auto lui_common = utils::nt::load_resource(LUI_COMMON);
const auto lui_updater = utils::nt::load_resource(LUI_UPDATER);
auto& get_scripts()
{
static std::vector<std::unique_ptr<context>> scripts{};
return scripts;
}
void load_scripts(const std::string& script_dir)
{
if (!utils::io::directory_exists(script_dir))
{
return;
}
const auto scripts = utils::io::list_files(script_dir);
for (const auto& script : scripts)
{
if (std::filesystem::is_directory(script) && utils::io::file_exists(script + "/__init__.lua"))
{
get_scripts().push_back(std::make_unique<context>(script, script_type::file));
}
}
}
void load_code(const std::string& code)
{
get_scripts().push_back(std::make_unique<context>(code, script_type::code));
}
}
void start()
{
clear_converted_functions();
get_scripts().clear();
load_code(lui_common);
load_code(lui_updater);
for (const auto& path : filesystem::get_search_paths())
{
load_scripts(path + "/ui_scripts/");
if (game::environment::is_sp())
{
load_scripts(path + "/ui_scripts/sp/");
}
else
{
load_scripts(path + "/ui_scripts/mp/");
}
}
}
void stop()
{
clear_converted_functions();
get_scripts().clear();
}
void run_frame()
{
for (auto& script : get_scripts())
{
script->run_frame();
}
}
}

View File

@ -1,8 +0,0 @@
#pragma once
namespace ui_scripting::lua::engine
{
void start();
void stop();
void run_frame();
}

View File

@ -1,18 +0,0 @@
#include <std_include.hpp>
#include "error.hpp"
namespace ui_scripting::lua
{
void handle_error(const sol::protected_function_result& result)
{
if (!result.valid())
{
printf("************** UI Script execution error **************\n");
const sol::error err = result;
printf("%s\n", err.what());
printf("****************************************************\n");
}
}
}

View File

@ -1,8 +0,0 @@
#pragma once
#include "context.hpp"
namespace ui_scripting::lua
{
void handle_error(const sol::protected_function_result& result);
}

View File

@ -1,122 +0,0 @@
#include "std_include.hpp"
#include "context.hpp"
#include "error.hpp"
namespace ui_scripting::lua
{
scheduler::scheduler(sol::state& state)
{
auto task_handle_type = state.new_usertype<task_handle>("task_handle");
task_handle_type["clear"] = [this](const task_handle& handle)
{
this->remove(handle);
};
}
void scheduler::run_frame()
{
callbacks_.access([&](task_list& tasks)
{
this->merge_callbacks();
for (auto i = tasks.begin(); i != tasks.end();)
{
const auto now = std::chrono::high_resolution_clock::now();
const auto diff = now - i->last_call;
if (diff < i->delay)
{
++i;
continue;
}
i->last_call = now;
if (!i->is_deleted)
{
handle_error(i->callback());
}
if (i->is_volatile || i->is_deleted)
{
i = tasks.erase(i);
}
else
{
++i;
}
}
});
}
void scheduler::clear()
{
callbacks_.access([&](task_list& tasks)
{
new_callbacks_.access([&](task_list& new_tasks)
{
new_tasks.clear();
tasks.clear();
});
});
}
task_handle scheduler::add(const sol::protected_function& callback, const long long milliseconds,
const bool is_volatile)
{
return this->add(callback, std::chrono::milliseconds(milliseconds), is_volatile);
}
task_handle scheduler::add(const sol::protected_function& callback, const std::chrono::milliseconds delay,
const bool is_volatile)
{
const uint64_t id = ++this->current_task_id_;
task task;
task.is_volatile = is_volatile;
task.callback = callback;
task.delay = delay;
task.last_call = std::chrono::steady_clock::now();
task.id = id;
task.is_deleted = false;
new_callbacks_.access([&task](task_list& tasks)
{
tasks.emplace_back(std::move(task));
});
return {id};
}
void scheduler::remove(const task_handle& handle)
{
auto mask_as_deleted = [&](task_list& tasks)
{
for (auto& task : tasks)
{
if (task.id == handle.id)
{
task.is_deleted = true;
break;
}
}
};
callbacks_.access(mask_as_deleted);
new_callbacks_.access(mask_as_deleted);
}
void scheduler::merge_callbacks()
{
callbacks_.access([&](task_list& tasks)
{
new_callbacks_.access([&](task_list& new_tasks)
{
tasks.insert(tasks.end(), std::move_iterator<task_list::iterator>(new_tasks.begin()),
std::move_iterator<task_list::iterator>(new_tasks.end()));
new_tasks = {};
});
});
}
}

View File

@ -1,50 +0,0 @@
#pragma once
#include <utils/concurrency.hpp>
namespace ui_scripting::lua
{
class context;
class task_handle
{
public:
uint64_t id = 0;
};
class task final : public task_handle
{
public:
std::chrono::steady_clock::time_point last_call{};
sol::protected_function callback{};
std::chrono::milliseconds delay{};
bool is_volatile = false;
bool is_deleted = false;
};
class scheduler final
{
public:
scheduler(sol::state& state);
scheduler(scheduler&&) noexcept = delete;
scheduler& operator=(scheduler&&) noexcept = delete;
scheduler(const scheduler&) = delete;
scheduler& operator=(const scheduler&) = delete;
void run_frame();
void clear();
task_handle add(const sol::protected_function& callback, long long milliseconds, bool is_volatile);
task_handle add(const sol::protected_function& callback, std::chrono::milliseconds delay, bool is_volatile);
private:
using task_list = std::vector<task>;
utils::concurrency::container<task_list> new_callbacks_;
utils::concurrency::container<task_list, std::recursive_mutex> callbacks_;
std::atomic_int64_t current_task_id_ = 0;
void remove(const task_handle& handle);
void merge_callbacks();
};
}

View File

@ -1,144 +0,0 @@
#include <std_include.hpp>
#include "value_conversion.hpp"
#include "../execution.hpp"
#include "../../../component/ui_scripting.hpp"
namespace ui_scripting::lua
{
namespace
{
table convert_table(const sol::table& t)
{
table res{};
t.for_each([res](const sol::object& key, const sol::object& value)
{
res.set(convert(key), convert(value));
});
return res;
}
script_value convert_function(const sol::protected_function& function)
{
const auto closure = game::hks::cclosure_Create(*game::hks::lua_state, main_function_handler, 0, 0, 0);
add_converted_function(closure, function);
game::hks::HksObject value{};
value.t = game::hks::TCFUNCTION;
value.v.cClosure = closure;
return value;
}
}
script_value convert(const sol::lua_value& value)
{
if (value.is<bool>())
{
return {value.as<bool>()};
}
if (value.is<int>())
{
return {value.as<int>()};
}
if (value.is<unsigned int>())
{
return {value.as<unsigned int>()};
}
if (value.is<double>())
{
return {value.as<double>()};
}
if (value.is<float>())
{
return {value.as<float>()};
}
if (value.is<std::string>())
{
return {value.as<std::string>()};
}
if (value.is<lightuserdata>())
{
return {value.as<lightuserdata>()};
}
if (value.is<userdata>())
{
return {value.as<userdata>()};
}
if (value.is<table>())
{
return {value.as<table>()};
}
if (value.is<function>())
{
return {value.as<function>()};
}
if (value.is<sol::table>())
{
return {convert_table(value.as<sol::table>())};
}
if (value.is<sol::protected_function>())
{
return {convert_function(value.as<sol::protected_function>())};
}
return {};
}
sol::lua_value convert(lua_State* state, const script_value& value)
{
if (value.is<int>())
{
return {state, value.as<int>()};
}
if (value.is<float>())
{
return {state, value.as<float>()};
}
if (value.is<bool>())
{
return {state, value.as<bool>()};
}
if (value.is<std::string>())
{
return {state, value.as<std::string>()};
}
if (value.is<lightuserdata>())
{
return {state, value.as<lightuserdata>()};
}
if (value.is<userdata>())
{
return {state, value.as<userdata>()};
}
if (value.is<table>())
{
return {state, value.as<table>()};
}
if (value.is<function>())
{
return {state, value.as<function>()};
}
return {state, sol::lua_nil};
}
}

View File

@ -1,9 +0,0 @@
#pragma once
#include "context.hpp"
#include "../script_value.hpp"
namespace ui_scripting::lua
{
script_value convert(const sol::lua_value& value);
sol::lua_value convert(lua_State* state, const script_value& value);
}

View File

@ -2,9 +2,79 @@
#include "execution.hpp" #include "execution.hpp"
#include "types.hpp" #include "types.hpp"
#include "script_value.hpp" #include "script_value.hpp"
#include "../../component/ui_scripting.hpp"
namespace ui_scripting namespace ui_scripting
{ {
hks_object::hks_object(const game::hks::HksObject& value)
{
this->assign(value);
}
hks_object::hks_object(const hks_object& other) noexcept
{
this->operator=(other);
}
hks_object::hks_object(hks_object&& other) noexcept
{
this->operator=(std::move(other));
}
hks_object& hks_object::operator=(const hks_object& other) noexcept
{
if (this != &other)
{
this->release();
this->assign(other.value_);
}
return *this;
}
hks_object& hks_object::operator=(hks_object&& other) noexcept
{
if (this != &other)
{
this->release();
this->value_ = other.value_;
other.value_.t = game::hks::TNONE;
}
return *this;
}
hks_object::~hks_object()
{
this->release();
}
const game::hks::HksObject& hks_object::get() const
{
return this->value_;
}
void hks_object::assign(const game::hks::HksObject& value)
{
this->value_ = value;
const auto state = *game::hks::lua_state;
const auto top = state->m_apistack.top;
push_value(this->value_);
this->ref_ = game::hks::hksi_luaL_ref(*game::hks::lua_state, -10000);
state->m_apistack.top = top;
}
void hks_object::release()
{
if (this->ref_)
{
game::hks::hksi_luaL_unref(*game::hks::lua_state, -10000, this->ref_);
this->value_.t = game::hks::TNONE;
}
}
/*************************************************************** /***************************************************************
* Constructors * Constructors
**************************************************************/ **************************************************************/
@ -32,6 +102,24 @@ namespace ui_scripting
this->value_ = obj; this->value_ = obj;
} }
script_value::script_value(const long long value)
{
game::hks::HksObject obj{};
obj.t = game::hks::TUI64;
obj.v.i64 = value;
this->value_ = obj;
}
script_value::script_value(const unsigned long long value)
{
game::hks::HksObject obj{};
obj.t = game::hks::TUI64;
obj.v.ui64 = value;
this->value_ = obj;
}
script_value::script_value(const bool value) script_value::script_value(const bool value)
{ {
game::hks::HksObject obj{}; game::hks::HksObject obj{};
@ -55,46 +143,68 @@ namespace ui_scripting
{ {
} }
script_value::script_value(const char* value) script_value::script_value(const char* value, const size_t len)
{ {
game::hks::HksObject obj{}; game::hks::HksObject obj{};
const auto state = *game::hks::lua_state; const auto state = *game::hks::lua_state;
state->m_apistack.top = state->m_apistack.base; if (state == nullptr)
{
return;
}
game::hks::hksi_lua_pushlstring(state, value, (unsigned int)strlen(value)); const auto top = state->m_apistack.top;
game::hks::hksi_lua_pushlstring(state, value, static_cast<unsigned int>(len));
obj = state->m_apistack.top[-1]; obj = state->m_apistack.top[-1];
state->m_apistack.top = top;
this->value_ = obj; this->value_ = obj;
} }
script_value::script_value(const char* value)
: script_value(value, strlen(value))
{
}
script_value::script_value(const std::string& value) script_value::script_value(const std::string& value)
: script_value(value.data()) : script_value(value.data(), value.size())
{ {
} }
script_value::script_value(const lightuserdata& value) script_value::script_value(const lightuserdata& value)
{ {
this->value_.t = game::hks::TLIGHTUSERDATA; game::hks::HksObject obj{};
this->value_.v.ptr = value.ptr; obj.t = game::hks::TLIGHTUSERDATA;
obj.v.ptr = value.ptr;
this->value_ = obj;
} }
script_value::script_value(const userdata& value) script_value::script_value(const userdata& value)
{ {
this->value_.t = game::hks::TUSERDATA; game::hks::HksObject obj{};
this->value_.v.ptr = value.ptr; obj.t = game::hks::TUSERDATA;
obj.v.ptr = value.ptr;
this->value_ = obj;
} }
script_value::script_value(const table& value) script_value::script_value(const table& value)
{ {
this->value_.t = game::hks::TTABLE; game::hks::HksObject obj{};
this->value_.v.ptr = value.ptr; obj.t = game::hks::TTABLE;
obj.v.ptr = value.ptr;
this->value_ = obj;
} }
script_value::script_value(const function& value) script_value::script_value(const function& value)
{ {
this->value_.t = value.type; game::hks::HksObject obj{};
this->value_.v.ptr = value.ptr; obj.t = value.type;
obj.v.ptr = value.ptr;
this->value_ = obj;
} }
/*************************************************************** /***************************************************************
@ -126,6 +236,34 @@ namespace ui_scripting
return static_cast<unsigned int>(this->get_raw().v.number); return static_cast<unsigned int>(this->get_raw().v.number);
} }
/***************************************************************
* Integer 64
**************************************************************/
template <>
bool script_value::is<long long>() const
{
return this->get_raw().t == game::hks::TUI64;
}
template <>
bool script_value::is<unsigned long long>() const
{
return this->is<long long>();
}
template <>
long long script_value::get() const
{
return static_cast<long long>(this->get_raw().v.ui64);
}
template <>
unsigned long long script_value::get() const
{
return static_cast<unsigned long long>(this->get_raw().v.ui64);
}
/*************************************************************** /***************************************************************
* Boolean * Boolean
**************************************************************/ **************************************************************/
@ -260,7 +398,7 @@ namespace ui_scripting
template <> template <>
function script_value::get() const function script_value::get() const
{ {
return { this->get_raw().v.cClosure, this->get_raw().t }; return {this->get_raw().v.cClosure, this->get_raw().t};
} }
/*************************************************************** /***************************************************************
@ -269,6 +407,43 @@ namespace ui_scripting
const game::hks::HksObject& script_value::get_raw() const const game::hks::HksObject& script_value::get_raw() const
{ {
return this->value_; return this->value_.get();
}
bool script_value::operator==(const script_value& other) const
{
if (this->get_raw().t != other.get_raw().t)
{
return false;
}
if (this->get_raw().t == game::hks::TSTRING)
{
return this->get<std::string>() == other.get<std::string>();
}
return this->get_raw().v.native == other.get_raw().v.native;
}
arguments script_value::operator()() const
{
return this->as<function>()();
}
arguments script_value::operator()(const arguments& arguments) const
{
return this->as<function>()(arguments);
}
function_argument::function_argument(const arguments& args, const script_value& value, const int index)
: values_(args)
, value_(value)
, index_(index)
{
}
function_arguments::function_arguments(const arguments& values)
: values_(values)
{
} }
} }

View File

@ -1,12 +1,92 @@
#pragma once #pragma once
#include "game/game.hpp" #include "game/game.hpp"
#include <utils/string.hpp>
namespace ui_scripting namespace ui_scripting
{ {
class lightuserdata; class lightuserdata;
class userdata_value;
class userdata; class userdata;
class table_value;
class table; class table;
class function; class function;
class script_value;
namespace
{
template <typename T>
std::string get_typename()
{
auto& info = typeid(T);
if (info == typeid(std::string) ||
info == typeid(const char*))
{
return "string";
}
if (info == typeid(lightuserdata))
{
return "lightuserdata";
}
if (info == typeid(userdata))
{
return "userdata";
}
if (info == typeid(table))
{
return "table";
}
if (info == typeid(function))
{
return "function";
}
if (info == typeid(int) ||
info == typeid(float) ||
info == typeid(unsigned int))
{
return "number";
}
if (info == typeid(bool))
{
return "boolean";
}
return info.name();
}
}
class hks_object
{
public:
hks_object() = default;
hks_object(const game::hks::HksObject& value);
hks_object(const hks_object& other) noexcept;
hks_object(hks_object&& other) noexcept;
hks_object& operator=(const hks_object& other) noexcept;
hks_object& operator=(hks_object&& other) noexcept;
~hks_object();
const game::hks::HksObject& get() const;
private:
void assign(const game::hks::HksObject& value);
void release();
game::hks::HksObject value_{game::hks::TNONE, {}};
int ref_{};
};
using arguments = std::vector<script_value>;
using event_arguments = std::unordered_map<std::string, script_value>;
class script_value class script_value
{ {
@ -16,12 +96,15 @@ namespace ui_scripting
script_value(int value); script_value(int value);
script_value(unsigned int value); script_value(unsigned int value);
script_value(long long value);
script_value(unsigned long long value);
script_value(bool value); script_value(bool value);
script_value(float value); script_value(float value);
script_value(double value); script_value(double value);
script_value(const char* value); script_value(const char* value);
script_value(const char* value, const size_t len);
script_value(const std::string& value); script_value(const std::string& value);
script_value(const lightuserdata& value); script_value(const lightuserdata& value);
@ -29,6 +112,53 @@ namespace ui_scripting
script_value(const table& value); script_value(const table& value);
script_value(const function& value); script_value(const function& value);
template <template<class, class> class C, class T, typename TableType = table>
script_value(const C<T, std::allocator<T>>& container)
{
TableType table_{};
int index = 1;
for (const auto& value : container)
{
table_.set(index++, value);
}
game::hks::HksObject obj{};
obj.t = game::hks::TTABLE;
obj.v.ptr = table_.ptr;
this->value_ = obj;
}
template <typename F>
script_value(F f)
: script_value(function(f))
{
}
bool operator==(const script_value& other) const;
arguments operator()() const;
arguments operator()(const arguments& arguments) const;
template<class ...T>
arguments operator()(T... arguments) const
{
return this->as<function>().call({arguments...});
}
template <size_t Size>
table_value operator[](const char(&key)[Size]) const
{
return {this->as<table>(), key};
}
template <typename T = script_value>
table_value operator[](const T& key) const
{
return {this->as<table>(), key};
}
template <typename T> template <typename T>
bool is() const; bool is() const;
@ -37,21 +167,93 @@ namespace ui_scripting
{ {
if (!this->is<T>()) if (!this->is<T>())
{ {
throw std::runtime_error("Invalid type"); const auto hks_typename = game::hks::typenames[this->get_raw().t + 2];
const auto typename_ = get_typename<T>();
throw std::runtime_error(utils::string::va("%s expected, got %s",
typename_.data(), hks_typename));
} }
return get<T>(); return get<T>();
} }
template <typename T>
operator T() const
{
return this->as<T>();
}
const game::hks::HksObject& get_raw() const; const game::hks::HksObject& get_raw() const;
hks_object value_{};
private: private:
template <typename T> template <typename T>
T get() const; T get() const;
game::hks::HksObject value_{};
}; };
using arguments = std::vector<script_value>; class variadic_args : public arguments
using event_arguments = std::unordered_map<std::string, script_value>; {
};
class function_argument
{
public:
function_argument(const arguments& args, const script_value& value, const int index);
template <typename T>
T as() const
{
try
{
return this->value_.as<T>();
}
catch (const std::exception& e)
{
throw std::runtime_error(utils::string::va("bad argument #%d (%s)",
this->index_ + 1, e.what()));
}
}
template <>
variadic_args as() const
{
variadic_args args{};
for (auto i = this->index_; i < this->values_.size(); i++)
{
args.push_back(this->values_[i]);
}
return args;
}
template <typename T>
operator T() const
{
return this->as<T>();
}
private:
arguments values_{};
script_value value_{};
int index_{};
};
class function_arguments
{
public:
function_arguments(const arguments& values);
function_argument operator[](const int index) const
{
if (index >= values_.size())
{
return {values_, {}, index};
}
return {values_, values_[index], index};
}
private:
arguments values_{};
};
} }

View File

@ -1,350 +1,355 @@
#include <std_include.hpp> #include <std_include.hpp>
#include "types.hpp" #include "types.hpp"
#include "execution.hpp" #include "execution.hpp"
#include "../../component/ui_scripting.hpp"
namespace ui_scripting namespace ui_scripting
{ {
/*************************************************************** /***************************************************************
* Lightuserdata * Lightuserdata
**************************************************************/
lightuserdata::lightuserdata(void* ptr_)
: ptr(ptr_)
{
}
bool lightuserdata::operator==(const lightuserdata& other)
{
return this->ptr == other.ptr;
}
bool lightuserdata::operator!=(const lightuserdata& other)
{
return this->ptr != other.ptr;
}
/***************************************************************
* Userdata
**************************************************************/
userdata::userdata(void* ptr_)
: ptr(ptr_)
{
this->add();
}
userdata::userdata(const userdata& other)
{
this->operator=(other);
}
userdata::userdata(userdata&& other) noexcept
{
this->ptr = other.ptr;
this->ref = other.ref;
other.ref = 0;
}
userdata::~userdata()
{
this->release();
}
userdata& userdata::operator=(const userdata& other)
{
if (&other != this)
{
this->release();
this->ptr = other.ptr;
this->ref = other.ref;
this->add();
}
return *this;
}
userdata& userdata::operator=(userdata&& other) noexcept
{
if (&other != this)
{
this->release();
this->ptr = other.ptr;
this->ref = other.ref;
other.ref = 0;
}
return *this;
}
bool userdata::operator==(const userdata& other)
{
return this->ptr == other.ptr;
}
bool userdata::operator!=(const userdata& other)
{
return this->ptr != other.ptr;
}
void userdata::add()
{
game::hks::HksObject value{};
value.v.ptr = this->ptr;
value.t = game::hks::TUSERDATA;
const auto state = *game::hks::lua_state;
state->m_apistack.top = state->m_apistack.base;
push_value(value);
this->ref = game::hks::hksi_luaL_ref(*game::hks::lua_state, -10000);
}
void userdata::release()
{
if (this->ref)
{
game::hks::hksi_luaL_unref(*game::hks::lua_state, -10000, this->ref);
}
}
void userdata::set(const script_value& key, const script_value& value) const
{
set_field(*this, key, value);
}
script_value userdata::get(const script_value& key) const
{
return get_field(*this, key);
}
/***************************************************************
* Table
**************************************************************/
table::table()
{
const auto state = *game::hks::lua_state;
this->ptr = game::hks::Hashtable_Create(state, 0, 0);
this->add();
}
table::table(game::hks::HashTable* ptr_)
: ptr(ptr_)
{
this->add();
}
table::table(const table& other)
{
this->operator=(other);
}
table::table(table&& other) noexcept
{
this->ptr = other.ptr;
this->ref = other.ref;
other.ref = 0;
}
table::~table()
{
this->release();
}
table& table::operator=(const table& other)
{
if (&other != this)
{
this->release();
this->ptr = other.ptr;
this->ref = other.ref;
this->add();
}
return *this;
}
table& table::operator=(table&& other) noexcept
{
if (&other != this)
{
this->release();
this->ptr = other.ptr;
this->ref = other.ref;
other.ref = 0;
}
return *this;
}
bool table::operator==(const table& other)
{
return this->ptr == other.ptr;
}
bool table::operator!=(const table& other)
{
return this->ptr != other.ptr;
}
void table::add()
{
game::hks::HksObject value{};
value.v.table = this->ptr;
value.t = game::hks::TTABLE;
const auto state = *game::hks::lua_state;
state->m_apistack.top = state->m_apistack.base;
push_value(value);
this->ref = game::hks::hksi_luaL_ref(*game::hks::lua_state, -10000);
}
void table::release()
{
if (this->ref)
{
game::hks::hksi_luaL_unref(*game::hks::lua_state, -10000, this->ref);
}
}
void table::set(const script_value& key, const script_value& value) const
{
set_field(*this, key, value);
}
script_value table::get(const script_value& key) const
{
return get_field(*this, key);
}
/***************************************************************
* Function
**************************************************************/
function::function(game::hks::cclosure* ptr_, game::hks::HksObjectType type_)
: ptr(ptr_)
, type(type_)
{
this->add();
}
function::function(const function& other)
{
this->operator=(other);
}
function::function(function&& other) noexcept
{
this->ptr = other.ptr;
this->type = other.type;
this->ref = other.ref;
other.ref = 0;
}
function::~function()
{
this->release();
}
function& function::operator=(const function& other)
{
if (&other != this)
{
this->release();
this->ptr = other.ptr;
this->type = other.type;
this->ref = other.ref;
this->add();
}
return *this;
}
function& function::operator=(function&& other) noexcept
{
if (&other != this)
{
this->release();
this->ptr = other.ptr;
this->type = other.type;
this->ref = other.ref;
other.ref = 0;
}
return *this;
}
bool function::operator==(const function& other)
{
return this->ptr == other.ptr;
}
bool function::operator!=(const function& other)
{
return this->ptr != other.ptr;
}
void function::add()
{
game::hks::HksObject value{};
value.v.cClosure = this->ptr;
value.t = this->type;
const auto state = *game::hks::lua_state;
state->m_apistack.top = state->m_apistack.base;
push_value(value);
this->ref = game::hks::hksi_luaL_ref(*game::hks::lua_state, -10000);
}
void function::release()
{
if (this->ref)
{
game::hks::hksi_luaL_unref(*game::hks::lua_state, -10000, this->ref);
}
}
arguments function::call(const arguments& arguments) const
{
return call_script_function(*this, arguments);
}
/***************************************************************
* Stack
**************************************************************/ **************************************************************/
stack::stack() lightuserdata::lightuserdata(void* ptr_)
: ptr(ptr_)
{ {
this->state = *game::hks::lua_state;
this->state->m_apistack.top = this->state->m_apistack.base;
} }
void stack::save(int num_args) /***************************************************************
* Userdata
**************************************************************/
userdata::userdata(void* ptr_)
: ptr(ptr_)
{ {
this->num_args_ = num_args; this->add();
this->num_calls_ = state->m_numberOfCCalls;
this->base_bottom_ = state->m_apistack.base - state->m_apistack.bottom;
this->top_bottom_ = state->m_apistack.top - state->m_apistack.bottom;
this->callstack_ = state->m_callStack.m_current - state->m_callStack.m_records;
} }
void stack::fix() userdata::userdata(const userdata& other)
{ {
this->state->m_numberOfCCalls = this->num_calls_; this->operator=(other);
}
game::hks::closePendingUpvalues(this->state, &this->state->m_apistack.bottom[this->top_bottom_ - this->num_args_]); userdata::userdata(userdata&& other) noexcept
this->state->m_callStack.m_current = &this->state->m_callStack.m_records[this->callstack_]; {
this->ptr = other.ptr;
this->ref = other.ref;
other.ref = 0;
}
this->state->m_apistack.base = &this->state->m_apistack.bottom[this->base_bottom_]; userdata::~userdata()
this->state->m_apistack.top = &this->state->m_apistack.bottom[this->top_bottom_ - static_cast<uint64_t>(this->num_args_ + 1)]; {
this->release();
}
this->state->m_apistack.bottom[this->top_bottom_].t = this->state->m_apistack.top[-1].t; userdata& userdata::operator=(const userdata& other)
this->state->m_apistack.bottom[this->top_bottom_].v.ptr = this->state->m_apistack.top[-1].v.ptr; {
this->state->m_apistack.top = &this->state->m_apistack.bottom[this->top_bottom_ + 1]; if (&other != this)
{
this->release();
this->ptr = other.ptr;
this->ref = other.ref;
this->add();
}
return *this;
}
userdata& userdata::operator=(userdata&& other) noexcept
{
if (&other != this)
{
this->release();
this->ptr = other.ptr;
this->ref = other.ref;
other.ref = 0;
}
return *this;
}
void userdata::add()
{
game::hks::HksObject value{};
value.v.ptr = this->ptr;
value.t = game::hks::TUSERDATA;
const auto state = *game::hks::lua_state;
const auto top = state->m_apistack.top;
push_value(value);
this->ref = game::hks::hksi_luaL_ref(*game::hks::lua_state, -10000);
state->m_apistack.top = top;
}
void userdata::release()
{
if (this->ref)
{
game::hks::hksi_luaL_unref(*game::hks::lua_state, -10000, this->ref);
}
}
void userdata::set(const script_value& key, const script_value& value) const
{
set_field(*this, key, value);
}
script_value userdata::get(const script_value& key) const
{
return get_field(*this, key);
}
userdata_value userdata::operator[](const script_value& key) const
{
return {*this, key};
}
userdata_value::userdata_value(const userdata& table, const script_value& key)
: userdata_(table)
, key_(key)
{
this->value_ = this->userdata_.get(key).get_raw();
}
void userdata_value::operator=(const script_value& value)
{
this->userdata_.set(this->key_, value);
this->value_ = value.get_raw();
}
bool userdata_value::operator==(const script_value& value)
{
return this->userdata_.get(this->key_) == value;
}
/***************************************************************
* Table
**************************************************************/
table::table()
{
const auto state = *game::hks::lua_state;
this->ptr = game::hks::Hashtable_Create(state, 0, 0);
this->add();
}
table::table(game::hks::HashTable* ptr_)
: ptr(ptr_)
{
this->add();
}
table::table(const table& other)
{
this->operator=(other);
}
table::table(table&& other) noexcept
{
this->ptr = other.ptr;
this->ref = other.ref;
other.ref = 0;
}
table::~table()
{
this->release();
}
table& table::operator=(const table& other)
{
if (&other != this)
{
this->release();
this->ptr = other.ptr;
this->ref = other.ref;
this->add();
}
return *this;
}
table& table::operator=(table&& other) noexcept
{
if (&other != this)
{
this->release();
this->ptr = other.ptr;
this->ref = other.ref;
other.ref = 0;
}
return *this;
}
void table::add()
{
game::hks::HksObject value{};
value.v.table = this->ptr;
value.t = game::hks::TTABLE;
const auto state = *game::hks::lua_state;
const auto top = state->m_apistack.top;
push_value(value);
this->ref = game::hks::hksi_luaL_ref(*game::hks::lua_state, -10000);
state->m_apistack.top = top;
}
void table::release()
{
if (this->ref)
{
game::hks::hksi_luaL_unref(*game::hks::lua_state, -10000, this->ref);
}
}
void table::set(const script_value& key, const script_value& value) const
{
set_field(*this, key, value);
}
table_value table::operator[](const script_value& key) const
{
return {*this, key};
}
script_value table::get(const script_value& key) const
{
return get_field(*this, key);
}
table_value::table_value(const table& table, const script_value& key)
: table_(table)
, key_(key)
{
this->value_ = this->table_.get(key).get_raw();
}
void table_value::operator=(const script_value& value)
{
this->table_.set(this->key_, value);
this->value_ = value.get_raw();
}
void table_value::operator=(const table_value& value)
{
this->table_.set(this->key_, value);
this->value_ = value.get_raw();
}
bool table_value::operator==(const script_value& value)
{
return this->table_.get(this->key_) == value;
}
bool table_value::operator==(const table_value& value)
{
return this->table_.get(this->key_) == value;
}
/***************************************************************
* Function
**************************************************************/
function::function(game::hks::lua_function func)
{
const auto state = *game::hks::lua_state;
this->ptr = game::hks::cclosure_Create(state, func, 0, 0, 0);
this->type = game::hks::HksObjectType::TCFUNCTION;
this->add();
}
function::function(game::hks::cclosure* ptr_, game::hks::HksObjectType type_)
: ptr(ptr_)
, type(type_)
{
this->add();
}
function::function(const function& other)
{
this->operator=(other);
}
function::function(function&& other) noexcept
{
this->ptr = other.ptr;
this->type = other.type;
this->ref = other.ref;
other.ref = 0;
}
function::~function()
{
this->release();
}
function& function::operator=(const function& other)
{
if (&other != this)
{
this->release();
this->ptr = other.ptr;
this->type = other.type;
this->ref = other.ref;
this->add();
}
return *this;
}
function& function::operator=(function&& other) noexcept
{
if (&other != this)
{
this->release();
this->ptr = other.ptr;
this->type = other.type;
this->ref = other.ref;
other.ref = 0;
}
return *this;
}
void function::add()
{
game::hks::HksObject value{};
value.v.cClosure = this->ptr;
value.t = this->type;
const auto state = *game::hks::lua_state;
const auto top = state->m_apistack.top;
push_value(value);
this->ref = game::hks::hksi_luaL_ref(*game::hks::lua_state, -10000);
state->m_apistack.top = top;
}
void function::release()
{
if (this->ref)
{
game::hks::hksi_luaL_unref(*game::hks::lua_state, -10000, this->ref);
}
}
arguments function::call(const arguments& arguments) const
{
return call_script_function(*this, arguments);
}
arguments function::operator()(const arguments& arguments) const
{
return this->call(arguments);
}
arguments function::operator()() const
{
return this->call({});
} }
} }

View File

@ -1,126 +1,140 @@
#pragma once #pragma once
#include "game/game.hpp" #include "game/game.hpp"
#include "script_value.hpp" #include "script_value.hpp"
#include "../../component/ui_scripting.hpp"
namespace ui_scripting namespace ui_scripting
{ {
class lightuserdata class lightuserdata
{ {
public: public:
lightuserdata(void*); lightuserdata(void*);
void* ptr;
};
bool operator==(const lightuserdata& other); class userdata_value;
bool operator!=(const lightuserdata& other);
void* ptr; class userdata
}; {
public:
userdata(void*);
class userdata userdata(const userdata& other);
{ userdata(userdata&& other) noexcept;
public:
userdata(void*);
userdata(const userdata& other); ~userdata();
userdata(userdata&& other) noexcept;
~userdata(); userdata& operator=(const userdata& other);
userdata& operator=(userdata&& other) noexcept;
userdata& operator=(const userdata& other); script_value get(const script_value& key) const;
userdata& operator=(userdata&& other) noexcept; void set(const script_value& key, const script_value& value) const;
bool operator==(const userdata& other); userdata_value operator[](const script_value& key) const;
bool operator!=(const userdata& other);
script_value get(const script_value& key) const; void* ptr;
void set(const script_value& key, const script_value& value) const;
void* ptr; private:
void add();
void release();
private: int ref{};
void add(); };
void release();
int ref{}; class userdata_value : public script_value
}; {
public:
userdata_value(const userdata& table, const script_value& key);
void operator=(const script_value& value);
bool operator==(const script_value& value);
private:
userdata userdata_;
script_value key_;
};
class table class table_value;
{
public:
table();
table(game::hks::HashTable* ptr_);
table(const table& other); class table
table(table&& other) noexcept; {
public:
table();
table(game::hks::HashTable* ptr_);
~table(); table(const table& other);
table(table&& other) noexcept;
table& operator=(const table& other); ~table();
table& operator=(table&& other) noexcept;
bool operator==(const table& other); table& operator=(const table& other);
bool operator!=(const table& other); table& operator=(table&& other) noexcept;
script_value get(const script_value& key) const; script_value get(const script_value& key) const;
void set(const script_value& key, const script_value& value) const; void set(const script_value& key, const script_value& value) const;
game::hks::HashTable* ptr; table_value operator[](const script_value& key) const;
private: game::hks::HashTable* ptr;
void add();
void release();
int ref{}; private:
}; void add();
void release();
class function int ref{};
{ };
public:
function(game::hks::cclosure*, game::hks::HksObjectType);
function(const function& other); class table_value : public script_value
function(function&& other) noexcept; {
public:
table_value(const table& table, const script_value& key);
void operator=(const script_value& value);
void operator=(const table_value& value);
bool operator==(const script_value& value);
bool operator==(const table_value& value);
private:
table table_;
script_value key_;
};
~function(); class function
{
public:
function(game::hks::lua_function);
function(game::hks::cclosure*, game::hks::HksObjectType);
function& operator=(const function& other); template <typename F>
function& operator=(function&& other) noexcept; function(F f)
{
this->ptr = ui_scripting::convert_function(f);
this->type = game::hks::TCFUNCTION;
}
bool operator==(const function& other); function(const function& other);
bool operator!=(const function& other); function(function&& other) noexcept;
arguments call(const arguments& arguments) const; ~function();
game::hks::cclosure* ptr; function& operator=(const function& other);
game::hks::HksObjectType type; function& operator=(function&& other) noexcept;
private: arguments call(const arguments& arguments) const;
void add();
void release();
int ref{}; arguments operator()(const arguments& arguments) const;
};
class stack final template<class ...T>
{ arguments operator()(T... arguments) const
public: {
stack(); return this->call({arguments...});
}
void save(int num_args); arguments operator()() const;
void fix();
stack(stack&&) = delete; game::hks::cclosure* ptr;
stack(const stack&) = delete; game::hks::HksObjectType type;
stack& operator=(stack&&) = delete;
stack& operator=(const stack&) = delete;
private: private:
game::hks::lua_State* state; void add();
void release();
int num_args_; int ref{};
int num_calls_; };
uint64_t base_bottom_;
uint64_t top_bottom_;
uint64_t callstack_;
};
} }

View File

@ -31,7 +31,7 @@ FARPROC loader::load(const utils::nt::library& library, const std::string& buffe
return FARPROC(library.get_ptr() + source.get_relative_entry_point()); return FARPROC(library.get_ptr() + source.get_relative_entry_point());
} }
FARPROC loader::load_library(const std::string& filename) const FARPROC loader::load_library(const std::string& filename, uint64_t* base_address) const
{ {
const auto target = utils::nt::library::load(filename); const auto target = utils::nt::library::load(filename);
if (!target) if (!target)
@ -40,10 +40,7 @@ FARPROC loader::load_library(const std::string& filename) const
} }
const auto base = size_t(target.get_ptr()); const auto base = size_t(target.get_ptr());
if(base != 0x140000000) *base_address = base;
{
throw std::runtime_error{utils::string::va("Binary was mapped at 0x%llX (instead of 0x%llX). Something is severely broken :(", base, 0x140000000)};
}
this->load_imports(target, target); this->load_imports(target, target);
this->load_tls(target, target); this->load_tls(target, target);

View File

@ -5,7 +5,7 @@ class loader final
{ {
public: public:
FARPROC load(const utils::nt::library& library, const std::string& buffer) const; FARPROC load(const utils::nt::library& library, const std::string& buffer) const;
FARPROC load_library(const std::string& filename) const; FARPROC load_library(const std::string& filename, uint64_t* base_address) const;
void set_import_resolver(const std::function<void*(const std::string&, const std::string&)>& resolver); void set_import_resolver(const std::function<void*(const std::string&, const std::string&)>& resolver);

View File

@ -20,16 +20,6 @@ BOOL WINAPI system_parameters_info_a(const UINT uiAction, const UINT uiParam, co
return SystemParametersInfoA(uiAction, uiParam, pvParam, fWinIni); return SystemParametersInfoA(uiAction, uiParam, pvParam, fWinIni);
} }
FARPROC WINAPI get_proc_address(const HMODULE hModule, const LPCSTR lpProcName)
{
if (lpProcName == "GlobalMemoryStatusEx"s)
{
component_loader::post_unpack();
}
return GetProcAddress(hModule, lpProcName);
}
launcher::mode detect_mode_from_arguments() launcher::mode detect_mode_from_arguments()
{ {
if (utils::flags::has_flag("dedicated")) if (utils::flags::has_flag("dedicated"))
@ -50,14 +40,52 @@ launcher::mode detect_mode_from_arguments()
return launcher::mode::none; return launcher::mode::none;
} }
FARPROC load_binary(const launcher::mode mode) bool apply_aslr_patch(std::string* data)
{
// mp binary, sp binary
if (data->size() != 0x1B97788 && data->size() != 0x1346D88)
{
return false;
}
auto* dos_header = reinterpret_cast<PIMAGE_DOS_HEADER>(&data->at(0));
auto* nt_headers = reinterpret_cast<PIMAGE_NT_HEADERS>(&data->at(dos_header->e_lfanew));
auto* optional_header = &nt_headers->OptionalHeader;
if (optional_header->DllCharacteristics & IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE)
{
optional_header->DllCharacteristics &= ~(IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE);
}
return true;
}
void get_aslr_patched_binary(std::string* binary, std::string* data)
{
std::string patched_binary = "h1-mod\\" + *binary;
if (!apply_aslr_patch(data) ||
(!utils::io::file_exists(patched_binary) &&
!utils::io::write_file(patched_binary, *data, false)))
{
throw std::runtime_error(utils::string::va(
"Could not create aslr patched binary!\n(%s)",
binary->data()
));
}
*binary = patched_binary;
}
FARPROC load_binary(const launcher::mode mode, uint64_t* base_address)
{ {
loader loader; loader loader;
utils::nt::library self; utils::nt::library self;
loader.set_import_resolver([self](const std::string& library, const std::string& function) -> void* loader.set_import_resolver([self](const std::string& library, const std::string& function) -> void*
{ {
if (library == "steam_api64.dll") if (library == "steam_api64.dll"
&& function != "SteamAPI_GetSteamInstallPath") // Arxan requires one valid steam api import - maybe SteamAPI_Shutdown is better?
{ {
return self.get_proc<FARPROC>(function); return self.get_proc<FARPROC>(function);
} }
@ -69,10 +97,6 @@ FARPROC load_binary(const launcher::mode mode)
{ {
return system_parameters_info_a; return system_parameters_info_a;
} }
else if (function == "GetProcAddress")
{
return get_proc_address;
}
return component_loader::load_import(library, function); return component_loader::load_import(library, function);
}); });
@ -100,7 +124,14 @@ FARPROC load_binary(const launcher::mode mode)
binary.data())); binary.data()));
} }
return loader.load_library(binary); get_aslr_patched_binary(&binary, &data);
#ifdef INJECT_HOST_AS_LIB
return loader.load_library(binary, base_address);
#else
*base_address = 0x140000000;
return loader.load(self, data);
#endif
} }
void remove_crash_file() void remove_crash_file()
@ -158,6 +189,8 @@ void apply_proper_directory()
int main() int main()
{ {
ShowWindow(GetConsoleWindow(), SW_HIDE);
FARPROC entry_point; FARPROC entry_point;
enable_dpi_awareness(); enable_dpi_awareness();
@ -166,6 +199,7 @@ int main()
limit_parallel_dll_loading(); limit_parallel_dll_loading();
srand(uint32_t(time(nullptr))); srand(uint32_t(time(nullptr)));
remove_crash_file();
{ {
auto premature_shutdown = true; auto premature_shutdown = true;
@ -179,8 +213,7 @@ int main()
try try
{ {
apply_proper_directory(); //apply_proper_directory();
remove_crash_file();
if (!component_loader::post_start()) return 0; if (!component_loader::post_start()) return 0;
@ -194,12 +227,15 @@ int main()
game::environment::set_mode(mode); game::environment::set_mode(mode);
entry_point = load_binary(mode); uint64_t base_address{};
entry_point = load_binary(mode, &base_address);
if (!entry_point) if (!entry_point)
{ {
throw std::runtime_error("Unable to load binary into memory"); throw std::runtime_error("Unable to load binary into memory");
} }
game::base_address = base_address;
if (!component_loader::post_load()) return 0; if (!component_loader::post_load()) return 0;
premature_shutdown = false; premature_shutdown = false;

View File

@ -22,7 +22,7 @@
-- SOFTWARE. -- SOFTWARE.
-- --
local json = { _version = "0.1.2" } json = { _version = "0.1.2" }
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
-- Encode -- Encode

View File

@ -1,6 +1,30 @@
menucallbacks = {} local menucallbacks = {}
originalmenus = {} local originalmenus = {}
stack = {} local stack = {}
function getchildren(element)
local children = {}
local first = element:getFirstChild()
while (first) do
table.insert(children, first)
first = first:getNextSibling()
end
return children
end
function printchildtree(element, indent, last)
indent = indent or ""
print(indent .. "+- " .. element.id .. " " .. (element:getText() or ""))
indent = indent .. (last and " " or "| ")
local children = getchildren(element)
for i = 1, #children do
printchildtree(children[i], indent, i == #children)
end
end
LUI.MenuBuilder.m_types_build["generic_waiting_popup_"] = function (menu, event) LUI.MenuBuilder.m_types_build["generic_waiting_popup_"] = function (menu, event)
local oncancel = stack.oncancel local oncancel = stack.oncancel
@ -14,9 +38,7 @@ LUI.MenuBuilder.m_types_build["generic_waiting_popup_"] = function (menu, event)
end end
}) })
local listchildren = popup:getChildById("LUIHorizontalList"):getchildren() popup.text = popup:getLastChild():getPreviousSibling():getPreviousSibling()
local children = listchildren[2]:getchildren()
popup.text = children[2]
stack = { stack = {
ret = popup ret = popup
@ -76,10 +98,10 @@ LUI.onmenuopen = function(name, callback)
originalmenus[name] = LUI.MenuBuilder.m_types_build[name] originalmenus[name] = LUI.MenuBuilder.m_types_build[name]
LUI.MenuBuilder.m_types_build[name] = function(...) LUI.MenuBuilder.m_types_build[name] = function(...)
local args = {...} local args = {...}
local menu = originalmenus[name](table.unpack(args)) local menu = originalmenus[name](unpack(args))
for k, v in luiglobals.next, menucallbacks[name] do for k, v in next, menucallbacks[name] do
v(menu, table.unpack(args)) v(menu, unpack(args))
end end
return menu return menu
@ -136,7 +158,7 @@ LUI.openpopupmenu = function(menu, args)
end end
LUI.yesnopopup = function(data) LUI.yesnopopup = function(data)
for k, v in luiglobals.next, data do for k, v in next, data do
stack[k] = v stack[k] = v
end end
LUI.FlowManager.RequestPopupMenu(nil, "generic_yes_no_popup_") LUI.FlowManager.RequestPopupMenu(nil, "generic_yes_no_popup_")
@ -144,21 +166,9 @@ LUI.yesnopopup = function(data)
end end
LUI.confirmationpopup = function(data) LUI.confirmationpopup = function(data)
for k, v in luiglobals.next, data do for k, v in next, data do
stack[k] = v stack[k] = v
end end
LUI.FlowManager.RequestPopupMenu(nil, "generic_confirmation_popup_") LUI.FlowManager.RequestPopupMenu(nil, "generic_confirmation_popup_")
return stack.ret return stack.ret
end end
function userdata_:getchildren()
local children = {}
local first = self:getFirstChild()
while (first) do
table.insert(children, first)
first = first:getNextSibling()
end
return children
end

View File

@ -1,6 +1,23 @@
#include <std_include.hpp> #include <std_include.hpp>
#pragma comment(linker, "/stack:0x1000000")
#ifdef INJECT_HOST_AS_LIB
#pragma comment(linker, "/base:0x160000000") #pragma comment(linker, "/base:0x160000000")
#else
#pragma comment(linker, "/base:0x140000000")
#pragma comment(linker, "/merge:.data=.cld")
#pragma comment(linker, "/merge:.rdata=.clr")
#pragma comment(linker, "/merge:.cl=.main")
#pragma comment(linker, "/merge:.text=.main")
#endif
#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='amd64' publicKeyToken='6595b64144ccf1df' language=''\"")
#ifndef INJECT_HOST_AS_LIB
#pragma bss_seg(".payload")
char payload_data[BINARY_PAYLOAD_SIZE];
#endif
extern "C" extern "C"
{ {

View File

@ -1,6 +1,9 @@
#pragma once #pragma once
#define BINARY_PAYLOAD_SIZE 0x12000000 #define BINARY_PAYLOAD_SIZE 0x14000000
// Decide whether to load the game as lib or to inject it
#define INJECT_HOST_AS_LIB
#pragma warning(push) #pragma warning(push)
#pragma warning(disable: 4100) #pragma warning(disable: 4100)

View File

@ -50,7 +50,7 @@ namespace steam
unsigned int utils::GetAppID() unsigned int utils::GetAppID()
{ {
return 209660; return 393100;
} }
void utils::SetOverlayNotificationPosition(int eNotificationPosition) void utils::SetOverlayNotificationPosition(int eNotificationPosition)

View File

@ -1,6 +1,8 @@
#include <std_include.hpp> #include <std_include.hpp>
#include "steam.hpp" #include "steam.hpp"
#include <utils/nt.hpp>
namespace steam namespace steam
{ {
uint64_t callbacks::call_id_ = 0; uint64_t callbacks::call_id_ = 0;
@ -82,13 +84,13 @@ namespace steam
result_handlers_[result.call]->run(result.data, false, result.call); result_handlers_[result.call]->run(result.data, false, result.call);
} }
for (const auto& callback : callback_list_) /*for (const auto& callback : callback_list_)
{ {
if (callback && callback->get_i_callback() == result.type) if (callback && callback->get_i_callback() == result.type)
{ {
callback->run(result.data, false, 0); callback->run(result.data, false, 0);
} }
} }*/
if (result.data) if (result.data)
{ {
@ -108,6 +110,13 @@ namespace steam
bool SteamAPI_Init() bool SteamAPI_Init()
{ {
const std::filesystem::path steam_path = steam::SteamAPI_GetSteamInstallPath();
if (steam_path.empty()) return true;
::utils::nt::library::load(steam_path / "tier0_s64.dll");
::utils::nt::library::load(steam_path / "vstdlib_s64.dll");
::utils::nt::library::load(steam_path / "gameoverlayrenderer64.dll");
::utils::nt::library::load(steam_path / "steamclient64.dll");
return true; return true;
} }
@ -235,4 +244,4 @@ namespace steam
} }
} }
} }