mirror of
https://github.com/djhackersdev/bemanitools.git
synced 2026-05-04 03:45:20 -05:00
The paths changed for 18 and again for 19. Refactor and improve the detection logic to consider this. This also should make the code more maintainable.
743 lines
19 KiB
C
743 lines
19 KiB
C
#define LOG_MODULE "chart-patch"
|
|
|
|
#include <math.h>
|
|
#include <string.h>
|
|
#include <windows.h>
|
|
|
|
#include "hook/iohook.h"
|
|
|
|
#include "util/crc.h"
|
|
#include "util/defs.h"
|
|
#include "util/fs.h"
|
|
#include "util/log.h"
|
|
#include "util/mem.h"
|
|
#include "util/str.h"
|
|
|
|
struct chart_patch_file {
|
|
HANDLE fd;
|
|
struct const_iobuf state;
|
|
bool active;
|
|
};
|
|
|
|
struct chart_toc_entry {
|
|
uint32_t offset;
|
|
uint32_t length;
|
|
} __attribute__((__packed__));
|
|
|
|
struct chart_event {
|
|
uint32_t time;
|
|
uint8_t type;
|
|
uint8_t data1;
|
|
uint16_t data2;
|
|
} __attribute__((__packed__));
|
|
|
|
struct ospreybin_entry {
|
|
char filename[32];
|
|
uint32_t checksum1;
|
|
uint32_t checksum2;
|
|
uint32_t offset_in_file;
|
|
uint32_t checksum_length;
|
|
uint32_t filesize;
|
|
} __attribute__((__packed__));
|
|
|
|
struct ospreybin {
|
|
/* IIDX */
|
|
char magic[4];
|
|
/* e.g. 17 for Sirius */
|
|
uint32_t version;
|
|
/* seems to be unused */
|
|
uint32_t num_entries;
|
|
uint32_t checksum_type;
|
|
};
|
|
|
|
enum song_data_path_format {
|
|
SONG_DATA_PATH_INVALID = 0,
|
|
SONG_DATA_PATH_09_TO_17 = 1,
|
|
SONG_DATA_PATH_18 = 2,
|
|
SONG_DATA_PATH_19 = 3,
|
|
};
|
|
|
|
enum song_data_file_type {
|
|
SONG_DATA_FILE_TYPE_UNKNKOWN = 0,
|
|
SONG_DATA_FILE_TYPE_DOT_1 = 1,
|
|
SONG_DATA_FILE_TYPE_CHECKSUM = 2,
|
|
};
|
|
|
|
static bool chart_patch_initted;
|
|
|
|
static double chart_patch_hz;
|
|
static double chart_patch_timebase;
|
|
|
|
static bool chart_patch_file_detour;
|
|
static char chart_patch_file_1_name[6];
|
|
static struct chart_patch_file chart_patch_file_1;
|
|
static struct chart_patch_file chart_patch_file_checksum;
|
|
static CRITICAL_SECTION chart_patch_lock;
|
|
|
|
static bool chart_patch_file_load(
|
|
struct irp *irp_open, struct chart_patch_file *file, const wchar_t *path)
|
|
{
|
|
struct irp irp;
|
|
void *bytes;
|
|
size_t nbytes;
|
|
HANDLE fd;
|
|
HRESULT hr;
|
|
char *tmp;
|
|
|
|
/* Reset file to ensure consistent not-available state */
|
|
memset(file, 0, sizeof(struct chart_patch_file));
|
|
|
|
memset(&irp, 0, sizeof(irp));
|
|
irp.op = IRP_OP_OPEN;
|
|
irp.next_handler = irp_open->next_handler;
|
|
irp.open_filename = path;
|
|
irp.open_access = GENERIC_READ;
|
|
irp.open_creation = OPEN_EXISTING;
|
|
|
|
hr = iohook_invoke_next(&irp);
|
|
|
|
if (FAILED(hr)) {
|
|
return false;
|
|
}
|
|
|
|
fd = irp.fd;
|
|
|
|
memset(&irp, 0, sizeof(irp));
|
|
irp.op = IRP_OP_SEEK;
|
|
irp.fd = fd;
|
|
irp.seek_origin = FILE_END;
|
|
irp.seek_offset = 0;
|
|
|
|
hr = iohook_invoke_next(&irp);
|
|
|
|
if (FAILED(hr)) {
|
|
log_fatal("Seeking end failed");
|
|
}
|
|
|
|
nbytes = (size_t) irp.seek_pos;
|
|
bytes = xmalloc(nbytes);
|
|
|
|
memset(&irp, 0, sizeof(irp));
|
|
irp.op = IRP_OP_SEEK;
|
|
irp.fd = fd;
|
|
irp.seek_origin = FILE_BEGIN;
|
|
irp.seek_offset = 0;
|
|
|
|
hr = iohook_invoke_next(&irp);
|
|
|
|
if (FAILED(hr)) {
|
|
log_fatal("Seeking begin failed");
|
|
}
|
|
|
|
memset(&irp, 0, sizeof(irp));
|
|
irp.op = IRP_OP_READ;
|
|
irp.fd = fd;
|
|
irp.read.bytes = bytes;
|
|
irp.read.nbytes = nbytes;
|
|
irp.read.pos = 0;
|
|
|
|
hr = iohook_invoke_next(&irp);
|
|
|
|
if (FAILED(hr)) {
|
|
log_fatal("Reading failed");
|
|
}
|
|
|
|
if (irp.read.pos != nbytes) {
|
|
hr = HRESULT_FROM_WIN32(ERROR_HANDLE_EOF);
|
|
|
|
log_fatal("Reading entire file failed");
|
|
}
|
|
|
|
memset(&irp, 0, sizeof(irp));
|
|
irp.op = IRP_OP_CLOSE;
|
|
irp.fd = fd;
|
|
|
|
hr = iohook_invoke_next(&irp);
|
|
|
|
if (FAILED(hr)) {
|
|
log_fatal("Closing failed");
|
|
}
|
|
|
|
hr = iohook_open_nul_fd(&file->fd);
|
|
|
|
if (hr != S_OK) {
|
|
log_fatal("Opening nul fd failed: %08lx", hr);
|
|
}
|
|
|
|
memset(&file->state, 0, sizeof(struct const_iobuf));
|
|
file->state.bytes = bytes;
|
|
file->state.nbytes = nbytes;
|
|
file->active = false;
|
|
|
|
wstr_narrow(path, &tmp);
|
|
log_misc("Loading %s successful, fd %p", tmp, file->fd);
|
|
free(tmp);
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool chart_patch_is_valid_song_id(const wchar_t *str)
|
|
{
|
|
for (int i = 0; i < 4; i++) {
|
|
if (!(str[i] >= L'0' && str[i] <= L'9')) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static void chart_patch_execute_patch_1()
|
|
{
|
|
double factor;
|
|
struct chart_toc_entry *toc;
|
|
struct chart_event *event;
|
|
size_t chart_offset;
|
|
size_t i;
|
|
|
|
log_assert(chart_patch_file_1.fd != NULL);
|
|
log_assert(chart_patch_hz > 0);
|
|
log_assert(chart_patch_timebase > 0);
|
|
|
|
factor = chart_patch_hz / chart_patch_timebase;
|
|
toc = (struct chart_toc_entry *) chart_patch_file_1.state.bytes;
|
|
|
|
log_misc(
|
|
"Patching chart: orig %f, target %f, factor %f",
|
|
chart_patch_timebase,
|
|
chart_patch_hz,
|
|
factor);
|
|
|
|
for (i = 0; i < 12; i++) {
|
|
if (toc[i].offset == 0) {
|
|
continue; /* No such chart */
|
|
}
|
|
|
|
for (chart_offset = toc[i].offset;
|
|
chart_offset < toc[i].offset + toc[i].length;
|
|
chart_offset += sizeof(struct chart_event)) {
|
|
event = (struct chart_event *) &chart_patch_file_1.state
|
|
.bytes[chart_offset];
|
|
|
|
/* Don't clobber the end-of-chart marker */
|
|
|
|
if (event->time != 0x7FFFFFFF) {
|
|
event->time = (uint32_t) round(event->time * factor);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void chart_patch_execute_patch_checksum()
|
|
{
|
|
const struct ospreybin *header;
|
|
struct ospreybin_entry *entries;
|
|
struct ospreybin_entry *entry;
|
|
const void *span_start;
|
|
size_t span_len;
|
|
uint32_t checksum;
|
|
size_t i;
|
|
|
|
header = (struct ospreybin *) &chart_patch_file_checksum.state.bytes[0];
|
|
entries = (struct ospreybin_entry *) &chart_patch_file_checksum.state
|
|
.bytes[sizeof(*header)];
|
|
|
|
for (i = 0; i < header->num_entries; i++) {
|
|
entry = &entries[i];
|
|
|
|
if (!str_eq(chart_patch_file_1_name, entry->filename)) {
|
|
continue;
|
|
}
|
|
|
|
span_start = &chart_patch_file_1.state.bytes[entry->offset_in_file];
|
|
span_len = entry->checksum_length;
|
|
checksum = crc32(span_start, span_len, 0);
|
|
|
|
entry->checksum1 = checksum;
|
|
entry->checksum2 = checksum;
|
|
|
|
return;
|
|
}
|
|
|
|
log_warning("Could not find checksum row for %s", chart_patch_file_1_name);
|
|
}
|
|
|
|
static enum song_data_path_format chart_patch_is_song_data_path(const wchar_t *path, wchar_t *song_id)
|
|
{
|
|
log_assert(path);
|
|
log_assert(song_id);
|
|
|
|
// Song data path for 09 to 17
|
|
// data folder next to revision folder and working dir set to revision folder
|
|
if (swscanf(
|
|
path,
|
|
// Ensure to match %lc and not %c to extract wide string chars
|
|
L"..\\data\\sd_data%*[\\/]%lc%lc%lc%lc%*[\\/]",
|
|
&song_id[0],
|
|
&song_id[1],
|
|
&song_id[2],
|
|
&song_id[3]) == 4) {
|
|
song_id[4] = '\0';
|
|
|
|
if (!chart_patch_is_valid_song_id(song_id)) {
|
|
return SONG_DATA_PATH_INVALID;
|
|
}
|
|
|
|
return SONG_DATA_PATH_09_TO_17;
|
|
}
|
|
|
|
// Song data path for 18
|
|
// data dir next to modules folder but working dir set to root folder
|
|
if (swscanf(
|
|
path,
|
|
// Ensure to match %lc and not %c to extract wide string chars
|
|
L".\\data\\sd_data%*[\\/]%lc%lc%lc%lc%*[\\/]",
|
|
&song_id[0],
|
|
&song_id[1],
|
|
&song_id[2],
|
|
&song_id[3]) == 4) {
|
|
song_id[4] = '\0';
|
|
|
|
if (!chart_patch_is_valid_song_id(song_id)) {
|
|
return SONG_DATA_PATH_INVALID;
|
|
}
|
|
|
|
return SONG_DATA_PATH_18;
|
|
}
|
|
|
|
// Song data path for 19
|
|
// data dir next to modules folder but working dir set to root folder
|
|
// ...and they had to rename sd_data to sound *tableflip*
|
|
if (swscanf(
|
|
path,
|
|
// Ensure to match %lc and not %c to extract wide string chars
|
|
L".\\data\\sound%*[\\/]%lc%lc%lc%lc%*[\\/]",
|
|
&song_id[0],
|
|
&song_id[1],
|
|
&song_id[2],
|
|
&song_id[3]) == 4) {
|
|
song_id[4] = '\0';
|
|
|
|
if (!chart_patch_is_valid_song_id(song_id)) {
|
|
return SONG_DATA_PATH_INVALID;
|
|
}
|
|
|
|
return SONG_DATA_PATH_19;
|
|
}
|
|
|
|
return SONG_DATA_PATH_INVALID;
|
|
}
|
|
|
|
static void chart_patch_get_song_chart_file_name(const wchar_t *song_id, wchar_t *chart_file_name)
|
|
{
|
|
log_assert(song_id);
|
|
log_assert(chart_file_name);
|
|
log_assert(wcslen(song_id) == 4);
|
|
|
|
memcpy(chart_file_name, song_id, sizeof(wchar_t) * 4);
|
|
chart_file_name[4] = L'.';
|
|
chart_file_name[5] = L'1';
|
|
chart_file_name[6] = L'\0';
|
|
}
|
|
|
|
static enum song_data_file_type chart_patch_get_song_data_file_type(const wchar_t *path, const wchar_t *song_id)
|
|
{
|
|
wchar_t chart_file[7];
|
|
|
|
log_assert(path);
|
|
log_assert(song_id);
|
|
|
|
chart_patch_get_song_chart_file_name(song_id, chart_file);
|
|
|
|
if (wstr_ends_with(path, chart_file)) {
|
|
return SONG_DATA_FILE_TYPE_DOT_1;
|
|
}
|
|
|
|
if (wstr_ends_with(path, L"osprey.bin")) {
|
|
return SONG_DATA_FILE_TYPE_CHECKSUM;
|
|
}
|
|
|
|
return SONG_DATA_FILE_TYPE_UNKNKOWN;
|
|
}
|
|
|
|
static void chart_patch_get_path_dot_1_file(const wchar_t *song_id, enum song_data_path_format path_format, wchar_t *path)
|
|
{
|
|
switch (path_format) {
|
|
case SONG_DATA_PATH_09_TO_17:
|
|
// Note: These use %s also for wide strings!
|
|
wsprintfW(
|
|
path,
|
|
L"..\\data\\sd_data\\%s\\%s.1",
|
|
song_id,
|
|
song_id);
|
|
break;
|
|
|
|
case SONG_DATA_PATH_18:
|
|
// Note: These use %s also for wide strings!
|
|
wsprintfW(
|
|
path,
|
|
L".\\data\\sd_data\\%s\\%s.1",
|
|
song_id,
|
|
song_id);
|
|
break;
|
|
|
|
case SONG_DATA_PATH_19:
|
|
// Note: These use %s also for wide strings!
|
|
wsprintfW(
|
|
path,
|
|
L".\\data\\sound\\%s\\%s.1",
|
|
song_id,
|
|
song_id);
|
|
break;
|
|
|
|
case SONG_DATA_FILE_TYPE_UNKNKOWN:
|
|
default:
|
|
log_fatal("Illegal state");
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void chart_patch_get_path_checksum_file(const wchar_t *song_id, enum song_data_path_format path_format, wchar_t *path)
|
|
{
|
|
switch (path_format) {
|
|
case SONG_DATA_PATH_09_TO_17:
|
|
// Note: These use %s also for wide strings!
|
|
wsprintfW(
|
|
path,
|
|
L"..\\data\\sd_data\\%s\\osprey.bin",
|
|
song_id);
|
|
break;
|
|
|
|
case SONG_DATA_PATH_18:
|
|
// Note: These use %s also for wide strings!
|
|
wsprintfW(
|
|
path,
|
|
L".\\data\\sd_data\\%s\\osprey.bin",
|
|
song_id);
|
|
break;
|
|
|
|
case SONG_DATA_PATH_19:
|
|
// Note: These use %s also for wide strings!
|
|
wsprintfW(
|
|
path,
|
|
L".\\data\\sound\\%s\\osprey.bin",
|
|
song_id);
|
|
break;
|
|
|
|
case SONG_DATA_FILE_TYPE_UNKNKOWN:
|
|
default:
|
|
log_fatal("Illegal state");
|
|
break;
|
|
}
|
|
}
|
|
|
|
static bool chart_patch_file_trap(struct irp *irp)
|
|
{
|
|
enum song_data_path_format path_format;
|
|
enum song_data_file_type file_type;
|
|
|
|
wchar_t buffer_1_path[MAX_PATH];
|
|
wchar_t buffer_checksum_path[MAX_PATH];
|
|
wchar_t song_id[5];
|
|
wchar_t chart_file_name[7];
|
|
|
|
char *tmp;
|
|
|
|
/* This has to trap three different scenarios:
|
|
1. open .1 chart file only
|
|
2. open .1 chart file first, then osprey.bin
|
|
3. open osprey.bin first, then .1 chart file
|
|
In the end, we have to serve either just the .1 file or both the .1
|
|
and checksum file from memory until the application closes one or both
|
|
files */
|
|
|
|
path_format = chart_patch_is_song_data_path(irp->open_filename, song_id);
|
|
|
|
if (path_format != SONG_DATA_PATH_INVALID) {
|
|
EnterCriticalSection(&chart_patch_lock);
|
|
|
|
file_type = chart_patch_get_song_data_file_type(irp->open_filename, song_id);
|
|
|
|
// %S for printing wide strings
|
|
log_misc("Trapping song ID %S, path format %d, file type %d, path %S", song_id, path_format, file_type, irp->open_filename);
|
|
|
|
/* Block a second open call to either .1 or checksum file if detouring
|
|
is already active */
|
|
if (chart_patch_file_detour) {
|
|
switch (file_type) {
|
|
case SONG_DATA_FILE_TYPE_DOT_1:
|
|
irp->fd = chart_patch_file_1.fd;
|
|
chart_patch_file_1.active = true;
|
|
break;
|
|
|
|
case SONG_DATA_FILE_TYPE_CHECKSUM:
|
|
irp->fd = chart_patch_file_checksum.fd;
|
|
chart_patch_file_checksum.active = true;
|
|
break;
|
|
|
|
case SONG_DATA_FILE_TYPE_UNKNKOWN:
|
|
default:
|
|
LeaveCriticalSection(&chart_patch_lock);
|
|
/* other file, like *.2dx */
|
|
return false;
|
|
}
|
|
|
|
log_misc("Data already prepared");
|
|
|
|
LeaveCriticalSection(&chart_patch_lock);
|
|
|
|
return true;
|
|
}
|
|
|
|
if (file_type == SONG_DATA_FILE_TYPE_DOT_1 || file_type == SONG_DATA_FILE_TYPE_CHECKSUM) {
|
|
log_misc("Preparing in-memory chart data for song ID %S...", song_id);
|
|
|
|
chart_patch_get_song_chart_file_name(song_id, chart_file_name);
|
|
chart_patch_get_path_dot_1_file(song_id, path_format, buffer_1_path);
|
|
chart_patch_get_path_checksum_file(song_id, path_format, buffer_checksum_path);
|
|
|
|
if (!chart_patch_file_load(
|
|
irp, &chart_patch_file_1, buffer_1_path)) {
|
|
log_fatal("Cannot load .1 file %S", buffer_1_path);
|
|
}
|
|
|
|
chart_patch_execute_patch_1();
|
|
|
|
wstr_narrow(chart_file_name, &tmp);
|
|
memcpy(
|
|
chart_patch_file_1_name, tmp, sizeof(chart_patch_file_1_name));
|
|
free(tmp);
|
|
|
|
/* Optional on some games */
|
|
if (chart_patch_file_load(
|
|
irp, &chart_patch_file_checksum, buffer_checksum_path)) {
|
|
chart_patch_execute_patch_checksum();
|
|
}
|
|
|
|
/* Return dummy handle */
|
|
switch (file_type) {
|
|
case SONG_DATA_FILE_TYPE_DOT_1:
|
|
irp->fd = chart_patch_file_1.fd;
|
|
chart_patch_file_1.active = true;
|
|
break;
|
|
|
|
case SONG_DATA_FILE_TYPE_CHECKSUM:
|
|
irp->fd = chart_patch_file_checksum.fd;
|
|
chart_patch_file_checksum.active = true;
|
|
break;
|
|
|
|
case SONG_DATA_FILE_TYPE_UNKNKOWN:
|
|
default:
|
|
log_fatal("Illegal state 2");
|
|
break;
|
|
}
|
|
|
|
log_misc("Chart patch for song ID %S prepared in memory", song_id);
|
|
|
|
chart_patch_file_detour = true;
|
|
|
|
LeaveCriticalSection(&chart_patch_lock);
|
|
|
|
return true;
|
|
}
|
|
|
|
LeaveCriticalSection(&chart_patch_lock);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static HRESULT
|
|
chart_patch_file_close_irp(struct chart_patch_file *file, struct irp *irp_close)
|
|
{
|
|
HRESULT hr;
|
|
struct irp irp;
|
|
|
|
file->active = false;
|
|
|
|
// log_misc("chart_patch_file_close_irp %d %p %d %p",
|
|
// chart_patch_file_1.active,
|
|
// chart_patch_file_1.fd,
|
|
// chart_patch_file_checksum.active,
|
|
// chart_patch_file_checksum.fd);
|
|
|
|
/* Check if both files are closed and free resources */
|
|
if (!chart_patch_file_1.active && !chart_patch_file_checksum.active) {
|
|
log_misc("Closing fd %p and cleaning up", file->fd);
|
|
|
|
if (chart_patch_file_1.fd != NULL) {
|
|
memcpy(&irp, irp_close, sizeof(struct irp));
|
|
irp.fd = chart_patch_file_1.fd;
|
|
|
|
hr = iohook_invoke_next(&irp);
|
|
|
|
if (hr != S_OK) {
|
|
log_warning("Closing dummy .1 file handle failed");
|
|
}
|
|
|
|
free((void *) chart_patch_file_1.state.bytes);
|
|
memset(&chart_patch_file_1, 0, sizeof(struct chart_patch_file));
|
|
}
|
|
|
|
if (chart_patch_file_checksum.fd != NULL) {
|
|
memcpy(&irp, irp_close, sizeof(struct irp));
|
|
irp.fd = chart_patch_file_checksum.fd;
|
|
|
|
hr = iohook_invoke_next(&irp);
|
|
|
|
if (hr != S_OK) {
|
|
log_warning("Closing dummy checksum file handle failed");
|
|
}
|
|
|
|
free((void *) chart_patch_file_checksum.state.bytes);
|
|
memset(
|
|
&chart_patch_file_checksum, 0, sizeof(struct chart_patch_file));
|
|
}
|
|
|
|
chart_patch_file_detour = false;
|
|
} else {
|
|
log_misc("Closing fd %p", file->fd);
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT
|
|
chart_patch_file_read(struct chart_patch_file *file, struct irp *irp)
|
|
{
|
|
// log_misc("chart_patch_file_read %p", file->fd);
|
|
|
|
iobuf_move(&irp->read, &file->state);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT
|
|
chart_patch_file_seek(struct chart_patch_file *file, struct irp *irp)
|
|
{
|
|
ssize_t pos;
|
|
|
|
switch (irp->seek_origin) {
|
|
case FILE_BEGIN:
|
|
pos = irp->seek_offset;
|
|
|
|
break;
|
|
|
|
case FILE_CURRENT:
|
|
pos = irp->seek_offset + file->state.pos;
|
|
|
|
break;
|
|
|
|
case FILE_END:
|
|
pos = irp->seek_offset + file->state.nbytes;
|
|
|
|
break;
|
|
|
|
default:
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
file->state.pos = max(0, min(pos, file->state.nbytes));
|
|
irp->seek_pos = file->state.pos;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT
|
|
chart_patch_file_dispatch_irp(struct chart_patch_file *file, struct irp *irp)
|
|
{
|
|
HRESULT hr;
|
|
|
|
EnterCriticalSection(&chart_patch_lock);
|
|
|
|
switch (irp->op) {
|
|
case IRP_OP_CLOSE:
|
|
hr = chart_patch_file_close_irp(file, irp);
|
|
break;
|
|
case IRP_OP_READ:
|
|
hr = chart_patch_file_read(file, irp);
|
|
break;
|
|
case IRP_OP_WRITE:
|
|
hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED);
|
|
break;
|
|
case IRP_OP_SEEK:
|
|
hr = chart_patch_file_seek(file, irp);
|
|
break;
|
|
default:
|
|
hr = E_NOTIMPL;
|
|
break;
|
|
}
|
|
|
|
LeaveCriticalSection(&chart_patch_lock);
|
|
|
|
return hr;
|
|
}
|
|
|
|
void iidxhook_util_chart_patch_init(double orig_timebase)
|
|
{
|
|
log_assert(!chart_patch_initted);
|
|
|
|
chart_patch_file_detour = false;
|
|
|
|
chart_patch_timebase = orig_timebase;
|
|
|
|
chart_patch_file_1.fd = INVALID_HANDLE_VALUE;
|
|
chart_patch_file_checksum.fd = INVALID_HANDLE_VALUE;
|
|
|
|
InitializeCriticalSection(&chart_patch_lock);
|
|
|
|
chart_patch_initted = true;
|
|
|
|
log_info("Initialized with orig_timebase %f", orig_timebase);
|
|
}
|
|
|
|
void iidxhook_util_chart_patch_fini(void)
|
|
{
|
|
log_assert(chart_patch_initted);
|
|
|
|
DeleteCriticalSection(&chart_patch_lock);
|
|
chart_patch_initted = false;
|
|
|
|
log_info("Fini");
|
|
}
|
|
|
|
void iidxhook_util_chart_patch_set_refresh_rate(double hz)
|
|
{
|
|
chart_patch_hz = hz;
|
|
|
|
log_info("Set target refresh rate %f", chart_patch_hz);
|
|
}
|
|
|
|
HRESULT
|
|
iidxhook_util_chart_patch_dispatch_irp(struct irp *irp)
|
|
{
|
|
HRESULT hr;
|
|
|
|
if (chart_patch_initted) {
|
|
hr = S_FALSE;
|
|
|
|
if (irp->op == IRP_OP_OPEN) {
|
|
if (chart_patch_file_trap(irp)) {
|
|
hr = S_OK;
|
|
} else {
|
|
hr = iohook_invoke_next(irp);
|
|
}
|
|
} else {
|
|
if (irp->fd != INVALID_HANDLE_VALUE && irp->fd != NULL) {
|
|
if (irp->fd == chart_patch_file_1.fd) {
|
|
hr =
|
|
chart_patch_file_dispatch_irp(&chart_patch_file_1, irp);
|
|
} else if (irp->fd == chart_patch_file_checksum.fd) {
|
|
hr = chart_patch_file_dispatch_irp(
|
|
&chart_patch_file_checksum, irp);
|
|
} else {
|
|
hr = iohook_invoke_next(irp);
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
} else {
|
|
return iohook_invoke_next(irp);
|
|
}
|
|
} |