changeset 62:4c6dd5999b39

*: update 1. updated animia 2. use widestrings for filesystem on Windows
author Paper <mrpapersonic@gmail.com>
date Sun, 01 Oct 2023 06:16:06 -0400
parents 327568ad9be9
children 3d2decf093bb
files CMakeLists.txt dep/animia/README.md dep/animia/include/animia.h dep/animia/include/win32.h dep/animia/src/bsd.cpp dep/animia/src/linux.cpp dep/animia/src/main.cpp dep/animia/src/win32.cpp dep/animia/test/main.cpp include/core/config.h include/core/strings.h include/gui/dialog/settings.h src/core/filesystem.cpp src/core/strings.cpp src/gui/dark_theme.cpp src/gui/dialog/information.cpp src/gui/dialog/settings.cpp src/gui/dialog/settings/application.cpp src/gui/pages/anime_list.cpp src/gui/widgets/optional_date.cpp src/gui/widgets/sidebar.cpp src/gui/widgets/text.cpp src/gui/window.cpp
diffstat 23 files changed, 344 insertions(+), 233 deletions(-) [+]
line wrap: on
line diff
--- a/CMakeLists.txt	Fri Sep 29 15:52:31 2023 -0400
+++ b/CMakeLists.txt	Sun Oct 01 06:16:06 2023 -0400
@@ -8,22 +8,30 @@
 endif()
 
 add_subdirectory(dep/anitomy)
-add_subdirectory(dep/animia)
+# add_subdirectory(dep/animia)
 add_subdirectory(dep/pugixml)
 
 # Fix for mingw64
 list(APPEND CMAKE_FIND_LIBRARY_SUFFIXES ".dll.a" ".a")
 
-find_package(Qt5 COMPONENTS Widgets REQUIRED)
+if(USE_QT6)
+	find_package(Qt6 COMPONENTS Widgets REQUIRED)
+else()
+	find_package(Qt5 COMPONENTS Widgets REQUIRED)
+endif()
 find_package(CURL REQUIRED)
 
 set(LIBRARIES
-	${Qt5Widgets_LIBRARIES}
 	${CURL_LIBRARIES}
 	anitomy
-	animia
 )
 
+if(USE_QT6)
+	list(APPEND LIBRARIES ${Qt6Widgets_LIBRARIES})
+else()
+	list(APPEND LIBRARIES ${Qt5Widgets_LIBRARIES})
+endif()
+
 # We need Cocoa for some OS X stuff
 if(APPLE)
 	find_library(COCOA_LIBRARY Cocoa)
@@ -98,7 +106,12 @@
 set_property(TARGET minori PROPERTY AUTOMOC ON)
 set_property(TARGET minori PROPERTY AUTORCC ON)
 
-target_include_directories(minori PUBLIC ${Qt5Widgets_INCLUDE_DIRS} ${CURL_INCLUDE_DIRS} PRIVATE include dep/pugixml/src)
+target_include_directories(minori PUBLIC ${CURL_INCLUDE_DIRS} PRIVATE include dep/pugixml/src dep/animia/include)
+if(USE_QT6)
+	target_include_directories(minori PUBLIC ${Qt6Widgets_INCLUDE_DIRS})
+else()
+	target_include_directories(minori PUBLIC ${Qt5Widgets_INCLUDE_DIRS})
+endif()
 target_compile_options(minori PRIVATE -Wall -Wextra -Wsuggest-override)
 if(APPLE)
 	target_compile_definitions(minori PUBLIC MACOSX)
--- a/dep/animia/README.md	Fri Sep 29 15:52:31 2023 -0400
+++ b/dep/animia/README.md	Sun Oct 01 06:16:06 2023 -0400
@@ -1,3 +1,5 @@
 # Animia
-Animia is a wrapper around Linux's `/proc` and BSD's system calls.
-Its primary purpose is to provide a map of PIDs and a list of opened files to the user.
\ No newline at end of file
+Animia is a cross-platform library for getting a list of open read-only files from a PID (or, an entire list of open files, in an std::unordered_map).
+
+## Usage
+Check the `test/` directory for a simple usage example.
--- a/dep/animia/include/animia.h	Fri Sep 29 15:52:31 2023 -0400
+++ b/dep/animia/include/animia.h	Sun Oct 01 06:16:06 2023 -0400
@@ -9,8 +9,9 @@
 std::vector<int> get_all_pids();
 std::string get_process_name(int pid);
 std::vector<std::string> get_open_files(int pid);
+std::vector<std::string> filter_system_files(const std::vector<std::string>& source);
 std::unordered_map<int, std::vector<std::string>> get_all_open_files();
 
 }
 
-#endif // __animia__animia_h
\ No newline at end of file
+#endif // __animia__animia_h
--- a/dep/animia/include/win32.h	Fri Sep 29 15:52:31 2023 -0400
+++ b/dep/animia/include/win32.h	Sun Oct 01 06:16:06 2023 -0400
@@ -9,6 +9,7 @@
 std::vector<int> get_all_pids();
 std::string get_process_name(int pid);
 std::vector<std::string> get_open_files(int pid);
+std::vector<std::string> filter_system_files(const std::vector<std::string>& source);
 std::unordered_map<int, std::vector<std::string>> get_all_open_files();
 
 }
--- a/dep/animia/src/bsd.cpp	Fri Sep 29 15:52:31 2023 -0400
+++ b/dep/animia/src/bsd.cpp	Sun Oct 01 06:16:06 2023 -0400
@@ -1,23 +1,23 @@
 /**
- * bsd.cpp 
+ * bsd.cpp
  *  - provides support for most* versions of BSD
- *  - this *should* also work for OS X
+ *  - 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 <vector>
-#include <string>
-#include <unordered_map>
-#include <sys/types.h>
-#include <sys/sysctl.h>
-#include <sys/user.h>
+ **/
 #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>
+#	include <libutil.h>
 #elif defined(__APPLE__)
-#include <libproc.h>
+#	include <libproc.h>
 #endif
 
 namespace Animia::Unix {
@@ -26,77 +26,81 @@
    ...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 };
+	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() */
-    sysctl((int*)name, (sizeof(name) / sizeof(*name)) - 1, NULL, &length, NULL, 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>();
+	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();
-    }
+	/* 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);
+	/* 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;
+	return ret;
 }
 
 std::string get_process_name(int pid) {
-    std::string ret;
+	std::string ret;
 #ifdef __FreeBSD__
-    struct kinfo_proc* proc = kinfo_getproc(pid);
-    if (!proc) {
-        return "";
-    ret = proc->ki_comm;
-    free(proc);
+	struct kinfo_proc* proc = kinfo_getproc(pid);
+	if (!proc)
+		return "";
+	ret = proc->ki_comm;
+	free(proc);
 #elif defined(__APPLE__)
-    struct proc_bsdinfo proc;
+	struct proc_bsdinfo proc;
 
-    int st = proc_pidinfo(pid, PROC_PIDTBSDINFO, 0, &proc, PROC_PIDTBSDINFO_SIZE);
-    if (st != PROC_PIDTBSDINFO_SIZE)
-        return "";
-    return proc.pbi_comm;
+	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;
+	return ret;
 }
 
 std::vector<std::string> get_open_files(int pid) {
-    // initialize buffer
-    std::vector<std::string> ret;
-    int bufsz = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, NULL, 0);
-    struct proc_fdinfo *info = (struct proc_fdinfo *)malloc(bufsz);
-    proc_pidinfo(pid, PROC_PIDLISTFDS, 0, info, bufsz);
+	/* 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;
+	int bufsz = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, NULL, 0);
+	struct proc_fdinfo* info = (struct proc_fdinfo*)malloc(bufsz);
+	proc_pidinfo(pid, PROC_PIDLISTFDS, 0, info, bufsz);
 
-    // iterate over stuff
-    for (int i = 0; i < bufsz/sizeof(info[0]); i++) {
-        if (info[i].proc_fdtype == PROX_FDTYPE_VNODE) {
-            struct vnode_fdinfowithpath vnodeInfo;
-            proc_pidfdinfo(pid, info[i].proc_fd, PROC_PIDFDVNODEPATHINFO, &vnodeInfo, PROC_PIDFDVNODEPATHINFO_SIZE);
-	    ret.push_back(vnodeInfo.pvip.vip_path);
-        }
-    }
-    return ret;
+	// 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;
+			proc_pidfdinfo(pid, info[i].proc_fd, PROC_PIDFDVNODEPATHINFO, &vnodeInfo, PROC_PIDFDVNODEPATHINFO_SIZE);
+			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;
+	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 Animia::Unix
--- a/dep/animia/src/linux.cpp	Fri Sep 29 15:52:31 2023 -0400
+++ b/dep/animia/src/linux.cpp	Sun Oct 01 06:16:06 2023 -0400
@@ -1,14 +1,14 @@
-#include <string>
-#include <vector>
+#include <algorithm>
+#include <fcntl.h>
+#include <filesystem>
 #include <fstream>
-#include <filesystem>
-#include <unordered_map>
 #include <iostream>
 #include <sstream>
-#include <unistd.h>
+#include <string>
 #include <sys/stat.h>
-#include <fcntl.h>
-#include <algorithm>
+#include <unistd.h>
+#include <unordered_map>
+#include <vector>
 
 #define PROC_LOCATION "/proc"
 
@@ -33,6 +33,7 @@
 	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;
@@ -79,17 +80,16 @@
 			/* get filename size */
 			int size = get_size_of_link(dir.path().string());
 			/* allocate buffer */
-			char* buf = (char*)calloc(size, sizeof(char));
-			//std::string buf('\0', size);
+			std::string buf(size, '\0');
+			// std::string buf('\0', size);
 			/* read filename to buffer */
-			int r = readlink(dir.path().c_str(), buf, size);
+			int r = readlink(dir.path().c_str(), &buf.front(), buf.length());
 
 			if (r < 0)
 				continue;
 			if (!is_regular_file(buf))
 				continue;
 			ret.push_back(buf);
-			free(buf);
 		}
 	} catch (std::filesystem::filesystem_error const& ex) {
 		if (ex.code().value() != 13) // 13 == permissions error, common with /proc, ignore
@@ -101,9 +101,9 @@
 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)
+	for (int i : pids)
 		map[i] = get_open_files(i);
 	return map;
 }
 
-}
+} // namespace Animia::Linux
--- a/dep/animia/src/main.cpp	Fri Sep 29 15:52:31 2023 -0400
+++ b/dep/animia/src/main.cpp	Sun Oct 01 06:16:06 2023 -0400
@@ -1,13 +1,12 @@
 #include "bsd.h"
 #include "linux.h"
 #include "win32.h"
-#include <vector>
 #include <string>
 #include <unordered_map>
+#include <vector>
 #ifdef __linux__
 #	define ON_LINUX
-#elif (defined(unix) || defined(__unix__) || defined(__unix) || \
-		(defined(__APPLE__) && defined(__MACH__)))
+#elif (defined(unix) || defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__)))
 #	define ON_UNIX
 #elif defined(_WIN32)
 #	define ON_WINDOWS
@@ -51,6 +50,14 @@
 #endif
 }
 
+std::vector<std::string> filter_system_files(const std::vector<std::string>& source) {
+#ifdef ON_WINDOWS
+	return Windows::filter_system_files(source);
+#else
+	return source;
+#endif
+}
+
 std::unordered_map<int, std::vector<std::string>> get_all_open_files() {
 #ifdef ON_UNIX
 	return Unix::get_all_open_files();
@@ -63,4 +70,4 @@
 #endif
 }
 
-}
+} // namespace Animia
--- a/dep/animia/src/win32.cpp	Fri Sep 29 15:52:31 2023 -0400
+++ b/dep/animia/src/win32.cpp	Sun Oct 01 06:16:06 2023 -0400
@@ -1,21 +1,29 @@
+/**
+ * win32.cpp
+ *  - provides support for Windows clients
+ *
+ **/
 #include "win32.h"
-#include <windows.h>
-#include <winternl.h>
+#include <fileapi.h>
+#include <handleapi.h>
+#include <iostream>
 #include <libloaderapi.h>
 #include <ntdef.h>
 #include <psapi.h>
-#include <tlhelp32.h>
-#include <fileapi.h>
-#include <handleapi.h>
-#include <vector>
-#include <iostream>
+#include <shlobj.h>
+#include <stdexcept>
 #include <string>
+#include <stringapiset.h>
+#include <tlhelp32.h>
 #include <unordered_map>
-#include <stdexcept>
-#include <locale>
-#include <codecvt>
+#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. */
+   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;
@@ -24,74 +32,40 @@
 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;
+		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];
+		ULONG_PTR NumberOfHandles;
+		ULONG_PTR Reserved;
+		SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX Handles[1];
 };
 
 namespace Animia::Windows {
 
-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) {
-	HANDLE handle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
-	if (!handle)
-		return "";
-
-	std::string ret(MAX_PATH, '\0');
-	if (!GetModuleBaseNameA(handle, 0, &ret.front(), ret.size()))
-		throw std::runtime_error("GetModuleBaseNameA failed: " + std::to_string(GetLastError()));
-	CloseHandle(handle);
-
-	return ret;
-}
-
 /* 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);
+	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));
+	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"));
+	static const auto func =
+	    reinterpret_cast<decltype(::NtQuerySystemInformation)*>(GetNTDLLAddress("NtQuerySystemInformation"));
 	return func(cls, sysinfo, len, retlen);
 }
 
@@ -112,11 +86,14 @@
 		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);
+					if (entry)
+						res.push_back(*entry);
 				} while (entry++, --handles);
 			}
 		}
@@ -132,51 +109,43 @@
 	return info;
 }
 
-std::string UnicodeStringToStdString(UNICODE_STRING string) {
-	ANSI_STRING result;
-	static const auto uc_to_ansi = reinterpret_cast<decltype(::RtlUnicodeStringToAnsiString)*>(GetNTDLLAddress("RtlUnicodeStringToAnsiString"));
-	uc_to_ansi(&result, &string, TRUE);
-	std::string ret = std::string(result.Buffer, result.Length);
-	static const auto free_ansi = reinterpret_cast<decltype(::RtlFreeAnsiString)*>(GetNTDLLAddress("RtlFreeAnsiString"));
-	free_ansi(&result);
+/* 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 UnicodeStringToStdString(info.TypeName);
+	return UnicodeStringToUtf8(info.TypeName);
 }
 
-/* GetFinalPathNameByHandleA literally just doesn't work */
 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);
-
-	std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> converter;
-
-	return converter.to_bytes(buffer);
-}
-
-std::string GetSystemDirectory() {
-	std::string windir = std::string(MAX_PATH, '\0');
-	::GetWindowsDirectoryA(&windir.front(), windir.length());
-	return "\\\\?\\" + windir;
-}
+	buffer.resize(buffer.find('\0'));
 
-/* This function is useless. I'm not exactly sure why, but whenever I try to compare the two
-   values, they both come up as different. I'm assuming it's just some Unicode BS I can't be bothered
-   to deal with. */
-bool IsSystemDirectory(const std::string& path) {
-	std::string path_l = path;
-	CharUpperBuffA(&path_l.front(), path_l.length());
-
-	std::string windir = GetSystemDirectory();
-	CharUpperBuffA(&windir.front(), windir.length());
-
-	return path_l.rfind(windir, 0) != std::string::npos;
+	return UnicodeStringToUtf8(buffer);
 }
 
 bool IsFileHandle(HANDLE handle, unsigned short object_type_index) {
@@ -192,37 +161,123 @@
 }
 
 bool IsFileMaskOk(ACCESS_MASK access_mask) {
-	/* this filters out any file handles that, legitimately,
-	   do not make sense (for what we're using it for)
-	
-	   shoutout to erengy for having these in Anisthesia */
-
 	if (!(access_mask & FILE_READ_DATA))
 		return false;
 
-	if ((access_mask & FILE_APPEND_DATA) ||
-	    (access_mask & FILE_WRITE_EA) ||
-	    (access_mask & FILE_WRITE_ATTRIBUTES))
+	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() || IsSystemDirectory(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))
+	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::unordered_map<int, std::vector<std::string>> map = get_all_open_files();
-	return map[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() {
@@ -255,4 +310,4 @@
 	return map;
 }
 
-}
+} // namespace Animia::Windows
--- a/dep/animia/test/main.cpp	Fri Sep 29 15:52:31 2023 -0400
+++ b/dep/animia/test/main.cpp	Sun Oct 01 06:16:06 2023 -0400
@@ -2,10 +2,14 @@
 #include <iostream>
 
 int main() {
-	std::unordered_map<int, std::vector<std::string>> map = Animia::get_all_open_files();
-	for (int i = 0; i < map.size(); i++) {
-		for (std::string s: map[i]) {
-			std::cout << Animia::get_process_name(i) << " (" << i << "): " << s << "\n";
+	/* getting all open files */
+	std::vector<int> pids = Animia::get_all_pids();
+	for (int i: pids) {
+		if (Animia::get_process_name(i) == "mpc-hc64.exe") {
+			std::vector<std::string> files = Animia::filter_system_files(Animia::get_open_files(i));
+			for (std::string s: files) {
+				std::cout << Animia::get_process_name(i) << " (" << i << "): " << s << "\n";
+			}
 		}
 	}
 }
--- a/include/core/config.h	Fri Sep 29 15:52:31 2023 -0400
+++ b/include/core/config.h	Sun Oct 01 06:16:06 2023 -0400
@@ -33,7 +33,12 @@
 		} anilist;
 };
 
+#define WIDEIFY_EX(x) L ## x
+#define WIDEIFY(x) WIDEIFY_EX(x)
 #define CONFIG_DIR      "minori"
+#define CONFIG_WDIR     WIDEIFY(CONFIG_DIR)
 #define CONFIG_NAME     "config.json"
+#define CONFIG_WNAME    WIDEIFY(CONFIG_NAME)
+
 #define MAX_LINE_LENGTH 256
 #endif // __core__config_h
--- a/include/core/strings.h	Fri Sep 29 15:52:31 2023 -0400
+++ b/include/core/strings.h	Sun Oct 01 06:16:06 2023 -0400
@@ -21,5 +21,8 @@
 std::string ToUpper(const std::string& string);
 std::string ToLower(const std::string& string);
 
+std::wstring ToWstring(const std::string& string);
+std::string ToUtf8String(const std::wstring& wstring);
+
 };     // namespace Strings
 #endif // __core__strings_h
\ No newline at end of file
--- a/include/gui/dialog/settings.h	Fri Sep 29 15:52:31 2023 -0400
+++ b/include/gui/dialog/settings.h	Sun Oct 01 06:16:06 2023 -0400
@@ -16,7 +16,7 @@
 	public:
 		SettingsPage(QWidget* parent = nullptr, QString title = "");
 		void SetTitle(QString title);
-		virtual void SaveInfo();
+		virtual void SaveInfo() = 0;
 		void AddTab(QWidget* tab, QString title = "");
 
 	private:
--- a/src/core/filesystem.cpp	Fri Sep 29 15:52:31 2023 -0400
+++ b/src/core/filesystem.cpp	Sun Oct 01 06:16:06 2023 -0400
@@ -18,6 +18,7 @@
 
 #include "core/filesystem.h"
 #include "core/config.h"
+#include "core/strings.h"
 #include <limits.h>
 
 namespace Filesystem {
@@ -36,7 +37,7 @@
 		end = _path.find(DELIM, start);
 		temp.append(_path.substr(start, end - start));
 #ifdef WIN32
-		if (!CreateDirectoryA(temp.c_str(), NULL) && GetLastError() == ERROR_PATH_NOT_FOUND)
+		if (!CreateDirectoryW(Strings::ToWstring(temp).c_str(), NULL) && GetLastError() == ERROR_PATH_NOT_FOUND)
 			/* ERROR_PATH_NOT_FOUND should NOT happen here */
 			return false;
 #else
@@ -51,7 +52,8 @@
 
 bool Path::Exists() const {
 #if WIN32
-	return GetFileAttributes(_path.c_str()) != INVALID_FILE_ATTRIBUTES;
+	std::wstring buf = Strings::ToWstring(_path);
+	return GetFileAttributesW(buf.c_str()) != INVALID_FILE_ATTRIBUTES;
 #else
 	struct stat st;
 	return stat(_path.c_str(), &st) == 0;
@@ -87,9 +89,10 @@
 Path GetDotPath(void) {
 	std::string ret = "";
 #ifdef WIN32
-	char buf[PATH_MAX + 1];
-	if (SHGetFolderPathAndSubDir(NULL, CSIDL_APPDATA | CSIDL_FLAG_CREATE, NULL, 0, CONFIG_DIR, buf) == S_OK) {
-		ret += buf;
+	std::wstring buf(MAX_PATH, '\0');
+	if (SHGetFolderPathAndSubDirW(NULL, CSIDL_APPDATA | CSIDL_FLAG_CREATE, NULL, 0, CONFIG_WDIR, &buf.front()) == S_OK) {
+		buf.resize(buf.find('\0'));
+		ret += Strings::ToUtf8String(buf);
 	}
 #elif defined(MACOSX)
 	ret += osx::GetApplicationSupportDirectory();
--- a/src/core/strings.cpp	Fri Sep 29 15:52:31 2023 -0400
+++ b/src/core/strings.cpp	Sun Oct 01 06:16:06 2023 -0400
@@ -4,6 +4,7 @@
 #include "core/strings.h"
 #include <algorithm>
 #include <cctype>
+#include <codecvt>
 #include <locale>
 #include <string>
 #include <vector>
@@ -75,4 +76,14 @@
 	return result;
 }
 
+std::wstring ToWstring(const std::string& string) {
+	std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> converter;
+	return converter.from_bytes(string);
+}
+
+std::string ToUtf8String(const std::wstring& wstring) {
+	std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> converter;
+	return converter.to_bytes(wstring);
+}
+
 } // namespace Strings
--- a/src/gui/dark_theme.cpp	Fri Sep 29 15:52:31 2023 -0400
+++ b/src/gui/dark_theme.cpp	Sun Oct 01 06:16:06 2023 -0400
@@ -67,7 +67,7 @@
 		osx::SetToLightTheme();
 	else
 #endif
-	SetStyleSheet(Themes::LIGHT);
+		SetStyleSheet(Themes::LIGHT);
 }
 
 enum Themes GetCurrentOSTheme() {
--- a/src/gui/dialog/information.cpp	Fri Sep 29 15:52:31 2023 -0400
+++ b/src/gui/dialog/information.cpp	Sun Oct 01 06:16:06 2023 -0400
@@ -143,7 +143,7 @@
 		subsection->setFixedWidth(LAYOUT_ITEM_WIDTH); \
 		subsection->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred); \
 		subsection->layout()->setSpacing(LAYOUT_VERT_SPACING); \
-		subsection->layout()->setMargin(0); \
+		subsection->layout()->setContentsMargins(0, 0, 0, 0); \
 		x; \
 		layout->addWidget(subsection); \
 	}
@@ -153,7 +153,7 @@
 		QWidget* section = new QWidget(a); \
 		QHBoxLayout* layout = new QHBoxLayout(section); \
 		layout->setSpacing(LAYOUT_HORIZ_SPACING); \
-		layout->setMargin(0); \
+		layout->setContentsMargins(0, 0, 0, 0); \
 		x; \
 		layout->addStretch(); \
 		a->layout()->addWidget(section); \
@@ -165,7 +165,7 @@
 		subsection->setLayout(new QVBoxLayout); \
 		subsection->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); \
 		subsection->layout()->setSpacing(LAYOUT_VERT_SPACING); \
-		subsection->layout()->setMargin(0); \
+		subsection->layout()->setContentsMargins(0, 0, 0, 0); \
 		x; \
 		layout->addWidget(subsection); \
 	}
@@ -175,7 +175,7 @@
 		QWidget* section = new QWidget(a); \
 		QHBoxLayout* layout = new QHBoxLayout(section); \
 		layout->setSpacing(LAYOUT_HORIZ_SPACING); \
-		layout->setMargin(0); \
+		layout->setContentsMargins(0, 0, 0, 0); \
 		x; \
 		a->layout()->addWidget(section); \
 	}
@@ -319,7 +319,7 @@
 	QVBoxLayout* main_layout = new QVBoxLayout;
 	main_layout->addWidget(anime_title);
 	main_layout->addWidget(tabbed_widget);
-	main_layout->setMargin(0);
+	main_layout->setContentsMargins(0, 0, 0, 0);
 	main_widget->setLayout(main_layout);
 
 	QHBoxLayout* layout = new QHBoxLayout;
--- a/src/gui/dialog/settings.cpp	Fri Sep 29 15:52:31 2023 -0400
+++ b/src/gui/dialog/settings.cpp	Sun Oct 01 06:16:06 2023 -0400
@@ -37,7 +37,7 @@
 	tab_widget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding);
 
 	QVBoxLayout* layout = new QVBoxLayout;
-	layout->setMargin(0);
+	layout->setContentsMargins(0, 0, 0, 0);
 	layout->addWidget(page_title);
 	layout->addWidget(tab_widget);
 	setLayout(layout);
@@ -101,7 +101,7 @@
 	layout = new QHBoxLayout;
 	layout->addWidget(sidebar);
 	layout->addWidget(stacked);
-	layout->setMargin(0);
+	layout->setContentsMargins(0, 0, 0, 0);
 	widget->setLayout(layout);
 
 	QDialogButtonBox* button_box = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
--- a/src/gui/dialog/settings/application.cpp	Fri Sep 29 15:52:31 2023 -0400
+++ b/src/gui/dialog/settings/application.cpp	Sun Oct 01 06:16:06 2023 -0400
@@ -22,7 +22,7 @@
 	QVBoxLayout* double_click_layout = new QVBoxLayout;
 	double_click_layout->addWidget(dc_combo_box_label);
 	double_click_layout->addWidget(dc_combo_box);
-	double_click_layout->setMargin(0);
+	double_click_layout->setContentsMargins(0, 0, 0, 0);
 	double_click_widget->setLayout(double_click_layout);
 
 	/* Actions/Middle click */
@@ -34,7 +34,7 @@
 	QVBoxLayout* middle_click_layout = new QVBoxLayout;
 	middle_click_layout->addWidget(mc_combo_box_label);
 	middle_click_layout->addWidget(mc_combo_box);
-	middle_click_layout->setMargin(0);
+	middle_click_layout->setContentsMargins(0, 0, 0, 0);
 	middle_click_widget->setLayout(middle_click_layout);
 
 	/* Actions */
--- a/src/gui/pages/anime_list.cpp	Fri Sep 29 15:52:31 2023 -0400
+++ b/src/gui/pages/anime_list.cpp	Sun Oct 01 06:16:06 2023 -0400
@@ -445,7 +445,7 @@
 
 	QHBoxLayout* layout = new QHBoxLayout;
 	layout->addWidget(tree_view);
-	layout->setMargin(0);
+	layout->setContentsMargins(0, 0, 0, 0);
 	tree_widget->setLayout(layout);
 
 	/* Double click stuff */
--- a/src/gui/widgets/optional_date.cpp	Fri Sep 29 15:52:31 2023 -0400
+++ b/src/gui/widgets/optional_date.cpp	Sun Oct 01 06:16:06 2023 -0400
@@ -10,7 +10,7 @@
 
 OptionalDate::OptionalDate(bool enabled, QWidget* parent) : QWidget(parent) {
 	QHBoxLayout* layout = new QHBoxLayout(this);
-	layout->setMargin(0);
+	layout->setContentsMargins(0, 0, 0, 0);
 
 	_checkbox = new QCheckBox(this);
 	_checkbox->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
--- a/src/gui/widgets/sidebar.cpp	Fri Sep 29 15:52:31 2023 -0400
+++ b/src/gui/widgets/sidebar.cpp	Sun Oct 01 06:16:06 2023 -0400
@@ -26,7 +26,6 @@
 }
 
 void SideBar::SetCurrentItem(int index) {
-	qDebug() << AddSeparatorsToIndex(index) << "\n";
 	setCurrentRow(AddSeparatorsToIndex(index));
 }
 
--- a/src/gui/widgets/text.cpp	Fri Sep 29 15:52:31 2023 -0400
+++ b/src/gui/widgets/text.cpp	Sun Oct 01 06:16:06 2023 -0400
@@ -28,7 +28,7 @@
 	layout()->addWidget(static_text_title);
 	layout()->addWidget(static_text_line);
 	layout()->setSpacing(0);
-	layout()->setMargin(0);
+	layout()->setContentsMargins(0, 0, 0, 0);
 }
 
 void Header::SetTitle(QString title) {
@@ -50,13 +50,13 @@
 
 	content->layout()->addWidget(paragraph);
 	content->layout()->setSpacing(0);
-	content->layout()->setMargin(0);
+	content->layout()->setContentsMargins(0, 0, 0, 0);
 	content->setContentsMargins(12, 0, 0, 0);
 
 	layout()->addWidget(header);
 	layout()->addWidget(paragraph);
 	layout()->setSpacing(0);
-	layout()->setMargin(0);
+	layout()->setContentsMargins(0, 0, 0, 0);
 }
 
 Header* TextParagraph::GetHeader() {
@@ -93,14 +93,14 @@
 	content_layout->addWidget(labels, 0, Qt::AlignTop);
 	content_layout->addWidget(paragraph, 0, Qt::AlignTop);
 	content_layout->setSpacing(20);
-	content_layout->setMargin(0);
+	content_layout->setContentsMargins(0, 0, 0, 0);
 
 	content->setContentsMargins(12, 0, 0, 0);
 
 	layout()->addWidget(header);
 	layout()->addWidget(content);
 	layout()->setSpacing(0);
-	layout()->setMargin(0);
+	layout()->setContentsMargins(0, 0, 0, 0);
 }
 
 Header* LabelledTextParagraph::GetHeader() {
@@ -127,13 +127,13 @@
 
 	content->layout()->addWidget(paragraph);
 	content->layout()->setSpacing(0);
-	content->layout()->setMargin(0);
+	content->layout()->setContentsMargins(0, 0, 0, 0);
 	content->setContentsMargins(12, 0, 0, 0);
 
 	layout()->addWidget(header);
 	layout()->addWidget(content);
 	layout()->setSpacing(0);
-	layout()->setMargin(0);
+	layout()->setContentsMargins(0, 0, 0, 0);
 }
 
 Header* SelectableTextParagraph::GetHeader() {
--- a/src/gui/window.cpp	Fri Sep 29 15:52:31 2023 -0400
+++ b/src/gui/window.cpp	Sun Oct 01 06:16:06 2023 -0400
@@ -2,15 +2,15 @@
 #include "core/config.h"
 #include "core/session.h"
 #include "gui/dark_theme.h"
+#include "gui/dialog/about.h"
 #include "gui/dialog/settings.h"
-#include "gui/dialog/about.h"
 #include "gui/pages/anime_list.h"
+#include "gui/pages/history.h"
 #include "gui/pages/now_playing.h"
+#include "gui/pages/search.h"
+#include "gui/pages/seasons.h"
 #include "gui/pages/statistics.h"
-#include "gui/pages/seasons.h"
-#include "gui/pages/search.h"
 #include "gui/pages/torrents.h"
-#include "gui/pages/history.h"
 #include "gui/widgets/sidebar.h"
 #include "services/services.h"
 #include <QApplication>
@@ -19,6 +19,9 @@
 #include <QMenuBar>
 #include <QPlainTextEdit>
 #include <QStackedWidget>
+#include <QTimer>
+#include <QActionGroup>
+#include <QMessageBox>
 #include <QTextStream>
 #if MACOSX
 #	include "sys/osx/dark_theme.h"
@@ -79,9 +82,9 @@
 	menu->addSeparator();
 
 	action = menu->addAction(tr("Play &next episode"));
-	action->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_N));
+	action->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_N));
 	action = menu->addAction(tr("Play &random episode"));
-	action->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_R));
+	action->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_R));
 
 	menu->addSeparator();
 
@@ -92,7 +95,7 @@
 		Services::Synchronize();
 		((AnimeListWidget*)stack->widget((int)Pages::ANIME_LIST))->Refresh();
 	});
-	action->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_S));
+	action->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_S));
 
 	menu->addSeparator();