From 39008603cbab265a06b9f69e5612cdc662bf9b49 Mon Sep 17 00:00:00 2001 From: Matt Haynie Date: Fri, 8 Jan 2021 19:04:20 -0800 Subject: [PATCH 01/11] support usage of some winrt apis without breaking compatibility with windows 7/8 --- CMakeLists.txt | 5 + submodules/mh_stuff | 2 +- tf2_bot_detector/CMakeLists.txt | 4 + tf2_bot_detector/Filesystem.cpp | 15 +- tf2_bot_detector/Filesystem.h | 1 + tf2_bot_detector/Log.cpp | 29 +++ tf2_bot_detector/Log.h | 20 +- tf2_bot_detector/Networking/SteamAPI.cpp | 13 +- tf2_bot_detector/Platform/Platform.h | 1 + tf2_bot_detector/Platform/Windows/Windows.cpp | 187 ++++++++++++++---- tf2_bot_detector/UpdateManager.cpp | 3 +- tf2_bot_detector_updater/CMakeLists.txt | 16 +- tf2_bot_detector_updater/Update_MSIX.cpp | 2 +- tf2_bot_detector_winrt/CMakeLists.txt | 69 +++++++ tf2_bot_detector_winrt/empty.cpp | 0 .../setup_winrt.bat | 0 .../tf2_bot_detector_winrt.cpp | 41 ++++ .../tf2_bot_detector_winrt.h | 18 ++ 18 files changed, 353 insertions(+), 73 deletions(-) create mode 100644 tf2_bot_detector_winrt/CMakeLists.txt create mode 100644 tf2_bot_detector_winrt/empty.cpp rename {tf2_bot_detector_updater => tf2_bot_detector_winrt}/setup_winrt.bat (100%) create mode 100644 tf2_bot_detector_winrt/tf2_bot_detector_winrt.cpp create mode 100644 tf2_bot_detector_winrt/tf2_bot_detector_winrt.h 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/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/Filesystem.cpp b/tf2_bot_detector/Filesystem.cpp index 44988b45..9752a7ec 100644 --- a/tf2_bot_detector/Filesystem.cpp +++ b/tf2_bot_detector/Filesystem.cpp @@ -25,6 +25,7 @@ namespace std::filesystem::path GetMutableDataDir() const override; std::filesystem::path GetRealMutableDataDir() const override; + std::filesystem::path GetRealTempDataDir() const override; private: static constexpr char NON_PORTABLE_MARKER[] = ".non_portable"; @@ -47,7 +48,7 @@ IFilesystem& IFilesystem::Get() Filesystem::Filesystem() try { - DebugLog(MH_SOURCE_LOCATION_CURRENT(), "Initializing filesystem..."); + DebugLog("Initializing filesystem..."); m_SearchPaths.insert(m_SearchPaths.begin(), m_ExeDir); @@ -81,9 +82,9 @@ Filesystem::Filesystem() try DebugLog(std::move(initMsg)); } } -catch (const std::exception& e) +catch (...) { - LogFatalException(MH_SOURCE_LOCATION_CURRENT(), e, "Failed to initialize filesystem"); + LogFatalException("Failed to initialize filesystem"); } mh::generator Filesystem::GetSearchPaths() const @@ -191,3 +192,11 @@ std::filesystem::path Filesystem::GetRealMutableDataDir() const else return Platform::GetRealAppDataDir() / APPDATA_SUBFOLDER; } + +std::filesystem::path Filesystem::GetRealTempDataDir() const +{ + if (m_IsPortable) + return m_WorkingDir / "temp"; + else + return Platform::GetRealTempDataDir() / "TF2 Bot Detector"; +} diff --git a/tf2_bot_detector/Filesystem.h b/tf2_bot_detector/Filesystem.h index e658f07b..7f5a62e4 100644 --- a/tf2_bot_detector/Filesystem.h +++ b/tf2_bot_detector/Filesystem.h @@ -31,6 +31,7 @@ namespace tf2_bot_detector virtual std::filesystem::path GetMutableDataDir() const = 0; virtual std::filesystem::path GetRealMutableDataDir() const = 0; + virtual std::filesystem::path GetRealTempDataDir() const = 0; //virtual std::fstream OpenFile(const std::filesystem::path& path) = 0; virtual std::string ReadFile(std::filesystem::path path) const = 0; diff --git a/tf2_bot_detector/Log.cpp b/tf2_bot_detector/Log.cpp index f27cce20..f1da4569 100644 --- a/tf2_bot_detector/Log.cpp +++ b/tf2_bot_detector/Log.cpp @@ -360,3 +360,32 @@ 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..b229e919 100644 --- a/tf2_bot_detector/Log.h +++ b/tf2_bot_detector/Log.h @@ -155,22 +155,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..1fd2f6e3 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().GetRealTempDataDir() / "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..e9cd03ec 100644 --- a/tf2_bot_detector/Platform/Platform.h +++ b/tf2_bot_detector/Platform/Platform.h @@ -25,6 +25,7 @@ namespace tf2_bot_detector std::filesystem::path GetCurrentExeDir(); std::filesystem::path GetAppDataDir(); std::filesystem::path GetRealAppDataDir(); + std::filesystem::path GetRealTempDataDir(); bool IsDebuggerAttached(); diff --git a/tf2_bot_detector/Platform/Windows/Windows.cpp b/tf2_bot_detector/Platform/Windows/Windows.cpp index 8d945d28..55e462ef 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,17 +102,6 @@ namespace tf2_bot_detector } } -static std::filesystem::path GetKnownFolderPath(const KNOWNFOLDERID& id) -{ - PWSTR str; - CHECK_HR(SHGetKnownFolderPath(id, 0, nullptr, &str)); - - std::filesystem::path retVal(str); - - CoTaskMemFree(str); - return retVal; -} - std::filesystem::path tf2_bot_detector::Platform::GetRealAppDataDir() { const auto lad = GetKnownFolderPath(FOLDERID_LocalAppData); @@ -97,7 +114,107 @@ std::filesystem::path tf2_bot_detector::Platform::GetRealAppDataDir() return GetAppDataDir(); } +namespace +{ + 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() / "TF2 Bot Detector"; + } + }; +} + +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)); + + 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::GetCurrentExeDir() +{ + WCHAR path[32768]; + const auto length = GetModuleFileNameW(nullptr, path, (DWORD)std::size(path)); + + const auto error = GetLastError(); + if (error != ERROR_SUCCESS) + throw tf2_bot_detector::Windows::GetLastErrorException(E_FAIL, error, "Call to GetModuleFileNameW() failed"); + + 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::GetAppDataDir() { return GetKnownFolderPath(FOLDERID_RoamingAppData); } + +std::filesystem::path tf2_bot_detector::Platform::GetRealTempDataDir() +{ + return GetWinRTInterface()->GetTempDir(); +} diff --git a/tf2_bot_detector/UpdateManager.cpp b/tf2_bot_detector/UpdateManager.cpp index cb9192e7..52df3d82 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 @@ -245,7 +246,7 @@ namespace bool CanReplaceUpdateCheckState() const; inline static const std::filesystem::path DOWNLOAD_DIR_ROOT = - std::filesystem::temp_directory_path() / "TF2 Bot Detector" / "Portable Updates"; + IFilesystem::Get().GetRealTempDataDir() / "Portable Updates"; void CleanupOldUpdates() const; diff --git a/tf2_bot_detector_updater/CMakeLists.txt b/tf2_bot_detector_updater/CMakeLists.txt index a0b0b61a..9eecf36d 100644 --- a/tf2_bot_detector_updater/CMakeLists.txt +++ b/tf2_bot_detector_updater/CMakeLists.txt @@ -26,20 +26,10 @@ if (WIN32) "Update_MSIX.h" ) - execute_process( - COMMAND_ECHO STDOUT - RESULT_VARIABLE EXECUTE_PROCESS_RESULT - COMMAND ${CMAKE_SOURCE_DIR}/setup_winrt.bat + add_subdirectory(../tf2_bot_detector_winrt "${PROJECT_BINARY_DIR}/tf2_bot_detector_winrt") + target_link_libraries(tf2_bot_detector_updater PUBLIC + tf2_bot_detector::winrt ) - - 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}) 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..079a0ed3 --- /dev/null +++ b/tf2_bot_detector_winrt/CMakeLists.txt @@ -0,0 +1,69 @@ +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 tf2_bot_detector POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different $ $ + ) + + # Copy PDB + add_custom_command( + TARGET tf2_bot_detector POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different $ $ + ) + +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*(*)(); +} From ff1ca73277646dd5a2956d8295b916cb04ed3f4e Mon Sep 17 00:00:00 2001 From: Matt Haynie Date: Fri, 8 Jan 2021 20:15:53 -0800 Subject: [PATCH 02/11] bad workaround for badly designed system --- tf2_bot_detector/Filesystem.cpp | 32 ++++- tf2_bot_detector/Filesystem.h | 8 ++ tf2_bot_detector/Log.cpp | 110 +++++++++--------- tf2_bot_detector/Platform/Windows/Windows.cpp | 2 +- 4 files changed, 98 insertions(+), 54 deletions(-) diff --git a/tf2_bot_detector/Filesystem.cpp b/tf2_bot_detector/Filesystem.cpp index 9752a7ec..283948b7 100644 --- a/tf2_bot_detector/Filesystem.cpp +++ b/tf2_bot_detector/Filesystem.cpp @@ -38,14 +38,38 @@ namespace const std::filesystem::path m_AppDataDir = Platform::GetAppDataDir() / APPDATA_SUBFOLDER; //std::filesystem::path m_MutableDataDir = ChooseMutableDataPath(); }; + + static IFilesystem::InitStatus s_FSInitStatus = IFilesystem::InitStatus::Uninitialized; } IFilesystem& IFilesystem::Get() { - static Filesystem s_Filesystem; + static Filesystem s_Filesystem = []() -> Filesystem + { + struct Helper + { + Helper() + { + s_FSInitStatus = IFilesystem::InitStatus::Initializing; + } + ~Helper() + { + s_FSInitStatus = IFilesystem::InitStatus::Initialized; + } + + } helper; + + return Filesystem{}; + }(); + return s_Filesystem; } +IFilesystem::InitStatus IFilesystem::GetInitStatus() +{ + return s_FSInitStatus; +} + Filesystem::Filesystem() try { DebugLog("Initializing filesystem..."); @@ -81,6 +105,12 @@ Filesystem::Filesystem() try DebugLog(std::move(initMsg)); } + + DebugLog("\nMutableDataDir: {}", GetMutableDataDir()); + DebugLog("\nRealMutableDataDir: {}", GetRealMutableDataDir()); + + DebugLog("\nRealTempDataDir: {}", GetRealTempDataDir()); + std::filesystem::create_directories(GetRealTempDataDir()); } catch (...) { diff --git a/tf2_bot_detector/Filesystem.h b/tf2_bot_detector/Filesystem.h index 7f5a62e4..5f4e2c5d 100644 --- a/tf2_bot_detector/Filesystem.h +++ b/tf2_bot_detector/Filesystem.h @@ -25,6 +25,14 @@ namespace tf2_bot_detector static IFilesystem& Get(); + enum class InitStatus + { + Uninitialized, + Initializing, + Initialized, + }; + static InitStatus GetInitStatus(); + virtual mh::generator GetSearchPaths() const = 0; virtual std::filesystem::path ResolvePath(const std::filesystem::path& path, PathUsage usage) const = 0; diff --git a/tf2_bot_detector/Log.cpp b/tf2_bot_detector/Log.cpp index f1da4569..fef9d68a 100644 --- a/tf2_bot_detector/Log.cpp +++ b/tf2_bot_detector/Log.cpp @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -40,8 +41,6 @@ namespace LogManager(const LogManager&) = delete; LogManager& operator=(const LogManager&) = delete; - void Init(); - void Log(std::string msg, const LogMessageColor& color, LogSeverity severity, LogVisibility visibility = LogVisibility::Default, time_point_t timestamp = tfbd_clock_t::now()) override; void LogToStream(std::string msg, std::ostream& output, time_point_t timestamp = tfbd_clock_t::now(), bool skipScrub = false) const; @@ -58,7 +57,11 @@ namespace void AddSecret(std::string value, std::string replace) override; + bool TryInit(); + private: + std::atomic_bool m_InitOnceFlag; + std::filesystem::path m_FileName; std::ofstream m_File; mutable std::recursive_mutex m_LogMutex; @@ -81,18 +84,8 @@ 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; + s_LogState.TryInit(); return s_LogState; } } @@ -110,55 +103,68 @@ LogManager::LogManager() { } -void LogManager::Init() +bool LogManager::TryInit() { - ::DebugLog(MH_SOURCE_LOCATION_CURRENT()); - std::lock_guard lock(m_LogMutex); - - const auto t = ToTM(tfbd_clock_t::now()); - const mh::fmtstr<128> timestampStr("{}", std::put_time(&t, "%Y-%m-%d_%H-%M-%S")); + if (IFilesystem::GetInitStatus() != IFilesystem::InitStatus::Initialized) + return false; - // Pick file name + if (bool expected = false; + m_InitOnceFlag.compare_exchange_strong(expected, true)) { - std::filesystem::path logPath = IFilesystem::Get().ResolvePath("logs", PathUsage::Write); + ::DebugLog(MH_SOURCE_LOCATION_CURRENT()); + 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().ResolvePath("logs", PathUsage::Write); + + 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 + { - 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); + } } + + return true; + } + else + { + return false; } } diff --git a/tf2_bot_detector/Platform/Windows/Windows.cpp b/tf2_bot_detector/Platform/Windows/Windows.cpp index 55e462ef..7b651bc1 100644 --- a/tf2_bot_detector/Platform/Windows/Windows.cpp +++ b/tf2_bot_detector/Platform/Windows/Windows.cpp @@ -129,7 +129,7 @@ namespace } std::filesystem::path GetTempDir() const override { - return std::filesystem::temp_directory_path() / "TF2 Bot Detector"; + return std::filesystem::temp_directory_path(); } }; } From 281c201fff68e6fae51ae6169e4764186081943b Mon Sep 17 00:00:00 2001 From: Matt Haynie Date: Sat, 9 Jan 2021 09:23:21 -0800 Subject: [PATCH 03/11] Ignore the new temp folder. --- .gitignore | 1 + 1 file changed, 1 insertion(+) 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/ From 925ec75841b58701c5ec994f5c80f487ca72074b Mon Sep 17 00:00:00 2001 From: Matt Haynie Date: Sat, 9 Jan 2021 09:33:25 -0800 Subject: [PATCH 04/11] Fixed PDBs not being generated in release builds for modules --- cmake/init-postproject.cmake | 1 + 1 file changed, 1 insertion(+) 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") From 3a1590a6e80af50e9a076d38d55c2d55e62a03b6 Mon Sep 17 00:00:00 2001 From: Matt Haynie Date: Sat, 9 Jan 2021 09:52:55 -0800 Subject: [PATCH 05/11] Fixed some issues with "linking" with tf2_bot_detector_winrt --- tf2_bot_detector_updater/CMakeLists.txt | 4 +--- tf2_bot_detector_winrt/CMakeLists.txt | 15 +++++++++------ 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/tf2_bot_detector_updater/CMakeLists.txt b/tf2_bot_detector_updater/CMakeLists.txt index 9eecf36d..8a3a27b0 100644 --- a/tf2_bot_detector_updater/CMakeLists.txt +++ b/tf2_bot_detector_updater/CMakeLists.txt @@ -27,9 +27,7 @@ if (WIN32) ) add_subdirectory(../tf2_bot_detector_winrt "${PROJECT_BINARY_DIR}/tf2_bot_detector_winrt") - target_link_libraries(tf2_bot_detector_updater PUBLIC - 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_winrt/CMakeLists.txt b/tf2_bot_detector_winrt/CMakeLists.txt index 079a0ed3..a721b27f 100644 --- a/tf2_bot_detector_winrt/CMakeLists.txt +++ b/tf2_bot_detector_winrt/CMakeLists.txt @@ -56,14 +56,17 @@ function(Include_TF2BD_WinRT _target) # Copy DLL add_custom_command( - TARGET tf2_bot_detector POST_BUILD + TARGET ${_target} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different $ $ ) - # Copy PDB - add_custom_command( - TARGET tf2_bot_detector 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() From b1c04cde46bb975e673800130089ee739805097b Mon Sep 17 00:00:00 2001 From: Matt Haynie Date: Sat, 9 Jan 2021 10:32:31 -0800 Subject: [PATCH 06/11] well-defined initialization order for filesystem and log manager --- tf2_bot_detector/DLLMain.cpp | 4 + tf2_bot_detector/Filesystem.cpp | 126 +++++++++++++++-------------- tf2_bot_detector/Filesystem.h | 8 +- tf2_bot_detector/Log.cpp | 61 ++++++++------ tf2_bot_detector/Log.h | 2 + tf2_bot_detector/UpdateManager.cpp | 22 ++--- 6 files changed, 122 insertions(+), 101 deletions(-) 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 283948b7..049b55d2 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,7 +16,7 @@ namespace class Filesystem final : public IFilesystem { public: - Filesystem(); + void Init() override; mh::generator GetSearchPaths() const override; @@ -28,6 +29,9 @@ namespace std::filesystem::path GetRealTempDataDir() 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; @@ -38,83 +42,68 @@ namespace const std::filesystem::path m_AppDataDir = Platform::GetAppDataDir() / APPDATA_SUBFOLDER; //std::filesystem::path m_MutableDataDir = ChooseMutableDataPath(); }; - - static IFilesystem::InitStatus s_FSInitStatus = IFilesystem::InitStatus::Uninitialized; } IFilesystem& IFilesystem::Get() { - static Filesystem s_Filesystem = []() -> Filesystem - { - struct Helper - { - Helper() - { - s_FSInitStatus = IFilesystem::InitStatus::Initializing; - } - ~Helper() - { - s_FSInitStatus = IFilesystem::InitStatus::Initialized; - } - - } helper; - - return Filesystem{}; - }(); - + static Filesystem s_Filesystem; return s_Filesystem; } -IFilesystem::InitStatus IFilesystem::GetInitStatus() +void Filesystem::Init() { - return s_FSInitStatus; -} + assert(mh::is_main_thread()); -Filesystem::Filesystem() try -{ - DebugLog("Initializing filesystem..."); + if (!m_IsInit) + { + m_IsInit = true; + try + { + DebugLog("Initializing filesystem..."); - m_SearchPaths.insert(m_SearchPaths.begin(), m_ExeDir); + m_SearchPaths.insert(m_SearchPaths.begin(), m_ExeDir); - if (m_WorkingDir != m_ExeDir) - m_SearchPaths.insert(m_SearchPaths.begin(), m_WorkingDir); + if (m_WorkingDir != m_ExeDir) + m_SearchPaths.insert(m_SearchPaths.begin(), m_WorkingDir); - 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; + 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; - // 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 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); - std::filesystem::current_path(m_AppDataDir); - DebugLog("Set working directory to {}", m_AppDataDir); - } - else - { - DebugLog("Installation detected as portable."); - m_IsPortable = true; - } + std::filesystem::current_path(m_AppDataDir); + DebugLog("Set working directory to {}", m_AppDataDir); + } + else + { + DebugLog("Installation detected as portable."); + m_IsPortable = true; + } - { - std::string initMsg = "Filesystem initialized. Search paths:"; - for (const auto& searchPath : m_SearchPaths) - initMsg << "\n\t" << searchPath; + { + std::string initMsg = "Filesystem initialized. Search paths:"; + for (const auto& searchPath : m_SearchPaths) + initMsg << "\n\t" << searchPath; - DebugLog(std::move(initMsg)); - } + DebugLog(std::move(initMsg)); + } - DebugLog("\nMutableDataDir: {}", GetMutableDataDir()); - DebugLog("\nRealMutableDataDir: {}", GetRealMutableDataDir()); + DebugLog("\nMutableDataDir: {}", GetMutableDataDir()); + DebugLog("\nRealMutableDataDir: {}", GetRealMutableDataDir()); - DebugLog("\nRealTempDataDir: {}", GetRealTempDataDir()); - std::filesystem::create_directories(GetRealTempDataDir()); -} -catch (...) -{ - LogFatalException("Failed to initialize filesystem"); + DebugLog("\nRealTempDataDir: {}", GetRealTempDataDir()); + std::filesystem::create_directories(GetRealTempDataDir()); + } + catch (...) + { + LogFatalException("Failed to initialize filesystem"); + } + } } mh::generator Filesystem::GetSearchPaths() const @@ -128,6 +117,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) @@ -209,6 +200,8 @@ catch (...) std::filesystem::path Filesystem::GetMutableDataDir() const { + EnsureInit(); + if (m_IsPortable) return m_WorkingDir; else @@ -217,6 +210,8 @@ std::filesystem::path Filesystem::GetMutableDataDir() const std::filesystem::path Filesystem::GetRealMutableDataDir() const { + EnsureInit(); + if (m_IsPortable) return m_WorkingDir; else @@ -225,8 +220,19 @@ std::filesystem::path Filesystem::GetRealMutableDataDir() const std::filesystem::path Filesystem::GetRealTempDataDir() const { + EnsureInit(); + if (m_IsPortable) return m_WorkingDir / "temp"; else return Platform::GetRealTempDataDir() / "TF2 Bot Detector"; } + +void Filesystem::EnsureInit(const mh::source_location& location) const +{ + if (!m_IsInit) + { + assert(false); + LogFatalError(location, "Filesystem::EnsureInit failed"); + } +} diff --git a/tf2_bot_detector/Filesystem.h b/tf2_bot_detector/Filesystem.h index 5f4e2c5d..a7fa03d7 100644 --- a/tf2_bot_detector/Filesystem.h +++ b/tf2_bot_detector/Filesystem.h @@ -25,13 +25,7 @@ namespace tf2_bot_detector static IFilesystem& Get(); - enum class InitStatus - { - Uninitialized, - Initializing, - Initialized, - }; - static InitStatus GetInitStatus(); + virtual void Init() = 0; virtual mh::generator GetSearchPaths() const = 0; diff --git a/tf2_bot_detector/Log.cpp b/tf2_bot_detector/Log.cpp index fef9d68a..2c05894b 100644 --- a/tf2_bot_detector/Log.cpp +++ b/tf2_bot_detector/Log.cpp @@ -37,9 +37,7 @@ namespace class LogManager final : public ILogManager { public: - LogManager(); - LogManager(const LogManager&) = delete; - LogManager& operator=(const LogManager&) = delete; + 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; @@ -57,10 +55,9 @@ namespace void AddSecret(std::string value, std::string replace) override; - bool TryInit(); - private: - std::atomic_bool m_InitOnceFlag; + bool m_IsInit = false; + void EnsureInit(MH_SOURCE_LOCATION_AUTO(location)) const; std::filesystem::path m_FileName; std::ofstream m_File; @@ -85,7 +82,6 @@ namespace static LogManager& GetLogState() { static LogManager s_LogState; - s_LogState.TryInit(); return s_LogState; } } @@ -99,19 +95,13 @@ 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() -{ -} - -bool LogManager::TryInit() +void LogManager::Init() { - if (IFilesystem::GetInitStatus() != IFilesystem::InitStatus::Initialized) - return false; + assert(!m_IsInit); - if (bool expected = false; - m_InitOnceFlag.compare_exchange_strong(expected, true)) + if (!m_IsInit) { - ::DebugLog(MH_SOURCE_LOCATION_CURRENT()); + ::DebugLog("Initializing LogManager..."); std::lock_guard lock(m_LogMutex); const auto t = ToTM(tfbd_clock_t::now()); @@ -139,7 +129,17 @@ bool LogManager::TryInit() { 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); + } } { @@ -160,11 +160,7 @@ bool LogManager::TryInit() } } - return true; - } - else - { - return false; + m_IsInit = true; } } @@ -192,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; @@ -311,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)); @@ -321,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; @@ -333,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(); @@ -340,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); @@ -362,6 +368,15 @@ 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) { diff --git a/tf2_bot_detector/Log.h b/tf2_bot_detector/Log.h index b229e919..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; diff --git a/tf2_bot_detector/UpdateManager.cpp b/tf2_bot_detector/UpdateManager.cpp index 52df3d82..f75ee3d7 100644 --- a/tf2_bot_detector/UpdateManager.cpp +++ b/tf2_bot_detector/UpdateManager.cpp @@ -245,15 +245,15 @@ namespace bool CanReplaceUpdateCheckState() const; - inline static const std::filesystem::path DOWNLOAD_DIR_ROOT = + const std::filesystem::path DOWNLOAD_DIR_ROOT = IFilesystem::Get().GetRealTempDataDir() / "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; @@ -401,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())) @@ -438,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())) @@ -599,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(); @@ -615,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); @@ -758,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)); From cc9faa4fa586ead4304a649ffac9e4d5182490d7 Mon Sep 17 00:00:00 2001 From: Matt Haynie Date: Sat, 9 Jan 2021 11:42:37 -0800 Subject: [PATCH 07/11] this is probably overengineered and a bad idea --- tf2_bot_detector/Config/ConfigHelpers.cpp | 6 +- tf2_bot_detector/Filesystem.cpp | 72 ++++++++++--------- tf2_bot_detector/Filesystem.h | 26 +++---- tf2_bot_detector/Log.cpp | 2 +- tf2_bot_detector/Networking/SteamAPI.cpp | 2 +- tf2_bot_detector/Platform/Platform.h | 6 +- tf2_bot_detector/Platform/Windows/Windows.cpp | 34 +++++---- tf2_bot_detector/UI/MainWindow.cpp | 12 ++-- tf2_bot_detector/UpdateManager.cpp | 2 +- 9 files changed, 87 insertions(+), 75 deletions(-) diff --git a/tf2_bot_detector/Config/ConfigHelpers.cpp b/tf2_bot_detector/Config/ConfigHelpers.cpp index 43e897c1..8996f5f3 100644 --- a/tf2_bot_detector/Config/ConfigHelpers.cpp +++ b/tf2_bot_detector/Config/ConfigHelpers.cpp @@ -29,7 +29,7 @@ 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"); + if (const auto cfg = IFilesystem::Get().GetRoamingAppDataDir() / std::filesystem::path("cfg"); std::filesystem::is_directory(cfg)) { try @@ -58,7 +58,7 @@ auto tf2_bot_detector::GetConfigFilePaths(const std::string_view& basename) -> C 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 +217,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/Filesystem.cpp b/tf2_bot_detector/Filesystem.cpp index 049b55d2..92b8956f 100644 --- a/tf2_bot_detector/Filesystem.cpp +++ b/tf2_bot_detector/Filesystem.cpp @@ -22,11 +22,11 @@ namespace 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 GetRealTempDataDir() const override; + std::filesystem::path GetLocalAppDataDir() const override; + std::filesystem::path GetRoamingAppDataDir() const override; + std::filesystem::path GetTempDir() const override; private: bool m_IsInit = false; @@ -37,9 +37,10 @@ namespace 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(); }; } @@ -61,6 +62,11 @@ void Filesystem::Init() { DebugLog("Initializing filesystem..."); + m_ExeDir = Platform::GetCurrentExeDir(); + m_WorkingDir = std::filesystem::current_path(); + m_LocalAppDataDir = Platform::GetRootLocalAppDataDir() / APPDATA_SUBFOLDER; + m_RoamingAppDataDir = Platform::GetRootRoamingAppDataDir() / APPDATA_SUBFOLDER; + m_SearchPaths.insert(m_SearchPaths.begin(), m_ExeDir); if (m_WorkingDir != m_ExeDir) @@ -69,15 +75,18 @@ void Filesystem::Init() 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_SearchPaths.insert(m_SearchPaths.begin(), m_RoamingAppDataDir); m_IsPortable = false; // 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 (std::filesystem::create_directories(m_RoamingAppDataDir)) + DebugLog("Created {}", m_RoamingAppDataDir); + + if (std::filesystem::create_directories(m_LocalAppDataDir)) + DebugLog("Created {}", m_LocalAppDataDir); - std::filesystem::current_path(m_AppDataDir); - DebugLog("Set working directory to {}", m_AppDataDir); + std::filesystem::current_path(m_LocalAppDataDir); + DebugLog("Set working directory to {}", m_LocalAppDataDir); } else { @@ -93,11 +102,10 @@ void Filesystem::Init() DebugLog(std::move(initMsg)); } - DebugLog("\nMutableDataDir: {}", GetMutableDataDir()); - DebugLog("\nRealMutableDataDir: {}", GetRealMutableDataDir()); - - DebugLog("\nRealTempDataDir: {}", GetRealTempDataDir()); - std::filesystem::create_directories(GetRealTempDataDir()); + DebugLog("\tLocalAppDataDir: {}", GetLocalAppDataDir()); + DebugLog("\tRoamingAppDataDir: {}", GetRoamingAppDataDir()); + DebugLog("\tTempDir: {}", GetTempDir()); + std::filesystem::create_directories(GetTempDir()); } catch (...) { @@ -136,9 +144,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 { @@ -177,9 +189,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)) @@ -198,34 +210,28 @@ catch (...) throw; } -std::filesystem::path Filesystem::GetMutableDataDir() const +std::filesystem::path Filesystem::GetLocalAppDataDir() const { EnsureInit(); - if (m_IsPortable) - return m_WorkingDir; - else - return m_AppDataDir; + return m_IsPortable ? m_WorkingDir : m_LocalAppDataDir; } -std::filesystem::path Filesystem::GetRealMutableDataDir() const +std::filesystem::path Filesystem::GetRoamingAppDataDir() const { EnsureInit(); - if (m_IsPortable) - return m_WorkingDir; - else - return Platform::GetRealAppDataDir() / APPDATA_SUBFOLDER; + return m_IsPortable ? m_WorkingDir : m_RoamingAppDataDir; } -std::filesystem::path Filesystem::GetRealTempDataDir() const +std::filesystem::path Filesystem::GetTempDir() const { EnsureInit(); if (m_IsPortable) return m_WorkingDir / "temp"; else - return Platform::GetRealTempDataDir() / "TF2 Bot Detector"; + return Platform::GetRootTempDataDir() / "TF2 Bot Detector"; } void Filesystem::EnsureInit(const mh::source_location& location) const diff --git a/tf2_bot_detector/Filesystem.h b/tf2_bot_detector/Filesystem.h index a7fa03d7..e063f4d4 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 @@ -31,32 +34,30 @@ namespace tf2_bot_detector 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 GetRealTempDataDir() 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; - 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 { @@ -67,5 +68,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 2c05894b..f852b1b8 100644 --- a/tf2_bot_detector/Log.cpp +++ b/tf2_bot_detector/Log.cpp @@ -109,7 +109,7 @@ void LogManager::Init() // Pick file name { - std::filesystem::path logPath = IFilesystem::Get().ResolvePath("logs", PathUsage::Write); + std::filesystem::path logPath = IFilesystem::Get().GetLogsDir(); std::error_code ec; std::filesystem::create_directories(logPath, ec); diff --git a/tf2_bot_detector/Networking/SteamAPI.cpp b/tf2_bot_detector/Networking/SteamAPI.cpp index 1fd2f6e3..795c59dc 100644 --- a/tf2_bot_detector/Networking/SteamAPI.cpp +++ b/tf2_bot_detector/Networking/SteamAPI.cpp @@ -52,7 +52,7 @@ namespace public: AvatarCacheManager() { - m_CacheDir = IFilesystem::Get().GetRealTempDataDir() / "Steam Avatar Cache"; + m_CacheDir = IFilesystem::Get().GetTempDir() / "Steam Avatar Cache"; std::filesystem::create_directories(m_CacheDir); DeleteOldFiles(m_CacheDir, 24h * 7); } diff --git a/tf2_bot_detector/Platform/Platform.h b/tf2_bot_detector/Platform/Platform.h index e9cd03ec..245fce59 100644 --- a/tf2_bot_detector/Platform/Platform.h +++ b/tf2_bot_detector/Platform/Platform.h @@ -23,9 +23,9 @@ namespace tf2_bot_detector SteamID GetCurrentActiveSteamID(); std::filesystem::path GetCurrentExeDir(); - std::filesystem::path GetAppDataDir(); - std::filesystem::path GetRealAppDataDir(); - std::filesystem::path GetRealTempDataDir(); + std::filesystem::path GetRootLocalAppDataDir(); + std::filesystem::path GetRootRoamingAppDataDir(); + 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 7b651bc1..cd81e7af 100644 --- a/tf2_bot_detector/Platform/Windows/Windows.cpp +++ b/tf2_bot_detector/Platform/Windows/Windows.cpp @@ -102,18 +102,6 @@ namespace tf2_bot_detector } } -std::filesystem::path tf2_bot_detector::Platform::GetRealAppDataDir() -{ - const auto lad = GetKnownFolderPath(FOLDERID_LocalAppData); - - const auto packageAppDataDir = lad / "Packages" / GetCurrentPackageFamilyName() / "LocalCache" / "Roaming"; - - if (std::filesystem::exists(packageAppDataDir)) - return packageAppDataDir; - else - return GetAppDataDir(); -} - namespace { class FallbackWinRTInterface final : public tf2_bot_detector::WinRT @@ -209,12 +197,28 @@ std::filesystem::path tf2_bot_detector::Platform::GetCurrentExeDir() return std::filesystem::path(path, path + length).remove_filename(); } -std::filesystem::path tf2_bot_detector::Platform::GetAppDataDir() +std::filesystem::path tf2_bot_detector::Platform::GetRootLocalAppDataDir() +{ +#if 0 + const auto lad = GetKnownFolderPath(FOLDERID_LocalAppData); + + const auto packageAppDataDir = lad / "Packages" / GetCurrentPackageFamilyName() / "LocalCache" / "Roaming"; + + if (std::filesystem::exists(packageAppDataDir)) + return packageAppDataDir; + else + return GetAppDataDir(); +#else + return GetWinRTInterface()->GetLocalAppDataDir(); +#endif +} + +std::filesystem::path tf2_bot_detector::Platform::GetRootRoamingAppDataDir() { - return GetKnownFolderPath(FOLDERID_RoamingAppData); + return GetWinRTInterface()->GetRoamingAppDataDir(); } -std::filesystem::path tf2_bot_detector::Platform::GetRealTempDataDir() +std::filesystem::path tf2_bot_detector::Platform::GetRootTempDataDir() { 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 f75ee3d7..96484b06 100644 --- a/tf2_bot_detector/UpdateManager.cpp +++ b/tf2_bot_detector/UpdateManager.cpp @@ -246,7 +246,7 @@ namespace bool CanReplaceUpdateCheckState() const; const std::filesystem::path DOWNLOAD_DIR_ROOT = - IFilesystem::Get().GetRealTempDataDir() / "Portable Updates"; + IFilesystem::Get().GetTempDir() / "Portable Updates"; void CleanupOldUpdates() const; From 3e8efa728921f774d784626118f52202fc05678a Mon Sep 17 00:00:00 2001 From: Matt Haynie Date: Sat, 9 Jan 2021 12:03:21 -0800 Subject: [PATCH 08/11] Add support for loading from the old, incorrect appdata folder --- tf2_bot_detector/Filesystem.cpp | 6 ++++++ tf2_bot_detector/Platform/Platform.h | 1 + tf2_bot_detector/Platform/Windows/Windows.cpp | 18 ++++++++---------- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/tf2_bot_detector/Filesystem.cpp b/tf2_bot_detector/Filesystem.cpp index 92b8956f..1a3cee5c 100644 --- a/tf2_bot_detector/Filesystem.cpp +++ b/tf2_bot_detector/Filesystem.cpp @@ -94,6 +94,12 @@ void Filesystem::Init() m_IsPortable = true; } + if (auto legacyPath = Platform::GetLegacyAppDataDir(); std::filesystem::exists(legacyPath)) + { + Log("Found legacy appdata folder {}, adding to search paths...", legacyPath); + m_SearchPaths.push_back(legacyPath); + } + { std::string initMsg = "Filesystem initialized. Search paths:"; for (const auto& searchPath : m_SearchPaths) diff --git a/tf2_bot_detector/Platform/Platform.h b/tf2_bot_detector/Platform/Platform.h index 245fce59..ee940e51 100644 --- a/tf2_bot_detector/Platform/Platform.h +++ b/tf2_bot_detector/Platform/Platform.h @@ -25,6 +25,7 @@ namespace tf2_bot_detector std::filesystem::path GetCurrentExeDir(); 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 cd81e7af..0949ed3c 100644 --- a/tf2_bot_detector/Platform/Windows/Windows.cpp +++ b/tf2_bot_detector/Platform/Windows/Windows.cpp @@ -197,20 +197,18 @@ std::filesystem::path tf2_bot_detector::Platform::GetCurrentExeDir() return std::filesystem::path(path, path + length).remove_filename(); } -std::filesystem::path tf2_bot_detector::Platform::GetRootLocalAppDataDir() +std::filesystem::path tf2_bot_detector::Platform::GetLegacyAppDataDir() { -#if 0 - const auto lad = GetKnownFolderPath(FOLDERID_LocalAppData); + auto packageFamilyName = GetCurrentPackageFamilyName(); + if (packageFamilyName.empty()) + return {}; - const auto packageAppDataDir = lad / "Packages" / GetCurrentPackageFamilyName() / "LocalCache" / "Roaming"; + return GetKnownFolderPath(FOLDERID_LocalAppData) / "Packages" / packageFamilyName / "LocalCache" / "Roaming"; +} - if (std::filesystem::exists(packageAppDataDir)) - return packageAppDataDir; - else - return GetAppDataDir(); -#else +std::filesystem::path tf2_bot_detector::Platform::GetRootLocalAppDataDir() +{ return GetWinRTInterface()->GetLocalAppDataDir(); -#endif } std::filesystem::path tf2_bot_detector::Platform::GetRootRoamingAppDataDir() From 96de47593518e0eddd0e3055e75433941f42f8cc Mon Sep 17 00:00:00 2001 From: Matt Haynie Date: Sat, 9 Jan 2021 12:18:54 -0800 Subject: [PATCH 09/11] Fixed legacy search path. --- tf2_bot_detector/Filesystem.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tf2_bot_detector/Filesystem.cpp b/tf2_bot_detector/Filesystem.cpp index 1a3cee5c..ccc2a689 100644 --- a/tf2_bot_detector/Filesystem.cpp +++ b/tf2_bot_detector/Filesystem.cpp @@ -96,8 +96,12 @@ void Filesystem::Init() if (auto legacyPath = Platform::GetLegacyAppDataDir(); std::filesystem::exists(legacyPath)) { - Log("Found legacy appdata folder {}, adding to search paths...", legacyPath); - m_SearchPaths.push_back(legacyPath); + legacyPath /= APPDATA_SUBFOLDER; + if (std::filesystem::exists(legacyPath)) + { + Log("Found legacy appdata folder {}, adding to search paths...", legacyPath); + m_SearchPaths.push_back(legacyPath); + } } { From 3044ab809c321a44a4fe60a885a52b203c85e42e Mon Sep 17 00:00:00 2001 From: Matt Haynie Date: Sat, 9 Jan 2021 12:59:53 -0800 Subject: [PATCH 10/11] Fixed an issue that could cause config files to not be read from all search paths. --- tf2_bot_detector/Config/ConfigHelpers.cpp | 30 +++++++++++------------ tf2_bot_detector/Filesystem.cpp | 30 +++++++++++++++++++++++ tf2_bot_detector/Filesystem.h | 3 +++ 3 files changed, 47 insertions(+), 16 deletions(-) diff --git a/tf2_bot_detector/Config/ConfigHelpers.cpp b/tf2_bot_detector/Config/ConfigHelpers.cpp index 8996f5f3..64866641 100644 --- a/tf2_bot_detector/Config/ConfigHelpers.cpp +++ b/tf2_bot_detector/Config/ConfigHelpers.cpp @@ -29,29 +29,27 @@ 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().GetRoamingAppDataDir() / 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; } diff --git a/tf2_bot_detector/Filesystem.cpp b/tf2_bot_detector/Filesystem.cpp index ccc2a689..f0f7a7a0 100644 --- a/tf2_bot_detector/Filesystem.cpp +++ b/tf2_bot_detector/Filesystem.cpp @@ -28,6 +28,9 @@ namespace 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; @@ -252,3 +255,30 @@ void Filesystem::EnsureInit(const mh::source_location& location) const 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 IterateDirImpl(m_SearchPaths, std::move(path), options); +} diff --git a/tf2_bot_detector/Filesystem.h b/tf2_bot_detector/Filesystem.h index e063f4d4..4e50525e 100644 --- a/tf2_bot_detector/Filesystem.h +++ b/tf2_bot_detector/Filesystem.h @@ -42,6 +42,9 @@ namespace tf2_bot_detector virtual std::string ReadFile(std::filesystem::path path) 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, PathUsage usage) const { return WriteFile(path, data.data(), data.data() + data.size(), usage); From b9e577ffbaf18c0826fbb29fd95f9129f6a43161 Mon Sep 17 00:00:00 2001 From: Matt Haynie Date: Sat, 9 Jan 2021 13:30:02 -0800 Subject: [PATCH 11/11] Instead of adding the legacy path to the search directory, just copy everything to the new folder and delete the old one. --- tf2_bot_detector/Filesystem.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tf2_bot_detector/Filesystem.cpp b/tf2_bot_detector/Filesystem.cpp index f0f7a7a0..0ef38281 100644 --- a/tf2_bot_detector/Filesystem.cpp +++ b/tf2_bot_detector/Filesystem.cpp @@ -102,8 +102,12 @@ void Filesystem::Init() legacyPath /= APPDATA_SUBFOLDER; if (std::filesystem::exists(legacyPath)) { - Log("Found legacy appdata folder {}, adding to search paths...", legacyPath); - m_SearchPaths.push_back(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); } }