Update to spice2x-25-04-25 (pre-apply)

> broken commit
This commit is contained in:
[ ]
2025-05-07 00:25:31 +09:00
parent 04dee88276
commit c94de456b5
543 changed files with 78491 additions and 91698 deletions
+255 -1
View File
@@ -9,9 +9,22 @@
#include <winternl.h>
#include <ntstatus.h>
#include "cpuinfo_x86.h"
#include "util/libutils.h"
#include "util/logging.h"
#include "util/utils.h"
#include "util/unique_plain_ptr.h"
// redefinition of PROCESSOR_RELATIONSHIP; only win10 exposes EfficiencyClass field
// instead of setting _WIN32_WINNT to win10 we'll just redefine it to avoid the compat headache
typedef struct _PROCESSOR_RELATIONSHIP_WIN10 {
BYTE Flags;
BYTE EfficiencyClass;
BYTE Reserved[20];
WORD GroupCount;
GROUP_AFFINITY GroupMask[ANYSIZE_ARRAY];
} PROCESSOR_RELATIONSHIP_WIN10, *PPROCESSOR_RELATIONSHIP_WIN10;
/*
#ifndef NT_SUCCESS
@@ -19,6 +32,8 @@
#endif
*/
using namespace cpu_features;
namespace cpuutils {
typedef struct _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION {
@@ -37,8 +52,22 @@ namespace cpuutils {
PULONG ReturnLength
);
static NtQuerySystemInformation_t NtQuerySystemInformation = nullptr;
typedef BOOL (WINAPI *GetLogicalProcessorInformationEx_t)(
LOGICAL_PROCESSOR_RELATIONSHIP RelationshipType,
PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX Buffer,
PDWORD ReturnedLength
);
static GetLogicalProcessorInformationEx_t GetLogicalProcessorInformationEx = nullptr;
typedef void (WINAPI *GetCurrentProcessorNumberEx_t)(
PPROCESSOR_NUMBER ProcNumber
);
static GetCurrentProcessorNumberEx_t GetCurrentProcessorNumberEx = nullptr;
static size_t PROCESSOR_COUNT = 0;
static std::vector<SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION> PROCESSOR_STATES;
static USHORT PRIMARY_GROUP = UINT16_MAX;
static void init() {
@@ -67,12 +96,71 @@ namespace cpuutils {
GetSystemInfo(&info);
PROCESSOR_COUNT = info.dwNumberOfProcessors;
log_misc("cpuutils", "detected {} processors", PROCESSOR_COUNT);
done = true;
done = true;
// init processor states
get_load();
}
static void init_kernel32_routines() {
auto kernel32 = libutils::try_module("kernel32.dll");
if (kernel32 == nullptr) {
log_warning("cpuutils", "failed to find kernel32");
return;
}
if (GetLogicalProcessorInformationEx == nullptr) {
GetLogicalProcessorInformationEx = libutils::try_proc<GetLogicalProcessorInformationEx_t>(
kernel32, "GetLogicalProcessorInformationEx");
if (GetLogicalProcessorInformationEx == nullptr) {
log_warning("cpuutils", "GetLogicalProcessorInformationEx not found");
}
}
if (GetCurrentProcessorNumberEx == nullptr) {
GetCurrentProcessorNumberEx = libutils::try_proc<GetCurrentProcessorNumberEx_t>(
kernel32, "GetCurrentProcessorNumberEx");
if (GetCurrentProcessorNumberEx == nullptr) {
log_warning("cpuutils", "GetCurrentProcessorNumberEx not found");
}
}
// figure out the Primary Group for this process
// if GetCurrentProcessorNumberEx isn't supported, assume OS only allows single-group
// https://learn.microsoft.com/en-us/windows/win32/procthread/processor-groups
if (GetCurrentProcessorNumberEx != nullptr && PRIMARY_GROUP == UINT16_MAX) {
PROCESSOR_NUMBER ProcNumber;
GetCurrentProcessorNumberEx(&ProcNumber);
PRIMARY_GROUP = ProcNumber.Group;
log_misc("cpuutils", "primary group: {}", PRIMARY_GROUP);
}
}
void print_cpu_features() {
log_misc("cpuinfo", "dumping processor information...");
const auto cpu = GetX86Info();
// dump cpu id
log_misc("cpuinfo", "{}, {} ({})",
cpu.vendor,
cpu.brand_string,
GetX86MicroarchitectureName(GetX86Microarchitecture(&cpu)));
log_misc("cpuinfo", "family 0x{:x}, model 0x{:x}, stepping 0x{:x}", cpu.family, cpu.model, cpu.stepping);
// dump features
std::string features = "";
for (size_t i = 0; i < X86_LAST_; ++i) {
if (GetX86FeaturesEnumValue(&cpu.features, static_cast<X86FeaturesEnum>(i))) {
features += GetX86FeaturesEnumName(static_cast<X86FeaturesEnum>(i));
features += " ";
}
}
log_misc("cpuinfo", "features : {}", features);
log_misc("cpuinfo", " SSE4.2 : {}", cpu.features.sse4_2 ? "supported" : "NOT supported");
log_misc("cpuinfo", " AVX2 : {}", cpu.features.avx2 ? "supported" : "NOT supported");
}
std::vector<float> get_load() {
// lazy init
@@ -131,4 +219,170 @@ namespace cpuutils {
// return data
return cpu_load_values;
}
void set_processor_priority(std::string priority) {
DWORD process_priority = HIGH_PRIORITY_CLASS;
if (priority == "belownormal") {
process_priority = BELOW_NORMAL_PRIORITY_CLASS;
} else if (priority == "normal") {
process_priority = NORMAL_PRIORITY_CLASS;
} else if (priority == "abovenormal") {
process_priority = ABOVE_NORMAL_PRIORITY_CLASS;
// high is the default so it's skipped!
} else if (priority == "realtime") {
process_priority = REALTIME_PRIORITY_CLASS;
}
// while testing, realtime only worked when being set to high before
if (process_priority == REALTIME_PRIORITY_CLASS) {
if (!SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS)) {
log_warning("cpuutils", "could not set process priority to high, GLE:{}", GetLastError());
}
}
if (!SetPriorityClass(GetCurrentProcess(), process_priority)) {
log_warning("cpuutils", "could not set process priority to {}, GLE:{}", priority, GetLastError());
} else {
log_info("cpuutils", "SetPriorityClass succeeded, set priority to {}", priority);
}
}
void set_processor_affinity(CpuEfficiencyClass eff_class) {
DWORD returned_length;
BOOL result;
init_kernel32_routines();
if (GetLogicalProcessorInformationEx == nullptr) {
return;
}
// determine buffer size
returned_length = 0;
result = GetLogicalProcessorInformationEx(
RelationProcessorCore,
nullptr,
&returned_length);
if (result || GetLastError() != ERROR_INSUFFICIENT_BUFFER || returned_length == 0) {
log_warning("cpuutils", "unexpected return from GetLogicalProcessorInformationEx");
return;
}
const auto buffer =
util::make_unique_plain<SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX>(returned_length);
result = GetLogicalProcessorInformationEx(
RelationProcessorCore,
buffer.get(),
&returned_length);
if (!result) {
log_warning(
"cpuutils",
"unexpected return from GetLogicalProcessorInformationEx, GLE:{}",
GetLastError());
return;
}
KAFFINITY affinity_eff_0 = 0;
KAFFINITY affinity_eff_non_0 = 0;
DWORD_PTR byte_offset = 0;
PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX procs = buffer.get();
while (byte_offset < returned_length) {
// ignore processors outside of primary group for this processor
// (GroupCount is always 1 for RelationProcessorCore)
if (procs->Processor.GroupMask[0].Group != PRIMARY_GROUP) {
continue;
}
// check efficiency class and add up affinities
PPROCESSOR_RELATIONSHIP_WIN10 relationship =
(PPROCESSOR_RELATIONSHIP_WIN10)&procs->Processor;
if (relationship->EfficiencyClass == 0) {
affinity_eff_0 |= procs->Processor.GroupMask[0].Mask;
} else {
affinity_eff_non_0 |= procs->Processor.GroupMask[0].Mask;
}
// debug info
// log_info("cpuutils", "eff = {}, 0x{:x}", relationship->EfficiencyClass, procs->Processor.GroupMask[0].Mask);
// move onto next entry
byte_offset += procs->Size;
procs = (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX)(((PBYTE)procs) + procs->Size);
}
if (affinity_eff_non_0 == 0) {
log_warning("cpuutils", "not a heterogeneous system, or OS doesn't understand it; ignoring -processefficiency");
} else if (eff_class == CpuEfficiencyClass::PreferECores) {
log_info("cpuutils", "force efficient cores: 0x{:x}", affinity_eff_0);
set_processor_affinity(affinity_eff_0, true);
} else if (eff_class == CpuEfficiencyClass::PreferPCores) {
log_info("cpuutils", "force performant cores: 0x{:x}", affinity_eff_non_0);
set_processor_affinity(affinity_eff_non_0, true);
}
}
void set_processor_affinity(uint64_t affinity, bool is_user_override) {
// two possible sources: user sets a parameter, or game needs errata
static bool is_user_override_set = false;
if (is_user_override) {
is_user_override_set = true;
} else if (is_user_override_set) {
log_misc(
"cpuutils",
"ignoring call to set_processor_affinity for 0x{:x}, user already set affinity override",
affinity);
return;
}
// get system affinity
DWORD_PTR sys_affinity;
DWORD_PTR proc_affinity;
if (GetProcessAffinityMask(GetCurrentProcess(), &proc_affinity, &sys_affinity) != 0) {
log_misc(
"cpuutils",
"GetProcessAffinityMask: process=0x{:x}, system=0x{:x}",
proc_affinity, sys_affinity);
} else {
const auto gle = GetLastError();
if (gle == ERROR_INVALID_PARAMETER) {
log_fatal("cpuutils", "GetProcessAffinityMask failed, GLE: ERROR_INVALID_PARAMETER.");
} else {
log_fatal("cpuutils", "GetProcessAffinityMask failed, GLE: {}", gle);
}
}
DWORD_PTR affinity_to_apply = sys_affinity & (DWORD_PTR)affinity;
log_info(
"cpuutils",
"affinity mask: 0x{:x} & 0x{:x} = 0x{:x}",
sys_affinity, (DWORD_PTR)affinity, affinity_to_apply);
if (affinity_to_apply == proc_affinity) {
log_misc(
"cpuutils",
"no need to call GetProcessAffinityMask, process affinity is already the desired value");
return;
}
// call SetProcessAffinityMask; failures are fatal
if (SetProcessAffinityMask(GetCurrentProcess(), affinity_to_apply) != 0) {
log_info(
"cpuutils",
"SetProcessAffinityMask succeeded, affinity set to 0x{:x}",
affinity_to_apply);
} else {
const auto gle = GetLastError();
if (gle == ERROR_INVALID_PARAMETER) {
log_fatal(
"cpuutils",
"SetProcessAffinityMask failed, provided 0x{:x}, GLE: ERROR_INVALID_PARAMETER.",
affinity_to_apply);
} else {
log_fatal(
"cpuutils",
"SetProcessAffinityMask failed, provided 0x{:x}, GLE: {}",
affinity_to_apply,
gle);
}
}
}
}
+10
View File
@@ -1,8 +1,18 @@
#pragma once
#include <vector>
#include <string>
#include <cstdint>
namespace cpuutils {
enum class CpuEfficiencyClass {
PreferECores,
PreferPCores
};
std::vector<float> get_load();
void print_cpu_features();
void set_processor_priority(std::string priority);
void set_processor_affinity(uint64_t affinity, bool is_user_override);
void set_processor_affinity(CpuEfficiencyClass eff_class);
}
+1
View File
@@ -1,5 +1,6 @@
#pragma once
#include <cstdint>
#include <cstddef>
#include <string>
+180
View File
@@ -0,0 +1,180 @@
#include "execexe.h"
#include "util/logging.h"
#include "util/libutils.h"
#include "util/utils.h"
#include "util/detour.h"
namespace execexe {
static HMODULE execexe_module = nullptr;
static decltype(&LoadLibraryW) execexe_LoadLibraryW = nullptr;
static decltype(&GetModuleHandleW) execexe_GetModuleHandleW = nullptr;
static decltype(&GetProcAddress) execexe_GetProcAddress = nullptr;
static decltype(&CreateFileA) execexe_CreateFileA = nullptr;
static decltype(&CreateFileW) execexe_CreateFileW = nullptr;
static decltype(&CloseHandle) execexe_CloseHandle = nullptr;
static std::wstring plugins_dir;
static acioemu::ACIOHandle *acio = nullptr;
static std::wstring port_name;
static bool port_opened = false;
static HANDLE WINAPI execexe_CreateFileA_hook(LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode,
LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition,
DWORD dwFlagsAndAttributes, HANDLE hTemplateFile) {
const auto lpFileNameW = s2ws(lpFileName);
if (lpFileNameW == port_name) {
if (!port_opened) {
port_opened = acio->open(port_name.c_str());
} else {
log_info("execexe", "ignored handle open. ({})", ws2s(port_name));
}
SetLastError(0);
return (HANDLE) acio;
}
return execexe_CreateFileA(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes,
dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
}
static HANDLE WINAPI execexe_CreateFileW_hook(LPCWSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode,
LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition,
DWORD dwFlagsAndAttributes, HANDLE hTemplateFile) {
if (lpFileName == port_name && acio->open(lpFileName)) {
if (!port_opened) {
port_opened = acio->open(port_name.c_str());
} else {
log_info("execexe", "ignored handle open. ({})", ws2s(port_name));
}
SetLastError(0);
return (HANDLE) acio;
} else {
return execexe_CreateFileW(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes,
dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
}
}
static WINBOOL WINAPI execexe_CloseHandle_hook(HANDLE hObject) {
if (hObject == acio && port_opened) {
log_info("execexe", "ignored handle close. ({})", ws2s(port_name));
return TRUE;
}
return execexe_CloseHandle(hObject);
}
HMODULE init() {
execexe_module = libutils::load_library("execexe.dll");
execexe_LoadLibraryW = libutils::get_proc<decltype(&LoadLibraryW)>(execexe_module, MAKEINTRESOURCE(34));
execexe_GetModuleHandleW = libutils::get_proc<decltype(&GetModuleHandleW)>(execexe_module, MAKEINTRESOURCE(25));
execexe_GetProcAddress = libutils::get_proc<decltype(&GetProcAddress)>(execexe_module, MAKEINTRESOURCE(27));
execexe_CloseHandle = libutils::get_proc<decltype(&CloseHandle)>(execexe_module, MAKEINTRESOURCE(7));
execexe_CreateFileA = libutils::get_proc<decltype(&CreateFileA)>(execexe_module, MAKEINTRESOURCE(9));
execexe_CreateFileW = libutils::get_proc<decltype(&CreateFileW)>(execexe_module, MAKEINTRESOURCE(11));
auto module_path = libutils::module_file_name(nullptr);
module_path = module_path.replace_extension("");
module_path = module_path.replace_filename(module_path.filename().wstring() + L"_Data");
plugins_dir = (module_path / L"Plugins" / L"x86_64").wstring() + L"\\";
return execexe_module;
}
void init_port_hook(const std::wstring &portName, acioemu::ACIOHandle *acioHandle) {
static bool init = false;
if (init)
return;
init = true;
port_name = portName;
acio = acioHandle;
detour::trampoline_try("execexe.dll", MAKEINTRESOURCE(7),
execexe_CloseHandle_hook, &execexe_CloseHandle);
detour::trampoline_try("execexe.dll", MAKEINTRESOURCE(9),
execexe_CreateFileA_hook, &execexe_CreateFileA);
detour::trampoline_try("execexe.dll", MAKEINTRESOURCE(11),
execexe_CreateFileW_hook, &execexe_CreateFileW);
}
HMODULE load_library(const char *module_name, bool fatal) {
std::wstring module_name_w = s2ws(module_name);
std::wstring plugin_path = plugins_dir + module_name_w;
HMODULE module = execexe_LoadLibraryW(plugin_path.c_str());
if (module != nullptr) {
return module;
}
module = execexe_LoadLibraryW(module_name_w.c_str());
if (module != nullptr) {
return module;
}
if (fatal) {
log_fatal("execexe", "failed to load library {}", module_name);
}
return nullptr;
}
HMODULE get_module(const char *module_name, bool fatal) {
std::wstring module_name_w = s2ws(module_name);
std::wstring plugin_path = plugins_dir + module_name_w;
HMODULE module = execexe_GetModuleHandleW(plugin_path.c_str());
if (module != nullptr) {
return module;
}
module = execexe_GetModuleHandleW(module_name_w.c_str());
if (module != nullptr) {
return module;
}
if (fatal) {
log_fatal("execexe", "failed to get module {}", module_name);
}
return nullptr;
}
FARPROC get_proc(HMODULE module, const char *proc_name, bool fatal) {
FARPROC proc = execexe_GetProcAddress(module, proc_name);
if (proc != nullptr) {
return proc;
}
if (fatal) {
log_fatal("execexe", "proc {} not found", proc_name);
}
return nullptr;
}
bool trampoline(const char *dll, const char *func, void *hook, void **orig) {
HMODULE module = get_module(dll);
FARPROC proc = get_proc(module, func);
return detour::trampoline(
reinterpret_cast<void *>(proc),
hook,
orig
);
}
bool trampoline_try(const char *dll, const char *func, void *hook, void **orig) {
HMODULE module = get_module(dll, false);
if (module == nullptr) {
return false;
}
FARPROC proc = get_proc(module, func, false);
if (proc == nullptr) {
return false;
}
return detour::trampoline(
reinterpret_cast<void *>(proc),
hook,
orig
);
}
}
+38
View File
@@ -0,0 +1,38 @@
#pragma once
#include <string>
#include <windows.h>
#include "acioemu/handle.h"
namespace execexe {
HMODULE init();
void init_port_hook(const std::wstring &portName, acioemu::ACIOHandle *acioHandle);
HMODULE load_library(const char *module_name, bool fatal = true);
FARPROC get_proc(HMODULE module, const char *proc_name, bool fatal = true);
HMODULE get_module(const char *module_name, bool fatal = true);
bool trampoline(const char *dll, const char *func, void *hook, void **orig);
bool trampoline_try(const char *dll, const char *func, void *hook, void **orig);
template<typename T>
inline T get_proc(HMODULE module, const char *proc_name, bool fatal = true) {
return reinterpret_cast<T>(get_proc(module, proc_name, fatal));
}
template<typename T>
inline bool trampoline(const char *dll, const char *func, T hook, T *orig) {
return trampoline(
dll,
func,
reinterpret_cast<void *>(hook),
reinterpret_cast<void **>(orig));
}
template<typename T>
inline bool trampoline_try(const char *dll, const char *func, T hook, T *orig) {
return trampoline_try(
dll,
func,
reinterpret_cast<void *>(hook),
reinterpret_cast<void **>(orig));
}
}
+64
View File
@@ -171,6 +171,20 @@ bool fileutils::dir_create_recursive(const std::filesystem::path &dir_path) {
return ret && !err;
}
bool fileutils::dir_create_recursive_log(const std::string_view &module, const std::filesystem::path &dir_path) {
std::error_code err;
auto ret = std::filesystem::create_directories(dir_path, err);
if (err) {
log_warning(module, "failed to create directory (recursive) '{}': {}", dir_path.string(), err.message());
} else if (ret) {
log_misc(module, "created directory (recursive) '{}'", dir_path.string());
}
return ret && !err;
}
void fileutils::dir_scan(const std::string &path, std::vector<std::string> &vec, bool recursive) {
// check directory
@@ -250,3 +264,53 @@ std::vector<uint8_t> *fileutils::bin_read(const std::filesystem::path &path) {
}
return contents;
}
std::filesystem::path fileutils::get_config_file_path(const std::string module, const std::string filename, bool* file_exists) {
// try %appdata%\spice2x path first, if it exists
const auto appdata_spice2x = std::filesystem::path(_wgetenv(L"APPDATA")) / "spice2x" / filename;
if (fileutils::file_exists(appdata_spice2x)) {
log_info(module, "loading config from %appdata%\\spice2x\\{}", filename);
if (file_exists) {
*file_exists = true;
}
return appdata_spice2x;
}
// fallback to older %appdata% path (older spice2x or mainline spicetools), if it exists
const auto appdata = std::filesystem::path(_wgetenv(L"APPDATA")) / filename;
if (fileutils::file_exists(appdata)) {
log_info(module, "loading config from %appdata%\\{}", filename);
if (file_exists) {
*file_exists = true;
}
return appdata;
}
// prefer new path if no existing file found
if (file_exists) {
*file_exists = false;
}
return appdata_spice2x;
}
bool fileutils::write_config_file(const std::string_view &module, const std::filesystem::path path, std::string text) {
// attempt to undo %appdata% expansion to hide user name
const auto appdata = std::filesystem::path(_wgetenv(L"APPDATA")).string();
auto censored = path.string();
const auto substr_offset = censored.find(appdata);
if (substr_offset != std::string::npos) {
censored.replace(substr_offset, appdata.length(), "%appdata%");
}
// create directory path up to where the config file lives
if (!path.parent_path().empty() && !std::filesystem::exists(path.parent_path())) {
log_misc(module, "creating directory path to config file: {}", censored);
if (!fileutils::dir_create_recursive(path.parent_path())) {
return false;
}
}
// save file
log_info(module, "saving config file: {}", censored);
return fileutils::text_write(path, text);
}
+4
View File
@@ -25,6 +25,7 @@ namespace fileutils {
bool dir_create(const std::filesystem::path &dir_path);
bool dir_create_log(const std::string_view &module, const std::filesystem::path &dir_path);
bool dir_create_recursive(const std::filesystem::path &dir_path);
bool dir_create_recursive_log(const std::string_view &module, const std::filesystem::path &dir_path);
void dir_scan(const std::string &path, std::vector<std::string> &vec, bool recursive);
// IO
@@ -32,4 +33,7 @@ namespace fileutils {
std::string text_read(const std::filesystem::path &file_path);
bool bin_write(const std::filesystem::path &path, uint8_t *data, size_t len);
std::vector<uint8_t> *bin_read(const std::filesystem::path &path);
std::filesystem::path get_config_file_path(const std::string module, const std::string filename, bool* file_exists=nullptr);
bool write_config_file(const std::string_view &module, const std::filesystem::path path, std::string text);
}
+45 -19
View File
@@ -7,6 +7,7 @@
#include "logging.h"
#include "utils.h"
#include "peb.h"
#include "util/fileutils.h"
std::filesystem::path libutils::module_file_name(HMODULE module) {
std::wstring buf;
@@ -29,25 +30,18 @@ std::filesystem::path libutils::module_file_name(HMODULE module) {
static inline void load_library_fail(const std::string &file_name, bool fatal) {
std::string info_str { fmt::format(
"\n\nPlease check if {} exists and the permissions are fine.\n"
"If the problem still persists, try installing:\n"
"* DirectX End-User Runtimes (June 2010) \n"
" https://www.microsoft.com/en-us/download/details.aspx?id=8109 \n"
"* Microsoft Visual C++ Redistributable Runtimes (*all* versions, x86 *AND* x64)\n"
" https://github.com/abbodi1406/vcredist (recommended All-In-One installer)\n"
" You may need to run the installer *multiple times* and reboot after each install\n"
"* Running Windows 10 \"N\" or \"KN\" Editions?\n"
" Grab: https://www.microsoft.com/en-us/software-download/mediafeaturepack \n"
" Check: https://support.microsoft.com/en-us/help/4562569/media-feature-pack-for-windows-10-n-may-2020 \n"
"* Running Windows 7 \"N\" or \"KN\" Editions?\n"
" x86: https://web.archive.org/web/20190810145509/https://download.microsoft.com/download/B/9/B/B9BED058-8669-490E-BA61-D502E4E8BEB1/Windows6.1-KB968211-x86-RefreshPkg.msu \n"
" x64: https://web.archive.org/web/20190810145509/https://download.microsoft.com/download/B/9/B/B9BED058-8669-490E-BA61-D502E4E8BEB1/Windows6.1-KB968211-x64-RefreshPkg.msu \n"
"* Still have problems after installing above?\n"
" Ensure you do NOT have multiple copies of the game DLLs\n"
" Ensure the game DLLs are in the correct place, and double check -modules parameter\n"
" Certain games require specific NVIDIA DLLs when running with AMD GPUs\n"
" Find the missing dependency using:\n"
" https://github.com/lucasg/Dependencies (recommended for most) \n"
" http://www.dependencywalker.com/ (for old OS) \n"
"\n"
"* If the problem still persists, try installing things on this list:\n"
" https://github.com/spice2x/spice2x.github.io/wiki/DLL-Dependencies \n"
"\n"
"* Still have problems after installing from above and rebooting PC?\n"
" Avoid manually specifying DLL path (-exec) and module directory (-modules); let spice2x auto-detect unless you have a good reason not to\n"
" Ensure you do NOT have multiple copies of the game DLLs (e.g., in contents and in contents\\modules)\n"
" Certain games require specific NVIDIA DLLs when running with AMD/Intel GPUs (hint: look inside stub directory for DLLs)\n"
"\n"
"* (For advanced users) if none of the above helps, find the missing dependency using:\n"
" https://github.com/lucasg/Dependencies (recommended for most) \n"
" http://www.dependencywalker.com/ (for old OS) \n"
, file_name) };
if (fatal) {
log_fatal("libutils", "{}", info_str);
@@ -330,3 +324,35 @@ intptr_t libutils::offset2rva(const std::filesystem::path &path, intptr_t offset
return rva;
}
void libutils::check_duplicate_dlls() {
const auto &spice_bin_path = libutils::module_file_name(nullptr).parent_path();
if (MODULE_PATH == spice_bin_path) {
return;
}
for (const auto &file : std::filesystem::directory_iterator(MODULE_PATH)) {
const auto &filename = file.path().filename();
const auto extension = strtolower(filename.extension().string());
if (extension == ".dll" &&
fileutils::file_exists(spice_bin_path / filename)) {
log_warning(
"libutils",
"DLL CONFLICT WARNING\n\n\n"
"-------------------------------------------------------------------\n"
"WARNING - WARNING - WARNING - WARNING - WARNING - WARNING - WARNING\n"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"
"{} exists in BOTH of these directories:\n\n"
" 1. {}\n"
" 2. {}\n\n"
"due to Windows DLL load order rules, #1 will load instead of #2.\n"
"this has unintended consequences and may crash your game!\n"
"resolve the conflict by deleting the stale copy of the DLL\n"
"-------------------------------------------------------------------\n\n\n",
filename.string(),
(spice_bin_path / filename).string(),
(MODULE_PATH / filename).string());
}
}
}
+2
View File
@@ -24,6 +24,8 @@ namespace libutils {
return try_library(module_name.c_str());
}
void check_duplicate_dlls();
// get module handle helpers
HMODULE get_module(const char *module_name);
HMODULE try_module(const char *module_name);
+2 -2
View File
@@ -82,9 +82,9 @@ struct fmt::formatter<fmt_hresult> {
LOG_FORMAT("W", module, format_str, ## __VA_ARGS__), logger::Style::YELLOW)
#define log_fatal(module, format_str, ...) { \
logger::push(LOG_FORMAT("F", module, format_str, ## __VA_ARGS__), logger::Style::RED); \
\
logger::push(LOG_FORMAT("F", "spice", "encountered a fatal error, you can close the window or press ctrl + c"), logger::Style::RED); \
launcher::stop_subsystems(); \
Sleep(30000); \
Sleep(10000); \
launcher::kill(); \
std::terminate(); \
} ((void) 0 )
+89
View File
@@ -111,4 +111,93 @@ namespace netutils {
free(adapter_addresses);
return return_addresses;
}
/*!
*
* HTTP Status Codes - C++ Variant
*
* https://github.com/j-ulrich/http-status-codes-cpp
*
* \version 1.5.0
* \author Jochen Ulrich <jochenulrich@t-online.de>
* \copyright Licensed under Creative Commons CC0 (http://creativecommons.org/publicdomain/zero/1.0/)
*/
std::string http_status_reason_phrase(int code) {
switch (code) {
//####### 1xx - Informational #######
case 100: return "Continue";
case 101: return "Switching Protocols";
case 102: return "Processing";
case 103: return "Early Hints";
//####### 2xx - Successful #######
case 200: return "OK";
case 201: return "Created";
case 202: return "Accepted";
case 203: return "Non-Authoritative Information";
case 204: return "No Content";
case 205: return "Reset Content";
case 206: return "Partial Content";
case 207: return "Multi-Status";
case 208: return "Already Reported";
case 226: return "IM Used";
//####### 3xx - Redirection #######
case 300: return "Multiple Choices";
case 301: return "Moved Permanently";
case 302: return "Found";
case 303: return "See Other";
case 304: return "Not Modified";
case 305: return "Use Proxy";
case 307: return "Temporary Redirect";
case 308: return "Permanent Redirect";
//####### 4xx - Client Error #######
case 400: return "Bad Request";
case 401: return "Unauthorized";
case 402: return "Payment Required";
case 403: return "Forbidden";
case 404: return "Not Found";
case 405: return "Method Not Allowed";
case 406: return "Not Acceptable";
case 407: return "Proxy Authentication Required";
case 408: return "Request Timeout";
case 409: return "Conflict";
case 410: return "Gone";
case 411: return "Length Required";
case 412: return "Precondition Failed";
case 413: return "Content Too Large";
case 414: return "URI Too Long";
case 415: return "Unsupported Media Type";
case 416: return "Range Not Satisfiable";
case 417: return "Expectation Failed";
case 418: return "I'm a teapot";
case 421: return "Misdirected Request";
case 422: return "Unprocessable Content";
case 423: return "Locked";
case 424: return "Failed Dependency";
case 425: return "Too Early";
case 426: return "Upgrade Required";
case 428: return "Precondition Required";
case 429: return "Too Many Requests";
case 431: return "Request Header Fields Too Large";
case 451: return "Unavailable For Legal Reasons";
//####### 5xx - Server Error #######
case 500: return "Internal Server Error";
case 501: return "Not Implemented";
case 502: return "Bad Gateway";
case 503: return "Service Unavailable";
case 504: return "Gateway Timeout";
case 505: return "HTTP Version Not Supported";
case 506: return "Variant Also Negotiates";
case 507: return "Insufficient Storage";
case 508: return "Loop Detected";
case 510: return "Not Extended";
case 511: return "Network Authentication Required";
default: return std::string();
}
}
}
+1
View File
@@ -5,4 +5,5 @@
namespace netutils {
std::vector<std::string> get_local_addresses();
std::string http_status_reason_phrase(int code);
}
+46
View File
@@ -0,0 +1,46 @@
#pragma once
#include <winternl.h>
#define LDR_DLL_NOTIFICATION_REASON_LOADED 1
#define LDR_DLL_NOTIFICATION_REASON_UNLOADED 2
typedef struct _LDR_DLL_LOADED_NOTIFICATION_DATA {
ULONG Flags;
PCUNICODE_STRING FullDllName;
PCUNICODE_STRING BaseDllName;
PVOID DllBase;
ULONG SizeOfImage;
} LDR_DLL_LOADED_NOTIFICATION_DATA, *PLDR_DLL_LOADED_NOTIFICATION_DATA;
typedef struct _LDR_DLL_UNLOADED_NOTIFICATION_DATA {
ULONG Flags;
PCUNICODE_STRING FullDllName;
PCUNICODE_STRING BaseDllName;
PVOID DllBase;
ULONG SizeOfImage;
} LDR_DLL_UNLOADED_NOTIFICATION_DATA, *PLDR_DLL_UNLOADED_NOTIFICATION_DATA;
typedef union _LDR_DLL_NOTIFICATION_DATA {
LDR_DLL_LOADED_NOTIFICATION_DATA Loaded;
LDR_DLL_UNLOADED_NOTIFICATION_DATA Unloaded;
} LDR_DLL_NOTIFICATION_DATA, *PLDR_DLL_NOTIFICATION_DATA;
typedef const _LDR_DLL_NOTIFICATION_DATA* PCLDR_DLL_NOTIFICATION_DATA;
typedef VOID (CALLBACK* PLDR_DLL_NOTIFICATION_FUNCTION) (
ULONG NotificationReason,
PCLDR_DLL_NOTIFICATION_DATA NotificationData,
PVOID Context
);
NTSTATUS NTAPI LdrRegisterDllNotification(
ULONG Flags,
PLDR_DLL_NOTIFICATION_FUNCTION NotificationFunction,
PVOID Context,
PVOID *Cookie
);
NTSTATUS NTAPI LdrUnregisterDllNotification(
PVOID Cookie
);
+138
View File
@@ -1,5 +1,7 @@
#include "sigscan.h"
#include <format>
#include <fstream>
#include <sstream>
#include <vector>
@@ -79,6 +81,109 @@ intptr_t find_pattern(HMODULE module, const uint8_t *pattern, const char *mask,
}
}
intptr_t find_pattern(HMODULE module, const std::string &pattern, const char *mask,
intptr_t offset, intptr_t result_usage)
{
std::string pattern_str(pattern);
auto pattern_bin = std::make_unique<uint8_t[]>(pattern.length() / 2);
if (!hex2bin(pattern_str.c_str(), pattern_bin.get())) {
log_warning("sigscan", "hex2bin failed");
return false;
}
return find_pattern(module, pattern_bin.get(), mask, offset, result_usage);
}
///
intptr_t find_pattern_from(std::vector<uint8_t> &data, intptr_t base, const uint8_t *pattern,
const char *mask, intptr_t offset, intptr_t usage, intptr_t start_from)
{
// build pattern
std::vector<std::pair<uint8_t, bool>> pattern_vector;
size_t mask_size = strlen(mask);
for (size_t i = 0; i < mask_size; i++) {
pattern_vector.emplace_back(pattern[i], mask[i] == 'X');
}
// the scan loop
auto data_begin = data.begin();
std::advance(data_begin, start_from);
auto cur_usage = 0;
while (true) {
// search for the pattern
auto search_result = std::search(data_begin, data.end(), pattern_vector.begin(), pattern_vector.end(),
[&](uint8_t c, std::pair<uint8_t, bool> pat) {
return (!pat.second) || c == pat.first;
});
// check for a match
if (search_result != data.end()) {
// return the result if we hit the usage count
if (cur_usage == usage) {
return (std::distance(data.begin(), search_result) + base) + offset;
}
// increment the found count
++cur_usage;
data_begin = ++search_result;
} else {
break;
}
}
return 0;
}
intptr_t find_pattern_from(HMODULE module, const uint8_t *pattern, const char *mask,
intptr_t offset, intptr_t result_usage, intptr_t start_from)
{
// get module information
MODULEINFO module_info {};
if (!GetModuleInformation(GetCurrentProcess(), module, &module_info, sizeof(module_info))) {
return 0;
}
auto size = static_cast<size_t>(module_info.SizeOfImage);
try {
// copy data
std::vector<uint8_t> data(size);
memcpy(data.data(), module_info.lpBaseOfDll, size);
// find pattern
return find_pattern_from(
data,
reinterpret_cast<intptr_t>(module_info.lpBaseOfDll),
pattern,
mask,
offset,
result_usage,
start_from);
} catch (const std::bad_alloc &e) {
log_warning("sigscan", "failed to allocate buffer of size {} for image data", size);
return false;
}
}
intptr_t find_pattern_from(HMODULE module, const std::string &pattern, const char *mask,
intptr_t offset, intptr_t result_usage, intptr_t start_from)
{
std::string pattern_str(pattern);
auto pattern_bin = std::make_unique<uint8_t[]>(pattern.length() / 2);
if (!hex2bin(pattern_str.c_str(), pattern_bin.get())) {
log_warning("sigscan", "hex2bin failed");
return false;
}
return find_pattern_from(module, pattern_bin.get(), mask, offset, result_usage, start_from);
}
intptr_t replace_pattern(HMODULE module, const uint8_t *pattern, const char *mask, intptr_t offset,
intptr_t usage, const uint8_t *replace_data, const char *replace_mask)
{
@@ -164,3 +269,36 @@ intptr_t replace_pattern(HMODULE module, const std::string &signature,
replace_mask.str().c_str()
);
}
bool get_pe_identifier(const std::filesystem::path& dll_path, uint32_t* time_date_stamp, uint32_t* address_of_entry_point) {
std::ifstream file(dll_path, std::ios::binary);
if (!file) {
log_warning("sigscan", "Failed to open file: {}", dll_path.string().c_str());
return false;
}
// read the DOS header
IMAGE_DOS_HEADER dos_header;
file.read(reinterpret_cast<char*>(&dos_header), sizeof(dos_header));
if (dos_header.e_magic != IMAGE_DOS_SIGNATURE) {
log_warning("sigscan", "Invalid DOS signature: {}", dll_path.string().c_str());
return false;
}
// move to the NT headers
file.seekg(dos_header.e_lfanew);
// read the NT headers
IMAGE_NT_HEADERS nt_headers;
file.read(reinterpret_cast<char*>(&nt_headers), sizeof(nt_headers));
if (nt_headers.Signature != IMAGE_NT_SIGNATURE) {
log_warning("sigscan", "Invalid NT signature: {}", dll_path.string().c_str());
return false;
}
// get the TimeDateStamp and AddressOfEntryPoint from the file header
*time_date_stamp = nt_headers.FileHeader.TimeDateStamp;
*address_of_entry_point = nt_headers.OptionalHeader.AddressOfEntryPoint;
return true;
}
+36
View File
@@ -2,7 +2,9 @@
#include <algorithm>
#include <string>
#include <cstdint>
#include <vector>
#include <filesystem>
#include "windows.h"
#include "psapi.h"
@@ -21,6 +23,38 @@ intptr_t find_pattern(
intptr_t offset,
intptr_t usage);
intptr_t find_pattern(
HMODULE module,
const std::string &pattern,
const char *mask,
intptr_t offset,
intptr_t result_usage);
intptr_t find_pattern_from(
std::vector<unsigned char> &data,
intptr_t base,
const unsigned char *pattern,
const char *mask,
intptr_t offset,
intptr_t usage,
intptr_t start_from);
intptr_t find_pattern_from(
HMODULE module,
const unsigned char *pattern,
const char *mask,
intptr_t offset,
intptr_t usage,
intptr_t start_from);
intptr_t find_pattern_from(
HMODULE module,
const std::string &pattern,
const char *mask,
intptr_t offset,
intptr_t result_usage,
intptr_t start_from);
intptr_t replace_pattern(
HMODULE module,
const unsigned char *pattern,
@@ -36,3 +70,5 @@ intptr_t replace_pattern(
const std::string &replacement,
intptr_t offset,
intptr_t usage);
bool get_pe_identifier(const std::filesystem::path& dll_path, uint32_t* time_date_stamp, uint32_t* address_of_entry_point);
+276
View File
@@ -0,0 +1,276 @@
#include "sysutils.h"
#include <cstdlib>
#define WIN32_NO_STATUS
#include <windows.h>
#undef WIN32_NO_STATUS
#include "util/libutils.h"
#include "util/logging.h"
#if 0
#define log_dbug(module, format_str, ...) logger::push( \
LOG_FORMAT("M", module, format_str, ## __VA_ARGS__), logger::Style::GREY)
#else
#define log_dbug(module, format_str, ...)
#endif
namespace sysutils {
#pragma pack(push)
#pragma pack(1)
// from https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getsystemfirmwaretable
typedef struct _RawSMBIOSData {
BYTE Used20CallingMethod;
BYTE SMBIOSMajorVersion;
BYTE SMBIOSMinorVersion;
BYTE DmiRevision;
DWORD Length;
PBYTE SMBIOSTableData;
} RawSMBIOSData, *PRawSMBIOSData;
// SMBIOS
// https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_3.4.0.pdf
typedef struct _SMBIOS_STRUCT_HEADER {
uint8_t Type;
uint8_t Length;
uint16_t Handle;
} SMBIOS_STRUCT_HEADER, *PSMBIOS_STRUCT_HEADER;
typedef struct _SMBIOS_TYPE_1 {
SMBIOS_STRUCT_HEADER Header;
uint8_t Manufacturer;
uint8_t ProductName;
uint8_t Version;
uint8_t SerialNumber;
uint8_t UUID[16];
uint8_t WakeUpType;
uint8_t SKUNumber;
uint8_t Family;
} SMBIOS_TYPE_1, *PSMBIOS_TYPE_1;
typedef struct _SMBIOS_TYPE_2 {
SMBIOS_STRUCT_HEADER Header;
uint8_t Manufacturer;
uint8_t Product;
uint8_t Version;
uint8_t SerialNumber;
uint8_t AssetTag;
uint8_t FeatureFlags;
uint8_t LocationInChassis;
uint16_t ChassisHandle;
uint8_t BoardType;
uint8_t NumObjHandle;
uint16_t *ObjHandles;
} SMBIOS_TYPE_2, *PSMBIOS_TYPE_2;
#pragma pack(pop)
static void dump_smbios_section(PBYTE smbios, DWORD length);
static void dump_smbios_section_1(PSMBIOS_TYPE_1 table);
static void dump_smbios_section_2(PSMBIOS_TYPE_2 table);
static const char *find_string_after_struct(PSMBIOS_STRUCT_HEADER table, uint8_t str_number);
typedef UINT (WINAPI *GetSystemFirmwareTable_t)(
DWORD FirmwareTableProviderSignature,
DWORD FirmwareTableID,
PVOID pFirmwareTableBuffer,
DWORD BufferSize
);
static GetSystemFirmwareTable_t GetSystemFirmwareTable = nullptr;
void print_smbios() {
DWORD bytes_written = 0;
DWORD table_size = 0;
LPBYTE table = nullptr;
if (GetSystemFirmwareTable == nullptr) {
auto k32 = libutils::try_module("kernel32.dll");
if (k32 != nullptr) {
GetSystemFirmwareTable = libutils::try_proc<GetSystemFirmwareTable_t>(
k32, "GetSystemFirmwareTable");
}
}
if (GetSystemFirmwareTable == nullptr) {
log_warning("smbios", "GetSystemFirmwareTable not found");
return;
}
// calculate how big of a buffer is needed
const uint32_t RSMB = 0x52534D42;
table_size = GetSystemFirmwareTable(RSMB, 0, NULL, 0);
if (table_size == 0){
log_warning("smbios", "initial call to GetSystemFirmwareTable failed, GLE:{}", GetLastError());
return;
}
// allocate memory
table = (LPBYTE)malloc(table_size);
if (table == nullptr) {
log_warning("smbios", "malloc failed");
return;
}
// actually get the table
bytes_written = GetSystemFirmwareTable(RSMB, 0, table, table_size);
if (bytes_written != table_size) {
log_warning("smbios", "call to GetSystemFirmwareTable failed, GLE:{}", GetLastError());
return;
}
// dump to console
const PRawSMBIOSData raw_smbios = (PRawSMBIOSData)table;
dump_smbios_section((PBYTE)&raw_smbios->SMBIOSTableData, raw_smbios->Length);
// clean up
if (table != nullptr) {
free(table);
table = nullptr;
}
}
static void dump_smbios_section(PBYTE smbios, DWORD length) {
PBYTE curr = smbios;
size_t tables_dumped = 0;
log_misc("smbios", "dumping SMBIOS information...");
while (curr < (smbios + length)) {
PSMBIOS_STRUCT_HEADER header = (PSMBIOS_STRUCT_HEADER)curr;
log_dbug("smbios", "table: type {}; {} bytes", header->Type, header->Length);
// spec-defined End-of-Table
if (header->Type == 127 && header->Length == 4) {
break;
}
if (header->Type == 1) {
dump_smbios_section_1((PSMBIOS_TYPE_1)curr);
tables_dumped += 1;
} else if (header->Type == 2) {
dump_smbios_section_2((PSMBIOS_TYPE_2)curr);
tables_dumped += 1;
}
// stop once type 1 and type 2 are dumped
if (2 <= tables_dumped) {
break;
}
curr += header->Length;
// skip over the string area, indicated by double null
while (!(*curr == 0 && *(curr+1) == 0)) {
curr += 1;
if (curr >= (smbios + length)) {
break;
}
}
curr += 2;
}
}
static void dump_smbios_section_1(PSMBIOS_TYPE_1 table) {
const char* manufacturer = find_string_after_struct(&table->Header, table->Manufacturer);
const char* product_name = find_string_after_struct(&table->Header, table->ProductName);
const char* sku = find_string_after_struct(&table->Header, table->SKUNumber);
log_misc("smbios", "system manufacturer : {}", manufacturer);
log_misc("smbios", "system product name : {}", product_name);
log_misc("smbios", "system SKU : {}", sku);
return;
}
static void dump_smbios_section_2(PSMBIOS_TYPE_2 table) {
const char* manufacturer = find_string_after_struct(&table->Header, table->Manufacturer);
const char* product_name = find_string_after_struct(&table->Header, table->Product);
log_misc("smbios", "baseboard manufacturer : {}", manufacturer);
log_misc("smbios", "baseboard product name : {}", product_name);
return;
}
static const char *find_string_after_struct(PSMBIOS_STRUCT_HEADER table, uint8_t str_number) {
const char* strings = (const char*)((PBYTE)table + table->Length);
// string numbers are 1-based in this spec (first string is at 1)
if (str_number == 0 || *strings == 0) {
return "";
}
for (uint8_t i = 1; i < str_number; i++) {
strings += strlen((char*)strings);
strings += 1;
}
return strings;
}
static void print_adapter(DWORD index, PDISPLAY_DEVICEA adapter, bool is_monitor) {
if (adapter->StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER) {
return;
}
if (!(adapter->StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP)) {
return;
}
std::string prefix("device");
if (is_monitor) {
prefix = " adapter";
}
log_misc("gpuinfo", "{} {} name : {}", prefix.c_str(), index, adapter->DeviceName);
log_misc("gpuinfo", "{} {} string : {}", prefix.c_str(), index, adapter->DeviceString);
log_dbug("gpuinfo", "{} {} flags : 0x{:x}", prefix.c_str(), index, adapter->StateFlags);
}
void print_gpus() {
DWORD device_index = 0;
DISPLAY_DEVICEA device;
device.cb = sizeof(device);
log_misc("smbios", "dumping GPU/monitor information...");
while (EnumDisplayDevicesA(nullptr, device_index, &device, 0)) {
print_adapter(device_index, &device, false);
DWORD monitor_index = 0;
DISPLAY_DEVICEA monitor;
monitor.cb = sizeof(monitor);
while (EnumDisplayDevicesA((PCHAR)device.DeviceName, monitor_index, &monitor, 0)) {
print_adapter(monitor_index, &monitor, true);
monitor_index++;
}
device_index++;
}
}
typedef void (WINAPI *RtlGetNtVersionNumbers_t)(
PDWORD pNtMajorVersion,
PDWORD pNtMinorVersion,
PDWORD pNtBuildNumber
);
static RtlGetNtVersionNumbers_t RtlGetNtVersionNumbers = nullptr;
void print_os() {
if (RtlGetNtVersionNumbers == nullptr) {
auto ntdll = libutils::try_module("ntdll.dll");
if (ntdll != nullptr) {
RtlGetNtVersionNumbers = libutils::try_proc<RtlGetNtVersionNumbers_t>(
ntdll, "RtlGetNtVersionNumbers");
if (RtlGetNtVersionNumbers == nullptr) {
log_warning("sysutils", "RtlGetNtVersionNumbers not found");
}
}
}
if (RtlGetNtVersionNumbers != nullptr) {
// OS major/minor can no longer be relied on
// https://learn.microsoft.com/en-us/windows/win32/sysinfo/operating-system-version
// there are other ways to obtain better strings (WMI, registry, winbrand.dll)
// but they either don't work in Win7 or too expensive to link against
DWORD buildnum = 0;
RtlGetNtVersionNumbers(nullptr, nullptr, &buildnum);
buildnum = buildnum & 0x0FFFFFFF;
log_misc("sysutils", "Windows OS build number: {}", buildnum);
return;
}
}
}
+10
View File
@@ -0,0 +1,10 @@
#pragma once
#include <string>
#include <cstdint>
namespace sysutils {
void print_smbios();
void print_gpus();
void print_os();
}
+79
View File
@@ -0,0 +1,79 @@
#include "tapeled.h"
namespace tapeledutils {
led_tape_color_pick_algorithm TAPE_LED_ALGORITHM = TAPE_LED_USE_MIDDLE;
bool is_enabled() {
return (TAPE_LED_ALGORITHM != TAPE_LED_USE_NONE);
}
// for bi2x-style byte array of all colors and LEDs at once
rgb_float3_t pick_color_from_led_tape(uint8_t *data, size_t data_size) {
rgb_float3_t result = {0.f, 0.f, 0.f};
if (TAPE_LED_ALGORITHM == TAPE_LED_USE_AVERAGE) {
// calculate average color
size_t avg_ri = 0;
size_t avg_gi = 0;
size_t avg_bi = 0;
for (size_t i = 0; i < data_size; i++) {
const auto color = &data[i * 3];
avg_ri += color[0];
avg_gi += color[1];
avg_bi += color[2];
}
// normalize
const float avg_mult = 1.f / (data_size * 255);
result.r = avg_ri * avg_mult;
result.g = avg_gi * avg_mult;
result.b = avg_bi * avg_mult;
} else if (TAPE_LED_ALGORITHM == TAPE_LED_USE_FIRST ||
TAPE_LED_ALGORITHM == TAPE_LED_USE_MIDDLE ||
TAPE_LED_ALGORITHM == TAPE_LED_USE_LAST ) {
// pick one LED
const uint8_t *color;
switch (TAPE_LED_ALGORITHM) {
case TAPE_LED_USE_FIRST:
color = &data[0];
break;
case TAPE_LED_USE_LAST:
color = &data[(data_size - 1) * 3];
break;
case TAPE_LED_USE_MIDDLE:
default:
color = &data[(data_size / 2) * 3];
break;
}
// normalize
const float single_mult = 1.f / 255;
result.r = color[0] * single_mult;
result.g = color[1] * single_mult;
result.b = color[2] * single_mult;
}
return result;
}
// for bi2a-style that calls for each individual LED
size_t get_led_index_using_avg_algo(size_t data_size) {
size_t index_to_use;
if (TAPE_LED_ALGORITHM == TAPE_LED_USE_FIRST) {
index_to_use = 0;
} else if (TAPE_LED_ALGORITHM == TAPE_LED_USE_LAST) {
index_to_use = data_size - 1;
} else if (TAPE_LED_ALGORITHM == TAPE_LED_USE_MIDDLE) {
index_to_use = (size_t)(data_size / 2);
} else {
// TAPE_LED_USE_AVERAGE can't work for this model since we don't cache the entire tape
// LED array, so just use the middle-of-tape value instead
index_to_use = (size_t)(data_size / 2);
}
return index_to_use;
}
}
+26
View File
@@ -0,0 +1,26 @@
#pragma once
#include <cstdint>
namespace tapeledutils {
enum led_tape_color_pick_algorithm {
TAPE_LED_USE_NONE = 0,
TAPE_LED_USE_FIRST = 1,
TAPE_LED_USE_MIDDLE = 2,
TAPE_LED_USE_LAST = 3,
TAPE_LED_USE_AVERAGE = 4,
};
extern led_tape_color_pick_algorithm TAPE_LED_ALGORITHM;
typedef struct {
float r;
float g;
float b;
} rgb_float3_t;
bool is_enabled();
rgb_float3_t pick_color_from_led_tape(uint8_t *data, size_t data_size);
size_t get_led_index_using_avg_algo(size_t data_size);
}
+84
View File
@@ -0,0 +1,84 @@
#include "unity_player.h"
#include "cfg/screen_resize.h"
#include "hooks/graphics/graphics.h"
#include "external/fmt/include/fmt/format.h"
#include "util/detour.h"
#include "util/logging.h"
namespace unity_utils {
std::string get_unity_player_args() {
std::string args = "";
// windowed
if (GRAPHICS_WINDOWED) {
args += " -screen-fullscreen 0";
// window size - by default unity player will attempt to create a window that fills the
// screen, so instead fall back to 1080p resolution
uint32_t w = 1920;
uint32_t h = 1080;
if (GRAPHICS_WINDOW_SIZE.has_value()) {
w = GRAPHICS_WINDOW_SIZE.value().first;
h = GRAPHICS_WINDOW_SIZE.value().second;
}
args += fmt::format(" -screen-width {} -screen-height {}", w, h);
// window border
// eventually we should launch the player inside a parent window that we have full control
// over using -parentHWND so we can let the user resize it by dragging the border...
if (GRAPHICS_WINDOW_STYLE.has_value()) {
if (GRAPHICS_WINDOW_STYLE == cfg::WindowDecorationMode::Borderless) {
args += " -popupwindow";
}
}
} else {
// need to specify this, otherwise it gets cached and uses previous value
args += " -screen-fullscreen 1";
}
// monitor
if (D3D9_ADAPTER.has_value()) {
args += fmt::format(" -monitor {}", D3D9_ADAPTER.value());
}
return args;
}
static std::string cmdLine;
static decltype(GetCommandLineA) *GetCommandLineA_orig = nullptr;
static LPSTR WINAPI GetCommandLineA_hook() {
return (LPSTR) cmdLine.c_str();
}
void set_args(const std::string &args) {
static bool init = false;
if (!init) {
init = true;
detour::trampoline_try("kernel32.dll", "GetCommandLineA",
(void*)GetCommandLineA_hook, (void**)&GetCommandLineA_orig);
}
cmdLine = args;
log_info("unity", "unity player args: ```{}```", cmdLine);
}
static bool show = false;
static decltype(ShowCursor) *ShowCursor_orig = nullptr;
static int WINAPI ShowCursor_hook(BOOL bShow) {
return show;
}
void force_show_cursor(bool bShow) {
static bool init = false;
if (!init) {
init = true;
detour::trampoline_try("user32.dll", "ShowCursor",
(void*)ShowCursor_hook, (void**)&ShowCursor_orig);
}
show = bShow;
}
}
+9
View File
@@ -0,0 +1,9 @@
#pragma once
#include <string>
namespace unity_utils {
std::string get_unity_player_args();
void set_args(const std::string &args);
void force_show_cursor(bool bShow);
}
+48
View File
@@ -1,5 +1,6 @@
#include <winsock2.h>
#include <ws2tcpip.h>
#include <random>
#include "utils.h"
@@ -76,3 +77,50 @@ std::string ws2s(const std::wstring &wstr) {
return buffer;
}
bool acquire_shutdown_privs() {
// check if already acquired
static bool acquired = false;
if (acquired)
return true;
// get process token
HANDLE hToken;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
return false;
// get the LUID for the shutdown privilege
TOKEN_PRIVILEGES tkp;
LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, &tkp.Privileges[0].Luid);
tkp.PrivilegeCount = 1;
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
// get the shutdown privilege for this process
AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, (PTOKEN_PRIVILEGES) NULL, 0);
// check for error
bool success = GetLastError() == ERROR_SUCCESS;
if (success)
acquired = true;
return success;
}
void generate_ea_card(char card[17]) {
// don't ask why the existing codebase uses 18 char array when there are only 16+1 chars
// create random
std::random_device rd;
std::mt19937 generator(rd());
std::uniform_int_distribution<> uniform(0, 15);
// randomize card
strcpy(card, "E00401");
char hex[] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
for (int i = 6; i < 16; i++) {
card[i] = hex[uniform(generator)];
}
// terminate and flush
card[16] = 0;
}
+44
View File
@@ -79,6 +79,23 @@ static inline void strreplace(std::string &s, const std::string &search, const s
}
}
static inline std::string strtrim(const std::string& input) {
std::string output = input;
// trim spaces
output.erase(0, output.find_first_not_of("\t\n\v\f\r "));
output.erase(output.find_last_not_of("\t\n\v\f\r ") + 1);
return output;
}
static inline std::string strtolower(const std::string& input) {
std::string output = strtrim(input);
// replace with lower case
std::transform(
output.begin(), output.end(), output.begin(),
[](unsigned char c){ return std::tolower(c); });
return output;
}
static inline int _hex2bin_helper(char input) {
if (input >= '0' && input <= '9') {
return input - '0';
@@ -267,3 +284,30 @@ static inline std::string guid2s(const GUID guid) {
guid.Data4[0], guid.Data4[1], guid.Data4[2], guid.Data4[3],
guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]);
}
bool acquire_shutdown_privs();
void generate_ea_card(char card[17]);
static inline int get_async_primary_mouse() {
int vk = GetSystemMetrics(SM_SWAPBUTTON) ? VK_RBUTTON : VK_LBUTTON;
return GetAsyncKeyState(vk);
}
static inline int get_async_secondary_mouse() {
int vk = GetSystemMetrics(SM_SWAPBUTTON) ? VK_LBUTTON : VK_RBUTTON;
return GetAsyncKeyState(vk);
}
static inline bool parse_width_height(const std::string wh, std::pair<uint32_t, uint32_t> &result) {
std::string s = wh;
uint32_t w, h;
const auto remove_spaces = [](const char& c) { return c == ' '; };
s.erase(std::remove_if(s.begin(), s.end(), remove_spaces), s.end());
if (sscanf(s.c_str(), "%u,%u", &w, &h) == 2) {
result = std::pair(w, h);
return true;
} else {
return false;
}
}