Vfs: Add ability to redirect arbitary paths (= serial ports) (#62)

As someone running several games on the same cabinet that share most of the hardware (like KCA and FGO), it's a bit of a hassle switching the COM ports of the hardware around all the time.

This commit adds the ability to place arbitary path redirections in segatools.ini, which is mostly useful for hardcoded serial ports.
Technically this would also allow you to redirect arbitary paths, but I haven't tested that yet.

An example that I personally use is:
```
[vfs]
amfs=amfs
appdata=.
option=option

; aime reader
redirection0from=\\.\COM10
redirection0to=\\.\COM3
; rfid reader
redirection1from=\\.\COM12
redirection1to=\\.\COM2
; POP led begone
redirection2from=\\.\COM9
redirection2to=\\.\COM90
; partition to pop
redirection3from=\\.\COM11
redirection3to=\\.\COM9
```

Reviewed-on: https://gitea.tendokyu.moe/TeamTofuShop/segatools/pulls/62
Co-authored-by: kyoubate-haruka <46010460+kyoubate-haruka@users.noreply.github.com>
Co-committed-by: kyoubate-haruka <46010460+kyoubate-haruka@users.noreply.github.com>
This commit is contained in:
kyoubate-haruka
2026-02-08 11:12:02 +00:00
committed by Dniel97
parent 21e5238b0a
commit a3ac466dba
6 changed files with 109 additions and 0 deletions

View File

@@ -24,6 +24,7 @@
#include "platform/vfs.h"
#include "platform/system.h"
#include "platform/openssl.h"
#include "util/dprintf.h"
void platform_config_load(struct platform_config *cfg, const wchar_t *filename)
@@ -337,6 +338,32 @@ void vfs_config_load(struct vfs_config *cfg, const wchar_t *filename)
cfg->option,
_countof(cfg->option),
filename);
for (int i = 0; i < MAX_REDIRECTIONS; i++){
wchar_t key[32];
wsprintfW(key, L"redirection%dfrom", i);
GetPrivateProfileStringW(
L"vfs",
key,
L"",
cfg->redirections_from[i],
_countof(cfg->redirections_from[i]),
filename);
wsprintfW(key, L"redirection%dto", i);
GetPrivateProfileStringW(
L"vfs",
key,
L"",
cfg->redirections_to[i],
_countof(cfg->redirections_to[i]),
filename);
cfg->redirections_from_len[i] = (int)wcslen(cfg->redirections_from[i]);
if (cfg->redirections_from_len[i] > 0) {
dprintf("Vfs: Set up custom redirection from %ls to %ls\n", cfg->redirections_from[i], cfg->redirections_to[i]);
}
}
}
void system_config_load(struct system_config *cfg, const wchar_t *filename)

View File

@@ -36,6 +36,10 @@ static HRESULT vfs_path_hook_apm(
const wchar_t *src,
wchar_t *dest,
size_t *count);
static HRESULT vfs_custom_path_hook(
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);
@@ -217,6 +221,12 @@ HRESULT vfs_hook_init(const struct vfs_config *config, const char* game_id)
}
}
hr = path_hook_push(vfs_custom_path_hook);
if (FAILED(hr)) {
return hr;
}
hr = reg_hook_push_key(
HKEY_LOCAL_MACHINE,
L"SYSTEM\\SEGA\\SystemProperty\\mount",
@@ -601,3 +611,49 @@ static wchar_t* hook_AppImage_getOptionMountRootPath()
return path;
}
static HRESULT vfs_custom_path_hook(
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 one of our custom paths */
for (int i = 0; i < MAX_REDIRECTIONS; i++){
wchar_t* from = vfs_config.redirections_from[i];
wchar_t* to = vfs_config.redirections_to[i];
if (from[0] == '\0' || to[0] == '\0'){
return S_FALSE;
}
if (path_compare_w(src, from, vfs_config.redirections_from_len[i]) != 0) {
continue;
}
size_t required = wcslen(to) + 1;
#if defined(LOG_CUSTOM_VFS)
dprintf("Vfs: Redirection matched: %ls -> %ls\n", from, to);
#endif
if (dest != NULL) {
if (required > *count) {
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
}
wcscpy_s(dest, *count, to);
}
*count = required;
break;
}
return S_OK;
}

View File

@@ -5,11 +5,16 @@
#include <stdbool.h>
#include <stddef.h>
#define MAX_REDIRECTIONS 64
struct vfs_config {
bool enable;
wchar_t amfs[MAX_PATH];
wchar_t appdata[MAX_PATH];
wchar_t option[MAX_PATH];
wchar_t redirections_from[MAX_REDIRECTIONS][MAX_PATH];
int redirections_from_len[MAX_REDIRECTIONS];
wchar_t redirections_to[MAX_REDIRECTIONS][MAX_PATH];
};
HRESULT vfs_hook_init(const struct vfs_config *config, const char* game_id);

View File

@@ -722,3 +722,16 @@ Enabling full virtualization will virtualize ALL writes to the C:\ drive. Using
unless you know what you're doing!
The Windows directory is always excluded from virtualization.
### `redirection#from`, `redirection#to`
Default: Empty string
Advanced feature intended for owners of real hardware. This allows arbitrary file reads to be redirected to other paths. Since serial ports are also "file" reads, this can be used to redirect hardcoded COM ports to other ports. Up to 64 redirections can be used by incrementing the number in the key: `redirection1from`, `redirection2from`, ...
Example for redirecting COM 5 to COM 10:
```
redirection0from=\\.\COM5
redirection0to=\\.\COM10
```

View File

@@ -88,6 +88,9 @@ endif
if get_option('log_all') or get_option('log_ewf')
add_project_arguments('-DLOG_EWF', language : 'c')
endif
if get_option('log_all') or get_option('log_custom_vfs')
add_project_arguments('-DLOG_CUSTOM_VFS', language: 'c')
endif
shlwapi_lib = cc.find_library('shlwapi')
dinput8_lib = cc.find_library('dinput8')

View File

@@ -83,3 +83,8 @@ option('log_ewf',
value : false,
description : 'Enable debug logging for file system virtualization'
)
option('log_custom_vfs',
type : 'boolean',
value : false,
description : 'Enable debug logging for custom path redirections'
)