# HG changeset patch # User Paper # Date 1700033699 18000 # Node ID 8700806c2cc29a79a76d117ba866fe948c835c9d # Parent 54744a48a7d7854bac202844c32a2336f81b7227 dep/animia: awesome new breaking changes! I'm so tired diff -r 54744a48a7d7 -r 8700806c2cc2 dep/animia/CMakeLists.txt --- a/dep/animia/CMakeLists.txt Tue Nov 14 16:31:21 2023 -0500 +++ b/dep/animia/CMakeLists.txt Wed Nov 15 02:34:59 2023 -0500 @@ -7,6 +7,7 @@ src/util.cc src/strategist.cc src/fd.cc + src/win.cc ) if(LINUX) @@ -26,6 +27,15 @@ ) endif() +if(WIN32) + list(APPEND SRC_FILES + src/win/win32.cc + src/util/win32.cc + ) +else() + # soon x11 and apple stuff will be here... +endif() + add_library(animia SHARED ${SRC_FILES}) set_target_properties(animia PROPERTIES PUBLIC_HEADER include/animia.h @@ -37,6 +47,9 @@ elseif(LINUX) target_compile_definitions(animia PUBLIC LINUX) elseif(UNIX) + if(APPLE) + target_compile_definitions(animia PUBLIC MACOSX) + endif() target_compile_definitions(animia PUBLIC UNIX) endif() diff -r 54744a48a7d7 -r 8700806c2cc2 dep/animia/README.md --- a/dep/animia/README.md Tue Nov 14 16:31:21 2023 -0500 +++ b/dep/animia/README.md Wed Nov 15 02:34:59 2023 -0500 @@ -1,4 +1,4 @@ # Animia -Animia is a work-in-progress cross-platform clone of Anisthesia and part of Minori. +Animia is a work-in-progress cross-platform hard fork of Anisthesia and part of Minori. -Most (if not all) Anisthesia configs should also work in this library as well. +Most (if not all) Anisthesia configs should also work in this library as well (at least on Windows). diff -r 54744a48a7d7 -r 8700806c2cc2 dep/animia/include/animia.h --- a/dep/animia/include/animia.h Tue Nov 14 16:31:21 2023 -0500 +++ b/dep/animia/include/animia.h Wed Nov 15 02:34:59 2023 -0500 @@ -7,6 +7,11 @@ namespace animia { +enum class ResultType { + Process, + Window +}; + struct Process { internal::pid_t pid = 0; std::string name; @@ -19,14 +24,15 @@ }; struct Result { + ResultType type; Player player; - Process process; + Process process; // unused when using window_title. it's dumb, blame X11 Window window; // unused with file descriptors std::vector media; }; bool GetResults(const std::vector& players, std::vector& results); -} // namespace Animia +} // namespace animia #endif // __animia__animia_h diff -r 54744a48a7d7 -r 8700806c2cc2 dep/animia/include/animia/fd.h --- a/dep/animia/include/animia/fd.h Tue Nov 14 16:31:21 2023 -0500 +++ b/dep/animia/include/animia/fd.h Wed Nov 15 02:34:59 2023 -0500 @@ -3,28 +3,35 @@ #include #include -#include -#include -#include +#include #include "animia/types.h" -namespace animia::internal { +namespace animia { + +struct Process; + +namespace internal { struct OpenFile { pid_t pid = 0; std::string path; }; +using process_proc_t = std::function; + +using open_file_proc_t = std::function; + class BaseFdTools { public: - virtual bool GetAllPids(std::set& pids) { return false; } - virtual bool GetProcessName(pid_t pid, std::string& result) { return false; } - virtual bool EnumerateOpenFiles(const std::set& pids, std::vector& files) { return false; } + virtual bool EnumerateOpenProcesses(process_proc_t process_proc) { return false; } + virtual bool EnumerateOpenFiles(const std::set& pids, open_file_proc_t open_file_proc) { return false; } }; extern BaseFdTools& fd; // defined in fd.cc -} +} // namespace internal + +} // namespace animia #endif // __animia__animia__fd_h diff -r 54744a48a7d7 -r 8700806c2cc2 dep/animia/include/animia/fd/bsd.h --- a/dep/animia/include/animia/fd/bsd.h Tue Nov 14 16:31:21 2023 -0500 +++ b/dep/animia/include/animia/fd/bsd.h Wed Nov 15 02:34:59 2023 -0500 @@ -1,8 +1,6 @@ #ifndef __animia__animia__fd__linux_h #define __animia__animia__fd__linux_h -#include -#include #include #include @@ -17,9 +15,8 @@ class UnixFdTools final : public BaseFdTools { public: - bool GetAllPids(std::set& pids) override; - bool GetProcessName(pid_t pid, std::string& result) override; - bool EnumerateOpenFiles(const std::set& pids, std::vector& files) override; + bool EnumerateOpenProcesses(process_proc_t process_proc) override; + bool EnumerateOpenFiles(const std::set& pids, open_file_proc_t open_file_proc) override; }; } diff -r 54744a48a7d7 -r 8700806c2cc2 dep/animia/include/animia/fd/linux.h --- a/dep/animia/include/animia/fd/linux.h Tue Nov 14 16:31:21 2023 -0500 +++ b/dep/animia/include/animia/fd/linux.h Wed Nov 15 02:34:59 2023 -0500 @@ -1,8 +1,6 @@ #ifndef __animia__animia__fd__linux_h #define __animia__animia__fd__linux_h -#include -#include #include #include @@ -18,9 +16,8 @@ class LinuxFdTools final : public BaseFdTools { public: - bool GetAllPids(std::set& pids) override; - bool GetProcessName(pid_t pid, std::string& result) override; - bool EnumerateOpenFiles(const std::set& pids, std::vector& files) override; + bool EnumerateOpenProcesses(process_proc_t process_proc) override; + bool EnumerateOpenFiles(const std::set& pids, open_file_proc_t open_file_proc) override; }; } diff -r 54744a48a7d7 -r 8700806c2cc2 dep/animia/include/animia/fd/win32.h --- a/dep/animia/include/animia/fd/win32.h Tue Nov 14 16:31:21 2023 -0500 +++ b/dep/animia/include/animia/fd/win32.h Wed Nov 15 02:34:59 2023 -0500 @@ -1,11 +1,8 @@ #ifndef __animia__animia__fd__win32_h #define __animia__animia__fd__win32_h -#include -#include #include #include -#include #include @@ -14,18 +11,10 @@ namespace animia::internal::win32 { -struct HandleDeconstructor { - using pointer = HANDLE; - void operator()(pointer t) const { ::CloseHandle(t); }; -}; - -using Handle = std::unique_ptr; - class Win32FdTools final : public BaseFdTools { public: - bool GetAllPids(std::set& pids) override; - bool GetProcessName(pid_t pid, std::string& result) override; - bool EnumerateOpenFiles(const std::set& pids, std::vector& files) override; + bool EnumerateOpenProcesses(process_proc_t process_proc) override; + bool EnumerateOpenFiles(const std::set& pids, open_file_proc_t open_file_proc) override; }; } diff -r 54744a48a7d7 -r 8700806c2cc2 dep/animia/include/animia/os.h --- a/dep/animia/include/animia/os.h Tue Nov 14 16:31:21 2023 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,15 +0,0 @@ -#ifndef __animia__animia__os_h -#define __animia__animia__os_h - -#ifdef __linux__ -# define ANIMIA_ON_LINUX -#elif (defined(unix) || defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) -# if (defined(__APPLE__) && defined(__MACH__)) -# define ANIMIA_ON_OSX -# endif -# define ANIMIA_ON_UNIX -#elif defined(_WIN32) -# define ANIMIA_ON_WIN32 -#endif - -#endif // __animia__animia__os_h diff -r 54744a48a7d7 -r 8700806c2cc2 dep/animia/include/animia/util.h --- a/dep/animia/include/animia/util.h Tue Nov 14 16:31:21 2023 -0500 +++ b/dep/animia/include/animia/util.h Wed Nov 15 02:34:59 2023 -0500 @@ -7,6 +7,8 @@ bool ReadFile(const std::string& path, std::string& data); bool EqualStrings(const std::string& str1, const std::string& str2); +bool Stem(const std::string& filename, std::string& stem); +bool CheckPattern(const std::string& pattern, const std::string& str); bool TrimLeft(std::string& str, const char* chars); bool TrimRight(std::string& str, const char* chars); diff -r 54744a48a7d7 -r 8700806c2cc2 dep/animia/include/animia/util/win32.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dep/animia/include/animia/util/win32.h Wed Nov 15 02:34:59 2023 -0500 @@ -0,0 +1,28 @@ +#include +#include + +#include +#include + +namespace animia::internal::win32 { + +struct HandleDeconstructor { + using pointer = HANDLE; + void operator()(pointer t) const { ::CloseHandle(t); }; +}; + +using Handle = std::unique_ptr; + +/* ----------------------------------------------- */ + +std::string ToUtf8String(const std::wstring& string); +std::string ToUtf8String(const UNICODE_STRING& string); +std::wstring ToWstring(const std::string& string); + +std::wstring GetFileNameFromPath(const std::wstring& path); +std::wstring GetFileNameWithoutExtension(const std::wstring& filename); + +bool IsSystemDirectory(const std::string& path); +bool IsSystemDirectory(std::wstring path); + +} \ No newline at end of file diff -r 54744a48a7d7 -r 8700806c2cc2 dep/animia/include/animia/win.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dep/animia/include/animia/win.h Wed Nov 15 02:34:59 2023 -0500 @@ -0,0 +1,27 @@ +#ifndef __animia__animia__win_h +#define __animia__animia__win_h + +#include +#include + +namespace animia { + +struct Process; +struct Window; + +namespace internal { + +using window_proc_t = std::function; + +class BaseWinTools { + public: + virtual bool EnumerateWindows(window_proc_t window_proc) { return false; (void)window_proc; } +}; + +extern BaseWinTools& win; + +} // namespace internal + +} // namespace animia + +#endif // __animia__animia__win_h diff -r 54744a48a7d7 -r 8700806c2cc2 dep/animia/include/animia/win/win32.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dep/animia/include/animia/win/win32.h Wed Nov 15 02:34:59 2023 -0500 @@ -0,0 +1,15 @@ +#ifndef __animia__animia__fd__win32_h +#define __animia__animia__fd__win32_h + +#include "animia/win.h" + +namespace animia::internal::win32 { + +class Win32WinTools final : public BaseWinTools { + public: + bool EnumerateWindows(window_proc_t window_proc) override; +}; + +} + +#endif // __animia__animia__fd__win32_h diff -r 54744a48a7d7 -r 8700806c2cc2 dep/animia/src/animia.cc --- a/dep/animia/src/animia.cc Tue Nov 14 16:31:21 2023 -0500 +++ b/dep/animia/src/animia.cc Wed Nov 15 02:34:59 2023 -0500 @@ -3,51 +3,90 @@ #include #include "animia.h" +#include "animia/util.h" #include "animia/strategies.h" #include "animia/types.h" #include "animia/fd.h" +#include "animia/win.h" + +#include namespace animia { -static bool ProcessInPlayers(const std::vector& players, std::string name, Player& player_) { - for (const auto& player : players) { - for (const auto& exe : player.executables) { - /* this is only really relevant on Windows, and even then - executables can end in any number of extensions, so we should - really remove them all... */ - auto pos = name.rfind(".exe"); - if (pos != std::string::npos) - name = name.substr(0, pos); +namespace internal { + +static bool IsExecutableInList(const Player& player, const std::string& name) { + std::string stem; +#ifdef WIN32 + if (!util::Stem(name, stem)) +#endif + stem = name; + + for (const auto& pattern : player.executables) + if (util::CheckPattern(pattern, stem)) + return true; - if (exe == name) { - player_ = player; - return true; - } - } + return false; +} + +static bool IsWindowInList(const Player& player, const std::string& name) { + for (const auto& pattern : player.windows) + if (util::CheckPattern(pattern, name)) + return true; + + return false; +} + +static bool PlayerHasStrategy(const Player& player, const Strategy& strategy) { + for (const auto& pstrategy : player.strategies) { + if (pstrategy == strategy) + return true; } return false; } +} // namespace internal + bool GetResults(const std::vector& players, std::vector& results) { - std::set pids; + /* Start out with file descriptors. */ + auto process_proc = [&](const Process& process) -> bool { + for (const auto& player : players) { + if (!internal::PlayerHasStrategy(player, Strategy::OpenFiles)) + continue; - if (!internal::fd.GetAllPids(pids)) + if (!internal::IsExecutableInList(player, process.name)) + continue; + + results.push_back({ResultType::Process, player, process, {}, {}}); + break; + } + return true; + }; + + if (!internal::fd.EnumerateOpenProcesses(process_proc)) return false; - for (const auto& pid : pids) { - std::string name; - internal::fd.GetProcessName(pid, name); + /* Then add our cool windows. + Note: X11 is stupid and there's no reliable way to get a PID from a given window. + This is because some windows might not even have a process attached to them. + We should set the PID of the process if we can get it, but that'll be for when + I can actually be arsed to implement the X11 backend. */ + auto window_proc = [&](const Process& process, const Window& window) -> bool { + for (const auto& player : players) { + if (!internal::PlayerHasStrategy(player, Strategy::WindowTitle)) + continue; - Player player; - if (!ProcessInPlayers(players, name, player)) - continue; + if (!internal::IsWindowInList(player, window.class_name)) + continue; - Result result; - result.process.pid = pid; - result.process.name = name; - result.player = player; - results.push_back(result); - } + results.push_back({ResultType::Window, player, process, window, {}}); + break; + } + return true; + }; + + if (!internal::win.EnumerateWindows(window_proc)) + return false; return internal::ApplyStrategies(results); } diff -r 54744a48a7d7 -r 8700806c2cc2 dep/animia/src/fd.cc --- a/dep/animia/src/fd.cc Tue Nov 14 16:31:21 2023 -0500 +++ b/dep/animia/src/fd.cc Wed Nov 15 02:34:59 2023 -0500 @@ -1,23 +1,20 @@ -#include "animia/os.h" #include "animia/fd.h" -#ifdef ANIMIA_ON_WIN32 +#ifdef WIN32 # include "animia/fd/win32.h" -#elif defined(ANIMIA_ON_LINUX) +#elif defined(LINUX) # include "animia/fd/linux.h" -#elif defined(ANIMIA_ON_UNIX) +#elif defined(UNIX) # include "animia/fd/bsd.h" #endif namespace animia::internal { -/* really stupid hack to get fd to point to the right - thing */ -#ifdef ANIMIA_ON_WIN32 +#ifdef WIN32 win32::Win32FdTools os_fd; -#elif defined(ANIMIA_ON_LINUX) +#elif defined(LINUX) linux::LinuxFdTools os_fd; -#elif defined(ANIMIA_ON_UNIX) +#elif defined(UNIX) unix::UnixFdTools os_fd; #else BaseFdTools os_fd; diff -r 54744a48a7d7 -r 8700806c2cc2 dep/animia/src/fd/bsd.cc --- a/dep/animia/src/fd/bsd.cc Tue Nov 14 16:31:21 2023 -0500 +++ b/dep/animia/src/fd/bsd.cc Wed Nov 15 02:34:59 2023 -0500 @@ -1,13 +1,13 @@ -/** - * fd/bsd.cpp - * - this ONLY* supports OS X as of now - * (*there is some FreeBSD support code) - **/ +/* +** fd/bsd.cpp +** - this ONLY* supports OS X as of now +** (*there is some FreeBSD support code) +*/ #include "animia/fd/bsd.h" +#include "animia.h" #include #include -#include #include #include @@ -23,40 +23,7 @@ namespace animia::internal::unix { -/* this is a cleaned up version of a function from... Apple? - ...anyway, what it essentially does is gets the size and stuff from - sysctl() and reserves the space in a vector to store the PIDs - - TODO: https://kaashif.co.uk/2015/06/18/how-to-get-a-list-of-processes-on-openbsd-in-c/ */ -bool UnixFdTools::GetAllPids(std::set& pids) { - struct kinfo_proc* result = NULL; - size_t length = 0; - static const int name[] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0}; - - /* get appropriate length from sysctl() - note: the reason this isn't checked is actually because this will - *always* return an error on OS X (or... maybe I'm doing it wrong :) ) */ - sysctl((int*)name, (sizeof(name) / sizeof(*name)) - 1, NULL, &length, NULL, 0); - - result = (struct kinfo_proc*)malloc(length); - if (result == NULL) - return std::vector(); - - /* TODO: this might actually return ENOMEM if the amount of file handles changes between the - original sysctl() call and this one, which is technically possible */ - if (sysctl((int*)name, (sizeof(name) / sizeof(*name)) - 1, result, &length, NULL, 0) == ENOMEM) { - assert(result != NULL); - free(result); - throw std::bad_alloc(); - } - - /* add pids to our vector */ - pids.reserve(length / sizeof(*result)); - for (int i = 0; i < length / sizeof(*result); i++) - pids.insert(result[i].kp_proc.p_pid); -} - -bool UnixFdTools::GetProcessName(pid_t pid, std::string& result) { +static bool GetProcessName(pid_t pid, std::string& result) { #ifdef __FreeBSD__ struct kinfo_proc* proc = kinfo_getproc(pid); if (!proc) @@ -83,8 +50,45 @@ #endif } +/* this is a cleaned up version of a function from... Apple? + ...anyway, what it essentially does is gets the size and stuff from + sysctl() and reserves the space in a vector to store the PIDs + + TODO: https://kaashif.co.uk/2015/06/18/how-to-get-a-list-of-processes-on-openbsd-in-c/ */ +bool UnixFdTools::EnumerateOpenProcesses(process_proc_t process_proc) { + struct kinfo_proc* result = NULL; + size_t length = 0; + static const int name[] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0}; + + /* get appropriate length from sysctl() + note: the reason this isn't checked is actually because this will + *always* return an error on OS X (or... maybe I'm doing it wrong :) ) */ + sysctl((int*)name, (sizeof(name) / sizeof(*name)) - 1, NULL, &length, NULL, 0); + + result = (struct kinfo_proc*)malloc(length); + if (result == NULL) + return std::vector(); + + /* TODO: this might actually return ENOMEM if the amount of file handles changes between the + original sysctl() call and this one, which is technically possible */ + if (sysctl((int*)name, (sizeof(name) / sizeof(*name)) - 1, result, &length, NULL, 0) == ENOMEM) { + assert(result != NULL); + free(result); + throw std::bad_alloc(); + } + + for (int i = 0; i < length / sizeof(*result); i++) { + const pid_t pid = result[i].kp_proc.p_pid; + if (!process_proc({pid, GetProcessName(pid)})) + return false; + } +} + /* this only works on OS X :( */ -bool UnixFdTools::EnumerateOpenFiles(const std::set& pids, std::vector>& files) { +bool UnixFdTools::EnumerateOpenFiles(const std::set& pids, open_file_proc_t open_file_proc) { + if (!open_file_proc) + return false; + for (const auto& pid : pids) { int bufsz = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, NULL, 0); if (bufsz == -1) @@ -109,7 +113,8 @@ continue; */ - files.push_back({pid, vnodeInfo.pvip.vip_path}); + if (!open_file_proc({pid, vnodeInfo.pvip.vip_path})) + return false; } } } diff -r 54744a48a7d7 -r 8700806c2cc2 dep/animia/src/fd/linux.cc --- a/dep/animia/src/fd/linux.cc Tue Nov 14 16:31:21 2023 -0500 +++ b/dep/animia/src/fd/linux.cc Wed Nov 15 02:34:59 2023 -0500 @@ -1,10 +1,10 @@ #include "animia/util.h" #include "animia/fd/linux.h" +#include "animia.h" #include #include #include -#include #include #include #include @@ -99,7 +99,17 @@ return ret.c_str(); } -bool LinuxFdTools::GetAllPids(std::set& pids) { +static bool GetProcessName(pid_t pid, std::string& result) { + const std::string path = PROC_LOCATION "/" + std::to_string(pid) + "/comm"; + + if (!util::ReadFile(path, result)) + return false; + + result.erase(std::remove(result.begin(), result.end(), '\n'), result.end()); + return true; +} + +bool LinuxFdTools::EnumerateOpenProcesses(process_proc_t process_proc) { bool success = false; for (const auto& dir : GetAllFilesInDir(PROC_LOCATION)) { pid_t pid; @@ -109,22 +119,16 @@ } catch (std::invalid_argument) { continue; } - pids.insert(pid); + if (!process_proc({pid, GetProcessName(pid)})) + return false; } return success; } -bool LinuxFdTools::GetProcessName(pid_t pid, std::string& result) { - const std::string path = PROC_LOCATION "/" + std::to_string(pid) + "/comm"; - - if (!util::ReadFile(path, result)) +bool LinuxFdTools::EnumerateOpenFiles(const std::set& pids, open_file_proc_t open_file_proc) { + if (!open_file_proc) return false; - result.erase(std::remove(result.begin(), result.end(), '\n'), result.end()); - return true; -} - -bool LinuxFdTools::EnumerateOpenFiles(const std::set& pids, std::vector& files) { for (const auto& pid : pids) { const std::string path = PROC_LOCATION "/" + std::to_string(pid) + "/fd"; @@ -137,7 +141,8 @@ if (!IsRegularFile(name)) continue; - files.push_back({pid, name}); + if (!open_file_proc({pid, name})) + return false; } } return true; diff -r 54744a48a7d7 -r 8700806c2cc2 dep/animia/src/fd/win32.cc --- a/dep/animia/src/fd/win32.cc Tue Nov 14 16:31:21 2023 -0500 +++ b/dep/animia/src/fd/win32.cc Wed Nov 15 02:34:59 2023 -0500 @@ -1,9 +1,11 @@ -/** - * win32.cpp - * - provides support for Windows clients - * - **/ +/* +** win32.cpp +** - provides support for Windows clients +** +*/ #include "animia/fd/win32.h" +#include "animia/util/win32.h" +#include "animia.h" #include #include @@ -26,10 +28,15 @@ you to use *internal functions* that can't even be linked to, hence why we have to use GetProcAddress and such. What a mess. */ -#define SystemExtendedHandleInformation ((SYSTEM_INFORMATION_CLASS)0x40) +/* SystemExtendedHandleInformation is only available in NT 5.1+ (XP and higher) and provides information for + 32-bit PIDs, unlike SystemHandleInformation */ +constexpr SYSTEM_INFORMATION_CLASS SystemExtendedHandleInformation = static_cast(0x40); + +/* more constants not in winternl.h */ constexpr NTSTATUS STATUS_INFO_LENGTH_MISMATCH = 0xC0000004UL; -constexpr NTSTATUS STATUS_SUCCESS = 0x00000000UL; +/* this is filled in at runtime because it's not guaranteed to be (and isn't) + constant between different versions of Windows */ static unsigned short file_type_index = 0; struct SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX { @@ -77,19 +84,23 @@ std::vector res; /* we should really put a cap on this */ ULONG cb = 1 << 19; - NTSTATUS status = STATUS_SUCCESS; - SYSTEM_HANDLE_INFORMATION_EX* info; - do { + for (NTSTATUS status = STATUS_INFO_LENGTH_MISMATCH; status == STATUS_INFO_LENGTH_MISMATCH; ) { + /* why are we doing this? */ status = STATUS_NO_MEMORY; - if (!(info = (SYSTEM_HANDLE_INFORMATION_EX*)malloc(cb *= 2))) + SYSTEM_HANDLE_INFORMATION_EX* info = (SYSTEM_HANDLE_INFORMATION_EX*)malloc(cb *= 2); + if (!info) continue; res.reserve(cb); - if (0 <= (status = QuerySystemInformation(SystemExtendedHandleInformation, info, cb, &cb))) { - if (ULONG_PTR handles = info->NumberOfHandles) { + status = QuerySystemInformation(SystemExtendedHandleInformation, info, cb, &cb); + if (0 <= status) { + ULONG_PTR handles = info->NumberOfHandles; + if (handles) { + res.reserve(res.size() + handles); + SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX* entry = info->Handles; do { if (entry) @@ -97,8 +108,9 @@ } while (entry++, --handles); } } + free(info); - } while (status == STATUS_INFO_LENGTH_MISMATCH); + } return res; } @@ -109,32 +121,9 @@ return info; } -/* we're using UTF-8. originally, I had used just the ANSI versions of functions, but that - sucks massive dick. this way we get unicode in the way every single other OS does it */ -static std::string UnicodeStringToUtf8(std::wstring string) { - unsigned long size = ::WideCharToMultiByte(CP_UTF8, 0, string.c_str(), string.length(), NULL, 0, NULL, NULL); - std::string ret = std::string(size, '\0'); - ::WideCharToMultiByte(CP_UTF8, 0, string.c_str(), string.length(), &ret.front(), ret.length(), NULL, NULL); - return ret; -} - -static std::string UnicodeStringToUtf8(UNICODE_STRING string) { - unsigned long size = ::WideCharToMultiByte(CP_UTF8, 0, string.Buffer, string.Length, NULL, 0, NULL, NULL); - std::string ret = std::string(size, '\0'); - ::WideCharToMultiByte(CP_UTF8, 0, string.Buffer, string.Length, &ret.front(), ret.length(), NULL, NULL); - return ret; -} - -static std::wstring Utf8StringToUnicode(std::string string) { - unsigned long size = ::MultiByteToWideChar(CP_UTF8, 0, string.c_str(), string.length(), NULL, 0); - std::wstring ret = std::wstring(size, L'\0'); - ::MultiByteToWideChar(CP_UTF8, 0, string.c_str(), string.length(), &ret.front(), ret.length()); - return ret; -} - static std::string GetHandleType(HANDLE handle) { OBJECT_TYPE_INFORMATION info = QueryObjectTypeInfo(handle); - return UnicodeStringToUtf8(info.TypeName); + return ToUtf8String(info.TypeName); } static std::string GetFinalPathNameByHandle(HANDLE handle) { @@ -145,7 +134,7 @@ ::GetFinalPathNameByHandleW(handle, &buffer.front(), buffer.size(), FILE_NAME_NORMALIZED | VOLUME_NAME_DOS); buffer.resize(buffer.find('\0')); - return UnicodeStringToUtf8(buffer); + return ToUtf8String(buffer); } static bool IsFileHandle(HANDLE handle, unsigned short object_type_index) { @@ -170,27 +159,11 @@ return true; } -static std::string GetSystemDirectory() { - PWSTR path_wch; - SHGetKnownFolderPath(FOLDERID_Windows, 0, NULL, &path_wch); - std::wstring path_wstr(path_wch); - CoTaskMemFree(path_wch); - return UnicodeStringToUtf8(path_wstr); -} - -static bool IsSystemFile(const std::string& path) { - std::wstring path_w = Utf8StringToUnicode(path); - CharUpperBuffW(&path_w.front(), path_w.length()); - std::wstring windir_w = Utf8StringToUnicode(GetSystemDirectory()); - CharUpperBuffW(&windir_w.front(), windir_w.length()); - return path_w.find(windir_w) == 4; -} - static bool IsFilePathOk(const std::string& path) { if (path.empty()) return false; - if (IsSystemFile(path)) + if (IsSystemDirectory(path)) return false; const auto file_attributes = GetFileAttributesA(path.c_str()); @@ -200,7 +173,7 @@ return true; } -bool Win32FdTools::GetAllPids(std::set& pids) { +bool Win32FdTools::EnumerateOpenProcesses(process_proc_t process_proc) { HANDLE hProcessSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (hProcessSnap == INVALID_HANDLE_VALUE) return false; @@ -211,39 +184,23 @@ if (!::Process32First(hProcessSnap, &pe32)) return false; - pids.insert(pe32.th32ProcessID); + if (!process_proc({pe32.th32ProcessID, pe32.szExeFile})) + return false; while (::Process32Next(hProcessSnap, &pe32)) - pids.insert(pe32.th32ProcessID); + if (!process_proc({pe32.th32ProcessID, pe32.szExeFile})) + return false; ::CloseHandle(hProcessSnap); return true; } -bool Win32FdTools::GetProcessName(pid_t pid, std::string& result) { - unsigned long ret_size = 0; // size given by GetModuleBaseNameW - Handle handle(::OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid)); - if (handle.get() == INVALID_HANDLE_VALUE) +/* this could be changed to being a callback, but... I'm too lazy right now :) */ +bool Win32FdTools::EnumerateOpenFiles(const std::set& pids, open_file_proc_t open_file_proc) { + if (!open_file_proc) return false; - /* agh... */ - std::wstring ret(256, L'\0'); - for (; ret.length() < 32768; ret.resize(ret.length() * 2)) { - if (!(ret_size = ::GetModuleBaseNameW(handle.get(), 0, &ret.front(), ret.length()))) { - return false; - } else if (ret.length() > ret_size) { - ret.resize(ret.find(L'\0')); - result = UnicodeStringToUtf8(ret); - break; - } - } - - return true; -} - -/* this could be changed to being a callback, but... I'm too lazy right now :) */ -bool Win32FdTools::EnumerateOpenFiles(const std::set& pids, std::vector& files) { std::unordered_map proc_handles; for (const pid_t& pid : pids) { @@ -279,7 +236,8 @@ if (!IsFilePathOk(path)) continue; - files.push_back({pid, path}); + if (!open_file_proc({pid, path})) + return false; } return true; diff -r 54744a48a7d7 -r 8700806c2cc2 dep/animia/src/strategist.cc --- a/dep/animia/src/strategist.cc Tue Nov 14 16:31:21 2023 -0500 +++ b/dep/animia/src/strategist.cc Wed Nov 15 02:34:59 2023 -0500 @@ -3,6 +3,10 @@ #include "animia/fd.h" #include "animia.h" +#include + +#include + namespace animia::internal { class Strategist { @@ -15,6 +19,7 @@ bool AddMedia(const MediaInfo media_information); bool ApplyOpenFilesStrategy(); + bool ApplyWindowTitleStrategy(); Result& result_; }; @@ -22,12 +27,14 @@ bool Strategist::ApplyStrategies() { bool success = false; - for (const auto strategy : result_.player.strategies) { - switch (strategy) { - case Strategy::OpenFiles: - success |= ApplyOpenFilesStrategy(); - break; - } + switch (result_.type) { + case ResultType::Process: + success |= ApplyOpenFilesStrategy(); + break; + case ResultType::Window: + std::cout << "Wat" << std::endl; + success |= ApplyWindowTitleStrategy(); + break; } return success; @@ -46,17 +53,56 @@ //////////////////////////////////////////////////////////////////////////////// +static bool ApplyWindowTitleFormat(const std::string& format, std::string& title) { + if (format.empty()) + return false; + + const std::regex pattern(format); + std::smatch match; + std::regex_match(title, match, pattern); + + // Use the first non-empty match result, because the regular expression may + // contain multiple sub-expressions. + for (size_t i = 1; i < match.size(); ++i) { + if (!match.str(i).empty()) { + title = match.str(i); + return true; + } + } + + // Results are empty, but the match was successful + if (!match.empty()) { + title.clear(); + return true; + } + + return true; +} + +static MediaInfoType InferMediaInformationType(const std::string& str) { + const std::regex path_pattern(R"(^(?:[A-Za-z]:[/\\]|\\\\)[^<>:"/\\|?*]+)"); + return (std::regex_search(str, path_pattern)) + ? MediaInfoType::File : MediaInfoType::Unknown; +} + +bool Strategist::ApplyWindowTitleStrategy() { + auto title = result_.window.text; + ApplyWindowTitleFormat(result_.player.window_title_format, title); + + return AddMedia({InferMediaInformationType(title), title}); +} + bool Strategist::ApplyOpenFilesStrategy() { bool success = false; const std::set pids{result_.process.pid}; - std::vector files; - - fd.EnumerateOpenFiles(pids, files); - for (const auto& [pid, path] : files) { - success |= AddMedia({MediaInfoType::File, path}); - } + auto open_file_proc = [&](const OpenFile& file) -> bool { + success |= AddMedia({MediaInfoType::File, file.path}); + return true; + }; + + fd.EnumerateOpenFiles(pids, open_file_proc); return success; } diff -r 54744a48a7d7 -r 8700806c2cc2 dep/animia/src/util.cc --- a/dep/animia/src/util.cc Tue Nov 14 16:31:21 2023 -0500 +++ b/dep/animia/src/util.cc Wed Nov 15 02:34:59 2023 -0500 @@ -3,6 +3,9 @@ #include #include #include +#include + +#include #include "animia/util.h" @@ -32,6 +35,23 @@ return str1.size() == str2.size() && std::equal(str1.begin(), str1.end(), str2.begin(), equal_chars); } +bool Stem(const std::string& filename, std::string& stem) { + unsigned long long pos = filename.find_last_of("."); + if (pos != std::string::npos) + return false; + + stem = filename.substr(0, pos); + return true; +} + +bool CheckPattern(const std::string& pattern, const std::string& str) { + if (pattern.empty()) + return false; + if (pattern.front() == '^' && std::regex_match(str, std::regex(pattern))) + return true; + return util::EqualStrings(pattern, str); +} + bool TrimLeft(std::string& str, const char* chars) { if (str.empty()) return false; diff -r 54744a48a7d7 -r 8700806c2cc2 dep/animia/src/util/win32.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dep/animia/src/util/win32.cc Wed Nov 15 02:34:59 2023 -0500 @@ -0,0 +1,90 @@ +#include "animia/util/win32.h" + +#include +#include /* SHGetKnownFolderPath */ +#include /* UNICODE_STRING */ + +namespace animia::internal::win32 { + +std::string ToUtf8String(const std::wstring& string) { + const auto wctomb = [&string](LPSTR out, int size) -> int { + return ::WideCharToMultiByte(CP_UTF8, 0, string.c_str(), string.length(), out, size, nullptr, nullptr); + }; + + if (string.empty()) + return std::string(); + + + long size = ::WideCharToMultiByte(CP_UTF8, 0, string.c_str(), string.length(), nullptr, 0, nullptr, nullptr); + std::string ret = std::string(size, '\0'); + ::WideCharToMultiByte(CP_UTF8, 0, string.c_str(), string.length(), &ret.front(), ret.length(), nullptr, nullptr); + return ret; +} + +std::string ToUtf8String(const UNICODE_STRING& string) { + const auto wctomb = [&string](LPSTR out, int size) -> int { + return ::WideCharToMultiByte(CP_UTF8, 0, string.Buffer, string.Length, out, size, nullptr, nullptr); + }; + + if (string.Length <= 0) + return std::string(); + + long size = wctomb(nullptr, 0); + std::string ret = std::string(size, '\0'); + wctomb(&ret.front(), ret.length()); + return ret; +} + +std::wstring ToWstring(const std::string& string) { + const auto mbtowc = [&string](LPWSTR out, int size) -> int { + return ::MultiByteToWideChar(CP_UTF8, 0, string.c_str(), string.length(), out, size); + }; + + if (string.empty()) + return std::wstring(); + + long size = mbtowc(nullptr, 0); + std::wstring ret = std::wstring(size, L'\0'); + mbtowc(&ret.front(), ret.length()); + return ret; +} + +std::wstring GetFileNameFromPath(const std::wstring& path) { + const auto pos = path.find_last_of(L"/\\"); + return pos != std::wstring::npos ? path.substr(pos + 1) : path; +} + +std::wstring GetFileNameWithoutExtension(const std::wstring& filename) { + const auto pos = filename.find_last_of(L"."); + return pos != std::wstring::npos ? filename.substr(0, pos) : filename; +} + +static std::wstring GetSystemDirectory() { + PWSTR path_wch; + SHGetKnownFolderPath(FOLDERID_Windows, 0, NULL, &path_wch); + std::wstring path_wstr(path_wch); + CoTaskMemFree(path_wch); + return path_wstr; +} + +bool IsSystemDirectory(const std::string& path) { + std::wstring path_w = ToWstring(path); + ::CharUpperBuffW(&path_w.front(), path_w.length()); + + std::wstring windir = GetSystemDirectory(); + ::CharUpperBuffW(&windir.front(), windir.length()); + + /* wtf is 4? */ + return path_w.find(windir) == 4; +} + +bool IsSystemDirectory(std::wstring path) { + ::CharUpperBuffW(&path.front(), path.length()); + + std::wstring windir = GetSystemDirectory(); + ::CharUpperBuffW(&windir.front(), windir.length()); + + return path.find(windir) == 4; +} + +} diff -r 54744a48a7d7 -r 8700806c2cc2 dep/animia/src/win.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dep/animia/src/win.cc Wed Nov 15 02:34:59 2023 -0500 @@ -0,0 +1,17 @@ +#include "animia/fd.h" + +#ifdef WIN32 +# include "animia/win/win32.h" +#endif + +namespace animia::internal { + +#ifdef WIN32 +win32::Win32WinTools os_win; +#else +BaseWinTools os_win; +#endif + +BaseWinTools& win = os_win; + +} diff -r 54744a48a7d7 -r 8700806c2cc2 dep/animia/src/win/win32.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dep/animia/src/win/win32.cc Wed Nov 15 02:34:59 2023 -0500 @@ -0,0 +1,171 @@ +#include "animia/win/win32.h" +#include "animia/util/win32.h" +#include "animia/win.h" +#include "animia.h" + +#include +#include + +#include + +namespace animia::internal::win32 { + +static std::wstring GetWindowClassName(HWND hwnd) { + // The maximum size for lpszClassName, according to the documentation of + // WNDCLASSEX structure + constexpr int kMaxSize = 256; + + std::wstring buffer(kMaxSize, L'\0'); + const auto size = ::GetClassNameW(hwnd, &buffer.front(), buffer.length()); + return buffer; +} + +static std::wstring GetWindowText(HWND hwnd) { + const int size = ::GetWindowTextLengthW(hwnd); + + std::wstring buffer(size, L'\0'); + ::GetWindowTextW(hwnd, &buffer.front(), buffer.length()); + return buffer; +} + +static DWORD GetWindowProcessId(HWND hwnd) { + DWORD process_id = 0; + ::GetWindowThreadProcessId(hwnd, &process_id); + return process_id; +} + +static std::wstring GetProcessPath(DWORD process_id) { + // If we try to open a SYSTEM process, this function fails and the last error + // code is ERROR_ACCESS_DENIED. + // + // Note that if we requested PROCESS_QUERY_INFORMATION access right instead + // of PROCESS_QUERY_LIMITED_INFORMATION, this function would fail when used + // to open an elevated process. + Handle process_handle(::OpenProcess( + PROCESS_QUERY_LIMITED_INFORMATION, FALSE, process_id)); + + if (!process_handle) + return std::wstring(); + + std::wstring buffer(MAX_PATH, L'\0'); + DWORD buf_size = buffer.length(); + + // Note that this function requires Windows Vista or above. You may use + // GetProcessImageFileName or GetModuleFileNameEx on earlier versions. + if (!::QueryFullProcessImageNameW(process_handle.get(), 0, &buffer.front(), &buf_size)) + return std::wstring(); + + return buffer.substr(0, buf_size + 1); +} + +//////////////////////////////////////////////////////////////////////////////// + +static bool VerifyWindowStyle(HWND hwnd) { + const auto window_style = ::GetWindowLong(hwnd, GWL_STYLE); + const auto window_ex_style = ::GetWindowLong(hwnd, GWL_EXSTYLE); + + auto has_style = [&window_style](DWORD style) { + return (window_style & style) != 0; + }; + auto has_ex_style = [&window_ex_style](DWORD ex_style) { + return (window_ex_style & ex_style) != 0; + }; + + // Toolbars, tooltips and similar topmost windows + if (has_style(WS_POPUP) && has_ex_style(WS_EX_TOOLWINDOW)) + return false; + if (has_ex_style(WS_EX_TOPMOST) && has_ex_style(WS_EX_TOOLWINDOW)) + return false; + + return true; +} + +static bool VerifyClassName(const std::wstring& name) { + static const std::set invalid_names = { + // System classes + L"#32770", // Dialog box + L"CabinetWClass", // Windows Explorer + L"ComboLBox", + L"DDEMLEvent", + L"DDEMLMom", + L"DirectUIHWND", + L"GDI+ Hook Window Class", + L"IME", + L"Internet Explorer_Hidden", + L"MSCTFIME UI", + L"tooltips_class32", + }; + + return !name.empty() && !invalid_names.count(name); +} + +static bool VerifyProcessPath(const std::wstring& path) { + return !path.empty() && !IsSystemDirectory(path); +} + +static bool VerifyProcessFileName(const std::wstring& name) { + static const std::set invalid_names = { + // System files + L"explorer", // Windows Explorer + L"taskeng", // Task Scheduler Engine + L"taskhost", // Host Process for Windows Tasks + L"taskhostex", // Host Process for Windows Tasks + L"Taskmgr", // Task Manager + }; + + return !name.empty() && !invalid_names.count(name); +} + +//////////////////////////////////////////////////////////////////////////////// + +static BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM param) { + if (!::IsWindowVisible(hwnd)) + return TRUE; + + if (!VerifyWindowStyle(hwnd)) + return TRUE; + + Window window; + window.id = static_cast(reinterpret_cast(hwnd)); + window.text = ToUtf8String(GetWindowText(hwnd)); + + { + std::wstring class_name = GetWindowClassName(hwnd); + window.class_name = ToUtf8String(class_name); + if (!VerifyClassName(class_name)) + return TRUE; + } + + Process process; + process.pid = GetWindowProcessId(hwnd); + + const auto path = GetProcessPath(process.pid); + if (!VerifyProcessPath(path)) + return TRUE; + + { + std::wstring name = GetFileNameWithoutExtension(GetFileNameFromPath(path)); + process.name = ToUtf8String(name); + if (!VerifyProcessFileName(name)) + return TRUE; + } + + auto& window_proc = *reinterpret_cast(param); + if (!window_proc(process, window)) + return FALSE; + + return TRUE; +} + +bool Win32WinTools::EnumerateWindows(window_proc_t window_proc) { + if (!window_proc) + return false; + + const auto param = reinterpret_cast(&window_proc); + + // Note that EnumWindows enumerates only top-level windows of desktop apps + // (as opposed to UWP apps) on Windows 8 and above. + return ::EnumWindows(EnumWindowsProc, param) != FALSE; +} + +} // namespace animia::win::detail \ No newline at end of file diff -r 54744a48a7d7 -r 8700806c2cc2 src/track/media.cc --- a/src/track/media.cc Tue Nov 14 16:31:21 2023 -0500 +++ b/src/track/media.cc Wed Nov 15 02:34:59 2023 -0500 @@ -41,6 +41,7 @@ for (const auto& result : results) { for (const auto& media : result.media) { for (const auto& info : media.information) { + std::cout << static_cast(info.type) << ": " << info.value << std::endl; vec.push_back(info.value); success |= true; } @@ -50,9 +51,8 @@ return success; } +/* this sucks. use anitomy directly like a real man */ std::unordered_map GetMapFromElements(const anitomy::Elements& elements) { - /* there are way more than this in anitomy, but we only need basic information - I also just prefer using maps than using the ".get()" stuff which is why I'm doing this */ std::unordered_map ret; ret["title"] = Strings::ToUtf8String(elements.get(anitomy::kElementAnimeTitle));