Merge pull request #237 from fedddddd/develop

Release v2.0.4
This commit is contained in:
fed 2022-03-30 12:12:46 +00:00 committed by GitHub
commit c344037c11
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
83 changed files with 2120 additions and 1462 deletions

3
.gitmodules vendored
View File

@ -22,9 +22,6 @@
[submodule "deps/asmjit"] [submodule "deps/asmjit"]
path = deps/asmjit path = deps/asmjit
url = https://github.com/asmjit/asmjit.git url = https://github.com/asmjit/asmjit.git
[submodule "deps/protobuf"]
path = deps/protobuf
url = https://github.com/google/protobuf.git
[submodule "deps/udis86"] [submodule "deps/udis86"]
path = deps/udis86 path = deps/udis86
url = https://github.com/vmt/udis86.git url = https://github.com/vmt/udis86.git

View File

@ -2,7 +2,7 @@
[![open bugs](https://img.shields.io/github/issues/fedddddd/h2-mod/bug?label=bugs)](https://github.com/fedddddd/h2-mod/issues?q=is%3Aissue+is%3Aopen+label%3Abug) [![open bugs](https://img.shields.io/github/issues/fedddddd/h2-mod/bug?label=bugs)](https://github.com/fedddddd/h2-mod/issues?q=is%3Aissue+is%3Aopen+label%3Abug)
[![Build](https://github.com/fedddddd/h2-mod/workflows/Build/badge.svg)](https://github.com/fedddddd/h2-mod/actions) [![Build](https://github.com/fedddddd/h2-mod/workflows/Build/badge.svg)](https://github.com/fedddddd/h2-mod/actions)
[![Build status](https://ci.appveyor.com/api/projects/status/0sh80kdnsvm53rno?svg=true)](https://ci.appveyor.com/project/fedddddd/h2-mod) [![Build status](https://ci.appveyor.com/api/projects/status/0sh80kdnsvm53rno?svg=true)](https://ci.appveyor.com/project/fedddddd/h2-mod)
[![Discord](https://img.shields.io/discord/955362057581129738?color=%237289DA&label=members&logo=discord&logoColor=%23FFFFFF)](https://discord.gg/dpnRn2tKT9)
# H2: Mod # H2: Mod
<p align="center"> <p align="center">
@ -15,7 +15,7 @@
**NOTE**: Cracked/Pirated versions of the game are NOT compatible with this mod, if you run such a version and have issues/crashes launching the client refer to [this issue](https://github.com/fedddddd/h2-mod/issues/111). **NOTE**: Cracked/Pirated versions of the game are NOT compatible with this mod, if you run such a version and have issues/crashes launching the client refer to [this issue](https://github.com/fedddddd/h2-mod/issues/111).
- **[Click here to get the latest release](https://ci.appveyor.com/api/projects/fedddddd/h2-mod/artifacts/build%2Fbin%2Fx64%2FRelease%2Fh2-mod.exe?branch=develop&job=Environment%3A%20APPVEYOR_BUILD_WORKER_IMAGE%3DVisual%20Studio%202019%2C%20PREMAKE_ACTION%3Dvs2019%2C%20CI%3D1%3B%20Configuration%3A%20Release)** - **[Click here to get the latest release](https://ci.appveyor.com/api/projects/fedddddd/h2-mod/artifacts/build%2Fbin%2Fx64%2FRelease%2Fh2-mod.exe?branch=develop&job=Environment%3A%20APPVEYOR_BUILD_WORKER_IMAGE%3DVisual%20Studio%202022%2C%20PREMAKE_ACTION%3Dvs2022%2C%20CI%3D1%3B%20Configuration%3A%20Release)**
- **You will need to drop this in your Call of Duty: Modern Warfare 2 Campaign Remastered installation folder. If you don't have Call of Duty: Modern Warfare 2 Campaign Remastered, get those game files first.** - **You will need to drop this in your Call of Duty: Modern Warfare 2 Campaign Remastered installation folder. If you don't have Call of Duty: Modern Warfare 2 Campaign Remastered, get those game files first.**
## Compile from source ## Compile from source

View File

@ -1,3 +1,14 @@
game:addlocalizedstring("MENU_MODS", "MODS")
game:addlocalizedstring("MENU_MODS_DESC", "Load installed mods.")
game:addlocalizedstring("LUA_MENU_MOD_DESC_DEFAULT", "Load &&1.")
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: ^3&&1")
game:addlocalizedstring("LUA_MENU_AVAILABLE_MODS", "Available mods")
game:addlocalizedstring("LUA_MENU_UNLOAD", "Unload")
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,
@ -12,7 +23,7 @@ function createdivider(menu, text)
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(text)
})) }))
menu.list:addElement(element) menu.list:addElement(element)
@ -28,8 +39,8 @@ end
LUI.addmenubutton("main_campaign", { LUI.addmenubutton("main_campaign", {
index = 6, index = 6,
text = "$_MODS", text = "@MENU_MODS",
description = "Load installed mods.", description = Engine.Localize("@MENU_MODS_DESC"),
callback = function() callback = function()
LUI.FlowManager.RequestAddMenu(nil, "mods_menu") LUI.FlowManager.RequestAddMenu(nil, "mods_menu")
end end
@ -37,13 +48,13 @@ LUI.addmenubutton("main_campaign", {
function getmodname(path) function getmodname(path)
local name = path local name = path
local desc = "Load " .. name 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))
desc = string.format("%s\nAuthor: %s\nVersion: %s", 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
end) end)
@ -54,33 +65,33 @@ end
LUI.MenuBuilder.m_types_build["mods_menu"] = function(a1) LUI.MenuBuilder.m_types_build["mods_menu"] = function(a1)
local menu = LUI.MenuTemplate.new(a1, { local menu = LUI.MenuTemplate.new(a1, {
menu_title = "$_MODS", menu_title = "@MENU_MODS",
exclusiveController = 0, exclusiveController = 0,
menu_width = 400, menu_width = 400,
menu_top_indent = LUI.MenuTemplate.spMenuOffset, menu_top_indent = LUI.MenuTemplate.spMenuOffset,
showTopRightSmallBar = true showTopRightSmallBar = true
}) })
menu:AddButton("$_OPEN STORE", function() menu:AddButton("@LUA_MENU_OPEN_STORE", function()
if (LUI.MenuBuilder.m_types_build["mod_store_menu"]) then if (LUI.MenuBuilder.m_types_build["mod_store_menu"]) then
LUI.FlowManager.RequestAddMenu(nil, "mod_store_menu") LUI.FlowManager.RequestAddMenu(nil, "mod_store_menu")
end end
end, nil, true, nil, { end, nil, true, nil, {
desc_text = "Download and install mods." desc_text = Engine.Localize("@LUA_MENU_OPEN_STORE_DESC")
}) })
local modfolder = game:getloadedmod() local modfolder = game:getloadedmod()
if (modfolder ~= "") then if (modfolder ~= "") then
createdivider(menu, "$_Loaded mod: ^3" .. getmodname(modfolder):truncate(20)) createdivider(menu, Engine.Localize("@LUA_MENU_LOADED_MOD", getmodname(modfolder):truncate(24)))
menu:AddButton("$_UNLOAD", function() menu:AddButton("@LUA_MENU_UNLOAD", function()
game:executecommand("unloadmod") game:executecommand("unloadmod")
end, nil, true, nil, { end, nil, true, nil, {
desc_text = "Unload the currently loaded mod." desc_text = Engine.Localize("@LUA_MENU_UNLOAD_DESC")
}) })
end end
createdivider(menu, "$_Available mods") createdivider(menu, Engine.Localize("@LUA_MENU_AVAILABLE_MODS"))
if (io.directoryexists("mods")) then if (io.directoryexists("mods")) then
local mods = io.listfiles("mods/") local mods = io.listfiles("mods/")
@ -89,7 +100,8 @@ LUI.MenuBuilder.m_types_build["mods_menu"] = function(a1)
local name, desc = getmodname(mods[i]) local name, desc = getmodname(mods[i])
if (mods[i] ~= modfolder) then if (mods[i] ~= modfolder) then
menu:AddButton("$_" .. name, function() game:addlocalizedstring(name, name)
menu:AddButton(name, function()
game:executecommand("loadmod " .. mods[i]) game:executecommand("loadmod " .. mods[i])
end, nil, true, nil, { end, nil, true, nil, {
desc_text = desc desc_text = desc
@ -110,14 +122,3 @@ LUI.MenuBuilder.m_types_build["mods_menu"] = function(a1)
return menu return menu
end end
local localize = Engine.Localize
Engine.Localize = function(...)
local args = {...}
if (type(args[1]) == "string" and args[1]:sub(1, 2) == "$_") then
return args[1]:sub(3, -1)
end
return localize(table.unpack(args))
end

View File

@ -1,3 +1,28 @@
game:addlocalizedstring("MENU_GENERAL", "GENERAL")
game:addlocalizedstring("MENU_GENERAL_DESC", "Set the client's settings.")
game:addlocalizedstring("LUA_MENU_AUTO_UPDATE", "Automatic updates")
game:addlocalizedstring("LUA_MENU_CHECK_UPDATES", "Check for updates")
game:addlocalizedstring("LUA_MENU_CHECK_UPDATES_DESC", "Check for updates.")
game:addlocalizedstring("LUA_MENU_DRAWING", "Drawing")
game:addlocalizedstring("LUA_MENU_UPDATES", "Updates")
game:addlocalizedstring("LUA_MENU_RENDERING", "Rendering")
game:addlocalizedstring("LUA_MENU_DRAW_FPS", "Draw FPS")
game:addlocalizedstring("LUA_MENU_DRAW_FPS_DESC", "Enable or disable drawing fps or viewpos on screen.")
game:addlocalizedstring("LUA_MENU_FPS_ONLY", "FPS only")
game:addlocalizedstring("LUA_MENU_FPS_AND_VIEWPOS", "FPS and View Pos")
game:addlocalizedstring("LUA_MENU_DRAW_SPEED", "Draw speed")
game:addlocalizedstring("LUA_MENU_DRAW_SPEED_DESC", "Enable or disable drawing the player speed on screen.")
game:addlocalizedstring("LUA_MENU_DRAW_SPEEDGRAPH", "Draw speed graph")
game:addlocalizedstring("LUA_MENU_DRAW_SPEEDGRAPH_DESC", "Enable or disable the speed graph.")
game:addlocalizedstring("LUA_MENU_R_FULLBRIGHT", "Fullbright")
game:addlocalizedstring("LUA_MENU_R_FULLBRIGHT_DESC", "Change the fullbright mode.")
game:addlocalizedstring("LUA_MENU_MODE2", "Mode 2")
game:addlocalizedstring("LUA_MENU_MODE3", "Mode 3")
function createdivider(menu, text) function createdivider(menu, text)
local element = LUI.UIElement.new({ local element = LUI.UIElement.new({
leftAnchor = true, leftAnchor = true,
@ -20,8 +45,8 @@ end
LUI.addmenubutton("pc_controls", { LUI.addmenubutton("pc_controls", {
index = 4, index = 4,
text = "$_GENERAL", text = "@MENU_GENERAL",
description = "Set the client's settings.", description = Engine.Localize("@MENU_GENERAL_DESC"),
callback = function() callback = function()
LUI.FlowManager.RequestAddMenu(nil, "settings_menu") LUI.FlowManager.RequestAddMenu(nil, "settings_menu")
end end
@ -29,134 +54,118 @@ LUI.addmenubutton("pc_controls", {
LUI.MenuBuilder.m_types_build["settings_menu"] = function(a1) LUI.MenuBuilder.m_types_build["settings_menu"] = function(a1)
local menu = LUI.MenuTemplate.new(a1, { local menu = LUI.MenuTemplate.new(a1, {
menu_title = "$_GENERAL", menu_title = "@MENU_GENERAL",
menu_list_divider_top_offset = -(LUI.H1MenuTab.tabChangeHoldingElementHeight + luiglobals.H1MenuDims.spacing), menu_list_divider_top_offset = -(LUI.H1MenuTab.tabChangeHoldingElementHeight + luiglobals.H1MenuDims.spacing),
menu_width = luiglobals.GenericMenuDims.OptionMenuWidth menu_width = luiglobals.GenericMenuDims.OptionMenuWidth
}) })
Engine.SetDvarFromString("ui_cg_autoUpdate", Engine.GetDvarBool("cg_autoUpdate") and "1" or "0") createdivider(menu, "@LUA_MENU_UPDATES")
Engine.SetDvarFromString("ui_cg_drawFps", Engine.GetDvarInt("cg_drawFps") .. "")
Engine.SetDvarFromString("ui_cg_speedGraph", Engine.GetDvarBool("cg_speedGraph") and "1" or "0")
Engine.SetDvarFromString("ui_cg_drawSpeed", Engine.GetDvarBool("cg_drawSpeed") and "1" or "0")
Engine.SetDvarFromString("ui_r_fullbright", Engine.GetDvarInt("r_fullbright") .. "")
createdivider(menu, "$_UPDATES")
LUI.Options.CreateOptionButton( LUI.Options.CreateOptionButton(
menu, menu,
"ui_cg_autoUpdate", "cg_auto_update",
"$_AUTOMATIC UPDATES", "@LUA_MENU_AUTO_UPDATE",
"Enable or disable automatic updates on startup.", "Enable or disable automatic updates on startup.",
{ {
{ {
text = "$_ENABLED", text = "@LUA_MENU_ENABLED",
value = "1" value = true
}, },
{ {
text = "$_DISABLED", text = "@LUA_MENU_DISABLED",
value = "0" value = false
}
} }
}, nil, nil, function(value)
Engine.SetDvarBool("cg_autoUpdate", Engine.GetDvarString("ui_cg_autoUpdate") == "1")
end
) )
menu:AddButton("$_CHECK FOR UPDATES", function() menu:AddButton("@LUA_MENU_CHECK_UPDATES", function()
LUI.tryupdating(false) LUI.tryupdating(false)
end, nil, true, nil, { end, nil, true, nil, {
desc_text = "Check for updates." desc_text = Engine.Localize("@LUA_MENU_CHECK_UPDATES_DESC")
}) })
createdivider(menu, "$_DRAWING") createdivider(menu, "@LUA_MENU_DRAWING")
LUI.Options.CreateOptionButton( LUI.Options.CreateOptionButton(
menu, menu,
"ui_cg_drawFps", "cg_drawFps",
"$_DRAW FPS", "@LUA_MENU_DRAW_FPS",
"Enable or disable drawing fps or viewpos on screen.", "@LUA_MENU_DRAW_FPS_DESC",
{ {
{ {
text = "$_DISABLED", text = "@LUA_MENU_DISABLED",
value = "0" value = 0
}, },
{ {
text = "$_FPS ONLY", text = "@LUA_MENU_FPS_ONLY",
value = "1" value = 1
}, },
{ {
text = "$_FPS AND VIEWPOS", text = "@LUA_MENU_FPS_AND_VIEWPOS",
value = "2" value = 2
}
} }
}, nil, nil, function(value)
Engine.SetDvarInt("cg_drawFps", tonumber(Engine.GetDvarString("ui_cg_drawFps")))
end
) )
LUI.Options.CreateOptionButton( LUI.Options.CreateOptionButton(
menu, menu,
"ui_cg_drawSpeed", "cg_drawSpeed",
"$_DRAW SPEED", "@LUA_MENU_DRAW_SPEED",
"Enable or disable drawing the player speed on screen.", "Enable or disable drawing the player speed on screen.",
{ {
{ {
text = "$_DISABLED", text = "@LUA_MENU_ENABLED",
value = "0" value = true
}, },
{ {
text = "$_ENABLED", text = "@LUA_MENU_DISABLED",
value = "1" value = false
}
} }
}, nil, nil, function(value)
Engine.SetDvarBool("cg_drawSpeed", Engine.GetDvarString("ui_cg_drawSpeed") == "1")
end
) )
LUI.Options.CreateOptionButton( LUI.Options.CreateOptionButton(
menu, menu,
"ui_cg_speedGraph", "cg_speedGraph",
"$_DRAW SPEED GRAPH", "@LUA_MENU_DRAW_SPEEDGRAPH",
"Enable or disable the speed graph.", "@LUA_MENU_DRAW_SPEEDGRAPH_DESC",
{ {
{ {
text = "$_DISABLED", text = "@LUA_MENU_ENABLED",
value = "0" value = true
}, },
{ {
text = "$_ENABLED", text = "@LUA_MENU_DISABLED",
value = "1" value = false
}
} }
}, nil, nil, function(value)
Engine.SetDvarBool("cg_speedGraph", Engine.GetDvarString("ui_cg_speedGraph") == "1")
end
) )
createdivider(menu, "$_RENDERING") createdivider(menu, "@LUA_MENU_RENDERING")
LUI.Options.CreateOptionButton( LUI.Options.CreateOptionButton(
menu, menu,
"ui_r_fullbright", "r_fullbright",
"$_FULLBRIGHT", "@LUA_MENU_R_FULLBRIGHT",
"Change the fullbright mode.", "@LUA_MENU_R_FULLBRIGHT_DESC",
{ {
{ {
text = "$_DISABLED", text = "@LUA_MENU_DISABLED",
value = "0" value = 0
}, },
{ {
text = "$_ENABLED", text = "@LUA_MENU_ENABLED",
value = "1" value = 1
}, },
{ {
text = "$_MODE 2", text = "@LUA_MENU_MODE2",
value = "2" value = 2
}, },
{ {
text = "$_MODE 3", text = "@LUA_MENU_MODE3",
value = "3" value = 3
}
} }
}, nil, nil, function(value)
Engine.SetDvarInt("r_fullbright", tonumber(Engine.GetDvarString("ui_r_fullbright")))
end
) )
LUI.Options.InitScrollingList(menu.list, nil) LUI.Options.InitScrollingList(menu.list, nil)

2
deps/GSL vendored

@ -1 +1 @@
Subproject commit 4377f6e603c64a86c934f1546aa9db482f2e1a4e Subproject commit 383723676cd548d615159701ac3d050f8dd1e128

2
deps/asmjit vendored

@ -1 +1 @@
Subproject commit 28c4d8c528527141955006f09124ce672ddfbe3f Subproject commit 21a31b8a338da3341d2b423f85913597b8ec3d63

2
deps/curl vendored

@ -1 +1 @@
Subproject commit 049f3765c7016a3f7a92b7f88aae6405c49b84fb Subproject commit 47048e02878c59367db1d42813f32dcce543eed3

2
deps/imgui vendored

@ -1 +1 @@
Subproject commit fc84214988b74179b3941d35393c24ee8ba7a794 Subproject commit 22e9093da37f0443f008b947caa6221724e760d6

2
deps/libtomcrypt vendored

@ -1 +1 @@
Subproject commit 673f5ce29015a9bba3c96792920a10601b5b0718 Subproject commit 06a81aeb227424182125363f7554fad5146d6d2a

2
deps/libtommath vendored

@ -1 +1 @@
Subproject commit 04e9d1e7a0493910b2eb5e757d623870692ada04 Subproject commit 66de86426e9cdb88526974c765108f01554af2b0

2
deps/lua vendored

@ -1 +1 @@
Subproject commit 5d708c3f9cae12820e415d4f89c9eacbe2ab964b Subproject commit 8426d9b4d4df1da3c5b2d759e509ae1c50a86667

View File

@ -1,60 +0,0 @@
protobuf = {
source = path.join(dependencies.basePath, "protobuf"),
}
function protobuf.import()
links {
"protobuf"
}
protobuf.includes()
end
function protobuf.includes()
includedirs {
path.join(protobuf.source, "src"),
}
end
function protobuf.project()
project "protobuf"
language "C++"
protobuf.includes()
files {
path.join(protobuf.source, "src/**.cc"),
"./src/**.proto",
}
removefiles {
path.join(protobuf.source, "src/**/*test.cc"),
path.join(protobuf.source, "src/google/protobuf/*test*.cc"),
path.join(protobuf.source, "src/google/protobuf/testing/**.cc"),
path.join(protobuf.source, "src/google/protobuf/compiler/**.cc"),
path.join(protobuf.source, "src/google/protobuf/arena_nc.cc"),
path.join(protobuf.source, "src/google/protobuf/util/internal/error_listener.cc"),
path.join(protobuf.source, "**/*_gcc.cc"),
}
rules {
"ProtobufCompiler"
}
defines {
"_SCL_SECURE_NO_WARNINGS",
"_SILENCE_ALL_CXX17_DEPRECATION_WARNINGS",
"_SILENCE_ALL_CXX20_DEPRECATION_WARNINGS",
}
linkoptions {
"-IGNORE:4221"
}
warnings "Off"
kind "StaticLib"
end
table.insert(dependencies, protobuf)

1
deps/protobuf vendored

@ -1 +0,0 @@
Subproject commit 2f7ee91e326d95915b63918f968244cfefbc022a

2
deps/rapidjson vendored

@ -1 +1 @@
Subproject commit e4bde977440d4a00f820b6586899e48a972d2493 Subproject commit 8261c1ddf43f10de00fd8c9a67811d1486b2c784

View File

@ -1,4 +1,3 @@
@echo off @echo off
git submodule update --init --recursive git submodule update --init --recursive
tools\premake5 %* vs2022 tools\premake5 %* vs2022
pause

View File

@ -118,13 +118,13 @@ namespace binding
void post_unpack() override void post_unpack() override
{ {
// write all bindings to config file // write all bindings to config file
key_write_bindings_to_buffer_hook.create(0x3D3840_b, key_write_bindings_to_buffer_stub); key_write_bindings_to_buffer_hook.create(0x1403D3840, key_write_bindings_to_buffer_stub);
// links a custom command to an index // links a custom command to an index
utils::hook::jump(0x59AE30_b, key_get_binding_for_cmd_stub, true); utils::hook::jump(0x14059AE30, key_get_binding_for_cmd_stub, true);
// execute custom binds // execute custom binds
cl_execute_key_hook.create(0x3CF1E0_b, &cl_execute_key_stub); cl_execute_key_hook.create(0x1403CF1E0, &cl_execute_key_stub);
} }
}; };
} }

View File

@ -45,7 +45,7 @@ namespace branding
localized_strings::override("MENU_SYSINFO_DONATION_LINK", "Donation Link:"); localized_strings::override("MENU_SYSINFO_DONATION_LINK", "Donation Link:");
localized_strings::override("MENU_SYSINFO_DONATION_URL", "https://paypal.me/fedecek"); localized_strings::override("MENU_SYSINFO_DONATION_URL", "https://paypal.me/fedecek");
utils::hook::jump(0x33D550_b, get_build_number_stub, true); utils::hook::jump(0x14033D550, get_build_number_stub, true);
} }
}; };
} }

View File

@ -1,95 +0,0 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "game/game.hpp"
#include "game/dvars.hpp"
#include "chat.hpp"
#include "scheduler.hpp"
#include <utils/string.hpp>
#include <utils/hook.hpp>
#define chat_font game::R_RegisterFont("fonts/fira_mono_regular.ttf", 25)
namespace chat
{
namespace
{
struct message
{
std::string text;
std::chrono::steady_clock::time_point time;
};
std::deque<message> history;
float color_white[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
float screen_max[2];
void check_resize()
{
screen_max[0] = game::ScrPlace_GetViewPlacement()->realViewportSize[0];
screen_max[1] = game::ScrPlace_GetViewPlacement()->realViewportSize[1];
}
float relative(float value)
{
const auto ratio = screen_max[0] / 2560.f;
return value * ratio;
}
void draw_chat()
{
check_resize();
const auto now = std::chrono::high_resolution_clock::now();
for (auto i = 0; i < std::min(15, (int)history.size()); i++)
{
if (now - history[i].time > 11s)
{
return;
}
const auto diff = now - history[i].time;
float color[4] = { color_white[0], color_white[1], color_white[2], 1.f };
if (diff > 10.5s)
{
const auto milliseconds = (float)(11000 - std::chrono::duration_cast<std::chrono::milliseconds>(diff).count());
color[3] = (float)(milliseconds / 500.f);
}
game::R_AddCmdDrawText(history[i].text.data(), 0x7FFFFFFF, chat_font, relative(15.f), relative(600.f + i * 25), 1.f, 1.f, 0.f, color, 0);
}
}
}
void print(const std::string& msg)
{
message m;
m.text = msg;
m.time = std::chrono::high_resolution_clock::now();
history.push_front(m);
}
class component final : public component_interface
{
public:
void post_unpack() override
{
scheduler::loop([]()
{
draw_chat();
}, scheduler::pipeline::renderer);
}
};
}
REGISTER_COMPONENT(chat::component)

View File

@ -1,6 +0,0 @@
#pragma once
namespace chat
{
void print(const std::string& msg);
}

View File

@ -9,7 +9,6 @@
#include "command.hpp" #include "command.hpp"
#include "scheduler.hpp" #include "scheduler.hpp"
#include "game_console.hpp" #include "game_console.hpp"
#include "chat.hpp"
#include "fastfiles.hpp" #include "fastfiles.hpp"
#include <utils/hook.hpp> #include <utils/hook.hpp>
@ -116,7 +115,9 @@ namespace command
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())
{
add_raw(name, main_handler); add_raw(name, main_handler);
}
handlers[command] = callback; handlers[command] = callback;
} }
@ -148,7 +149,7 @@ namespace command
public: public:
void post_unpack() override void post_unpack() override
{ {
utils::hook::jump(0x5A74F0_b, dvar_command_stub, true); utils::hook::jump(0x1405A74F0, dvar_command_stub, true);
add("quit", game::Quit); add("quit", game::Quit);
@ -156,7 +157,7 @@ namespace command
{ {
const auto map = params.get(1); const auto map = params.get(1);
const auto exists = utils::hook::invoke<bool>(0x412B50_b, map, 0); const auto exists = utils::hook::invoke<bool>(0x140412B50, map, 0);
if (!exists) if (!exists)
{ {
@ -165,12 +166,7 @@ namespace command
} }
// SV_SpawnServer // SV_SpawnServer
utils::hook::invoke<void>(0x6B3AA0_b, map, 0, 0, 0, 0); utils::hook::invoke<void>(0x1406B3AA0, map, 0, 0, 0, 0);
});
add("say", [](const params& params)
{
chat::print(params.join(1));
}); });
add("listassetpool", [](const params& params) add("listassetpool", [](const params& params)
@ -207,11 +203,6 @@ namespace command
} }
}); });
add("baseAddress", []()
{
printf("%p\n", (void*)game::base_address);
});
add("commandDump", []() add("commandDump", []()
{ {
printf("======== Start command dump =========\n"); printf("======== Start command dump =========\n");
@ -333,8 +324,7 @@ namespace command
} }
else if (arg == "health"s) else if (arg == "health"s)
{ {
if (params.size() > 2)
if (params.size() > 3)
{ {
const auto amount = atoi(params.get(2)); const auto amount = atoi(params.get(2));
const auto health = player.get("health").as<int>(); const auto health = player.get("health").as<int>();
@ -414,7 +404,7 @@ namespace command
try try
{ {
const scripting::entity player = scripting::call("getentbynum", {0}).as<scripting::entity>(); const auto player = scripting::call("getentbynum", {0}).as<scripting::entity>();
if (weapon == "all"s) if (weapon == "all"s)
{ {
player.call("takeallweapons"); player.call("takeallweapons");

View File

@ -1,24 +0,0 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "game/game.hpp"
#include "game/dvars.hpp"
#include "command.hpp"
#include "game_console.hpp"
#include <utils/hook.hpp>
namespace config
{
class component final : public component_interface
{
public:
void post_unpack() override
{
dvars::register_bool("cg_autoUpdate", true, game::DvarFlags::DVAR_FLAG_SAVED);
}
};
}
REGISTER_COMPONENT(config::component)

View File

@ -158,7 +158,6 @@ namespace exception
line("Timestamp: "s + get_timestamp()); line("Timestamp: "s + get_timestamp());
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

@ -51,7 +51,7 @@ namespace fastfiles
public: public:
void post_unpack() override void post_unpack() override
{ {
db_try_load_x_file_internal_hook.create(0x4173B0_b, &db_try_load_x_file_internal); db_try_load_x_file_internal_hook.create(0x1404173B0, &db_try_load_x_file_internal);
command::add("loadzone", [](const command::params& params) command::add("loadzone", [](const command::params& params)
{ {

View File

@ -0,0 +1,56 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "filesystem.hpp"
#include <utils/io.hpp>
namespace filesystem
{
std::unordered_set<std::string>& get_search_paths()
{
static std::unordered_set<std::string> search_paths{};
return search_paths;
}
std::string read_file(const std::string& path)
{
for (const auto& search_path : get_search_paths())
{
const auto path_ = search_path + "/" + path;
if (utils::io::file_exists(path_))
{
return utils::io::read_file(path_);
}
}
return {};
}
bool read_file(const std::string& path, std::string* data)
{
for (const auto& search_path : get_search_paths())
{
const auto path_ = search_path + "/" + path;
if (utils::io::read_file(path_, data))
{
return true;
}
}
return false;
}
class component final : public component_interface
{
public:
void post_unpack() override
{
get_search_paths().insert(".");
get_search_paths().insert("h2-mod");
get_search_paths().insert("data");
}
};
}
REGISTER_COMPONENT(filesystem::component)

View File

@ -0,0 +1,8 @@
#pragma once
namespace filesystem
{
std::unordered_set<std::string>& get_search_paths();
std::string read_file(const std::string& path);
bool read_file(const std::string& path, std::string* data);
}

View File

@ -0,0 +1,131 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "fonts.hpp"
#include "game_console.hpp"
#include "filesystem.hpp"
#include "game/game.hpp"
#include "game/dvars.hpp"
#include <utils/hook.hpp>
#include <utils/memory.hpp>
#include <utils/io.hpp>
#include <utils/string.hpp>
#include <utils/image.hpp>
#include <utils/concurrency.hpp>
namespace fonts
{
namespace
{
struct font_data_t
{
std::unordered_map<std::string, game::TTF*> fonts;
std::unordered_map<std::string, std::string> raw_fonts;
};
utils::concurrency::container<font_data_t> font_data;
game::TTF* create_font(const std::string& name, const std::string& data)
{
const auto font = utils::memory::get_allocator()->allocate<game::TTF>();
font->name = utils::memory::get_allocator()->duplicate_string(name);
font->buffer = utils::memory::get_allocator()->duplicate_string(data);
font->len = static_cast<int>(data.size());
font->fontFace = 0;
return font;
}
void free_font(game::TTF* font)
{
utils::memory::get_allocator()->free(font->buffer);
utils::memory::get_allocator()->free(font->name);
utils::memory::get_allocator()->free(font);
}
game::TTF* load_font(const std::string& name)
{
return font_data.access<game::TTF*>([&](font_data_t& data_) -> game::TTF*
{
if (const auto i = data_.fonts.find(name); i != data_.fonts.end())
{
return i->second;
}
std::string data{};
if (const auto i = data_.raw_fonts.find(name); i != data_.raw_fonts.end())
{
data = i->second;
}
if (data.empty() && !filesystem::read_file(name, &data))
{
return nullptr;
}
const auto material = create_font(name, data);
data_.fonts[name] = material;
return material;
});
}
game::TTF* try_load_font(const std::string& name)
{
try
{
return load_font(name);
}
catch (const std::exception& e)
{
game_console::print(game_console::con_type_error, "Failed to load font %s: %s\n", name.data(), e.what());
}
return nullptr;
}
game::TTF* db_find_xasset_header_stub(game::XAssetType type, const char* name, int create_default)
{
auto result = try_load_font(name);
if (result == nullptr)
{
result = game::DB_FindXAssetHeader(type, name, create_default).ttf;
}
return result;
}
}
void add(const std::string& name, const std::string& data)
{
font_data.access([&](font_data_t& data_)
{
data_.raw_fonts[name] = data;
});
}
void clear()
{
font_data.access([&](font_data_t& data_)
{
for (auto& font : data_.fonts)
{
free_font(font.second);
}
data_.fonts.clear();
utils::hook::set<int>(0x14EE3ACB8, 0); // reset registered font count
});
}
class component final : public component_interface
{
public:
void post_unpack() override
{
utils::hook::call(0x140747096, db_find_xasset_header_stub);
}
};
}
REGISTER_COMPONENT(fonts::component)

View File

@ -0,0 +1,7 @@
#pragma once
namespace fonts
{
void add(const std::string& name, const std::string& data);
void clear();
}

View File

@ -4,7 +4,6 @@
#include "game/game.hpp" #include "game/game.hpp"
#include "game/dvars.hpp" #include "game/dvars.hpp"
#include "chat.hpp"
#include "scheduler.hpp" #include "scheduler.hpp"
#include "command.hpp" #include "command.hpp"
@ -303,7 +302,7 @@ namespace fps
{ {
scheduler::loop(draw, scheduler::pipeline::renderer); scheduler::loop(draw, scheduler::pipeline::renderer);
sub_7C55D0_hook.create(0x7C55D0_b, perf_update); sub_7C55D0_hook.create(0x1407C55D0, perf_update);
cg_drawSpeed = dvars::register_bool("cg_drawSpeed", 0, game::DVAR_FLAG_SAVED); cg_drawSpeed = dvars::register_bool("cg_drawSpeed", 0, game::DVAR_FLAG_SAVED);
cg_drawFps = dvars::register_int("cg_drawFPS", 0, 0, 4, game::DVAR_FLAG_SAVED); cg_drawFps = dvars::register_int("cg_drawFPS", 0, 0, 4, game::DVAR_FLAG_SAVED);

View File

@ -46,10 +46,10 @@ namespace gameplay
a.jnz(allsolid); a.jnz(allsolid);
a.bind(stand); a.bind(stand);
a.jmp(0x6878CD_b); a.jmp(0x1406878CD);
a.bind(allsolid); a.bind(allsolid);
a.jmp(0x6878D4_b); a.jmp(0x1406878D4);
} }
void pm_crashland_stub(void* ps, void* pm) void pm_crashland_stub(void* ps, void* pm)
@ -70,12 +70,12 @@ namespace gameplay
dvars::jump_enableFallDamage = dvars::register_bool("jump_enableFallDamage", true, game::DVAR_FLAG_REPLICATED); dvars::jump_enableFallDamage = dvars::register_bool("jump_enableFallDamage", true, game::DVAR_FLAG_REPLICATED);
// Influence PM_JitterPoint code flow so the trace->startsolid checks are 'ignored' // Influence PM_JitterPoint code flow so the trace->startsolid checks are 'ignored'
pm_player_trace_hook.create(0x068F0A0_b, &pm_player_trace_stub); pm_player_trace_hook.create(0x14068F0A0, &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(0x6878C1_b, utils::hook::assemble(pm_trace_stub), true); utils::hook::jump(0x1406878C1, utils::hook::assemble(pm_trace_stub), true);
pm_crashland_hook.create(0x688A20_b, pm_crashland_stub); pm_crashland_hook.create(0x140688A20, pm_crashland_stub);
dvars::register_float("jump_height", 39, 0, 1000, game::DVAR_FLAG_REPLICATED); dvars::register_float("jump_height", 39, 0, 1000, game::DVAR_FLAG_REPLICATED);
dvars::register_float("g_gravity", 800, 1, 1000, game::DVAR_FLAG_REPLICATED); dvars::register_float("g_gravity", 800, 1, 1000, game::DVAR_FLAG_REPLICATED);

View File

@ -208,7 +208,7 @@ namespace gui
a.call_aligned(rbx); a.call_aligned(rbx);
a.mov(ecx, eax); a.mov(ecx, eax);
a.jmp(0x7A14D1_b); a.jmp(0x1407A14D1);
} }
utils::hook::detour wnd_proc_hook; utils::hook::detour wnd_proc_hook;
@ -301,8 +301,8 @@ namespace gui
void post_unpack() override void post_unpack() override
{ {
utils::hook::jump(0x7A14C4_b, utils::hook::assemble(dxgi_swap_chain_present_stub), true); utils::hook::jump(0x1407A14C4, utils::hook::assemble(dxgi_swap_chain_present_stub), true);
wnd_proc_hook.create(0x650F10_b, wnd_proc_stub); wnd_proc_hook.create(0x140650F10, wnd_proc_stub);
on_frame([]() on_frame([]()
{ {
@ -312,10 +312,13 @@ namespace gui
} }
void pre_destroy() override void pre_destroy() override
{
if (initialized)
{ {
ImGui_ImplWin32_Shutdown(); ImGui_ImplWin32_Shutdown();
ImGui::DestroyContext(); ImGui::DestroyContext();
} }
}
}; };
} }

View File

@ -22,13 +22,13 @@ namespace asset_list
void on_frame() void on_frame()
{ {
if (!gui::enabled_menus["asset_list"]) static auto* enabled = &gui::enabled_menus["asset_list"];
if (!*enabled)
{ {
return; return;
} }
{ ImGui::Begin("Asset list", enabled);
ImGui::Begin("Asset list", &gui::enabled_menus["asset_list"]);
ImGui::InputText("asset type", &asset_type_filter); ImGui::InputText("asset type", &asset_type_filter);
ImGui::BeginChild("asset type list"); ImGui::BeginChild("asset type list");
@ -46,7 +46,6 @@ namespace asset_list
ImGui::EndChild(); ImGui::EndChild();
ImGui::End(); ImGui::End();
}
for (auto i = 0; i < game::XAssetType::ASSET_TYPE_COUNT; i++) for (auto i = 0; i < game::XAssetType::ASSET_TYPE_COUNT; i++)
{ {

View File

@ -13,7 +13,7 @@
#include <utils/hook.hpp> #include <utils/hook.hpp>
#include <utils/concurrency.hpp> #include <utils/concurrency.hpp>
namespace gui_console namespace gui::console
{ {
namespace namespace
{ {
@ -52,7 +52,7 @@ namespace gui_console
} }
case ImGuiInputTextFlags_CallbackHistory: case ImGuiInputTextFlags_CallbackHistory:
{ {
const auto history = game_console::get_history(); const auto& history = game_console::get_history();
if (data->EventKey == ImGuiKey_UpArrow) if (data->EventKey == ImGuiKey_UpArrow)
{ {
@ -96,7 +96,7 @@ namespace gui_console
{ {
std::string text{}; std::string text{};
const auto output = game_console::get_output(); const auto& output = game_console::get_output();
for (const auto& line : output) for (const auto& line : output)
{ {
if (utils::string::find_lower(line, filter)) if (utils::string::find_lower(line, filter))
@ -116,7 +116,8 @@ namespace gui_console
void on_frame() void on_frame()
{ {
if (!gui::enabled_menus["console"]) static auto* enabled = &gui::enabled_menus["console"];
if (!*enabled)
{ {
return; return;
} }
@ -126,7 +127,7 @@ namespace gui_console
static const auto input_text_flags = ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_CallbackCompletion | static const auto input_text_flags = ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_CallbackCompletion |
ImGuiInputTextFlags_CallbackHistory; ImGuiInputTextFlags_CallbackHistory;
ImGui::Begin("Console", &gui::enabled_menus["console"]); ImGui::Begin("Console", enabled);
if (ImGui::BeginPopup("Options")) if (ImGui::BeginPopup("Options"))
{ {
@ -159,7 +160,7 @@ namespace gui_console
ImGui::BeginChild("console_scroll", ImVec2(0, -footer_height_to_reserve), false); ImGui::BeginChild("console_scroll", ImVec2(0, -footer_height_to_reserve), false);
const auto output = game_console::get_output(); const auto& output = game_console::get_output();
for (const auto& line : output) for (const auto& line : output)
{ {
if (utils::string::find_lower(line, filter)) if (utils::string::find_lower(line, filter))
@ -177,7 +178,7 @@ namespace gui_console
if (ImGui::InputText("Input", &input, input_text_flags, input_text_edit)) if (ImGui::InputText("Input", &input, input_text_flags, input_text_edit))
{ {
auto history = game_console::get_history(); auto& history = game_console::get_history();
if (history_index != -1) if (history_index != -1)
{ {
@ -209,4 +210,4 @@ namespace gui_console
}; };
} }
REGISTER_COMPONENT(gui_console::component) REGISTER_COMPONENT(gui::console::component)

View File

@ -16,7 +16,7 @@
#include <utils/hook.hpp> #include <utils/hook.hpp>
#include <utils/concurrency.hpp> #include <utils/concurrency.hpp>
namespace gui_debug namespace gui::debug
{ {
namespace namespace
{ {
@ -403,12 +403,13 @@ namespace gui_debug
void draw_window() void draw_window()
{ {
if (!gui::enabled_menus["debug"]) static auto* enabled = &gui::enabled_menus["debug"];
if (!*enabled)
{ {
return; return;
} }
ImGui::Begin("Debug", &gui::enabled_menus["debug"]); ImGui::Begin("Debug", enabled);
if (ImGui::TreeNode("Path nodes")) if (ImGui::TreeNode("Path nodes"))
{ {
@ -724,4 +725,4 @@ namespace gui_debug
}; };
} }
REGISTER_COMPONENT(gui_debug::component) REGISTER_COMPONENT(gui::debug::component)

View File

@ -15,7 +15,7 @@
#include <utils/hook.hpp> #include <utils/hook.hpp>
#include <utils/concurrency.hpp> #include <utils/concurrency.hpp>
namespace entity_list namespace gui::entity_list
{ {
namespace namespace
{ {
@ -593,8 +593,9 @@ namespace entity_list
void show_entity_list_window(data_t& data) void show_entity_list_window(data_t& data)
{ {
static auto* enabled = &gui::enabled_menus["entity_list"];
ImGui::SetNextWindowSizeConstraints(ImVec2(500, 500), ImVec2(1000, 1000)); ImGui::SetNextWindowSizeConstraints(ImVec2(500, 500), ImVec2(1000, 1000));
ImGui::Begin("Entity list", &gui::enabled_menus["entity_list"]); ImGui::Begin("Entity list", enabled);
if (ImGui::Button("Update list")) if (ImGui::Button("Update list"))
{ {
@ -808,7 +809,8 @@ namespace entity_list
void on_frame() void on_frame()
{ {
if (!gui::enabled_menus["entity_list"]) static auto* enabled = &gui::enabled_menus["entity_list"];
if (!*enabled)
{ {
return; return;
} }
@ -858,4 +860,4 @@ namespace entity_list
}; };
} }
REGISTER_COMPONENT(entity_list::component) REGISTER_COMPONENT(gui::entity_list::component)

View File

@ -15,7 +15,7 @@
#include <utils/hook.hpp> #include <utils/hook.hpp>
#include <utils/concurrency.hpp> #include <utils/concurrency.hpp>
namespace gui_script_console namespace gui::script_console
{ {
namespace namespace
{ {
@ -228,4 +228,4 @@ namespace gui_script_console
}; };
} }
REGISTER_COMPONENT(gui_script_console::component) REGISTER_COMPONENT(gui::script_console::component)

View File

@ -1,8 +1,11 @@
#include <std_include.hpp> #include <std_include.hpp>
#include "loader/component_loader.hpp" #include "loader/component_loader.hpp"
#include "game/game.hpp"
#include "images.hpp" #include "images.hpp"
#include "game_console.hpp" #include "game_console.hpp"
#include "filesystem.hpp"
#include "game/game.hpp"
#include <utils/hook.hpp> #include <utils/hook.hpp>
#include <utils/string.hpp> #include <utils/string.hpp>
@ -29,7 +32,7 @@ namespace images
} }
}); });
if (data.empty() && !utils::io::read_file(utils::string::va("images/%s.png", image->name), &data)) if (data.empty() && !filesystem::read_file(utils::string::va("images/%s.png", image->name), &data))
{ {
return {}; return {};
} }
@ -111,8 +114,8 @@ namespace images
public: public:
void post_unpack() override void post_unpack() override
{ {
setup_texture_hook.create(0x74A390_b, setup_texture_stub); setup_texture_hook.create(0x14074A390, setup_texture_stub);
load_texture_hook.create(0x2A7940_b, load_texture_stub); load_texture_hook.create(0x1402A7940, load_texture_stub);
} }
}; };
} }

View File

@ -6,6 +6,7 @@
#include "game_console.hpp" #include "game_console.hpp"
#include "gui.hpp" #include "gui.hpp"
#include "game/ui_scripting/lua/engine.hpp" #include "game/ui_scripting/lua/engine.hpp"
#include "game/ui_scripting/execution.hpp"
#include <utils/hook.hpp> #include <utils/hook.hpp>
@ -25,7 +26,11 @@ namespace input
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)
{ {
ui_scripting::lua::engine::ui_event("char", {key}); ui_scripting::notify("keypress",
{
{"keynum", key},
{"key", game::Key_KeynumToString(key, 0, 1)},
});
if (!game_console::console_char_event(local_client_num, key)) if (!game_console::console_char_event(local_client_num, key))
{ {
@ -42,7 +47,11 @@ namespace input
void cl_key_event_stub(const int local_client_num, const int key, const int down) void cl_key_event_stub(const int local_client_num, const int key, const int down)
{ {
ui_scripting::lua::engine::ui_event("key", {key, down}); ui_scripting::notify(down ? "keydown" : "keyup",
{
{"keynum", key},
{"key", game::Key_KeynumToString(key, 0, 1)},
});
if (!game_console::console_key_event(local_client_num, key, down)) if (!game_console::console_key_event(local_client_num, key, down))
{ {
@ -64,7 +73,6 @@ namespace input
return; return;
} }
ui_scripting::lua::engine::ui_event("mousemove", {x, y});
cl_mouse_move_hook.invoke<void>(local_client_num, x, y); cl_mouse_move_hook.invoke<void>(local_client_num, x, y);
} }
} }
@ -74,9 +82,9 @@ namespace input
public: public:
void post_unpack() override void post_unpack() override
{ {
cl_char_event_hook.create(0x3D27B0_b, cl_char_event_stub); cl_char_event_hook.create(0x1403D27B0, cl_char_event_stub);
cl_key_event_hook.create(0x3D2AE0_b, cl_key_event_stub); cl_key_event_hook.create(0x1403D2AE0, cl_key_event_stub);
cl_mouse_move_hook.create(0x3296F0_b, cl_mouse_move_stub); cl_mouse_move_hook.create(0x1403296F0, cl_mouse_move_stub);
} }
}; };
} }

View File

@ -46,7 +46,7 @@ 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(0x5E5FD0_b, &seh_string_ed_get_string); seh_string_ed_get_string_hook.create(0x1405E5FD0, &seh_string_ed_get_string);
} }
}; };
} }

View File

@ -142,10 +142,10 @@ namespace logger
public: public:
void post_unpack() override void post_unpack() override
{ {
utils::hook::jump(0x32C620_b, print_warning, true); utils::hook::jump(0x14032C620, print_warning, true);
utils::hook::jump(0x32C630_b, print_warning, true); utils::hook::jump(0x14032C630, print_warning, true);
utils::hook::jump(0x32AEF0_b, lui_print, true); utils::hook::jump(0x14032AEF0, lui_print, true);
com_error_hook.create(0x5A2D80_b, com_error_stub); com_error_hook.create(0x1405A2D80, com_error_stub);
} }
}; };
} }

View File

@ -39,8 +39,8 @@ namespace lui
command::add("lui_restart", []() command::add("lui_restart", []()
{ {
utils::hook::invoke<void>(0x3203B0_b); utils::hook::invoke<void>(0x1403203B0);
utils::hook::invoke<void>(0x32D370_b); utils::hook::invoke<void>(0x14032D370);
}); });
} }
}; };

View File

@ -0,0 +1,200 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "materials.hpp"
#include "game_console.hpp"
#include "filesystem.hpp"
#include "game/game.hpp"
#include "game/dvars.hpp"
#include <utils/hook.hpp>
#include <utils/memory.hpp>
#include <utils/io.hpp>
#include <utils/string.hpp>
#include <utils/image.hpp>
#include <utils/concurrency.hpp>
namespace materials
{
namespace
{
utils::hook::detour db_material_streaming_fail_hook;
utils::hook::detour material_register_handle_hook;
utils::hook::detour db_get_material_index_hook;
struct material_data_t
{
std::unordered_map<std::string, game::Material*> materials;
std::unordered_map<std::string, std::string> images;
};
char constant_table[0x20] = {};
utils::concurrency::container<material_data_t> material_data;
game::GfxImage* setup_image(game::GfxImage* image, const utils::image& raw_image)
{
image->imageFormat = 0x1000003;
image->resourceSize = -1;
D3D11_SUBRESOURCE_DATA data{};
data.SysMemPitch = raw_image.get_width() * 4;
data.SysMemSlicePitch = data.SysMemPitch * raw_image.get_height();
data.pSysMem = raw_image.get_buffer();
game::Image_Setup(image, raw_image.get_width(), raw_image.get_height(), image->depth, image->numElements,
image->imageFormat, DXGI_FORMAT_R8G8B8A8_UNORM, 0, image->name, &data);
return image;
}
game::Material* create_material(const std::string& name, const std::string& data)
{
const auto white = *reinterpret_cast<game::Material**>(0x141B09208);
const auto material = utils::memory::get_allocator()->allocate<game::Material>();
const auto texture_table = utils::memory::get_allocator()->allocate<game::MaterialTextureDef>();
const auto image = utils::memory::get_allocator()->allocate<game::GfxImage>();
std::memcpy(material, white, sizeof(game::Material));
std::memcpy(texture_table, white->textureTable, sizeof(game::MaterialTextureDef));
std::memcpy(image, white->textureTable->u.image, sizeof(game::GfxImage));
material->constantTable = &constant_table;
material->name = utils::memory::get_allocator()->duplicate_string(name);
image->name = material->name;
material->textureTable = texture_table;
material->textureTable->u.image = setup_image(image, data);
return material;
}
void free_material(game::Material* material)
{
material->textureTable->u.image->textures.___u0.map->Release();
material->textureTable->u.image->textures.shaderView->Release();
utils::memory::get_allocator()->free(material->textureTable->u.image);
utils::memory::get_allocator()->free(material->textureTable);
utils::memory::get_allocator()->free(material->name);
utils::memory::get_allocator()->free(material);
}
game::Material* load_material(const std::string& name)
{
return material_data.access<game::Material*>([&](material_data_t& data_) -> game::Material*
{
if (const auto i = data_.materials.find(name); i != data_.materials.end())
{
return i->second;
}
std::string data{};
if (const auto i = data_.images.find(name); i != data_.images.end())
{
data = i->second;
}
if (data.empty() && !filesystem::read_file(utils::string::va("materials/%s.png", name.data()), &data))
{
data_.materials[name] = nullptr;
return nullptr;
}
const auto material = create_material(name, data);
data_.materials[name] = material;
return material;
});
}
game::Material* try_load_material(const std::string& name)
{
if (name == "white")
{
return nullptr;
}
try
{
return load_material(name);
}
catch (const std::exception& e)
{
game_console::print(game_console::con_type_error, "Failed to load material %s: %s\n", name.data(), e.what());
}
return nullptr;
}
game::Material* material_register_handle_stub(const char* name)
{
auto result = try_load_material(name);
if (result == nullptr)
{
result = material_register_handle_hook.invoke<game::Material*>(name);
}
return result;
}
int db_material_streaming_fail_stub(game::Material* material)
{
if (material->constantTable == &constant_table)
{
return 0;
}
return db_material_streaming_fail_hook.invoke<int>(material);
}
unsigned int db_get_material_index_stub(game::Material* material)
{
if (material->constantTable == &constant_table)
{
return 0;
}
return db_get_material_index_hook.invoke<unsigned int>(material);
}
}
void add(const std::string& name, const std::string& data)
{
material_data.access([&](material_data_t& data_)
{
data_.images[name] = data;
});
}
void clear()
{
material_data.access([&](material_data_t& data_)
{
for (auto& material : data_.materials)
{
if (material.second == nullptr)
{
continue;
}
free_material(material.second);
}
data_.materials.clear();
});
}
class component final : public component_interface
{
public:
void post_unpack() override
{
material_register_handle_hook.create(game::Material_RegisterHandle.get(), material_register_handle_stub);
db_material_streaming_fail_hook.create(0x14041D140, db_material_streaming_fail_stub);
db_get_material_index_hook.create(0x140413BC0, db_get_material_index_stub);
}
};
}
REGISTER_COMPONENT(materials::component)

View File

@ -0,0 +1,7 @@
#pragma once
namespace materials
{
void add(const std::string& name, const std::string& data);
void clear();
}

View File

@ -6,12 +6,45 @@
#include "command.hpp" #include "command.hpp"
#include "game_console.hpp" #include "game_console.hpp"
#include "scheduler.hpp" #include "scheduler.hpp"
#include "filesystem.hpp"
#include "materials.hpp"
#include "fonts.hpp"
#include "mods.hpp"
#include <utils/hook.hpp> #include <utils/hook.hpp>
#include <utils/io.hpp> #include <utils/io.hpp>
namespace mods namespace mods
{ {
std::string mod_path{};
namespace
{
utils::hook::detour db_release_xassets_hook;
bool release_assets = false;
void db_release_xassets_stub()
{
if (release_assets)
{
materials::clear();
fonts::clear();
}
db_release_xassets_hook.invoke<void>();
}
void restart()
{
scheduler::once([]()
{
release_assets = true;
game::Com_Shutdown("");
release_assets = false;
}, scheduler::pipeline::main);
}
}
class component final : public component_interface class component final : public component_interface
{ {
public: public:
@ -22,6 +55,8 @@ namespace mods
utils::io::create_directory("mods"); utils::io::create_directory("mods");
} }
db_release_xassets_hook.create(0x140416A80, db_release_xassets_stub);
command::add("loadmod", [](const command::params& params) command::add("loadmod", [](const command::params& params)
{ {
if (params.size() < 2) if (params.size() < 2)
@ -30,7 +65,7 @@ namespace mods
return; return;
} }
if (::game::SV_Loaded()) if (!game::Com_InFrontend())
{ {
game_console::print(game_console::con_type_error, "Cannot load mod while in-game!\n"); game_console::print(game_console::con_type_error, "Cannot load mod while in-game!\n");
game::CG_GameMessage(0, "^1Cannot unload mod while in-game!"); game::CG_GameMessage(0, "^1Cannot unload mod while in-game!");
@ -38,44 +73,38 @@ namespace mods
} }
const auto path = params.get(1); const auto path = params.get(1);
game_console::print(game_console::con_type_info, "Loading mod %s\n", path);
if (!utils::io::directory_exists(path)) if (!utils::io::directory_exists(path))
{ {
game_console::print(game_console::con_type_error, "Mod %s not found!\n", path); game_console::print(game_console::con_type_error, "Mod %s not found!\n", path);
return; return;
} }
game::mod_folder = path; game_console::print(game_console::con_type_info, "Loading mod %s\n", path);
filesystem::get_search_paths().erase(mod_path);
scheduler::once([]() filesystem::get_search_paths().insert(path);
{ mod_path = path;
command::execute("lui_restart", true); restart();
}, scheduler::pipeline::renderer);
}); });
command::add("unloadmod", [](const command::params& params) command::add("unloadmod", [](const command::params& params)
{ {
if (game::mod_folder.empty()) if (mod_path.empty())
{ {
game_console::print(game_console::con_type_info, "No mod loaded\n"); game_console::print(game_console::con_type_info, "No mod loaded\n");
return; return;
} }
if (::game::SV_Loaded()) if (!game::Com_InFrontend())
{ {
game_console::print(game_console::con_type_error, "Cannot unload mod while in-game!\n"); game_console::print(game_console::con_type_error, "Cannot unload mod while in-game!\n");
game::CG_GameMessage(0, "^1Cannot unload mod while in-game!"); game::CG_GameMessage(0, "^1Cannot unload mod while in-game!");
return; return;
} }
game_console::print(game_console::con_type_info, "Unloading mod %s\n", game::mod_folder.data()); game_console::print(game_console::con_type_info, "Unloading mod %s\n", mod_path.data());
game::mod_folder.clear(); filesystem::get_search_paths().erase(mod_path);
mod_path.clear();
scheduler::once([]() restart();
{
command::execute("lui_restart", true);
}, scheduler::pipeline::renderer);
}); });
} }
}; };

View File

@ -0,0 +1,6 @@
#pragma once
namespace mods
{
extern std::string mod_path;
}

View File

@ -32,20 +32,20 @@ namespace notifies
{ {
if (vm_execute_hooks.find(pos) == vm_execute_hooks.end()) if (vm_execute_hooks.find(pos) == vm_execute_hooks.end())
{ {
hook_enabled = true;
return false; return false;
} }
if (!hook_enabled && pos > (char*)vm_execute_hooks.size()) if (!hook_enabled && pos > reinterpret_cast<char*>(vm_execute_hooks.size()))
{ {
hook_enabled = true; hook_enabled = true;
return false; return false;
} }
const auto hook = vm_execute_hooks[pos]; const auto& hook = vm_execute_hooks[pos];
const auto state = hook.lua_state(); const auto state = hook.lua_state();
const auto self_id = local_id_to_entity(game::scr_VmPub->function_frame->fs.localId); const scripting::entity self = local_id_to_entity(game::scr_VmPub->function_frame->fs.localId);
const auto self = scripting::entity(self_id);
std::vector<sol::lua_value> args; std::vector<sol::lua_value> args;
@ -59,16 +59,6 @@ namespace notifies
const auto result = hook(self, sol::as_args(args)); const auto result = hook(self, sol::as_args(args));
scripting::lua::handle_error(result); scripting::lua::handle_error(result);
const auto value = scripting::lua::convert({state, result});
const auto type = value.get_raw().type;
game::Scr_ClearOutParams();
if (result.valid() && type && type < game::SCRIPT_END)
{
scripting::push_value(value);
}
return true; return true;
} }
@ -94,12 +84,12 @@ namespace notifies
a.inc(r14); a.inc(r14);
a.mov(dword_ptr(rbp, 0xA4), r15d); a.mov(dword_ptr(rbp, 0xA4), r15d);
a.jmp(0x5C90B3_b); a.jmp(0x1405C90B3);
a.bind(replace); a.bind(replace);
a.popad64(); a.popad64();
a.mov(r14, (char*)empty_function); a.mov(r14, reinterpret_cast<char*>(empty_function));
a.jmp(end); a.jmp(end);
} }
@ -137,7 +127,7 @@ namespace notifies
std::string convert_mod(const int meansOfDeath) std::string convert_mod(const int meansOfDeath)
{ {
const auto value = reinterpret_cast<game::scr_string_t**>(0xBF49B0_b)[meansOfDeath]; const auto value = reinterpret_cast<game::scr_string_t**>(0x140BF49B0)[meansOfDeath];
const auto string = game::SL_ConvertToString(*value); const auto string = game::SL_ConvertToString(*value);
return string; return string;
@ -147,7 +137,7 @@ namespace notifies
int damage, int dflags, const unsigned int hitLoc, const unsigned int weapon, bool isAlternate, unsigned int a11, const int meansOfDeath, unsigned int a13, unsigned int a14) int damage, int dflags, const unsigned int hitLoc, const unsigned int weapon, bool isAlternate, unsigned int a11, const int meansOfDeath, unsigned int a13, unsigned int a14)
{ {
{ {
const std::string _hitLoc = reinterpret_cast<const char**>(0xBF4AA0_b)[hitLoc]; const std::string _hitLoc = reinterpret_cast<const char**>(0x140BF4AA0)[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);
@ -199,9 +189,9 @@ namespace notifies
public: public:
void post_unpack() override void post_unpack() override
{ {
utils::hook::jump(0x5C90A5_b, utils::hook::assemble(vm_execute_stub), true); utils::hook::jump(0x1405C90A5, utils::hook::assemble(vm_execute_stub), true);
scr_entity_damage_hook.create(0x4BD2E0_b, scr_entity_damage_stub); scr_entity_damage_hook.create(0x1404BD2E0, scr_entity_damage_stub);
} }
}; };
} }

View File

@ -16,7 +16,7 @@ namespace patches
void* sub_46148() void* sub_46148()
{ {
static uint64_t off_11C52460 = 0xAD0C58_b; static uint64_t off_11C52460 = 0x140AD0C58;
return &off_11C52460; return &off_11C52460;
} }
@ -28,7 +28,7 @@ namespace patches
void gscr_set_save_dvar_stub() void gscr_set_save_dvar_stub()
{ {
const auto string = utils::string::to_lower(utils::hook::invoke<const char*>(0x5C7C20_b, 0)); const auto string = utils::string::to_lower(utils::hook::invoke<const char*>(0x1405C7C20, 0));
if (string == "cg_fov" || string == "cg_fovscale") if (string == "cg_fov" || string == "cg_fovscale")
{ {
return; return;
@ -65,24 +65,24 @@ namespace patches
void post_unpack() override void post_unpack() override
{ {
// Fix startup crashes // Fix startup crashes
utils::hook::set(0x633080_b, 0xC301B0); utils::hook::set(0x140633080, 0xC301B0);
utils::hook::set(0x272F70_b, 0xC301B0); utils::hook::set(0x140272F70, 0xC301B0);
utils::hook::jump(0x46148_b, sub_46148, true); utils::hook::jump(0x140046148, sub_46148, true);
utils::hook::jump(0x64EF10_b, quit_stub, true); utils::hook::jump(0x14064EF10, quit_stub, true);
// Unlock fps in main menu // Unlock fps in main menu
utils::hook::set<BYTE>(0x3D8E1B_b, 0xEB); utils::hook::set<BYTE>(0x1403D8E1B, 0xEB);
// Disable battle net popup // Disable battle net popup
utils::hook::nop(0x5F4496_b, 5); utils::hook::nop(0x1405F4496, 5);
// Allow kbam input when gamepad is enabled // Allow kbam input when gamepad is enabled
utils::hook::nop(0x3D2F8E_b, 2); utils::hook::nop(0x1403D2F8E, 2);
utils::hook::nop(0x3D0C9C_b, 6); utils::hook::nop(0x1403D0C9C, 6);
// Prevent game from overriding cg_fov and cg_fovscale values // Prevent game from overriding cg_fov and cg_fovscale values
gscr_set_save_dvar_hook.create(0x504C60_b, &gscr_set_save_dvar_stub); gscr_set_save_dvar_hook.create(0x140504C60, &gscr_set_save_dvar_stub);
// Make cg_fov and cg_fovscale saved dvars // Make cg_fov and cg_fovscale saved dvars

View File

@ -63,8 +63,8 @@ namespace renderer
{ {
dvars::r_fullbright = dvars::register_int("r_fullbright", 0, 0, 3, game::DVAR_FLAG_SAVED); dvars::r_fullbright = dvars::register_int("r_fullbright", 0, 0, 3, game::DVAR_FLAG_SAVED);
r_init_draw_method_hook.create(0x72F950_b, &r_init_draw_method_stub); r_init_draw_method_hook.create(0x14072F950, &r_init_draw_method_stub);
r_update_front_end_dvar_options_hook.create(0x76EE70_b, &r_update_front_end_dvar_options_stub); r_update_front_end_dvar_options_hook.create(0x14076EE70, &r_update_front_end_dvar_options_stub);
} }
}; };
} }

View File

@ -183,9 +183,9 @@ namespace scheduler
void post_unpack() override void post_unpack() override
{ {
r_end_frame_hook.create(0x76D7B0_b, scheduler::r_end_frame_stub); r_end_frame_hook.create(0x14076D7B0, scheduler::r_end_frame_stub);
g_run_frame_hook.create(0x4CB030_b, scheduler::server_frame_stub); g_run_frame_hook.create(0x1404CB030, scheduler::server_frame_stub);
main_frame_hook.create(0x417FA0_b, scheduler::main_frame_stub); main_frame_hook.create(0x140417FA0, scheduler::main_frame_stub);
} }
void pre_destroy() override void pre_destroy() override

View File

@ -25,7 +25,8 @@ namespace scripting
utils::hook::detour vm_notify_hook; utils::hook::detour vm_notify_hook;
utils::hook::detour g_shutdown_game_hook; utils::hook::detour g_shutdown_game_hook;
utils::hook::detour player_spawn_hook; utils::hook::detour client_spawn_hook;
utils::hook::detour sv_check_load_level_hook;
utils::hook::detour scr_add_class_field_hook; utils::hook::detour scr_add_class_field_hook;
@ -55,9 +56,9 @@ namespace scripting
vm_notify_hook.invoke<void>(notify_list_owner_id, string_value, top); vm_notify_hook.invoke<void>(notify_list_owner_id, string_value, top);
} }
void player_spawn_stub(const game::gentity_s* player) void client_spawn_stub(const game::gentity_s* client)
{ {
player_spawn_hook.invoke<void>(player); client_spawn_hook.invoke<void>(client);
lua::engine::start(); lua::engine::start();
} }
@ -67,16 +68,16 @@ namespace scripting
g_shutdown_game_hook.invoke<void>(free_scripts); g_shutdown_game_hook.invoke<void>(free_scripts);
} }
void scr_add_class_field_stub(unsigned int classnum, game::scr_string_t _name, unsigned int canonicalString, unsigned int offset) void scr_add_class_field_stub(unsigned int classnum, game::scr_string_t name, unsigned int canonicalString, unsigned int offset)
{ {
const auto name = game::SL_ConvertToString(_name); const auto name_ = game::SL_ConvertToString(name);
if (fields_table[classnum].find(name) == fields_table[classnum].end()) if (fields_table[classnum].find(name_) == fields_table[classnum].end())
{ {
fields_table[classnum][name] = offset; fields_table[classnum][name_] = offset;
} }
scr_add_class_field_hook.invoke<void>(classnum, _name, canonicalString, offset); scr_add_class_field_hook.invoke<void>(classnum, name, canonicalString, offset);
} }
void process_script_stub(const char* filename) void process_script_stub(const char* filename)
@ -99,11 +100,10 @@ namespace scripting
scr_set_thread_position_hook.invoke<void>(threadName, codePos); scr_set_thread_position_hook.invoke<void>(threadName, codePos);
} }
utils::hook::detour sub_6B2940_hook; char sv_check_load_level_stub(void* save_game)
char sub_6B2940_stub(void* a1)
{ {
const auto result = sub_6B2940_hook.invoke<char>(a1); const auto result = sv_check_load_level_hook.invoke<char>(save_game);
if (a1 != nullptr) if (save_game != nullptr)
{ {
lua::engine::start(); lua::engine::start();
} }
@ -116,19 +116,15 @@ namespace scripting
public: public:
void post_unpack() override void post_unpack() override
{ {
vm_notify_hook.create(0x5CC450_b, vm_notify_stub); vm_notify_hook.create(0x1405CC450, vm_notify_stub);
g_shutdown_game_hook.create(0x4CBAD0_b, g_shutdown_game_stub); g_shutdown_game_hook.create(0x1404CBAD0, g_shutdown_game_stub);
player_spawn_hook.create(0x4B0710_b, player_spawn_stub); client_spawn_hook.create(0x1404B0710, client_spawn_stub);
sv_check_load_level_hook.create(0x1406B2940, sv_check_load_level_stub);
scr_add_class_field_hook.create(0x5C2C30_b, scr_add_class_field_stub); scr_add_class_field_hook.create(0x1405C2C30, scr_add_class_field_stub);
scr_set_thread_position_hook.create(0x5BC7E0_b, scr_set_thread_position_stub); scr_set_thread_position_hook.create(0x1405BC7E0, scr_set_thread_position_stub);
process_script_hook.create(0x5C6160_b, process_script_stub); process_script_hook.create(0x1405C6160, process_script_stub);
// Loading last checkpoint doesn't call spawn player again (player_spawn_hook)
// Not sure what this function does but `a1` is != nullptr when loading
// the last checkpoint so we need to start lua in this context
sub_6B2940_hook.create(0x6B2940_b, sub_6B2940_stub);
scheduler::loop([]() scheduler::loop([]()
{ {

View File

@ -4,7 +4,6 @@
#include "game/game.hpp" #include "game/game.hpp"
#include "game/dvars.hpp" #include "game/dvars.hpp"
#include "chat.hpp"
#include "scheduler.hpp" #include "scheduler.hpp"
#include "command.hpp" #include "command.hpp"
@ -27,8 +26,12 @@ namespace ui_scripting
utils::hook::detour hksi_lual_error_hook2; 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 lui_error_hook;
utils::hook::detour hksi_hks_error_hook;
utils::hook::detour hks_frame_hook;
bool error_hook_enabled = false; int error_hook_enabled = 0;
void hksi_lual_error_stub(game::hks::lua_State* s, const char* fmt, ...) void hksi_lual_error_stub(game::hks::lua_State* s, const char* fmt, ...)
{ {
@ -45,10 +48,37 @@ namespace ui_scripting
{ {
return hksi_lual_error_hook.invoke<void>(s, formatted.data()); return hksi_lual_error_hook.invoke<void>(s, formatted.data());
} }
else
{
throw std::runtime_error(formatted); throw std::runtime_error(formatted);
} }
void hksi_hks_error_stub(game::hks::lua_State* s, int a2)
{
if (!error_hook_enabled)
{
return hksi_hks_error_hook.invoke<void>(s, a2);
}
throw std::runtime_error("unknown error");
}
void lui_error_stub(game::hks::lua_State* s)
{
if (!error_hook_enabled)
{
return lui_error_hook.invoke<void>(s);
}
const auto count = static_cast<int>(s->m_apistack.top - s->m_apistack.base);
const auto arguments = get_return_values(count);
std::string error_str = "LUI Error";
if (count && arguments[0].is<std::string>())
{
error_str = arguments[0].as<std::string>();
}
throw std::runtime_error(error_str);
} }
void* hks_start_stub(char a1) void* hks_start_stub(char a1)
@ -66,6 +96,26 @@ namespace ui_scripting
ui_scripting::lua::engine::stop(); ui_scripting::lua::engine::stop();
hks_shutdown_hook.invoke<void*>(); hks_shutdown_hook.invoke<void*>();
} }
void* hks_allocator_stub(void* userData, void* oldMemory, unsigned __int64 oldSize, unsigned __int64 newSize)
{
const auto closure = reinterpret_cast<game::hks::cclosure*>(oldMemory);
if (converted_functions.find(closure) != converted_functions.end())
{
converted_functions.erase(closure);
}
return hks_allocator_hook.invoke<void*>(userData, oldMemory, oldSize, newSize);
}
void hks_frame_stub()
{
const auto state = *game::hks::lua_state;
if (state)
{
ui_scripting::lua::engine::run_frame();
}
}
} }
int main_function_handler(game::hks::lua_State* state) int main_function_handler(game::hks::lua_State* state)
@ -117,17 +167,12 @@ namespace ui_scripting
void enable_error_hook() void enable_error_hook()
{ {
error_hook_enabled = true; error_hook_enabled++;
} }
void disable_error_hook() void disable_error_hook()
{ {
error_hook_enabled = false; error_hook_enabled--;
}
void notify(const event& e)
{
lua::engine::notify(e);
} }
class component final : public component_interface class component final : public component_interface
@ -136,12 +181,14 @@ namespace ui_scripting
void post_unpack() override void post_unpack() override
{ {
scheduler::loop(ui_scripting::lua::engine::run_frame, scheduler::pipeline::lui); hks_frame_hook.create(0x140327880, hks_frame_stub);
hks_start_hook.create(0x140328BE0, hks_start_stub);
hks_start_hook.create(0x328BE0_b, hks_start_stub); hks_shutdown_hook.create(0x1403203B0, hks_shutdown_stub);
hks_shutdown_hook.create(0x3203B0_b, hks_shutdown_stub); hksi_lual_error_hook.create(0x1402E3E40, hksi_lual_error_stub);
hksi_lual_error_hook.create(0x2E3E40_b, hksi_lual_error_stub); hksi_lual_error_hook2.create(0x1402DCB40, hksi_lual_error_stub);
hksi_lual_error_hook2.create(0x2DCB40_b, hksi_lual_error_stub); hks_allocator_hook.create(0x1402D92A0, hks_allocator_stub);
lui_error_hook.create(0x1402B9D90, lui_error_stub);
hksi_hks_error_hook.create(0x1402DBC00, hksi_hks_error_stub);
} }
}; };
} }

View File

@ -10,6 +10,4 @@ namespace ui_scripting
void enable_error_hook(); void enable_error_hook();
void disable_error_hook(); void disable_error_hook();
void notify(const event& e);
} }

View File

@ -0,0 +1,408 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "scheduler.hpp"
#include "updater.hpp"
#include "version.h"
#include "game/game.hpp"
#include "game/dvars.hpp"
#include <utils/nt.hpp>
#include <utils/concurrency.hpp>
#include <utils/http.hpp>
#include <utils/cryptography.hpp>
#include <utils/io.hpp>
#include <utils/string.hpp>
#define MASTER "https://master.fed0001.xyz/"
#define FILES_PATH "files.json"
#define FILES_PATH_DEV "files-dev.json"
#define DATA_PATH "data/"
#define DATA_PATH_DEV "data-dev/"
#define ERR_UPDATE_CHECK_FAIL "Failed to check for updates"
#define ERR_DOWNLOAD_FAIL "Failed to download file "
#define ERR_WRITE_FAIL "Failed to write file "
namespace updater
{
namespace
{
game::dvar_t* cl_auto_update;
bool has_tried_update = false;
struct status
{
bool done;
bool success;
};
struct file_data
{
std::string name;
std::string data;
};
struct update_data_t
{
bool restart_required{};
bool cancelled{};
status check{};
status download{};
std::string error{};
std::string current_file{};
std::vector<std::string> required_files{};
};
utils::concurrency::container<update_data_t> update_data;
std::string select(const std::string& main, const std::string& develop)
{
if (GIT_BRANCH == "develop"s)
{
return develop;
}
return main;
}
void set_update_check_status(bool done, bool success, const std::string& error = {})
{
update_data.access([done, success, error](update_data_t& data_)
{
data_.check.done = done;
data_.check.success = success;
data_.error = error;
});
}
void set_update_download_status(bool done, bool success, const std::string& error = {})
{
update_data.access([done, success, error](update_data_t& data_)
{
data_.download.done = done;
data_.download.success = success;
data_.error = error;
});
}
bool check_file(const std::string& name, const std::string& sha)
{
std::string data;
if (!utils::io::read_file(name, &data))
{
return false;
}
if (utils::cryptography::sha1::compute(data, true) != sha)
{
return false;
}
return true;
}
std::string load_binary_name()
{
utils::nt::library self;
return self.get_name();
}
std::string get_binary_name()
{
static const auto name = load_binary_name();
return name;
}
std::string get_time_str()
{
return utils::string::va("%i", uint32_t(time(nullptr)));
}
std::optional<std::string> download_file(const std::string& name)
{
return utils::http::get_data(MASTER + select(DATA_PATH, DATA_PATH_DEV) + name + "?" + get_time_str());
}
bool is_update_cancelled()
{
return update_data.access<bool>([](update_data_t& data_)
{
return data_.cancelled;
});
}
bool write_file(const std::string& name, const std::string& data)
{
if (get_binary_name() == name &&
utils::io::file_exists(name) &&
!utils::io::move_file(name, name + ".old"))
{
return false;
}
return utils::io::write_file(name, data);
}
void delete_old_file()
{
utils::io::remove_file(get_binary_name() + ".old");
}
void reset_data()
{
update_data.access([](update_data_t& data_)
{
data_ = {};
});
}
}
void relaunch()
{
utils::nt::relaunch_self("-singleplayer");
utils::nt::terminate();
}
void set_has_tried_update(bool tried)
{
has_tried_update = tried;
}
bool get_has_tried_update()
{
return has_tried_update;
}
bool auto_updates_enabled()
{
return cl_auto_update->current.enabled;
}
bool is_update_check_done()
{
return update_data.access<bool>([](update_data_t& data_)
{
return data_.check.done;
});
}
bool is_update_download_done()
{
return update_data.access<bool>([](update_data_t& data_)
{
return data_.download.done;
});
}
bool get_update_check_status()
{
return update_data.access<bool>([](update_data_t& data_)
{
return data_.check.success;
});
}
bool get_update_download_status()
{
return update_data.access<bool>([](update_data_t& data_)
{
return data_.download.success;
});
}
bool is_update_available()
{
return update_data.access<bool>([](update_data_t& data_)
{
return data_.required_files.size() > 0;
});
}
bool is_restart_required()
{
return update_data.access<bool>([](update_data_t& data_)
{
return data_.restart_required;
});
}
std::string get_last_error()
{
return update_data.access<std::string>([](update_data_t& data_)
{
return data_.error;
});
}
std::string get_current_file()
{
return update_data.access<std::string>([](update_data_t& data_)
{
return data_.current_file;
});
}
void cancel_update()
{
#ifdef DEBUG
printf("[Updater] Cancelling update\n");
#endif
return update_data.access([](update_data_t& data_)
{
data_.cancelled = true;
});
}
void start_update_check()
{
cancel_update();
reset_data();
#ifdef DEBUG
printf("[Updater] starting update check\n");
#endif
scheduler::once([]()
{
const auto files_data = utils::http::get_data(MASTER + select(FILES_PATH, FILES_PATH_DEV) + "?" + get_time_str());
if (is_update_cancelled())
{
reset_data();
return;
}
if (!files_data.has_value())
{
set_update_check_status(true, false, ERR_UPDATE_CHECK_FAIL);
return;
}
rapidjson::Document j;
j.Parse(files_data.value().data());
if (!j.IsArray())
{
set_update_check_status(true, false, ERR_UPDATE_CHECK_FAIL);
return;
}
std::vector<std::string> required_files;
const auto files = j.GetArray();
for (const auto& file : files)
{
if (!file.IsArray() || file.Size() != 3 || !file[0].IsString() || !file[2].IsString())
{
continue;
}
const auto name = file[0].GetString();
const auto sha = file[2].GetString();
if (!check_file(name, sha))
{
if (get_binary_name() == name)
{
update_data.access([](update_data_t& data_)
{
data_.restart_required = true;
});
}
#ifdef DEBUG
printf("[Updater] need file %s\n", name);
#endif
required_files.push_back(name);
}
}
update_data.access([&required_files](update_data_t& data_)
{
data_.check.done = true;
data_.check.success = true;
data_.required_files = required_files;
});
}, scheduler::pipeline::async);
}
void start_update_download()
{
#ifdef DEBUG
printf("[Updater] starting update download\n");
#endif
if (!is_update_check_done() || !get_update_check_status() || is_update_cancelled())
{
return;
}
scheduler::once([]()
{
const auto required_files = update_data.access<std::vector<std::string>>([](update_data_t& data_)
{
return data_.required_files;
});
std::vector<file_data> downloads;
for (const auto& file : required_files)
{
update_data.access([file](update_data_t& data_)
{
data_.current_file = file;
});
#ifdef DEBUG
printf("[Updater] downloading file %s\n", file.data());
#endif
const auto data = download_file(file);
if (is_update_cancelled())
{
reset_data();
return;
}
if (!data.has_value())
{
set_update_download_status(true, false, ERR_DOWNLOAD_FAIL + file);
return;
}
downloads.push_back({file, data.value()});
}
for (const auto& download : downloads)
{
if (!write_file(download.name, download.data))
{
set_update_download_status(true, false, ERR_WRITE_FAIL + download.name);
return;
}
}
set_update_download_status(true, true);
}, scheduler::pipeline::async);
}
class component final : public component_interface
{
public:
void post_unpack() override
{
delete_old_file();
cl_auto_update = dvars::register_bool("cg_auto_update", true, game::DVAR_FLAG_SAVED);
}
};
}
REGISTER_COMPONENT(updater::component)

View File

@ -0,0 +1,26 @@
#pragma once
namespace updater
{
void relaunch();
void set_has_tried_update(bool tried);
bool get_has_tried_update();
bool auto_updates_enabled();
bool is_update_available();
bool is_update_check_done();
bool get_update_check_status();
bool is_update_download_done();
bool get_update_download_status();
bool is_restart_required();
std::string get_last_error();
std::string get_current_file();
void start_update_check();
void start_update_download();
void cancel_update();
}

View File

@ -3,16 +3,6 @@
namespace game namespace game
{ {
uint64_t base_address;
void load_base_address()
{
const auto module = GetModuleHandle(NULL);
base_address = uint64_t(module);
}
std::string mod_folder{};
namespace environment namespace environment
{ {
launcher::mode mode = launcher::mode::none; launcher::mode mode = launcher::mode::none;
@ -81,8 +71,3 @@ namespace game
} }
} }
} }
uintptr_t operator"" _b(const uintptr_t ptr)
{
return game::base_address + ptr;
}

View File

@ -5,11 +5,6 @@
namespace game namespace game
{ {
extern uint64_t base_address;
void load_base_address();
extern std::string mod_folder;
namespace environment namespace environment
{ {
launcher::mode get_mode(); launcher::mode get_mode();
@ -35,7 +30,7 @@ namespace game
T* get() const T* get() const
{ {
return reinterpret_cast<T*>((uint64_t)address_ + base_address); return reinterpret_cast<T*>(address_);
} }
operator T* () const operator T* () const
@ -53,6 +48,4 @@ namespace game
}; };
} }
uintptr_t operator"" _b(const uintptr_t ptr);
#include "symbols.hpp" #include "symbols.hpp"

View File

@ -59,15 +59,15 @@ 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 = 0xB153F90; static const auto function_table = 0x14B153F90;
static const auto method_table = 0xB155890; static const auto method_table = 0x14B155890;
if (index < 0x320) if (index < 0x320)
{ {
return reinterpret_cast<script_function*>(game::base_address + function_table)[index - 1]; return reinterpret_cast<script_function*>(function_table)[index - 1];
} }
return reinterpret_cast<script_function*>(game::base_address + method_table)[index - 0x8000]; return reinterpret_cast<script_function*>(method_table)[index - 0x8000];
} }
} }

View File

@ -9,7 +9,6 @@
#include "../../../component/notifies.hpp" #include "../../../component/notifies.hpp"
#include "../../../component/scripting.hpp" #include "../../../component/scripting.hpp"
#include "../../../component/command.hpp" #include "../../../component/command.hpp"
#include "../../../component/chat.hpp"
#include "../../../component/fastfiles.hpp" #include "../../../component/fastfiles.hpp"
#include <utils/string.hpp> #include <utils/string.hpp>
@ -48,16 +47,6 @@ namespace scripting::lua
state["level"] = entity{*::game::levelEntityId}; state["level"] = entity{*::game::levelEntityId};
state["player"] = call("getentbynum", {0}).as<entity>(); state["player"] = call("getentbynum", {0}).as<entity>();
state["io"]["fileexists"] = utils::io::file_exists;
state["io"]["writefile"] = utils::io::write_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"]["readfile"] = static_cast<std::string(*)(const std::string&)>(utils::io::read_file);
auto vector_type = state.new_usertype<vector>("vector", sol::constructors<vector(float, float, float)>()); auto vector_type = state.new_usertype<vector>("vector", sol::constructors<vector(float, float, float)>());
vector_type["x"] = sol::property(&vector::get_x, &vector::set_x); vector_type["x"] = sol::property(&vector::get_x, &vector::set_x);
vector_type["y"] = sol::property(&vector::get_y, &vector::set_y); vector_type["y"] = sol::property(&vector::get_y, &vector::set_y);
@ -198,8 +187,8 @@ namespace scripting::lua
void setup_entity_type(sol::state& state, event_handler& handler, scheduler& scheduler) void setup_entity_type(sol::state& state, event_handler& handler, scheduler& scheduler)
{ {
state["level"] = entity{ *::game::levelEntityId }; state["level"] = entity{*::game::levelEntityId};
state["player"] = call("getentbynum", { 0 }).as<entity>(); state["player"] = call("getentbynum", {0}).as<entity>();
auto entity_type = state.new_usertype<entity>("entity"); auto entity_type = state.new_usertype<entity>("entity");
@ -387,9 +376,8 @@ namespace scripting::lua
command::execute(utils::string::va("setdiscordstate %s", state.data()), false); command::execute(utils::string::va("setdiscordstate %s", state.data()), false);
}; };
game_type["say"] = [](const game&, const std::string& msg) game_type["say"] = [](const game&)
{ {
chat::print(msg);
}; };
game_type["detour"] = [](const game&, const sol::this_state s, const std::string& filename, game_type["detour"] = [](const game&, const sol::this_state s, const std::string& filename,

View File

@ -3,6 +3,7 @@
#include "context.hpp" #include "context.hpp"
#include "../../../component/notifies.hpp" #include "../../../component/notifies.hpp"
#include "../../../component/filesystem.hpp"
#include "../execution.hpp" #include "../execution.hpp"
#include <utils/io.hpp> #include <utils/io.hpp>
@ -49,13 +50,9 @@ namespace scripting::lua::engine
load_generic_script(); load_generic_script();
load_scripts("scripts/"); for (const auto& path : filesystem::get_search_paths())
load_scripts("h2-mod/scripts/");
load_scripts("data/scripts/");
if (!game::mod_folder.empty())
{ {
load_scripts(utils::string::va("%s/scripts/", game::mod_folder.data())); load_scripts(path + "/scripts/");
} }
} }

View File

@ -35,14 +35,18 @@ namespace scripting::lua
for (auto i = tasks.begin(); i != tasks.end();) for (auto i = tasks.begin(); i != tasks.end();)
{ {
if (i->is_deleted)
{
i = tasks.erase(i);
continue;
}
if (i->event != event.name || i->entity != event.entity) if (i->event != event.name || i->entity != event.entity)
{ {
++i; ++i;
continue; continue;
} }
if (!i->is_deleted)
{
if (!has_built_arguments) if (!has_built_arguments)
{ {
has_built_arguments = true; has_built_arguments = true;
@ -50,7 +54,6 @@ namespace scripting::lua
} }
handle_error(i->callback(sol::as_args(arguments))); handle_error(i->callback(sol::as_args(arguments)));
}
if (i->is_volatile || i->is_deleted) if (i->is_volatile || i->is_deleted)
{ {

View File

@ -44,7 +44,7 @@ namespace scripting::lua
sol::state& state_; sol::state& state_;
std::atomic_int64_t current_listener_id_ = 0; std::atomic_int64_t current_listener_id_ = 0;
using task_list = std::vector<event_listener>; using task_list = std::list<event_listener>;
utils::concurrency::container<task_list> new_callbacks_; utils::concurrency::container<task_list> new_callbacks_;
utils::concurrency::container<task_list, std::recursive_mutex> callbacks_; utils::concurrency::container<task_list, std::recursive_mutex> callbacks_;

View File

@ -126,11 +126,90 @@ namespace game
// ... // ...
}; };
struct GfxImage;
union MaterialTextureDefInfo
{
GfxImage* image;
void* water;
};
struct MaterialTextureDef
{
unsigned int nameHash;
char nameStart;
char nameEnd;
char samplerState;
char semantic;
MaterialTextureDefInfo u;
};
struct MaterialPass
{
void* vertexShader;
void* vertexDecl;
void* hullShader;
void* domainShader;
void* pixelShader;
char pixelOutputMask;
char perPrimArgCount;
char perObjArgCount;
char stableArgCount;
unsigned __int16 perPrimArgSize;
unsigned __int16 perObjArgSize;
unsigned __int16 stableArgSize;
char zone;
char perPrimConstantBuffer;
char perObjConstantBuffer;
char stableConstantBuffer;
unsigned int customBufferFlags;
char customSamplerFlags;
char precompiledIndex;
char stageConfig;
void* args;
};
struct MaterialTechnique
{
const char* name;
unsigned __int16 flags;
unsigned __int16 passCount;
MaterialPass passArray[1];
};
struct MaterialTechniqueSet
{
const char* name;
unsigned __int16 flags;
char worldVertFormat;
char preDisplacementOnlyCount;
MaterialTechnique* techniques[309];
};
struct GfxStateBits
{
unsigned int loadBits[3];
char zone;
char depthStencilState[11];
char blendState;
char rasterizerState;
};
struct Material struct Material
{ {
const char* name; const char* name;
char __pad0[0x124];
char textureCount;
char __pad1[0xB];
MaterialTechniqueSet* techniqueSet;
MaterialTextureDef* textureTable;
void* constantTable;
GfxStateBits* stateBitsTable;
char __pad2[0x118];
}; };
static_assert(sizeof(Material) == 0x270);
struct point struct point
{ {
float x; float x;
@ -678,6 +757,14 @@ namespace game
const char* buffer; const char* buffer;
}; };
struct TTF
{
const char* name;
int len;
const char* buffer;
int fontFace;
};
union XAssetHeader union XAssetHeader
{ {
void* data; void* data;
@ -687,6 +774,7 @@ namespace game
ScriptFile* scriptfile; ScriptFile* scriptfile;
StringTable* stringTable; StringTable* stringTable;
LuaFile* luaFile; LuaFile* luaFile;
TTF* ttf;
}; };
struct XAsset struct XAsset

View File

@ -6,198 +6,210 @@ namespace game
{ {
// Functions // Functions
WEAK symbol<void(int type, VariableUnion u)> AddRefToValue{0x5C0EB0}; WEAK symbol<void(int type, VariableUnion u)> AddRefToValue{0x1405C0EB0};
WEAK symbol<void(unsigned int id)> AddRefToObject{0x5C0EA0}; WEAK symbol<void(unsigned int id)> AddRefToObject{0x1405C0EA0};
WEAK symbol<unsigned int(unsigned int id)> AllocThread{0x5C1200}; WEAK symbol<unsigned int(unsigned int id)> AllocThread{0x1405C1200};
WEAK symbol<ObjectVariableValue*(unsigned int* id)> AllocVariable{0x5C1260}; WEAK symbol<ObjectVariableValue*(unsigned int* id)> AllocVariable{0x1405C1260};
WEAK symbol<void(int type, VariableUnion u)> RemoveRefToValue{0x5C29B0}; WEAK symbol<void(int type, VariableUnion u)> RemoveRefToValue{0x1405C29B0};
WEAK symbol<void(unsigned int id)> RemoveRefToObject{0x5C28A0}; WEAK symbol<void(unsigned int id)> RemoveRefToObject{0x1405C28A0};
WEAK symbol<void(unsigned int weapon, bool isAlternate, WEAK symbol<void(unsigned int weapon, bool isAlternate,
char* output, unsigned int maxStringLen)> BG_GetWeaponNameComplete{0x6A0800}; char* output, unsigned int maxStringLen)> BG_GetWeaponNameComplete{0x1406A0800};
WEAK symbol<void(int localClientNum, const char* text)> Cbuf_AddText{0x59A050}; WEAK symbol<void(int localClientNum, const char* text)> Cbuf_AddText{0x14059A050};
WEAK symbol<void(int localClientNum, const char* message)> CG_GameMessage{0x37F450}; WEAK symbol<void(int localClientNum, const char* message)> CG_GameMessage{0x14037F450};
WEAK symbol<void(int localClientNum, const char* message)> CG_GameMessageBold{0x37F1B0}; WEAK symbol<void(int localClientNum, const char* message)> CG_GameMessageBold{0x14037F1B0};
WEAK symbol<char*(const unsigned int weapon, WEAK symbol<char*(const unsigned int weapon,
bool isAlternate, char* outputBuffer, int bufferLen)> CG_GetWeaponDisplayName{0x3B9210}; bool isAlternate, char* outputBuffer, int bufferLen)> CG_GetWeaponDisplayName{0x1403B9210};
WEAK symbol<void(const char* cmdName, void(), cmd_function_s* allocedCmd)> Cmd_AddCommandInternal{0x59A5F0}; WEAK symbol<void(const char* cmdName, void(), cmd_function_s* allocedCmd)> Cmd_AddCommandInternal{0x14059A5F0};
WEAK symbol<void(int localClientNum, int controllerIndex, const char* text)> Cmd_ExecuteSingleCommand{0x59ABA0}; WEAK symbol<void(int localClientNum, int controllerIndex, const char* text)> Cmd_ExecuteSingleCommand{0x14059ABA0};
WEAK symbol<void(errorParm code, const char* message, ...)> Com_Error{0x5A2D80}; WEAK symbol<void(errorParm code, const char* message, ...)> Com_Error{0x1405A2D80};
WEAK symbol<void()> Com_Quit_f{0x5A50D0}; WEAK symbol<void(char const* finalMessage)> Com_Shutdown{0x1405A62C0};
WEAK symbol<void()> Quit{0x5A52A0}; WEAK symbol<void()> Com_Quit_f{0x1405A50D0};
WEAK symbol<bool()> Com_InFrontend{0x140328BD0};
WEAK symbol<void()> Quit{0x1405A52A0};
WEAK symbol<void(XAssetType type, void(__cdecl* func)(game::XAssetHeader, void*), const void* inData, bool includeOverride)> WEAK symbol<void(XAssetType type, void(__cdecl* func)(game::XAssetHeader, void*), const void* inData, bool includeOverride)>
DB_EnumXAssets_Internal{0x4129F0}; DB_EnumXAssets_Internal{0x1404129F0};
WEAK symbol<const char*(const XAsset* asset)> DB_GetXAssetName{0x3E4090}; WEAK symbol<const char*(const XAsset* asset)> DB_GetXAssetName{0x1403E4090};
WEAK symbol<void(XZoneInfo* zoneInfo, unsigned int zoneCount, DBSyncMode syncMode)> DB_LoadXAssets{0x414FF0}; WEAK symbol<void(XZoneInfo* zoneInfo, unsigned int zoneCount, DBSyncMode syncMode)> DB_LoadXAssets{0x140414FF0};
WEAK symbol<XAssetHeader(XAssetType type, const char* name, int allowCreateDefault)> DB_FindXAssetHeader{0x412F60}; WEAK symbol<XAssetHeader(XAssetType type, const char* name, int allowCreateDefault)> DB_FindXAssetHeader{0x140412F60};
WEAK symbol<int(const RawFile* rawfile)> DB_GetRawFileLen{0x413D80}; WEAK symbol<int(const RawFile* rawfile)> DB_GetRawFileLen{0x140413D80};
WEAK symbol<int(const RawFile* rawfile, char* buf, int size)> DB_GetRawBuffer{0x413C40}; WEAK symbol<int(const RawFile* rawfile, char* buf, int size)> DB_GetRawBuffer{0x140413C40};
WEAK symbol<dvar_t*(const char* name)> Dvar_FindVar{0x618F90}; WEAK symbol<dvar_t*(const char* name)> Dvar_FindVar{0x140618F90};
WEAK symbol<void(char* buffer, int index)> Dvar_GetCombinedString{0x5A75D0}; WEAK symbol<void(char* buffer, int index)> Dvar_GetCombinedString{0x1405A75D0};
WEAK symbol<dvar_t*(int hash, const char* name, bool value, unsigned int flags)> Dvar_RegisterBool{0x617BB0}; WEAK symbol<dvar_t*(int hash, const char* name, bool value, unsigned int flags)> Dvar_RegisterBool{0x140617BB0};
WEAK symbol<dvar_t*(int hash, const char* name, int value, int min, int max, unsigned int flags)> Dvar_RegisterInt{0x618090}; WEAK symbol<dvar_t*(int hash, const char* name, int value, int min, int max, unsigned int flags)> Dvar_RegisterInt{0x140618090};
WEAK symbol<dvar_t*(int hash, const char* dvarName, float value, float min, float max, unsigned int flags)> WEAK symbol<dvar_t*(int hash, const char* dvarName, float value, float min, float max, unsigned int flags)>
Dvar_RegisterFloat{0x617F80}; Dvar_RegisterFloat{0x140617F80};
WEAK symbol<dvar_t*(int hash, const char* dvarName, const char* value, unsigned int flags)> WEAK symbol<dvar_t*(int hash, const char* dvarName, const char* value, unsigned int flags)>
Dvar_RegisterString{0x618170}; Dvar_RegisterString{0x140618170};
WEAK symbol<dvar_t*(int dvarName, const char* a2, float x, float y, float z, float w, float min, float max, 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{0x6185F0}; unsigned int flags)> Dvar_RegisterVec4{0x1406185F0};
WEAK symbol<const char*(const dvar_t* dvar)> Dvar_DisplayableValue{0x618EA0}; WEAK symbol<const char*(const dvar_t* dvar)> Dvar_DisplayableValue{0x140618EA0};
WEAK symbol<const char*(dvar_t* dvar, void* a2, void* value)> Dvar_ValueToString{0x61B8F0}; WEAK symbol<const char*(dvar_t* dvar, void* a2, void* value)> Dvar_ValueToString{0x14061B8F0};
WEAK symbol<void(int hash, const char* name, const char* buffer)> Dvar_SetCommand{0x61A5C0}; WEAK symbol<void(int hash, const char* name, const char* buffer)> Dvar_SetCommand{0x14061A5C0};
WEAK symbol<void(const char* dvarName, const char* string, DvarSetSource source)> Dvar_SetFromStringFromSource{0x61A910}; WEAK symbol<void(const char* dvarName, const char* string, DvarSetSource source)> Dvar_SetFromStringFromSource{0x14061A910};
WEAK symbol<int(const char* fname)> generateHashValue{0x343D20}; WEAK symbol<int(const char* fname)> generateHashValue{0x140343D20};
WEAK symbol<bool()> CL_IsCgameInitialized{0x3CA0C0}; WEAK symbol<bool()> CL_IsCgameInitialized{0x1403CA0C0};
WEAK symbol<void(const char* text, int maxChars, Font_s* font, float x, float y, float xScale, float yScale, WEAK symbol<void(const char* text, int maxChars, Font_s* font, float x, float y, float xScale, float yScale,
const float* color, int style, const float* glowColor, Material* fxMaterial, Material* fxMaterialGlow, const float* color, int style, const float* glowColor, Material* fxMaterial, Material* fxMaterialGlow,
int fxBirthTime, int fxLetterTime, int fxDecayStartTime, int fxDecayDuration, int a17)> CL_DrawTextPhysicalWithEffects{0x3D4990}; int fxBirthTime, int fxLetterTime, int fxDecayStartTime, int fxDecayDuration, int a17)> CL_DrawTextPhysicalWithEffects{0x1403D4990};
WEAK symbol<unsigned int(unsigned int parentId, unsigned int name)> FindVariable{0x5C1D50}; WEAK symbol<unsigned int(unsigned int parentId, unsigned int name)> FindVariable{0x1405C1D50};
WEAK symbol<unsigned int(int entnum, unsigned int classnum)> FindEntityId{0x5C1C50}; WEAK symbol<unsigned int(int entnum, unsigned int classnum)> FindEntityId{0x1405C1C50};
WEAK symbol<void(VariableValue* result, unsigned int classnum, int entnum, int offset)> GetEntityFieldValue{0x5C6100}; WEAK symbol<void(VariableValue* result, unsigned int classnum, int entnum, int offset)> GetEntityFieldValue{0x1405C6100};
WEAK symbol<unsigned int(unsigned int parentId, unsigned int unsignedValue)> GetVariable{0x5C2690}; WEAK symbol<unsigned int(unsigned int parentId, unsigned int unsignedValue)> GetVariable{0x1405C2690};
WEAK symbol<unsigned int(unsigned int parentId, unsigned int unsignedValue)> GetNewVariable{0x5C22B0}; WEAK symbol<unsigned int(unsigned int parentId, unsigned int unsignedValue)> GetNewVariable{0x1405C22B0};
WEAK symbol<unsigned int(unsigned int parentId, unsigned int unsignedValue)> GetNewArrayVariable{0x5C2130}; WEAK symbol<unsigned int(unsigned int parentId, unsigned int unsignedValue)> GetNewArrayVariable{0x1405C2130};
WEAK symbol<void(unsigned int parentId, unsigned int id, VariableValue* value)> SetNewVariableValue{0x5C5EA0}; WEAK symbol<void(unsigned int parentId, unsigned int id, VariableValue* value)> SetNewVariableValue{0x1405C5EA0};
WEAK symbol<void(unsigned int parentId, unsigned int index)> RemoveVariableValue{0x5C2A50}; WEAK symbol<void(unsigned int parentId, unsigned int index)> RemoveVariableValue{0x1405C2A50};
WEAK symbol<unsigned int(const char* name)> G_GetWeaponForName{0x51B260}; WEAK symbol<unsigned int(const char* name)> G_GetWeaponForName{0x14051B260};
WEAK symbol<int(void* ps, unsigned int weapon, int a3, int a4, __int64 a5, int a6)> WEAK symbol<int(void* ps, unsigned int weapon, int a3, int a4, __int64 a5, int a6)>
G_GivePlayerWeapon{0x51B660}; G_GivePlayerWeapon{0x14051B660};
WEAK symbol<void(void* ps, const unsigned int weapon, int hadWeapon)> G_InitializeAmmo{0x4C4110}; WEAK symbol<void(void* ps, const unsigned int weapon, int hadWeapon)> G_InitializeAmmo{0x1404C4110};
WEAK symbol<void(int clientNum, const unsigned int weapon)> G_SelectWeapon{0x51C0D0}; WEAK symbol<void(int clientNum, const unsigned int weapon)> G_SelectWeapon{0x14051C0D0};
WEAK symbol<bool(int localClientNum, ScreenPlacement* screenPlacement, const float* worldDir, float* outScreenPos)> WorldPosToScreenPos{0x36F310}; WEAK symbol<bool(int localClientNum, ScreenPlacement* screenPlacement, const float* worldDir, float* outScreenPos)> WorldPosToScreenPos{0x14036F310};
WEAK symbol<char*(char* string)> I_CleanStr{0x620660}; WEAK symbol<char*(char* string)> I_CleanStr{0x140620660};
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, int a8, const char* name, const void* initData)> Image_Setup{0x74B2A0}; uint32_t imageFlags, DXGI_FORMAT imageFormat, int a8, const char* name, const void* initData)> Image_Setup{0x14074B2A0};
WEAK symbol<const char*(int, int, int)> Key_KeynumToString{0x3D32D0}; WEAK symbol<const char*(int, int, int)> Key_KeynumToString{0x1403D32D0};
WEAK symbol<void(int clientNum, const char* menu, int a3, int a4, unsigned int a5)> LUI_OpenMenu{0x5F0EE0}; WEAK symbol<void(int clientNum, const char* menu, int a3, int a4, unsigned int a5)> LUI_OpenMenu{0x1405F0EE0};
WEAK symbol<bool(int clientNum, const char* menu)> Menu_IsMenuOpenAndVisible{0x5EE1A0}; WEAK symbol<bool(int clientNum, const char* name, hks::lua_State* s)> LUI_BeginEvent{0x1403155E0};
WEAK symbol<void(hks::lua_State* s)> LUI_EndEvent{0x140316890};
WEAK symbol<void()> LUI_EnterCriticalSection{0x140316980};
WEAK symbol<void()> LUI_LeaveCriticalSection{0x14031BC20};
WEAK symbol<bool(int clientNum, const char* menu)> Menu_IsMenuOpenAndVisible{0x1405EE1A0};
WEAK symbol<Material*(const char* material)> Material_RegisterHandle{0x759BA0}; WEAK symbol<Material*(const char* material)> Material_RegisterHandle{0x140759BA0};
WEAK symbol<void(pathnode_t*, float* out)> PathNode_WorldifyPosFromParent{0x525830}; WEAK symbol<void(pathnode_t*, float* out)> PathNode_WorldifyPosFromParent{0x140525830};
WEAK symbol<const float*(const float* v)> Scr_AllocVector{0x5C3220}; WEAK symbol<const float*(const float* v)> Scr_AllocVector{0x1405C3220};
WEAK symbol<void()> Scr_ClearOutParams{0x5C6E50}; WEAK symbol<void()> Scr_ClearOutParams{0x1405C6E50};
WEAK symbol<scr_entref_t(unsigned int entId)> Scr_GetEntityIdRef{0x5C56C0}; WEAK symbol<scr_entref_t(unsigned int entId)> Scr_GetEntityIdRef{0x1405C56C0};
WEAK symbol<unsigned int(int entnum, unsigned int classnum)> Scr_GetEntityId{0x5C5610}; WEAK symbol<unsigned int(int entnum, unsigned int classnum)> Scr_GetEntityId{0x1405C5610};
WEAK symbol<int(unsigned int classnum, int entnum, int offset)> Scr_SetObjectField{0x512190}; WEAK symbol<int(unsigned int classnum, int entnum, int offset)> Scr_SetObjectField{0x140512190};
WEAK symbol<void(unsigned int id, scr_string_t stringValue, unsigned int paramcount)> Scr_NotifyId{0x5C8240}; WEAK symbol<void(unsigned int id, scr_string_t stringValue, unsigned int paramcount)> Scr_NotifyId{0x1405C8240};
WEAK symbol<unsigned int(unsigned int threadId)> Scr_GetSelf{0x5C57C0}; WEAK symbol<unsigned int(unsigned int threadId)> Scr_GetSelf{0x1405C57C0};
WEAK symbol<void()> Scr_ErrorInternal{0x5C6EC0}; WEAK symbol<void()> Scr_ErrorInternal{0x1405C6EC0};
WEAK symbol<unsigned int(unsigned int localId, const char* pos, unsigned int paramcount)> VM_Execute{0x5C8DB0}; WEAK symbol<unsigned int(unsigned int localId, const char* pos, unsigned int paramcount)> VM_Execute{0x1405C8DB0};
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{0x3C9710}; float* color, Material* material)> R_AddCmdDrawStretchPic{0x1403C9710};
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 angle, float* color, Material* material)> R_AddCmdDrawStretchPicRotateXY{0x3C99B0}; float angle, float* color, Material* material)> R_AddCmdDrawStretchPicRotateXY{0x1403C99B0};
WEAK symbol<void*(const char* text, int maxChars, Font_s* font, float x, float y, float xScale, float yScale, WEAK symbol<void*(const char* text, int maxChars, Font_s* font, float x, float y, float xScale, float yScale,
float rotation, float* color, int style)> R_AddCmdDrawText{0x76C660}; float rotation, float* color, int style)> R_AddCmdDrawText{0x14076C660};
WEAK symbol<void(const char* text, int maxChars, Font_s* font, float x, float y, float xScale, float yScale, WEAK symbol<void(const char* text, int maxChars, Font_s* font, float x, float y, float xScale, float yScale,
float rotation, float* color1, float* color2, int style)> R_AddCmdDrawText2{0x76C860}; float rotation, float* color1, float* color2, int style)> R_AddCmdDrawText2{0x14076C860};
WEAK symbol<void(rectangle* rect, float a2, float a3, float a4, float a5, float* color, Material* material)> R_DrawRectangle{0x76A280}; WEAK symbol<void(rectangle* rect, float a2, float a3, float a4, float a5, float* color, Material* material)> R_DrawRectangle{0x14076A280};
WEAK symbol<void(const char* text, int maxChars, Font_s* font, int fontSize, float x, float y, float xScale, float yScale, float rotation, WEAK symbol<void(const char* text, int maxChars, Font_s* font, int fontSize, float x, float y, float xScale, float yScale, float rotation,
const float* color, int style, int cursorPos, char cursor)> R_AddCmdDrawTextWithCursor{0x76CAF0}; const float* color, int style, int cursorPos, char cursor)> R_AddCmdDrawTextWithCursor{0x14076CAF0};
WEAK symbol<void*(const char* text, int maxChars, Font_s* font, float x, float y, float xScale, float yScale, WEAK symbol<void*(const char* text, int maxChars, Font_s* font, float x, float y, float xScale, float yScale,
int a8, float* color1, unsigned int style, rgba color2)> R_AddCmdDrawTextGradient{0x76C570}; int a8, float* color1, unsigned int style, rgba color2)> R_AddCmdDrawTextGradient{0x14076C570};
WEAK symbol<Font_s*(const char* font, int size)> R_RegisterFont{0x746FE0}; WEAK symbol<Font_s*(const char* font, int size)> R_RegisterFont{0x140746FE0};
WEAK symbol<int(const char* text, int maxChars, Font_s* font)> R_TextWidth{0x7472A0}; WEAK symbol<int(const char* text, int maxChars, Font_s* font)> R_TextWidth{0x1407472A0};
WEAK symbol<void()> R_SyncRenderThread{0x76E7D0}; WEAK symbol<void()> R_SyncRenderThread{0x14076E7D0};
WEAK symbol<void()> R_WaitWorkerCmds{0x140794330};
WEAK symbol<void(const void* obj, void* pose, unsigned int entnum, unsigned int renderFxFlags, float* lightingOrigin, WEAK symbol<void(const void* obj, void* pose, unsigned int entnum, unsigned int renderFxFlags, float* lightingOrigin,
float materialTime, __int64 a7, __int64 a8)> R_AddDObjToScene{0x775C40}; float materialTime, __int64 a7, __int64 a8)> R_AddDObjToScene{0x140775C40};
WEAK symbol<ScreenPlacement*()> ScrPlace_GetViewPlacement{0x3E16A0}; WEAK symbol<ScreenPlacement*()> ScrPlace_GetViewPlacement{0x1403E16A0};
WEAK symbol<ScreenPlacement*()> ScrPlace_GetView{0x3E1660}; WEAK symbol<ScreenPlacement*()> ScrPlace_GetView{0x1403E1660};
WEAK symbol<const char*(scr_string_t stringValue)> SL_ConvertToString{0x5BFBB0}; WEAK symbol<const char*(scr_string_t stringValue)> SL_ConvertToString{0x1405BFBB0};
WEAK symbol<scr_string_t(const char* str, unsigned int user)> SL_GetString{0x5C0170}; WEAK symbol<scr_string_t(const char* str, unsigned int user)> SL_GetString{0x1405C0170};
WEAK symbol<bool()> SV_Loaded{0x6B3860}; WEAK symbol<bool()> SV_Loaded{0x1406B3860};
WEAK symbol<void()> Sys_ShowConsole{0x633080}; WEAK symbol<void()> Sys_ShowConsole{0x140633080};
WEAK symbol<bool()> Sys_IsDatabaseReady2{0x5A9FE0}; WEAK symbol<bool()> Sys_IsDatabaseReady2{0x1405A9FE0};
WEAK symbol<int()> Sys_Milliseconds{0x650720}; WEAK symbol<int()> Sys_Milliseconds{0x140650720};
WEAK symbol<bool()> Sys_IsMainThread{0x5AA020}; WEAK symbol<bool()> Sys_IsMainThread{0x1405AA020};
WEAK symbol<void(int critSec)> Sys_EnterCriticalSection{0x140624240};
WEAK symbol<void(int critSec)> Sys_LeaveCriticalSection{0x1406242C0};
WEAK symbol<const char*(const char* string)> UI_SafeTranslateString{0x5A2930}; WEAK symbol<const char*(const char* string)> UI_SafeTranslateString{0x1405A2930};
WEAK symbol<int(int localClientNum, const char* sound)> UI_PlayLocalSoundAlias{0x606080}; WEAK symbol<int(int localClientNum, const char* sound)> UI_PlayLocalSoundAlias{0x140606080};
WEAK symbol<void(pmove_t* move, trace_t*, const float*, const float*, WEAK symbol<void(pmove_t* move, trace_t*, const float*, const float*,
const Bounds*, int, int)> PM_playerTrace{0x68F0A0}; const Bounds*, int, int)> PM_playerTrace{0x14068F0A0};
WEAK symbol<void(pmove_t*, trace_t*, const float*, const float*, WEAK symbol<void(pmove_t*, trace_t*, const float*, const float*,
const Bounds*, int, int)> PM_trace{0x68F1D0}; const Bounds*, int, int)> PM_trace{0x14068F1D0};
WEAK symbol<void*(jmp_buf* Buf, int Value)> longjmp{0x89EED0}; WEAK symbol<void*(jmp_buf* Buf, int Value)> longjmp{0x14089EED0};
WEAK symbol<int(jmp_buf* Buf)> _setjmp{0x8EC2E0}; WEAK symbol<int(jmp_buf* Buf)> _setjmp{0x1408EC2E0};
// Variables // Variables
WEAK symbol<cmd_function_s*> cmd_functions{0xAD17BB8}; WEAK symbol<cmd_function_s*> cmd_functions{0x14AD17BB8};
WEAK symbol<CmdArgs> cmd_args{0xAD17A60}; WEAK symbol<CmdArgs> cmd_args{0x14AD17A60};
WEAK symbol<const char*> command_whitelist{0xBF84E0}; WEAK symbol<const char*> command_whitelist{0x140BF84E0};
WEAK symbol<HWND> hWnd{0xCCF81C0}; WEAK symbol<HWND> hWnd{0x14CCF81C0};
WEAK symbol<const char*> g_assetNames{0xBEF280}; WEAK symbol<const char*> g_assetNames{0x140BEF280};
WEAK symbol<int> g_poolSize{0xBF2E40}; WEAK symbol<int> g_poolSize{0x140BF2E40};
WEAK symbol<gentity_s> g_entities{0x52DDDA0}; WEAK symbol<gentity_s> g_entities{0x1452DDDA0};
WEAK symbol<int> num_entities{0x55CC738}; WEAK symbol<int> num_entities{0x1455CC738};
WEAK symbol<PathData> pathData{0x52CCDA0}; WEAK symbol<PathData> pathData{0x1452CCDA0};
WEAK symbol<int> vehicle_pathnode_count{0xD009A30}; WEAK symbol<int> vehicle_pathnode_count{0x14D009A30};
WEAK symbol<DWORD> threadIds{0xB11DC80}; WEAK symbol<DWORD> threadIds{0x14B11DC80};
WEAK symbol<GfxDrawMethod_s> gfxDrawMethod{0xEDF9E00}; WEAK symbol<GfxDrawMethod_s> gfxDrawMethod{0x14EDF9E00};
WEAK symbol<refdef_t> refdef{0x1BC2500}; WEAK symbol<refdef_t> refdef{0x141BC2500};
WEAK symbol<int> keyCatchers{0x203F3C0}; WEAK symbol<int> keyCatchers{0x14203F3C0};
WEAK symbol<PlayerKeyState> playerKeys{0x1E8767C}; WEAK symbol<PlayerKeyState> playerKeys{0x141E8767C};
WEAK symbol<int> dvarCount{0xBFBB310}; WEAK symbol<int> dvarCount{0x14BFBB310};
WEAK symbol<dvar_t> dvarPool{0xBFBB320}; WEAK symbol<dvar_t> dvarPool{0x14BFBB320};
WEAK symbol<unsigned int> levelEntityId{0xB5E0B30}; WEAK symbol<unsigned int> levelEntityId{0x14B5E0B30};
WEAK symbol<int> g_script_error_level{0xBA9CC24}; WEAK symbol<int> g_script_error_level{0x14BA9CC24};
WEAK symbol<jmp_buf> g_script_error{0xBA9CD40}; WEAK symbol<jmp_buf> g_script_error{0x14BA9CD40};
WEAK symbol<scr_classStruct_t> g_classMap{0xBF95C0}; WEAK symbol<scr_classStruct_t> g_classMap{0x140BF95C0};
WEAK symbol<scrVarGlob_t> scr_VarGlob{0xB617C00}; WEAK symbol<scrVarGlob_t> scr_VarGlob{0x14B617C00};
WEAK symbol<scrVmPub_t> scr_VmPub{0xBA9EE40}; WEAK symbol<scrVmPub_t> scr_VmPub{0x14BA9EE40};
WEAK symbol<function_stack_t> scr_function_stack{0xBAA93C0}; WEAK symbol<function_stack_t> scr_function_stack{0x14BAA93C0};
namespace hks namespace hks
{ {
WEAK symbol<lua_State*> lua_state{0x19D83E8}; WEAK symbol<lua_State*> lua_state{0x1419D83E8};
WEAK symbol<void(lua_State* s, const char* str, unsigned int l)> hksi_lua_pushlstring{0x287410}; WEAK symbol<void(lua_State* s, const char* str, unsigned int l)> hksi_lua_pushlstring{0x140287410};
WEAK symbol<const char*(lua_State* s, const HksObject* obj, unsigned int* len)> hks_obj_tolstring{0x287410}; WEAK symbol<const char*(lua_State* s, const HksObject* obj, unsigned int* len)> hks_obj_tolstring{0x140287410};
WEAK symbol<int(lua_State* s, const HksObject* obj, HksObject* ret)> hks_obj_getmetatable{0x2DA210}; WEAK symbol<int(lua_State* s, const HksObject* obj, HksObject* ret)> hks_obj_getmetatable{0x1402DA210};
WEAK symbol<HksObject*(HksObject* result, lua_State* s, const HksObject* table, const HksObject* key)> hks_obj_getfield{0x2D9E20}; WEAK symbol<HksObject*(HksObject* result, lua_State* s, const HksObject* table, const HksObject* key)> hks_obj_getfield{0x1402D9E20};
WEAK symbol<void(lua_State* s, const HksObject* tbl, const HksObject* key, const HksObject* val)> hks_obj_settable{0x2DB040}; WEAK symbol<void(lua_State* s, const HksObject* tbl, const HksObject* key, const HksObject* val)> hks_obj_settable{0x1402DB040};
WEAK symbol<HksObject* (HksObject* result, lua_State* s, const HksObject* table, const HksObject* key)> hks_obj_gettable{0x2DA300}; WEAK symbol<HksObject* (HksObject* result, lua_State* s, const HksObject* table, const HksObject* key)> hks_obj_gettable{0x1402DA300};
WEAK symbol<void(lua_State* s, int nargs, int nresults, const unsigned int* pc)> vm_call_internal{0x30AB60}; WEAK symbol<void(lua_State* s, int nargs, int nresults, const unsigned int* pc)> vm_call_internal{0x14030AB60};
WEAK symbol<void(lua_State* s, int index)> hksi_lua_pushvalue{0x2DE040}; WEAK symbol<void(lua_State* s, int index)> hksi_lua_pushvalue{0x1402DE040};
WEAK symbol<HashTable*(lua_State* s, unsigned int arraySize, unsigned int hashSize)> Hashtable_Create{0x2C8290}; WEAK symbol<HashTable*(lua_State* s, unsigned int arraySize, unsigned int hashSize)> Hashtable_Create{0x1402C8290};
WEAK symbol<HksObject*(HashTable* t, HksObject* result, HksObject* key)> Hashtable_getNextHash{0x2D5150}; WEAK symbol<HksObject*(HashTable* t, HksObject* result, HksObject* key)> Hashtable_getNextHash{0x1402D5150};
WEAK symbol<void(lua_State* s, const HksObject* tbl, HksObject* key, HksObject* retval)> hks_obj_next{0x2DA850}; WEAK symbol<void(lua_State* s, const HksObject* tbl, HksObject* key, HksObject* retval)> hks_obj_next{0x1402DA850};
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{0x2C84B0}; int internal_, int profilerTreatClosureAsFunc)> cclosure_Create{0x1402C84B0};
WEAK symbol<int(lua_State* s, int t)> hksi_luaL_ref{0x2E4520}; WEAK symbol<int(lua_State* s, int t)> hksi_luaL_ref{0x1402E4520};
WEAK symbol<void(lua_State* s, int t, int ref)> hksi_luaL_unref{0x2DCE50}; WEAK symbol<void(lua_State* s, int t, int ref)> hksi_luaL_unref{0x1402DCE50};
WEAK symbol<void(lua_State* s, int index, const char* k)> hksi_lua_setfield{0x1402DEA30};
WEAK symbol<int(lua_State* s, int nargs, int nresults, int errfunc)> hksi_lua_pcall{0x1402DDE50};
WEAK symbol<void(lua_State* s, HksObject* lfp)> closePendingUpvalues{0x1402CBAD0};
} }
} }

View File

@ -37,38 +37,79 @@ namespace ui_scripting
return values; return values;
} }
bool notify(const std::string& name, const event_arguments& arguments)
{
const auto state = *game::hks::lua_state;
if (!state)
{
return false;
}
const auto _1 = gsl::finally(game::LUI_LeaveCriticalSection);
game::LUI_EnterCriticalSection();
try
{
const auto globals = table((*::game::hks::lua_state)->globals.v.table);
const auto engine = globals.get("Engine").as<table>();
const auto root = engine.get("GetLuiRoot").as<function>().call({})[0].as<userdata>();
const auto process_event = root.get("processEvent").as<function>();
table event{};
event.set("name", name);
for (const auto& arg : arguments)
{
event.set(arg.first, arg.second);
}
process_event.call({root, event});
return true;
}
catch (const std::exception& e)
{
printf("Error processing event '%s' %s\n", name.data(), e.what());
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;
state->m_apistack.top = state->m_apistack.base;
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());
stack.save(num_args + 1);
const auto _1 = gsl::finally(&disable_error_hook); const auto _1 = gsl::finally(&disable_error_hook);
enable_error_hook(); enable_error_hook();
try try
{ {
game::hks::vm_call_internal(state, static_cast<int>(arguments.size()), -1, 0); game::hks::vm_call_internal(state, num_args, -1, 0);
const auto count = static_cast<int>(state->m_apistack.top - state->m_apistack.base); const auto count = static_cast<int>(state->m_apistack.top - state->m_apistack.base);
return get_return_values(count); return get_return_values(count);
} }
catch (const std::exception& e) catch (const std::exception& e)
{ {
throw std::runtime_error(std::string("Error executing script function: ") + e.what()); stack.fix();
throw std::runtime_error("Error executing script function: "s + 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; const auto state = *game::hks::lua_state;
state->m_apistack.top = state->m_apistack.base;
stack stack;
push_value(key); push_value(key);
stack.save(1);
const auto _1 = gsl::finally(&disable_error_hook); const auto _1 = gsl::finally(&disable_error_hook);
enable_error_hook(); enable_error_hook();
@ -85,16 +126,18 @@ namespace ui_scripting
} }
catch (const std::exception& e) catch (const std::exception& e)
{ {
throw std::runtime_error(std::string("Error getting userdata field: ") + e.what()); 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; const auto state = *game::hks::lua_state;
state->m_apistack.top = state->m_apistack.base;
stack stack;
push_value(key); push_value(key);
stack.save(1);
const auto _1 = gsl::finally(&disable_error_hook); const auto _1 = gsl::finally(&disable_error_hook);
enable_error_hook(); enable_error_hook();
@ -111,14 +154,17 @@ namespace ui_scripting
} }
catch (const std::exception& e) catch (const std::exception& e)
{ {
throw std::runtime_error(std::string("Error getting table field: ") + e.what()); 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; const auto state = *game::hks::lua_state;
state->m_apistack.top = state->m_apistack.base;
stack stack;
stack.save(0);
const auto _1 = gsl::finally(&disable_error_hook); const auto _1 = gsl::finally(&disable_error_hook);
enable_error_hook(); enable_error_hook();
@ -133,14 +179,17 @@ namespace ui_scripting
} }
catch (const std::exception& e) catch (const std::exception& e)
{ {
throw std::runtime_error(std::string("Error setting userdata field: ") + e.what()); 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; const auto state = *game::hks::lua_state;
state->m_apistack.top = state->m_apistack.base;
stack stack;
stack.save(0);
const auto _1 = gsl::finally(&disable_error_hook); const auto _1 = gsl::finally(&disable_error_hook);
enable_error_hook(); enable_error_hook();
@ -155,7 +204,8 @@ namespace ui_scripting
} }
catch (const std::exception& e) catch (const std::exception& e)
{ {
throw std::runtime_error(std::string("Error setting table field: ") + e.what()); stack.fix();
throw std::runtime_error("Error setting table field: "s + e.what());
} }
} }
} }

View File

@ -9,6 +9,8 @@ namespace ui_scripting
script_value get_return_value(int offset); script_value get_return_value(int offset);
arguments get_return_values(int count); arguments get_return_values(int count);
bool notify(const std::string& name, const event_arguments& arguments);
arguments call_script_function(const function& function, const arguments& arguments); arguments call_script_function(const function& function, const arguments& arguments);
script_value get_field(const userdata& self, const script_value& key); script_value get_field(const userdata& self, const script_value& key);

View File

@ -10,6 +10,9 @@
#include "../../../component/scripting.hpp" #include "../../../component/scripting.hpp"
#include "../../../component/command.hpp" #include "../../../component/command.hpp"
#include "../../../component/fastfiles.hpp" #include "../../../component/fastfiles.hpp"
#include "../../../component/updater.hpp"
#include "../../../component/localized_strings.hpp"
#include "../../../component/mods.hpp"
#include "component/game_console.hpp" #include "component/game_console.hpp"
#include "component/scheduler.hpp" #include "component/scheduler.hpp"
@ -25,7 +28,7 @@ namespace ui_scripting::lua
{ {
namespace namespace
{ {
const auto json_script = utils::nt::load_resource(LUA_JSON_SCRIPT); const auto json_script = utils::nt::load_resource(LUA_JSON);
scripting::script_value script_convert(const sol::lua_value& value) scripting::script_value script_convert(const sol::lua_value& value)
{ {
@ -207,7 +210,7 @@ namespace ui_scripting::lua
}; };
} }
void setup_game_type(sol::state& state, event_handler& handler, scheduler& scheduler) void setup_game_type(sol::state& state, scheduler& scheduler)
{ {
struct game struct game
{ {
@ -243,28 +246,6 @@ namespace ui_scripting::lua
return scheduler.add(callback, milliseconds, false); return scheduler.add(callback, milliseconds, false);
}; };
game_type["onnotify"] = [&handler](const game&, const std::string& event,
const event_callback& callback)
{
event_listener listener{};
listener.callback = callback;
listener.event = event;
listener.is_volatile = false;
return handler.add_event_listener(std::move(listener));
};
game_type["onnotifyonce"] = [&handler](const game&, const std::string& event,
const event_callback& callback)
{
event_listener listener{};
listener.callback = callback;
listener.event = event;
listener.is_volatile = true;
return handler.add_event_listener(std::move(listener));
};
game_type["isingame"] = []() game_type["isingame"] = []()
{ {
return ::game::CL_IsCgameInitialized() && ::game::g_entities[0].client; return ::game::CL_IsCgameInitialized() && ::game::g_entities[0].client;
@ -360,7 +341,7 @@ namespace ui_scripting::lua
game_type["playmenuvideo"] = [](const game&, const std::string& video) game_type["playmenuvideo"] = [](const game&, const std::string& video)
{ {
reinterpret_cast<void (*)(const char* a1, int a2, int a3)> reinterpret_cast<void (*)(const char* a1, int a2, int a3)>
(0x71B970_b)(video.data(), 64, 0); (0x14071B970)(video.data(), 64, 0);
}; };
game_type["sharedset"] = [](const game&, const std::string& key, const std::string& value) game_type["sharedset"] = [](const game&, const std::string& key, const std::string& value)
@ -432,124 +413,13 @@ namespace ui_scripting::lua
game_type["getloadedmod"] = [](const game&) game_type["getloadedmod"] = [](const game&)
{ {
return ::game::mod_folder; return mods::mod_path;
}; };
static int request_id{}; game_type["addlocalizedstring"] = [](const game&, const std::string& string,
game_type["httpget"] = [](const game&, const std::string& url) const std::string& value)
{ {
const auto id = request_id++; localized_strings::override(string, value);
::scheduler::once([url, id]()
{
const auto result = utils::http::get_data(url);
::scheduler::once([result, id]
{
event event;
event.name = "http_request_done";
if (result.has_value())
{
event.arguments = {id, true, result.value()};
}
else
{
event.arguments = {id, false};
}
notify(event);
}, ::scheduler::pipeline::lui);
}, ::scheduler::pipeline::async);
return id;
};
game_type["httpgettofile"] = [](const game&, const std::string& url,
const std::string& dest)
{
const auto id = request_id++;
::scheduler::once([url, id, dest]()
{
auto last_report = std::chrono::high_resolution_clock::now();
const auto result = utils::http::get_data(url, {}, [&last_report, id](size_t progress, size_t total, size_t speed)
{
const auto now = std::chrono::high_resolution_clock::now();
if (now - last_report < 100ms && progress < total)
{
return;
}
last_report = now;
::scheduler::once([id, progress, total, speed]
{
event event;
event.name = "http_request_progress";
event.arguments = {
id,
static_cast<int>(progress),
static_cast<int>(total),
static_cast<int>(speed)
};
notify(event);
}, ::scheduler::pipeline::lui);
});
if (result.has_value())
{
const auto write = utils::io::write_file(dest, result.value(), false);
::scheduler::once([result, id, write]()
{
event event;
event.name = "http_request_done";
event.arguments = {id, true, write};
notify(event);
}, ::scheduler::pipeline::lui);
}
else
{
::scheduler::once([result, id]()
{
event event;
event.name = "http_request_done";
event.arguments = {id, false};
notify(event);
}, ::scheduler::pipeline::lui);
}
}, ::scheduler::pipeline::async);
return id;
};
game_type["sha"] = [](const game&, const std::string& data)
{
return utils::string::to_upper(utils::cryptography::sha1::compute(data, true));
};
game_type["environment"] = [](const game&)
{
return GIT_BRANCH;
};
game_type["binaryname"] = [](const game&)
{
utils::nt::library self;
return self.get_name();
};
game_type["relaunch"] = [](const game&)
{
utils::nt::relaunch_self("-singleplayer");
utils::nt::terminate();
};
game_type["isdebugbuild"] = [](const game&)
{
#ifdef DEBUG
return true;
#else
return false;
#endif
}; };
struct player struct player
@ -586,7 +456,7 @@ namespace ui_scripting::lua
}; };
} }
void setup_lui_types(sol::state& state, event_handler& handler, scheduler& scheduler) void setup_lui_types(sol::state& state)
{ {
auto userdata_type = state.new_usertype<userdata>("userdata_"); auto userdata_type = state.new_usertype<userdata>("userdata_");
@ -602,15 +472,15 @@ namespace ui_scripting::lua
); );
userdata_type[sol::meta_function::index] = [](const userdata& userdata, const sol::this_state s, userdata_type[sol::meta_function::index] = [](const userdata& userdata, const sol::this_state s,
const std::string& name) const sol::lua_value& key)
{ {
return convert(s, userdata.get(name)); return convert(s, userdata.get(convert({s, key})));
}; };
userdata_type[sol::meta_function::new_index] = [](const userdata& userdata, const sol::this_state s, userdata_type[sol::meta_function::new_index] = [](const userdata& userdata, const sol::this_state s,
const std::string& name, const sol::lua_value& value) const sol::lua_value& key, const sol::lua_value& value)
{ {
userdata.set(name, convert({s, value})); userdata.set(convert({s, key}), convert({s, value}));
}; };
auto table_type = state.new_usertype<table>("table_"); auto table_type = state.new_usertype<table>("table_");
@ -627,27 +497,27 @@ namespace ui_scripting::lua
); );
table_type["get"] = [](const table& table, const sol::this_state s, table_type["get"] = [](const table& table, const sol::this_state s,
const std::string& name) const sol::lua_value& key)
{ {
return convert(s, table.get(name)); return convert(s, table.get(convert({s, key})));
}; };
table_type["set"] = [](const table& table, const sol::this_state s, table_type["set"] = [](const table& table, const sol::this_state s,
const std::string& name, const sol::lua_value& value) const sol::lua_value& key, const sol::lua_value& value)
{ {
table.set(name, convert({s, value})); table.set(convert({s, key}), convert({s, value}));
}; };
table_type[sol::meta_function::index] = [](const table& table, const sol::this_state s, table_type[sol::meta_function::index] = [](const table& table, const sol::this_state s,
const std::string& name) const sol::lua_value& key)
{ {
return convert(s, table.get(name)); return convert(s, table.get(convert({s, key})));
}; };
table_type[sol::meta_function::new_index] = [](const table& table, const sol::this_state s, table_type[sol::meta_function::new_index] = [](const table& table, const sol::this_state s,
const std::string& name, const sol::lua_value& value) const sol::lua_value& key, const sol::lua_value& value)
{ {
table.set(name, convert({s, value})); table.set(convert({s, key}), convert({s, value}));
}; };
auto function_type = state.new_usertype<function>("function_"); auto function_type = state.new_usertype<function>("function_");
@ -677,13 +547,35 @@ namespace ui_scripting::lua
state["LUI"] = state["luiglobals"]["LUI"]; state["LUI"] = state["luiglobals"]["LUI"];
state["Engine"] = state["luiglobals"]["Engine"]; state["Engine"] = state["luiglobals"]["Engine"];
state["Game"] = state["luiglobals"]["Game"]; 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;
} }
} }
context::context(std::string data, script_type type) context::context(std::string data, script_type type)
: scheduler_(state_) : scheduler_(state_)
, event_handler_(state_)
{ {
this->state_.open_libraries(sol::lib::base, this->state_.open_libraries(sol::lib::base,
sol::lib::package, sol::lib::package,
@ -696,8 +588,8 @@ namespace ui_scripting::lua
setup_io(this->state_); setup_io(this->state_);
setup_json(this->state_); setup_json(this->state_);
setup_vector_type(this->state_); setup_vector_type(this->state_);
setup_game_type(this->state_, this->event_handler_, this->scheduler_); setup_game_type(this->state_, this->scheduler_);
setup_lui_types(this->state_, this->event_handler_, this->scheduler_); setup_lui_types(this->state_);
if (type == script_type::file) if (type == script_type::file)
{ {
@ -734,7 +626,6 @@ namespace ui_scripting::lua
{ {
this->state_.collect_garbage(); this->state_.collect_garbage();
this->scheduler_.clear(); this->scheduler_.clear();
this->event_handler_.clear();
this->state_ = {}; this->state_ = {};
} }
@ -744,12 +635,6 @@ namespace ui_scripting::lua
this->state_.collect_garbage(); this->state_.collect_garbage();
} }
void context::notify(const event& e)
{
this->scheduler_.dispatch(e);
this->event_handler_.dispatch(e);
}
void context::load_script(const std::string& script) void context::load_script(const std::string& script)
{ {
if (!this->loaded_scripts_.emplace(script).second) if (!this->loaded_scripts_.emplace(script).second)

View File

@ -41,7 +41,6 @@ namespace ui_scripting::lua
std::unordered_set<std::string> loaded_scripts_; std::unordered_set<std::string> loaded_scripts_;
scheduler scheduler_; scheduler scheduler_;
event_handler event_handler_;
void load_script(const std::string& script); void load_script(const std::string& script);
}; };

View File

@ -4,6 +4,7 @@
#include "../../../component/scheduler.hpp" #include "../../../component/scheduler.hpp"
#include "../../../component/ui_scripting.hpp" #include "../../../component/ui_scripting.hpp"
#include "../../../component/filesystem.hpp"
#include <utils/io.hpp> #include <utils/io.hpp>
#include <utils/string.hpp> #include <utils/string.hpp>
@ -13,28 +14,8 @@ namespace ui_scripting::lua::engine
{ {
namespace namespace
{ {
const auto updater_script = utils::nt::load_resource(LUI_UPDATER_MENU); const auto lui_common = utils::nt::load_resource(LUI_COMMON);
const auto lui_updater = utils::nt::load_resource(LUI_UPDATER);
void handle_key_event(const int key, const int down)
{
event event;
event.name = down
? "keydown"
: "keyup";
event.arguments = {key};
engine::notify(event);
}
void handle_char_event(const int key)
{
std::string key_str = {(char)key};
event event;
event.name = "keypress";
event.arguments = {key_str};
engine::notify(event);
}
auto& get_scripts() auto& get_scripts()
{ {
@ -71,15 +52,12 @@ namespace ui_scripting::lua::engine
clear_converted_functions(); clear_converted_functions();
get_scripts().clear(); get_scripts().clear();
load_code(updater_script); load_code(lui_common);
load_code(lui_updater);
load_scripts("ui_scripts/"); for (const auto& path : filesystem::get_search_paths())
load_scripts("h2-mod/ui_scripts/");
load_scripts("data/ui_scripts/");
if (!game::mod_folder.empty())
{ {
load_scripts(utils::string::va("%s/ui_scripts/", game::mod_folder.data())); load_scripts(path + "/ui_scripts/");
} }
} }
@ -89,27 +67,6 @@ namespace ui_scripting::lua::engine
get_scripts().clear(); get_scripts().clear();
} }
void ui_event(const std::string& type, const std::vector<int>& arguments)
{
if (type == "key")
{
handle_key_event(arguments[0], arguments[1]);
}
if (type == "char")
{
handle_char_event(arguments[0]);
}
}
void notify(const event& e)
{
for (auto& script : get_scripts())
{
script->notify(e);
}
}
void run_frame() void run_frame()
{ {
for (auto& script : get_scripts()) for (auto& script : get_scripts())

View File

@ -6,8 +6,5 @@ namespace ui_scripting::lua::engine
{ {
void start(); void start();
void stop(); void stop();
void ui_event(const std::string&, const std::vector<int>&);
void notify(const event& e);
void run_frame(); void run_frame();
} }

View File

@ -56,4 +56,5 @@ namespace ui_scripting
}; };
using arguments = std::vector<script_value>; using arguments = std::vector<script_value>;
using event_arguments = std::unordered_map<std::string, script_value>;
} }

View File

@ -273,4 +273,38 @@ namespace ui_scripting
{ {
return call_script_function(*this, arguments); return call_script_function(*this, arguments);
} }
/***************************************************************
* Stack
**************************************************************/
stack::stack()
{
this->state = *game::hks::lua_state;
this->state->m_apistack.top = this->state->m_apistack.base;
}
void stack::save(int num_args)
{
this->num_args_ = num_args;
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()
{
this->state->m_numberOfCCalls = this->num_calls_;
game::hks::closePendingUpvalues(this->state, &this->state->m_apistack.bottom[this->top_bottom_ - this->num_args_]);
this->state->m_callStack.m_current = &this->state->m_callStack.m_records[this->callstack_];
this->state->m_apistack.base = &this->state->m_apistack.bottom[this->base_bottom_];
this->state->m_apistack.top = &this->state->m_apistack.bottom[this->top_bottom_ - static_cast<uint64_t>(this->num_args_ + 1)];
this->state->m_apistack.bottom[this->top_bottom_].t = this->state->m_apistack.top[-1].t;
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];
}
} }

View File

@ -86,4 +86,28 @@ namespace ui_scripting
int ref{}; int ref{};
}; };
class stack final
{
public:
stack();
void save(int num_args);
void fix();
stack(stack&&) = delete;
stack(const stack&) = delete;
stack& operator=(stack&&) = delete;
stack& operator=(const stack&) = delete;
private:
game::hks::lua_State* state;
int num_args_;
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, uint64_t* base_address) const FARPROC loader::load_library(const std::string& filename) const
{ {
const auto target = utils::nt::library::load(filename); const auto target = utils::nt::library::load(filename);
if (!target) if (!target)
@ -39,9 +39,6 @@ FARPROC loader::load_library(const std::string& filename, uint64_t* base_address
throw std::runtime_error{"Failed to map binary!"}; throw std::runtime_error{"Failed to map binary!"};
} }
const auto base = size_t(target.get_ptr());
*base_address = base;
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, uint64_t* base_address) const; FARPROC load_library(const std::string& filename) 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

@ -39,7 +39,7 @@ launcher::mode detect_mode_from_arguments()
return launcher::mode::none; return launcher::mode::none;
} }
FARPROC load_binary(const launcher::mode mode, uint64_t* base_address) FARPROC load_binary(const launcher::mode mode)
{ {
loader loader; loader loader;
utils::nt::library self; utils::nt::library self;
@ -87,7 +87,7 @@ FARPROC load_binary(const launcher::mode mode, uint64_t* base_address)
binary.data())); binary.data()));
} }
return loader.load_library(binary, base_address); return loader.load(self, data);
} }
void remove_crash_file() void remove_crash_file()
@ -97,7 +97,7 @@ void remove_crash_file()
void verify_version() void verify_version()
{ {
const auto value = *reinterpret_cast<DWORD*>(0x123456_b); const auto value = *reinterpret_cast<DWORD*>(0x140123456);
if (value != 0xE465E151) if (value != 0xE465E151)
{ {
throw std::runtime_error("Unsupported Call of Duty: Modern Warfare 2 Campaign Remastered version"); throw std::runtime_error("Unsupported Call of Duty: Modern Warfare 2 Campaign Remastered version");
@ -184,14 +184,12 @@ int main()
game::environment::set_mode(mode); game::environment::set_mode(mode);
uint64_t base_address{}; entry_point = load_binary(mode);
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;
verify_version(); verify_version();
if (!component_loader::post_load()) if (!component_loader::post_load())

View File

@ -2,23 +2,13 @@
#define ID_ICON 102 #define ID_ICON 102
#define IMAGE_SPLASH 300 #define MENU_MAIN 300
#define IMAGE_LOGO 301
#define DW_ENTITLEMENT_CONFIG 302 #define TLS_DLL 301
#define DW_SOCIAL_CONFIG 303
#define DW_MM_CONFIG 304
#define DW_LOOT_CONFIG 305
#define DW_STORE_CONFIG 306
#define DW_MOTD 307
#define DW_FASTFILE 308
#define DW_PLAYLISTS 309
#define MENU_MAIN 310 #define ICON_IMAGE 302
#define TLS_DLL 311 #define LUI_COMMON 303
#define LUI_UPDATER 304
#define ICON_IMAGE 312 #define LUA_JSON 305
#define LUA_JSON_SCRIPT 313
#define LUI_UPDATER_MENU 314

View File

@ -97,8 +97,10 @@ ID_ICON ICON "resources/icon.ico"
MENU_MAIN RCDATA "resources/main.html" MENU_MAIN RCDATA "resources/main.html"
LUA_JSON_SCRIPT RCDATA "resources/json.lua" LUI_COMMON RCDATA "resources/ui_scripts/common.lua"
LUI_UPDATER_MENU RCDATA "resources/updater.lua" LUI_UPDATER RCDATA "resources/ui_scripts/updater.lua"
LUA_JSON RCDATA "resources/json.lua"
#ifdef _DEBUG #ifdef _DEBUG
TLS_DLL RCDATA "../../build/bin/x64/Debug/tlsdll.dll" TLS_DLL RCDATA "../../build/bin/x64/Debug/tlsdll.dll"

View File

@ -0,0 +1,162 @@
menucallbacks = {}
originalmenus = {}
stack = {}
LUI.MenuBuilder.m_types_build["generic_waiting_popup_"] = function (menu, event)
local oncancel = stack.oncancel
local popup = LUI.MenuBuilder.BuildRegisteredType("waiting_popup", {
message_text = stack.text,
isLiveWithCancel = true,
cancel_func = function(...)
local args = {...}
oncancel()
LUI.FlowManager.RequestLeaveMenu(args[1])
end
})
popup.text = popup:getchildren()[7]
stack = {
ret = popup
}
return popup
end
LUI.MenuBuilder.m_types_build["generic_yes_no_popup_"] = function()
local callback = stack.callback
local popup = LUI.MenuBuilder.BuildRegisteredType("generic_yesno_popup", {
popup_title = stack.title,
message_text = stack.text,
yes_action = function()
callback(true)
end,
no_action = function()
callback(false)
end
})
stack = {
ret = popup
}
return popup
end
LUI.MenuBuilder.m_types_build["generic_confirmation_popup_"] = function()
local popup = LUI.MenuBuilder.BuildRegisteredType( "generic_confirmation_popup", {
cancel_will_close = false,
popup_title = stack.title,
message_text = stack.text,
button_text = stack.buttontext,
confirmation_action = stack.callback
})
stack = {
ret = popup
}
return stack.ret
end
LUI.onmenuopen = function(name, callback)
if (not LUI.MenuBuilder.m_types_build[name]) then
return
end
if (not menucallbacks[name]) then
menucallbacks[name] = {}
end
table.insert(menucallbacks[name], callback)
if (not originalmenus[name]) then
originalmenus[name] = LUI.MenuBuilder.m_types_build[name]
LUI.MenuBuilder.m_types_build[name] = function(...)
local args = {...}
local menu = originalmenus[name](table.unpack(args))
for k, v in luiglobals.next, menucallbacks[name] do
v(menu, table.unpack(args))
end
return menu
end
end
end
local addoptionstextinfo = LUI.Options.AddOptionTextInfo
LUI.Options.AddOptionTextInfo = function(menu)
local result = addoptionstextinfo(menu)
menu.optionTextInfo = result
return result
end
LUI.addmenubutton = function(name, data)
LUI.onmenuopen(name, function(menu)
if (not menu.list) then
return
end
local button = menu:AddButton(data.text, data.callback, nil, true, nil, {
desc_text = data.description
})
local buttonlist = menu:getChildById(menu.type .. "_list")
if (data.id) then
button.id = data.id
end
if (data.index) then
buttonlist:removeElement(button)
buttonlist:insertElement(button, data.index)
end
local hintbox = menu.optionTextInfo
menu:removeElement(hintbox)
LUI.Options.InitScrollingList(menu.list, nil)
menu.optionTextInfo = LUI.Options.AddOptionTextInfo(menu)
end)
end
LUI.openmenu = function(menu, args)
stack = args
LUI.FlowManager.RequestAddMenu(nil, menu)
return stack.ret
end
LUI.openpopupmenu = function(menu, args)
stack = args
LUI.FlowManager.RequestPopupMenu(nil, menu)
return stack.ret
end
LUI.yesnopopup = function(data)
for k, v in luiglobals.next, data do
stack[k] = v
end
LUI.FlowManager.RequestPopupMenu(nil, "generic_yes_no_popup_")
return stack.ret
end
LUI.confirmationpopup = function(data)
for k, v in luiglobals.next, data do
stack[k] = v
end
LUI.FlowManager.RequestPopupMenu(nil, "generic_confirmation_popup_")
return stack.ret
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

@ -0,0 +1,160 @@
updatecancelled = false
taskinterval = 100
updater.cancelupdate()
function startupdatecheck(popup, autoclose)
updatecancelled = false
local callback = function()
if (not updater.getupdatecheckstatus()) then
if (autoclose) then
LUI.FlowManager.RequestLeaveMenu(popup)
return
end
popup.text:setText("Error: " .. updater.getlasterror())
return
end
if (not updater.isupdateavailable()) then
if (autoclose) then
LUI.FlowManager.RequestLeaveMenu(popup)
return
end
popup.text:setText("No updates available")
return
end
LUI.yesnopopup({
title = "NOTICE",
text = "An update is available, proceed with installation?",
callback = function(result)
if (result) then
startupdatedownload(popup, autoclose)
else
LUI.FlowManager.RequestLeaveMenu(popup)
end
end
})
end
updater.startupdatecheck()
createtask({
done = updater.isupdatecheckdone,
cancelled = isupdatecancelled,
callback = callback,
interval = taskinterval
})
end
function startupdatedownload(popup, autoclose)
updater.startupdatedownload()
local textupdate = nil
local previousfile = nil
textupdate = game:oninterval(function()
local file = updater.getcurrentfile()
if (file == previousfile) then
return
end
file = previousfile
popup.text:setText("Downloading file " .. updater.getcurrentfile() .. "...")
end, 10)
local callback = function()
textupdate:clear()
if (not updater.getupdatedownloadstatus()) then
if (autoclose) then
LUI.FlowManager.RequestLeaveMenu(popup)
return
end
popup.text:setText("Error: " .. updater.getlasterror())
return
end
popup.text:setText("Update successful")
if (updater.isrestartrequired()) then
LUI.confirmationpopup({
title = "RESTART REQUIRED",
text = "Update requires restart",
buttontext = "RESTART",
callback = function()
updater.relaunch()
end
})
else
Engine.Exec("lui_restart")
end
if (autoclose) then
LUI.FlowManager.RequestLeaveMenu(popup)
end
end
createtask({
done = updater.isupdatedownloaddone,
cancelled = isupdatecancelled,
callback = callback,
interval = taskinterval
})
end
function updaterpopup(oncancel)
return LUI.openpopupmenu("generic_waiting_popup_", {
oncancel = oncancel,
withcancel = true,
text = "Checking for updates..."
})
end
function createtask(data)
local interval = nil
interval = game:oninterval(function()
if (data.cancelled()) then
interval:clear()
return
end
if (data.done()) then
interval:clear()
data.callback()
end
end, data.interval)
return interval
end
function isupdatecancelled()
return updatecancelled
end
function tryupdate(autoclose)
updatecancelled = false
local popup = updaterpopup(function()
updater.cancelupdate()
updatecancelled = true
end)
startupdatecheck(popup, autoclose)
end
function tryautoupdate()
if (not updater.autoupdatesenabled()) then
return
end
if (not updater.gethastriedupdate()) then
game:ontimeout(function()
updater.sethastriedupdate(true)
tryupdate(true)
end, 100)
end
end
LUI.tryupdating = tryupdate
LUI.onmenuopen("main_lockout", tryautoupdate)

View File

@ -1,411 +0,0 @@
menucallbacks = {}
originalmenus = {}
LUI.onmenuopen = function(name, callback)
if (not LUI.MenuBuilder.m_types_build[name]) then
return
end
if (not menucallbacks[name]) then
menucallbacks[name] = {}
end
table.insert(menucallbacks[name], callback)
if (not originalmenus[name]) then
originalmenus[name] = LUI.MenuBuilder.m_types_build[name]
LUI.MenuBuilder.m_types_build[name] = function(...)
local args = {...}
local menu = originalmenus[name](table.unpack(args))
for k, v in luiglobals.next, menucallbacks[name] do
v(menu, table.unpack(args))
end
return menu
end
end
end
local addoptionstextinfo = LUI.Options.AddOptionTextInfo
LUI.Options.AddOptionTextInfo = function(menu)
local result = addoptionstextinfo(menu)
menu.optionTextInfo = result
return result
end
LUI.addmenubutton = function(name, data)
LUI.onmenuopen(name, function(menu)
if (not menu.list) then
return
end
local button = menu:AddButton(data.text, data.callback, nil, true, nil, {
desc_text = data.description
})
local buttonlist = menu:getChildById(menu.type .. "_list")
if (data.id) then
button.id = data.id
end
if (data.index) then
buttonlist:removeElement(button)
buttonlist:insertElement(button, data.index)
end
local hintbox = menu.optionTextInfo
menu:removeElement(hintbox)
LUI.Options.InitScrollingList(menu.list, nil)
menu.optionTextInfo = LUI.Options.AddOptionTextInfo(menu)
end)
end
stack = {}
LUI.openmenu = function(menu, args)
stack = args
LUI.FlowManager.RequestAddMenu(nil, menu)
return stack.ret
end
LUI.openpopupmenu = function(menu, args)
stack = args
LUI.FlowManager.RequestPopupMenu(nil, menu)
return stack.ret
end
LUI.onmenuopen("main_lockout", function()
if (game:isdebugbuild() or not Engine.GetDvarBool("cg_autoUpdate")) then
return
end
if (game:sharedget("has_tried_updating") == "") then
game:ontimeout(function()
game:sharedset("has_tried_updating", "1")
tryupdate(true)
end, 0)
end
end)
stack = {}
LUI.MenuBuilder.m_types_build["generic_waiting_popup_"] = function (menu, event)
local oncancel = stack.oncancel
local popup = LUI.MenuBuilder.BuildRegisteredType("waiting_popup", {
message_text = stack.text,
isLiveWithCancel = true,
cancel_func = function(...)
local args = {...}
oncancel()
LUI.common_menus.CommonPopups.CancelCSSDownload(table.unpack(args))
end
})
stack = {
ret = popup
}
return popup
end
LUI.MenuBuilder.m_types_build["generic_yes_no_popup_"] = function()
local callback = stack.callback
local popup = LUI.MenuBuilder.BuildRegisteredType("generic_yesno_popup", {
popup_title = stack.title,
message_text = stack.text,
yes_action = function()
callback(true)
end,
no_action = function()
callback(false)
end
})
stack = {
ret = popup
}
return popup
end
LUI.MenuBuilder.m_types_build["generic_confirmation_popup_"] = function()
local popup = LUI.MenuBuilder.BuildRegisteredType( "generic_confirmation_popup", {
cancel_will_close = false,
popup_title = stack.title,
message_text = stack.text,
button_text = stack.buttontext,
confirmation_action = stack.callback
})
stack = {
ret = popup
}
return stack.ret
end
LUI.yesnopopup = function(data)
for k, v in luiglobals.next, data do
stack[k] = v
end
LUI.FlowManager.RequestPopupMenu(nil, "generic_yes_no_popup_")
return stack.ret
end
LUI.confirmationpopup = function(data)
for k, v in luiglobals.next, data do
stack[k] = v
end
LUI.FlowManager.RequestPopupMenu(nil, "generic_confirmation_popup_")
return stack.ret
end
function updaterpopup(oncancel)
return LUI.openpopupmenu("generic_waiting_popup_", {
oncancel = oncancel,
withcancel = true,
text = "Checking for updates..."
})
end
function deleteoldfile()
io.removefile(game:binaryname() .. ".old")
end
deleteoldfile()
function verifyfiles(files)
local needed = {}
local updatebinary = false
if (game:isdebugbuild()) then
return needed, updatebinary
end
local binaryname = game:binaryname()
for i = 1, #files do
local name = files[i][1]
if (io.fileexists(name) and game:sha(io.readfile(name)) == files[i][3]) then
goto continue
end
if (name == binaryname) then
updatebinary = true
end
table.insert(needed, files[i])
::continue::
end
return needed, updatebinary
end
local canceled = false
function downloadfiles(popup, files, callback)
deleteoldfile()
local text = popup:getchildren()[7]
local folder = game:environment() == "develop" and "data-dev" or "data"
if (#files == 0) then
callback(true)
return
end
local total = 0
local filedownloaded = function()
total = total + 1
if (total == #files) then
callback(true)
end
end
local download = nil
local stop = false
download = function(index)
if (canceled or stop) then
return
end
local filename = files[index][1]
local url = "https://master.fed0001.xyz/" .. folder .. "/" .. filename .. "?" .. os.time()
text:setText(string.format("Downloading file [%i/%i]\n%s", index, #files, filename))
if (filename == game:binaryname()) then
io.movefile(filename, filename .. ".old")
end
httprequesttofile(url, filename, function(valid, success)
if (not valid) then
callback(false, "Invalid server response")
stop = true
return
end
if (not success) then
callback(false, "Failed to write file " .. filename)
stop = true
return
end
filedownloaded()
if (files[index + 1]) then
download(index + 1)
else
callback(true)
end
end)
end
download(1)
end
function userdata_:getchildren()
local children = {}
local first = self:getFirstChild()
while (first) do
table.insert(children, first)
first = first:getNextSibling()
end
return children
end
function tryupdate(autoclose)
canceled = false
local popup = updaterpopup(function()
canceled = true
end)
local text = popup:getchildren()[7]
local file = game:environment() == "develop" and "files-dev.json" or "files.json"
local url = "https://master.fed0001.xyz/" .. file .. "?" .. os.time()
httprequest(url, function(valid, data)
if (not valid) then
text:setText("Update check failed: Invalid server response")
return
end
local valid = pcall(function()
local files = json.decode(data)
local needed, updatebinary = verifyfiles(files)
if (#needed == 0) then
text:setText("No updates available")
if (autoclose) then
LUI.common_menus.CommonPopups.CancelCSSDownload(popup, {})
end
return
end
local download = function()
local gotresult = false
downloadfiles(popup, needed, function(result, error)
if (gotresult or canceled) then
return
end
gotresult = true
if (not result) then
text:setText("Update failed: " .. error)
return
end
if (updatebinary) then
LUI.confirmationpopup({
title = "RESTART REQUIRED",
text = "Update requires restart",
buttontext = "RESTART",
callback = function()
game:relaunch()
end
})
return
end
if (result and #needed > 0) then
game:executecommand("lui_restart")
return
end
text:setText("Update successful!")
if (autoclose) then
LUI.common_menus.CommonPopups.CancelCSSDownload(popup, {})
end
end)
end
if (autoclose) then
download()
else
LUI.yesnopopup({
title = "NOTICE",
text = "An update is available, proceed with installation?",
callback = function(result)
if (not result) then
LUI.common_menus.CommonPopups.CancelCSSDownload(popup, {})
else
download()
end
end
})
end
end)
if (not valid) then
text:setText("Update failed: unknown error")
end
end)
end
LUI.tryupdating = function(autoclose)
tryupdate(autoclose)
end
function httprequest(url, callback)
local request = game:httpget(url)
local listener = nil
listener = game:onnotify("http_request_done", function(id, valid, data)
if (id ~= request) then
return
end
listener:clear()
callback(valid, data)
end)
end
function httprequesttofile(url, dest, callback)
local request = game:httpgettofile(url, dest)
local listener = nil
listener = game:onnotify("http_request_done", function(id, valid, success)
if (id ~= request) then
return
end
listener:clear()
callback(valid, success)
end)
end
local localize = Engine.Localize
Engine.Localize = function(...)
local args = {...}
if (type(args[1]) == "string" and args[1]:sub(1, 2) == "$_") then
return args[1]:sub(3, -1)
end
return localize(table.unpack(args))
end

View File

@ -1,6 +1,13 @@
#include <std_include.hpp> #include <std_include.hpp>
#pragma comment(linker, "/base:0x7FFF00000000") #pragma comment(linker, "/merge:.data=.cld")
#pragma comment(linker, "/merge:.rdata=.clr")
#pragma comment(linker, "/merge:.cl=.main")
#pragma comment(linker, "/merge:.text=.main")
#pragma comment(linker, "/stack:0x1000000")
#pragma comment(linker, "/base:0x140000000")
#pragma bss_seg(".payload")
char payload_data[BINARY_PAYLOAD_SIZE];
extern "C" extern "C"
{ {
@ -10,6 +17,12 @@ extern "C"
extern "C" extern "C"
{ {
// workaround for release build removing 'payload_data'
char* dont_remove_this()
{
return payload_data;
}
int s_read_arc4random(void*, size_t) int s_read_arc4random(void*, size_t)
{ {
return -1; return -1;

View File

@ -86,8 +86,6 @@
#include <asmjit/core/jitruntime.h> #include <asmjit/core/jitruntime.h>
#include <asmjit/x86/x86assembler.h> #include <asmjit/x86/x86assembler.h>
#include <google/protobuf/stubs/logging.h>
#include <d3d11.h> #include <d3d11.h>
#include <imgui.h> #include <imgui.h>
#include <imgui_internal.h> #include <imgui_internal.h>