diff --git a/.gitignore b/.gitignore index 8335e74d..31f7f3f3 100644 --- a/.gitignore +++ b/.gitignore @@ -348,3 +348,4 @@ staging/debug_report.zip staging/cfg/playerlist.json staging/cfg/.non_portable staging/cfg/account_ages.json +staging/temp/ diff --git a/CMakeLists.txt b/CMakeLists.txt index b62c0166..094f0662 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,6 +14,11 @@ add_subdirectory(submodules/imgui_desktop) add_subdirectory(submodules/mh_stuff) add_subdirectory(tf2_bot_detector_common) + +if (WIN32) + add_subdirectory(tf2_bot_detector_winrt) +endif() + # add_subdirectory(tf2_bot_detector_updater) add_subdirectory(tf2_bot_detector) diff --git a/cmake/init-postproject.cmake b/cmake/init-postproject.cmake index 97718e39..6f3ea6aa 100644 --- a/cmake/init-postproject.cmake +++ b/cmake/init-postproject.cmake @@ -65,6 +65,7 @@ if (MSVC) # Generate PDBs for release builds - RelWithDebInfo is NOT a Release build! set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /Zi") set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} /DEBUG") + set(CMAKE_MODULE_LINKER_FLAGS_RELEASE "${CMAKE_MODULE_LINKER_FLAGS_RELEASE} /DEBUG") set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /DEBUG") if (CMAKE_BUILD_TYPE MATCHES "Release") diff --git a/submodules/mh_stuff b/submodules/mh_stuff index 740aa183..45117a2e 160000 --- a/submodules/mh_stuff +++ b/submodules/mh_stuff @@ -1 +1 @@ -Subproject commit 740aa183a141cfcaa414185302c7adf5c435a89e +Subproject commit 45117a2efea7e1aad03e1a4de27c2830136fcad8 diff --git a/tf2_bot_detector/CMakeLists.txt b/tf2_bot_detector/CMakeLists.txt index 9f19970c..8adac13d 100644 --- a/tf2_bot_detector/CMakeLists.txt +++ b/tf2_bot_detector/CMakeLists.txt @@ -195,6 +195,10 @@ if (TF2BD_ENABLE_DISCORD_INTEGRATION) ) endif() +if (WIN32) + INCLUDE_TF2BD_WINRT(tf2_bot_detector) +endif() + find_package(OpenSSL REQUIRED) find_package(nlohmann_json CONFIG REQUIRED) find_package(libzip CONFIG REQUIRED) diff --git a/tf2_bot_detector/Config/ConfigHelpers.cpp b/tf2_bot_detector/Config/ConfigHelpers.cpp index 43e897c1..64866641 100644 --- a/tf2_bot_detector/Config/ConfigHelpers.cpp +++ b/tf2_bot_detector/Config/ConfigHelpers.cpp @@ -29,36 +29,34 @@ auto tf2_bot_detector::GetConfigFilePaths(const std::string_view& basename) -> C if (auto path = mh::format("cfg/{}.json", basename); IFilesystem::Get().Exists(path)) retVal.m_User = path; - if (const auto cfg = IFilesystem::Get().GetMutableDataDir() / std::filesystem::path("cfg"); - std::filesystem::is_directory(cfg)) + constexpr std::filesystem::directory_options options = + std::filesystem::directory_options::skip_permission_denied | std::filesystem::directory_options::follow_directory_symlink; + + try { - try + for (const auto& file : IFilesystem::Get().IterateDir("cfg", false, options)) { const std::regex filenameRegex(mh::format("{}{}", basename, R"regex(\.(?!official).*\.json)regex"), std::regex::optimize | std::regex::icase); - for (const auto& file : std::filesystem::directory_iterator(cfg, - std::filesystem::directory_options::follow_directory_symlink | std::filesystem::directory_options::skip_permission_denied)) - { - const auto path = file.path(); - const auto filename = path.filename().string(); - if (std::regex_match(filename.begin(), filename.end(), filenameRegex)) - retVal.m_Others.push_back(cfg / filename); - } - } - catch (const std::filesystem::filesystem_error& e) - { - LogException(MH_SOURCE_LOCATION_CURRENT(), e, - "Failed to gather names matching {}.*.json in {}", basename, cfg); + const auto path = file.path(); + const auto filename = path.filename().string(); + if (std::regex_match(filename.begin(), filename.end(), filenameRegex)) + retVal.m_Others.push_back(path); } } + catch (const std::filesystem::filesystem_error& e) + { + LogException(MH_SOURCE_LOCATION_CURRENT(), e, + "Failed to gather names matching {}.*.json in cfg", basename); + } return retVal; } static void SaveJSONToFile(const std::filesystem::path& filename, const nlohmann::json& json) { - IFilesystem::Get().WriteFile(filename, json.dump(1, '\t', true, nlohmann::detail::error_handler_t::ignore) << '\n'); + IFilesystem::Get().WriteFile(filename, json.dump(1, '\t', true, nlohmann::detail::error_handler_t::ignore) << '\n', PathUsage::WriteRoaming); } static ConfigSchemaInfo LoadAndValidateSchema(const ConfigFileBase& config, const nlohmann::json& json) @@ -217,7 +215,7 @@ static void SaveConfigFileBackup(const std::filesystem::path& filename) noexcept std::filesystem::path backupPath; for (size_t i = 1; ; i++) { - backupPath = fs.ResolvePath(filename, PathUsage::Write).remove_filename(); + backupPath = fs.ResolvePath(filename, PathUsage::WriteLocal).remove_filename(); backupPath /= mh::format("{}_BACKUP_{}{}", baseFilename.string(), i, extension.string()); if (!std::filesystem::exists(backupPath)) diff --git a/tf2_bot_detector/DLLMain.cpp b/tf2_bot_detector/DLLMain.cpp index 7b13063c..3f0b664a 100644 --- a/tf2_bot_detector/DLLMain.cpp +++ b/tf2_bot_detector/DLLMain.cpp @@ -4,6 +4,7 @@ #include "UI/MainWindow.h" #include "Util/TextUtils.h" #include "Log.h" +#include "Filesystem.h" #include @@ -66,6 +67,9 @@ TF2_BOT_DETECTOR_EXPORT int tf2_bot_detector::RunProgram(int argc, const char** } #endif + IFilesystem::Get().Init(); + ILogManager::GetInstance().Init(); + for (int i = 1; i < argc; i++) { #ifdef _DEBUG diff --git a/tf2_bot_detector/Filesystem.cpp b/tf2_bot_detector/Filesystem.cpp index 44988b45..0ef38281 100644 --- a/tf2_bot_detector/Filesystem.cpp +++ b/tf2_bot_detector/Filesystem.cpp @@ -2,6 +2,7 @@ #include "Log.h" #include "Platform/Platform.h" +#include #include #include #include @@ -15,26 +16,34 @@ namespace class Filesystem final : public IFilesystem { public: - Filesystem(); + void Init() override; mh::generator GetSearchPaths() const override; std::filesystem::path ResolvePath(const std::filesystem::path& path, PathUsage usage) const override; std::string ReadFile(std::filesystem::path path) const override; - void WriteFile(std::filesystem::path path, const void* begin, const void* end) const override; + void WriteFile(std::filesystem::path path, const void* begin, const void* end, PathUsage usage) const override; - std::filesystem::path GetMutableDataDir() const override; - std::filesystem::path GetRealMutableDataDir() const override; + std::filesystem::path GetLocalAppDataDir() const override; + std::filesystem::path GetRoamingAppDataDir() const override; + std::filesystem::path GetTempDir() const override; + + mh::generator IterateDir(std::filesystem::path path, bool recursive, + std::filesystem::directory_options options) const override; private: + bool m_IsInit = false; + void EnsureInit(MH_SOURCE_LOCATION_AUTO(location)) const; + static constexpr char NON_PORTABLE_MARKER[] = ".non_portable"; static constexpr char APPDATA_SUBFOLDER[] = "TF2 Bot Detector"; std::vector m_SearchPaths; bool m_IsPortable; - const std::filesystem::path m_ExeDir = Platform::GetCurrentExeDir(); - const std::filesystem::path m_WorkingDir = std::filesystem::current_path(); - const std::filesystem::path m_AppDataDir = Platform::GetAppDataDir() / APPDATA_SUBFOLDER; + std::filesystem::path m_ExeDir; + std::filesystem::path m_WorkingDir; + std::filesystem::path m_LocalAppDataDir; + std::filesystem::path m_RoamingAppDataDir; //std::filesystem::path m_MutableDataDir = ChooseMutableDataPath(); }; } @@ -45,46 +54,82 @@ IFilesystem& IFilesystem::Get() return s_Filesystem; } -Filesystem::Filesystem() try +void Filesystem::Init() { - DebugLog(MH_SOURCE_LOCATION_CURRENT(), "Initializing filesystem..."); + assert(mh::is_main_thread()); - m_SearchPaths.insert(m_SearchPaths.begin(), m_ExeDir); + if (!m_IsInit) + { + m_IsInit = true; + try + { + DebugLog("Initializing filesystem..."); - if (m_WorkingDir != m_ExeDir) - m_SearchPaths.insert(m_SearchPaths.begin(), m_WorkingDir); + m_ExeDir = Platform::GetCurrentExeDir(); + m_WorkingDir = std::filesystem::current_path(); + m_LocalAppDataDir = Platform::GetRootLocalAppDataDir() / APPDATA_SUBFOLDER; + m_RoamingAppDataDir = Platform::GetRootRoamingAppDataDir() / APPDATA_SUBFOLDER; - if (!ResolvePath(std::filesystem::path("cfg") / NON_PORTABLE_MARKER, PathUsage::Read).empty()) - { - DebugLog("Installation detected as non-portable."); - m_SearchPaths.insert(m_SearchPaths.begin(), m_AppDataDir); - m_IsPortable = false; + m_SearchPaths.insert(m_SearchPaths.begin(), m_ExeDir); - // If we crash, we want our working directory to be somewhere we can write to. - if (std::filesystem::create_directories(m_AppDataDir)) - DebugLog("Created {}", m_AppDataDir); + if (m_WorkingDir != m_ExeDir) + m_SearchPaths.insert(m_SearchPaths.begin(), m_WorkingDir); - std::filesystem::current_path(m_AppDataDir); - DebugLog("Set working directory to {}", m_AppDataDir); - } - else - { - DebugLog("Installation detected as portable."); - m_IsPortable = true; - } + if (!ResolvePath(std::filesystem::path("cfg") / NON_PORTABLE_MARKER, PathUsage::Read).empty()) + { + DebugLog("Installation detected as non-portable."); + m_SearchPaths.insert(m_SearchPaths.begin(), m_RoamingAppDataDir); + m_IsPortable = false; - { - std::string initMsg = "Filesystem initialized. Search paths:"; - for (const auto& searchPath : m_SearchPaths) - initMsg << "\n\t" << searchPath; + // If we crash, we want our working directory to be somewhere we can write to. + if (std::filesystem::create_directories(m_RoamingAppDataDir)) + DebugLog("Created {}", m_RoamingAppDataDir); + + if (std::filesystem::create_directories(m_LocalAppDataDir)) + DebugLog("Created {}", m_LocalAppDataDir); - DebugLog(std::move(initMsg)); + std::filesystem::current_path(m_LocalAppDataDir); + DebugLog("Set working directory to {}", m_LocalAppDataDir); + } + else + { + DebugLog("Installation detected as portable."); + m_IsPortable = true; + } + + if (auto legacyPath = Platform::GetLegacyAppDataDir(); std::filesystem::exists(legacyPath)) + { + legacyPath /= APPDATA_SUBFOLDER; + if (std::filesystem::exists(legacyPath)) + { + Log("Found legacy appdata folder {}, copying to {}...", legacyPath, m_RoamingAppDataDir); + std::filesystem::copy(legacyPath, m_RoamingAppDataDir, + std::filesystem::copy_options::recursive | std::filesystem::copy_options::skip_existing); + + Log("Copy complete. Deleting {}...", legacyPath); + std::filesystem::remove_all(legacyPath); + } + } + + { + std::string initMsg = "Filesystem initialized. Search paths:"; + for (const auto& searchPath : m_SearchPaths) + initMsg << "\n\t" << searchPath; + + DebugLog(std::move(initMsg)); + } + + DebugLog("\tLocalAppDataDir: {}", GetLocalAppDataDir()); + DebugLog("\tRoamingAppDataDir: {}", GetRoamingAppDataDir()); + DebugLog("\tTempDir: {}", GetTempDir()); + std::filesystem::create_directories(GetTempDir()); + } + catch (...) + { + LogFatalException("Failed to initialize filesystem"); + } } } -catch (const std::exception& e) -{ - LogFatalException(MH_SOURCE_LOCATION_CURRENT(), e, "Failed to initialize filesystem"); -} mh::generator Filesystem::GetSearchPaths() const { @@ -97,6 +142,8 @@ std::filesystem::path Filesystem::ResolvePath(const std::filesystem::path& path, if (path.is_absolute()) return path; + EnsureInit(); + auto retVal = std::invoke([&]() -> std::filesystem::path { if (usage == PathUsage::Read) @@ -114,9 +161,13 @@ std::filesystem::path Filesystem::ResolvePath(const std::filesystem::path& path, DebugLogWarning("Unable to find {} in any search path", path); return {}; } - else if (usage == PathUsage::Write) + else if (usage == PathUsage::WriteLocal) + { + return GetLocalAppDataDir() / path; + } + else if (usage == PathUsage::WriteRoaming) { - return GetRealMutableDataDir() / path; + return GetRoamingAppDataDir() / path; } else { @@ -155,9 +206,9 @@ catch (...) throw; } -void Filesystem::WriteFile(std::filesystem::path path, const void* begin, const void* end) const try +void Filesystem::WriteFile(std::filesystem::path path, const void* begin, const void* end, PathUsage usage) const try { - path = ResolvePath(path, PathUsage::Write); + path = ResolvePath(path, usage); // Create any missing directories if (auto folderPath = mh::copy(path).remove_filename(); std::filesystem::create_directories(folderPath)) @@ -176,18 +227,62 @@ catch (...) throw; } -std::filesystem::path Filesystem::GetMutableDataDir() const +std::filesystem::path Filesystem::GetLocalAppDataDir() const { + EnsureInit(); + + return m_IsPortable ? m_WorkingDir : m_LocalAppDataDir; +} + +std::filesystem::path Filesystem::GetRoamingAppDataDir() const +{ + EnsureInit(); + + return m_IsPortable ? m_WorkingDir : m_RoamingAppDataDir; +} + +std::filesystem::path Filesystem::GetTempDir() const +{ + EnsureInit(); + if (m_IsPortable) - return m_WorkingDir; + return m_WorkingDir / "temp"; else - return m_AppDataDir; + return Platform::GetRootTempDataDir() / "TF2 Bot Detector"; } -std::filesystem::path Filesystem::GetRealMutableDataDir() const +void Filesystem::EnsureInit(const mh::source_location& location) const { - if (m_IsPortable) - return m_WorkingDir; + if (!m_IsInit) + { + assert(false); + LogFatalError(location, "Filesystem::EnsureInit failed"); + } +} + +template +static mh::generator IterateDirImpl( + const std::vector& searchPaths, std::filesystem::path path, std::filesystem::directory_options options) +{ + for (std::filesystem::path searchPath : searchPaths) + { + searchPath /= path; + + if (std::filesystem::exists(searchPath)) + { + for (const std::filesystem::directory_entry& entry : TIter(searchPath, options)) + co_yield entry; + } + } +} + +mh::generator Filesystem::IterateDir(std::filesystem::path path, bool recursive, + std::filesystem::directory_options options) const +{ + assert(!path.is_absolute()); + + if (recursive) + return IterateDirImpl(m_SearchPaths, std::move(path), options); else - return Platform::GetRealAppDataDir() / APPDATA_SUBFOLDER; + return IterateDirImpl(m_SearchPaths, std::move(path), options); } diff --git a/tf2_bot_detector/Filesystem.h b/tf2_bot_detector/Filesystem.h index e658f07b..4e50525e 100644 --- a/tf2_bot_detector/Filesystem.h +++ b/tf2_bot_detector/Filesystem.h @@ -15,7 +15,10 @@ namespace tf2_bot_detector enum class PathUsage { Read, - Write, + + WriteRoaming, + WriteLocal, + Write [[deprecated]] = WriteRoaming, }; class IFilesystem @@ -25,35 +28,39 @@ namespace tf2_bot_detector static IFilesystem& Get(); + virtual void Init() = 0; + virtual mh::generator GetSearchPaths() const = 0; virtual std::filesystem::path ResolvePath(const std::filesystem::path& path, PathUsage usage) const = 0; - virtual std::filesystem::path GetMutableDataDir() const = 0; - virtual std::filesystem::path GetRealMutableDataDir() const = 0; + virtual std::filesystem::path GetLocalAppDataDir() const = 0; + virtual std::filesystem::path GetRoamingAppDataDir() const = 0; + virtual std::filesystem::path GetTempDir() const = 0; //virtual std::fstream OpenFile(const std::filesystem::path& path) = 0; virtual std::string ReadFile(std::filesystem::path path) const = 0; - virtual void WriteFile(std::filesystem::path path, const void* begin, const void* end) const = 0; + virtual void WriteFile(std::filesystem::path path, const void* begin, const void* end, PathUsage usage) const = 0; + + virtual mh::generator IterateDir(std::filesystem::path path, bool recursive, + std::filesystem::directory_options options = std::filesystem::directory_options::none) const = 0; - void WriteFile(const std::filesystem::path& path, const std::string_view& data) const + void WriteFile(const std::filesystem::path& path, const std::string_view& data, PathUsage usage) const { - return WriteFile(path, data.data(), data.data() + data.size()); + return WriteFile(path, data.data(), data.data() + data.size(), usage); } static std::filesystem::path GetLogsDir(const std::filesystem::path& baseDataDir) { return baseDataDir / "logs"; } - std::filesystem::path GetLogsDir() const { return GetLogsDir(GetMutableDataDir()); } - std::filesystem::path GetRealLogsDir() const { return GetLogsDir(GetRealMutableDataDir()); } + std::filesystem::path GetLogsDir() const { return GetLogsDir(GetLocalAppDataDir()); } static std::filesystem::path GetConfigDir(const std::filesystem::path& baseDataDir) { return baseDataDir / "cfg"; } - std::filesystem::path GetConfigDir() const { return GetConfigDir(GetMutableDataDir()); } - std::filesystem::path GetRealConfigDir() const { return GetConfigDir(GetRealMutableDataDir()); } + std::filesystem::path GetConfigDir() const { return GetConfigDir(GetRoamingAppDataDir()); } bool Exists(const std::filesystem::path& path) const { @@ -64,5 +71,6 @@ namespace tf2_bot_detector MH_ENUM_REFLECT_BEGIN(tf2_bot_detector::PathUsage) MH_ENUM_REFLECT_VALUE(Read) - MH_ENUM_REFLECT_VALUE(Write) + MH_ENUM_REFLECT_VALUE(WriteRoaming) + MH_ENUM_REFLECT_VALUE(WriteLocal) MH_ENUM_REFLECT_END() diff --git a/tf2_bot_detector/Log.cpp b/tf2_bot_detector/Log.cpp index f27cce20..f852b1b8 100644 --- a/tf2_bot_detector/Log.cpp +++ b/tf2_bot_detector/Log.cpp @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -36,11 +37,7 @@ namespace class LogManager final : public ILogManager { public: - LogManager(); - LogManager(const LogManager&) = delete; - LogManager& operator=(const LogManager&) = delete; - - void Init(); + void Init() override; void Log(std::string msg, const LogMessageColor& color, LogSeverity severity, LogVisibility visibility = LogVisibility::Default, time_point_t timestamp = tfbd_clock_t::now()) override; @@ -59,6 +56,9 @@ namespace void AddSecret(std::string value, std::string replace) override; private: + bool m_IsInit = false; + void EnsureInit(MH_SOURCE_LOCATION_AUTO(location)) const; + std::filesystem::path m_FileName; std::ofstream m_File; mutable std::recursive_mutex m_LogMutex; @@ -81,18 +81,7 @@ namespace static LogManager& GetLogState() { - // We need this song and dance because this initialization might be reentrant - // (filesystem might print log messages) - bool isFirstInit = false; - static LogManager s_LogState = [&]() - { - isFirstInit = true; - return LogManager{}; - }(); - - if (isFirstInit) - s_LogState.Init(); - + static LogManager s_LogState; return s_LogState; } } @@ -106,59 +95,72 @@ static constexpr LogMessageColor COLOR_DEFAULT = { 1, 1, 1, 1 }; static constexpr LogMessageColor COLOR_WARNING = { 1, 0.5, 0, 1 }; static constexpr LogMessageColor COLOR_ERROR = { 1, 0.25, 0, 1 }; -LogManager::LogManager() -{ -} - void LogManager::Init() { - ::DebugLog(MH_SOURCE_LOCATION_CURRENT()); - std::lock_guard lock(m_LogMutex); + assert(!m_IsInit); - const auto t = ToTM(tfbd_clock_t::now()); - const mh::fmtstr<128> timestampStr("{}", std::put_time(&t, "%Y-%m-%d_%H-%M-%S")); - - // Pick file name + if (!m_IsInit) { - std::filesystem::path logPath = IFilesystem::Get().ResolvePath("logs", PathUsage::Write); + ::DebugLog("Initializing LogManager..."); + std::lock_guard lock(m_LogMutex); - std::error_code ec; - std::filesystem::create_directories(logPath, ec); - if (ec) - { - ::LogWarning("Failed to create one or more directory in the path {}. Log output will go to stdout.", - logPath); - } - else + const auto t = ToTM(tfbd_clock_t::now()); + const mh::fmtstr<128> timestampStr("{}", std::put_time(&t, "%Y-%m-%d_%H-%M-%S")); + + // Pick file name { - m_FileName = logPath / mh::fmtstr<128>("{}.log", timestampStr).view(); + std::filesystem::path logPath = IFilesystem::Get().GetLogsDir(); + + std::error_code ec; + std::filesystem::create_directories(logPath, ec); + if (ec) + { + ::LogWarning("Failed to create one or more directory in the path {}. Log output will go to stdout.", + logPath); + } + else + { + m_FileName = logPath / mh::fmtstr<128>("{}.log", timestampStr).view(); + } } - } - - // Try open file - if (!m_FileName.empty()) - { - m_File = std::ofstream(m_FileName, std::ofstream::ate | std::ofstream::app | std::ofstream::out | std::ofstream::binary); - if (!m_File.good()) - ::LogWarning("Failed to open log file {}. Log output will go to stdout only.", m_FileName); - } - { - auto logDir = std::filesystem::path("logs") / "console"; - std::error_code ec; - std::filesystem::create_directories(logDir, ec); - if (ec) + // Try open file + if (!m_FileName.empty()) { - ::LogWarning("Failed to create one or more directory in the path {}. Console output will not be logged.", - logDir); + m_File = std::ofstream(m_FileName, std::ofstream::ate | std::ofstream::app | std::ofstream::out | std::ofstream::binary); + if (!m_File.good()) + { + ::LogWarning("Failed to open log file {}. Log output will go to stdout only.", m_FileName); + } + else + { + // Dump all log messages being held in memory to the file, if it was successfully opened + for (const auto& msg : m_LogMessages) + LogToStream(msg.m_Text, m_File, msg.m_Timestamp, true); + + ::DebugLog("Dumped all pending log messages to {}.", m_FileName); + } } - else + { - auto logPath = logDir / mh::fmtstr<128>("console_{}.log", timestampStr).view(); - m_ConsoleLogFile = std::ofstream(logPath, std::ofstream::ate | std::ofstream::binary); - if (!m_ConsoleLogFile.good()) - ::LogWarning("Failed to open console log file {}. Console output will not be logged.", logPath); + auto logDir = std::filesystem::path("logs") / "console"; + std::error_code ec; + std::filesystem::create_directories(logDir, ec); + if (ec) + { + ::LogWarning("Failed to create one or more directory in the path {}. Console output will not be logged.", + logDir); + } + else + { + auto logPath = logDir / mh::fmtstr<128>("console_{}.log", timestampStr).view(); + m_ConsoleLogFile = std::ofstream(logPath, std::ofstream::ate | std::ofstream::binary); + if (!m_ConsoleLogFile.good()) + ::LogWarning("Failed to open console log file {}. Console output will not be logged.", logPath); + } } + + m_IsInit = true; } } @@ -186,6 +188,8 @@ void LogManager::LogToStream(std::string msg, std::ostream& output, time_point_t void LogManager::AddSecret(std::string value, std::string replace) { + EnsureInit(); + if (value.empty()) return; @@ -305,7 +309,7 @@ void LogManager::Log(std::string msg, const LogMessageColor& color, { m_LogMessages.push_back({ timestamp, std::move(msg), { color.r, color.g, color.b, color.a } }); - if (m_LogMessages.size() > MAX_LOG_MESSAGES) + if (m_IsInit && m_LogMessages.size() > MAX_LOG_MESSAGES) { m_LogMessages.erase(m_LogMessages.begin(), std::next(m_LogMessages.begin(), m_LogMessages.size() - MAX_LOG_MESSAGES)); @@ -315,6 +319,8 @@ void LogManager::Log(std::string msg, const LogMessageColor& color, mh::generator LogManager::GetVisibleMsgs() const { + EnsureInit(); + std::lock_guard lock(m_LogMutex); size_t start = m_VisibleLogMessagesStart; @@ -327,6 +333,8 @@ mh::generator LogManager::GetVisibleMsgs() const void LogManager::ClearVisibleMsgs() { + EnsureInit(); + std::lock_guard lock(m_LogMutex); DebugLog("Clearing visible log messages..."); m_VisibleLogMessagesStart = m_LogMessages.size(); @@ -334,12 +342,16 @@ void LogManager::ClearVisibleMsgs() void LogManager::LogConsoleOutput(const std::string_view& consoleOutput) { + EnsureInit(); + std::lock_guard lock(m_ConsoleLogMutex); m_ConsoleLogFile << consoleOutput << std::flush; } void LogManager::CleanupLogFiles() try { + EnsureInit(); + constexpr auto MAX_LOG_LIFETIME = 24h * 7; { std::lock_guard lock(m_LogMutex); @@ -356,7 +368,45 @@ catch (const std::filesystem::filesystem_error& e) LogError(MH_SOURCE_LOCATION_CURRENT(), e.what()); } +void LogManager::EnsureInit(const mh::source_location& location) const +{ + if (!m_IsInit) + { + assert(false); + LogFatalError(location, "LogManager::EnsureInit failed"); + } +} + LogMessageColor::LogMessageColor(const ImVec4& vec) : LogMessageColor(vec.x, vec.y, vec.z, vec.w) { } + +#define LOG_DEFINITION_HELPER(name, defaultColor, severity, visibility) \ + void name(const mh::source_location& location) \ + { \ + name((defaultColor), location); \ + } \ + void name(const LogMessageColor& color, const std::string_view& msg, const mh::source_location& location) \ + { \ + name(color, location, msg); \ + } \ + void name(const std::string_view& msg, const mh::source_location& location) \ + { \ + name(location, msg); \ + } \ + void name(const LogMessageColor& color, const mh::source_location& location) \ + { \ + name(color, location, std::string_view{}); \ + } \ + +namespace tf2_bot_detector +{ + LOG_DEFINITION_HELPER(Log, LogColors::DEFAULT, LogSeverity::Info, LogVisibility::Default); + LOG_DEFINITION_HELPER(DebugLog, LogColors::DEFAULT_DEBUG, LogSeverity::Info, LogVisibility::Debug); + LOG_DEFINITION_HELPER(LogWarning, LogColors::WARN, LogSeverity::Warning, LogVisibility::Default); + LOG_DEFINITION_HELPER(DebugLogWarning, LogColors::WARN_DEBUG, LogSeverity::Warning, LogVisibility::Debug); + LOG_DEFINITION_HELPER(LogError, LogColors::ERROR, LogSeverity::Error, LogVisibility::Default); +} + +#undef LOG_DEFINITION_HELPER diff --git a/tf2_bot_detector/Log.h b/tf2_bot_detector/Log.h index 55174bc7..579ee824 100644 --- a/tf2_bot_detector/Log.h +++ b/tf2_bot_detector/Log.h @@ -71,6 +71,8 @@ namespace tf2_bot_detector static ILogManager& GetInstance(); + virtual void Init() = 0; + virtual void Log(std::string msg, const LogMessageColor& color, LogSeverity severity, LogVisibility visibility, time_point_t timestamp = clock_t::now()) = 0; @@ -155,22 +157,10 @@ namespace tf2_bot_detector { \ name(fmtStr.m_Location, fmtStr.m_Value, args...); \ } \ - inline void name(const LogMessageColor& color, const std::string_view& msg, MH_SOURCE_LOCATION_AUTO(location)) \ - { \ - name(color, location, msg); \ - } \ - inline void name(const std::string_view& msg, MH_SOURCE_LOCATION_AUTO(location)) \ - { \ - name(location, msg); \ - } \ - inline void name(const LogMessageColor& color, MH_SOURCE_LOCATION_AUTO(location)) \ - { \ - name(color, location, std::string_view{}); \ - } \ - inline void name(MH_SOURCE_LOCATION_AUTO(location)) \ - { \ - name((defaultColor), location); \ - } + void name(const LogMessageColor& color, const std::string_view& msg, MH_SOURCE_LOCATION_AUTO(location)); \ + void name(const std::string_view& msg, MH_SOURCE_LOCATION_AUTO(location)); \ + void name(const LogMessageColor& color, MH_SOURCE_LOCATION_AUTO(location)); \ + void name(MH_SOURCE_LOCATION_AUTO(location)); LOG_DEFINITION_HELPER(Log, LogColors::DEFAULT, LogSeverity::Info, LogVisibility::Default); LOG_DEFINITION_HELPER(DebugLog, LogColors::DEFAULT_DEBUG, LogSeverity::Info, LogVisibility::Debug); diff --git a/tf2_bot_detector/Networking/SteamAPI.cpp b/tf2_bot_detector/Networking/SteamAPI.cpp index bcdf17f4..795c59dc 100644 --- a/tf2_bot_detector/Networking/SteamAPI.cpp +++ b/tf2_bot_detector/Networking/SteamAPI.cpp @@ -4,6 +4,7 @@ #include "HTTPClient.h" #include "HTTPHelpers.h" #include "Log.h" +#include "Filesystem.h" #include #include @@ -51,7 +52,7 @@ namespace public: AvatarCacheManager() { - m_CacheDir = std::filesystem::temp_directory_path() / "TF2 Bot Detector/Steam Avatar Cache"; + m_CacheDir = IFilesystem::Get().GetTempDir() / "Steam Avatar Cache"; std::filesystem::create_directories(m_CacheDir); DeleteOldFiles(m_CacheDir, 24h * 7); } @@ -96,7 +97,13 @@ namespace std::filesystem::path m_CacheDir; mutable std::mutex m_CacheMutex; - } s_AvatarCacheManager; + }; + + static AvatarCacheManager& GetAvatarCacheManager() + { + static AvatarCacheManager s_AvatarCacheManager; + return s_AvatarCacheManager; + } } std::optional PlayerSummary::GetAccountAge() const @@ -134,7 +141,7 @@ std::string PlayerSummary::GetAvatarURL(AvatarQuality quality) const mh::task PlayerSummary::GetAvatarBitmap(std::shared_ptr client, AvatarQuality quality) const { - return s_AvatarCacheManager.GetAvatarBitmap(client.get(), GetAvatarURL(quality), m_AvatarHash); + return GetAvatarCacheManager().GetAvatarBitmap(client.get(), GetAvatarURL(quality), m_AvatarHash); } std::string_view PlayerSummary::GetVanityURL() const diff --git a/tf2_bot_detector/Platform/Platform.h b/tf2_bot_detector/Platform/Platform.h index e702d6d4..ee940e51 100644 --- a/tf2_bot_detector/Platform/Platform.h +++ b/tf2_bot_detector/Platform/Platform.h @@ -23,8 +23,10 @@ namespace tf2_bot_detector SteamID GetCurrentActiveSteamID(); std::filesystem::path GetCurrentExeDir(); - std::filesystem::path GetAppDataDir(); - std::filesystem::path GetRealAppDataDir(); + std::filesystem::path GetRootLocalAppDataDir(); + std::filesystem::path GetRootRoamingAppDataDir(); + std::filesystem::path GetLegacyAppDataDir(); + std::filesystem::path GetRootTempDataDir(); bool IsDebuggerAttached(); diff --git a/tf2_bot_detector/Platform/Windows/Windows.cpp b/tf2_bot_detector/Platform/Windows/Windows.cpp index 8d945d28..0949ed3c 100644 --- a/tf2_bot_detector/Platform/Windows/Windows.cpp +++ b/tf2_bot_detector/Platform/Windows/Windows.cpp @@ -2,30 +2,72 @@ #include "Util/TextUtils.h" #include "Log.h" #include "WindowsHelpers.h" +#include "tf2_bot_detector_winrt.h" +#include +#include #include +#include #include #define WIN32_LEAN_AND_MEAN 1 #include #include #include +#include +using namespace tf2_bot_detector; using namespace std::string_view_literals; -std::filesystem::path tf2_bot_detector::Platform::GetCurrentExeDir() +namespace tf2_bot_detector { - WCHAR path[32768]; - const auto length = GetModuleFileNameW(nullptr, path, (DWORD)std::size(path)); + class WinRT; +} - const auto error = GetLastError(); - if (error != ERROR_SUCCESS) - throw tf2_bot_detector::Windows::GetLastErrorException(E_FAIL, error, "Call to GetModuleFileNameW() failed"); +static std::filesystem::path GetKnownFolderPath(const KNOWNFOLDERID& id) +{ + PWSTR str; + CHECK_HR(SHGetKnownFolderPath(id, 0, nullptr, &str)); - if (length == 0) - throw std::runtime_error("Call to GetModuleFileNameW() failed: return value was 0"); + std::filesystem::path retVal(str); - return std::filesystem::path(path, path + length).remove_filename(); + CoTaskMemFree(str); + return retVal; +} + +static void* GetProcAddressHelper(const wchar_t* moduleName, const char* symbolName, bool isCritical = false, MH_SOURCE_LOCATION_AUTO(location)) +{ + if (!moduleName) + throw std::invalid_argument("moduleName was nullptr"); + if (!moduleName[0]) + throw std::invalid_argument("moduleName was empty"); + if (!symbolName) + throw std::invalid_argument("symbolName was nullptr"); + if (!symbolName[0]) + throw std::invalid_argument("symbolName was empty"); + + HMODULE moduleHandle = GetModuleHandleW(moduleName); + if (!moduleHandle) + { + auto err = GetLastError(); + throw std::system_error(err, std::system_category(), mh::format("Failed to GetModuleHandle({})", mh::change_encoding(moduleName))); + } + + auto address = GetProcAddress(moduleHandle, symbolName); + if (!address) + { + auto err = GetLastError(); + auto ec = std::error_code(err, std::system_category()); + + auto msg = mh::format("{}: Failed to find function {} in {}", location, symbolName, mh::change_encoding(moduleName)); + + if (!isCritical) + DebugLogWarning(location, "{}: {}", msg, ec); + else + throw std::system_error(ec, msg); + } + + return address; } namespace tf2_bot_detector @@ -37,23 +79,9 @@ namespace tf2_bot_detector WCHAR name[PACKAGE_FAMILY_NAME_MAX_LENGTH + 1]; UINT32 nameLength = UINT32(std::size(name)); - constexpr const char FUNC_NAME[] = "GetCurrentPackageFamilyName"; - constexpr const char MODULE_NAME[] = "Kernel32.dll"; using func_type = LONG(*)(UINT32* packageFamilyNameLength, PWSTR packageFamilyName); - HMODULE kernel32 = GetModuleHandleA(MODULE_NAME); - if (!kernel32) - { - throw std::runtime_error( - mh::format("{}: Unable to get module handle for {}", MH_SOURCE_LOCATION_CURRENT(), MODULE_NAME)); - } - - const auto func = reinterpret_cast(GetProcAddress(kernel32, FUNC_NAME)); - if (!func) - { - throw std::runtime_error( - mh::format("{}: Unable to find function for {}", MH_SOURCE_LOCATION_CURRENT(), FUNC_NAME)); - } + const auto func = reinterpret_cast(GetProcAddressHelper(L"Kernel32.dll", "GetCurrentPackageFamilyName", true)); const auto errc = func(&nameLength, name); @@ -74,30 +102,121 @@ namespace tf2_bot_detector } } -static std::filesystem::path GetKnownFolderPath(const KNOWNFOLDERID& id) +namespace { - PWSTR str; - CHECK_HR(SHGetKnownFolderPath(id, 0, nullptr, &str)); + class FallbackWinRTInterface final : public tf2_bot_detector::WinRT + { + public: + std::filesystem::path GetLocalAppDataDir() const override + { + return GetKnownFolderPath(FOLDERID_LocalAppData); + } + std::filesystem::path GetRoamingAppDataDir() const override + { + return GetKnownFolderPath(FOLDERID_RoamingAppData); + } + std::filesystem::path GetTempDir() const override + { + return std::filesystem::temp_directory_path(); + } + }; +} - std::filesystem::path retVal(str); +static const tf2_bot_detector::WinRT* GetWinRTInterface() +{ + struct WinRTHelper + { + WinRTHelper() + { + constexpr wchar_t WINRT_DLL_NAME[] = L"tf2_bot_detector_winrt.dll"; + m_Module = mh_ensure(LoadLibraryW(WINRT_DLL_NAME)); - CoTaskMemFree(str); - return retVal; + CreateWinRTInterfaceFn func = reinterpret_cast(GetProcAddressHelper(WINRT_DLL_NAME, "CreateWinRTInterface")); + + m_WinRT.reset(func()); + } + WinRTHelper(WinRTHelper&& other) noexcept : + m_Module(std::exchange(other.m_Module, nullptr)), + m_WinRT(std::move(other.m_WinRT)) + { + } + WinRTHelper& operator=(WinRTHelper&& other) noexcept + { + destroy(); + + m_Module = std::exchange(other.m_Module, nullptr); + m_WinRT = std::move(other.m_WinRT); + + return *this; + } + ~WinRTHelper() + { + destroy(); + } + + void destroy() + { + m_WinRT.reset(); + + if (m_Module) + { + mh_ensure(FreeLibrary(m_Module)); + m_Module = {}; + } + } + + HMODULE m_Module{}; + std::unique_ptr m_WinRT; + }; + + static std::optional s_Helper = []() -> std::optional + { + std::optional helper; + if (IsWindows10OrGreater()) + helper.emplace(); + + return std::move(helper); + }(); + static const FallbackWinRTInterface s_FallbackInterface; + + return s_Helper.has_value() ? s_Helper->m_WinRT.get() : &s_FallbackInterface; } -std::filesystem::path tf2_bot_detector::Platform::GetRealAppDataDir() +std::filesystem::path tf2_bot_detector::Platform::GetCurrentExeDir() { - const auto lad = GetKnownFolderPath(FOLDERID_LocalAppData); + WCHAR path[32768]; + const auto length = GetModuleFileNameW(nullptr, path, (DWORD)std::size(path)); - const auto packageAppDataDir = lad / "Packages" / GetCurrentPackageFamilyName() / "LocalCache" / "Roaming"; + const auto error = GetLastError(); + if (error != ERROR_SUCCESS) + throw tf2_bot_detector::Windows::GetLastErrorException(E_FAIL, error, "Call to GetModuleFileNameW() failed"); - if (std::filesystem::exists(packageAppDataDir)) - return packageAppDataDir; - else - return GetAppDataDir(); + if (length == 0) + throw std::runtime_error("Call to GetModuleFileNameW() failed: return value was 0"); + + return std::filesystem::path(path, path + length).remove_filename(); +} + +std::filesystem::path tf2_bot_detector::Platform::GetLegacyAppDataDir() +{ + auto packageFamilyName = GetCurrentPackageFamilyName(); + if (packageFamilyName.empty()) + return {}; + + return GetKnownFolderPath(FOLDERID_LocalAppData) / "Packages" / packageFamilyName / "LocalCache" / "Roaming"; +} + +std::filesystem::path tf2_bot_detector::Platform::GetRootLocalAppDataDir() +{ + return GetWinRTInterface()->GetLocalAppDataDir(); +} + +std::filesystem::path tf2_bot_detector::Platform::GetRootRoamingAppDataDir() +{ + return GetWinRTInterface()->GetRoamingAppDataDir(); } -std::filesystem::path tf2_bot_detector::Platform::GetAppDataDir() +std::filesystem::path tf2_bot_detector::Platform::GetRootTempDataDir() { - return GetKnownFolderPath(FOLDERID_RoamingAppData); + return GetWinRTInterface()->GetTempDir(); } diff --git a/tf2_bot_detector/UI/MainWindow.cpp b/tf2_bot_detector/UI/MainWindow.cpp index 9915ba12..1757e56f 100644 --- a/tf2_bot_detector/UI/MainWindow.cpp +++ b/tf2_bot_detector/UI/MainWindow.cpp @@ -438,22 +438,22 @@ void MainWindow::PrintDebugInfo() void MainWindow::GenerateDebugReport() try { Log("Generating debug_report.zip..."); - const auto dbgReportLocation = IFilesystem::Get().ResolvePath("debug_report.zip", PathUsage::Write); + const auto dbgReportLocation = IFilesystem::Get().ResolvePath("debug_report.zip", PathUsage::WriteLocal); { using namespace libzippp; ZipArchive archive(dbgReportLocation.string()); archive.open(ZipArchive::New); - const auto mutableDir = IFilesystem::Get().GetMutableDataDir(); - for (const auto& entry : std::filesystem::recursive_directory_iterator(mutableDir / "logs")) + const auto logsDir = IFilesystem::Get().GetLogsDir(); + for (const auto& entry : std::filesystem::recursive_directory_iterator(logsDir)) { if (!entry.is_regular_file()) continue; const auto& path = entry.path(); - if (archive.addFile(std::filesystem::relative(path, mutableDir).string(), path.string())) + if (archive.addFile(std::filesystem::relative(path, logsDir / "..").string(), path.string())) Log("Added file to debug report: {}", path); else LogWarning("Failed to add file to debug report: {}", path); @@ -617,9 +617,9 @@ void MainWindow::OnDrawMenuBar() if (ImGui::BeginMenu("File")) { if (ImGui::MenuItem("Open Config Folder")) - Shell::ExploreTo(IFilesystem::Get().GetRealConfigDir()); + Shell::ExploreTo(IFilesystem::Get().GetConfigDir()); if (ImGui::MenuItem("Open Logs Folder")) - Shell::ExploreTo(IFilesystem::Get().GetRealLogsDir()); + Shell::ExploreTo(IFilesystem::Get().GetLogsDir()); ImGui::Separator(); diff --git a/tf2_bot_detector/UpdateManager.cpp b/tf2_bot_detector/UpdateManager.cpp index cb9192e7..96484b06 100644 --- a/tf2_bot_detector/UpdateManager.cpp +++ b/tf2_bot_detector/UpdateManager.cpp @@ -7,6 +7,7 @@ #include "Util/JSONUtils.h" #include "Log.h" #include "ReleaseChannel.h" +#include "Filesystem.h" #include #include @@ -244,15 +245,15 @@ namespace bool CanReplaceUpdateCheckState() const; - inline static const std::filesystem::path DOWNLOAD_DIR_ROOT = - std::filesystem::temp_directory_path() / "TF2 Bot Detector" / "Portable Updates"; + const std::filesystem::path DOWNLOAD_DIR_ROOT = + IFilesystem::Get().GetTempDir() / "Portable Updates"; void CleanupOldUpdates() const; static std::future> DownloadBuild(const HTTPClient& client, - BuildInfo::BuildVariant tool, BuildInfo::BuildVariant updater); + BuildInfo::BuildVariant tool, BuildInfo::BuildVariant updater, std::filesystem::path downloadDirRoot); static std::future> DownloadUpdateTool(const HTTPClient& client, - BuildInfo::BuildVariant updater, std::string args); + BuildInfo::BuildVariant updater, std::string args, std::filesystem::path downloadDirRoot); static std::future> RunUpdateTool(std::filesystem::path path, std::string args); bool m_IsUpdateQueued = true; @@ -400,7 +401,7 @@ namespace DownloadUpdateTool(*client, downloadedBuild->m_UpdaterVariant, mh::format("--update-type Portable --source-path {} --dest-path {}", - downloadedBuild->m_ExtractedLocation, Platform::GetCurrentExeDir()))); + downloadedBuild->m_ExtractedLocation, Platform::GetCurrentExeDir()), DOWNLOAD_DIR_ROOT)); }(); } else if (auto installUpdateResult = std::get_if(&m_State.GetVariant())) @@ -437,7 +438,7 @@ namespace m_State.Set(MH_SOURCE_LOCATION_CURRENT(), UpdateStatus::UpdateToolDownloading, "Platform app updater unavailable. Downloading update tool...", - DownloadUpdateTool(*client, *availableUpdate->m_Updater, needsUpdateTool->m_UpdateToolArgs)); + DownloadUpdateTool(*client, *availableUpdate->m_Updater, needsUpdateTool->m_UpdateToolArgs, DOWNLOAD_DIR_ROOT)); }(); } else if (auto downloadedUpdateTool = std::get_if(&m_State.GetVariant())) @@ -598,10 +599,10 @@ namespace } auto UpdateManager::DownloadBuild(const HTTPClient& client, - BuildInfo::BuildVariant tool, BuildInfo::BuildVariant updater) -> + BuildInfo::BuildVariant tool, BuildInfo::BuildVariant updater, std::filesystem::path downloadDirRoot) -> std::future> { - const auto downloadDir = DOWNLOAD_DIR_ROOT / mh::format("tool_{}", + const auto downloadDir = downloadDirRoot / mh::format("tool_{}", std::chrono::high_resolution_clock::now().time_since_epoch().count()); auto clientPtr = client.shared_from_this(); @@ -614,13 +615,13 @@ namespace } auto UpdateManager::DownloadUpdateTool(const HTTPClient& client, BuildInfo::BuildVariant updater, - std::string args) -> std::future> + std::string args, std::filesystem::path downloadDirRoot) -> std::future> { auto clientPtr = client.shared_from_this(); - return std::async([clientPtr, updater, args]() -> std::optional + return std::async([clientPtr, updater, args, downloadDirRoot]() -> std::optional { - const auto downloadDir = DOWNLOAD_DIR_ROOT / mh::format("updater_{}", + const auto downloadDir = downloadDirRoot / mh::format("updater_{}", std::chrono::high_resolution_clock::now().time_since_epoch().count()); DownloadAndExtractZip(*clientPtr, updater.m_DownloadURL, downloadDir); @@ -757,7 +758,7 @@ namespace auto portable = m_Portable.value(); auto updater = m_Updater.value(); - auto downloadBuildFuture = DownloadBuild(*client, portable, updater); + auto downloadBuildFuture = DownloadBuild(*client, portable, updater, m_Parent.DOWNLOAD_DIR_ROOT); m_Parent.m_State.Set(MH_SOURCE_LOCATION_CURRENT(), UpdateStatus::Downloading, "Downloading new build...", std::move(downloadBuildFuture)); diff --git a/tf2_bot_detector_updater/CMakeLists.txt b/tf2_bot_detector_updater/CMakeLists.txt index a0b0b61a..8a3a27b0 100644 --- a/tf2_bot_detector_updater/CMakeLists.txt +++ b/tf2_bot_detector_updater/CMakeLists.txt @@ -26,20 +26,8 @@ if (WIN32) "Update_MSIX.h" ) - execute_process( - COMMAND_ECHO STDOUT - RESULT_VARIABLE EXECUTE_PROCESS_RESULT - COMMAND ${CMAKE_SOURCE_DIR}/setup_winrt.bat - ) - - if (NOT EXECUTE_PROCESS_RESULT EQUAL 0) - message(FATAL_ERROR "Failed to run setup_winrt.bat (exit code ${EXECUTE_PROCESS_RESULT})") - endif() - - file(READ "${CMAKE_CURRENT_BINARY_DIR}/nuget/winrt_include_dir.txt" WINRT_INCLUDE_DIR) - string(STRIP "${WINRT_INCLUDE_DIR}" WINRT_INCLUDE_DIR) - message("WINRT_INCLUDE_DIR = ${WINRT_INCLUDE_DIR}") - target_include_directories(tf2_bot_detector_updater SYSTEM BEFORE PRIVATE ${WINRT_INCLUDE_DIR}) + add_subdirectory(../tf2_bot_detector_winrt "${PROJECT_BINARY_DIR}/tf2_bot_detector_winrt") + INCLUDE_TF2BD_WINRT(tf2_bot_detector_updater) endif() target_compile_features(tf2_bot_detector_updater PUBLIC cxx_std_20) diff --git a/tf2_bot_detector_updater/Update_MSIX.cpp b/tf2_bot_detector_updater/Update_MSIX.cpp index cb545190..a52b9cad 100644 --- a/tf2_bot_detector_updater/Update_MSIX.cpp +++ b/tf2_bot_detector_updater/Update_MSIX.cpp @@ -30,7 +30,7 @@ int tf2_bot_detector::Updater::Update_MSIX() try const auto mainBundleURL = mh::change_encoding(s_CmdLineArgs.m_SourcePath); - std::wcerr << "Main bundle URL: " << mainBundleURL; + std::wcerr << "Main bundle URL: " << mainBundleURL << std::endl; const Uri uri = Uri(winrt::param::hstring(mainBundleURL)); IVector deps{ winrt::single_threaded_vector() }; diff --git a/tf2_bot_detector_winrt/CMakeLists.txt b/tf2_bot_detector_winrt/CMakeLists.txt new file mode 100644 index 00000000..a721b27f --- /dev/null +++ b/tf2_bot_detector_winrt/CMakeLists.txt @@ -0,0 +1,72 @@ +cmake_minimum_required(VERSION 3.17) + +include(../cmake/init-preproject.cmake) + project(tf2_bot_detector_winrt) +include(../cmake/init-postproject.cmake) + +include(GenerateExportHeader) + +if (VCPKG_LIBRARY_LINKAGE MATCHES static) + set(TF2BD_WINRT_LIBRARY_LINKAGE STATIC) +else() + set(TF2BD_WINRT_LIBRARY_LINKAGE MODULE) +endif() + +message("TF2BD_WINRT_LIBRARY_LINKAGE = ${TF2BD_WINRT_LIBRARY_LINKAGE}") +add_library(tf2_bot_detector_winrt ${TF2BD_WINRT_LIBRARY_LINKAGE} + "tf2_bot_detector_winrt.cpp" + "tf2_bot_detector_winrt.h" +) + +generate_export_header(tf2_bot_detector_winrt + EXPORT_FILE_NAME "include/tf2_bot_detector_winrt_export.h" +) +target_include_directories(tf2_bot_detector_winrt PUBLIC "${CMAKE_CURRENT_BINARY_DIR}/include/") + +execute_process( + COMMAND_ECHO STDOUT + RESULT_VARIABLE EXECUTE_PROCESS_RESULT + COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/setup_winrt.bat + WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" +) + +if (NOT EXECUTE_PROCESS_RESULT EQUAL 0) + message(FATAL_ERROR "Failed to run setup_winrt.bat (exit code ${EXECUTE_PROCESS_RESULT})") +endif() + +file(READ "${CMAKE_CURRENT_BINARY_DIR}/nuget/winrt_include_dir.txt" WINRT_INCLUDE_DIR) +string(STRIP "${WINRT_INCLUDE_DIR}" WINRT_INCLUDE_DIR) +target_include_directories(tf2_bot_detector_winrt SYSTEM BEFORE PUBLIC ${WINRT_INCLUDE_DIR}) + +target_include_directories(tf2_bot_detector_winrt PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) + +find_package(fmt CONFIG REQUIRED) + +target_link_libraries(tf2_bot_detector_winrt PUBLIC + tf2_bot_detector::common + mh::stuff + fmt::fmt +) + +function(Include_TF2BD_WinRT _target) + + # Add include directories + get_target_property(TF2BD_WINRT_INCLUDE_DIRECTORIES tf2_bot_detector_winrt INCLUDE_DIRECTORIES) + target_include_directories(${_target} PUBLIC ${TF2BD_WINRT_INCLUDE_DIRECTORIES}) + + # Copy DLL + add_custom_command( + TARGET ${_target} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different $ $ + ) + + get_target_property(TF2BD_WINRT_TARGET_TYPE tf2_bot_detector_winrt TYPE) + if (NOT TF2BD_WINRT_TARGET_TYPE STREQUAL "STATIC_LIBRARY") + # Copy PDB + add_custom_command( + TARGET ${_target} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different $ $ + ) + endif() + +endfunction() diff --git a/tf2_bot_detector_winrt/empty.cpp b/tf2_bot_detector_winrt/empty.cpp new file mode 100644 index 00000000..e69de29b diff --git a/tf2_bot_detector_updater/setup_winrt.bat b/tf2_bot_detector_winrt/setup_winrt.bat similarity index 100% rename from tf2_bot_detector_updater/setup_winrt.bat rename to tf2_bot_detector_winrt/setup_winrt.bat diff --git a/tf2_bot_detector_winrt/tf2_bot_detector_winrt.cpp b/tf2_bot_detector_winrt/tf2_bot_detector_winrt.cpp new file mode 100644 index 00000000..8d1fc848 --- /dev/null +++ b/tf2_bot_detector_winrt/tf2_bot_detector_winrt.cpp @@ -0,0 +1,41 @@ +#include "tf2_bot_detector_winrt.h" +#include "tf2_bot_detector_winrt_export.h" + +#include + +namespace +{ + class WinRTImpl final : public tf2_bot_detector::WinRT + { + public: + std::filesystem::path GetLocalAppDataDir() const override; + std::filesystem::path GetRoamingAppDataDir() const override; + std::filesystem::path GetTempDir() const override; + }; + + std::filesystem::path WinRTImpl::GetLocalAppDataDir() const + { + auto appData = winrt::Windows::Storage::ApplicationData::Current(); + auto path = appData.LocalFolder().Path(); + return std::filesystem::path(path.begin(), path.end()); + } + + std::filesystem::path WinRTImpl::GetRoamingAppDataDir() const + { + auto appData = winrt::Windows::Storage::ApplicationData::Current(); + auto path = appData.RoamingFolder().Path(); + return std::filesystem::path(path.begin(), path.end()); + } + + std::filesystem::path WinRTImpl::GetTempDir() const + { + auto appData = winrt::Windows::Storage::ApplicationData::Current(); + auto path = appData.TemporaryFolder().Path(); + return std::filesystem::path(path.begin(), path.end()); + } +} + +extern "C" TF2_BOT_DETECTOR_WINRT_EXPORT tf2_bot_detector::WinRT* CreateWinRTInterface() +{ + return new WinRTImpl(); +} diff --git a/tf2_bot_detector_winrt/tf2_bot_detector_winrt.h b/tf2_bot_detector_winrt/tf2_bot_detector_winrt.h new file mode 100644 index 00000000..e53a7f4a --- /dev/null +++ b/tf2_bot_detector_winrt/tf2_bot_detector_winrt.h @@ -0,0 +1,18 @@ +#pragma once + +#include + +namespace tf2_bot_detector +{ + class WinRT + { + public: + virtual ~WinRT() = default; + + virtual std::filesystem::path GetLocalAppDataDir() const = 0; + virtual std::filesystem::path GetRoamingAppDataDir() const = 0; + virtual std::filesystem::path GetTempDir() const = 0; + }; + + using CreateWinRTInterfaceFn = WinRT*(*)(); +}