diff --git a/UnleashedRecomp/CMakeLists.txt b/UnleashedRecomp/CMakeLists.txt index 10a6968e..4aa448d1 100644 --- a/UnleashedRecomp/CMakeLists.txt +++ b/UnleashedRecomp/CMakeLists.txt @@ -178,7 +178,8 @@ set(UNLEASHED_RECOMP_INSTALL_CXX_SOURCES set(UNLEASHED_RECOMP_USER_CXX_SOURCES "user/achievement_data.cpp" "user/achievement_manager.cpp" - "user/config.cpp" + "user/config.cpp" + "user/registry.cpp" ) set(UNLEASHED_RECOMP_MOD_CXX_SOURCES diff --git a/UnleashedRecomp/app.cpp b/UnleashedRecomp/app.cpp index a8bff741..2b9c219c 100644 --- a/UnleashedRecomp/app.cpp +++ b/UnleashedRecomp/app.cpp @@ -8,6 +8,7 @@ #include #include #include +#include void App::Restart(std::vector restartArgs) { @@ -18,6 +19,7 @@ void App::Restart(std::vector restartArgs) void App::Exit() { Config::Save(); + Registry::Save(); #ifdef _WIN32 timeEndPeriod(1); diff --git a/UnleashedRecomp/framework.h b/UnleashedRecomp/framework.h index 9bdf1cf3..db88d36f 100644 --- a/UnleashedRecomp/framework.h +++ b/UnleashedRecomp/framework.h @@ -7,6 +7,8 @@ typedef returnType _##procName(__VA_ARGS__); \ _##procName* procName = (_##procName*)PROC_ADDRESS(libraryName, #procName); +#define STR(x) #x + template inline T RoundUp(const T& in_rValue, uint32_t in_round) { diff --git a/UnleashedRecomp/main.cpp b/UnleashedRecomp/main.cpp index 0ff2803f..7cdc605d 100644 --- a/UnleashedRecomp/main.cpp +++ b/UnleashedRecomp/main.cpp @@ -12,9 +12,11 @@ #include #include #include +#include #include #include #include +#include #include #include @@ -165,6 +167,13 @@ int main(int argc, char *argv[]) } Config::Load(); + Registry::Load(); + + if (!Registry::RootDirectoryPath.empty()) + { + if (!os::process::SetWorkingDirectory(std::filesystem::path(Registry::RootDirectoryPath))) + LOGFN_ERROR("Failed to set working directory: \"{}\"", Registry::RootDirectoryPath); + } HostStartup(); diff --git a/UnleashedRecomp/os/linux/process_linux.cpp b/UnleashedRecomp/os/linux/process_linux.cpp index 20f88e43..64fb5262 100644 --- a/UnleashedRecomp/os/linux/process_linux.cpp +++ b/UnleashedRecomp/os/linux/process_linux.cpp @@ -29,6 +29,12 @@ std::filesystem::path os::process::GetWorkingDirectory() } } +// TODO +bool os::process::SetWorkingDirectory(const std::filesystem::path& path) +{ + return false; +} + bool os::process::StartProcess(const std::filesystem::path& path, const std::vector& args, std::filesystem::path work) { pid_t pid = fork(); diff --git a/UnleashedRecomp/os/linux/registry_linux.inl b/UnleashedRecomp/os/linux/registry_linux.inl new file mode 100644 index 00000000..429ba5ea --- /dev/null +++ b/UnleashedRecomp/os/linux/registry_linux.inl @@ -0,0 +1,15 @@ +#include + +// TODO: read from file? +template +bool os::registry::ReadValue(const std::filesystem::path& path, const std::string& name, T& data) +{ + return false; +} + +// TODO: write to file? +template +bool os::registry::WriteValue(const std::filesystem::path& path, const std::string& name, const T& data) +{ + return false; +} diff --git a/UnleashedRecomp/os/process.h b/UnleashedRecomp/os/process.h index 3ddf4fc6..f93ce53a 100644 --- a/UnleashedRecomp/os/process.h +++ b/UnleashedRecomp/os/process.h @@ -4,5 +4,6 @@ namespace os::process { std::filesystem::path GetExecutablePath(); std::filesystem::path GetWorkingDirectory(); + bool SetWorkingDirectory(const std::filesystem::path& path); bool StartProcess(const std::filesystem::path& path, const std::vector& args, std::filesystem::path work = {}); } diff --git a/UnleashedRecomp/os/registry.h b/UnleashedRecomp/os/registry.h new file mode 100644 index 00000000..d71c5944 --- /dev/null +++ b/UnleashedRecomp/os/registry.h @@ -0,0 +1,16 @@ +#pragma once + +namespace os::registry +{ + template + bool ReadValue(const std::filesystem::path& path, const std::string& name, T& data); + + template + bool WriteValue(const std::filesystem::path& path, const std::string& name, const T& data); +} + +#if _WIN32 +#include +#elif defined(__linux__) +#include +#endif diff --git a/UnleashedRecomp/os/win32/process_win32.cpp b/UnleashedRecomp/os/win32/process_win32.cpp index fe7e5be0..1ad6145a 100644 --- a/UnleashedRecomp/os/win32/process_win32.cpp +++ b/UnleashedRecomp/os/win32/process_win32.cpp @@ -20,6 +20,11 @@ std::filesystem::path os::process::GetWorkingDirectory() return std::filesystem::path(workPath); } +bool os::process::SetWorkingDirectory(const std::filesystem::path& path) +{ + return SetCurrentDirectoryW(path.c_str()); +} + bool os::process::StartProcess(const std::filesystem::path& path, const std::vector& args, std::filesystem::path work) { if (path.empty()) diff --git a/UnleashedRecomp/os/win32/registry_win32.inl b/UnleashedRecomp/os/win32/registry_win32.inl new file mode 100644 index 00000000..bffa322f --- /dev/null +++ b/UnleashedRecomp/os/win32/registry_win32.inl @@ -0,0 +1,142 @@ +#include +#include + +static const std::unordered_map g_rootKeys = +{ + { "HKEY_CLASSES_ROOT", HKEY_CLASSES_ROOT }, + { "HKEY_CURRENT_USER", HKEY_CURRENT_USER }, + { "HKEY_LOCAL_MACHINE", HKEY_LOCAL_MACHINE }, + { "HKEY_USERS", HKEY_USERS }, + { "HKEY_CURRENT_CONFIG", HKEY_CURRENT_CONFIG } +}; + +static HKEY ParseRootKey(const std::string& name) +{ + auto it = g_rootKeys.find(name); + + if (it == g_rootKeys.end()) + return nullptr; + + return it->second; +} + +template +bool os::registry::ReadValue(const std::filesystem::path& path, const std::string& name, T& data) +{ + auto pathStr = path.string(); + auto pathSeparator = pathStr.find('\\'); + + if (pathSeparator == std::string::npos) + return false; + + auto rootKey = pathStr.substr(0, pathSeparator); + auto subKey = pathStr.substr(pathSeparator + 1); + + HKEY hRootKey = ParseRootKey(rootKey); + + if (!hRootKey) + return false; + + HKEY hKey; + + if (RegOpenKeyExA(hRootKey, subKey.c_str(), 0, KEY_READ, &hKey) != ERROR_SUCCESS) + return false; + + BYTE buffer[512]; + DWORD bufferSize = sizeof(buffer); + DWORD dataType = 0; + + auto result = RegQueryValueExA(hKey, name.c_str(), NULL, &dataType, buffer, &bufferSize); + + RegCloseKey(hKey); + + if (result != ERROR_SUCCESS) + return false; + + if constexpr (std::is_same_v) + { + if (dataType != REG_SZ) + return false; + + data = std::string((char*)buffer, bufferSize - 1); + } + else if constexpr (std::is_same_v) + { + if (dataType != REG_DWORD) + return false; + + data = *(uint32_t*)buffer; + } + else if constexpr (std::is_same_v) + { + if (dataType != REG_QWORD) + return false; + + data = *(uint32_t*)buffer; + } + else + { + static_assert(false, "Unsupported data type."); + } + + return true; +} + +template +bool os::registry::WriteValue(const std::filesystem::path& path, const std::string& name, const T& data) +{ + auto pathStr = path.string(); + auto pathSeparator = pathStr.find('\\'); + + if (pathSeparator == std::string::npos) + return false; + + auto rootKey = pathStr.substr(0, pathSeparator); + auto subKey = pathStr.substr(pathSeparator + 1); + + HKEY hRootKey = ParseRootKey(rootKey); + + if (!hRootKey) + return false; + + HKEY hKey; + + if (RegCreateKeyExA(hRootKey, subKey.c_str(), 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKey, NULL) != ERROR_SUCCESS) + return false; + + BYTE* pData = nullptr; + DWORD dataSize = 0; + DWORD dataType = 0; + + if constexpr (std::is_same_v) + { + pData = (BYTE*)data.c_str(); + dataSize = data.size() + 1; + dataType = REG_SZ; + } + else if constexpr (std::is_same_v) + { + pData = &data; + dataSize = sizeof(T); + dataType = REG_DWORD; + } + else if constexpr (std::is_same_v) + { + pData = &data; + dataSize = sizeof(T); + dataType = REG_QWORD; + } + else + { + static_assert(false, "Unsupported data type."); + } + + auto result = RegSetValueExA(hKey, name.c_str(), 0, dataType, (const BYTE*)pData, dataSize); + + RegCloseKey(hKey); + + if (result != ERROR_SUCCESS) + return false; + + return true; +} diff --git a/UnleashedRecomp/user/registry.cpp b/UnleashedRecomp/user/registry.cpp new file mode 100644 index 00000000..9e6ca087 --- /dev/null +++ b/UnleashedRecomp/user/registry.cpp @@ -0,0 +1,21 @@ +#include "registry.h" +#include +#include +#include + +void Registry::Load() +{ + std::filesystem::path path = "HKEY_CURRENT_USER\\Software\\UnleashedRecomp"; + + os::registry::ReadValue(path, STR(ExecutableFilePath), ExecutableFilePath); + os::registry::ReadValue(path, STR(RootDirectoryPath), RootDirectoryPath); +} + +void Registry::Save() +{ + std::filesystem::path path = "HKEY_CURRENT_USER\\Software\\UnleashedRecomp"; + + ExecutableFilePath = os::process::GetExecutablePath().string(); + + os::registry::WriteValue(path, STR(ExecutableFilePath), ExecutableFilePath); +} diff --git a/UnleashedRecomp/user/registry.h b/UnleashedRecomp/user/registry.h new file mode 100644 index 00000000..f8977500 --- /dev/null +++ b/UnleashedRecomp/user/registry.h @@ -0,0 +1,11 @@ +#pragma once + +class Registry +{ +public: + inline static std::string ExecutableFilePath; + inline static std::string RootDirectoryPath; + + static void Load(); + static void Save(); +};