Merge remote-tracking branch 'upstream/main' into usermaps

This commit is contained in:
BrentVL-1952840 2023-03-08 22:00:46 +01:00
commit 9f8537ae7b
22 changed files with 477 additions and 34 deletions

View File

@ -79,7 +79,7 @@ jobs:
name: Deploy artifacts name: Deploy artifacts
needs: build needs: build
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop') if: github.repository_owner == 'momo5502' && github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop')
steps: steps:
- name: Setup environment - name: Setup environment
run: echo "BOIII_MASTER_PATH=${{ secrets.BOIII_MASTER_SSH_PATH }}" >> $GITHUB_ENV run: echo "BOIII_MASTER_PATH=${{ secrets.BOIII_MASTER_SSH_PATH }}" >> $GITHUB_ENV
@ -107,7 +107,7 @@ jobs:
uses: shimataro/ssh-key-action@v2.5.0 uses: shimataro/ssh-key-action@v2.5.0
with: with:
key: ${{ secrets.BOIII_MASTER_SSH_PRIVATE_KEY }} key: ${{ secrets.BOIII_MASTER_SSH_PRIVATE_KEY }}
known_hosts: 'just-a-placeholder-so-we-dont-get-errors' known_hosts: "just-a-placeholder-so-we-dont-get-errors"
- name: Add known hosts - name: Add known hosts
run: ssh-keyscan -H ${{ secrets.BOIII_MASTER_SSH_ADDRESS }} >> ~/.ssh/known_hosts run: ssh-keyscan -H ${{ secrets.BOIII_MASTER_SSH_ADDRESS }} >> ~/.ssh/known_hosts

17
.github/workflows/discord-notify.yml vendored Normal file
View File

@ -0,0 +1,17 @@
name: Notify Discord
on:
push:
branches:
- "*"
issues:
jobs:
notify:
runs-on: ubuntu-latest
if: github.repository_owner == 'momo5502'
steps:
- name: Send notification to Discord
uses: Ilshidur/action-discord@master
env:
DISCORD_WEBHOOK: ${{ secrets.DISCORD_CI_BOT_WEBHOOK }}

2
deps/asmjit vendored

@ -1 +1 @@
Subproject commit 915186f6c5c2f5a4638e5cb97ccc23d741521a64 Subproject commit 1098b7d8873777f281e54a84a2b422fec30d91d4

2
deps/curl vendored

@ -1 +1 @@
Subproject commit ad4997e5b289c97724fdcbaeb3c8c50222a757c4 Subproject commit 93eefa6ba134aceef4f6cd51fc602319b382e629

2
deps/rapidjson vendored

@ -1 +1 @@
Subproject commit 012be8528783cdbf4b7a9e64f78bd8f056b97e24 Subproject commit 083f359f5c36198accc2b9360ce1e32a333231d9

View File

@ -0,0 +1,103 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "name.hpp"
#include "steam_proxy.hpp"
#include "command.hpp"
#include <utils/nt.hpp>
#include <utils/string.hpp>
#include <utils/properties.hpp>
#include <utils/concurrency.hpp>
namespace name
{
namespace
{
utils::concurrency::container<std::string> player_name{};
void store_player_name(const std::string& name)
{
utils::properties::store("playerName", name);
}
void activate_player_name(std::string new_name)
{
player_name.access([&](std::string& name)
{
name = std::move(new_name);
});
}
void update_player_name(const std::string& new_name)
{
store_player_name(new_name);
activate_player_name(new_name);
}
void setup_player_name()
{
std::string initial_name = steam_proxy::get_player_name();
if (initial_name.empty())
{
initial_name = utils::nt::get_user_name();
}
if (initial_name.empty())
{
initial_name = "Unknown Soldier";
}
update_player_name(initial_name);
}
void load_player_name()
{
const auto stored_name = utils::properties::load("playerName");
if (stored_name)
{
activate_player_name(*stored_name);
}
else
{
setup_player_name();
}
}
}
struct component final : client_component
{
void post_load() override
{
load_player_name();
}
void post_unpack() override
{
command::add("name", [](const command::params& params)
{
if (params.size() != 2)
{
return;
}
update_player_name(params[1]);
});
}
component_priority priority() const override
{
return component_priority::name;
}
};
const char* get_player_name()
{
const auto name = player_name.copy();
return utils::string::va("%.*s", static_cast<int>(name.size()), name.data());
}
}
REGISTER_COMPONENT(name::component)

View File

@ -0,0 +1,6 @@
#pragma once
namespace name
{
const char* get_player_name();
}

View File

@ -265,6 +265,9 @@ namespace network
// Kill lobby system // Kill lobby system
handle_packet_internal_hook.create(game::select(0x141EF7FE0, 0x1404A5B90), &handle_packet_internal_stub); handle_packet_internal_hook.create(game::select(0x141EF7FE0, 0x1404A5B90), &handle_packet_internal_stub);
// Kill voice chat
utils::hook::set<uint32_t>(game::select(0x141359310, 0x14018FE40), 0xC3C03148);
} }
}; };
} }

View File

@ -193,6 +193,8 @@ namespace party
} }
void connect_stub(const char* address) void connect_stub(const char* address)
{
if (address)
{ {
const auto target = network::address_from_string(address); const auto target = network::address_from_string(address);
if (target.type == game::NA_BAD) if (target.type == game::NA_BAD)
@ -201,7 +203,9 @@ namespace party
} }
connect_host = target; connect_host = target;
query_server(target, handle_connect_query_response); }
query_server(connect_host, handle_connect_query_response);
} }
void send_server_query(server_query& query) void send_server_query(server_query& query)

View File

@ -7,6 +7,8 @@
#include <utils/finally.hpp> #include <utils/finally.hpp>
#include <utils/concurrency.hpp> #include <utils/concurrency.hpp>
#include "game/utils.hpp"
#include "steam/interface.hpp" #include "steam/interface.hpp"
#include "steam/steam.hpp" #include "steam/steam.hpp"
@ -252,7 +254,7 @@ namespace steam_proxy
return client_friends.invoke<const char*>("GetPersonaName"); return client_friends.invoke<const char*>("GetPersonaName");
} }
return "boiii"; return "";
} }
void update_subscribed_items() void update_subscribed_items()

View File

@ -1,5 +1,7 @@
#pragma once #pragma once
#include <utils/nt.hpp>
namespace steam_proxy namespace steam_proxy
{ {
const utils::nt::library& get_overlay_module(); const utils::nt::library& get_overlay_module();

View File

@ -10,7 +10,8 @@ namespace game
// CL // CL
WEAK symbol<void(int controllerIndex, XSESSION_INFO* hostInfo, const netadr_t* addr, int numPublicSlots, WEAK symbol<void(int controllerIndex, XSESSION_INFO* hostInfo, const netadr_t* addr, int numPublicSlots,
int numPrivateSlots, const char* mapname, const char* gametype, const char* somethingWithUserMaps)> CL_ConnectFromLobby int numPrivateSlots, const char* mapname, const char* gametype,
const char* somethingWithUserMaps)> CL_ConnectFromLobby
{0x14134C570}; {0x14134C570};
// Game // Game
@ -24,7 +25,9 @@ namespace game
WEAK symbol<void(eNetworkModes networkMode)> Com_SessionMode_SetNetworkMode{0x1420F75B0, 0x140500B80}; WEAK symbol<void(eNetworkModes networkMode)> Com_SessionMode_SetNetworkMode{0x1420F75B0, 0x140500B80};
WEAK symbol<eGameModes(eGameModes gameMode)> Com_SessionMode_SetGameMode{0x1420F7570, 0x140500B40}; WEAK symbol<eGameModes(eGameModes gameMode)> Com_SessionMode_SetGameMode{0x1420F7570, 0x140500B40};
WEAK symbol<eModes(eModes mode)> Com_SessionMode_SetMode{0x1420F7570}; WEAK symbol<eModes(eModes mode)> Com_SessionMode_SetMode{0x1420F7570};
WEAK symbol<void(const char* gametype, bool loadDefaultSettings, bool isModified)> Com_GametypeSettings_SetGametype{0x1420F5980}; WEAK symbol<void(const char* gametype, bool loadDefaultSettings, bool isModified)> Com_GametypeSettings_SetGametype{
0x1420F5980
};
WEAK symbol<bool()> Com_IsRunningUILevel{0x142148350}; WEAK symbol<bool()> Com_IsRunningUILevel{0x142148350};
WEAK symbol<void(int localClientNum, eModes fromMode, eModes toMode, uint32_t flags)> Com_SwitchMode{ WEAK symbol<void(int localClientNum, eModes fromMode, eModes toMode, uint32_t flags)> Com_SwitchMode{
0x14214A4D0 0x14214A4D0
@ -81,7 +84,7 @@ namespace game
WEAK symbol<TLSData*()> Sys_IsDatabaseReady{0x142183A60}; WEAK symbol<TLSData*()> Sys_IsDatabaseReady{0x142183A60};
// Unnamed // Unnamed
WEAK symbol<const char* (const char* name)> CopyString{0x1422AC220, 0x14056BD70}; WEAK symbol<const char*(const char* name)> CopyString{0x1422AC220, 0x14056BD70};
WEAK symbol<bool()> isModLoaded{0x1420D5020}; WEAK symbol<bool()> isModLoaded{0x1420D5020};
WEAK symbol<void(int, const char*, int)> loadMod{0x1420D6930}; WEAK symbol<void(int, const char*, int)> loadMod{0x1420D6930};
@ -95,9 +98,14 @@ namespace game
WEAK symbol<const char*(const dvar_t* dvar)> Dvar_GetString{0x1422BF590, 0x140575E30}; WEAK symbol<const char*(const dvar_t* dvar)> Dvar_GetString{0x1422BF590, 0x140575E30};
WEAK symbol<const char*(const dvar_t* dvar)> Dvar_DisplayableValue{0x1422BC080}; WEAK symbol<const char*(const dvar_t* dvar)> Dvar_DisplayableValue{0x1422BC080};
WEAK symbol<bool(const dvar_t* dvar)> Dvar_GetBool{0x1422BCED0}; WEAK symbol<bool(const dvar_t* dvar)> Dvar_GetBool{0x1422BCED0};
WEAK symbol<dvar_t*(dvarStrHash_t hash, const char* dvarName, bool value, dvarFlags_e flags, const char* description)> Dvar_RegisterBool{ WEAK symbol<dvar_t*(dvarStrHash_t hash, const char* dvarName, bool value, dvarFlags_e flags,
const char* description)> Dvar_RegisterBool{
0x1422D0900 0x1422D0900
}; };
WEAK symbol<dvar_t*(dvarStrHash_t hash, const char* dvarName, const char* value, dvarFlags_e flags,
const char* description)> Dvar_RegisterString{
0x1422D0B70
};
WEAK symbol<void (void (*callback)(const dvar_t*, void*), void* userData)> Dvar_ForEach{0x1422BCD00}; WEAK symbol<void (void (*callback)(const dvar_t*, void*), void* userData)> Dvar_ForEach{0x1422BCD00};
WEAK symbol<void(const char* dvarName, const char* string, bool createIfMissing)> Dvar_SetFromStringByName{ WEAK symbol<void(const char* dvarName, const char* string, bool createIfMissing)> Dvar_SetFromStringByName{
0x1422C7500 0x1422C7500
@ -108,7 +116,7 @@ namespace game
WEAK symbol<void()> UI_CoD_LobbyUI_Init{0x141F2BD80, 0x1404A1F50}; WEAK symbol<void()> UI_CoD_LobbyUI_Init{0x141F2BD80, 0x1404A1F50};
WEAK symbol<void()> UI_CoD_Shutdown{0x141F32E10, 0x0}; WEAK symbol<void()> UI_CoD_Shutdown{0x141F32E10, 0x0};
WEAK symbol<void(const char*, const char*, int, game::hks::lua_State*)> UI_AddMenu{0x1427018F0, 0x0}; WEAK symbol<void(const char*, const char*, int, game::hks::lua_State*)> UI_AddMenu{0x1427018F0, 0x0};
WEAK symbol<const char* (int)> UI_CoD_GetRootNameForController{0x141F28940, 0x0}; WEAK symbol<const char*(int)> UI_CoD_GetRootNameForController{0x141F28940, 0x0};
WEAK symbol<void(game::hks::lua_State*, const char*)> Lua_CoD_LoadLuaFile{0x141F11A20, 0x0}; WEAK symbol<void(game::hks::lua_State*, const char*)> Lua_CoD_LoadLuaFile{0x141F11A20, 0x0};
WEAK symbol<void(int localClientNum)> CG_LUIHUDRestart{0x140F7E970}; WEAK symbol<void(int localClientNum)> CG_LUIHUDRestart{0x140F7E970};
WEAK symbol<void(int localClientNum)> CL_CheckKeepDrawingConnectScreen{0x1413CCAE0}; WEAK symbol<void(int localClientNum)> CL_CheckKeepDrawingConnectScreen{0x1413CCAE0};
@ -122,7 +130,8 @@ namespace game
}; };
WEAK symbol<unsigned int(scriptInstance_t inst)> Scr_GetNumParam{0x0, 0x140171320}; WEAK symbol<unsigned int(scriptInstance_t inst)> Scr_GetNumParam{0x0, 0x140171320};
WEAK symbol<void(const char* name, const char* key, unsigned int playbackFlags, float volume, void* callbackInfo, int id)> Cinematic_StartPlayback{0x1412BE3A0}; WEAK symbol<void(const char* name, const char* key, unsigned int playbackFlags, float volume, void* callbackInfo,
int id)> Cinematic_StartPlayback{0x1412BE3A0};
WEAK symbol<void(uint64_t id, bool cancelAll)> Cinematic_StopPlayback{0x1412BEA70}; WEAK symbol<void(uint64_t id, bool cancelAll)> Cinematic_StopPlayback{0x1412BEA70};
// Rendering // Rendering
@ -136,10 +145,12 @@ namespace game
WEAK symbol<void*()> SV_AddTestClient{0x142248F40, 0x14052E3E0}; WEAK symbol<void*()> SV_AddTestClient{0x142248F40, 0x14052E3E0};
WEAK symbol<void(client_s* cl_0, svscmd_type type, const char* fmt, ...)> SV_SendServerCommand{0x0, 0x140537F10}; WEAK symbol<void(client_s* cl_0, svscmd_type type, const char* fmt, ...)> SV_SendServerCommand{0x0, 0x140537F10};
WEAK symbol<bool(int clientNum)> SV_IsTestClient{0x14224AB60, 0x14052FF40}; WEAK symbol<bool(int clientNum)> SV_IsTestClient{0x14224AB60, 0x14052FF40};
WEAK symbol<void(int controllerIndex, const char* server, MapPreload preload, bool savegame)> SV_SpawnServer{0x1422528C0, 0x140535B20}; WEAK symbol<void(int controllerIndex, const char* server, MapPreload preload, bool savegame)> SV_SpawnServer{
0x1422528C0, 0x140535B20
};
// Utils // Utils
WEAK symbol<const char* (char* str)> I_CleanStr{0x1422E9050, 0x140580E80}; WEAK symbol<const char*(char* str)> I_CleanStr{0x1422E9050, 0x140580E80};
// Variables // Variables
WEAK symbol<cmd_function_s> cmd_functions{0x15689DF58, 0x14946F860}; WEAK symbol<cmd_function_s> cmd_functions{0x15689DF58, 0x14946F860};
@ -181,18 +192,26 @@ namespace game
namespace hks namespace hks
{ {
WEAK symbol<lua_State*> lua_state {0x159C76D88, 0x14858C408}; WEAK symbol<lua_State*> lua_state{0x159C76D88, 0x14858C408};
WEAK symbol<void(lua_State* s, const char* str, unsigned int l)> hksi_lua_pushlstring{0x140A18430, 0x1401DE6F0}; WEAK symbol<void(lua_State* s, const char* str, unsigned int l)> hksi_lua_pushlstring{0x140A18430, 0x1401DE6F0};
WEAK symbol<void(lua_State* s, const HksObject* tbl, const HksObject* key, const HksObject* val)> hks_obj_settable{0x141D4B660, 0x1403F41B0}; WEAK symbol<void(lua_State* s, const HksObject* tbl, const HksObject* key, const HksObject* val)>
WEAK symbol<HksObject* (HksObject* result, lua_State* s, const HksObject* table, const HksObject* key)> hks_obj_gettable{0x141D4ABF0, 0x1403F3750}; hks_obj_settable{0x141D4B660, 0x1403F41B0};
WEAK symbol<void(lua_State* s, int nargs, int nresults, const unsigned int* pc)> vm_call_internal{0x141D70FE0, 0x140418E40}; WEAK symbol<HksObject*(HksObject* result, lua_State* s, const HksObject* table, const HksObject* key)>
WEAK symbol<HashTable* (lua_State* s, unsigned int arraySize, unsigned int hashSize)> Hashtable_Create{0x141D3B5F0, 0x1403E46D0}; hks_obj_gettable{0x141D4ABF0, 0x1403F3750};
WEAK symbol<cclosure* (lua_State* s, lua_function function, int num_upvalues, int internal_, int profilerTreatClosureAsFunc)> cclosure_Create{0x141D3B7E0, 0x1403E48C0}; WEAK symbol<void(lua_State* s, int nargs, int nresults, const unsigned int* pc)> vm_call_internal{
0x141D70FE0, 0x140418E40
};
WEAK symbol<HashTable*(lua_State* s, unsigned int arraySize, unsigned int hashSize)> Hashtable_Create{
0x141D3B5F0, 0x1403E46D0
};
WEAK symbol<cclosure*(lua_State* s, lua_function function, int num_upvalues, int internal_,
int profilerTreatClosureAsFunc)> cclosure_Create{0x141D3B7E0, 0x1403E48C0};
WEAK symbol<int(lua_State* s, int t)> hksi_luaL_ref{0x141D4D1A0, 0x1403F5CF0}; WEAK symbol<int(lua_State* s, int t)> hksi_luaL_ref{0x141D4D1A0, 0x1403F5CF0};
WEAK symbol<void(lua_State* s, int t, int ref)> hksi_luaL_unref{0x141D4D320, 0x1403F5E70}; WEAK symbol<void(lua_State* s, int t, int ref)> hksi_luaL_unref{0x141D4D320, 0x1403F5E70};
WEAK symbol<int(lua_State* s, const HksCompilerSettings* options, const char* buff, unsigned __int64 sz, const char* name)> hksi_hksL_loadbuffer{0x141D4BD80, 0x1403F48D0}; WEAK symbol<int(lua_State* s, const HksCompilerSettings* options, const char* buff, unsigned __int64 sz,
const char* name)> hksi_hksL_loadbuffer{0x141D4BD80, 0x1403F48D0};
WEAK symbol<int(lua_State* s, const char* what, lua_Debug* ar)> hksi_lua_getinfo{0x141D4D8D0, 0x1403F64B0}; WEAK symbol<int(lua_State* s, const char* what, lua_Debug* ar)> hksi_lua_getinfo{0x141D4D8D0, 0x1403F64B0};
WEAK symbol<int(lua_State* s, int level, lua_Debug* ar)> hksi_lua_getstack{0x141D4DB90, 0x1403F6770}; WEAK symbol<int(lua_State* s, int level, lua_Debug* ar)> hksi_lua_getstack{0x141D4DB90, 0x1403F6770};
WEAK symbol<void(lua_State* s, const char* fmt, ...)> hksi_luaL_error{0x141D4D050, 0x1403F5BA0}; WEAK symbol<void(lua_State* s, const char* fmt, ...)> hksi_luaL_error{0x141D4D050, 0x1403F5BA0};

View File

@ -3,6 +3,8 @@
enum class component_priority enum class component_priority
{ {
min = 0, min = 0,
// must run after the steam_proxy
name,
// must run after the updater // must run after the updater
steam_proxy, steam_proxy,
updater, updater,

View File

@ -3,13 +3,13 @@
#include <utils/nt.hpp> #include <utils/nt.hpp>
#include "component/steam_proxy.hpp" #include "component/name.hpp"
namespace steam namespace steam
{ {
const char* friends::GetPersonaName() const char* friends::GetPersonaName()
{ {
return steam_proxy::get_player_name(); return name::get_player_name();
} }
unsigned long long friends::SetPersonaName(const char* pchPersonaName) unsigned long long friends::SetPersonaName(const char* pchPersonaName)

View File

@ -380,13 +380,13 @@ namespace updater
legal_files.reserve(files.size()); legal_files.reserve(files.size());
for (const auto& file : files) for (const auto& file : files)
{ {
if (file.name != UPDATE_HOST_BINARY) if (file.name.starts_with("data"))
{ {
legal_files.emplace_back(std::filesystem::absolute(base / file.name)); legal_files.emplace_back(std::filesystem::absolute(base / file.name));
} }
} }
const auto existing_files = utils::io::list_files(base.string(), true); const auto existing_files = utils::io::list_files(base / "data", true);
for (auto& file : existing_files) for (auto& file : existing_files)
{ {
const auto is_file = std::filesystem::is_regular_file(file); const auto is_file = std::filesystem::is_regular_file(file);

View File

@ -39,6 +39,12 @@ namespace utils::concurrency
T& get_raw() { return object_; } T& get_raw() { return object_; }
const T& get_raw() const { return object_; } const T& get_raw() const { return object_; }
T copy() const
{
std::unique_lock<MutexType> lock{mutex_};
return object_;
}
private: private:
mutable MutexType mutex_{}; mutable MutexType mutex_{};
T object_{}; T object_{};

View File

@ -0,0 +1,44 @@
#include "named_mutex.hpp"
#include "nt.hpp"
namespace utils
{
named_mutex::named_mutex(const std::string& name)
{
this->handle_ = CreateMutexA(nullptr, FALSE, name.data());
}
named_mutex::~named_mutex()
{
if (this->handle_)
{
CloseHandle(this->handle_);
}
}
void named_mutex::lock() const
{
if (this->handle_)
{
WaitForSingleObject(this->handle_, INFINITE);
}
}
bool named_mutex::try_lock(const std::chrono::milliseconds timeout) const
{
if (this->handle_)
{
return WAIT_OBJECT_0 == WaitForSingleObject(this->handle_, static_cast<DWORD>(timeout.count()));
}
return false;
}
void named_mutex::unlock() const noexcept
{
if (this->handle_)
{
ReleaseMutex(this->handle_);
}
}
}

View File

@ -0,0 +1,26 @@
#pragma once
#include <string>
#include <chrono>
namespace utils
{
class named_mutex
{
public:
named_mutex(const std::string& name);
~named_mutex();
named_mutex(named_mutex&&) = delete;
named_mutex(const named_mutex&) = delete;
named_mutex& operator=(named_mutex&&) = delete;
named_mutex& operator=(const named_mutex&) = delete;
void lock() const;
bool try_lock(std::chrono::milliseconds timeout = std::chrono::milliseconds{0}) const;
void unlock() const noexcept;
private:
void* handle_{};
};
}

View File

@ -1,4 +1,7 @@
#include "nt.hpp" #include "nt.hpp"
#include <lmcons.h>
#include "string.hpp" #include "string.hpp"
namespace utils::nt namespace utils::nt
@ -310,7 +313,8 @@ namespace utils::nt
GetCurrentDirectoryA(sizeof(current_dir), current_dir); GetCurrentDirectoryA(sizeof(current_dir), current_dir);
auto* const command_line = GetCommandLineA(); auto* const command_line = GetCommandLineA();
CreateProcessA(self.get_path().generic_string().data(), command_line, nullptr, nullptr, false, NULL, nullptr, current_dir, CreateProcessA(self.get_path().generic_string().data(), command_line, nullptr, nullptr, false, NULL, nullptr,
current_dir,
&startup_info, &process_info); &startup_info, &process_info);
if (process_info.hThread && process_info.hThread != INVALID_HANDLE_VALUE) CloseHandle(process_info.hThread); if (process_info.hThread && process_info.hThread != INVALID_HANDLE_VALUE) CloseHandle(process_info.hThread);
@ -322,4 +326,16 @@ namespace utils::nt
TerminateProcess(GetCurrentProcess(), code); TerminateProcess(GetCurrentProcess(), code);
_Exit(code); _Exit(code);
} }
std::string get_user_name()
{
char username[UNLEN + 1];
DWORD username_len = UNLEN + 1;
if (!GetUserNameA(username, &username_len))
{
return {};
}
return std::string(username, username_len - 1);
}
} }

View File

@ -252,4 +252,6 @@ namespace utils::nt
void relaunch_self(); void relaunch_self();
__declspec(noreturn) void terminate(uint32_t code = 0); __declspec(noreturn) void terminate(uint32_t code = 0);
std::string get_user_name();
} }

View File

@ -0,0 +1,172 @@
#include "properties.hpp"
#include "finally.hpp"
#include <rapidjson/document.h>
#include <rapidjson/prettywriter.h>
#include "rapidjson/filereadstream.h"
#include "rapidjson/filewritestream.h"
#include "rapidjson/encodedstream.h"
#include "io.hpp"
#include "com.hpp"
#include "string.hpp"
namespace utils::properties
{
namespace
{
typedef rapidjson::GenericDocument<rapidjson::UTF16LE<>> WDocument;
typedef rapidjson::GenericValue<rapidjson::UTF16LE<>> WValue;
typedef rapidjson::EncodedOutputStream<rapidjson::UTF16LE<>, rapidjson::FileWriteStream> OutputStream;
typedef rapidjson::EncodedInputStream<rapidjson::UTF16LE<>, rapidjson::FileReadStream> InputStream;
std::filesystem::path get_properties_folder()
{
static auto props = get_appdata_path() / "user";
return props;
}
std::filesystem::path get_properties_file()
{
static auto props = get_properties_folder() / "properties.json";
return props;
}
WDocument load_properties()
{
WDocument default_doc{};
default_doc.SetObject();
char read_buffer[256]; // Raw buffer for reading
const std::wstring& props = get_properties_file();
FILE* fp;
auto err = _wfopen_s(&fp, props.data(), L"rb");
if (err || !fp)
{
return default_doc;
}
// This will handle the BOM
rapidjson::FileReadStream bis(fp, read_buffer, sizeof(read_buffer));
InputStream eis(bis);
WDocument doc{};
const rapidjson::ParseResult result = doc.ParseStream<rapidjson::kParseNoFlags, rapidjson::UTF16LE<>>(eis);
fclose(fp);
if (!result || !doc.IsObject())
{
return default_doc;
}
return doc;
}
void store_properties(const WDocument& doc)
{
char write_buffer[256]; // Raw buffer for writing
const std::wstring& props = get_properties_file();
io::create_directory(get_properties_folder());
FILE* fp;
auto err = _wfopen_s(&fp, props.data(), L"wb");
if (err || !fp)
{
return;
}
rapidjson::FileWriteStream bos(fp, write_buffer, sizeof(write_buffer));
OutputStream eos(bos, true); // Write BOM
rapidjson::Writer<OutputStream, rapidjson::UTF16LE<>, rapidjson::UTF16LE<>> writer(eos);
doc.Accept(writer);
fclose(fp);
}
}
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 _ = utils::finally([&path]
{
CoTaskMemFree(path);
});
static auto appdata = std::filesystem::path(path) / "boiii";
return appdata;
}
std::unique_lock<named_mutex> lock()
{
static named_mutex mutex{"boiii-properties-lock"};
return std::unique_lock{mutex};
}
std::optional<std::wstring> load(const std::wstring& name)
{
const auto _ = lock();
const auto doc = load_properties();
if (!doc.HasMember(name))
{
return {};
}
const auto& value = doc[name];
if (!value.IsString())
{
return {};
}
return {std::wstring{value.GetString()}};
}
std::optional<std::string> load(const std::string& name)
{
const auto result = load(string::convert(name));
if (!result)
{
return {};
}
return {string::convert(*result)};
}
void store(const std::wstring& name, const std::wstring& value)
{
const auto _ = lock();
auto doc = load_properties();
while (doc.HasMember(name))
{
doc.RemoveMember(name);
}
WValue key{};
key.SetString(name, doc.GetAllocator());
WValue member{};
member.SetString(value, doc.GetAllocator());
doc.AddMember(key, member, doc.GetAllocator());
store_properties(doc);
}
void store(const std::string& name, const std::string& value)
{
store(string::convert(name), string::convert(value));
}
}

View File

@ -0,0 +1,19 @@
#pragma once
#include "named_mutex.hpp"
#include <mutex>
#include <optional>
#include <filesystem>
namespace utils::properties
{
std::filesystem::path get_appdata_path();
std::unique_lock<named_mutex> lock();
std::optional<std::wstring> load(const std::wstring& name);
std::optional<std::string> load(const std::string& name);
void store(const std::wstring& name, const std::wstring& value);
void store(const std::string& name, const std::string& value);
}