Merge branch 'develop' into develop

This commit is contained in:
azaneko
2026-06-18 02:18:35 +00:00
9 changed files with 233 additions and 16 deletions
+44
View File
@@ -200,6 +200,14 @@ static UINT WINAPI hook_GetDriveTypeW(
LPCWSTR lpRootPathName
);
static BOOL WINAPI hook_GetDiskFreeSpaceW(
LPCWSTR lpRootPathName,
LPDWORD lpSectorsPerCluster,
LPDWORD lpBytesPerSector,
LPDWORD lpNumberOfFreeClusters,
LPDWORD lpTotalNumberOfClusters
);
/* Link pointers */
static BOOL (WINAPI *next_CreateDirectoryA)(
@@ -395,6 +403,14 @@ static UINT (WINAPI *next_GetDriveTypeA)(
LPCSTR lpRootPathName
);
static BOOL (WINAPI *next_GetDiskFreeSpaceW)(
LPCWSTR lpRootPathName,
LPDWORD lpSectorsPerCluster,
LPDWORD lpBytesPerSector,
LPDWORD lpNumberOfFreeClusters,
LPDWORD lpTotalNumberOfClusters
);
/* Hook table */
static const struct hook_symbol path_hook_syms[] = {
@@ -538,6 +554,10 @@ static const struct hook_symbol path_hook_syms[] = {
.name = "GetDriveTypeW",
.patch = hook_GetDriveTypeW,
.link = (void **) &next_GetDriveTypeW,
}, {
.name = "GetDiskFreeSpaceW",
.patch = hook_GetDiskFreeSpaceW,
.link = (void **) &next_GetDiskFreeSpaceW,
}
};
@@ -1720,6 +1740,30 @@ static UINT WINAPI hook_GetDriveTypeW(
return result;
}
static BOOL WINAPI hook_GetDiskFreeSpaceW(
LPCWSTR lpRootPathName,
LPDWORD lpSectorsPerCluster,
LPDWORD lpBytesPerSector,
LPDWORD lpNumberOfFreeClusters,
LPDWORD lpTotalNumberOfClusters
) {
wchar_t *trans;
UINT result;
BOOL ok;
ok = path_transform_w(&trans, lpRootPathName);
if (!ok) {
return FALSE;
}
result = next_GetDiskFreeSpaceW(trans ? trans : lpRootPathName, lpSectorsPerCluster, lpBytesPerSector, lpNumberOfFreeClusters, lpTotalNumberOfClusters);
free(trans);
return result;
}
char** str_split_a(char* a_str, const char a_delim) {
char** result = 0;
size_t count = 0;
+22 -11
View File
@@ -12,12 +12,15 @@
#include "util/dprintf.h"
#include "util/str.h"
#define MAX_CONCURRENT_REG_HANDLES 4
struct reg_hook_key {
HKEY root;
const wchar_t *name;
const struct reg_hook_val *vals;
size_t nvals;
HKEY handle;
HKEY handles[MAX_CONCURRENT_REG_HANDLES];
size_t nhandles;
};
/* Helper functions */
@@ -344,7 +347,7 @@ static LRESULT reg_hook_propagate_hr(HRESULT hr)
static struct reg_hook_key *reg_hook_match_key_locked(HKEY handle)
{
struct reg_hook_key *key;
size_t i;
size_t i, j;
if (handle == NULL || handle == INVALID_HANDLE_VALUE) {
return NULL;
@@ -353,8 +356,10 @@ static struct reg_hook_key *reg_hook_match_key_locked(HKEY handle)
for (i = 0 ; i < reg_hook_nkeys ; i++) {
key = &reg_hook_keys[i];
if (key->handle == handle) {
return key;
for (j = 0; j < key->nhandles; j++) {
if (key->handles[j] == handle) {
return key;
}
}
}
@@ -414,9 +419,9 @@ static LSTATUS reg_hook_open_locked(
return ERROR_SUCCESS;
}
/* Assume only one handle will be open at a time */
/* Assume only MAX_CONCURRENT_REG_HANDLES handles will be open at a time */
if (key->handle != NULL) {
if (key->nhandles > MAX_CONCURRENT_REG_HANDLES) {
return ERROR_SHARING_VIOLATION;
}
@@ -433,7 +438,7 @@ static LSTATUS reg_hook_open_locked(
out);
if (err == ERROR_SUCCESS) {
key->handle = *out;
key->handles[key->nhandles++] = *out;
}
return err;
@@ -511,16 +516,22 @@ static LSTATUS WINAPI hook_RegCreateKeyExW(
static LSTATUS WINAPI hook_RegCloseKey(HKEY handle)
{
struct reg_hook_key *key;
size_t i;
size_t i, j, k;
EnterCriticalSection(&reg_hook_lock);
for (i = 0 ; i < reg_hook_nkeys ; i++) {
key = &reg_hook_keys[i];
if (key->handle == handle) {
//dprintf("Registry: Closed virtual key %S\n", key->name);
key->handle = NULL;
for (j = 0; j < key->nhandles; j++) {
if (key->handles[j] == handle) {
for (k = j; k < MAX_CONCURRENT_REG_HANDLES - 1; k++) {
key->handles[k] = key->handles[k + 1];
}
key->handles[MAX_CONCURRENT_REG_HANDLES - 1] = NULL;
key->nhandles--;
//dprintf("Registry: Closed virtual key %S\n", key->name);
}
}
}
+8
View File
@@ -170,6 +170,13 @@ void misc_config_load(struct misc_config *cfg, const wchar_t *filename)
cfg->enable = GetPrivateProfileIntW(L"misc", L"enable", 1, filename);
cfg->allowMasterKeyWrite = GetPrivateProfileIntW(L"misc", L"allowMasterKeyWrite", 0, filename);
cfg->allowReboot = GetPrivateProfileIntW(L"misc", L"allowReboot", 0, filename);
GetPrivateProfileStringW(
L"misc",
L"nextProcessFilePath",
L"DEVICE\\NextProcess.txt",
cfg->nextProcessFile,
_countof(cfg->nextProcessFile),
filename);
}
void netenv_config_load(struct netenv_config *cfg, const wchar_t *filename)
@@ -341,6 +348,7 @@ void vfs_config_load(struct vfs_config *cfg, const wchar_t *filename)
assert(filename != NULL);
cfg->enable = GetPrivateProfileIntW(L"vfs", L"enable", 1, filename);
cfg->allowAmfsDownloads = GetPrivateProfileIntW(L"vfs", L"allowAmfsDownloads", 0, filename);
GetPrivateProfileStringW(
L"vfs",
+3 -3
View File
@@ -177,15 +177,15 @@ HRESULT dns_platform_hook_init(const struct dns_config *cfg)
return hr;
}
// Disable api/polling to the original servers
// ABaaS log server, receives system and error logs stored in appdata
hr = dns_hook_push(L"*.amlog.sys-all.net", NULL);
hr = dns_hook_push(L"*.amlog.sys-all.net", cfg->startup);
if (FAILED(hr)) {
return hr;
}
hr = dns_hook_push(L"*.d-amlog.sys-all.net", NULL);
hr = dns_hook_push(L"*.d-amlog.sys-all.net", cfg->startup);
if (FAILED(hr)) {
return hr;
+102 -2
View File
@@ -12,6 +12,8 @@
#include "platform/misc.h"
#include <shlwapi.h>
#include "util/dprintf.h"
static BOOL WINAPI misc_ExitWindowsEx(unsigned int flags, uint32_t reason);
@@ -22,6 +24,11 @@ static HRESULT misc_read_cpu_temp_error(void *bytes, uint32_t *nbytes);
static HRESULT misc_read_cpu_temp_warning(void *bytes, uint32_t *nbytes);
static HRESULT misc_read_platform_id(void *bytes, uint32_t *nbytes);
static HRESULT misc_read_platform_name(void *bytes, uint32_t *nbytes);
static HRESULT misc_read_main_nic(void *bytes, uint32_t *nbytes);
static HRESULT misc_read_extend_nic(void *bytes, uint32_t *nbytes);
static HRESULT misc_read_downloadui_done(void *bytes, uint32_t *nbytes);
static HRESULT misc_read_next_process(void *bytes, uint32_t *nbytes);
static HRESULT misc_write_next_process(const void *bytes, uint32_t nbytes);
static const struct hook_symbol misc_syms[] = {
{
@@ -44,11 +51,12 @@ static const struct reg_hook_val misc_master_keys[] = {
.read = misc_read_app_loader_count,
.type = REG_DWORD,
}, {
/* Black-hole val, list it here so we don't get a warning msg */
.name = L"NextProcess",
.read = misc_read_next_process,
.write = misc_write_next_process,
.type = REG_SZ,
}, {
/* ditto */
/* Black-hole val for reading, list it here so we don't get a warning msg */
.name = L"SystemError",
.type = REG_SZ,
}
@@ -74,7 +82,28 @@ static const struct reg_hook_val misc_static_keys[] = {
}
};
static const struct reg_hook_val misc_wireless_keys[] = {
{
.name = L"main_nic",
.read = misc_read_main_nic,
.type = REG_DWORD,
}, {
.name = L"extend_nic",
.read = misc_read_extend_nic,
.type = REG_DWORD,
}
};
static const struct reg_hook_val misc_downloadui_keys[] = {
{
.name = L"IsDone",
.read = misc_read_downloadui_done,
.type = REG_DWORD,
},
};
static wchar_t misc_platform_id[5];
static const struct misc_config *config;
HRESULT misc_hook_init(const struct misc_config *cfg, const char *platform_id)
{
@@ -83,6 +112,8 @@ HRESULT misc_hook_init(const struct misc_config *cfg, const char *platform_id)
assert(cfg != NULL);
assert(platform_id != NULL && strlen(platform_id) == 4);
config = cfg;
if (!cfg->enable) {
return S_FALSE;
}
@@ -124,8 +155,29 @@ HRESULT misc_hook_init(const struct misc_config *cfg, const char *platform_id)
L"SYSTEM\\SEGA\\SystemProperty\\Master",
misc_master_keys,
_countof(misc_master_keys));
if (FAILED(hr)) {
return hr;
}
}
hr = reg_hook_push_key(
HKEY_LOCAL_MACHINE,
L"SYSTEM\\SEGA\\SystemProperty\\wirelessNetwork",
misc_wireless_keys,
_countof(misc_wireless_keys));
if (FAILED(hr)) {
return hr;
}
hr = reg_hook_push_key(
HKEY_LOCAL_MACHINE,
L"SYSTEM\\SEGA\\SystemProperty\\downloadui",
misc_downloadui_keys,
_countof(misc_downloadui_keys));
if (FAILED(hr)) {
return hr;
}
@@ -136,6 +188,10 @@ HRESULT misc_hook_init(const struct misc_config *cfg, const char *platform_id)
hook_table_apply(NULL, "user32.dll", misc_syms, _countof(misc_syms));
}
if (PathFileExistsW(cfg->nextProcessFile)) {
DeleteFileW(cfg->nextProcessFile);
}
return S_OK;
}
@@ -175,3 +231,47 @@ static HRESULT misc_read_platform_name(void *bytes, uint32_t *nbytes)
{
return reg_hook_read_wstr(bytes, nbytes, L"ALLS MX2.1"); // TODO: Dynamic
}
static HRESULT misc_read_main_nic(void *bytes, uint32_t *nbytes)
{
return reg_hook_read_wstr(bytes, nbytes, L"");
}
static HRESULT misc_read_extend_nic(void *bytes, uint32_t *nbytes)
{
return reg_hook_read_wstr(bytes, nbytes, L"");
}
static HRESULT misc_read_downloadui_done(void *bytes, uint32_t *nbytes)
{
return reg_hook_read_u32(bytes, nbytes, 1);
}
static HRESULT misc_read_next_process(void *bytes, uint32_t *nbytes)
{
return reg_hook_read_wstr(bytes, nbytes, L"");
}
static HRESULT misc_write_next_process(const void *bytes, uint32_t nbytes)
{
HRESULT hr;
DWORD dwBytesWritten;
wchar_t* nextProcess = malloc(nbytes);
memcpy(nextProcess, bytes, nbytes);
dprintf("Misc: Next Process: %ls\n", nextProcess);
HANDLE hFile = CreateFileW(config->nextProcessFile, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
hr = HRESULT_FROM_WIN32(GetLastError());
dprintf("Misc: Error opening %ls for writing: %x\n", config->nextProcessFile, (int) hr);
return hr;
}
WriteFile(hFile, nextProcess, nbytes, &dwBytesWritten, NULL);
CloseHandle(hFile);
return S_OK;
}
+1
View File
@@ -8,6 +8,7 @@ struct misc_config {
bool enable;
bool allowReboot;
bool allowMasterKeyWrite;
wchar_t nextProcessFile[MAX_PATH];
};
HRESULT misc_hook_init(const struct misc_config *cfg, const char *platform_id);
+36
View File
@@ -41,6 +41,10 @@ static HRESULT vfs_custom_path_hook(
const wchar_t *src,
wchar_t *dest,
size_t *count);
static HRESULT vfs_path_hook_tmp_icf(
const wchar_t *src,
wchar_t *dest,
size_t *count);
static HRESULT vfs_reg_read_amfs(void *bytes, uint32_t *nbytes);
static HRESULT vfs_reg_read_appdata(void *bytes, uint32_t *nbytes);
@@ -77,6 +81,9 @@ static const size_t vfs_option_len = _countof(vfs_option) - 1;
static const wchar_t vfs_apm3[] = L"C:\\Mount\\Apm";
static const size_t vfs_apm3_len = _countof(vfs_apm3) - 1;
static const wchar_t vfs_tmp_icf[] = L"E:\\tmpIcf.icf";
static const size_t vfs_tmp_icf_len = _countof(vfs_tmp_icf) - 1;
static const struct reg_hook_val vfs_reg_vals[] = {
{
.name = L"AMFS",
@@ -168,6 +175,12 @@ HRESULT vfs_hook_init(const struct vfs_config *config, const char* game_id)
vfs_fixup_path(vfs_config.option, _countof(vfs_config.option), true);
}
hr = path_hook_push(vfs_path_hook_tmp_icf);
if (FAILED(hr)) {
return hr;
}
hr = vfs_mkdir_rec(vfs_config.amfs);
if (FAILED(hr)) {
@@ -601,6 +614,29 @@ static HRESULT vfs_path_hook_apm(
return S_OK;
}
// Block writing of E:\tmpIcf.icf to intentionally break the download process if the user has not enabled it
static HRESULT vfs_path_hook_tmp_icf(
const wchar_t *src,
wchar_t *dest,
size_t *count)
{
assert(src != NULL);
assert(count != NULL);
/* Case-insensitive check to see if src starts with vfs_tmp_icf */
if (path_compare_w(src, vfs_tmp_icf, vfs_tmp_icf_len) != 0) {
return S_FALSE;
}
if (vfs_config.allowAmfsDownloads) {
return S_FALSE;
}
dprintf("Vfs: AMFS downloads are blocked\n");
return E_FAIL;
}
static HRESULT vfs_reg_read_amfs(void *bytes, uint32_t *nbytes)
{
return reg_hook_read_wstr(bytes, nbytes, L"E:\\");
+1
View File
@@ -15,6 +15,7 @@ struct vfs_config {
wchar_t redirections_from[MAX_REDIRECTIONS][MAX_PATH];
int redirections_from_len[MAX_REDIRECTIONS];
wchar_t redirections_to[MAX_REDIRECTIONS][MAX_PATH];
bool allowAmfsDownloads;
};
HRESULT vfs_hook_init(const struct vfs_config *config, const char* game_id);
+16
View File
@@ -707,6 +707,12 @@ redirection0from=\\.\COM5
redirection0to=\\.\COM10
```
### `allowAmfsDownloads`
Default: `0`
Allows network services to download arbitrary files to the AMFS directory specified above. This has security implications, do not enable this, unless you trust your server operator.
## `[epay]`
Configure Thinca Payment (E-Money) emulation and hooks.
@@ -783,3 +789,13 @@ Allows the game to write to specific registry keys relevant for the boot process
Default: `0`
Allows the game to reboot the computer. Only intended for owners of real hardware.
### `nextProcessFilePath`
Default: `DEVICE\NextProcess.txt`
This is a file that will be set to the content of what would be written to the `NextProcess` registry key when the game is terminated.
This allows whatever executed the game process to react what should happen next (System Test Mode selected, network delivery completed, ...) without requiring admin permissions.
The file is deleted on startup of segatools.