changeset 138:28842a8d0c6b

dep/animia: huge refactor (again...) but this time, it actually compiles! and it WORKS! (on win32... not sure about other platforms...) configuring players is still not supported: at some point I'll prune something up...
author Paper <mrpapersonic@gmail.com>
date Sun, 12 Nov 2023 04:53:19 -0500
parents 69db40272acd
children 478f3b366199
files CMakeLists.txt dep/animia/CMakeLists.txt dep/animia/README.md dep/animia/include/animia.h dep/animia/include/animia/fd/bsd.h dep/animia/include/animia/fd/linux.h dep/animia/include/animia/fd/win32.h dep/animia/include/animia/matroska.h dep/animia/include/animia/media.h dep/animia/include/animia/platform/win32.h dep/animia/include/animia/platform/win32/fd.h dep/animia/include/animia/platform/win32/ui_auto.h dep/animia/include/animia/platform/win32/util.h dep/animia/include/animia/platform/win32/win.h dep/animia/include/animia/player.h dep/animia/include/animia/strategies.h dep/animia/include/animia/util.h dep/animia/src/animia.cc dep/animia/src/fd/bsd.cc dep/animia/src/fd/linux.cc dep/animia/src/fd/win32.cc dep/animia/src/matroska.cc dep/animia/src/platform/bsd/fd.cc dep/animia/src/platform/linux/fd.cc dep/animia/src/platform/win32.cc dep/animia/src/platform/win32/fd.cc dep/animia/src/platform/win32/ui_auto.cc dep/animia/src/platform/win32/util.cc dep/animia/src/platform/win32/win.cc dep/animia/src/player.cc dep/animia/src/strategist.cc dep/animia/src/util.cc dep/animia/util/Anisthesia.sublime-syntax include/track/media.h rc/player_data.qrc src/core/filesystem.cc src/gui/window.cc src/track/media.cc
diffstat 33 files changed, 1160 insertions(+), 922 deletions(-) [+]
line wrap: on
line diff
--- a/CMakeLists.txt	Fri Nov 10 13:52:47 2023 -0500
+++ b/CMakeLists.txt	Sun Nov 12 04:53:19 2023 -0500
@@ -110,6 +110,7 @@
 	# Qt resources
 	rc/icons.qrc
 	rc/dark.qrc
+	rc/player_data.qrc
 )
 
 set(INCLUDE
@@ -204,3 +205,7 @@
 	target_compile_definitions(minori PUBLIC WIN32)
 endif()
 target_link_libraries(minori ${LIBRARIES})
+
+if(WIN32)
+	install(FILES $<TARGET_RUNTIME_DLLS:minori> TYPE BIN)
+endif()
--- a/dep/animia/CMakeLists.txt	Fri Nov 10 13:52:47 2023 -0500
+++ b/dep/animia/CMakeLists.txt	Sun Nov 12 04:53:19 2023 -0500
@@ -3,28 +3,24 @@
 set(SRC_FILES
 	# any non-platform-specific files go here
 	src/animia.cc
-	src/matroska.cc
 	src/player.cc
 	src/util.cc
+	src/strategist.cc
 )
 if(LINUX)
 	list(APPEND SRC_FILES
 		# linux
-		src/linux/fd.cc
+		src/fd/linux.cc
 	)
 elseif(UNIX) # this won't run on Linux
 	list(APPEND SRC_FILES
 		# bsd
-		src/bsd/fd.cc
+		src/fd/bsd.cc
 	)
 elseif(WIN32)
 	list(APPEND SRC_FILES
 		# win32
-		src/platform/win32.cc
-		src/platform/win32/fd.cc
-		src/platform/win32/ui_auto.cc
-		src/platform/win32/util.cc
-		src/platform/win32/win.cc
+		src/fd/win32.cc
 	)
 endif()
 add_library(animia SHARED ${SRC_FILES})
--- a/dep/animia/README.md	Fri Nov 10 13:52:47 2023 -0500
+++ b/dep/animia/README.md	Sun Nov 12 04:53:19 2023 -0500
@@ -1,5 +1,4 @@
 # Animia
-Animia is a work-in-progress cross-platform fork of Anisthesia and part of Minori.
+Animia is a work-in-progress cross-platform clone of Anisthesia and part of Minori.
 
-## License
-Because this project is a hard-fork of Anisthesia, it is under the MIT license.
+Most (if not all) Anisthesia configs should also work in this library as well.
--- a/dep/animia/include/animia.h	Fri Nov 10 13:52:47 2023 -0500
+++ b/dep/animia/include/animia.h	Sun Nov 12 04:53:19 2023 -0500
@@ -3,10 +3,31 @@
 
 #include "animia/media.h"
 #include "animia/player.h"
+#include "animia/util.h"
 
 namespace animia {
 
+/* pid_t should be DWORD on windows, and defined by the system
+   anywhere else */
+struct Process {
+	pid_t pid = 0;
+	std::string name;
+};
 
+struct Window {
+	unsigned int id = 0;
+	std::string class_name;
+	std::string text; // title bar text
+};
+
+struct Result {
+	Player player;
+	Process process;
+	//Window window;
+	std::vector<Media> media;
+};
+
+bool GetResults(const std::vector<Player>& players, std::vector<Result>& results);
 
 } // namespace Animia
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dep/animia/include/animia/fd/bsd.h	Sun Nov 12 04:53:19 2023 -0500
@@ -0,0 +1,19 @@
+#ifndef __animia__animia__fd__linux_h
+#define __animia__animia__fd__linux_h
+
+#include <vector>
+#include <tuple>
+#include <string>
+#include <set>
+
+#include <sys/types.h> // pid_t
+
+namespace animia::internal::unix {
+
+bool GetAllPids(std::set<pid_t>& pids);
+bool GetProcessName(pid_t pid, std::string& result);
+bool EnumerateOpenFiles(const std::set<pid_t>& pids, std::vector<std::tuple<pid_t, std::string>>& files);
+
+}
+
+#endif // __animia__animia__fd__linux_h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dep/animia/include/animia/fd/linux.h	Sun Nov 12 04:53:19 2023 -0500
@@ -0,0 +1,19 @@
+#ifndef __animia__animia__fd__linux_h
+#define __animia__animia__fd__linux_h
+
+#include <vector>
+#include <tuple>
+#include <string>
+#include <set>
+
+#include <sys/types.h> // pid_t
+
+namespace animia::internal::linux {
+
+bool GetAllPids(std::set<pid_t>& pids);
+bool GetProcessName(pid_t pid, std::string& result);
+bool EnumerateOpenFiles(const std::set<pid_t>& pids, std::vector<std::tuple<pid_t, std::string>>& files);
+
+}
+
+#endif // __animia__animia__fd__linux_h
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dep/animia/include/animia/fd/win32.h	Sun Nov 12 04:53:19 2023 -0500
@@ -0,0 +1,29 @@
+#ifndef __animia__animia__fd__win32_h
+#define __animia__animia__fd__win32_h
+
+#include <vector>
+#include <tuple>
+#include <string>
+#include <set>
+#include <memory>
+
+#include <windows.h>
+
+#include "animia/util.h"
+
+namespace animia::internal::win32 {
+
+struct HandleDeconstructor {
+	using pointer = HANDLE;
+	void operator()(pointer t) const { ::CloseHandle(t); };
+};
+
+using Handle = std::unique_ptr<HANDLE, HandleDeconstructor>;
+
+bool GetAllPids(std::set<pid_t>& pids);
+bool GetProcessName(pid_t pid, std::string& result);
+bool EnumerateOpenFiles(const std::set<pid_t>& pids, std::vector<std::tuple<pid_t, std::string>>& files);
+
+}
+
+#endif // __animia__animia__fd__win32_h
--- a/dep/animia/include/animia/matroska.h	Fri Nov 10 13:52:47 2023 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,70 +0,0 @@
-#ifndef __animia__animia__matroska_h
-#define __animia__animia__matroska_h
-
-#include <chrono>
-#include <cstdint>
-#include <string>
-#include <vector>
-
-namespace animia::matroska {
-
-namespace detail {
-
-using timecode_scale_t = std::chrono::duration<float, std::nano>;
-constexpr uint32_t kDefaultTimecodeScale = 1000000;  // 1 milliseconds
-
-enum ElementId {
-	// EBML Header
-	EL_EBML = 0x1A45DFA3,
-	// Segment
-	EL_SEGMENT = 0x18538067,
-	// Segment Information
-	EL_INFO = 0x1549A966,
-	EL_TIMECODESCALE = 0x2AD7B1,
-	EL_DURATION = 0x4489,
-	EL_TITLE = 0x7BA9,
-	// Track
-	EL_TRACKS = 0x1654AE6B,
-	EL_TRACKENTRY = 0xAE,
-	EL_TRACKTYPE = 0x83,
-	EL_TRACKNAME = 0x536E
-};
-
-enum TrackType {
-	kVideo = 1
-};
-
-class Buffer {
-	public:
-		Buffer(size_t size);
-
-		uint8_t* data();
-		size_t pos() const;
-		size_t size() const;
-		void skip(size_t size);
-
-		bool read_encoded_value(uint32_t& value, bool clear_leading_bits);
-		uint32_t read_uint32(const size_t size);
-		float read_float(const size_t size);
-		std::string read_string(const size_t size);
-
-	private:
-		std::vector<uint8_t> data_;
-		size_t pos_ = 0;
-};
-
-} // namespace detail
-
-using duration_t = std::chrono::duration<float, std::milli>;
-
-struct Info {
-	duration_t duration = duration_t::zero();
-	std::string title;
-	std::string video_track_name;
-};
-
-bool ReadInfoFromFile(const std::string& path, Info& info);
-
-}
-
-#endif // __animia__animia__matroska_h
--- a/dep/animia/include/animia/media.h	Fri Nov 10 13:52:47 2023 -0500
+++ b/dep/animia/include/animia/media.h	Sun Nov 12 04:53:19 2023 -0500
@@ -15,14 +15,7 @@
 	File,
 	Tab,
 	Title,
-	Url,
-};
-
-enum class MediaState {
-	Unknown,
-	Playing,
-	Paused,
-	Stopped,
+	Url
 };
 
 struct MediaInfo {
@@ -31,12 +24,9 @@
 };
 
 struct Media {
-	MediaState state = MediaState::Unknown;  // currently unused
-	media_time_t duration;                   // currently unused
-	media_time_t position;                   // currently unused
 	std::vector<MediaInfo> information;
 };
 
-using media_proc_t = std::function<bool(const MediaInfo&)>;
+}  // namespace animia
 
-#endif // __animia__animia__media_h
\ No newline at end of file
+#endif // __animia__animia__media_h
--- a/dep/animia/include/animia/platform/win32.h	Fri Nov 10 13:52:47 2023 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,43 +0,0 @@
-#ifndef __animia__animia__platform__win32_h
-#define __animia__animia__platform__win32_h
-
-#include <string>
-#include <vector>
-
-#include <windows.h>
-
-#include "animia/media.h"
-#include "animia/player.h"
-
-namespace animia::win {
-
-struct Process {
-	DWORD id = 0;
-	std::wstring name;
-};
-
-struct Window {
-	HWND handle = nullptr;
-	std::wstring class_name;
-	std::wstring text;
-};
-
-struct Result {
-	Player player;
-	Process process;
-	Window window;
-	std::vector<Media> media;
-};
-
-bool GetResults(const std::vector<Player>& players, media_proc_t media_proc,
-                std::vector<Result>& results);
-
-namespace detail {
-
-bool ApplyStrategies(media_proc_t media_proc, std::vector<Result>& results);
-
-}  // namespace detail
-
-}  // namespace animia::win
-
-#endif // __animia__animia__platform__win32_h
--- a/dep/animia/include/animia/platform/win32/fd.h	Fri Nov 10 13:52:47 2023 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,24 +0,0 @@
-#ifndef __animia__animia__platform__win32__fd_h
-#define __animia__animia__platform__win32__fd_h
-
-#include <functional>
-#include <set>
-#include <string>
-
-#include <windows.h>
-
-namespace animia::win::detail {
-
-struct OpenFile {
-	DWORD proc_id;
-	std::wstring path;
-}
-
-using open_file_proc_t = std::function<bool(const OpenFile&)>;
-
-bool EnumerateOpenFiles(const std::set<DWORD>& process_ids,
-	                    open_file_proc_t open_file_proc);
-
-}
-
-#endif // __animia__animia__platform__win32__fd_h
\ No newline at end of file
--- a/dep/animia/include/animia/platform/win32/ui_auto.h	Fri Nov 10 13:52:47 2023 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,30 +0,0 @@
-#ifndef __animia__animia__win32__ui_auto_h
-#define __animia__animia__win32__ui_auto_h
-
-/* "UI automation" == web browser stuff.. */
-
-#include <functional>
-#include <string>
-
-#include <windows.h>
-
-namespace animia::win::detail {
-
-enum class WebBrowserInformationType {
-	Address,
-	Tab,
-	Title,
-};
-
-struct WebBrowserInformation {
-	WebBrowserInformationType type = WebBrowserInformationType::Title;
-	std::string value;
-};
-
-using web_browser_proc_t = std::function<void(const WebBrowserInformation&)>;
-
-bool GetWebBrowserInformation(HWND hwnd, web_browser_proc_t web_browser_proc);
-
-}  // namespace animia::win::detail
-
-#endif // __animia__animia__win32__ui_auto_h
--- a/dep/animia/include/animia/platform/win32/util.h	Fri Nov 10 13:52:47 2023 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,37 +0,0 @@
-#ifndef __animia__animia__win32__util_h
-#define __animia__animia__win32__util_h
-
-#include <memory>
-#include <string>
-#include <type_traits>
-
-#include <windows.h>
-
-namespace animia::win::detail {
-
-struct HandleDeleter {
-	void operator()(HANDLE p) const { ::CloseHandle(p); }
-};
-
-using Handle = std::unique_ptr<HANDLE, HandleDeleter>;
-
-/* ----------- Alternative to Microsoft::WRL::ComPtr ------------- */
-template <typename T>
-struct ComInterfaceDeleter {
-	static_assert(std::is_base_of<IUnknown, T>::value, "Invalid COM interface");
-	void operator()(T* p) const { if (p) p->Release(); }
-};
-
-template <typename T>
-using ComInterface = std::unique_ptr<T, ComInterfaceDeleter<T>>;
-/* --------------------------------------------------------------- */
-
-std::wstring GetFileNameFromPath(const std::wstring& path);
-std::wstring GetFileNameWithoutExtension(const std::wstring& filename);
-bool IsSystemDirectory(const std::wstring& path);
-
-std::string ToUtf8String(const std::wstring& str);
-
-}
-
-#endif // __animia__animia__win32__util_h
--- a/dep/animia/include/animia/platform/win32/win.h	Fri Nov 10 13:52:47 2023 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,21 +0,0 @@
-#ifndef __animia__animia__win32__win_h
-#define __animia__animia__win32__win_h
-
-#include <functional>
-
-namespace animia::win {
-
-struct Process;
-struct Window;
-
-namespace detail {
-
-using window_proc_t = std::function<bool(const Process&, const Window&)>;
-
-bool EnumerateWindows(window_proc_t window_proc);
-
-} // namespace detail
-
-} // namespace animia::win
-
-#endif // __animia__animia__win32__win_h
--- a/dep/animia/include/animia/player.h	Fri Nov 10 13:52:47 2023 -0500
+++ b/dep/animia/include/animia/player.h	Sun Nov 12 04:53:19 2023 -0500
@@ -7,15 +7,15 @@
 namespace animia {
 
 enum class Strategy {
-	WindowTitle,
+	WindowTitle, // unused
 	OpenFiles,
-	UiAutomation // ???
-}
+	UiAutomation // unused
+};
 
 enum class PlayerType {
 	Default,
-	WebBrowser
-}
+	WebBrowser // unused
+};
 
 struct Player {
 	PlayerType type = PlayerType::Default;
@@ -24,11 +24,11 @@
 	std::vector<std::string> windows;
 	std::vector<std::string> executables;
 	std::vector<Strategy> strategies;
-}
+};
 
 bool ParsePlayersData(const std::string& data, std::vector<Player>& players);
 bool ParsePlayersFile(const std::string& path, std::vector<Player>& players);
 
-}
+} // namespace animia
 
-#endif // __animia__animia__player_h
+#endif // __animia__animia__player_h
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dep/animia/include/animia/strategies.h	Sun Nov 12 04:53:19 2023 -0500
@@ -0,0 +1,12 @@
+#ifndef __animia__animia__strategies_h
+#define __animia__animia__strategies_h
+
+#include "animia.h"
+
+namespace animia::internal {
+
+bool ApplyStrategies(std::vector<Result>& results);
+
+}
+
+#endif // __animia__animia__strategies_h
\ No newline at end of file
--- a/dep/animia/include/animia/util.h	Fri Nov 10 13:52:47 2023 -0500
+++ b/dep/animia/include/animia/util.h	Sun Nov 12 04:53:19 2023 -0500
@@ -1,16 +1,31 @@
 #ifndef __animia__animia__util_h
 #define __animia__animia__util_h
 
-#include <string>
+/* these should not be #defines, make them some constexpr
+   magic at some point */
+#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_LINUX
+#	endif
+#	define ANIMIA_ON_LINUX
+#elif defined(_WIN32)
+#	define ANIMIA_ON_WIN32
+/* move this to a types.h or something */
+#include <windows.h>
+namespace animia {
+	typedef DWORD pid_t;
+}
+#endif
 
-namespace animia::detail::util {
+namespace animia::internal::util {
 
 bool ReadFile(const std::string& path, std::string& data);
-
 bool EqualStrings(const std::string& str1, const std::string& str2);
 bool TrimLeft(std::string& str, const char* chars);
 bool TrimRight(std::string& str, const char* chars);
 
-}  // namespace animia::detail::util
+}
 
-#endif // __animia__animia__util_h
+#endif // __animia__animia__util_h
\ No newline at end of file
--- a/dep/animia/src/animia.cc	Fri Nov 10 13:52:47 2023 -0500
+++ b/dep/animia/src/animia.cc	Sun Nov 12 04:53:19 2023 -0500
@@ -0,0 +1,62 @@
+#include <string>
+#include <vector>
+
+#include <windows.h>
+
+#include "animia.h"
+#include "animia/util.h"
+#include "animia/strategies.h"
+#ifdef ANIMIA_ON_WIN32
+#include "animia/fd/win32.h"
+#endif
+#include <iostream>
+
+namespace animia {
+
+static bool ProcessInPlayers(const std::vector<Player>& players, const std::string& name, Player& player_) {
+	for (const auto& player : players) {
+		for (const auto& exe : player.executables) {
+			if (exe == name.substr(0, name.rfind(".exe"))) {
+				player_ = player;
+				return true;
+			}
+		}
+	}
+	return false;
+}
+
+bool GetResults(const std::vector<Player>& players, std::vector<Result>& results) {
+	std::set<pid_t> pids;
+
+#ifdef ANIMIA_ON_WIN32
+	/* todo: make these functions also return process names in a tuple,
+	   cause the win32 api gives it to us for free! */
+	if (!internal::win32::GetAllPids(pids))
+#elif ANIMIA_ON_LINUX
+	if (!internal::linux::GetAllPids(pids))
+#elif ANIMIA_ON_UNIX
+	if (!internal::unix::GetAllPids(pids))
+#endif
+		return false;
+
+	for (const auto& pid : pids) {
+		std::string name;
+#ifdef ANIMIA_ON_WIN32
+		animia::internal::win32::GetProcessName(pid, name);
+#endif
+
+		Player player;
+		if (!ProcessInPlayers(players, name, player))
+			continue;
+
+		Result result;
+		result.process.pid = pid;
+		result.process.name = name;
+		result.player = player;
+		results.push_back(result);
+	}
+
+	return internal::ApplyStrategies(results);
+}
+
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dep/animia/src/fd/bsd.cc	Sun Nov 12 04:53:19 2023 -0500
@@ -0,0 +1,116 @@
+/**
+ * bsd.cpp
+ *  - provides support for most* versions of BSD
+ *  - this also works for OS X :)
+ * more technical details: this is essentially a wrapper
+ * around the very C-like BSD system functions that are...
+ * kind of unnatural to use in modern C++.
+ **/
+#include <fcntl.h>
+#include <iostream>
+#include <string>
+#include <sys/sysctl.h>
+#include <sys/types.h>
+#include <sys/user.h>
+#include <unordered_map>
+#include <vector>
+#ifdef __FreeBSD__
+#	include <libutil.h>
+#elif defined(__APPLE__)
+#	include <libproc.h>
+#endif
+
+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 */
+bool GetAllPids(std::set<pid_t>& 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<int>();
+
+	/* 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.push_back(result[i].kp_proc.p_pid);
+}
+
+bool GetProcessName(pid_t pid, std::string& result) {
+#ifdef __FreeBSD__
+	struct kinfo_proc* proc = kinfo_getproc(pid);
+	if (!proc)
+		return false;
+	result = proc->ki_comm;
+
+	/* FreeBSD manpage for kinfo_getproc():
+	   "The pointer was obtained by an internal call to malloc(3) and
+	   must be freed by the caller with a call to free(3)." */
+	free(proc);
+
+	return true;
+#elif defined(__APPLE__)
+	struct proc_bsdinfo proc;
+
+	int st = proc_pidinfo(pid, PROC_PIDTBSDINFO, 0, &proc, PROC_PIDTBSDINFO_SIZE);
+	if (st != PROC_PIDTBSDINFO_SIZE)
+		return false;
+
+	/* fixme: is this right? pbi_comm is an alternative, but it reduces the string size to
+	   16 chars. does pbi_name do the same, or is it different? */
+	result = proc.pbi_name;
+	return true;
+#endif
+}
+
+/* this only works on OS X :( */
+bool EnumerateOpenFiles(const std::set<pid_t>& pids, std::vector<std::tuple<pid_t, std::string>>& files) {
+	for (const auto& pid : pids) {
+		int bufsz = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, NULL, 0);
+		if (bufsz == -1)
+			return false;
+
+		struct proc_fdinfo* info = (struct proc_fdinfo*)malloc(bufsz);
+		if (!info)
+			return false;
+
+		proc_pidinfo(pid, PROC_PIDLISTFDS, 0, info, bufsz);
+
+		for (int i = 0; i < bufsz / sizeof(info[0]); i++) {
+			if (info[i].proc_fdtype == PROX_FDTYPE_VNODE) {
+				struct vnode_fdinfowithpath vnodeInfo;
+
+				int sz = proc_pidfdinfo(pid, info[i].proc_fd, PROC_PIDFDVNODEPATHINFO, &vnodeInfo, PROC_PIDFDVNODEPATHINFO_SIZE);
+				if (sz != PROC_PIDFDVNODEPATHINFO_SIZE)
+					return false;
+
+				/* this doesn't work!
+				if (vnodeInfo.pfi.fi_openflags & O_WRONLY || vnodeInfo.pfi.fi_openflags & O_RDWR)
+					continue;
+				*/
+
+				files.push_back({pid, vnodeInfo.pvip.vip_path});
+			}
+		}
+	}
+	return true;
+}
+
+} // namespace animia::internal::unix
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dep/animia/src/fd/linux.cc	Sun Nov 12 04:53:19 2023 -0500
@@ -0,0 +1,132 @@
+#include <algorithm>
+#include <fcntl.h>
+#include <filesystem>
+#include <fstream>
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <unordered_map>
+#include <vector>
+#include <cstring>
+#include <dirent.h>
+
+#include "animia/util.h"
+
+#define PROC_LOCATION "/proc"
+
+namespace animia::internal::linux {
+
+/* this uses dirent instead of std::filesystem; it would make a bit
+   more sense to use the latter, but this is platform dependent already :) */
+std::vector<std::string> GetAllFilesInDir(const std::string& _dir) {
+	std::vector<std::string> ret;
+
+	DIR* dir = opendir(_dir.c_str());
+	if (!dir)
+		return ret;
+
+	struct dirent* dp;
+	while ((dp = readdir(dir)) != NULL) {
+		if (!(!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")))
+			ret.push_back(_dir + "/" + dp->d_name);
+	}
+
+	closedir(dir);
+	return ret;
+}
+
+std::string Basename(const std::string& path) {
+	return path.substr(path.find_last_of("/") + 1, path.length());
+}
+
+static bool IsRegularFile(std::string link) {
+	struct stat sb;
+	if (stat(link.c_str(), &sb) == -1)
+		return false;
+	return S_ISREG(sb.st_mode);
+}
+
+static bool AreFlagsOk(pid_t pid, int fd) {
+	const std::string path = PROC_LOCATION "/" + std::to_string(pid) + "/fdinfo/" + std::to_string(fd);
+	std::stringstream buffer(util::ReadFile(path));
+
+	int flags = 0;
+	for (std::string line; std::getline(buffer, line);) {
+		/* FIXME: exception handling here!! */
+		if (line.rfind("flags:", 0) == 0) {
+			flags = std::stoi(line.substr(line.find_last_not_of("0123456789") + 1));
+		}
+	}
+	if (flags & O_WRONLY || flags & O_RDWR)
+		return false;
+	return true;
+}
+
+static std::string GetFilenameFromFd(std::string link) {
+	/* gets around stupid linux limitation where /proc doesn't
+	   give actual filesize readings */
+	size_t  exe_size = 1024;
+	ssize_t exe_used;
+	std::string ret;
+
+	while (1) {
+		ret = std::string(exe_size, '\0');
+		exe_used = readlink(link.c_str(), &ret.front(), ret.length());
+		if (exe_used == (ssize_t)-1)
+			return NULL;
+
+		if (exe_used < (ssize_t)1) {
+			errno = ENOENT;
+			return NULL;
+		}
+
+		if (exe_used < (ssize_t)(exe_size - 1))
+			break;
+
+		exe_size += 1024;
+	}
+
+	return ret.c_str();
+}
+
+bool GetAllPids(std::set<pid_t>& pids) {
+	for (const auto& dir : get_all_files_in_dir(PROC_LOCATION)) {
+		pid_t pid;
+		try {
+			pid = std::stoul(basename(dir));
+		} catch (std::invalid_argument) {
+			continue;
+		}
+		pids.push_back(pid);
+	}
+}
+
+bool GetProcessName(pid_t pid, std::string& result) {
+	const std::string path = PROC_LOCATION "/" + std::to_string(pid) + "/comm";
+
+	std::string result = util::ReadFile(path);
+	result.erase(std::remove(result.begin(), result.end(), '\n'), result.end());
+}
+
+bool EnumerateOpenFiles(const std::set<pid_t>& pids, std::vector<std::tuple<pid_t, std::string>>& files) {
+	for (const auto& pid : pids) {
+		const std::string path = PROC_LOCATION "/" + std::to_string(pid) + "/fd";
+
+		for (const auto& dir : GetAllFilesInDir(path)) {
+			if (!AreFlagsOk(pid, std::stoi(basename(dir))))
+				continue;
+
+			std::string name = GetFilenameFromFd(dir);
+
+			if (!IsRegularFile(name))
+				continue;
+
+			files.push_back({pid, name});
+		}
+	}
+	return true;
+}
+
+} // namespace animia::internal::linux
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dep/animia/src/fd/win32.cc	Sun Nov 12 04:53:19 2023 -0500
@@ -0,0 +1,288 @@
+/**
+ * win32.cpp
+ *  - provides support for Windows clients
+ *
+ **/
+#include "animia/fd/win32.h"
+#include <fileapi.h>
+#include <handleapi.h>
+#include <iostream>
+#include <libloaderapi.h>
+#include <ntdef.h>
+#include <psapi.h>
+#include <shlobj.h>
+#include <stdexcept>
+#include <string>
+#include <stringapiset.h>
+#include <tlhelp32.h>
+#include <unordered_map>
+#include <vector>
+#include <windows.h>
+#include <winternl.h>
+
+/* This file is noticably more complex than Unix and Linux, and that's because
+   there is no "simple" way to get the paths of a file. In fact, this thing requires
+   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)
+constexpr NTSTATUS STATUS_INFO_LENGTH_MISMATCH = 0xC0000004UL;
+constexpr NTSTATUS STATUS_SUCCESS = 0x00000000UL;
+
+static unsigned short file_type_index = 0;
+
+struct SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX {
+		PVOID Object;
+		ULONG_PTR UniqueProcessId;
+		HANDLE HandleValue;
+		ACCESS_MASK GrantedAccess;
+		USHORT CreatorBackTraceIndex;
+		USHORT ObjectTypeIndex;
+		ULONG HandleAttributes;
+		ULONG Reserved;
+};
+
+struct SYSTEM_HANDLE_INFORMATION_EX {
+		ULONG_PTR NumberOfHandles;
+		ULONG_PTR Reserved;
+		SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX Handles[1];
+};
+
+namespace animia::internal::win32 {
+
+static HANDLE DuplicateHandle(HANDLE process_handle, HANDLE handle) {
+	HANDLE dup_handle = nullptr;
+	const bool result =
+	    ::DuplicateHandle(process_handle, handle, ::GetCurrentProcess(), &dup_handle, 0, false, DUPLICATE_SAME_ACCESS);
+	return result ? dup_handle : nullptr;
+}
+
+static PVOID GetNTDLLAddress(LPCSTR proc_name) {
+	return reinterpret_cast<PVOID>(::GetProcAddress(::GetModuleHandleA("ntdll.dll"), proc_name));
+}
+
+static NTSTATUS QuerySystemInformation(SYSTEM_INFORMATION_CLASS cls, PVOID sysinfo, ULONG len, PULONG retlen) {
+	static const auto func =
+	    reinterpret_cast<decltype(::NtQuerySystemInformation)*>(GetNTDLLAddress("NtQuerySystemInformation"));
+	return func(cls, sysinfo, len, retlen);
+}
+
+static NTSTATUS QueryObject(HANDLE handle, OBJECT_INFORMATION_CLASS cls, PVOID objinf, ULONG objinflen, PULONG retlen) {
+	static const auto func = reinterpret_cast<decltype(::NtQueryObject)*>(GetNTDLLAddress("NtQueryObject"));
+	return func(handle, cls, objinf, objinflen, retlen);
+}
+
+static std::vector<SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX> GetSystemHandleInformation() {
+	std::vector<SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX> res;
+	/* we should really put a cap on this */
+	ULONG cb = 1 << 19;
+	NTSTATUS status = STATUS_SUCCESS;
+	SYSTEM_HANDLE_INFORMATION_EX* info;
+
+	do {
+		status = STATUS_NO_MEMORY;
+
+		if (!(info = (SYSTEM_HANDLE_INFORMATION_EX*)malloc(cb *= 2)))
+			continue;
+
+		res.reserve(cb);
+
+		if (0 <= (status = QuerySystemInformation(SystemExtendedHandleInformation, info, cb, &cb))) {
+			if (ULONG_PTR handles = info->NumberOfHandles) {
+				SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX* entry = info->Handles;
+				do {
+					if (entry)
+						res.push_back(*entry);
+				} while (entry++, --handles);
+			}
+		}
+		free(info);
+	} while (status == STATUS_INFO_LENGTH_MISMATCH);
+
+	return res;
+}
+
+static OBJECT_TYPE_INFORMATION QueryObjectTypeInfo(HANDLE handle) {
+	OBJECT_TYPE_INFORMATION info;
+	QueryObject(handle, ObjectTypeInformation, &info, sizeof(info), NULL);
+	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);
+}
+
+static std::string GetFinalPathNameByHandle(HANDLE handle) {
+	std::wstring buffer;
+
+	int result = ::GetFinalPathNameByHandleW(handle, NULL, 0, FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
+	buffer.resize(result);
+	::GetFinalPathNameByHandleW(handle, &buffer.front(), buffer.size(), FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
+	buffer.resize(buffer.find('\0'));
+
+	return UnicodeStringToUtf8(buffer);
+}
+
+static bool IsFileHandle(HANDLE handle, unsigned short object_type_index) {
+	if (file_type_index)
+		return object_type_index == file_type_index;
+	else if (!handle)
+		return true;
+	else if (GetHandleType(handle) == "File") {
+		file_type_index = object_type_index;
+		return true;
+	}
+	return false;
+}
+
+static bool IsFileMaskOk(ACCESS_MASK access_mask) {
+	if (!(access_mask & FILE_READ_DATA))
+		return false;
+
+	if ((access_mask & FILE_APPEND_DATA) || (access_mask & FILE_WRITE_EA) || (access_mask & FILE_WRITE_ATTRIBUTES))
+		return false;
+
+	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))
+		return false;
+
+	const auto file_attributes = GetFileAttributesA(path.c_str());
+	if ((file_attributes == INVALID_FILE_ATTRIBUTES) || (file_attributes & FILE_ATTRIBUTE_DIRECTORY))
+		return false;
+
+	return true;
+}
+
+bool GetAllPids(std::set<pid_t>& pids) {
+	HANDLE hProcessSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
+	if (hProcessSnap == INVALID_HANDLE_VALUE)
+		return false;
+
+	PROCESSENTRY32 pe32;
+	pe32.dwSize = sizeof(PROCESSENTRY32);
+
+	if (!::Process32First(hProcessSnap, &pe32))
+		return false;
+
+	pids.insert(pe32.th32ProcessID);
+
+	while (::Process32Next(hProcessSnap, &pe32))
+		pids.insert(pe32.th32ProcessID);
+
+	::CloseHandle(hProcessSnap);
+
+	return true;
+}
+
+bool 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())
+		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 EnumerateOpenFiles(const std::set<pid_t>& pids, std::vector<std::tuple<pid_t, std::string>>& files) {
+	std::unordered_map<pid_t, Handle> proc_handles;
+
+	for (const pid_t& pid : pids) {
+		const HANDLE handle = ::OpenProcess(PROCESS_DUP_HANDLE, false, pid);
+		if (handle)
+			proc_handles[pid] = Handle(handle);
+	}
+
+	if (proc_handles.empty())
+		return false;
+
+	std::vector<SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX> info = GetSystemHandleInformation();
+
+	for (const auto& h : info) {
+		const pid_t pid = h.UniqueProcessId;
+		if (!pids.count(pid))
+			continue;
+
+		if (!IsFileHandle(nullptr, h.ObjectTypeIndex))
+			continue;
+
+		if (!IsFileMaskOk(h.GrantedAccess))
+			continue;
+
+		Handle handle(DuplicateHandle(proc_handles[pid].get(), h.HandleValue));
+		if (!handle.get())
+			continue;
+
+		if (GetFileType(handle.get()) != FILE_TYPE_DISK)
+			continue;
+
+		const std::string path = GetFinalPathNameByHandle(handle.get());
+		if (!IsFilePathOk(path))
+			continue;
+
+		/* create an empty vector if it doesn't exist, probably unnecessary */
+		files.push_back({pid, path});
+	}
+
+	return true;
+}
+
+} // namespace animia::internal::win32
--- a/dep/animia/src/platform/bsd/fd.cc	Fri Nov 10 13:52:47 2023 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,124 +0,0 @@
-/**
- * bsd.cpp
- *  - provides support for most* versions of BSD
- *  - this also works for OS X :)
- * more technical details: this is essentially a wrapper
- * around the very C-like BSD system functions that are...
- * kind of unnatural to use in modern C++.
- **/
-#include <fcntl.h>
-#include <iostream>
-#include <string>
-#include <sys/sysctl.h>
-#include <sys/types.h>
-#include <sys/user.h>
-#include <unordered_map>
-#include <vector>
-#ifdef __FreeBSD__
-#	include <libutil.h>
-#elif defined(__APPLE__)
-#	include <libproc.h>
-#endif
-
-namespace Animia { namespace 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 */
-std::vector<int> get_all_pids() {
-	std::vector<int> ret;
-	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<int>();
-
-	/* actually get our results */
-	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 */
-	ret.reserve(length / sizeof(*result));
-	for (int i = 0; i < length / sizeof(*result); i++)
-		ret.push_back(result[i].kp_proc.p_pid);
-
-	return ret;
-}
-
-std::string get_process_name(int pid) {
-	std::string ret;
-#ifdef __FreeBSD__
-	struct kinfo_proc* proc = kinfo_getproc(pid);
-	if (!proc)
-		return "";
-	ret = proc->ki_comm;
-	free(proc);
-#elif defined(__APPLE__)
-	struct proc_bsdinfo proc;
-
-	int st = proc_pidinfo(pid, PROC_PIDTBSDINFO, 0, &proc, PROC_PIDTBSDINFO_SIZE);
-	if (st != PROC_PIDTBSDINFO_SIZE)
-		return "";
-	ret = proc.pbi_comm;
-#endif
-	return ret;
-}
-
-std::vector<std::string> get_open_files(int pid) {
-	/* note: this is OS X only right now. eventually, I'll find a way
-	   to do this in FreeBSD, OpenBSD and the like */
-	std::vector<std::string> ret;
-
-	if (pid == 0)
-		return ret;
-
-	int bufsz = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, NULL, 0);
-	if (bufsz == -1)
-		return ret;
-
-	struct proc_fdinfo* info = (struct proc_fdinfo*)malloc(bufsz);
-	if (!info)
-		return ret;
-
-	proc_pidinfo(pid, PROC_PIDLISTFDS, 0, info, bufsz);
-
-	// iterate over stuff
-	ret.reserve(bufsz / sizeof(info[0]));
-	for (int i = 0; i < bufsz / sizeof(info[0]); i++) {
-		if (info[i].proc_fdtype == PROX_FDTYPE_VNODE) {
-			struct vnode_fdinfowithpath vnodeInfo;
-
-			int sz = proc_pidfdinfo(pid, info[i].proc_fd, PROC_PIDFDVNODEPATHINFO, &vnodeInfo, PROC_PIDFDVNODEPATHINFO_SIZE);
-			if (sz != PROC_PIDFDVNODEPATHINFO_SIZE)
-				continue;
-
-			if (vnodeInfo.pfi.fi_openflags & O_WRONLY || vnodeInfo.pfi.fi_openflags & O_RDWR)
-				continue;
-
-			ret.push_back(vnodeInfo.pvip.vip_path);
-		}
-	}
-	return ret;
-}
-
-std::unordered_map<int, std::vector<std::string>> get_all_open_files() {
-	std::unordered_map<int, std::vector<std::string>> map;
-	std::vector<int> pids = get_all_pids();
-	for (int i : pids) {
-		map[i] = get_open_files(i);
-	}
-	return map;
-}
-
-} // namespace Unix
-} // namespace Animia
--- a/dep/animia/src/platform/linux/fd.cc	Fri Nov 10 13:52:47 2023 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,147 +0,0 @@
-#include <algorithm>
-#include <fcntl.h>
-#include <filesystem>
-#include <fstream>
-#include <iostream>
-#include <sstream>
-#include <string>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <unordered_map>
-#include <vector>
-#include <cstring>
-#include <dirent.h>
-
-#define PROC_LOCATION "/proc"
-
-namespace Animia { namespace Linux {
-
-std::vector<std::string> get_all_files_in_dir(const std::string& _dir) {
-	std::vector<std::string> ret;
-
-	DIR* dir = opendir(_dir.c_str());
-	if (!dir)
-		return ret;
-
-	struct dirent* dp;
-	while ((dp = readdir(dir)) != NULL) {
-		if (!(!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")))
-			ret.push_back(_dir + "/" + dp->d_name);
-	}
-
-	closedir(dir);
-	return ret;
-}
-
-std::string basename(const std::string& path) {
-	return path.substr(path.find_last_of("/") + 1, path.length());
-}
-
-std::string stem(const std::string& path) {
-	std::string bn = basename(path);
-	return bn.substr(0, path.find_last_of("."));
-}
-
-std::vector<int> get_all_pids() {
-	std::vector<int> ret;
-
-	for (const auto& dir : get_all_files_in_dir(PROC_LOCATION)) {
-		int pid;
-		try {
-			pid = std::stoi(basename(dir));
-		} catch (std::invalid_argument) {
-			continue;
-		}
-		ret.push_back(pid);
-	}
-
-	return ret;
-}
-
-std::string get_process_name(int pid) {
-	std::string path = PROC_LOCATION "/" + std::to_string(pid) + "/comm";
-	std::ifstream t(path);
-	std::stringstream buf;
-	buf << t.rdbuf();
-
-	std::string str = buf.str();
-	str.erase(std::remove(str.begin(), str.end(), '\n'), str.end());
-	return str;
-}
-
-static bool is_regular_file(std::string link) {
-	struct stat sb;
-	if (stat(link.c_str(), &sb) == -1)
-		return false;
-	return S_ISREG(sb.st_mode);
-}
-
-static bool are_flags_ok(int pid, int fd) {
-	std::string path = PROC_LOCATION "/" + std::to_string(pid) + "/fdinfo/" + std::to_string(fd);
-	std::ifstream t(path);
-	std::stringstream buffer;
-	buffer << t.rdbuf();
-	std::string raw;
-	int flags = 0;
-	while (std::getline(buffer, raw)) {
-		if (raw.rfind("flags:", 0) == 0) {
-			flags = std::stoi(raw.substr(raw.find_last_not_of("0123456789") + 1));
-		}
-	}
-	if (flags & O_WRONLY || flags & O_RDWR)
-		return false;
-	return true;
-}
-
-static std::string get_name_from_fd(std::string link) {
-	size_t  exe_size = 1024;
-	ssize_t exe_used;
-	std::string ret;
-	while (1) {
-		ret = std::string(exe_size, '\0');
-		exe_used = readlink(link.c_str(), &ret.front(), ret.length());
-		if (exe_used == (ssize_t)-1)
-			return NULL;
-
-		if (exe_used < (ssize_t)1) {
-			errno = ENOENT;
-			return NULL;
-		}
-
-		if (exe_used < (ssize_t)(exe_size - 1))
-			break;
-
-		exe_size += 1024;
-	}
-
-	return ret.c_str();
-}
-
-std::vector<std::string> get_open_files(int pid) {
-	std::vector<std::string> ret;
-	std::string path = PROC_LOCATION "/" + std::to_string(pid) + "/fd";
-
-	for (const auto& dir : get_all_files_in_dir(path)) {
-		if (!are_flags_ok(pid, std::stoi(basename(dir))))
-			continue;
-
-		std::string buf = get_name_from_fd(dir);
-
-		if (!is_regular_file(buf))
-			continue;
-
-		ret.push_back(buf);
-	}
-	return ret;
-}
-
-std::unordered_map<int, std::vector<std::string>> get_all_open_files() {
-	std::unordered_map<int, std::vector<std::string>> map;
-	std::vector<int> pids = get_all_pids();
-	for (int i : pids)
-		map[i] = get_open_files(i);
-	return map;
-}
-
-} // namespace Linux
-} // namespace Animia
\ No newline at end of file
--- a/dep/animia/src/platform/win32/fd.cc	Fri Nov 10 13:52:47 2023 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,314 +0,0 @@
-/**
- * win32.cpp
- *  - provides support for Windows clients
- *
- **/
-#include "win32.h"
-#include <fileapi.h>
-#include <handleapi.h>
-#include <iostream>
-#include <libloaderapi.h>
-#include <ntdef.h>
-#include <psapi.h>
-#include <shlobj.h>
-#include <stdexcept>
-#include <string>
-#include <stringapiset.h>
-#include <tlhelp32.h>
-#include <unordered_map>
-#include <vector>
-#include <windows.h>
-#include <winternl.h>
-
-/* This file is noticably more complex than Unix and Linux, and that's because
-   there is no "simple" way to get the paths of a file. In fact, this thing requires
-   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)
-constexpr NTSTATUS STATUS_INFO_LENGTH_MISMATCH = 0xC0000004UL;
-constexpr NTSTATUS STATUS_SUCCESS = 0x00000000UL;
-
-static unsigned short file_type_index = 0;
-
-struct SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX {
-		PVOID Object;
-		ULONG_PTR UniqueProcessId;
-		HANDLE HandleValue;
-		ACCESS_MASK GrantedAccess;
-		USHORT CreatorBackTraceIndex;
-		USHORT ObjectTypeIndex;
-		ULONG HandleAttributes;
-		ULONG Reserved;
-};
-
-struct SYSTEM_HANDLE_INFORMATION_EX {
-		ULONG_PTR NumberOfHandles;
-		ULONG_PTR Reserved;
-		SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX Handles[1];
-};
-
-namespace Animia { namespace Windows {
-
-/* All of this BS is required on Windows. Why? */
-
-HANDLE DuplicateHandle(HANDLE process_handle, HANDLE handle) {
-	HANDLE dup_handle = nullptr;
-	const bool result =
-	    ::DuplicateHandle(process_handle, handle, ::GetCurrentProcess(), &dup_handle, 0, false, DUPLICATE_SAME_ACCESS);
-	return result ? dup_handle : nullptr;
-}
-
-PVOID GetNTDLLAddress(LPCSTR proc_name) {
-	return reinterpret_cast<PVOID>(::GetProcAddress(::GetModuleHandleA("ntdll.dll"), proc_name));
-}
-
-NTSTATUS QuerySystemInformation(SYSTEM_INFORMATION_CLASS cls, PVOID sysinfo, ULONG len, PULONG retlen) {
-	static const auto func =
-	    reinterpret_cast<decltype(::NtQuerySystemInformation)*>(GetNTDLLAddress("NtQuerySystemInformation"));
-	return func(cls, sysinfo, len, retlen);
-}
-
-NTSTATUS QueryObject(HANDLE handle, OBJECT_INFORMATION_CLASS cls, PVOID objinf, ULONG objinflen, PULONG retlen) {
-	static const auto func = reinterpret_cast<decltype(::NtQueryObject)*>(GetNTDLLAddress("NtQueryObject"));
-	return func(handle, cls, objinf, objinflen, retlen);
-}
-
-std::vector<SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX> GetSystemHandleInformation() {
-	std::vector<SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX> res;
-	ULONG cb = 1 << 19;
-	NTSTATUS status = STATUS_SUCCESS;
-	SYSTEM_HANDLE_INFORMATION_EX* info;
-
-	do {
-		status = STATUS_NO_MEMORY;
-
-		if (!(info = (SYSTEM_HANDLE_INFORMATION_EX*)malloc(cb *= 2)))
-			continue;
-
-		res.reserve(cb);
-
-		if (0 <= (status = QuerySystemInformation(SystemExtendedHandleInformation, info, cb, &cb))) {
-			if (ULONG_PTR handles = info->NumberOfHandles) {
-				SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX* entry = info->Handles;
-				do {
-					if (entry)
-						res.push_back(*entry);
-				} while (entry++, --handles);
-			}
-		}
-		free(info);
-	} while (status == STATUS_INFO_LENGTH_MISMATCH);
-
-	return res;
-}
-
-OBJECT_TYPE_INFORMATION QueryObjectTypeInfo(HANDLE handle) {
-	OBJECT_TYPE_INFORMATION info;
-	QueryObject(handle, ObjectTypeInformation, &info, sizeof(info), NULL);
-	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 */
-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;
-}
-
-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;
-}
-
-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;
-}
-
-std::string GetHandleType(HANDLE handle) {
-	OBJECT_TYPE_INFORMATION info = QueryObjectTypeInfo(handle);
-	return UnicodeStringToUtf8(info.TypeName);
-}
-
-std::string GetFinalPathNameByHandle(HANDLE handle) {
-	std::wstring buffer;
-
-	int result = ::GetFinalPathNameByHandleW(handle, NULL, 0, FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
-	buffer.resize(result);
-	::GetFinalPathNameByHandleW(handle, &buffer.front(), buffer.size(), FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
-	buffer.resize(buffer.find('\0'));
-
-	return UnicodeStringToUtf8(buffer);
-}
-
-bool IsFileHandle(HANDLE handle, unsigned short object_type_index) {
-	if (file_type_index)
-		return object_type_index == file_type_index;
-	else if (!handle)
-		return true;
-	else if (GetHandleType(handle) == "File") {
-		file_type_index = object_type_index;
-		return true;
-	}
-	return false;
-}
-
-bool IsFileMaskOk(ACCESS_MASK access_mask) {
-	if (!(access_mask & FILE_READ_DATA))
-		return false;
-
-	if ((access_mask & FILE_APPEND_DATA) || (access_mask & FILE_WRITE_EA) || (access_mask & FILE_WRITE_ATTRIBUTES))
-		return false;
-
-	return true;
-}
-
-bool IsFilePathOk(const std::string& path) {
-	if (path.empty())
-		return false;
-
-	const auto file_attributes = GetFileAttributesA(path.c_str());
-	if ((file_attributes == INVALID_FILE_ATTRIBUTES) || (file_attributes & FILE_ATTRIBUTE_DIRECTORY))
-		return false;
-
-	return true;
-}
-
-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);
-}
-
-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;
-}
-
-std::vector<int> get_all_pids() {
-	std::vector<int> ret;
-	HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
-	PROCESSENTRY32 pe32;
-	pe32.dwSize = sizeof(PROCESSENTRY32);
-
-	if (hProcessSnap == INVALID_HANDLE_VALUE)
-		return std::vector<int>();
-
-	if (!Process32First(hProcessSnap, &pe32))
-		return std::vector<int>();
-
-	ret.push_back(pe32.th32ProcessID);
-	while (Process32Next(hProcessSnap, &pe32)) {
-		ret.push_back(pe32.th32ProcessID);
-	}
-	// clean the snapshot object
-	CloseHandle(hProcessSnap);
-
-	return ret;
-}
-
-std::string get_process_name(int pid) {
-	unsigned long size = 256, ret_size = 0;
-	HANDLE handle = ::OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
-	if (!handle)
-		return "";
-
-	std::wstring ret(size, L'\0');
-	while (size < 32768) {
-		ret.resize(size, L'\0');
-
-		if (!(ret_size = ::GetModuleBaseNameW(handle, 0, &ret.front(), ret.length())))
-			ret = L"";
-		else if (size > ret_size)
-			ret.resize(ret.find('\0'));
-
-		size *= 2;
-	}
-
-	CloseHandle(handle);
-
-	return UnicodeStringToUtf8(ret);
-}
-
-std::vector<std::string> get_open_files(int pid) {
-	std::vector<std::string> ret;
-	std::vector<SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX> info = GetSystemHandleInformation();
-	for (auto& h : info) {
-		if (h.UniqueProcessId != pid)
-			continue;
-
-		if (!IsFileHandle(nullptr, h.ObjectTypeIndex))
-			continue;
-		if (!IsFileMaskOk(h.GrantedAccess))
-			continue;
-
-		const HANDLE proc = ::OpenProcess(PROCESS_DUP_HANDLE, false, pid);
-		HANDLE handle = DuplicateHandle(proc, h.HandleValue);
-		if (!handle)
-			continue;
-
-		if (GetFileType(handle) != FILE_TYPE_DISK)
-			continue;
-
-		std::string path = GetFinalPathNameByHandle(handle);
-		if (!IsFilePathOk(path))
-			continue;
-
-		ret.push_back(path);
-	}
-	return ret;
-}
-
-std::vector<std::string> filter_system_files(const std::vector<std::string>& source) {
-	std::vector<std::string> ret;
-	for (const std::string& s : source) {
-		if (!IsSystemFile(s))
-			ret.push_back(s);
-	}
-	return ret;
-}
-
-std::unordered_map<int, std::vector<std::string>> get_all_open_files() {
-	std::unordered_map<int, std::vector<std::string>> map;
-	std::vector<SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX> info = GetSystemHandleInformation();
-	for (auto& h : info) {
-		int pid = h.UniqueProcessId;
-
-		if (!IsFileHandle(nullptr, h.ObjectTypeIndex))
-			continue;
-		if (!IsFileMaskOk(h.GrantedAccess))
-			continue;
-
-		const HANDLE proc = ::OpenProcess(PROCESS_DUP_HANDLE, false, pid);
-		HANDLE handle = DuplicateHandle(proc, h.HandleValue);
-		if (!handle)
-			continue;
-
-		if (GetFileType(handle) != FILE_TYPE_DISK)
-			continue;
-
-		std::string path = GetFinalPathNameByHandle(handle);
-		if (!IsFilePathOk(path))
-			continue;
-
-		if (map.find(pid) == map.end())
-			map[pid] = {};
-		map[pid].push_back(path);
-	}
-	return map;
-}
-
-} // namespace Windows
-} // namespace Animia
--- a/dep/animia/src/player.cc	Fri Nov 10 13:52:47 2023 -0500
+++ b/dep/animia/src/player.cc	Sun Nov 12 04:53:19 2023 -0500
@@ -0,0 +1,198 @@
+#include <map>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include "animia/player.h"
+#include "animia/util.h"
+
+namespace animia {
+
+namespace internal::parser {
+
+enum class State {
+	ExpectPlayerName,
+	ExpectSection,
+	ExpectWindow,
+	ExpectExecutable,
+	ExpectStrategy,
+	ExpectType,
+	ExpectWindowTitle,
+};
+
+size_t GetIndentation(const std::string& line) {
+	return line.find_first_not_of('\t');
+}
+
+bool HandleIndentation(const size_t current,
+                       const std::vector<Player>& players,
+                       State& state) {
+	// Each state has a definitive expected indentation
+	const auto expected = [&state]() -> size_t {
+		switch (state) {
+			default:
+			case State::ExpectPlayerName:
+				return 0;
+			case State::ExpectSection:
+				return 1;
+			case State::ExpectWindow:
+			case State::ExpectExecutable:
+			case State::ExpectStrategy:
+			case State::ExpectType:
+				return 2;
+			case State::ExpectWindowTitle:
+				return 3;
+		}
+	}();
+
+	if (current > expected)
+		return false;  // Disallow excessive indentation
+
+	if (current < expected) {
+		auto fix_state = [&]() {
+			state = !current ? State::ExpectPlayerName : State::ExpectSection;
+		};
+		switch (state) {
+			case State::ExpectWindow:
+				if (players.back().windows.empty())
+					return false;
+				fix_state();
+				break;
+			case State::ExpectExecutable:
+				if (players.back().executables.empty())
+					return false;
+				fix_state();
+				break;
+			case State::ExpectStrategy:
+				if (players.back().strategies.empty())
+					return false;
+				fix_state();
+				break;
+			case State::ExpectType:
+				fix_state();
+				break;
+			case State::ExpectWindowTitle:
+				return false;
+		}
+	}
+
+	return true;
+}
+
+bool HandleState(std::string& line, std::vector<Player>& players, State& state) {
+	switch (state) {
+		case State::ExpectPlayerName:
+			players.push_back(Player());
+			players.back().name = line;
+			state = State::ExpectSection;
+			break;
+
+		case State::ExpectSection: {
+			static const std::map<std::string, State> sections = {
+				{"windows", State::ExpectWindow},
+				{"executables", State::ExpectExecutable},
+				{"strategies", State::ExpectStrategy},
+				{"type", State::ExpectType},
+			};
+			util::TrimRight(line, ":");
+			const auto it = sections.find(line);
+			if (it == sections.end())
+				return false;
+			state = it->second;
+			break;
+		}
+
+		case State::ExpectWindow:
+			players.back().windows.push_back(line);
+			break;
+
+		case State::ExpectExecutable:
+			players.back().executables.push_back(line);
+			break;
+
+		case State::ExpectStrategy: {
+			static const std::map<std::string, Strategy> strategies = {
+				{"window_title", Strategy::WindowTitle},
+				{"open_files", Strategy::OpenFiles},
+				{"ui_automation", Strategy::UiAutomation},
+			};
+			util::TrimRight(line, ":");
+			const auto it = strategies.find(line);
+			if (it == strategies.end())
+				return false;
+			const auto strategy = it->second;
+			players.back().strategies.push_back(strategy);
+			switch (strategy) {
+				case Strategy::WindowTitle:
+					state = State::ExpectWindowTitle;
+					break;
+			}
+			break;
+		}
+
+		case State::ExpectType: {
+			static const std::map<std::string, PlayerType> types = {
+				{"default", PlayerType::Default},
+				{"web_browser", PlayerType::WebBrowser},
+			};
+			const auto it = types.find(line);
+			if (it == types.end())
+				return false;
+			players.back().type = it->second;
+			break;
+		}
+
+		case State::ExpectWindowTitle:
+			players.back().window_title_format = line;
+			state = State::ExpectStrategy;
+			break;
+	}
+
+	return true;
+}
+
+}  // namespace internal::parser
+
+////////////////////////////////////////////////////////////////////////////////
+
+bool ParsePlayersData(const std::string& data, std::vector<Player>& players) {
+	if (data.empty())
+		return false;
+
+	std::istringstream stream(data);
+	std::string line;
+	size_t indentation = 0;
+	auto state = internal::parser::State::ExpectPlayerName;
+
+	while (std::getline(stream, line, '\n')) {
+		if (line.empty())
+			continue;  // Ignore empty lines
+
+		indentation = internal::parser::GetIndentation(line);
+
+		internal::util::TrimLeft(line, "\t");
+		internal::util::TrimRight(line, "\n\r");
+
+		if (line.empty() || line.front() == '#')
+			continue;  // Ignore empty lines and comments
+
+		if (!internal::parser::HandleIndentation(indentation, players, state))
+			return false;
+
+		if (!internal::parser::HandleState(line, players, state))
+			return false;
+	}
+
+	return !players.empty();
+}
+
+bool ParsePlayersFile(const std::string& path, std::vector<Player>& players) {
+	std::string data;
+
+	if (!internal::util::ReadFile(path, data))
+		return false;
+
+	return ParsePlayersData(data, players);
+}
+
+}  // namespace anisthesia
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dep/animia/src/strategist.cc	Sun Nov 12 04:53:19 2023 -0500
@@ -0,0 +1,91 @@
+#include "animia/strategies.h"
+#include "animia/util.h"
+#include "animia.h"
+#include <iostream>
+
+#ifdef ANIMIA_ON_WIN32
+#	include "animia/fd/win32.h"
+#elif defined(ANIMIA_ON_LINUX)
+#	include "animia/fd/linux.h"
+#elif defined(ANIMIA_ON_UNIX)
+#	include "animia/fd/bsd.h"
+#endif
+
+namespace animia::internal {
+
+class Strategist {
+	public:
+		Strategist(Result& result) : result_(result) {}
+
+		bool ApplyStrategies();
+
+	private:
+		bool AddMedia(const MediaInfo media_information);
+
+		bool ApplyOpenFilesStrategy();
+
+		Result& result_;
+};
+
+bool Strategist::ApplyStrategies() {
+	bool success = false;
+
+	for (const auto strategy : result_.player.strategies) {
+		switch (strategy) {
+			case Strategy::OpenFiles:
+				success |= ApplyOpenFilesStrategy();
+				break;
+		}
+	}
+
+	return success;
+}
+
+bool ApplyStrategies(std::vector<Result>& results) {
+	bool success = false;
+
+	for (auto& result : results) {
+		Strategist strategist(result);
+		success |= strategist.ApplyStrategies();
+	}
+
+	return success;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+bool Strategist::ApplyOpenFilesStrategy() {
+	bool success = false;
+
+	const std::set<pid_t> pids{result_.process.pid};
+	std::vector<std::tuple<pid_t, std::string>> files;
+
+#ifdef ANIMIA_ON_WIN32
+	win32::EnumerateOpenFiles(pids, files);
+#elif defined(ANIMIA_ON_LINUX)
+	linux::EnumerateOpenFiles(pids, files);
+#elif defined(ANIMIA_ON_UNIX)
+	unix::EnumerateOpenFiles(pids, files);
+#endif
+
+	for (const auto& [pid, file] : files) {
+		success |= AddMedia({MediaInfoType::File, file});
+	}
+
+	return success;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+bool Strategist::AddMedia(const MediaInfo media_information) {
+	if (media_information.value.empty())
+		return false;
+
+	Media media;
+	media.information.push_back(media_information);
+	result_.media.push_back(std::move(media));
+
+	return true;
+}
+
+}
--- a/dep/animia/src/util.cc	Fri Nov 10 13:52:47 2023 -0500
+++ b/dep/animia/src/util.cc	Sun Nov 12 04:53:19 2023 -0500
@@ -0,0 +1,69 @@
+#include <algorithm>
+#include <fstream>
+#include <sstream>
+#include <string>
+#include <cctype>
+
+#include "animia/util.h"
+
+namespace animia::internal::util {
+
+bool ReadFile(const std::string& path, std::string& data) {
+	std::ifstream file(path.c_str(), std::ios::in | std::ios::binary);
+
+	if (!file)
+		return false;
+
+	file.seekg(0, std::ios::end);
+	data.resize(static_cast<size_t>(file.tellg()));
+	file.seekg(0, std::ios::beg);
+
+	file.read(&data.front(), data.size());
+	file.close();
+
+	return true;
+}
+
+bool EqualStrings(const std::string& str1, const std::string& str2) {
+	auto equal_chars = [](const char c1, const char c2) -> bool {
+		return std::tolower(static_cast<unsigned char>(c1)) == std::tolower(static_cast<unsigned char>(c2));
+	};
+
+	return str1.size() == str2.size() && std::equal(str1.begin(), str1.end(), str2.begin(), equal_chars);
+}
+
+bool TrimLeft(std::string& str, const char* chars) {
+	if (str.empty())
+		return false;
+
+	const auto found = str.find_first_not_of(chars);
+
+	if (found == 0)
+		return false;
+
+	if (found == std::string::npos)
+		str.clear();
+	else
+		str.erase(0, found);
+
+	return true;
+}
+
+bool TrimRight(std::string& str, const char* chars) {
+	if (str.empty())
+		return false;
+
+	const auto found = str.find_last_not_of(chars);
+
+	if (found == str.size() - 1)
+		return false;
+
+	if (found == std::string::npos)
+		str.clear();
+	else
+		str.resize(found + 1);
+
+	return true;
+}
+
+} // namespace anisthesia::detail::util
--- a/dep/animia/util/Anisthesia.sublime-syntax	Fri Nov 10 13:52:47 2023 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,38 +0,0 @@
-%YAML 1.2
----
-name: Anisthesia
-file_extensions: [anisthesia]
-scope: text.anisthesia
-
-contexts:
-  main:
-    - include: comments
-    - include: keywords
-    - include: player
-    - include: strategies
-    - include: type
-    - include: string
-
-  comments:
-    - match: ^\t*#.*\n
-      scope: comment.anisthesia
-
-  keywords:
-    - match: ^\t+(executables|strategies|type|windows):?\n
-      scope: keyword.anisthesia
-
-  player:
-    - match: ^[^\s].+\n
-      scope: markup.heading.player.anisthesia
-
-  strategies:
-    - match: ^\t+(open_files|ui_automation|window_title):?\n
-      scope: constant.strategy.anisthesia
-
-  type:
-    - match: ^\t+(default|web_browser)\n
-      scope: constant.type.anisthesia
-
-  string:
-    - match: ^\t+\^?(.+)\n
-      scope: string.anisthesia
--- a/include/track/media.h	Fri Nov 10 13:52:47 2023 -0500
+++ b/include/track/media.h	Sun Nov 12 04:53:19 2023 -0500
@@ -2,13 +2,15 @@
 #define __track__media_h
 #include "core/filesystem.h"
 #include <unordered_map>
+#include <string>
+#include <vector>
 
 namespace Track {
 namespace Media {
 
-std::filesystem::path GetCurrentPlaying();
+bool GetCurrentlyPlaying(std::vector<std::string>& vec);
 std::unordered_map<std::string, std::string> GetFileElements(const std::string& basename);
-std::unordered_map<std::string, std::string> GetFileElements(const std::filesystem::path& path);
+//std::unordered_map<std::string, std::string> GetFileElements(const std::filesystem::path& path);
 
 } // namespace Media
 } // namespace Track
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rc/player_data.qrc	Sun Nov 12 04:53:19 2023 -0500
@@ -0,0 +1,5 @@
+<!DOCTYPE rcc><RCC version="1.0">
+	<qresource>
+		<file alias="players.anisthesia">../dep/animia/data/players.anisthesia</file>
+	</qresource>
+</RCC>
--- a/src/core/filesystem.cc	Fri Nov 10 13:52:47 2023 -0500
+++ b/src/core/filesystem.cc	Sun Nov 12 04:53:19 2023 -0500
@@ -43,15 +43,14 @@
 #elif defined(MACOSX)
 	return std::filesystem::path(osx::GetApplicationSupportDirectory()) / CONFIG_DIR;
 #else // just assume POSIX
+	std::filesystem::path path;
 	const char* home = getenv("HOME");
-	if (home != NULL)
-		path = home;
 #	ifdef __linux__
-	else
-		path = getpwuid(getuid())->pw_dir;
+	if (!home)
+		home = getpwuid(getuid())->pw_dir;
 #	endif // __linux__
-	if (!path.empty())
-		return path / ".config";
+	if (!home)
+		return std::filesystem::path(home) / ".config";
 	else
 		return std::filesystem::path();
 #endif     // !WIN32 && !MACOSX
--- a/src/gui/window.cc	Fri Nov 10 13:52:47 2023 -0500
+++ b/src/gui/window.cc	Sun Nov 12 04:53:19 2023 -0500
@@ -69,15 +69,22 @@
 	connect(timer, &QTimer::timeout, this, [this] {
 		NowPlayingPage* page = reinterpret_cast<NowPlayingPage*>(stack->widget(static_cast<int>(Pages::NOW_PLAYING)));
 
-		std::filesystem::path path = Track::Media::GetCurrentPlaying();
-		std::unordered_map<std::string, std::string> elements = Track::Media::GetFileElements(path);
-		int id = Anime::db.GetAnimeFromTitle(elements["title"]);
-		if (id <= 0) {
-			page->SetDefault();
+		std::vector<std::string> files;
+		if (!Track::Media::GetCurrentlyPlaying(files))
 			return;
-		}
 
-		page->SetPlaying(Anime::db.items[id], elements);
+		/* this should really be more intertwined with anitomy */
+		for (const auto& file : files) {
+			std::filesystem::path path(file); // in the future it will not be guaranteed this is a path!
+			std::unordered_map<std::string, std::string> elements = Track::Media::GetFileElements(path.filename().string());
+			int id = Anime::db.GetAnimeFromTitle(elements["title"]);
+			if (id <= 0)
+				continue;
+
+			qDebug() << id;
+
+			page->SetPlaying(Anime::db.items[id], elements);
+		}
 	});
 	timer->start(5000);
 }
--- a/src/track/media.cc	Fri Nov 10 13:52:47 2023 -0500
+++ b/src/track/media.cc	Sun Nov 12 04:53:19 2023 -0500
@@ -9,39 +9,51 @@
 #include <unordered_map>
 #include <vector>
 #include <iostream>
+#include <QFile>
 
 namespace Track {
 namespace Media {
 
-std::vector<std::filesystem::path> GetCurrentlyPlayingFiles() {
-	/* getting all open files */
-	std::vector<std::filesystem::path> ret;
+static bool GetCurrentlyPlayingResults(std::vector<animia::Result>& results) {
+	std::vector<animia::Player> players;
+
+	{
+		QFile f(":/players.anisthesia");
+		if (!f.exists())
+			return false;
+
+		f.open(QFile::ReadOnly | QFile::Text);
+		QTextStream ts(&f);
+
+		if (!animia::ParsePlayersData(Strings::ToUtf8String(ts.readAll()), players))
+			return false;
+	}
+
+	if (!animia::GetResults(players, results))
+		return false;
 
-	std::vector<int> pids = Animia::get_all_pids();
-	for (int pid : pids) {
-		for (const Types::MediaPlayer& player : session.recognition.players) {
-			if (!player.GetEnabled() || Animia::get_process_name(pid) != player.GetExecutable())
-				continue;
+	return true;
+}
+
+/* meh */
+bool GetCurrentlyPlaying(std::vector<std::string>& vec) {
+	std::vector<animia::Result> results;
 
-			for (const std::string& file : Animia::filter_system_files(Animia::get_open_files(pid))) {
-				const std::filesystem::path path(file);
+	if (!GetCurrentlyPlayingResults(results))
+		return false;
+
+	bool success = false;
 
-				for (const Types::MediaExtension& ext : session.recognition.extensions)
-					if (path.extension() == ext.GetExtension())
-						ret.push_back(path);
+	for (const auto& result : results) {
+		for (const auto& media : result.media) {
+			for (const auto& info : media.information) {
+				vec.push_back(info.value);
+				success |= true;
 			}
 		}
 	}
 
-	return ret;
-}
-
-std::filesystem::path GetCurrentPlaying() {
-	/* getting all open files */
-	std::vector<std::filesystem::path> paths = GetCurrentlyPlayingFiles();
-	if (paths.size())
-		return paths.at(0);
-	return std::filesystem::path();
+	return success;
 }
 
 std::unordered_map<std::string, std::string> GetMapFromElements(const anitomy::Elements& elements) {