From 0d8dd05cebd70fc2dbc7d301b6669b6241bf7d6f Mon Sep 17 00:00:00 2001 From: Shiz Date: Wed, 18 Dec 2019 20:51:09 +0100 Subject: [PATCH] platform/vfs.c: add option path redirection Optional for now, since not all games or configurations may need it. It may be better eventually to refactor hooklib/path.c to allow for static path remapping, to minimize boilerplate code around the functions. --- dist/chuni/segatools.ini | 2 ++ dist/idz/segatools.ini | 2 ++ platform/config.c | 8 +++++ platform/vfs.c | 65 ++++++++++++++++++++++++++++++++++++++-- platform/vfs.h | 1 + 5 files changed, 76 insertions(+), 2 deletions(-) diff --git a/dist/chuni/segatools.ini b/dist/chuni/segatools.ini index d474de5..7fc0d82 100644 --- a/dist/chuni/segatools.ini +++ b/dist/chuni/segatools.ini @@ -1,6 +1,8 @@ [vfs] ; Insert the path to the game AMFS directory here (contains ICF1 and ICF2) amfs= +; Insert the path to the game Option directory here (contains Axxx directories) +option= ; Create an empty directory somewhere and insert the path here. ; This directory may be shared between multiple SEGA games. ; NOTE: This has nothing to do with Windows %APPDATA%. diff --git a/dist/idz/segatools.ini b/dist/idz/segatools.ini index 19af346..4c4f196 100644 --- a/dist/idz/segatools.ini +++ b/dist/idz/segatools.ini @@ -1,6 +1,8 @@ [vfs] ; Insert the path to the game AMFS directory here (contains ICF1 and ICF2) amfs= +; Insert the path to the game Option directory here (contains OPxx directories) +option= ; Create an empty directory somewhere and insert the path here. ; This directory may be shared between multiple SEGA games. ; NOTE: This has nothing to do with Windows %APPDATA%. diff --git a/platform/config.c b/platform/config.c index 5a34d5f..94b9e40 100644 --- a/platform/config.c +++ b/platform/config.c @@ -295,5 +295,13 @@ void vfs_config_load(struct vfs_config *cfg, const wchar_t *filename) cfg->appdata, _countof(cfg->appdata), filename); + + GetPrivateProfileStringW( + L"vfs", + L"option", + L"", + cfg->option, + _countof(cfg->option), + filename); } diff --git a/platform/vfs.c b/platform/vfs.c index d78e6b5..1aecc9d 100644 --- a/platform/vfs.c +++ b/platform/vfs.c @@ -19,6 +19,10 @@ static HRESULT vfs_path_hook_nthome( const wchar_t *src, wchar_t *dest, size_t *count); +static HRESULT vfs_path_hook_option( + 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); @@ -26,6 +30,9 @@ static wchar_t vfs_nthome_real[MAX_PATH]; static const wchar_t vfs_nthome[] = L"C:\\Documents and Settings\\AppUser\\"; static const size_t vfs_nthome_len = _countof(vfs_nthome) - 1; +static const wchar_t vfs_option[] = L"C:\\Mount\\Option\\"; +static const size_t vfs_option_len = _countof(vfs_option) - 1; + static const struct reg_hook_val vfs_reg_vals[] = { { .name = L"AMFS", @@ -65,6 +72,10 @@ HRESULT vfs_hook_init(const struct vfs_config *config) return E_FAIL; } + if (config->option[0] == L'\0') { + dprintf("Vfs: WARNING: OPTION path not specified in INI file\n"); + } + home_ok = GetEnvironmentVariableW( L"USERPROFILE", vfs_nthome_real, @@ -80,9 +91,13 @@ HRESULT vfs_hook_init(const struct vfs_config *config) memcpy(&vfs_config, config, sizeof(*config)); + vfs_slashify(vfs_nthome_real, _countof(vfs_nthome_real)); vfs_slashify(vfs_config.amfs, _countof(vfs_config.amfs)); vfs_slashify(vfs_config.appdata, _countof(vfs_config.appdata)); - vfs_slashify(vfs_nthome_real, _countof(vfs_nthome_real)); + + if (vfs_config.option[0] != L'\0') { + vfs_slashify(vfs_config.option, _countof(vfs_config.option)); + } hr = vfs_mkdir_rec(vfs_config.amfs); @@ -114,6 +129,8 @@ HRESULT vfs_hook_init(const struct vfs_config *config) dprintf("Vfs: Failed to create %S: %x\n", temp, (int) hr); } + /* Not auto-creating option directory as it is normally a read-only mount */ + hr = path_hook_push(vfs_path_hook); if (FAILED(hr)) { @@ -126,6 +143,14 @@ HRESULT vfs_hook_init(const struct vfs_config *config) return hr; } + if (vfs_config.option[0] != L'\0') { + hr = path_hook_push(vfs_path_hook_option); + + if (FAILED(hr)) { + return hr; + } + } + hr = reg_hook_push_key( HKEY_LOCAL_MACHINE, L"SYSTEM\\SEGA\\SystemProperty\\mount", @@ -241,7 +266,7 @@ static HRESULT vfs_path_hook(const wchar_t *src, wchar_t *dest, size_t *count) return S_FALSE; } - /* Cut off E:\ prefix, replace with redir path, count NUL terminator */ + /* Cut off \, replace with redir path, count NUL terminator */ redir_len = wcslen(redir); required = wcslen(src) - 3 + redir_len + 1; @@ -296,6 +321,42 @@ static HRESULT vfs_path_hook_nthome( return S_OK; } +static HRESULT vfs_path_hook_option( + const wchar_t *src, + wchar_t *dest, + size_t *count) +{ + size_t required; + size_t redir_len; + + assert(src != NULL); + assert(count != NULL); + + /* Case-insensitive check to see if src starts with vfs_option */ + + if (path_compare_w(src, vfs_option, vfs_option_len) != 0) { + return S_FALSE; + } + + /* Cut off the matched prefix, add the replaced prefix, count NUL */ + + redir_len = wcslen(vfs_config.option); + required = wcslen(src) - vfs_option_len + redir_len + 1; + + if (dest != NULL) { + if (required > *count) { + return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + } + + wcscpy_s(dest, *count, vfs_config.option); + wcscpy_s(dest + redir_len, *count - redir_len, src + vfs_option_len); + } + + *count = required; + + return S_OK; +} + static HRESULT vfs_reg_read_amfs(void *bytes, uint32_t *nbytes) { return reg_hook_read_wstr(bytes, nbytes, vfs_config.amfs); diff --git a/platform/vfs.h b/platform/vfs.h index acdd99a..8767305 100644 --- a/platform/vfs.h +++ b/platform/vfs.h @@ -9,6 +9,7 @@ struct vfs_config { bool enable; wchar_t amfs[MAX_PATH]; wchar_t appdata[MAX_PATH]; + wchar_t option[MAX_PATH]; }; HRESULT vfs_hook_init(const struct vfs_config *config);