diff --git a/data/ui_scripts/custom_depot/__init__.lua b/data/cdata/ui_scripts/custom_depot/__init__.lua similarity index 100% rename from data/ui_scripts/custom_depot/__init__.lua rename to data/cdata/ui_scripts/custom_depot/__init__.lua diff --git a/data/ui_scripts/custom_depot/depot_override.lua b/data/cdata/ui_scripts/custom_depot/depot_override.lua similarity index 100% rename from data/ui_scripts/custom_depot/depot_override.lua rename to data/cdata/ui_scripts/custom_depot/depot_override.lua diff --git a/data/cdata/ui_scripts/custom_depot/mod_eula.lua b/data/cdata/ui_scripts/custom_depot/mod_eula.lua new file mode 100644 index 00000000..6d5b597c --- /dev/null +++ b/data/cdata/ui_scripts/custom_depot/mod_eula.lua @@ -0,0 +1,13 @@ +local mod_eula = function(unk1, unk2) + return LUI.EULABase.new(CoD.CreateState(0, 0, 0, 0, CoD.AnchorTypes.All), { + textStrings = LUI.EULABase.CreateTextStrings("@CUSTOM_DEPOT_EULA_", 6), + declineCallback = function(unk3) + unk2.declineCallback(unk3) + end, + acceptCallback = function(unk4) + unk2.acceptCallback(unk4) + end + }) +end + +LUI.MenuBuilder.registerPopupType("mod_eula", mod_eula) diff --git a/data/ui_scripts/custom_depot/scoreboard_override.lua b/data/cdata/ui_scripts/custom_depot/scoreboard_override.lua similarity index 100% rename from data/ui_scripts/custom_depot/scoreboard_override.lua rename to data/cdata/ui_scripts/custom_depot/scoreboard_override.lua diff --git a/data/ui_scripts/discord/__init__.lua b/data/cdata/ui_scripts/discord/__init__.lua similarity index 100% rename from data/ui_scripts/discord/__init__.lua rename to data/cdata/ui_scripts/discord/__init__.lua diff --git a/data/ui_scripts/hud_info/__init__.lua b/data/cdata/ui_scripts/hud_info/__init__.lua similarity index 100% rename from data/ui_scripts/hud_info/__init__.lua rename to data/cdata/ui_scripts/hud_info/__init__.lua diff --git a/data/ui_scripts/hud_info/hud.lua b/data/cdata/ui_scripts/hud_info/hud.lua similarity index 100% rename from data/ui_scripts/hud_info/hud.lua rename to data/cdata/ui_scripts/hud_info/hud.lua diff --git a/data/ui_scripts/hud_info/settings.lua b/data/cdata/ui_scripts/hud_info/settings.lua similarity index 90% rename from data/ui_scripts/hud_info/settings.lua rename to data/cdata/ui_scripts/hud_info/settings.lua index 603bad07..435a56d6 100644 --- a/data/ui_scripts/hud_info/settings.lua +++ b/data/cdata/ui_scripts/hud_info/settings.lua @@ -1,16 +1,5 @@ local pcdisplay = luiglobals.require("LUI.PCDisplay") -game:addlocalizedstring("LUA_MENU_FPS", "FPS Counter") -game:addlocalizedstring("LUA_MENU_FPS_DESC", "Show FPS Counter.") - -game:addlocalizedstring("LUA_MENU_LATENCY", "Server Latency") -game:addlocalizedstring("LUA_MENU_LATENCY_DESC", "Show server latency.") - -game:addlocalizedstring("LUA_MENU_RED_DOT_BRIGHTNESS", "Red dot Brightness") -game:addlocalizedstring("LUA_MENU_RED_DOT_BRIGHTNESS_DESC", "Adjust the brightness of red dot reticles.") - -game:addlocalizedstring("MENU_SYSINFO_CUSTOMER_SUPPORT_URL", "https://h1.gg/") - function createdivider(menu, text) local element = LUI.UIElement.new({ leftAnchor = true, diff --git a/data/ui_scripts/mods/__init__.lua b/data/cdata/ui_scripts/mods/__init__.lua similarity index 100% rename from data/ui_scripts/mods/__init__.lua rename to data/cdata/ui_scripts/mods/__init__.lua diff --git a/data/ui_scripts/mods/loading.lua b/data/cdata/ui_scripts/mods/loading.lua similarity index 83% rename from data/ui_scripts/mods/loading.lua rename to data/cdata/ui_scripts/mods/loading.lua index 85165f24..66efde1a 100644 --- a/data/ui_scripts/mods/loading.lua +++ b/data/cdata/ui_scripts/mods/loading.lua @@ -1,12 +1,3 @@ -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_LOADED_MOD", "Loaded mod: ^2&&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, @@ -52,7 +43,6 @@ end function getmodname(path) local name = path game:addlocalizedstring(name, name) - game:addlocalizedstring("LUA_MENU_MOD_DESC_DEFAULT", "Load &&1.") local desc = Engine.Localize("LUA_MENU_MOD_DESC_DEFAULT", name) local infofile = path .. "/info.json" diff --git a/data/ui_scripts/patches/__init__.lua b/data/cdata/ui_scripts/patches/__init__.lua similarity index 100% rename from data/ui_scripts/patches/__init__.lua rename to data/cdata/ui_scripts/patches/__init__.lua diff --git a/data/ui_scripts/patches/disable_useless_things.lua b/data/cdata/ui_scripts/patches/disable_useless_things.lua similarity index 100% rename from data/ui_scripts/patches/disable_useless_things.lua rename to data/cdata/ui_scripts/patches/disable_useless_things.lua diff --git a/data/ui_scripts/patches/gamemodes.lua b/data/cdata/ui_scripts/patches/gamemodes.lua similarity index 100% rename from data/ui_scripts/patches/gamemodes.lua rename to data/cdata/ui_scripts/patches/gamemodes.lua diff --git a/data/ui_scripts/patches/no_mode_switch.lua b/data/cdata/ui_scripts/patches/no_mode_switch.lua similarity index 100% rename from data/ui_scripts/patches/no_mode_switch.lua rename to data/cdata/ui_scripts/patches/no_mode_switch.lua diff --git a/data/ui_scripts/patches/shaderdialog.lua b/data/cdata/ui_scripts/patches/shader_dialog.lua similarity index 76% rename from data/ui_scripts/patches/shaderdialog.lua rename to data/cdata/ui_scripts/patches/shader_dialog.lua index 9d658d27..6c9fcbc1 100644 --- a/data/ui_scripts/patches/shaderdialog.lua +++ b/data/cdata/ui_scripts/patches/shader_dialog.lua @@ -1,8 +1,5 @@ LUI.MenuBuilder.registerPopupType("ShaderCacheDialog_original", LUI.ShaderCacheDialog.new) -game:addlocalizedstring("PLATFORM_SHADER_PRECACHE_ASK", "Would you like to populate the shader cache? It may cause crashes with certain GPUs (e.g. RTX cards) but will improve performance if successful.") -game:addlocalizedstring("MENU_NO_DONT_ASK", "No, don't ask me again") - local function dialog(...) if (game:sharedget("has_accepted_shader_caching") == "1") then return LUI.ShaderCacheDialog.new(...) diff --git a/data/ui_scripts/server_list/__init__.lua b/data/cdata/ui_scripts/server_list/__init__.lua similarity index 100% rename from data/ui_scripts/server_list/__init__.lua rename to data/cdata/ui_scripts/server_list/__init__.lua diff --git a/data/ui_scripts/server_list/lobby.lua b/data/cdata/ui_scripts/server_list/lobby.lua similarity index 98% rename from data/ui_scripts/server_list/lobby.lua rename to data/cdata/ui_scripts/server_list/lobby.lua index dc1be437..8d71585c 100644 --- a/data/ui_scripts/server_list/lobby.lua +++ b/data/cdata/ui_scripts/server_list/lobby.lua @@ -1,8 +1,6 @@ local Lobby = luiglobals.Lobby local MPLobbyOnline = LUI.mp_menus.MPLobbyOnline -game:addlocalizedstring("LUA_MENU_SERVERLIST", "SERVER LIST") - function LeaveLobby(f5_arg0) LeaveXboxLive() if Lobby.IsInPrivateParty() == false or Lobby.IsPrivatePartyHost() then diff --git a/data/ui_scripts/server_list/serverlist.lua b/data/cdata/ui_scripts/server_list/serverlist.lua similarity index 96% rename from data/ui_scripts/server_list/serverlist.lua rename to data/cdata/ui_scripts/server_list/serverlist.lua index 10e48598..0de3cdd4 100644 --- a/data/ui_scripts/server_list/serverlist.lua +++ b/data/cdata/ui_scripts/server_list/serverlist.lua @@ -5,11 +5,6 @@ if (not SystemLinkJoinMenu) then return end -game:addlocalizedstring("MENU_NUMPLAYERS", "Players") -game:addlocalizedstring("MENU_PING", "Ping") -game:addlocalizedstring("SERVERLIST_PLAYER_COUNT", "&&1 Players") -game:addlocalizedstring("SERVERLIST_SERVER_COUNT", "&&1 Servers") - local columns = { { offset = 40, diff --git a/data/ui_scripts/stats/__init__.lua b/data/cdata/ui_scripts/stats/__init__.lua similarity index 75% rename from data/ui_scripts/stats/__init__.lua rename to data/cdata/ui_scripts/stats/__init__.lua index 306c7ae0..128e4448 100644 --- a/data/ui_scripts/stats/__init__.lua +++ b/data/cdata/ui_scripts/stats/__init__.lua @@ -2,32 +2,6 @@ if (game:issingleplayer() or not Engine.InFrontend()) then return end -game:addlocalizedstring("LUA_MENU_STATS", "Stats") -game:addlocalizedstring("LUA_MENU_STATS_DESC", "Edit player stats settings.") - -game:addlocalizedstring("LUA_MENU_UNLOCKALL_ITEMS", "Unlock all items") -game:addlocalizedstring("LUA_MENU_UNLOCKALL_ITEMS_DESC", - "Whether items should be locked based on the player's stats or always unlocked.") - -game:addlocalizedstring("LUA_MENU_UNLOCKALL_LOOT", "Unlock all loot") -game:addlocalizedstring("LUA_MENU_UNLOCKALL_LOOT_DESC", - "Whether loot should be locked based on the player's stats or always unlocked.") - -game:addlocalizedstring("LUA_MENU_UNLOCKALL_CLASSES", "Unlock all classes") -game:addlocalizedstring("LUA_MENU_UNLOCKALL_CLASSES_DESC", - "Whether classes should be locked based on the player's stats or always unlocked.") - -game:addlocalizedstring("LUA_MENU_PRESTIGE", "Prestige") -game:addlocalizedstring("LUA_MENU_PRESTIGE_DESC", "Edit prestige level.") -game:addlocalizedstring("LUA_MENU_RANK", "Rank") -game:addlocalizedstring("LUA_MENU_RANK_DESC", "Edit rank.") - -game:addlocalizedstring("LUA_MENU_UNSAVED_CHANGES", "You have unsaved changes, are you sure you want to exit?") -game:addlocalizedstring("LUA_MENU_SAVE", "Save changes") -game:addlocalizedstring("LUA_MENU_SAVE_DESC", "Save changes.") -game:addlocalizedstring("LUA_MENU_SETTINGS", "Settings") -game:addlocalizedstring("LUA_MENU_EDIT_STATS", "Edit Stats") - function createdivider(menu, text) local element = LUI.UIElement.new({ leftAnchor = true, diff --git a/data/scripts/logging/__init__.lua b/data/scripts/logging/__init__.lua deleted file mode 100644 index 33db4cfc..00000000 --- a/data/scripts/logging/__init__.lua +++ /dev/null @@ -1,117 +0,0 @@ --- modified version of https://github.com/Joelrau/S1x-IW6x-g_log-script (permission to use by author) - -if (game:getdvar("gamemode") ~= "mp") then - return -end - --- setup dvars -game:setdvarifuninitialized("logfile", 1) -if (tonumber(game:getdvar("logfile")) < 1) then - return -end -game:setdvarifuninitialized("g_log", "logs/games_mp.log") - -start_time = 0 - -function get_time() - local seconds = math.floor((game:gettime() - start_time) / 1000) - local minutes = math.floor(seconds / 60) - time = string.format("%d:%02d", minutes, seconds - minutes * 60) - while (string.len(time) < 6) do - time = " " .. time - end - time = time .. " " - return time -end - -function create_path(path) - local dir = path:gsub("%/", "\\"):match("(.*[\\])") - os.execute("if not exist " .. dir .. " mkdir " .. dir) -end - -function log_print(message) - local path = game:getdvar("g_log") - local file = io.open(path, "a") - if (file == nil) then - create_path(path) - file = assert(io.open(path, "a")) - end - file:write(get_time() .. message .. "\n") - file:close() -end - -function init() - start_time = game:gettime() - - log_print("------------------------------------------------------------") - log_print("InitGame") - - -- player callbacks - level:onnotify("connected", function(player) - player:player_connected() - end) - level:onnotify("say", function(player, message, hidden) - player:say(message) - end) - level:onnotify("say_team", function(player, message, hidden) - player:say(message, "say_team") - end) - - -- damage/killed hooks - game:onplayerdamage(player_damage) - game:onplayerkilled(player_killed) - - -- other level notifies for log - level:onnotify("exitLevel_called", function() - log_print("ExitLevel: executed") - end) - level:onnotify("shutdownGame_called", function() - log_print("ShutdownGame:") - log_print("------------------------------------------------------------") - end) -end - -function entity:player_connected() - log_print(string.format("J;%s;%i;%s", self:getguid(), self:getentitynumber(), self.name)) - - self:onnotifyonce("disconnect", function() - self:disconnect() - end) -end - -function entity:disconnect() - log_print(string.format("Q;%s;%i;%s", self:getguid(), self:getentitynumber(), self.name)) -end - -function player_damage(self_, inflictor, attacker, damage, dflags, mod, weapon, vPoint, vDir, hitLoc) - if (game:isplayer(attacker) == 1) then - log_print(string.format("D;%s;%i;%s;%s;%s;%i;%s;%s;%s;%i;%s;%s", self_:getguid(), self_:getentitynumber(), - self_.team, self_.name, attacker:getguid(), attacker:getentitynumber(), attacker.team, attacker.name, - weapon, damage, mod, hitLoc)) - else - log_print(string.format("D;%s;%i;%s;%s;%s;%i;%s;%s;%s;%i;%s;%s", self_:getguid(), self_:getentitynumber(), - self_.team, self_.name, "", "-1", "world", "", weapon, damage, mod, hitLoc)) - end -end - -function player_killed(self_, inflictor, attacker, damage, mod, weapon, vDir, hitLoc, psTimeOffset, deathAnimDuration) - if (game:isplayer(attacker) == 1) then - log_print(string.format("K;%s;%i;%s;%s;%s;%i;%s;%s;%s;%i;%s;%s", self_:getguid(), self_:getentitynumber(), - self_.team, self_.name, attacker:getguid(), attacker:getentitynumber(), attacker.team, attacker.name, - weapon, damage, mod, hitLoc)) - else - log_print(string.format("K;%s;%i;%s;%s;%s;%i;%s;%s;%s;%i;%s;%s", self_:getguid(), self_:getentitynumber(), - self_.team, self_.name, "", "-1", "world", "", weapon, damage, mod, hitLoc)) - end -end - --- this function handles 'say' and 'say_team' -function entity:say(message, mode) - if (not mode) then - mode = "say" - end - - log_print(string.format("%s;%s;%i;%s;%s", mode, self:getguid(), self:getentitynumber(), self.name, message)) -end - -init() diff --git a/data/ui_scripts/custom_depot/mod_eula.lua b/data/ui_scripts/custom_depot/mod_eula.lua deleted file mode 100644 index 31bae99b..00000000 --- a/data/ui_scripts/custom_depot/mod_eula.lua +++ /dev/null @@ -1,23 +0,0 @@ -game:addlocalizedstring("CUSTOM_DEPOT_EULA_1", "Dear User,") -game:addlocalizedstring("CUSTOM_DEPOT_EULA_2", - "By using this feature, you acknowledge that you are over 18 years old, and that any sort of chance games / gambling are allowed in your country (even if they do not involve real money).") -game:addlocalizedstring("CUSTOM_DEPOT_EULA_3", - "The H1-Mod team is not responsible if you break the law within your country, and the sole responsibility will be upon you to respect the same.") -game:addlocalizedstring("CUSTOM_DEPOT_EULA_4", - "The H1-Mod team will never include real money transactions within the modified systems. The only way to get currency, should you wish to, is by playing the game.") -game:addlocalizedstring("CUSTOM_DEPOT_EULA_5", "Best Regards,") -game:addlocalizedstring("CUSTOM_DEPOT_EULA_6", "The H1-Mod Team.") - -local mod_eula = function(unk1, unk2) - return LUI.EULABase.new(CoD.CreateState(0, 0, 0, 0, CoD.AnchorTypes.All), { - textStrings = LUI.EULABase.CreateTextStrings("@CUSTOM_DEPOT_EULA_", 6), - declineCallback = function(unk3) - unk2.declineCallback(unk3) - end, - acceptCallback = function(unk4) - unk2.acceptCallback(unk4) - end - }) -end - -LUI.MenuBuilder.registerPopupType("mod_eula", mod_eula) diff --git a/data/ui_scripts/extra_gamemodes/__init__.lua b/data/ui_scripts/extra_gamemodes/__init__.lua deleted file mode 100644 index 6880b983..00000000 --- a/data/ui_scripts/extra_gamemodes/__init__.lua +++ /dev/null @@ -1 +0,0 @@ --- this patch has been moved to ui_scripts/patches/gamemodes.lua diff --git a/data/ui_scripts/no_mode_switch/__init__.lua b/data/ui_scripts/no_mode_switch/__init__.lua deleted file mode 100644 index 8761ea04..00000000 --- a/data/ui_scripts/no_mode_switch/__init__.lua +++ /dev/null @@ -1 +0,0 @@ --- this patch has been moved to ui_scripts/patches/no_mode_switch.lua diff --git a/data/zone_source/eng_h1_mod_common.csv b/data/zone_source/eng_h1_mod_common.csv new file mode 100644 index 00000000..7bf70ec8 --- /dev/null +++ b/data/zone_source/eng_h1_mod_common.csv @@ -0,0 +1 @@ +localize,english \ No newline at end of file diff --git a/data/zone_source/h1_mod_common.csv b/data/zone_source/h1_mod_common.csv new file mode 100644 index 00000000..1a7b02f3 --- /dev/null +++ b/data/zone_source/h1_mod_common.csv @@ -0,0 +1 @@ +build,eng_h1_mod_common \ No newline at end of file diff --git a/data/zonetool/localizedstrings/english.json b/data/zonetool/localizedstrings/english.json new file mode 100644 index 00000000..5b16ba14 --- /dev/null +++ b/data/zonetool/localizedstrings/english.json @@ -0,0 +1,53 @@ +{ + "CUSTOM_DEPOT_EULA_1": "Dear User,", + "CUSTOM_DEPOT_EULA_2": "By using this feature, you acknowledge that you are over the age of 18 years old, and that any sort of gambling is allowed in your country. (even if they do not involve real money)", + "CUSTOM_DEPOT_EULA_3": "The H1-mod team is not responsible if you break any law within your country, and the sole responsibility will be upon you to respect the same.", + "CUSTOM_DEPOT_EULA_4": "The H1-mod team will never include real money transactions within the modified systems. The only way to get currency, should you wish to, is by playing the game.", + "CUSTOM_DEPOT_EULA_5": "Best regards,", + "CUSTOM_DEPOT_EULA_6": "The H1-mod team.", + + "LUA_MENU_FPS": "FPS Counter", + "LUA_MENU_FPS_DESC": "Show FPS counter.", + "LUA_MENU_LATENCY": "Server Latency", + "LUA_MENU_LATENCY_DESC": "Show server latency.", + "LUA_MENU_RED_DOT_BRIGHTNESS": "Red Dot Brightness", + "LUA_MENU_RED_DOT_BRIGHTNESS_DESC": "Adjust the brightness of red dot reticles.", + + "MENU_SYSINFO_CUSTOMER_SUPPORT_URL": "https://h1.gg/", + + "MENU_MODS": "MODS", + "MENU_MODS_DESC": "Load installed mods.", + "LUA_MENU_MOD_DESC_DEFAULT": "Load &&1.", + "LUA_MENU_MOD_DESC": "&&1\nAuthor: &&2\nVersion: &&3", + "LUA_MENU_LOADED_MOD": "Loaded mod: ^2&&1", + "LUA_MENU_AVAILABLE_MODS": "Available mods", + "LUA_MENU_UNLOAD": "Unload", + "LUA_MENU_UNLOAD_DESC": "Unload the currently loaded mod.", + + "PLATFORM_SHADER_PRECACHE_ASK": "Would you like to populate the shader cache? It may cause crashes with certain GPUs (e.g. RTX cards) but will improve performance if successful.", + "MENU_NO_DONT_ASK": "No, don't ask me again", + + "LUA_MENU_SERVERLIST": "SERVER LIST", + "MENU_NUMPLAYERS": "Players", + "MENU_PING": "Ping", + "SERVERLIST_PLAYER_COUNT": "&&1 Players", + "SERVERLIST_SERVER_COUNT": "&&1 Servers", + + "LUA_MENU_STATS": "Stats", + "LUA_MENU_STATS_DESC": "Edit player stats settings.", + "LUA_MENU_UNLOCKALL_ITEMS": "Unlock all items", + "LUA_MENU_UNLOCKALL_ITEMS_DESC": "Whether items should be locked based on the player's stats or always unlocked.", + "LUA_MENU_UNLOCKALL_LOOT": "Unlock all loot", + "LUA_MENU_UNLOCKALL_LOOT_DESC": "Whether loot should be locked based on the player's stats or always unlocked.", + "LUA_MENU_UNLOCKALL_CLASSES": "Unlock all classes", + "LUA_MENU_UNLOCKALL_CLASSES_DESC": "Whether classes should be locked based on the player's stats or always unlocked.", + "LUA_MENU_PRESTIGE": "Prestige", + "LUA_MENU_PRESTIGE_DESC": "Edit prestige level.", + "LUA_MENU_RANK": "Rank", + "LUA_MENU_RANK_DESC": "Edit rank.", + "LUA_MENU_UNSAVED_CHANGES": "You have unsaved changes: are you sure you want to exit?", + "LUA_MENU_SAVE": "Save changes", + "LUA_MENU_SAVE_DESC": "Save changes.", + "LUA_MENU_SETTINGS": "Settings", + "LUA_MENU_EDIT_STATS": "Edit Stats" +} \ No newline at end of file diff --git a/src/client/component/filesystem.cpp b/src/client/component/filesystem.cpp index ccd0807e..ad8fd4b4 100644 --- a/src/client/component/filesystem.cpp +++ b/src/client/component/filesystem.cpp @@ -4,12 +4,14 @@ #include "console.hpp" #include "filesystem.hpp" #include "localized_strings.hpp" +#include "updater.hpp" #include "game/game.hpp" #include -#include #include +#include +#include namespace filesystem { @@ -39,9 +41,9 @@ namespace filesystem initialized = true; // hardcoded paths + filesystem::register_path(utils::properties::get_appdata_path() / CLIENT_DATA_FOLDER); filesystem::register_path(L"."); filesystem::register_path(L"h1-mod"); - filesystem::register_path(L"data"); // while this clears localizations, it also calls a function to load them again localized_strings::clear(); diff --git a/src/client/component/updater.cpp b/src/client/component/updater.cpp index 044c81b4..460008a7 100644 --- a/src/client/component/updater.cpp +++ b/src/client/component/updater.cpp @@ -1,6 +1,7 @@ #include #include "loader/component_loader.hpp" +#include "console.hpp" #include "scheduler.hpp" #include "dvars.hpp" #include "updater.hpp" @@ -11,11 +12,12 @@ #include "game/game.hpp" #include "game/dvars.hpp" -#include #include -#include #include +#include #include +#include +#include #include #define MASTER "https://master.fed0001.xyz/h1-mod/" @@ -61,6 +63,14 @@ namespace updater std::string error{}; std::string current_file{}; std::vector required_files{}; + std::vector garbage_files{}; + }; + + // remove this at some point + std::vector old_data_files = + { + {"./data"}, + {"./cdata"}, }; utils::concurrency::container update_data; @@ -75,6 +85,18 @@ namespace updater return main; } + 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; + } + void notify(const std::string& name) { scheduler::once([=]() @@ -109,9 +131,22 @@ namespace updater bool check_file(const std::string& name, const std::string& sha) { std::string data; - if (!utils::io::read_file(name, &data)) + + if (get_binary_name() == name) { - return false; + if (!utils::io::read_file(name, &data)) + { + return false; + } + } + else + { + const auto appdata_folder = utils::properties::get_appdata_path(); + const auto path = (appdata_folder / name).generic_string(); + if (!utils::io::read_file(path, &data)) + { + return false; + } } if (utils::cryptography::sha1::compute(data, true) != sha) @@ -122,18 +157,6 @@ namespace updater 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))); @@ -144,6 +167,28 @@ namespace updater return utils::http::get_data(MASTER + select(DATA_PATH, DATA_PATH_DEV) + name + "?" + get_time_str()); } + bool has_old_data_files() + { + bool has = false; + for (const auto& file : old_data_files) + { + if (utils::io::directory_exists(file)) + { + has = true; + } + } + + return has; + } + + void delete_old_data_files() + { + for (const auto& file : old_data_files) + { + std::filesystem::remove_all(file); + } + } + bool is_update_cancelled() { return update_data.access([](update_data_t& data_) @@ -161,7 +206,16 @@ namespace updater return false; } - return utils::io::write_file(name, data); + if (get_binary_name() == name) + { + return utils::io::write_file(name, data); + } + else + { + const auto appdata_folder = utils::properties::get_appdata_path(); + const auto path = (appdata_folder / name).generic_string(); + return utils::io::write_file(path, data); + } } void delete_old_file() @@ -177,6 +231,56 @@ namespace updater }); } + std::vector find_garbage_files(const std::vector& update_files) + { + std::vector garbage_files{}; + + const auto appdata_folder = utils::properties::get_appdata_path(); + const auto path = (appdata_folder / CLIENT_DATA_FOLDER).generic_string(); + if (!utils::io::directory_exists(path)) + { + return {}; + } + + const auto current_files = utils::io::list_files_recursively(path); + for (const auto& file : current_files) + { + bool found = false; + for (const auto& update_file : update_files) + { + const auto update_file_ = (appdata_folder / update_file).generic_string(); + const auto path_a = std::filesystem::path(file); + const auto path_b = std::filesystem::path(update_file_); + const auto is_directory = utils::io::directory_exists(file); + const auto compare = path_a.compare(path_b); + + if ((is_directory && compare == -1) || compare == 0) + { + found = true; + break; + } + } + + if (!found) + { +#ifdef DEBUG + console::info("[Updater] Found extra file %s\n", file.data()); +#endif + if (file.ends_with(".ff")) + { + update_data.access([](update_data_t& data_) + { + data_.restart_required = true; + }); + } + + garbage_files.push_back(file); + } + } + + return garbage_files; + } + std::string get_mode_flag() { if (game::environment::is_mp()) @@ -199,36 +303,11 @@ namespace updater } } - // workaround void relaunch() { - if (!utils::io::file_exists(BINARY_NAME)) - { - utils::nt::terminate(0); - return; - } - - STARTUPINFOA startup_info; - PROCESS_INFORMATION process_info; - - ZeroMemory(&startup_info, sizeof(startup_info)); - ZeroMemory(&process_info, sizeof(process_info)); - startup_info.cb = sizeof(startup_info); - - char current_dir[MAX_PATH]; - GetCurrentDirectoryA(sizeof(current_dir), current_dir); - - char buf[1024] = {0}; - const auto command_line = utils::string::va("%s %s", GetCommandLineA(), get_mode_flag().data()); - strcpy_s(buf, 1024, command_line); - - CreateProcess(BINARY_NAME, buf, nullptr, nullptr, false, NULL, nullptr, current_dir, - &startup_info, &process_info); - - if (process_info.hThread && process_info.hThread != INVALID_HANDLE_VALUE) CloseHandle(process_info.hThread); - if (process_info.hProcess && process_info.hProcess != INVALID_HANDLE_VALUE) CloseHandle(process_info.hProcess); - - utils::nt::terminate(0); + const auto mode = game::environment::is_mp() ? "-multiplayer" : "-singleplayer"; + utils::nt::relaunch_self(mode); + utils::nt::terminate(); } void set_has_tried_update(bool tried) @@ -282,7 +361,7 @@ namespace updater { return update_data.access([](update_data_t& data_) { - return data_.required_files.size() > 0; + return data_.required_files.size() > 0 || data_.garbage_files.size() > 0 || has_old_data_files(); }); } @@ -312,9 +391,7 @@ namespace updater void cancel_update() { -#ifdef DEBUG - printf("[Updater] Cancelling update\n"); -#endif + console::debug("[Updater] Cancelling update\n"); return update_data.access([](update_data_t& data_) { @@ -327,9 +404,7 @@ namespace updater cancel_update(); reset_data(); -#ifdef DEBUG - printf("[Updater] starting update check\n"); -#endif + console::debug("[Updater] starting update check\n"); scheduler::once([]() { @@ -365,6 +440,7 @@ namespace updater } std::vector required_files; + std::vector update_files; const auto files = j.GetArray(); for (const auto& file : files) @@ -377,6 +453,8 @@ namespace updater const auto name = file[0].GetString(); const auto sha = file[2].GetString(); + update_files.push_back(name); + if (!check_file(name, sha)) { if (get_binary_name() == name) @@ -387,19 +465,29 @@ namespace updater }); } -#ifdef DEBUG - printf("[Updater] need file %s\n", name); -#endif + std::string name_ = name; + if (name_.ends_with(".ff")) + { + update_data.access([](update_data_t& data_) + { + data_.restart_required = true; + }); + } + + console::debug("[Updater] need file %s\n", name); required_files.push_back(name); } } - update_data.access([&required_files](update_data_t& data_) + const auto garbage_files = find_garbage_files(update_files); + + update_data.access([&](update_data_t& data_) { data_.check.done = true; data_.check.success = true; data_.required_files = required_files; + data_.garbage_files = garbage_files; }); notify("update_check_done"); @@ -408,15 +496,32 @@ namespace updater void start_update_download() { -#ifdef DEBUG - printf("[Updater] starting update download\n"); -#endif + console::debug("[Updater] starting update download\n"); if (!is_update_check_done() || !get_update_check_status() || is_update_cancelled()) { return; } + delete_old_data_files(); + + const auto garbage_files = update_data.access>([](update_data_t& data_) + { + return data_.garbage_files; + }); + + for (const auto& file : garbage_files) + { + try + { + std::filesystem::remove_all(file); + } + catch (...) + { + console::error("Failed to delete %s\n", file.data()); + } + } + scheduler::once([]() { const auto required_files = update_data.access>([](update_data_t& data_) @@ -433,9 +538,7 @@ namespace updater data_.current_file = file; }); -#ifdef DEBUG - printf("[Updater] downloading file %s\n", file.data()); -#endif + console::debug("[Updater] downloading file %s\n", file.data()); const auto data = download_file(file); diff --git a/src/client/component/updater.hpp b/src/client/component/updater.hpp index 9a3dd45e..0301a6c3 100644 --- a/src/client/component/updater.hpp +++ b/src/client/component/updater.hpp @@ -1,5 +1,7 @@ #pragma once +#define CLIENT_DATA_FOLDER "cdata" + namespace updater { void relaunch(); diff --git a/src/client/main.cpp b/src/client/main.cpp index f769f457..594f11bf 100644 --- a/src/client/main.cpp +++ b/src/client/main.cpp @@ -6,9 +6,10 @@ #include "component/arxan.hpp" -#include #include #include +#include +#include DECLSPEC_NORETURN void WINAPI exit_hook(const int code) { @@ -62,7 +63,7 @@ void apply_aslr_patch(std::string* data) void get_aslr_patched_binary(std::string* binary, std::string* data) { - const auto patched_binary = "h1-mod\\"s + *binary; + const auto patched_binary = (utils::properties::get_appdata_path() / "bin/h1_mp64_ship.exe"s).generic_string(); try { @@ -159,6 +160,7 @@ FARPROC load_binary(const launcher::mode mode, uint64_t* base_address) void remove_crash_file() { utils::io::remove_file("__h1Exe"); + utils::io::remove_file("h1-mod\\h1_mp64_ship.exe"); // remove this at some point } void enable_dpi_awareness() diff --git a/src/common/utils/io.cpp b/src/common/utils/io.cpp index 52083ac9..5d898cc9 100644 --- a/src/common/utils/io.cpp +++ b/src/common/utils/io.cpp @@ -121,6 +121,18 @@ namespace utils::io return files; } + std::vector list_files_recursively(const std::string& directory) + { + std::vector files; + + for (auto& file : std::filesystem::recursive_directory_iterator(directory)) + { + files.push_back(file.path().generic_string()); + } + + return files; + } + void copy_folder(const std::filesystem::path& src, const std::filesystem::path& target) { std::filesystem::copy(src, target, diff --git a/src/common/utils/io.hpp b/src/common/utils/io.hpp index 38344987..19e8c143 100644 --- a/src/common/utils/io.hpp +++ b/src/common/utils/io.hpp @@ -18,5 +18,6 @@ namespace utils::io bool directory_is_empty(const std::string& directory); bool remove_directory(const std::string& directory); std::vector list_files(const std::string& directory); + std::vector list_files_recursively(const std::string& directory); void copy_folder(const std::filesystem::path& src, const std::filesystem::path& target); } diff --git a/src/common/utils/properties.cpp b/src/common/utils/properties.cpp new file mode 100644 index 00000000..21926d0c --- /dev/null +++ b/src/common/utils/properties.cpp @@ -0,0 +1,24 @@ +#include "io.hpp" +#include "properties.hpp" +#include +#include + +namespace utils::properties +{ + std::filesystem::path get_appdata_path() + { + PWSTR path; + if (!SUCCEEDED(SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, nullptr, &path))) + { + throw std::runtime_error("Failed to read APPDATA path!"); + } + + auto _ = gsl::finally([&path] + { + CoTaskMemFree(path); + }); + + static auto appdata = std::filesystem::path(path) / "h2-mod"; + return appdata; + } +} diff --git a/src/common/utils/properties.hpp b/src/common/utils/properties.hpp new file mode 100644 index 00000000..103e6ae6 --- /dev/null +++ b/src/common/utils/properties.hpp @@ -0,0 +1,6 @@ +#pragma once + +namespace utils::properties +{ + std::filesystem::path get_appdata_path(); +}