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"]
path = deps/asmjit
url = https://github.com/asmjit/asmjit.git
[submodule "deps/protobuf"]
path = deps/protobuf
url = https://github.com/google/protobuf.git
[submodule "deps/udis86"]
path = deps/udis86
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)
[![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)
[![Discord](https://img.shields.io/discord/955362057581129738?color=%237289DA&label=members&logo=discord&logoColor=%23FFFFFF)](https://discord.gg/dpnRn2tKT9)
# H2: Mod
<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).
- **[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.**
## Compile from source

View File

@ -5,88 +5,88 @@ local extrawidth = 50
local extraheight = 40
LUI.MenuBuilder.m_types_build["SystemInfo"] = function (f6_arg0, f6_arg1)
local f6_local0 = LUI.MenuTemplate.spMenuOffset
local title = "LUA_MENU_SYSTEM_INFO_CAPS"
local f6_local0 = LUI.MenuTemplate.spMenuOffset
local title = "LUA_MENU_SYSTEM_INFO_CAPS"
local f6_local2 = false
local f6_local3 = 0
local f6_local2 = false
local f6_local3 = 0
local menu = LUI.MenuTemplate.new(f6_arg0, {
menu_title = title,
menu_top_indent = f6_local0 + f6_local3,
showSelectButton = false,
skipAnim = f6_local2
})
local menu = LUI.MenuTemplate.new(f6_arg0, {
menu_title = title,
menu_top_indent = f6_local0 + f6_local3,
showSelectButton = false,
skipAnim = f6_local2
})
menu:setClass(LUI.SystemInfo)
menu:PopulateMissingProps(f6_arg1)
menu:ValidateProps(f6_arg1)
menu.id = "systemInfo_id"
menu:setClass(LUI.SystemInfo)
menu:PopulateMissingProps(f6_arg1)
menu:ValidateProps(f6_arg1)
menu.id = "systemInfo_id"
local f6_local5 = 300
local f6_local6 = LUI.MenuTemplate.ListTop + f6_local0
local f6_local7 = f6_arg1.menu_height
if not f6_local7 then
f6_local7 = f6_local5
end
local f6_local5 = 300
local f6_local6 = LUI.MenuTemplate.ListTop + f6_local0
local f6_local7 = f6_arg1.menu_height
if not f6_local7 then
f6_local7 = f6_local5
end
f6_local7 = f6_local7 + f6_local6 - extraheight
f6_local7 = f6_local7 + f6_local6 - extraheight
local f6_local9 = luiglobals.GenericMenuDims.OptionMenuWidth + 100
local f6_local10 = menu.properties
local topoffset2 = LUI.MenuTemplate.ListTop + LUI.MenuTemplate.spMenuOffset
local f6_local9 = luiglobals.GenericMenuDims.OptionMenuWidth + 100
local f6_local10 = menu.properties
local topoffset2 = LUI.MenuTemplate.ListTop + LUI.MenuTemplate.spMenuOffset
local decobox = LUI.MenuBuilder.BuildRegisteredType("h1_box_deco", {
decoTopOffset = topoffset2 - topoffset + 15,
decoBottomOffset = -f6_local7,
decoRightOffset = -665 + extrawidth
})
local decobox = LUI.MenuBuilder.BuildRegisteredType("h1_box_deco", {
decoTopOffset = topoffset2 - topoffset + 15,
decoBottomOffset = -f6_local7,
decoRightOffset = -665 + extrawidth
})
menu:addElement(decobox)
menu:addElement(decobox)
local decoleft = CoD.CreateState(0, 0.5, 8, 0.5, CoD.AnchorTypes.TopLeft)
decoleft.color = luiglobals.Colors.h1.light_grey
decobox:addElement(LUI.UILine.new(decoleft))
local decoleft = CoD.CreateState(0, 0.5, 8, 0.5, CoD.AnchorTypes.TopLeft)
decoleft.color = luiglobals.Colors.h1.light_grey
decobox:addElement(LUI.UILine.new(decoleft))
local decoright = CoD.CreateState(0, 0.5, -8, 0.5, CoD.AnchorTypes.TopRight)
decoright.color = luiglobals.Colors.h1.light_grey
decobox:addElement(LUI.UILine.new(decoright))
local decoright = CoD.CreateState(0, 0.5, -8, 0.5, CoD.AnchorTypes.TopRight)
decoright.color = luiglobals.Colors.h1.light_grey
decobox:addElement(LUI.UILine.new(decoright))
local element = LUI.UIVerticalList.new({
leftAnchor = true,
rightAnchor = true,
topAnchor = true,
bottomAnchor = true,
left = spacing,
right = 100,
top = topoffset2 + 15,
bottom = 0,
spacing = spacing * 0.8
})
local element = LUI.UIVerticalList.new({
leftAnchor = true,
rightAnchor = true,
topAnchor = true,
bottomAnchor = true,
left = spacing,
right = 100,
top = topoffset2 + 15,
bottom = 0,
spacing = spacing * 0.8
})
element.id = "systemInfoList_id"
menu.vlist = element
menu:addElement(element)
element.id = "systemInfoList_id"
menu.vlist = element
menu:addElement(element)
local optionmenuwidth = luiglobals.GenericMenuDims.OptionMenuWidth
luiglobals.GenericMenuDims.OptionMenuWidth = optionmenuwidth + extrawidth
local optionmenuwidth = luiglobals.GenericMenuDims.OptionMenuWidth
luiglobals.GenericMenuDims.OptionMenuWidth = optionmenuwidth + extrawidth
menu:AddInfo(Engine.Localize("MENU_SYSINFO_VERSION"), function()
return Engine.GetBuildNumber()
end)
menu:AddInfo(Engine.Localize("MENU_SYSINFO_VERSION"), function()
return Engine.GetBuildNumber()
end)
menu:AddInfo(Engine.Localize("MENU_SYSINFO_CUSTOMER_SUPPORT_LINK"), function()
return Engine.Localize("MENU_SYSINFO_CUSTOMER_SUPPORT_URL")
end)
menu:AddInfo(Engine.Localize("MENU_SYSINFO_CUSTOMER_SUPPORT_LINK"), function()
return Engine.Localize("MENU_SYSINFO_CUSTOMER_SUPPORT_URL")
end)
menu:AddInfo(Engine.Localize("MENU_SYSINFO_DONATION_LINK"), function()
return Engine.Localize("MENU_SYSINFO_DONATION_URL")
end)
menu:AddInfo(Engine.Localize("MENU_SYSINFO_DONATION_LINK"), function()
return Engine.Localize("MENU_SYSINFO_DONATION_URL")
end)
luiglobals.GenericMenuDims.OptionMenuWidth = optionmenuwidth
luiglobals.GenericMenuDims.OptionMenuWidth = optionmenuwidth
menu:AddBackButton()
menu:registerEventHandler("menu_close", LUI.SystemInfo.LeaveMenu)
menu:AddBackButton()
menu:registerEventHandler("menu_close", LUI.SystemInfo.LeaveMenu)
return menu
return menu
end

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)
local element = LUI.UIElement.new( {
leftAnchor = true,
@ -12,112 +23,102 @@ function createdivider(menu, text)
element.scrollingToNext = true
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)
end
function string:truncate(length)
if (#self <= length) then
return self
end
if (#self <= length) then
return self
end
return self:sub(1, length - 3) .. "..."
return self:sub(1, length - 3) .. "..."
end
LUI.addmenubutton("main_campaign", {
index = 6,
text = "$_MODS",
description = "Load installed mods.",
callback = function()
LUI.FlowManager.RequestAddMenu(nil, "mods_menu")
end
index = 6,
text = "@MENU_MODS",
description = Engine.Localize("@MENU_MODS_DESC"),
callback = function()
LUI.FlowManager.RequestAddMenu(nil, "mods_menu")
end
})
function getmodname(path)
local name = path
local desc = "Load " .. name
local infofile = path .. "/info.json"
local name = path
local desc = Engine.Localize("@LUA_MENU_MOD_DESC_DEFAULT", name)
local infofile = path .. "/info.json"
if (io.fileexists(infofile)) then
pcall(function()
local data = json.decode(io.readfile(infofile))
desc = string.format("%s\nAuthor: %s\nVersion: %s",
data.description, data.author, data.version)
name = data.name
end)
end
if (io.fileexists(infofile)) then
pcall(function()
local data = json.decode(io.readfile(infofile))
desc = Engine.Localize("@LUA_MENU_MOD_DESC",
data.description, data.author, data.version)
name = data.name
end)
end
return name, desc
return name, desc
end
LUI.MenuBuilder.m_types_build["mods_menu"] = function(a1)
local menu = LUI.MenuTemplate.new(a1, {
menu_title = "$_MODS",
exclusiveController = 0,
menu_width = 400,
menu_top_indent = LUI.MenuTemplate.spMenuOffset,
showTopRightSmallBar = true
})
local menu = LUI.MenuTemplate.new(a1, {
menu_title = "@MENU_MODS",
exclusiveController = 0,
menu_width = 400,
menu_top_indent = LUI.MenuTemplate.spMenuOffset,
showTopRightSmallBar = true
})
menu:AddButton("$_OPEN STORE", function()
if (LUI.MenuBuilder.m_types_build["mod_store_menu"]) then
LUI.FlowManager.RequestAddMenu(nil, "mod_store_menu")
end
end, nil, true, nil, {
desc_text = "Download and install mods."
})
menu:AddButton("@LUA_MENU_OPEN_STORE", function()
if (LUI.MenuBuilder.m_types_build["mod_store_menu"]) then
LUI.FlowManager.RequestAddMenu(nil, "mod_store_menu")
end
end, nil, true, nil, {
desc_text = Engine.Localize("@LUA_MENU_OPEN_STORE_DESC")
})
local modfolder = game:getloadedmod()
if (modfolder ~= "") then
createdivider(menu, "$_Loaded mod: ^3" .. getmodname(modfolder):truncate(20))
local modfolder = game:getloadedmod()
if (modfolder ~= "") then
createdivider(menu, Engine.Localize("@LUA_MENU_LOADED_MOD", getmodname(modfolder):truncate(24)))
menu:AddButton("$_UNLOAD", function()
game:executecommand("unloadmod")
end, nil, true, nil, {
desc_text = "Unload the currently loaded mod."
})
end
menu:AddButton("@LUA_MENU_UNLOAD", function()
game:executecommand("unloadmod")
end, nil, true, nil, {
desc_text = Engine.Localize("@LUA_MENU_UNLOAD_DESC")
})
end
createdivider(menu, "$_Available mods")
createdivider(menu, Engine.Localize("@LUA_MENU_AVAILABLE_MODS"))
if (io.directoryexists("mods")) then
local mods = io.listfiles("mods/")
for i = 1, #mods do
if (io.directoryexists(mods[i]) and not io.directoryisempty(mods[i])) then
local name, desc = getmodname(mods[i])
if (io.directoryexists("mods")) then
local mods = io.listfiles("mods/")
for i = 1, #mods do
if (io.directoryexists(mods[i]) and not io.directoryisempty(mods[i])) then
local name, desc = getmodname(mods[i])
if (mods[i] ~= modfolder) then
menu:AddButton("$_" .. name, function()
game:executecommand("loadmod " .. mods[i])
end, nil, true, nil, {
desc_text = desc
})
end
end
end
end
if (mods[i] ~= modfolder) then
game:addlocalizedstring(name, name)
menu:AddButton(name, function()
game:executecommand("loadmod " .. mods[i])
end, nil, true, nil, {
desc_text = desc
})
end
end
end
end
menu:AddBackButton(function(a1)
Engine.PlaySound(CoD.SFX.MenuBack)
LUI.FlowManager.RequestLeaveMenu(a1)
end)
menu:AddBackButton(function(a1)
Engine.PlaySound(CoD.SFX.MenuBack)
LUI.FlowManager.RequestLeaveMenu(a1)
end)
LUI.Options.InitScrollingList(menu.list, nil)
menu:CreateBottomDivider()
menu.optionTextInfo = LUI.Options.AddOptionTextInfo(menu)
LUI.Options.InitScrollingList(menu.list, nil)
menu:CreateBottomDivider()
menu.optionTextInfo = LUI.Options.AddOptionTextInfo(menu)
return menu
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))
return menu
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)
local element = LUI.UIElement.new({
leftAnchor = true,
@ -20,8 +45,8 @@ end
LUI.addmenubutton("pc_controls", {
index = 4,
text = "$_GENERAL",
description = "Set the client's settings.",
text = "@MENU_GENERAL",
description = Engine.Localize("@MENU_GENERAL_DESC"),
callback = function()
LUI.FlowManager.RequestAddMenu(nil, "settings_menu")
end
@ -29,134 +54,118 @@ LUI.addmenubutton("pc_controls", {
LUI.MenuBuilder.m_types_build["settings_menu"] = function(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_width = luiglobals.GenericMenuDims.OptionMenuWidth
})
Engine.SetDvarFromString("ui_cg_autoUpdate", Engine.GetDvarBool("cg_autoUpdate") and "1" or "0")
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")
createdivider(menu, "@LUA_MENU_UPDATES")
LUI.Options.CreateOptionButton(
menu,
"ui_cg_autoUpdate",
"$_AUTOMATIC UPDATES",
"cg_auto_update",
"@LUA_MENU_AUTO_UPDATE",
"Enable or disable automatic updates on startup.",
{
{
text = "$_ENABLED",
value = "1"
text = "@LUA_MENU_ENABLED",
value = true
},
{
text = "$_DISABLED",
value = "0"
text = "@LUA_MENU_DISABLED",
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)
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(
menu,
"ui_cg_drawFps",
"$_DRAW FPS",
"Enable or disable drawing fps or viewpos on screen.",
"cg_drawFps",
"@LUA_MENU_DRAW_FPS",
"@LUA_MENU_DRAW_FPS_DESC",
{
{
text = "$_DISABLED",
value = "0"
text = "@LUA_MENU_DISABLED",
value = 0
},
{
text = "$_FPS ONLY",
value = "1"
text = "@LUA_MENU_FPS_ONLY",
value = 1
},
{
text = "$_FPS AND VIEWPOS",
value = "2"
text = "@LUA_MENU_FPS_AND_VIEWPOS",
value = 2
}
}, nil, nil, function(value)
Engine.SetDvarInt("cg_drawFps", tonumber(Engine.GetDvarString("ui_cg_drawFps")))
end
}
)
LUI.Options.CreateOptionButton(
menu,
"ui_cg_drawSpeed",
"$_DRAW SPEED",
"cg_drawSpeed",
"@LUA_MENU_DRAW_SPEED",
"Enable or disable drawing the player speed on screen.",
{
{
text = "$_DISABLED",
value = "0"
text = "@LUA_MENU_ENABLED",
value = true
},
{
text = "$_ENABLED",
value = "1"
text = "@LUA_MENU_DISABLED",
value = false
}
}, nil, nil, function(value)
Engine.SetDvarBool("cg_drawSpeed", Engine.GetDvarString("ui_cg_drawSpeed") == "1")
end
}
)
LUI.Options.CreateOptionButton(
menu,
"ui_cg_speedGraph",
"$_DRAW SPEED GRAPH",
"Enable or disable the speed graph.",
"cg_speedGraph",
"@LUA_MENU_DRAW_SPEEDGRAPH",
"@LUA_MENU_DRAW_SPEEDGRAPH_DESC",
{
{
text = "$_DISABLED",
value = "0"
text = "@LUA_MENU_ENABLED",
value = true
},
{
text = "$_ENABLED",
value = "1"
text = "@LUA_MENU_DISABLED",
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(
menu,
"ui_r_fullbright",
"$_FULLBRIGHT",
"Change the fullbright mode.",
"r_fullbright",
"@LUA_MENU_R_FULLBRIGHT",
"@LUA_MENU_R_FULLBRIGHT_DESC",
{
{
text = "$_DISABLED",
value = "0"
text = "@LUA_MENU_DISABLED",
value = 0
},
{
text = "$_ENABLED",
value = "1"
text = "@LUA_MENU_ENABLED",
value = 1
},
{
text = "$_MODE 2",
value = "2"
text = "@LUA_MENU_MODE2",
value = 2
},
{
text = "$_MODE 3",
value = "3"
text = "@LUA_MENU_MODE3",
value = 3
}
}, nil, nil, function(value)
Engine.SetDvarInt("r_fullbright", tonumber(Engine.GetDvarString("ui_r_fullbright")))
end
}
)
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
git submodule update --init --recursive
tools\premake5 %* vs2022
pause

View File

@ -118,13 +118,13 @@ namespace binding
void post_unpack() override
{
// 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
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
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_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 "scheduler.hpp"
#include "game_console.hpp"
#include "chat.hpp"
#include "fastfiles.hpp"
#include <utils/hook.hpp>
@ -116,7 +115,9 @@ namespace command
const auto command = utils::string::to_lower(name);
if (handlers.find(command) == handlers.end())
{
add_raw(name, main_handler);
}
handlers[command] = callback;
}
@ -148,7 +149,7 @@ namespace command
public:
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);
@ -156,7 +157,7 @@ namespace command
{
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)
{
@ -165,12 +166,7 @@ namespace command
}
// SV_SpawnServer
utils::hook::invoke<void>(0x6B3AA0_b, map, 0, 0, 0, 0);
});
add("say", [](const params& params)
{
chat::print(params.join(1));
utils::hook::invoke<void>(0x1406B3AA0, map, 0, 0, 0, 0);
});
add("listassetpool", [](const params& params)
@ -207,11 +203,6 @@ namespace command
}
});
add("baseAddress", []()
{
printf("%p\n", (void*)game::base_address);
});
add("commandDump", []()
{
printf("======== Start command dump =========\n");
@ -333,8 +324,7 @@ namespace command
}
else if (arg == "health"s)
{
if (params.size() > 3)
if (params.size() > 2)
{
const auto amount = atoi(params.get(2));
const auto health = player.get("health").as<int>();
@ -414,7 +404,7 @@ namespace command
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)
{
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(utils::string::va("Exception: 0x%08X", exceptioninfo->ExceptionRecord->ExceptionCode));
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(disable: 4996)

View File

@ -51,7 +51,7 @@ namespace fastfiles
public:
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)
{

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/dvars.hpp"
#include "chat.hpp"
#include "scheduler.hpp"
#include "command.hpp"
@ -303,7 +302,7 @@ namespace fps
{
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_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.bind(stand);
a.jmp(0x6878CD_b);
a.jmp(0x1406878CD);
a.bind(allsolid);
a.jmp(0x6878D4_b);
a.jmp(0x1406878D4);
}
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);
// 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
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("g_gravity", 800, 1, 1000, game::DVAR_FLAG_REPLICATED);

View File

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

View File

@ -22,32 +22,31 @@ namespace asset_list
void on_frame()
{
if (!gui::enabled_menus["asset_list"])
static auto* enabled = &gui::enabled_menus["asset_list"];
if (!*enabled)
{
return;
}
ImGui::Begin("Asset list", enabled);
ImGui::InputText("asset type", &asset_type_filter);
ImGui::BeginChild("asset type list");
for (auto i = 0; i < game::XAssetType::ASSET_TYPE_COUNT; i++)
{
ImGui::Begin("Asset list", &gui::enabled_menus["asset_list"]);
const auto name = game::g_assetNames[i];
const auto type = static_cast<game::XAssetType>(i);
ImGui::InputText("asset type", &asset_type_filter);
ImGui::BeginChild("asset type list");
for (auto i = 0; i < game::XAssetType::ASSET_TYPE_COUNT; i++)
if (utils::string::find_lower(name, asset_type_filter))
{
const auto name = game::g_assetNames[i];
const auto type = static_cast<game::XAssetType>(i);
if (utils::string::find_lower(name, asset_type_filter))
{
ImGui::Checkbox(name, &shown_assets[type]);
}
ImGui::Checkbox(name, &shown_assets[type]);
}
ImGui::EndChild();
ImGui::End();
}
ImGui::EndChild();
ImGui::End();
for (auto i = 0; i < game::XAssetType::ASSET_TYPE_COUNT; i++)
{
const auto name = game::g_assetNames[i];

View File

@ -13,7 +13,7 @@
#include <utils/hook.hpp>
#include <utils/concurrency.hpp>
namespace gui_console
namespace gui::console
{
namespace
{
@ -52,7 +52,7 @@ namespace gui_console
}
case ImGuiInputTextFlags_CallbackHistory:
{
const auto history = game_console::get_history();
const auto& history = game_console::get_history();
if (data->EventKey == ImGuiKey_UpArrow)
{
@ -96,7 +96,7 @@ namespace gui_console
{
std::string text{};
const auto output = game_console::get_output();
const auto& output = game_console::get_output();
for (const auto& line : output)
{
if (utils::string::find_lower(line, filter))
@ -116,7 +116,8 @@ namespace gui_console
void on_frame()
{
if (!gui::enabled_menus["console"])
static auto* enabled = &gui::enabled_menus["console"];
if (!*enabled)
{
return;
}
@ -126,7 +127,7 @@ namespace gui_console
static const auto input_text_flags = ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_CallbackCompletion |
ImGuiInputTextFlags_CallbackHistory;
ImGui::Begin("Console", &gui::enabled_menus["console"]);
ImGui::Begin("Console", enabled);
if (ImGui::BeginPopup("Options"))
{
@ -159,7 +160,7 @@ namespace gui_console
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)
{
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))
{
auto history = game_console::get_history();
auto& history = game_console::get_history();
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/concurrency.hpp>
namespace gui_debug
namespace gui::debug
{
namespace
{
@ -403,12 +403,13 @@ namespace gui_debug
void draw_window()
{
if (!gui::enabled_menus["debug"])
static auto* enabled = &gui::enabled_menus["debug"];
if (!*enabled)
{
return;
}
ImGui::Begin("Debug", &gui::enabled_menus["debug"]);
ImGui::Begin("Debug", enabled);
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/concurrency.hpp>
namespace entity_list
namespace gui::entity_list
{
namespace
{
@ -593,8 +593,9 @@ namespace entity_list
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::Begin("Entity list", &gui::enabled_menus["entity_list"]);
ImGui::Begin("Entity list", enabled);
if (ImGui::Button("Update list"))
{
@ -808,7 +809,8 @@ namespace entity_list
void on_frame()
{
if (!gui::enabled_menus["entity_list"])
static auto* enabled = &gui::enabled_menus["entity_list"];
if (!*enabled)
{
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/concurrency.hpp>
namespace gui_script_console
namespace gui::script_console
{
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 "loader/component_loader.hpp"
#include "game/game.hpp"
#include "images.hpp"
#include "game_console.hpp"
#include "filesystem.hpp"
#include "game/game.hpp"
#include <utils/hook.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 {};
}
@ -111,8 +114,8 @@ namespace images
public:
void post_unpack() override
{
setup_texture_hook.create(0x74A390_b, setup_texture_stub);
load_texture_hook.create(0x2A7940_b, load_texture_stub);
setup_texture_hook.create(0x14074A390, setup_texture_stub);
load_texture_hook.create(0x1402A7940, load_texture_stub);
}
};
}

View File

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

View File

@ -46,7 +46,7 @@ namespace localized_strings
void post_unpack() override
{
// 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:
void post_unpack() override
{
utils::hook::jump(0x32C620_b, print_warning, true);
utils::hook::jump(0x32C630_b, print_warning, true);
utils::hook::jump(0x32AEF0_b, lui_print, true);
com_error_hook.create(0x5A2D80_b, com_error_stub);
utils::hook::jump(0x14032C620, print_warning, true);
utils::hook::jump(0x14032C630, print_warning, true);
utils::hook::jump(0x14032AEF0, lui_print, true);
com_error_hook.create(0x1405A2D80, com_error_stub);
}
};
}

View File

@ -39,8 +39,8 @@ namespace lui
command::add("lui_restart", []()
{
utils::hook::invoke<void>(0x3203B0_b);
utils::hook::invoke<void>(0x32D370_b);
utils::hook::invoke<void>(0x1403203B0);
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 "game_console.hpp"
#include "scheduler.hpp"
#include "filesystem.hpp"
#include "materials.hpp"
#include "fonts.hpp"
#include "mods.hpp"
#include <utils/hook.hpp>
#include <utils/io.hpp>
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
{
public:
@ -22,6 +55,8 @@ namespace mods
utils::io::create_directory("mods");
}
db_release_xassets_hook.create(0x140416A80, db_release_xassets_stub);
command::add("loadmod", [](const command::params& params)
{
if (params.size() < 2)
@ -30,7 +65,7 @@ namespace mods
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::CG_GameMessage(0, "^1Cannot unload mod while in-game!");
@ -38,44 +73,38 @@ namespace mods
}
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))
{
game_console::print(game_console::con_type_error, "Mod %s not found!\n", path);
return;
}
game::mod_folder = path;
scheduler::once([]()
{
command::execute("lui_restart", true);
}, scheduler::pipeline::renderer);
game_console::print(game_console::con_type_info, "Loading mod %s\n", path);
filesystem::get_search_paths().erase(mod_path);
filesystem::get_search_paths().insert(path);
mod_path = path;
restart();
});
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");
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::CG_GameMessage(0, "^1Cannot unload mod while in-game!");
return;
}
game_console::print(game_console::con_type_info, "Unloading mod %s\n", game::mod_folder.data());
game::mod_folder.clear();
scheduler::once([]()
{
command::execute("lui_restart", true);
}, scheduler::pipeline::renderer);
game_console::print(game_console::con_type_info, "Unloading mod %s\n", mod_path.data());
filesystem::get_search_paths().erase(mod_path);
mod_path.clear();
restart();
});
}
};

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())
{
hook_enabled = true;
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;
return false;
}
const auto hook = vm_execute_hooks[pos];
const auto& hook = vm_execute_hooks[pos];
const auto state = hook.lua_state();
const auto self_id = local_id_to_entity(game::scr_VmPub->function_frame->fs.localId);
const auto self = scripting::entity(self_id);
const scripting::entity self = local_id_to_entity(game::scr_VmPub->function_frame->fs.localId);
std::vector<sol::lua_value> args;
@ -59,16 +59,6 @@ namespace notifies
const auto result = hook(self, sol::as_args(args));
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;
}
@ -94,12 +84,12 @@ namespace notifies
a.inc(r14);
a.mov(dword_ptr(rbp, 0xA4), r15d);
a.jmp(0x5C90B3_b);
a.jmp(0x1405C90B3);
a.bind(replace);
a.popad64();
a.mov(r14, (char*)empty_function);
a.mov(r14, reinterpret_cast<char*>(empty_function));
a.jmp(end);
}
@ -137,7 +127,7 @@ namespace notifies
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);
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)
{
{
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 _weapon = get_weapon_name(weapon, isAlternate);
@ -199,9 +189,9 @@ namespace notifies
public:
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()
{
static uint64_t off_11C52460 = 0xAD0C58_b;
static uint64_t off_11C52460 = 0x140AD0C58;
return &off_11C52460;
}
@ -28,7 +28,7 @@ namespace patches
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")
{
return;
@ -65,24 +65,24 @@ namespace patches
void post_unpack() override
{
// Fix startup crashes
utils::hook::set(0x633080_b, 0xC301B0);
utils::hook::set(0x272F70_b, 0xC301B0);
utils::hook::jump(0x46148_b, sub_46148, true);
utils::hook::set(0x140633080, 0xC301B0);
utils::hook::set(0x140272F70, 0xC301B0);
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
utils::hook::set<BYTE>(0x3D8E1B_b, 0xEB);
utils::hook::set<BYTE>(0x1403D8E1B, 0xEB);
// Disable battle net popup
utils::hook::nop(0x5F4496_b, 5);
utils::hook::nop(0x1405F4496, 5);
// Allow kbam input when gamepad is enabled
utils::hook::nop(0x3D2F8E_b, 2);
utils::hook::nop(0x3D0C9C_b, 6);
utils::hook::nop(0x1403D2F8E, 2);
utils::hook::nop(0x1403D0C9C, 6);
// 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

View File

@ -63,8 +63,8 @@ namespace renderer
{
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_update_front_end_dvar_options_hook.create(0x76EE70_b, &r_update_front_end_dvar_options_stub);
r_init_draw_method_hook.create(0x14072F950, &r_init_draw_method_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
{
r_end_frame_hook.create(0x76D7B0_b, scheduler::r_end_frame_stub);
g_run_frame_hook.create(0x4CB030_b, scheduler::server_frame_stub);
main_frame_hook.create(0x417FA0_b, scheduler::main_frame_stub);
r_end_frame_hook.create(0x14076D7B0, scheduler::r_end_frame_stub);
g_run_frame_hook.create(0x1404CB030, scheduler::server_frame_stub);
main_frame_hook.create(0x140417FA0, scheduler::main_frame_stub);
}
void pre_destroy() override

View File

@ -25,7 +25,8 @@ namespace scripting
utils::hook::detour vm_notify_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;
@ -55,9 +56,9 @@ namespace scripting
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();
}
@ -67,16 +68,16 @@ namespace scripting
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)
@ -99,11 +100,10 @@ namespace scripting
scr_set_thread_position_hook.invoke<void>(threadName, codePos);
}
utils::hook::detour sub_6B2940_hook;
char sub_6B2940_stub(void* a1)
char sv_check_load_level_stub(void* save_game)
{
const auto result = sub_6B2940_hook.invoke<char>(a1);
if (a1 != nullptr)
const auto result = sv_check_load_level_hook.invoke<char>(save_game);
if (save_game != nullptr)
{
lua::engine::start();
}
@ -116,19 +116,15 @@ namespace scripting
public:
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);
player_spawn_hook.create(0x4B0710_b, player_spawn_stub);
g_shutdown_game_hook.create(0x1404CBAD0, g_shutdown_game_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_set_thread_position_hook.create(0x5BC7E0_b, scr_set_thread_position_stub);
process_script_hook.create(0x5C6160_b, 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);
scr_add_class_field_hook.create(0x1405C2C30, scr_add_class_field_stub);
scr_set_thread_position_hook.create(0x1405BC7E0, scr_set_thread_position_stub);
process_script_hook.create(0x1405C6160, process_script_stub);
scheduler::loop([]()
{

View File

@ -4,7 +4,6 @@
#include "game/game.hpp"
#include "game/dvars.hpp"
#include "chat.hpp"
#include "scheduler.hpp"
#include "command.hpp"
@ -27,8 +26,12 @@ namespace ui_scripting
utils::hook::detour hksi_lual_error_hook2;
utils::hook::detour hks_start_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, ...)
{
@ -45,10 +48,37 @@ namespace ui_scripting
{
return hksi_lual_error_hook.invoke<void>(s, formatted.data());
}
else
throw std::runtime_error(formatted);
}
void hksi_hks_error_stub(game::hks::lua_State* s, int a2)
{
if (!error_hook_enabled)
{
throw std::runtime_error(formatted);
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)
@ -66,6 +96,26 @@ namespace ui_scripting
ui_scripting::lua::engine::stop();
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)
@ -117,17 +167,12 @@ namespace ui_scripting
void enable_error_hook()
{
error_hook_enabled = true;
error_hook_enabled++;
}
void disable_error_hook()
{
error_hook_enabled = false;
}
void notify(const event& e)
{
lua::engine::notify(e);
error_hook_enabled--;
}
class component final : public component_interface
@ -136,12 +181,14 @@ namespace ui_scripting
void post_unpack() override
{
scheduler::loop(ui_scripting::lua::engine::run_frame, scheduler::pipeline::lui);
hks_start_hook.create(0x328BE0_b, hks_start_stub);
hks_shutdown_hook.create(0x3203B0_b, hks_shutdown_stub);
hksi_lual_error_hook.create(0x2E3E40_b, hksi_lual_error_stub);
hksi_lual_error_hook2.create(0x2DCB40_b, hksi_lual_error_stub);
hks_frame_hook.create(0x140327880, hks_frame_stub);
hks_start_hook.create(0x140328BE0, hks_start_stub);
hks_shutdown_hook.create(0x1403203B0, hks_shutdown_stub);
hksi_lual_error_hook.create(0x1402E3E40, hksi_lual_error_stub);
hksi_lual_error_hook2.create(0x1402DCB40, 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 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
{
uint64_t base_address;
void load_base_address()
{
const auto module = GetModuleHandle(NULL);
base_address = uint64_t(module);
}
std::string mod_folder{};
namespace environment
{
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
{
extern uint64_t base_address;
void load_base_address();
extern std::string mod_folder;
namespace environment
{
launcher::mode get_mode();
@ -35,7 +30,7 @@ namespace game
T* get() const
{
return reinterpret_cast<T*>((uint64_t)address_ + base_address);
return reinterpret_cast<T*>(address_);
}
operator T* () const
@ -53,6 +48,4 @@ namespace game
};
}
uintptr_t operator"" _b(const uintptr_t ptr);
#include "symbols.hpp"

View File

@ -59,15 +59,15 @@ namespace scripting
script_function get_function_by_index(const unsigned index)
{
static const auto function_table = 0xB153F90;
static const auto method_table = 0xB155890;
static const auto function_table = 0x14B153F90;
static const auto method_table = 0x14B155890;
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/scripting.hpp"
#include "../../../component/command.hpp"
#include "../../../component/chat.hpp"
#include "../../../component/fastfiles.hpp"
#include <utils/string.hpp>
@ -48,16 +47,6 @@ namespace scripting::lua
state["level"] = entity{*::game::levelEntityId};
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)>());
vector_type["x"] = sol::property(&vector::get_x, &vector::set_x);
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)
{
state["level"] = entity{ *::game::levelEntityId };
state["player"] = call("getentbynum", { 0 }).as<entity>();
state["level"] = entity{*::game::levelEntityId};
state["player"] = call("getentbynum", {0}).as<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);
};
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,

View File

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

View File

@ -35,23 +35,26 @@ namespace scripting::lua
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)
{
++i;
continue;
}
if (!i->is_deleted)
if (!has_built_arguments)
{
if (!has_built_arguments)
{
has_built_arguments = true;
arguments = this->build_arguments(event);
}
handle_error(i->callback(sol::as_args(arguments)));
has_built_arguments = true;
arguments = this->build_arguments(event);
}
handle_error(i->callback(sol::as_args(arguments)));
if (i->is_volatile || i->is_deleted)
{
i = tasks.erase(i);
@ -94,8 +97,8 @@ namespace scripting::lua
callbacks_.access([&](task_list& tasks)
{
merger(tasks);
new_callbacks_.access(merger);
merger(tasks);
new_callbacks_.access(merger);
});
}

View File

@ -44,7 +44,7 @@ namespace scripting::lua
sol::state& state_;
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, 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
{
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
{
float x;
@ -678,6 +757,14 @@ namespace game
const char* buffer;
};
struct TTF
{
const char* name;
int len;
const char* buffer;
int fontFace;
};
union XAssetHeader
{
void* data;
@ -687,6 +774,7 @@ namespace game
ScriptFile* scriptfile;
StringTable* stringTable;
LuaFile* luaFile;
TTF* ttf;
};
struct XAsset

View File

@ -6,198 +6,210 @@ namespace game
{
// Functions
WEAK symbol<void(int type, VariableUnion u)> AddRefToValue{0x5C0EB0};
WEAK symbol<void(unsigned int id)> AddRefToObject{0x5C0EA0};
WEAK symbol<unsigned int(unsigned int id)> AllocThread{0x5C1200};
WEAK symbol<ObjectVariableValue*(unsigned int* id)> AllocVariable{0x5C1260};
WEAK symbol<void(int type, VariableUnion u)> RemoveRefToValue{0x5C29B0};
WEAK symbol<void(unsigned int id)> RemoveRefToObject{0x5C28A0};
WEAK symbol<void(int type, VariableUnion u)> AddRefToValue{0x1405C0EB0};
WEAK symbol<void(unsigned int id)> AddRefToObject{0x1405C0EA0};
WEAK symbol<unsigned int(unsigned int id)> AllocThread{0x1405C1200};
WEAK symbol<ObjectVariableValue*(unsigned int* id)> AllocVariable{0x1405C1260};
WEAK symbol<void(int type, VariableUnion u)> RemoveRefToValue{0x1405C29B0};
WEAK symbol<void(unsigned int id)> RemoveRefToObject{0x1405C28A0};
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_GameMessageBold{0x37F1B0};
WEAK symbol<void(int localClientNum, const char* message)> CG_GameMessage{0x14037F450};
WEAK symbol<void(int localClientNum, const char* message)> CG_GameMessageBold{0x14037F1B0};
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(int localClientNum, int controllerIndex, const char* text)> Cmd_ExecuteSingleCommand{0x59ABA0};
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{0x14059ABA0};
WEAK symbol<void(errorParm code, const char* message, ...)> Com_Error{0x5A2D80};
WEAK symbol<void()> Com_Quit_f{0x5A50D0};
WEAK symbol<void()> Quit{0x5A52A0};
WEAK symbol<void(errorParm code, const char* message, ...)> Com_Error{0x1405A2D80};
WEAK symbol<void(char const* finalMessage)> Com_Shutdown{0x1405A62C0};
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)>
DB_EnumXAssets_Internal{0x4129F0};
WEAK symbol<const char*(const XAsset* asset)> DB_GetXAssetName{0x3E4090};
WEAK symbol<void(XZoneInfo* zoneInfo, unsigned int zoneCount, DBSyncMode syncMode)> DB_LoadXAssets{0x414FF0};
WEAK symbol<XAssetHeader(XAssetType type, const char* name, int allowCreateDefault)> DB_FindXAssetHeader{0x412F60};
WEAK symbol<int(const RawFile* rawfile)> DB_GetRawFileLen{0x413D80};
WEAK symbol<int(const RawFile* rawfile, char* buf, int size)> DB_GetRawBuffer{0x413C40};
DB_EnumXAssets_Internal{0x1404129F0};
WEAK symbol<const char*(const XAsset* asset)> DB_GetXAssetName{0x1403E4090};
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{0x140412F60};
WEAK symbol<int(const RawFile* rawfile)> DB_GetRawFileLen{0x140413D80};
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<void(char* buffer, int index)> Dvar_GetCombinedString{0x5A75D0};
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, int value, int min, int max, unsigned int flags)> Dvar_RegisterInt{0x618090};
WEAK symbol<dvar_t*(const char* name)> Dvar_FindVar{0x140618F90};
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{0x140617BB0};
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)>
Dvar_RegisterFloat{0x617F80};
Dvar_RegisterFloat{0x140617F80};
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,
unsigned int flags)> Dvar_RegisterVec4{0x6185F0};
WEAK symbol<const char*(const dvar_t* dvar)> Dvar_DisplayableValue{0x618EA0};
WEAK symbol<const char*(dvar_t* dvar, void* a2, void* value)> Dvar_ValueToString{0x61B8F0};
WEAK symbol<void(int hash, const char* name, const char* buffer)> Dvar_SetCommand{0x61A5C0};
WEAK symbol<void(const char* dvarName, const char* string, DvarSetSource source)> Dvar_SetFromStringFromSource{0x61A910};
unsigned int flags)> Dvar_RegisterVec4{0x1406185F0};
WEAK symbol<const char*(const dvar_t* dvar)> Dvar_DisplayableValue{0x140618EA0};
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{0x14061A5C0};
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,
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(int entnum, unsigned int classnum)> FindEntityId{0x5C1C50};
WEAK symbol<void(VariableValue* result, unsigned int classnum, int entnum, int offset)> GetEntityFieldValue{0x5C6100};
WEAK symbol<unsigned int(unsigned int parentId, unsigned int unsignedValue)> GetVariable{0x5C2690};
WEAK symbol<unsigned int(unsigned int parentId, unsigned int unsignedValue)> GetNewVariable{0x5C22B0};
WEAK symbol<unsigned int(unsigned int parentId, unsigned int unsignedValue)> GetNewArrayVariable{0x5C2130};
WEAK symbol<void(unsigned int parentId, unsigned int id, VariableValue* value)> SetNewVariableValue{0x5C5EA0};
WEAK symbol<void(unsigned int parentId, unsigned int index)> RemoveVariableValue{0x5C2A50};
WEAK symbol<unsigned int(unsigned int parentId, unsigned int name)> FindVariable{0x1405C1D50};
WEAK symbol<unsigned int(int entnum, unsigned int classnum)> FindEntityId{0x1405C1C50};
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{0x1405C2690};
WEAK symbol<unsigned int(unsigned int parentId, unsigned int unsignedValue)> GetNewVariable{0x1405C22B0};
WEAK symbol<unsigned int(unsigned int parentId, unsigned int unsignedValue)> GetNewArrayVariable{0x1405C2130};
WEAK symbol<void(unsigned int parentId, unsigned int id, VariableValue* value)> SetNewVariableValue{0x1405C5EA0};
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)>
G_GivePlayerWeapon{0x51B660};
WEAK symbol<void(void* ps, const unsigned int weapon, int hadWeapon)> G_InitializeAmmo{0x4C4110};
WEAK symbol<void(int clientNum, const unsigned int weapon)> G_SelectWeapon{0x51C0D0};
WEAK symbol<bool(int localClientNum, ScreenPlacement* screenPlacement, const float* worldDir, float* outScreenPos)> WorldPosToScreenPos{0x36F310};
G_GivePlayerWeapon{0x14051B660};
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{0x14051C0D0};
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,
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<bool(int clientNum, const char* menu)> Menu_IsMenuOpenAndVisible{0x5EE1A0};
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* 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<void()> Scr_ClearOutParams{0x5C6E50};
WEAK symbol<scr_entref_t(unsigned int entId)> Scr_GetEntityIdRef{0x5C56C0};
WEAK symbol<unsigned int(int entnum, unsigned int classnum)> Scr_GetEntityId{0x5C5610};
WEAK symbol<int(unsigned int classnum, int entnum, int offset)> Scr_SetObjectField{0x512190};
WEAK symbol<void(unsigned int id, scr_string_t stringValue, unsigned int paramcount)> Scr_NotifyId{0x5C8240};
WEAK symbol<unsigned int(unsigned int threadId)> Scr_GetSelf{0x5C57C0};
WEAK symbol<void()> Scr_ErrorInternal{0x5C6EC0};
WEAK symbol<const float*(const float* v)> Scr_AllocVector{0x1405C3220};
WEAK symbol<void()> Scr_ClearOutParams{0x1405C6E50};
WEAK symbol<scr_entref_t(unsigned int entId)> Scr_GetEntityIdRef{0x1405C56C0};
WEAK symbol<unsigned int(int entnum, unsigned int classnum)> Scr_GetEntityId{0x1405C5610};
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{0x1405C8240};
WEAK symbol<unsigned int(unsigned int threadId)> Scr_GetSelf{0x1405C57C0};
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,
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,
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,
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,
float rotation, float* color1, float* color2, int style)> R_AddCmdDrawText2{0x76C860};
WEAK symbol<void(rectangle* rect, float a2, float a3, float a4, float a5, float* color, Material* material)> R_DrawRectangle{0x76A280};
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{0x14076A280};
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,
int a8, float* color1, unsigned int style, rgba color2)> R_AddCmdDrawTextGradient{0x76C570};
WEAK symbol<Font_s*(const char* font, int size)> R_RegisterFont{0x746FE0};
WEAK symbol<int(const char* text, int maxChars, Font_s* font)> R_TextWidth{0x7472A0};
WEAK symbol<void()> R_SyncRenderThread{0x76E7D0};
int a8, float* color1, unsigned int style, rgba color2)> R_AddCmdDrawTextGradient{0x14076C570};
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{0x1407472A0};
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,
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_GetView{0x3E1660};
WEAK symbol<ScreenPlacement*()> ScrPlace_GetViewPlacement{0x1403E16A0};
WEAK symbol<ScreenPlacement*()> ScrPlace_GetView{0x1403E1660};
WEAK symbol<const char*(scr_string_t stringValue)> SL_ConvertToString{0x5BFBB0};
WEAK symbol<scr_string_t(const char* str, unsigned int user)> SL_GetString{0x5C0170};
WEAK symbol<const char*(scr_string_t stringValue)> SL_ConvertToString{0x1405BFBB0};
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<bool()> Sys_IsDatabaseReady2{0x5A9FE0};
WEAK symbol<int()> Sys_Milliseconds{0x650720};
WEAK symbol<bool()> Sys_IsMainThread{0x5AA020};
WEAK symbol<void()> Sys_ShowConsole{0x140633080};
WEAK symbol<bool()> Sys_IsDatabaseReady2{0x1405A9FE0};
WEAK symbol<int()> Sys_Milliseconds{0x140650720};
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<int(int localClientNum, const char* sound)> UI_PlayLocalSoundAlias{0x606080};
WEAK symbol<const char*(const char* string)> UI_SafeTranslateString{0x1405A2930};
WEAK symbol<int(int localClientNum, const char* sound)> UI_PlayLocalSoundAlias{0x140606080};
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*,
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<int(jmp_buf* Buf)> _setjmp{0x8EC2E0};
WEAK symbol<void*(jmp_buf* Buf, int Value)> longjmp{0x14089EED0};
WEAK symbol<int(jmp_buf* Buf)> _setjmp{0x1408EC2E0};
// Variables
WEAK symbol<cmd_function_s*> cmd_functions{0xAD17BB8};
WEAK symbol<CmdArgs> cmd_args{0xAD17A60};
WEAK symbol<const char*> command_whitelist{0xBF84E0};
WEAK symbol<cmd_function_s*> cmd_functions{0x14AD17BB8};
WEAK symbol<CmdArgs> cmd_args{0x14AD17A60};
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<int> g_poolSize{0xBF2E40};
WEAK symbol<const char*> g_assetNames{0x140BEF280};
WEAK symbol<int> g_poolSize{0x140BF2E40};
WEAK symbol<gentity_s> g_entities{0x52DDDA0};
WEAK symbol<int> num_entities{0x55CC738};
WEAK symbol<PathData> pathData{0x52CCDA0};
WEAK symbol<int> vehicle_pathnode_count{0xD009A30};
WEAK symbol<gentity_s> g_entities{0x1452DDDA0};
WEAK symbol<int> num_entities{0x1455CC738};
WEAK symbol<PathData> pathData{0x1452CCDA0};
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<refdef_t> refdef{0x1BC2500};
WEAK symbol<GfxDrawMethod_s> gfxDrawMethod{0x14EDF9E00};
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<dvar_t> dvarPool{0xBFBB320};
WEAK symbol<int> dvarCount{0x14BFBB310};
WEAK symbol<dvar_t> dvarPool{0x14BFBB320};
WEAK symbol<unsigned int> levelEntityId{0xB5E0B30};
WEAK symbol<int> g_script_error_level{0xBA9CC24};
WEAK symbol<jmp_buf> g_script_error{0xBA9CD40};
WEAK symbol<scr_classStruct_t> g_classMap{0xBF95C0};
WEAK symbol<unsigned int> levelEntityId{0x14B5E0B30};
WEAK symbol<int> g_script_error_level{0x14BA9CC24};
WEAK symbol<jmp_buf> g_script_error{0x14BA9CD40};
WEAK symbol<scr_classStruct_t> g_classMap{0x140BF95C0};
WEAK symbol<scrVarGlob_t> scr_VarGlob{0xB617C00};
WEAK symbol<scrVmPub_t> scr_VmPub{0xBA9EE40};
WEAK symbol<function_stack_t> scr_function_stack{0xBAA93C0};
WEAK symbol<scrVarGlob_t> scr_VarGlob{0x14B617C00};
WEAK symbol<scrVmPub_t> scr_VmPub{0x14BA9EE40};
WEAK symbol<function_stack_t> scr_function_stack{0x14BAA93C0};
namespace hks
{
WEAK symbol<lua_State*> lua_state{0x19D83E8};
WEAK symbol<void(lua_State* s, const char* str, unsigned int l)> hksi_lua_pushlstring{0x287410};
WEAK symbol<const char*(lua_State* s, const HksObject* obj, unsigned int* len)> hks_obj_tolstring{0x287410};
WEAK symbol<int(lua_State* s, const HksObject* obj, HksObject* ret)> hks_obj_getmetatable{0x2DA210};
WEAK symbol<HksObject*(HksObject* result, lua_State* s, const HksObject* table, const HksObject* key)> hks_obj_getfield{0x2D9E20};
WEAK symbol<void(lua_State* s, const HksObject* tbl, const HksObject* key, const HksObject* val)> hks_obj_settable{0x2DB040};
WEAK symbol<HksObject* (HksObject* result, lua_State* s, const HksObject* table, const HksObject* key)> hks_obj_gettable{0x2DA300};
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 index)> hksi_lua_pushvalue{0x2DE040};
WEAK symbol<HashTable*(lua_State* s, unsigned int arraySize, unsigned int hashSize)> Hashtable_Create{0x2C8290};
WEAK symbol<HksObject*(HashTable* t, HksObject* result, HksObject* key)> Hashtable_getNextHash{0x2D5150};
WEAK symbol<void(lua_State* s, const HksObject* tbl, HksObject* key, HksObject* retval)> hks_obj_next{0x2DA850};
WEAK symbol<lua_State*> lua_state{0x1419D83E8};
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{0x140287410};
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{0x1402D9E20};
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{0x1402DA300};
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{0x1402DE040};
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{0x1402D5150};
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,
int internal_, int profilerTreatClosureAsFunc)> cclosure_Create{0x2C84B0};
WEAK symbol<int(lua_State* s, int t)> hksi_luaL_ref{0x2E4520};
WEAK symbol<void(lua_State* s, int t, int ref)> hksi_luaL_unref{0x2DCE50};
int internal_, int profilerTreatClosureAsFunc)> cclosure_Create{0x1402C84B0};
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{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;
}
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)
{
const auto state = *game::hks::lua_state;
state->m_apistack.top = state->m_apistack.base;
stack stack;
push_value(function);
for (auto i = arguments.begin(); i != arguments.end(); ++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);
enable_error_hook();
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);
return get_return_values(count);
}
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)
{
const auto state = *game::hks::lua_state;
state->m_apistack.top = state->m_apistack.base;
stack stack;
push_value(key);
stack.save(1);
const auto _1 = gsl::finally(&disable_error_hook);
enable_error_hook();
@ -85,16 +126,18 @@ namespace ui_scripting
}
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)
{
const auto state = *game::hks::lua_state;
state->m_apistack.top = state->m_apistack.base;
stack stack;
push_value(key);
stack.save(1);
const auto _1 = gsl::finally(&disable_error_hook);
enable_error_hook();
@ -111,14 +154,17 @@ namespace ui_scripting
}
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)
{
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);
enable_error_hook();
@ -133,14 +179,17 @@ namespace ui_scripting
}
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)
{
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);
enable_error_hook();
@ -155,7 +204,8 @@ namespace ui_scripting
}
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);
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);
script_value get_field(const userdata& self, const script_value& key);

View File

@ -10,6 +10,9 @@
#include "../../../component/scripting.hpp"
#include "../../../component/command.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/scheduler.hpp"
@ -25,7 +28,7 @@ namespace ui_scripting::lua
{
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)
{
@ -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
{
@ -243,28 +246,6 @@ namespace ui_scripting::lua
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"] = []()
{
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)
{
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)
@ -432,124 +413,13 @@ namespace ui_scripting::lua
game_type["getloadedmod"] = [](const game&)
{
return ::game::mod_folder;
return mods::mod_path;
};
static int request_id{};
game_type["httpget"] = [](const game&, const std::string& url)
game_type["addlocalizedstring"] = [](const game&, const std::string& string,
const std::string& value)
{
const auto id = request_id++;
::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
localized_strings::override(string, value);
};
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_");
@ -602,15 +472,15 @@ namespace ui_scripting::lua
);
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,
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_");
@ -627,27 +497,27 @@ namespace ui_scripting::lua
);
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,
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,
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,
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_");
@ -677,13 +547,35 @@ namespace ui_scripting::lua
state["LUI"] = state["luiglobals"]["LUI"];
state["Engine"] = state["luiglobals"]["Engine"];
state["Game"] = state["luiglobals"]["Game"];
auto updater_table = sol::table::create(state.lua_state());
updater_table["relaunch"] = updater::relaunch;
updater_table["sethastriedupdate"] = updater::set_has_tried_update;
updater_table["gethastriedupdate"] = updater::get_has_tried_update;
updater_table["autoupdatesenabled"] = updater::auto_updates_enabled;
updater_table["startupdatecheck"] = updater::start_update_check;
updater_table["isupdatecheckdone"] = updater::is_update_check_done;
updater_table["getupdatecheckstatus"] = updater::get_update_check_status;
updater_table["isupdateavailable"] = updater::is_update_available;
updater_table["startupdatedownload"] = updater::start_update_download;
updater_table["isupdatedownloaddone"] = updater::is_update_download_done;
updater_table["getupdatedownloadstatus"] = updater::get_update_download_status;
updater_table["cancelupdate"] = updater::cancel_update;
updater_table["isrestartrequired"] = updater::is_restart_required;
updater_table["getlasterror"] = updater::get_last_error;
updater_table["getcurrentfile"] = updater::get_current_file;
state["updater"] = updater_table;
}
}
context::context(std::string data, script_type type)
: scheduler_(state_)
, event_handler_(state_)
{
this->state_.open_libraries(sol::lib::base,
sol::lib::package,
@ -696,8 +588,8 @@ namespace ui_scripting::lua
setup_io(this->state_);
setup_json(this->state_);
setup_vector_type(this->state_);
setup_game_type(this->state_, this->event_handler_, this->scheduler_);
setup_lui_types(this->state_, this->event_handler_, this->scheduler_);
setup_game_type(this->state_, this->scheduler_);
setup_lui_types(this->state_);
if (type == script_type::file)
{
@ -734,7 +626,6 @@ namespace ui_scripting::lua
{
this->state_.collect_garbage();
this->scheduler_.clear();
this->event_handler_.clear();
this->state_ = {};
}
@ -744,12 +635,6 @@ namespace ui_scripting::lua
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)
{
if (!this->loaded_scripts_.emplace(script).second)

View File

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

View File

@ -4,6 +4,7 @@
#include "../../../component/scheduler.hpp"
#include "../../../component/ui_scripting.hpp"
#include "../../../component/filesystem.hpp"
#include <utils/io.hpp>
#include <utils/string.hpp>
@ -13,28 +14,8 @@ namespace ui_scripting::lua::engine
{
namespace
{
const auto updater_script = utils::nt::load_resource(LUI_UPDATER_MENU);
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);
}
const auto lui_common = utils::nt::load_resource(LUI_COMMON);
const auto lui_updater = utils::nt::load_resource(LUI_UPDATER);
auto& get_scripts()
{
@ -71,15 +52,12 @@ namespace ui_scripting::lua::engine
clear_converted_functions();
get_scripts().clear();
load_code(updater_script);
load_code(lui_common);
load_code(lui_updater);
load_scripts("ui_scripts/");
load_scripts("h2-mod/ui_scripts/");
load_scripts("data/ui_scripts/");
if (!game::mod_folder.empty())
for (const auto& path : filesystem::get_search_paths())
{
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();
}
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()
{
for (auto& script : get_scripts())

View File

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

View File

@ -56,4 +56,5 @@ namespace ui_scripting
};
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);
}
/***************************************************************
* 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{};
};
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());
}
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);
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!"};
}
const auto base = size_t(target.get_ptr());
*base_address = base;
this->load_imports(target, target);
this->load_tls(target, target);

View File

@ -5,7 +5,7 @@ class loader final
{
public:
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);

View File

@ -39,7 +39,7 @@ launcher::mode detect_mode_from_arguments()
return launcher::mode::none;
}
FARPROC load_binary(const launcher::mode mode, uint64_t* base_address)
FARPROC load_binary(const launcher::mode mode)
{
loader loader;
utils::nt::library self;
@ -87,7 +87,7 @@ FARPROC load_binary(const launcher::mode mode, uint64_t* base_address)
binary.data()));
}
return loader.load_library(binary, base_address);
return loader.load(self, data);
}
void remove_crash_file()
@ -97,7 +97,7 @@ void remove_crash_file()
void verify_version()
{
const auto value = *reinterpret_cast<DWORD*>(0x123456_b);
const auto value = *reinterpret_cast<DWORD*>(0x140123456);
if (value != 0xE465E151)
{
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);
uint64_t base_address{};
entry_point = load_binary(mode, &base_address);
entry_point = load_binary(mode);
if (!entry_point)
{
throw std::runtime_error("Unable to load binary into memory");
}
game::base_address = base_address;
verify_version();
if (!component_loader::post_load())

View File

@ -2,23 +2,13 @@
#define ID_ICON 102
#define IMAGE_SPLASH 300
#define IMAGE_LOGO 301
#define MENU_MAIN 300
#define DW_ENTITLEMENT_CONFIG 302
#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 TLS_DLL 301
#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_SCRIPT 313
#define LUI_UPDATER_MENU 314
#define LUA_JSON 305

View File

@ -97,8 +97,10 @@ ID_ICON ICON "resources/icon.ico"
MENU_MAIN RCDATA "resources/main.html"
LUA_JSON_SCRIPT RCDATA "resources/json.lua"
LUI_UPDATER_MENU RCDATA "resources/updater.lua"
LUI_COMMON RCDATA "resources/ui_scripts/common.lua"
LUI_UPDATER RCDATA "resources/ui_scripts/updater.lua"
LUA_JSON RCDATA "resources/json.lua"
#ifdef _DEBUG
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>
#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"
{
@ -10,6 +17,12 @@ 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)
{
return -1;

View File

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