mirror of
https://github.com/djhackersdev/bemanitools.git
synced 2025-02-17 11:18:31 +01:00
[memfile] Add support for faking files in memory
This commit is contained in:
parent
be5a773cb7
commit
3d4df4cd64
3
dist/sdvx5/sdvxhook2.conf
vendored
3
dist/sdvx5/sdvxhook2.conf
vendored
@ -10,6 +10,9 @@ io.disable_poll_limiter=false
|
||||
# Forces game to think headphones are attached
|
||||
io.force_headphones=false
|
||||
|
||||
# Disables the built in file hooks, requiring manual file creation (/dev/raw/j.dest)
|
||||
io.disable_file_hooks=false
|
||||
|
||||
# Run the game in a framed window (requires windowed option)
|
||||
gfx.framed=true
|
||||
|
||||
|
@ -7,4 +7,5 @@ src_hooklib := \
|
||||
config-adapter.c \
|
||||
rs232.c \
|
||||
setupapi.c \
|
||||
memfile.c \
|
||||
|
||||
|
268
src/main/hooklib/memfile.c
Normal file
268
src/main/hooklib/memfile.c
Normal file
@ -0,0 +1,268 @@
|
||||
#include "hooklib/memfile.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <windows.h>
|
||||
|
||||
#include "hook/hr.h"
|
||||
#include "hook/iohook.h"
|
||||
#include "hook/table.h"
|
||||
|
||||
#include "util/array.h"
|
||||
#include "util/log.h"
|
||||
#include "util/str.h"
|
||||
|
||||
struct file_entry {
|
||||
char *path;
|
||||
wchar_t *wpath;
|
||||
HANDLE fd;
|
||||
int64_t pos;
|
||||
|
||||
const void *data;
|
||||
uint32_t sz;
|
||||
enum memfile_hook_path_mode path_mode;
|
||||
};
|
||||
|
||||
static struct array hooked_files;
|
||||
static CRITICAL_SECTION hooked_files_cs;
|
||||
|
||||
|
||||
BOOL my_GetFileInformationByHandle(
|
||||
HANDLE hFile,
|
||||
LPBY_HANDLE_FILE_INFORMATION lpFileInformation
|
||||
);
|
||||
|
||||
BOOL (*real_GetFileInformationByHandle)(
|
||||
HANDLE hFile,
|
||||
LPBY_HANDLE_FILE_INFORMATION lpFileInformation
|
||||
);
|
||||
|
||||
static const struct hook_symbol memfile_hook_kernel32_syms[] = {
|
||||
{.name = "GetFileInformationByHandle",
|
||||
.patch = my_GetFileInformationByHandle,
|
||||
.link = (void **) &real_GetFileInformationByHandle},
|
||||
};
|
||||
|
||||
|
||||
void memfile_hook_init(void)
|
||||
{
|
||||
array_init(&hooked_files);
|
||||
InitializeCriticalSection(&hooked_files_cs);
|
||||
|
||||
hook_table_apply(
|
||||
NULL,
|
||||
"Kernel32.dll",
|
||||
memfile_hook_kernel32_syms,
|
||||
lengthof(memfile_hook_kernel32_syms));
|
||||
|
||||
log_info("Initialized");
|
||||
}
|
||||
|
||||
void memfile_hook_fini(void)
|
||||
{
|
||||
EnterCriticalSection(&hooked_files_cs);
|
||||
struct file_entry *entry;
|
||||
|
||||
for (size_t i = 0; i < hooked_files.nitems; i++) {
|
||||
entry = array_item(struct file_entry, &hooked_files, i);
|
||||
|
||||
free(entry->wpath);
|
||||
free(entry->path);
|
||||
|
||||
if (entry->fd != NULL) {
|
||||
CloseHandle(entry->fd);
|
||||
}
|
||||
}
|
||||
|
||||
array_fini(&hooked_files);
|
||||
LeaveCriticalSection(&hooked_files_cs);
|
||||
|
||||
DeleteCriticalSection(&hooked_files_cs);
|
||||
|
||||
log_info("Finished");
|
||||
}
|
||||
|
||||
void memfile_hook_add_fd(
|
||||
const char *path, enum memfile_hook_path_mode path_mode, const void *data, uint32_t sz)
|
||||
{
|
||||
log_assert(path != NULL);
|
||||
|
||||
HRESULT hr;
|
||||
struct file_entry *entry = NULL;
|
||||
|
||||
EnterCriticalSection(&hooked_files_cs);
|
||||
entry = array_append(struct file_entry, &hooked_files);
|
||||
|
||||
entry->path = strdup(path);
|
||||
entry->wpath = str_widen(path);
|
||||
|
||||
hr = iohook_open_nul_fd(&entry->fd);
|
||||
|
||||
if (hr != S_OK) {
|
||||
LeaveCriticalSection(&hooked_files_cs);
|
||||
log_fatal("Opening nul fd failed: %08lx", hr);
|
||||
}
|
||||
|
||||
entry->pos = 0;
|
||||
|
||||
entry->data = data;
|
||||
entry->sz = sz;
|
||||
entry->path_mode = path_mode;
|
||||
|
||||
LeaveCriticalSection(&hooked_files_cs);
|
||||
|
||||
log_misc("memfile_hook_add_fd: path %s, mode %d, data size %d", path, path_mode, sz);
|
||||
}
|
||||
|
||||
static struct file_entry *memfile_hook_match_irp(const struct irp *irp)
|
||||
{
|
||||
struct file_entry *entry;
|
||||
log_assert(irp != NULL);
|
||||
|
||||
if (irp->op == IRP_OP_OPEN) {
|
||||
for (size_t i = 0; i < hooked_files.nitems; i++) {
|
||||
entry = array_item(struct file_entry, &hooked_files, i);
|
||||
|
||||
if (entry->path_mode == ABSOLUTE_MATCH) {
|
||||
if (wstr_eq(entry->wpath, irp->open_filename)) {
|
||||
log_misc("Memfile Open: Absolute matched: %s", entry->path);
|
||||
return entry;
|
||||
}
|
||||
} else if (entry->path_mode == ENDING_MATCH) {
|
||||
if (wstr_ends_with(irp->open_filename, entry->wpath)) {
|
||||
log_misc("Memfile Open: Ending matched: %s", entry->path);
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (size_t i = 0; i < hooked_files.nitems; i++) {
|
||||
entry = array_item(struct file_entry, &hooked_files, i);
|
||||
|
||||
if (entry->fd == irp->fd) {
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static HRESULT memfile_hook_irp_read(struct file_entry *entry, struct irp *irp)
|
||||
{
|
||||
log_assert(entry != NULL);
|
||||
log_assert(irp != NULL);
|
||||
|
||||
size_t nread = min(entry->sz - entry->pos, irp->read.nbytes);
|
||||
|
||||
if (nread > 0) {
|
||||
memcpy(irp->read.bytes + irp->read.pos, (uint8_t*)entry->data + entry->pos, nread);
|
||||
|
||||
entry->pos += nread;
|
||||
irp->read.pos += nread;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT memfile_hook_irp_seek(struct file_entry *entry, struct irp *irp)
|
||||
{
|
||||
log_assert(entry != NULL);
|
||||
log_assert(irp != NULL);
|
||||
|
||||
switch (irp->seek_origin) {
|
||||
case FILE_BEGIN:
|
||||
entry->pos = irp->seek_offset;
|
||||
break;
|
||||
case FILE_CURRENT:
|
||||
entry->pos += irp->seek_offset;
|
||||
break;
|
||||
case FILE_END:
|
||||
entry->pos = entry->sz + irp->seek_offset;
|
||||
break;
|
||||
default:
|
||||
log_fatal("Invalid seek origin");
|
||||
}
|
||||
|
||||
irp->seek_pos = entry->pos;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT
|
||||
memfile_hook_irp_handler(struct file_entry *entry, struct irp *irp)
|
||||
{
|
||||
log_assert(entry != NULL);
|
||||
log_assert(irp != NULL);
|
||||
|
||||
switch (irp->op) {
|
||||
case IRP_OP_OPEN:
|
||||
irp->fd = entry->fd;
|
||||
entry->pos = 0;
|
||||
return S_OK;
|
||||
|
||||
case IRP_OP_CLOSE:
|
||||
return S_OK;
|
||||
|
||||
case IRP_OP_READ:
|
||||
return memfile_hook_irp_read(entry, irp);
|
||||
|
||||
case IRP_OP_WRITE:
|
||||
log_warning("write attempted on memfile, unsupported");
|
||||
return S_OK;
|
||||
|
||||
case IRP_OP_IOCTL:
|
||||
log_warning("ioctl attempted on memfile, unsupported");
|
||||
return S_OK;
|
||||
|
||||
case IRP_OP_FSYNC:
|
||||
log_warning("fsync attempted on memfile, unsupported");
|
||||
return S_OK;
|
||||
|
||||
case IRP_OP_SEEK:
|
||||
return memfile_hook_irp_seek(entry, irp);
|
||||
|
||||
default:
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
}
|
||||
|
||||
HRESULT memfile_hook_dispatch_irp(struct irp *irp)
|
||||
{
|
||||
log_assert(irp != NULL);
|
||||
|
||||
HRESULT hr;
|
||||
|
||||
EnterCriticalSection(&hooked_files_cs);
|
||||
|
||||
struct file_entry *entry = memfile_hook_match_irp(irp);
|
||||
|
||||
if (!entry) {
|
||||
LeaveCriticalSection(&hooked_files_cs);
|
||||
return iohook_invoke_next(irp);
|
||||
}
|
||||
|
||||
hr = memfile_hook_irp_handler(entry, irp);
|
||||
|
||||
LeaveCriticalSection(&hooked_files_cs);
|
||||
return hr;
|
||||
}
|
||||
|
||||
// this isn't a standard iohook IRP atm.
|
||||
BOOL my_GetFileInformationByHandle(
|
||||
HANDLE hFile,
|
||||
LPBY_HANDLE_FILE_INFORMATION lpFileInformation
|
||||
)
|
||||
{
|
||||
struct file_entry *entry;
|
||||
for (size_t i = 0; i < hooked_files.nitems; i++) {
|
||||
entry = array_item(struct file_entry, &hooked_files, i);
|
||||
|
||||
if (entry->fd == hFile) {
|
||||
lpFileInformation->dwFileAttributes = FILE_ATTRIBUTE_READONLY;
|
||||
lpFileInformation->nFileSizeHigh = 0;
|
||||
lpFileInformation->nFileSizeLow = entry->sz;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return real_GetFileInformationByHandle(hFile, lpFileInformation);
|
||||
}
|
42
src/main/hooklib/memfile.h
Normal file
42
src/main/hooklib/memfile.h
Normal file
@ -0,0 +1,42 @@
|
||||
#ifndef HOOKLIB_MEMFILE_H
|
||||
#define HOOKLIB_MEMFILE_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <windows.h>
|
||||
|
||||
#include "hook/iohook.h"
|
||||
|
||||
/**
|
||||
* memfile allows you to stub read-only files in memory.
|
||||
*/
|
||||
|
||||
/**
|
||||
* This hooks required functions and sets up the hook array.
|
||||
* Please remember to install the irp below.
|
||||
*/
|
||||
void memfile_hook_init(void);
|
||||
|
||||
void memfile_hook_fini(void);
|
||||
|
||||
enum memfile_hook_path_mode {
|
||||
ABSOLUTE_MATCH = 0x1,
|
||||
ENDING_MATCH = 0x2,
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds the specified path to the list of files to hook
|
||||
*
|
||||
* @param path path to hook
|
||||
* @param path_mode path matching mode (1: abs, 2: ending)
|
||||
* @param data file contents (buffer must exist until fini is called)
|
||||
* @param sz size of file
|
||||
*/
|
||||
void memfile_hook_add_fd(
|
||||
const char *path, enum memfile_hook_path_mode path_mode, const void *data, uint32_t sz);
|
||||
|
||||
/**
|
||||
* iohook dispatch function. Needs to be installed.
|
||||
*/
|
||||
HRESULT memfile_hook_dispatch_irp(struct irp *irp);
|
||||
|
||||
#endif
|
@ -9,11 +9,13 @@
|
||||
#define SDVXHOOK2_CONFIG_IO_DISABLE_BIO2_EMU_KEY "io.disable_bio2_emu"
|
||||
#define SDVXHOOK2_CONFIG_IO_DISABLE_POLL_LIMITER_KEY "io.disable_poll_limiter"
|
||||
#define SDVXHOOK2_CONFIG_IO_FORCE_HEADPHONES_KEY "io.force_headphones"
|
||||
#define SDVXHOOK2_CONFIG_IO_DISABLE_FILE_HOOKS_KEY "io.disable_file_hooks"
|
||||
|
||||
#define SDVXHOOK2_CONFIG_IO_DEFAULT_DISABLE_CARD_READER_EMU_VALUE false
|
||||
#define SDVXHOOK2_CONFIG_IO_DEFAULT_DISABLE_BIO2_EMU_VALUE false
|
||||
#define SDVXHOOK2_CONFIG_IO_DEFAULT_DISABLE_POLL_LIMITER_VALUE false
|
||||
#define SDVXHOOK2_CONFIG_IO_DEFAULT_FORCE_HEADPHONES_VALUE false
|
||||
#define SDVXHOOK2_CONFIG_IO_DEFAULT_DISABLE_FILE_HOOKS_VALUE false
|
||||
|
||||
void sdvxhook2_config_io_init(struct cconfig *config)
|
||||
{
|
||||
@ -41,6 +43,12 @@ void sdvxhook2_config_io_init(struct cconfig *config)
|
||||
SDVXHOOK2_CONFIG_IO_FORCE_HEADPHONES_KEY,
|
||||
SDVXHOOK2_CONFIG_IO_DEFAULT_FORCE_HEADPHONES_VALUE,
|
||||
"Forces game to think headphones are attached");
|
||||
|
||||
cconfig_util_set_bool(
|
||||
config,
|
||||
SDVXHOOK2_CONFIG_IO_DISABLE_FILE_HOOKS_KEY,
|
||||
SDVXHOOK2_CONFIG_IO_DEFAULT_DISABLE_FILE_HOOKS_VALUE,
|
||||
"Disables the built in file hooks, requiring manual file creation (/dev/raw/j.dest)");
|
||||
}
|
||||
|
||||
void sdvxhook2_config_io_get(
|
||||
@ -93,4 +101,16 @@ void sdvxhook2_config_io_get(
|
||||
SDVXHOOK2_CONFIG_IO_FORCE_HEADPHONES_KEY,
|
||||
SDVXHOOK2_CONFIG_IO_DEFAULT_FORCE_HEADPHONES_VALUE);
|
||||
}
|
||||
|
||||
if (!cconfig_util_get_bool(
|
||||
config,
|
||||
SDVXHOOK2_CONFIG_IO_DISABLE_FILE_HOOKS_KEY,
|
||||
&config_io->disable_file_hooks,
|
||||
SDVXHOOK2_CONFIG_IO_DEFAULT_DISABLE_FILE_HOOKS_VALUE)) {
|
||||
log_warning(
|
||||
"Invalid value for key '%s' specified, fallback "
|
||||
"to default '%d'",
|
||||
SDVXHOOK2_CONFIG_IO_DISABLE_FILE_HOOKS_KEY,
|
||||
SDVXHOOK2_CONFIG_IO_DEFAULT_DISABLE_FILE_HOOKS_VALUE);
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ struct sdvxhook2_config_io {
|
||||
bool disable_bio2_emu;
|
||||
bool disable_poll_limiter;
|
||||
bool force_headphones;
|
||||
bool disable_file_hooks;
|
||||
};
|
||||
|
||||
void sdvxhook2_config_io_init(struct cconfig *config);
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "hooklib/adapter.h"
|
||||
#include "hooklib/app.h"
|
||||
#include "hooklib/config-adapter.h"
|
||||
#include "hooklib/memfile.h"
|
||||
#include "hooklib/rs232.h"
|
||||
|
||||
#include "bio2emu/emu.h"
|
||||
@ -51,6 +52,21 @@ static struct bio2emu_port bio2_emu = {
|
||||
.dispatcher = bio2_emu_bi2a_dispatch_request,
|
||||
};
|
||||
|
||||
static void attach_dest_fd_intercept(const char *sidcode)
|
||||
{
|
||||
char region = sidcode[3];
|
||||
|
||||
if (region == 'X') {
|
||||
region = 'J';
|
||||
}
|
||||
|
||||
char target_file[8] = "\\x.dest";
|
||||
target_file[1] = tolower(region);
|
||||
|
||||
// can only capture these by ending path due to /dev/raw being mountable
|
||||
memfile_hook_add_fd(target_file, ENDING_MATCH, NULL, 0);
|
||||
}
|
||||
|
||||
static bool my_dll_entry_init(char *sidcode, struct property_node *param)
|
||||
{
|
||||
struct cconfig *config;
|
||||
@ -113,12 +129,21 @@ static bool my_dll_entry_init(char *sidcode, struct property_node *param)
|
||||
iohook_push_handler(ac_io_port_dispatch_irp);
|
||||
iohook_push_handler(bio2emu_port_dispatch_irp);
|
||||
|
||||
if (!config_io.disable_file_hooks) {
|
||||
memfile_hook_init();
|
||||
iohook_push_handler(memfile_hook_dispatch_irp);
|
||||
attach_dest_fd_intercept(sidcode);
|
||||
}
|
||||
|
||||
rs232_hook_init();
|
||||
rs232_hook_limit_hooks();
|
||||
|
||||
if (!config_io.disable_bio2_emu) {
|
||||
bio2emu_init();
|
||||
bio2_emu_bi2a_init(&bio2_emu, config_io.disable_poll_limiter, config_io.force_headphones);
|
||||
bio2_emu_bi2a_init(
|
||||
&bio2_emu,
|
||||
config_io.disable_poll_limiter,
|
||||
config_io.force_headphones);
|
||||
}
|
||||
|
||||
if (!config_io.disable_card_reader_emu) {
|
||||
@ -157,6 +182,10 @@ static bool my_dll_entry_main(void)
|
||||
sdvx_io_fini();
|
||||
}
|
||||
|
||||
if (!config_io.disable_file_hooks) {
|
||||
memfile_hook_fini();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -250,3 +250,12 @@ bool wstr_eq(const wchar_t *lhs, const wchar_t *rhs)
|
||||
return wcscmp(lhs, rhs) == 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool wstr_insensitive_eq(const wchar_t *lhs, const wchar_t *rhs)
|
||||
{
|
||||
if (lhs == NULL || rhs == NULL) {
|
||||
return lhs == rhs;
|
||||
} else {
|
||||
return _wcsicmp(lhs, rhs) == 0;
|
||||
}
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ void wstr_cpy(wchar_t *dest, size_t dnchars, const wchar_t *src);
|
||||
wchar_t *wstr_dup(const wchar_t *src);
|
||||
bool wstr_ends_with(const wchar_t *haystack, const wchar_t *needle);
|
||||
bool wstr_eq(const wchar_t *lhs, const wchar_t *rhs);
|
||||
bool wstr_insensitive_eq(const wchar_t *lhs, const wchar_t *rhs);
|
||||
size_t wstr_format(wchar_t *buf, size_t nchars, const wchar_t *fmt, ...);
|
||||
bool wstr_narrow(const wchar_t *src, char **dest);
|
||||
size_t
|
||||
|
Loading…
x
Reference in New Issue
Block a user