Bemanitools v5.26 release
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
libs += iidxhook-util
|
||||
|
||||
libs_iidxhook-util := \
|
||||
util \
|
||||
|
||||
src_iidxhook-util := \
|
||||
acio.c \
|
||||
chart-patch.c \
|
||||
clock.c \
|
||||
config-eamuse.c \
|
||||
config-gfx.c \
|
||||
config-misc.c \
|
||||
config-sec.c \
|
||||
d3d8.c \
|
||||
d3d9.c \
|
||||
eamuse.c \
|
||||
effector.c \
|
||||
log-server.c \
|
||||
settings.c \
|
||||
@@ -0,0 +1,103 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <ntdef.h>
|
||||
#include <devioctl.h>
|
||||
#include <ntddser.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <wchar.h>
|
||||
|
||||
#include "acioemu/addr.h"
|
||||
#include "acioemu/emu.h"
|
||||
#include "acioemu/icca.h"
|
||||
|
||||
#include "hook/iohook.h"
|
||||
|
||||
#include "iidxhook-util/acio.h"
|
||||
|
||||
#include "imports/avs.h"
|
||||
|
||||
#include "util/defs.h"
|
||||
#include "util/iobuf.h"
|
||||
#include "util/log.h"
|
||||
#include "util/str.h"
|
||||
|
||||
static struct ac_io_emu iidxhook_util_acio_emu;
|
||||
static struct ac_io_emu_icca iidxhook_util_acio_emu_icca[2];
|
||||
|
||||
void iidxhook_util_acio_init(bool legacy_mode)
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
if (legacy_mode) {
|
||||
ac_io_legacy_mode();
|
||||
}
|
||||
|
||||
ac_io_emu_init(&iidxhook_util_acio_emu, L"COM1");
|
||||
|
||||
for (i = 0 ; i < lengthof(iidxhook_util_acio_emu_icca) ; i++) {
|
||||
ac_io_emu_icca_init(&iidxhook_util_acio_emu_icca[i],
|
||||
&iidxhook_util_acio_emu, i);
|
||||
}
|
||||
}
|
||||
|
||||
void iidxhook_util_acio_fini(void)
|
||||
{
|
||||
ac_io_emu_fini(&iidxhook_util_acio_emu);
|
||||
}
|
||||
|
||||
HRESULT iidxhook_util_acio_dispatch_irp(struct irp *irp)
|
||||
{
|
||||
const struct ac_io_message *msg;
|
||||
HRESULT hr;
|
||||
|
||||
log_assert(irp != NULL);
|
||||
|
||||
if (!ac_io_emu_match_irp(&iidxhook_util_acio_emu, irp)) {
|
||||
return irp_invoke_next(irp);
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
hr = ac_io_emu_dispatch_irp(&iidxhook_util_acio_emu, irp);
|
||||
|
||||
if (hr != S_OK) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
msg = ac_io_emu_request_peek(&iidxhook_util_acio_emu);
|
||||
|
||||
switch (msg->addr) {
|
||||
case 0:
|
||||
ac_io_emu_cmd_assign_addrs(&iidxhook_util_acio_emu, msg, 2);
|
||||
|
||||
break;
|
||||
|
||||
case 1:
|
||||
ac_io_emu_icca_dispatch_request(&iidxhook_util_acio_emu_icca[0],
|
||||
msg);
|
||||
|
||||
break;
|
||||
|
||||
case 2:
|
||||
ac_io_emu_icca_dispatch_request(&iidxhook_util_acio_emu_icca[1],
|
||||
msg);
|
||||
|
||||
break;
|
||||
|
||||
case AC_IO_BROADCAST:
|
||||
log_warning("Broadcast(?) message on IIDX ACIO bus?");
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
log_warning("ACIO message on unhandled bus address: %d",
|
||||
msg->addr);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
ac_io_emu_request_pop(&iidxhook_util_acio_emu);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
#ifndef IIDXHOOK_UTIL_ACIO_H
|
||||
#define IIDXHOOK_UTIL_ACIO_H
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "hook/iohook.h"
|
||||
|
||||
/**
|
||||
* Initialize an emulated ACIO on COM1 for IIDX.
|
||||
*
|
||||
* @param legacy_mode Set to true if running on slotted readers on iidx 13-18.
|
||||
* Old games using slotted readers might use an old libacio version that
|
||||
* does not support handling of multi messages per package. Instead,
|
||||
* legacy mode responds with one message per acio package. The game
|
||||
* logs MiCmd Retry errors with the code 0x00000002 if this option is
|
||||
* not set correctly.
|
||||
*/
|
||||
void iidxhook_util_acio_init(bool legacy_mode);
|
||||
|
||||
/**
|
||||
* Cleanup the ACIO emulation layer.
|
||||
*/
|
||||
void iidxhook_util_acio_fini(void);
|
||||
|
||||
/**
|
||||
* iohook dispatch function. Needs to be installed.
|
||||
*/
|
||||
HRESULT iidxhook_util_acio_dispatch_irp(struct irp *irp);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,536 @@
|
||||
#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;
|
||||
};
|
||||
|
||||
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 = irp_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 = irp_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 = irp_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 = irp_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 = irp_invoke_next(&irp);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
log_fatal("Closing failed");
|
||||
}
|
||||
|
||||
file->fd = iohook_open_dummy_fd();
|
||||
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_chart_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 bool chart_patch_file_trap(struct irp* irp)
|
||||
{
|
||||
wchar_t buffer_1_path[1024];
|
||||
wchar_t buffer_checksum_path[1024];
|
||||
wchar_t chart_id[5];
|
||||
wchar_t chart_file[7];
|
||||
bool is_1_file;
|
||||
bool is_checksum_file;
|
||||
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 */
|
||||
if (swscanf(irp->open_filename, L"..\\data\\sd_data%*[\\/]%c%c%c%c%*[\\/]",
|
||||
&chart_id[0], &chart_id[1], &chart_id[2], &chart_id[3]) == 4 &&
|
||||
chart_patch_is_chart_id(chart_id)) {
|
||||
chart_id[4] = '\0';
|
||||
|
||||
wstr_narrow(irp->open_filename, &tmp);
|
||||
log_misc("Trapping %s...", tmp);
|
||||
free(tmp);
|
||||
|
||||
memcpy(chart_file, chart_id, sizeof(chart_id));
|
||||
chart_file[4] = L'.';
|
||||
chart_file[5] = L'1';
|
||||
chart_file[6] = L'\0';
|
||||
|
||||
is_1_file = wstr_ends_with(irp->open_filename, chart_file);
|
||||
is_checksum_file = wstr_ends_with(irp->open_filename, L"osprey.bin");
|
||||
|
||||
/* Block a second open call to either .1 or checksum file if detouring
|
||||
is already active */
|
||||
if (chart_patch_file_detour) {
|
||||
if (is_1_file) {
|
||||
irp->fd = chart_patch_file_1.fd;
|
||||
chart_patch_file_1.active = true;
|
||||
} else if (is_checksum_file) {
|
||||
irp->fd = chart_patch_file_checksum.fd;
|
||||
chart_patch_file_checksum.active = true;
|
||||
} else {
|
||||
/* other file, like *.2dx */
|
||||
return false;
|
||||
}
|
||||
|
||||
log_misc("Data already prepared");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (is_1_file || is_checksum_file) {
|
||||
wstr_narrow(chart_id, &tmp);
|
||||
log_misc("Preparing in-memory chart %s...", tmp);
|
||||
free(tmp);
|
||||
|
||||
wsprintfW(buffer_1_path, L"..\\data\\sd_data\\\\%s\\%s.1", chart_id,
|
||||
chart_id);
|
||||
wsprintfW(buffer_checksum_path,
|
||||
L"..\\data\\sd_data\\%s\\osprey.bin", chart_id);
|
||||
|
||||
if (!chart_patch_file_load(irp, &chart_patch_file_1,
|
||||
buffer_1_path)) {
|
||||
wstr_narrow(buffer_1_path, &tmp);
|
||||
log_fatal("Cannot load .1 file %s", tmp);
|
||||
free(tmp);
|
||||
}
|
||||
|
||||
chart_patch_execute_patch_1();
|
||||
|
||||
wstr_narrow(chart_file, &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 */
|
||||
if (is_1_file) {
|
||||
irp->fd = chart_patch_file_1.fd;
|
||||
chart_patch_file_1.active = true;
|
||||
} else if (is_checksum_file) {
|
||||
irp->fd = chart_patch_file_checksum.fd;
|
||||
chart_patch_file_checksum.active = true;
|
||||
} else {
|
||||
log_fatal("Illegal state 2");
|
||||
}
|
||||
|
||||
wstr_narrow(chart_id, &tmp);
|
||||
log_misc("Patch for %s prepared in memory", tmp);
|
||||
free(tmp);
|
||||
|
||||
chart_patch_file_detour = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
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 = irp_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 = irp_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)
|
||||
{
|
||||
// log_misc("chart_patch_file_dispatch_irp %p", file->fd);
|
||||
|
||||
switch (irp->op) {
|
||||
case IRP_OP_CLOSE: return chart_patch_file_close_irp(file, irp);
|
||||
case IRP_OP_READ: return chart_patch_file_read(file, irp);
|
||||
case IRP_OP_WRITE: return HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED);
|
||||
case IRP_OP_SEEK: return chart_patch_file_seek(file, irp);
|
||||
default: return E_NOTIMPL;
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
EnterCriticalSection(&chart_patch_lock);
|
||||
|
||||
hr = S_FALSE;
|
||||
|
||||
if (irp->op == IRP_OP_OPEN) {
|
||||
if (chart_patch_file_trap(irp)) {
|
||||
hr = S_OK;
|
||||
} else {
|
||||
hr = irp_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 = irp_invoke_next(irp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LeaveCriticalSection(&chart_patch_lock);
|
||||
|
||||
return hr;
|
||||
} else {
|
||||
return irp_invoke_next(irp);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
#ifndef IIDXHOOK_UTIL_CHART_PATCH_H
|
||||
#define IIDXHOOK_UTIL_CHART_PATCH_H
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "hook/iohook.h"
|
||||
|
||||
/* 9th to DD */
|
||||
#define IIDXHOOK_UTIL_CHART_PATCH_TIMEBASE_9_TO_13 59.95
|
||||
/* GOLD to Resort Anthem */
|
||||
#define IIDXHOOK_UTIL_CHART_PATCH_TIMEBASE_14_TO_18_VGA 60.05
|
||||
|
||||
/**
|
||||
* Install hooks to intercept chart file loading. This allows you to patch
|
||||
* the charts in-memory to modify their timestamps and making the game run
|
||||
* on-sync on non-stock setups with different display refresh rates than
|
||||
* 59.94 or 60.04 (depending on game version).
|
||||
*
|
||||
* @param orig_timebase The original refresh rate (timebase value) of the game.
|
||||
* Use one of the defined macros.
|
||||
*/
|
||||
void iidxhook_util_chart_patch_init(double orig_timebase);
|
||||
|
||||
/**
|
||||
* Shut down this module.
|
||||
*/
|
||||
void iidxhook_util_chart_patch_fini(void);
|
||||
|
||||
/**
|
||||
* Set the target refresh rate of the display. Determine this value either
|
||||
* by using the d3d8 or d3d9 monitor check of bemanitools or get it from another
|
||||
* source (e.g. one of the newer games in-built monitor checks).
|
||||
*
|
||||
* @param hz Refresh rate of the display. Ensure your display provides a
|
||||
* constant value.
|
||||
*/
|
||||
void iidxhook_util_chart_patch_set_refresh_rate(double hz);
|
||||
|
||||
/**
|
||||
* iohook dispatch function. Needs to be installed.
|
||||
*/
|
||||
HRESULT iidxhook_util_chart_patch_dispatch_irp(struct irp *irp);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,37 @@
|
||||
#define LOG_MODULE "clock-hook"
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "hook/table.h"
|
||||
|
||||
#include "util/defs.h"
|
||||
#include "util/log.h"
|
||||
|
||||
static BOOL STDCALL my_SetLocalTime(const SYSTEMTIME* lpSystemTime);
|
||||
static BOOL (STDCALL *real_SetLocalTime)(const SYSTEMTIME* lpSystemTime);
|
||||
|
||||
static const struct hook_symbol clock_hook_syms[] = {
|
||||
{
|
||||
.name = "SetLocalTime",
|
||||
.patch = my_SetLocalTime,
|
||||
.link = (void **) &real_SetLocalTime
|
||||
},
|
||||
};
|
||||
|
||||
static BOOL STDCALL my_SetLocalTime(const SYSTEMTIME* lpSystemTime)
|
||||
{
|
||||
/* Stub, don't mess with system clock */
|
||||
log_misc("Blocked setting system clock time");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void iidxhook_util_clock_hook_init(void)
|
||||
{
|
||||
hook_table_apply(
|
||||
NULL,
|
||||
"kernel32.dll",
|
||||
clock_hook_syms,
|
||||
lengthof(clock_hook_syms));
|
||||
|
||||
log_info("Inserted clock hooks");
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#ifndef IIDXHOOK_UTIL_CLOCK_H
|
||||
#define IIDXHOOK_UTIL_CLOCK_H
|
||||
|
||||
/**
|
||||
* Hook clock related calls to avoid chaning the system clock
|
||||
* time from the operator menu. 9th style is a special case here as it
|
||||
* allows a max. year of 2009, only.
|
||||
*/
|
||||
void iidxhook_util_clock_hook_init(void);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,129 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "cconfig/cconfig-util.h"
|
||||
|
||||
#include "iidxhook-util/config-eamuse.h"
|
||||
|
||||
#include "security/mcode.h"
|
||||
|
||||
#include "util/log.h"
|
||||
|
||||
#define IIDXHOOK_CONFIG_EAMUSE_CARD_TYPE_KEY "eamuse.card_type"
|
||||
#define IIDXHOOK_CONFIG_EAMUSE_SERVER_KEY "eamuse.server"
|
||||
#define IIDXHOOK_CONFIG_EAMUSE_PCBID_KEY "eamuse.pcbid"
|
||||
#define IIDXHOOK_CONFIG_EAMUSE_EAMID_KEY "eamuse.eamid"
|
||||
|
||||
#define IIDXHOOK_CONFIG_EAMUSE_DEFAULT_CARD_TYPE_VALUE SECURITY_MCODE_GAME_IIDX_9
|
||||
#define IIDXHOOK_CONFIG_EAMUSE_DEFAULT_SERVER_VALUE "localhost:80"
|
||||
#define IIDXHOOK_CONFIG_EAMUSE_DEFAULT_PCBID_VALUE security_id_default
|
||||
#define IIDXHOOK_CONFIG_EAMUSE_DEFAULT_PCBID_VALUE_LEN sizeof(security_id_default)
|
||||
#define IIDXHOOK_CONFIG_EAMUSE_DEFAULT_EAMID_VALUE security_id_default
|
||||
#define IIDXHOOK_CONFIG_EAMUSE_DEFAULT_EAMID_VALUE_LEN sizeof(security_id_default)
|
||||
|
||||
const struct net_addr iidxhook_eamuse_default_server = {
|
||||
.type = NET_ADDR_TYPE_HOSTNAME,
|
||||
.hostname.host = "localhost",
|
||||
.hostname.port = 80,
|
||||
};
|
||||
|
||||
void iidxhook_util_config_eamuse_init(struct cconfig* config)
|
||||
{
|
||||
cconfig_util_set_str(config,
|
||||
IIDXHOOK_CONFIG_EAMUSE_CARD_TYPE_KEY,
|
||||
IIDXHOOK_CONFIG_EAMUSE_DEFAULT_CARD_TYPE_VALUE,
|
||||
"Magnetic card type, format XXX, 3 digit string (supports: C02, D01, "
|
||||
"E11, ECO)");
|
||||
|
||||
cconfig_util_set_str(config,
|
||||
IIDXHOOK_CONFIG_EAMUSE_SERVER_KEY,
|
||||
IIDXHOOK_CONFIG_EAMUSE_DEFAULT_SERVER_VALUE,
|
||||
"URL (e.g. http://my.eamuse.server:80/whatever) or IPV4 "
|
||||
"(e.g. 127.0.0.1:80) of the target eamuse server. The port is optional "
|
||||
"but defaults to 80.");
|
||||
|
||||
cconfig_util_set_data(config, IIDXHOOK_CONFIG_EAMUSE_PCBID_KEY,
|
||||
(uint8_t*) &IIDXHOOK_CONFIG_EAMUSE_DEFAULT_PCBID_VALUE,
|
||||
IIDXHOOK_CONFIG_EAMUSE_DEFAULT_PCBID_VALUE_LEN, "PCBID");
|
||||
|
||||
cconfig_util_set_data(config, IIDXHOOK_CONFIG_EAMUSE_EAMID_KEY,
|
||||
(uint8_t*) &IIDXHOOK_CONFIG_EAMUSE_DEFAULT_EAMID_VALUE,
|
||||
IIDXHOOK_CONFIG_EAMUSE_DEFAULT_EAMID_VALUE_LEN, "EAMID");
|
||||
}
|
||||
|
||||
void iidxhook_util_config_eamuse_get(
|
||||
struct iidxhook_util_config_eamuse* config_eamuse,
|
||||
struct cconfig* config)
|
||||
{
|
||||
char server_url[1024];
|
||||
char* tmp;
|
||||
char* tmp2;
|
||||
|
||||
memset(config_eamuse, 0, sizeof(struct iidxhook_util_config_eamuse));
|
||||
|
||||
if (!cconfig_util_get_str(config, IIDXHOOK_CONFIG_EAMUSE_CARD_TYPE_KEY,
|
||||
config_eamuse->card_type, sizeof(config_eamuse->card_type) - 1,
|
||||
IIDXHOOK_CONFIG_EAMUSE_DEFAULT_CARD_TYPE_VALUE)) {
|
||||
log_warning("Invalid value for key '%s' specified, fallback "
|
||||
"to default '%s'", IIDXHOOK_CONFIG_EAMUSE_CARD_TYPE_KEY,
|
||||
IIDXHOOK_CONFIG_EAMUSE_DEFAULT_CARD_TYPE_VALUE);
|
||||
}
|
||||
|
||||
if (!cconfig_util_get_str(config, IIDXHOOK_CONFIG_EAMUSE_SERVER_KEY,
|
||||
server_url, sizeof(server_url),
|
||||
IIDXHOOK_CONFIG_EAMUSE_DEFAULT_SERVER_VALUE)) {
|
||||
log_warning("Invalid value for key '%s' specified, fallback "
|
||||
"to default '%s'", IIDXHOOK_CONFIG_EAMUSE_SERVER_KEY,
|
||||
IIDXHOOK_CONFIG_EAMUSE_DEFAULT_SERVER_VALUE);
|
||||
}
|
||||
|
||||
if (!net_str_parse(server_url, &config_eamuse->server)) {
|
||||
memcpy(&config_eamuse->server, &iidxhook_eamuse_default_server,
|
||||
sizeof(config_eamuse->server));
|
||||
tmp = net_addr_to_str(&config_eamuse->server);
|
||||
log_warning("Invalid value for key '%s' specified, fallback "
|
||||
"to default", tmp);
|
||||
free(tmp);
|
||||
}
|
||||
|
||||
if (!cconfig_util_get_data(config, IIDXHOOK_CONFIG_EAMUSE_PCBID_KEY,
|
||||
(uint8_t*) &config_eamuse->pcbid,
|
||||
IIDXHOOK_CONFIG_EAMUSE_DEFAULT_PCBID_VALUE_LEN,
|
||||
(uint8_t*) &IIDXHOOK_CONFIG_EAMUSE_DEFAULT_PCBID_VALUE)) {
|
||||
tmp = security_id_to_str(&IIDXHOOK_CONFIG_EAMUSE_DEFAULT_PCBID_VALUE,
|
||||
false);
|
||||
log_warning("Invalid value for key '%s' specified, fallback "
|
||||
"to default", tmp);
|
||||
free(tmp);
|
||||
}
|
||||
|
||||
if (!security_id_verify(&config_eamuse->pcbid)) {
|
||||
tmp = security_id_to_str(&IIDXHOOK_CONFIG_EAMUSE_DEFAULT_PCBID_VALUE,
|
||||
false);
|
||||
tmp2 = security_id_to_str(&config_eamuse->pcbid, false);
|
||||
log_warning("PCBID verification of '%s' failed, fallback to default "
|
||||
"PCBID '%s'", tmp2, tmp);
|
||||
free(tmp);
|
||||
free(tmp2);
|
||||
}
|
||||
|
||||
if (!cconfig_util_get_data(config, IIDXHOOK_CONFIG_EAMUSE_EAMID_KEY,
|
||||
(uint8_t*) &config_eamuse->eamid,
|
||||
IIDXHOOK_CONFIG_EAMUSE_DEFAULT_EAMID_VALUE_LEN,
|
||||
(uint8_t*) &IIDXHOOK_CONFIG_EAMUSE_DEFAULT_EAMID_VALUE)) {
|
||||
tmp = security_id_to_str(&IIDXHOOK_CONFIG_EAMUSE_DEFAULT_EAMID_VALUE,
|
||||
false);
|
||||
log_warning("Invalid value for key '%s' specified, fallback "
|
||||
"to default", tmp);
|
||||
free(tmp);
|
||||
}
|
||||
|
||||
if (!security_id_verify(&config_eamuse->eamid)) {
|
||||
tmp = security_id_to_str(&IIDXHOOK_CONFIG_EAMUSE_DEFAULT_EAMID_VALUE,
|
||||
false);
|
||||
tmp2 = security_id_to_str(&config_eamuse->eamid, false);
|
||||
log_warning("EAMID verification of '%s' failed, fallback to default "
|
||||
"EAMID '%s'", tmp2, tmp);
|
||||
free(tmp);
|
||||
free(tmp2);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
#ifndef IIDXHOOK_UTIL_CONFIG_EAMUSE_H
|
||||
#define IIDXHOOK_UTIL_CONFIG_EAMUSE_H
|
||||
|
||||
#include "cconfig/cconfig.h"
|
||||
|
||||
#include "security/id.h"
|
||||
|
||||
#include "util/net.h"
|
||||
|
||||
struct iidxhook_util_config_eamuse {
|
||||
char card_type[4];
|
||||
struct net_addr server;
|
||||
struct security_id pcbid;
|
||||
struct security_id eamid;
|
||||
};
|
||||
|
||||
void iidxhook_util_config_eamuse_init(struct cconfig* config);
|
||||
|
||||
void iidxhook_util_config_eamuse_get(
|
||||
struct iidxhook_util_config_eamuse* config_eamuse,
|
||||
struct cconfig* config);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,235 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "cconfig/cconfig-util.h"
|
||||
|
||||
#include "iidxhook-util/config-gfx.h"
|
||||
|
||||
#include "util/log.h"
|
||||
|
||||
#define IIDXHOOK_CONFIG_GFX_BGVIDEO_UV_FIX_KEY "gfx.bgvideo_uv_fix"
|
||||
#define IIDXHOOK_CONFIG_GFX_FRAMED_KEY "gfx.framed"
|
||||
#define IIDXHOOK_CONFIG_GFX_FRAME_RATE_LIMIT_KEY "gfx.frame_rate_limit"
|
||||
#define IIDXHOOK_CONFIG_GFX_MONITOR_CHECK_KEY "gfx.monitor_check"
|
||||
#define IIDXHOOK_CONFIG_GFX_PCI_ID_KEY "gfx.pci_id"
|
||||
#define IIDXHOOK_CONFIG_GFX_WINDOWED_KEY "gfx.windowed"
|
||||
#define IIDXHOOK_CONFIG_GFX_WINDOW_WIDTH_KEY "gfx.window_width"
|
||||
#define IIDXHOOK_CONFIG_GFX_WINDOW_HEIGHT_KEY "gfx.window_height"
|
||||
#define IIDXHOOK_CONFIG_GFX_SCALE_BACK_BUFFER_WIDTH_KEY "gfx.scale_back_buffer_width"
|
||||
#define IIDXHOOK_CONFIG_GFX_SCALE_BACK_BUFFER_HEIGHT_KEY "gfx.scale_back_buffer_height"
|
||||
#define IIDXHOOK_CONFIG_GFX_SCALE_BACK_BUFFER_FILTER_KEY "gfx.scale_back_buffer_filter"
|
||||
|
||||
#define IIDXHOOK_CONFIG_GFX_DEFAULT_BGVIDEO_UV_FIX_VALUE false
|
||||
#define IIDXHOOK_CONFIG_GFX_DEFAULT_FRAMED_VALUE false
|
||||
#define IIDXHOOK_CONFIG_GFX_DEFAULT_FRAME_RATE_LIMIT_VALUE 0.0
|
||||
#define IIDXHOOK_CONFIG_GFX_DEFAULT_MONITOR_CHECK_VALUE -1.0
|
||||
#define IIDXHOOK_CONFIG_GFX_DEFAULT_PCI_ID_VALUE "1002:7146"
|
||||
#define IIDXHOOK_CONFIG_GFX_DEFAULT_WINDOWED_VALUE false
|
||||
#define IIDXHOOK_CONFIG_GFX_DEFAULT_WINDOW_WIDTH_VALUE -1
|
||||
#define IIDXHOOK_CONFIG_GFX_DEFAULT_WINDOW_HEIGHT_VALUE -1
|
||||
#define IIDXHOOK_CONFIG_GFX_DEFAULT_SCALE_BACK_BUFFER_WIDTH_VALUE 0
|
||||
#define IIDXHOOK_CONFIG_GFX_DEFAULT_SCALE_BACK_BUFFER_HEIGHT_VALUE 0
|
||||
#define IIDXHOOK_CONFIG_GFX_DEFAULT_SCALE_BACK_BUFFER_FILTER_VALUE "none"
|
||||
|
||||
void iidxhook_config_gfx_init(struct cconfig* config)
|
||||
{
|
||||
cconfig_util_set_bool(config,
|
||||
IIDXHOOK_CONFIG_GFX_BGVIDEO_UV_FIX_KEY,
|
||||
IIDXHOOK_CONFIG_GFX_DEFAULT_BGVIDEO_UV_FIX_VALUE,
|
||||
"Fix stretched BG videos on newer GPUs. Might appear on Red and newer");
|
||||
|
||||
cconfig_util_set_bool(config,
|
||||
IIDXHOOK_CONFIG_GFX_FRAMED_KEY,
|
||||
IIDXHOOK_CONFIG_GFX_DEFAULT_FRAMED_VALUE,
|
||||
"Run the game in a framed window (requires windowed option)");
|
||||
|
||||
cconfig_util_set_float(config,
|
||||
IIDXHOOK_CONFIG_GFX_FRAME_RATE_LIMIT_KEY,
|
||||
IIDXHOOK_CONFIG_GFX_DEFAULT_FRAME_RATE_LIMIT_VALUE,
|
||||
"Software limit the frame rate of the rendering loop in hz, e.g. 60 or 59.95 (0.0 = no software limit)");
|
||||
|
||||
cconfig_util_set_float(config,
|
||||
IIDXHOOK_CONFIG_GFX_MONITOR_CHECK_KEY,
|
||||
IIDXHOOK_CONFIG_GFX_DEFAULT_MONITOR_CHECK_VALUE,
|
||||
"Enable/disable software monitor check/auto timebase or set "
|
||||
"a pre-determined refresh value. -1 disables this feature. 0 enables "
|
||||
"auto detecting the current refresh rate on startup. Setting any "
|
||||
"positive value > 0 allows you to set a pre-determined refresh rate "
|
||||
"(e.g. retrieved from the monitor check on newer IIDX games). Either "
|
||||
"the auto detected value or pre-determined value is used to patch any "
|
||||
"chart files in-memory to fix song synchronization issues. Requires "
|
||||
"constant refresh rate!!!");
|
||||
|
||||
cconfig_util_set_str(config,
|
||||
IIDXHOOK_CONFIG_GFX_PCI_ID_KEY,
|
||||
IIDXHOOK_CONFIG_GFX_DEFAULT_PCI_ID_VALUE,
|
||||
"Patch the GPU device ID detection (leave empty to"
|
||||
" disable), format XXXX:YYYY, two 4 digit hex numbers (vid:pid)."
|
||||
" Examples: 1002:7146 (RV515, Radeon X1300), 1002:95C5 (RV620 LE,"
|
||||
" Radeon HD3450)");
|
||||
|
||||
cconfig_util_set_bool(config,
|
||||
IIDXHOOK_CONFIG_GFX_WINDOWED_KEY,
|
||||
IIDXHOOK_CONFIG_GFX_DEFAULT_WINDOWED_VALUE,
|
||||
"Run the game windowed");
|
||||
|
||||
cconfig_util_set_int(config,
|
||||
IIDXHOOK_CONFIG_GFX_WINDOW_WIDTH_KEY,
|
||||
IIDXHOOK_CONFIG_GFX_DEFAULT_WINDOW_WIDTH_VALUE,
|
||||
"Windowed width, -1 for default size");
|
||||
|
||||
cconfig_util_set_int(config,
|
||||
IIDXHOOK_CONFIG_GFX_WINDOW_HEIGHT_KEY,
|
||||
IIDXHOOK_CONFIG_GFX_DEFAULT_WINDOW_HEIGHT_VALUE,
|
||||
"Windowed height, -1 for default size");
|
||||
|
||||
cconfig_util_set_int(config,
|
||||
IIDXHOOK_CONFIG_GFX_SCALE_BACK_BUFFER_WIDTH_KEY,
|
||||
IIDXHOOK_CONFIG_GFX_DEFAULT_SCALE_BACK_BUFFER_WIDTH_VALUE,
|
||||
"Up-/downscale the back buffer's width. This does not change the game's rendering resolution but scales the "
|
||||
"final frame. Use this to target the native resolution of your monitor/TV, e.g. to avoid over-/underscan, bad "
|
||||
"image quality or latency caused by the monitors internal upscaler. 0 to disable this feature. Must be set in "
|
||||
"combination with the corresponding height parameter.");
|
||||
|
||||
cconfig_util_set_int(config,
|
||||
IIDXHOOK_CONFIG_GFX_SCALE_BACK_BUFFER_HEIGHT_KEY,
|
||||
IIDXHOOK_CONFIG_GFX_DEFAULT_SCALE_BACK_BUFFER_HEIGHT_VALUE,
|
||||
"Up-/downscale the back buffer's height. This does not change the game's rendering resolution but scales the "
|
||||
"final frame. Use this to target the native resolution of your monitor/TV, e.g. to avoid over-/underscan, bad "
|
||||
"image quality or latency caused by the monitors internal upscaler. 0 to disable this feature. Must be set in "
|
||||
"combination with the corresponding width parameter.");
|
||||
|
||||
cconfig_util_set_str(config,
|
||||
IIDXHOOK_CONFIG_GFX_SCALE_BACK_BUFFER_FILTER_KEY,
|
||||
IIDXHOOK_CONFIG_GFX_DEFAULT_SCALE_BACK_BUFFER_FILTER_VALUE,
|
||||
"Filter type to use for up-/downscaling the back buffer. Only used if scaling feature was enabled by setting "
|
||||
"the scaling width and height parameters. Available types: none, linear, point (refer to D3DTEXTUREFILTERTYPE "
|
||||
" for explanation).");
|
||||
}
|
||||
|
||||
void iidxhook_config_gfx_get(struct iidxhook_config_gfx* config_gfx,
|
||||
struct cconfig* config)
|
||||
{
|
||||
char tmp[10];
|
||||
char* vid;
|
||||
char* pid;
|
||||
int32_t tmp_int;
|
||||
|
||||
if (!cconfig_util_get_bool(config, IIDXHOOK_CONFIG_GFX_BGVIDEO_UV_FIX_KEY,
|
||||
&config_gfx->bgvideo_uv_fix,
|
||||
IIDXHOOK_CONFIG_GFX_DEFAULT_BGVIDEO_UV_FIX_VALUE)) {
|
||||
log_warning("Invalid value for key '%s' specified, fallback "
|
||||
"to default '%d'", IIDXHOOK_CONFIG_GFX_BGVIDEO_UV_FIX_KEY,
|
||||
IIDXHOOK_CONFIG_GFX_DEFAULT_BGVIDEO_UV_FIX_VALUE);
|
||||
}
|
||||
|
||||
if (!cconfig_util_get_bool(config, IIDXHOOK_CONFIG_GFX_FRAMED_KEY,
|
||||
&config_gfx->framed,
|
||||
IIDXHOOK_CONFIG_GFX_DEFAULT_FRAMED_VALUE)) {
|
||||
log_warning("Invalid value for key '%s' specified, fallback "
|
||||
"to default '%d'", IIDXHOOK_CONFIG_GFX_FRAMED_KEY,
|
||||
IIDXHOOK_CONFIG_GFX_DEFAULT_FRAMED_VALUE);
|
||||
}
|
||||
|
||||
if (!cconfig_util_get_float(config, IIDXHOOK_CONFIG_GFX_FRAME_RATE_LIMIT_KEY,
|
||||
&config_gfx->frame_rate_limit,
|
||||
IIDXHOOK_CONFIG_GFX_DEFAULT_FRAME_RATE_LIMIT_VALUE)) {
|
||||
log_warning("Invalid value for key '%s' specified, fallback "
|
||||
"to default '%f'", IIDXHOOK_CONFIG_GFX_FRAME_RATE_LIMIT_KEY,
|
||||
IIDXHOOK_CONFIG_GFX_DEFAULT_FRAME_RATE_LIMIT_VALUE);
|
||||
}
|
||||
|
||||
if (!cconfig_util_get_float(config, IIDXHOOK_CONFIG_GFX_MONITOR_CHECK_KEY,
|
||||
&config_gfx->monitor_check,
|
||||
IIDXHOOK_CONFIG_GFX_DEFAULT_MONITOR_CHECK_VALUE)) {
|
||||
log_warning("Invalid value for key '%s' specified, fallback "
|
||||
"to default '%f'", IIDXHOOK_CONFIG_GFX_MONITOR_CHECK_KEY,
|
||||
IIDXHOOK_CONFIG_GFX_DEFAULT_MONITOR_CHECK_VALUE);
|
||||
}
|
||||
|
||||
if (!cconfig_util_get_str(config, IIDXHOOK_CONFIG_GFX_PCI_ID_KEY,
|
||||
tmp, sizeof(tmp) - 1,
|
||||
IIDXHOOK_CONFIG_GFX_DEFAULT_PCI_ID_VALUE)) {
|
||||
log_warning("Invalid value for key '%s' specified, fallback "
|
||||
"to default '%s'", IIDXHOOK_CONFIG_GFX_PCI_ID_KEY,
|
||||
IIDXHOOK_CONFIG_GFX_DEFAULT_PCI_ID_VALUE);
|
||||
}
|
||||
|
||||
if (tmp[4] != ':') {
|
||||
log_warning("Invalid format for value for key '%s' specified, fallback "
|
||||
"to default '%s'", IIDXHOOK_CONFIG_GFX_PCI_ID_KEY,
|
||||
IIDXHOOK_CONFIG_GFX_DEFAULT_PCI_ID_VALUE);
|
||||
strcpy(tmp, IIDXHOOK_CONFIG_GFX_DEFAULT_PCI_ID_VALUE);
|
||||
}
|
||||
|
||||
tmp[4] = '\0';
|
||||
vid = tmp;
|
||||
pid = &tmp[5];
|
||||
config_gfx->pci_id_vid = strtol(vid, NULL, 16);
|
||||
config_gfx->pci_id_pid = strtol(pid, NULL, 16);
|
||||
|
||||
if (!cconfig_util_get_bool(config, IIDXHOOK_CONFIG_GFX_WINDOWED_KEY,
|
||||
&config_gfx->windowed,
|
||||
IIDXHOOK_CONFIG_GFX_DEFAULT_WINDOWED_VALUE)) {
|
||||
log_warning("Invalid value for key '%s' specified, fallback "
|
||||
"to default '%d'", IIDXHOOK_CONFIG_GFX_WINDOWED_KEY,
|
||||
IIDXHOOK_CONFIG_GFX_DEFAULT_WINDOWED_VALUE);
|
||||
}
|
||||
|
||||
if (!cconfig_util_get_int(config, IIDXHOOK_CONFIG_GFX_WINDOW_WIDTH_KEY,
|
||||
&config_gfx->window_width, IIDXHOOK_CONFIG_GFX_DEFAULT_WINDOW_WIDTH_VALUE)) {
|
||||
log_warning("Invalid value for key '%s' specified, fallback "
|
||||
"to default '%d'", IIDXHOOK_CONFIG_GFX_WINDOW_WIDTH_KEY,
|
||||
IIDXHOOK_CONFIG_GFX_DEFAULT_WINDOW_WIDTH_VALUE);
|
||||
}
|
||||
|
||||
if (!cconfig_util_get_int(config, IIDXHOOK_CONFIG_GFX_WINDOW_HEIGHT_KEY,
|
||||
&config_gfx->window_height, IIDXHOOK_CONFIG_GFX_DEFAULT_WINDOW_HEIGHT_VALUE)) {
|
||||
log_warning("Invalid value for key '%s' specified, fallback "
|
||||
"to default '%d'", IIDXHOOK_CONFIG_GFX_WINDOW_HEIGHT_KEY,
|
||||
IIDXHOOK_CONFIG_GFX_DEFAULT_WINDOW_HEIGHT_VALUE);
|
||||
}
|
||||
|
||||
if (!cconfig_util_get_int(config, IIDXHOOK_CONFIG_GFX_SCALE_BACK_BUFFER_WIDTH_KEY,
|
||||
&tmp_int,
|
||||
IIDXHOOK_CONFIG_GFX_DEFAULT_SCALE_BACK_BUFFER_WIDTH_VALUE) ||
|
||||
tmp_int < 0 || tmp_int > 0xFFFF) {
|
||||
log_warning("Invalid value for key '%s' specified, fallback "
|
||||
"to default '%d'", IIDXHOOK_CONFIG_GFX_SCALE_BACK_BUFFER_WIDTH_KEY,
|
||||
IIDXHOOK_CONFIG_GFX_DEFAULT_SCALE_BACK_BUFFER_WIDTH_VALUE);
|
||||
}
|
||||
|
||||
config_gfx->scale_back_buffer_width = (uint16_t) tmp_int;
|
||||
|
||||
if (!cconfig_util_get_int(config, IIDXHOOK_CONFIG_GFX_SCALE_BACK_BUFFER_HEIGHT_KEY,
|
||||
&tmp_int,
|
||||
IIDXHOOK_CONFIG_GFX_DEFAULT_SCALE_BACK_BUFFER_HEIGHT_VALUE) ||
|
||||
tmp_int < 0 || tmp_int > 0xFFFF) {
|
||||
log_warning("Invalid value for key '%s' specified, fallback "
|
||||
"to default '%d'", IIDXHOOK_CONFIG_GFX_SCALE_BACK_BUFFER_HEIGHT_KEY,
|
||||
IIDXHOOK_CONFIG_GFX_DEFAULT_SCALE_BACK_BUFFER_HEIGHT_VALUE);
|
||||
}
|
||||
|
||||
config_gfx->scale_back_buffer_height = (uint16_t) tmp_int;
|
||||
|
||||
if (!cconfig_util_get_str(config, IIDXHOOK_CONFIG_GFX_SCALE_BACK_BUFFER_FILTER_KEY,
|
||||
tmp, sizeof(tmp) - 1,
|
||||
IIDXHOOK_CONFIG_GFX_DEFAULT_SCALE_BACK_BUFFER_FILTER_VALUE)) {
|
||||
log_warning("Invalid value for key '%s' specified, fallback "
|
||||
"to default '%s'", IIDXHOOK_CONFIG_GFX_SCALE_BACK_BUFFER_FILTER_KEY,
|
||||
IIDXHOOK_CONFIG_GFX_DEFAULT_SCALE_BACK_BUFFER_FILTER_VALUE);
|
||||
}
|
||||
|
||||
if (!strcmp(tmp, "none")) {
|
||||
config_gfx->scale_back_buffer_filter = D3D9_BACK_BUFFER_SCALE_FILTER_NONE;
|
||||
} else if (!strcmp(tmp, "linear")) {
|
||||
config_gfx->scale_back_buffer_filter = D3D9_BACK_BUFFER_SCALE_FILTER_LINEAR;
|
||||
} else if (!strcmp(tmp, "point")) {
|
||||
config_gfx->scale_back_buffer_filter = D3D9_BACK_BUFFER_SCALE_FILTER_POINT;
|
||||
} else {
|
||||
config_gfx->scale_back_buffer_filter = D3D9_BACK_BUFFER_SCALE_FILTER_NONE;
|
||||
|
||||
log_warning("Invalid value for key '%s' specified, fallback "
|
||||
"to default '%s'", IIDXHOOK_CONFIG_GFX_SCALE_BACK_BUFFER_FILTER_KEY,
|
||||
IIDXHOOK_CONFIG_GFX_DEFAULT_SCALE_BACK_BUFFER_FILTER_VALUE);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
#ifndef IIDXHOOK_CONFIG_GFX_H
|
||||
#define IIDXHOOK_CONFIG_GFX_H
|
||||
|
||||
#include "cconfig/cconfig.h"
|
||||
|
||||
#include "iidxhook-util/d3d9.h"
|
||||
|
||||
struct iidxhook_config_gfx {
|
||||
bool bgvideo_uv_fix;
|
||||
bool framed;
|
||||
float frame_rate_limit;
|
||||
float monitor_check;
|
||||
uint16_t pci_id_vid;
|
||||
uint16_t pci_id_pid;
|
||||
bool windowed;
|
||||
int32_t window_width;
|
||||
int32_t window_height;
|
||||
uint16_t scale_back_buffer_width;
|
||||
uint16_t scale_back_buffer_height;
|
||||
enum d3d9_back_buffer_scale_filter scale_back_buffer_filter;
|
||||
};
|
||||
|
||||
void iidxhook_config_gfx_init(struct cconfig* config);
|
||||
|
||||
void iidxhook_config_gfx_get(struct iidxhook_config_gfx* config_gfx,
|
||||
struct cconfig* config);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,45 @@
|
||||
#include "cconfig/cconfig-util.h"
|
||||
|
||||
#include "iidxhook-util/config-misc.h"
|
||||
|
||||
#include "util/log.h"
|
||||
|
||||
#define IIDXHOOK_CONFIG_MISC_DISABLE_CLOCK_SET_KEY "misc.disable_clock_set"
|
||||
#define IIDXHOOK_CONFIG_MISC_RTEFFECT_STUB_KEY "misc.rteffect_stub"
|
||||
|
||||
#define IIDXHOOK_CONFIG_MISC_DEFAULT_DISABLE_CLOCK_SET_VALUE false
|
||||
#define IIDXHOOK_CONFIG_MISC_DEFAULT_RTEFFECT_STUB_VALUE false
|
||||
|
||||
void iidxhook_config_misc_init(struct cconfig* config)
|
||||
{
|
||||
cconfig_util_set_bool(config,
|
||||
IIDXHOOK_CONFIG_MISC_DISABLE_CLOCK_SET_KEY,
|
||||
IIDXHOOK_CONFIG_MISC_DEFAULT_DISABLE_CLOCK_SET_VALUE,
|
||||
"Disable operator clock setting system clock time");
|
||||
|
||||
cconfig_util_set_bool(config,
|
||||
IIDXHOOK_CONFIG_MISC_RTEFFECT_STUB_KEY,
|
||||
IIDXHOOK_CONFIG_MISC_DEFAULT_RTEFFECT_STUB_VALUE,
|
||||
"Stub calls to rteffect.dll (10th to DistorteD)");
|
||||
}
|
||||
|
||||
void iidxhook_config_misc_get(struct iidxhook_config_misc* config_misc,
|
||||
struct cconfig* config)
|
||||
{
|
||||
if (!cconfig_util_get_bool(config,
|
||||
IIDXHOOK_CONFIG_MISC_DISABLE_CLOCK_SET_KEY,
|
||||
&config_misc->disable_clock_set,
|
||||
IIDXHOOK_CONFIG_MISC_DEFAULT_DISABLE_CLOCK_SET_VALUE)) {
|
||||
log_warning("Invalid value for key '%s' specified, fallback "
|
||||
"to default '%d'", IIDXHOOK_CONFIG_MISC_DISABLE_CLOCK_SET_KEY,
|
||||
IIDXHOOK_CONFIG_MISC_DEFAULT_DISABLE_CLOCK_SET_VALUE);
|
||||
}
|
||||
|
||||
if (!cconfig_util_get_bool(config, IIDXHOOK_CONFIG_MISC_RTEFFECT_STUB_KEY,
|
||||
&config_misc->rteffect_stub,
|
||||
IIDXHOOK_CONFIG_MISC_DEFAULT_RTEFFECT_STUB_VALUE)) {
|
||||
log_warning("Invalid value for key '%s' specified, fallback "
|
||||
"to default '%d'", IIDXHOOK_CONFIG_MISC_RTEFFECT_STUB_KEY,
|
||||
IIDXHOOK_CONFIG_MISC_DEFAULT_RTEFFECT_STUB_VALUE);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
#ifndef IIDXHOOK_CONFIG_MISC_H
|
||||
#define IIDXHOOK_CONFIG_MISC_H
|
||||
|
||||
#include "cconfig/cconfig.h"
|
||||
|
||||
struct iidxhook_config_misc {
|
||||
bool disable_clock_set;
|
||||
bool rteffect_stub;
|
||||
};
|
||||
|
||||
void iidxhook_config_misc_init(struct cconfig* config);
|
||||
|
||||
void iidxhook_config_misc_get(struct iidxhook_config_misc* config_misc,
|
||||
struct cconfig* config);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,172 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "cconfig/cconfig-util.h"
|
||||
|
||||
#include "iidxhook-util/config-sec.h"
|
||||
|
||||
#include "security/mcode.h"
|
||||
#include "security/rp.h"
|
||||
|
||||
#include "util/log.h"
|
||||
#include "util/mem.h"
|
||||
|
||||
#define IIDXHOOK_CONFIG_SEC_BOOT_VERSION_KEY "sec.boot_version"
|
||||
#define IIDXHOOK_CONFIG_SEC_BOOT_SEEDS_KEY "sec.boot_seeds"
|
||||
#define IIDXHOOK_CONFIG_SEC_BLACK_PLUG_MCODE_KEY "sec.black_plug_mcode"
|
||||
|
||||
/* Use C02 defaults */
|
||||
#define IIDXHOOK_CONFIG_SEC_DEFAULT_BOOT_VERSION_VALUE iidxhook_security_default_boot_version
|
||||
#define IIDXHOOK_CONFIG_SEC_DEFAULT_BOOT_SEEDS_VALUE iidxhook_security_default_boot_seeds
|
||||
#define IIDXHOOK_CONFIG_SEC_DEFAULT_BLACK_PLUG_MCODE_VALUE iidxhook_security_default_black_plug_mcode
|
||||
|
||||
/* IIDX 09 default */
|
||||
static const struct security_mcode iidxhook_security_default_boot_version = {
|
||||
.header = SECURITY_MCODE_HEADER,
|
||||
.unkn = SECURITY_MCODE_UNKN_E,
|
||||
.game = SECURITY_MCODE_GAME_IIDX_9,
|
||||
.region = SECURITY_MCODE_FIELD_BLANK,
|
||||
.cabinet = SECURITY_MCODE_FIELD_BLANK,
|
||||
.revision = SECURITY_MCODE_FIELD_BLANK,
|
||||
};
|
||||
|
||||
/* IIDX 09 default */
|
||||
static const uint32_t iidxhook_security_default_boot_seeds[3] = {0, 0, 0};
|
||||
|
||||
/* IIDX 09 default */
|
||||
static const struct security_mcode iidxhook_security_default_black_plug_mcode = {
|
||||
.header = SECURITY_MCODE_HEADER,
|
||||
.unkn = SECURITY_MCODE_UNKN_Q,
|
||||
.game = SECURITY_MCODE_GAME_IIDX_9,
|
||||
.region = SECURITY_MCODE_REGION_JAPAN,
|
||||
.cabinet = SECURITY_MCODE_CABINET_A,
|
||||
.revision = SECURITY_MCODE_REVISION_A,
|
||||
};
|
||||
|
||||
static char* iidxhook_config_boot_seeds_to_str(const uint32_t* seeds)
|
||||
{
|
||||
char* res;
|
||||
size_t len;
|
||||
|
||||
len = snprintf(NULL, 0, "%d:%d:%d", seeds[0], seeds[1], seeds[2]);
|
||||
res = xmalloc(len + 1);
|
||||
sprintf(res, "%d:%d:%d", seeds[0], seeds[1], seeds[2]);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static bool iidxhook_config_boot_seeds_parse(const char* str, uint32_t* seeds)
|
||||
{
|
||||
if (strlen(str) < 5) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (str[1] != ':' || str[3] != ':') {
|
||||
return false;
|
||||
} else {
|
||||
seeds[0] = str[0] - '0';
|
||||
seeds[1] = str[2] - '0';
|
||||
seeds[2] = str[4] - '0';
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void iidxhook_config_sec_init(struct cconfig* config)
|
||||
{
|
||||
char* tmp;
|
||||
|
||||
tmp = security_mcode_to_str(
|
||||
&IIDXHOOK_CONFIG_SEC_DEFAULT_BOOT_VERSION_VALUE);
|
||||
|
||||
cconfig_util_set_str(config,
|
||||
IIDXHOOK_CONFIG_SEC_BOOT_VERSION_KEY, tmp,
|
||||
"Security boot version (e.g. GEC02).");
|
||||
|
||||
free(tmp);
|
||||
|
||||
tmp = iidxhook_config_boot_seeds_to_str(
|
||||
IIDXHOOK_CONFIG_SEC_DEFAULT_BOOT_SEEDS_VALUE);
|
||||
|
||||
cconfig_util_set_str(config,
|
||||
IIDXHOOK_CONFIG_SEC_BOOT_SEEDS_KEY, tmp,
|
||||
"Security boot seeds for ezusb, format: X:X:X where X is a number of "
|
||||
"0-9 (e.g. 0:0:0).");
|
||||
|
||||
free(tmp);
|
||||
|
||||
tmp = security_mcode_to_str(
|
||||
&IIDXHOOK_CONFIG_SEC_DEFAULT_BLACK_PLUG_MCODE_VALUE);
|
||||
|
||||
cconfig_util_set_str(config,
|
||||
IIDXHOOK_CONFIG_SEC_BLACK_PLUG_MCODE_KEY, tmp,
|
||||
"Security black plug mcode id string (e.g. GQC02JAA).");
|
||||
|
||||
free(tmp);
|
||||
}
|
||||
|
||||
void iidxhook_config_sec_get(struct iidxhook_config_sec* config_sec,
|
||||
struct cconfig* config)
|
||||
{
|
||||
char tmp_seeds[6];
|
||||
char tmp_mcode[sizeof(struct security_mcode) + 1];
|
||||
char* tmp_str;
|
||||
|
||||
tmp_str = security_mcode_to_str(
|
||||
&IIDXHOOK_CONFIG_SEC_DEFAULT_BOOT_VERSION_VALUE);
|
||||
|
||||
if (!cconfig_util_get_str(config, IIDXHOOK_CONFIG_SEC_BOOT_VERSION_KEY,
|
||||
tmp_mcode, sizeof(tmp_mcode) - 1, tmp_str)) {
|
||||
log_warning("Invalid value for key '%s' specified, fallback "
|
||||
"to default '%s'", IIDXHOOK_CONFIG_SEC_BOOT_VERSION_KEY, tmp_str);
|
||||
}
|
||||
|
||||
if (!security_mcode_parse(tmp_mcode, &config_sec->boot_version)) {
|
||||
log_warning("Invalid format for value of key '%s' specified, fallback "
|
||||
"to default '%s'", IIDXHOOK_CONFIG_SEC_BOOT_VERSION_KEY, tmp_str);
|
||||
memcpy(&config_sec->boot_version,
|
||||
&IIDXHOOK_CONFIG_SEC_DEFAULT_BOOT_VERSION_VALUE,
|
||||
sizeof(IIDXHOOK_CONFIG_SEC_DEFAULT_BOOT_VERSION_VALUE));
|
||||
}
|
||||
|
||||
free(tmp_str);
|
||||
|
||||
tmp_str = iidxhook_config_boot_seeds_to_str(
|
||||
IIDXHOOK_CONFIG_SEC_DEFAULT_BOOT_SEEDS_VALUE);
|
||||
|
||||
if (!cconfig_util_get_str(config, IIDXHOOK_CONFIG_SEC_BOOT_SEEDS_KEY,
|
||||
tmp_seeds, sizeof(tmp_seeds) - 1, tmp_str)) {
|
||||
log_warning("Invalid value for key '%s' specified, fallback "
|
||||
"to default '%s'", IIDXHOOK_CONFIG_SEC_BOOT_SEEDS_KEY, tmp_str);
|
||||
}
|
||||
|
||||
if (!iidxhook_config_boot_seeds_parse(tmp_seeds, config_sec->boot_seeds)) {
|
||||
log_warning("Invalid format for value of key '%s' specified, fallback "
|
||||
"to default '%s'", IIDXHOOK_CONFIG_SEC_BOOT_SEEDS_KEY, tmp_str);
|
||||
memcpy(config_sec->boot_seeds,
|
||||
IIDXHOOK_CONFIG_SEC_DEFAULT_BOOT_SEEDS_VALUE,
|
||||
sizeof(IIDXHOOK_CONFIG_SEC_DEFAULT_BOOT_SEEDS_VALUE));
|
||||
}
|
||||
|
||||
free(tmp_str);
|
||||
|
||||
tmp_str = security_mcode_to_str(
|
||||
&IIDXHOOK_CONFIG_SEC_DEFAULT_BLACK_PLUG_MCODE_VALUE);
|
||||
|
||||
if (!cconfig_util_get_str(config, IIDXHOOK_CONFIG_SEC_BLACK_PLUG_MCODE_KEY,
|
||||
tmp_mcode, sizeof(tmp_mcode) - 1, tmp_str)) {
|
||||
log_warning("Invalid value for key '%s' specified, fallback "
|
||||
"to default '%s'", IIDXHOOK_CONFIG_SEC_BLACK_PLUG_MCODE_KEY,
|
||||
tmp_str);
|
||||
}
|
||||
|
||||
if (!security_mcode_parse(tmp_mcode, &config_sec->black_plug_mcode)) {
|
||||
log_warning("Invalid format for value of key '%s' specified, fallback "
|
||||
"to default '%s'", IIDXHOOK_CONFIG_SEC_BLACK_PLUG_MCODE_KEY,
|
||||
tmp_str);
|
||||
memcpy(&config_sec->black_plug_mcode,
|
||||
&IIDXHOOK_CONFIG_SEC_DEFAULT_BLACK_PLUG_MCODE_VALUE,
|
||||
sizeof(IIDXHOOK_CONFIG_SEC_DEFAULT_BLACK_PLUG_MCODE_VALUE));
|
||||
}
|
||||
|
||||
free(tmp_str);
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
#ifndef IIDXHOOK_CONFIG_SEC_H
|
||||
#define IIDXHOOK_CONFIG_SEC_H
|
||||
|
||||
#include "cconfig/cconfig.h"
|
||||
|
||||
#include "security/mcode.h"
|
||||
|
||||
struct iidxhook_config_sec {
|
||||
struct security_mcode boot_version;
|
||||
uint32_t boot_seeds[3];
|
||||
struct security_mcode black_plug_mcode;
|
||||
};
|
||||
|
||||
void iidxhook_config_sec_init(struct cconfig* config);
|
||||
|
||||
void iidxhook_config_sec_get(struct iidxhook_config_sec* config_sec,
|
||||
struct cconfig* config);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,600 @@
|
||||
#define LOG_MODULE "d3d8-hook"
|
||||
|
||||
#include <windows.h>
|
||||
#include <d3d8.h>
|
||||
|
||||
#include <math.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "hook/com-proxy.h"
|
||||
#include "hook/table.h"
|
||||
|
||||
#include "iidxhook-util/d3d8.h"
|
||||
|
||||
#include "util/defs.h"
|
||||
#include "util/log.h"
|
||||
#include "util/str.h"
|
||||
#include "util/time.h"
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
struct d3d8_vertex {
|
||||
float x;
|
||||
float y;
|
||||
float z;
|
||||
uint32_t color;
|
||||
uint32_t unknown;
|
||||
float tu;
|
||||
float tv;
|
||||
};
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
static HWND STDCALL my_CreateWindowExA(
|
||||
DWORD dwExStyle, LPCSTR lpClassName, LPCSTR lpWindowName, DWORD dwStyle,
|
||||
int X, int Y, int nWidth, int nHeight, HWND hWndParent, HMENU hMenu,
|
||||
HINSTANCE hInstance, LPVOID lpParam);
|
||||
|
||||
static HRESULT STDCALL my_CreateDevice(
|
||||
IDirect3D8 *self, UINT adapter, D3DDEVTYPE type, HWND hwnd,
|
||||
DWORD flags, D3DPRESENT_PARAMETERS *pp, IDirect3DDevice8 **pdev);
|
||||
|
||||
static HRESULT STDCALL my_SetRenderState(IDirect3DDevice8* self,
|
||||
D3DRENDERSTATETYPE State, DWORD Value);
|
||||
|
||||
static HRESULT STDCALL my_DrawPrimitiveUP(
|
||||
IDirect3DDevice8* self, D3DPRIMITIVETYPE primitive_type,
|
||||
UINT primitive_count, const void *data, UINT stride);
|
||||
|
||||
static HRESULT STDCALL my_Present(IDirect3DDevice8* self,
|
||||
CONST RECT *pSourceRect, CONST RECT *pDestRect,
|
||||
HWND hDestWindowOverride, CONST RGNDATA *pDirtyRegion);
|
||||
|
||||
static IDirect3D8* STDCALL my_Direct3DCreate8(UINT sdk_ver);
|
||||
|
||||
static void execute_monitor_check(IDirect3DDevice8Vtbl* api_vtbl,
|
||||
IDirect3DDevice8* pdev);
|
||||
|
||||
static void calc_win_size_with_framed(HWND hwnd, DWORD x, DWORD y,
|
||||
DWORD width, DWORD height, LPWINDOWPOS wp);
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
static bool gfx_windowed = false;
|
||||
static int32_t gfx_window_width = -1;
|
||||
static int32_t gfx_window_height = -1;
|
||||
static bool gfx_window_framed = false;
|
||||
static float gfx_frame_rate_limit = 0.0f;
|
||||
static bool gfx_fix_stretched_bg_videos = false;
|
||||
static bool gfx_fix_iidx_12_fog = false;
|
||||
static bool gfx_fix_iidx_13_lighting = false;
|
||||
static d3d8_monitor_check_result_callback_t gfx_monitor_check_cb = NULL;
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
static IDirect3D8* (STDCALL *real_Direct3DCreate8)(UINT sdk_ver);
|
||||
static HWND (STDCALL *real_CreateWindowExA)(
|
||||
DWORD dwExStyle, LPCSTR lpClassName, LPCSTR lpWindowName, DWORD dwStyle,
|
||||
int X, int Y, int nWidth, int nHeight, HWND hWndParent, HMENU hMenu,
|
||||
HINSTANCE hInstance, LPVOID lpParam);
|
||||
static HRESULT (STDCALL* real_SetRenderState)(IDirect3DDevice8* self,
|
||||
D3DRENDERSTATETYPE State, DWORD Value);
|
||||
static HRESULT (STDCALL *real_DrawPrimitiveUP)(
|
||||
IDirect3DDevice8* self, D3DPRIMITIVETYPE primitive_type,
|
||||
UINT primitive_count, const void *data, UINT stride);
|
||||
static HRESULT (STDCALL* real_Present)(IDirect3DDevice8* self,
|
||||
CONST RECT *pSourceRect, CONST RECT *pDestRect,
|
||||
HWND hDestWindowOverride, CONST RGNDATA *pDirtyRegion);
|
||||
|
||||
static const struct hook_symbol d3d8_hook_syms[] = {
|
||||
{
|
||||
.name = "Direct3DCreate8",
|
||||
.patch = my_Direct3DCreate8,
|
||||
.link = (void **) &real_Direct3DCreate8,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct hook_symbol d3d8_hook_user32_syms[] = {
|
||||
{
|
||||
.name = "CreateWindowExA",
|
||||
.patch = my_CreateWindowExA,
|
||||
.link = (void **) &real_CreateWindowExA,
|
||||
},
|
||||
};
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
static bool float_equal(float a, float b, float eps)
|
||||
{
|
||||
return fabs(a - b) < eps;
|
||||
}
|
||||
|
||||
static HWND STDCALL my_CreateWindowExA(
|
||||
DWORD dwExStyle, LPCSTR lpClassName, LPCSTR lpWindowName, DWORD dwStyle,
|
||||
int X, int Y, int nWidth, int nHeight, HWND hWndParent, HMENU hMenu,
|
||||
HINSTANCE hInstance, LPVOID lpParam)
|
||||
{
|
||||
if (gfx_windowed && gfx_window_framed) {
|
||||
/* use a different style */
|
||||
dwStyle |= WS_OVERLAPPEDWINDOW;
|
||||
/* also show mouse cursor */
|
||||
ShowCursor(TRUE);
|
||||
}
|
||||
|
||||
if (gfx_window_width != -1 && gfx_window_height != -1) {
|
||||
log_misc("Overriding window size from %dx%d with %dx%d", nWidth,
|
||||
nHeight, gfx_window_width, gfx_window_height);
|
||||
|
||||
nWidth = gfx_window_width;
|
||||
nHeight = gfx_window_height;
|
||||
}
|
||||
|
||||
HWND hwnd = real_CreateWindowExA(dwExStyle, lpClassName, lpWindowName,
|
||||
dwStyle, X, Y, nWidth, nHeight, hWndParent, hMenu, hInstance,
|
||||
lpParam);
|
||||
|
||||
if (hwnd == INVALID_HANDLE_VALUE) {
|
||||
return hwnd;
|
||||
}
|
||||
|
||||
if (gfx_windowed && gfx_window_framed) {
|
||||
/* we have to adjust the window size, because the window needs to be a
|
||||
slightly bigger than the rendering resolution (window caption and
|
||||
stuff is included in the window size) */
|
||||
WINDOWPOS wp;
|
||||
calc_win_size_with_framed(hwnd, X, Y, nWidth, nHeight, &wp);
|
||||
SetWindowPos(hwnd, 0, wp.x, wp.y, wp.cx, wp.cy, 0);
|
||||
X = wp.x;
|
||||
Y = wp.y;
|
||||
nWidth = wp.cx;
|
||||
nHeight = wp.cy;
|
||||
}
|
||||
|
||||
return hwnd;
|
||||
}
|
||||
|
||||
static HRESULT STDCALL my_CreateDevice(
|
||||
IDirect3D8 *self, UINT adapter, D3DDEVTYPE type, HWND hwnd, DWORD flags,
|
||||
D3DPRESENT_PARAMETERS *pp, IDirect3DDevice8 **pdev)
|
||||
{
|
||||
IDirect3D8 *real = COM_PROXY_UNWRAP(self);
|
||||
HRESULT hr;
|
||||
IDirect3DDevice8* api;
|
||||
IDirect3DDevice8Vtbl *api_vtbl;
|
||||
struct com_proxy *api_proxy;
|
||||
char* error;
|
||||
|
||||
log_info("Direct3D8 CreateDevice hook hit");
|
||||
|
||||
/* Fix a long-standing bug in IIDX */
|
||||
|
||||
if (flags & D3DCREATE_SOFTWARE_VERTEXPROCESSING) {
|
||||
flags &= ~D3DCREATE_PUREDEVICE;
|
||||
}
|
||||
|
||||
if (gfx_windowed) {
|
||||
log_misc("Window mode");
|
||||
|
||||
pp->hDeviceWindow = 0;
|
||||
pp->Windowed = TRUE;
|
||||
pp->FullScreen_RefreshRateInHz = 0;
|
||||
pp->FullScreen_PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;
|
||||
}
|
||||
|
||||
/* If we don't do this, the game will crash on some versions of windows
|
||||
on either fullscreen or windowed or even both. Also, further reports
|
||||
about textures with green glowing borders are gone as well when applying
|
||||
this */
|
||||
D3DDISPLAYMODE mode;
|
||||
IDirect3D8_GetAdapterDisplayMode(real, adapter, &mode);
|
||||
pp->BackBufferFormat = mode.Format;
|
||||
|
||||
hr = IDirect3D8_CreateDevice(real, adapter, type, hwnd, flags, pp, pdev);
|
||||
|
||||
if (hr != S_OK) {
|
||||
switch (hr) {
|
||||
case D3DERR_INVALIDCALL:
|
||||
error = "invalid call";
|
||||
break;
|
||||
|
||||
case D3DERR_NOTAVAILABLE:
|
||||
error = "not available";
|
||||
break;
|
||||
|
||||
case D3DERR_OUTOFVIDEOMEMORY:
|
||||
error = "out of video memory";
|
||||
break;
|
||||
|
||||
default:
|
||||
error = "unknown";
|
||||
break;
|
||||
}
|
||||
|
||||
log_warning("Creating D3D8 device failed: %lX, %s", hr, error);
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
api = *pdev;
|
||||
api_proxy = com_proxy_wrap(api, sizeof(*api->lpVtbl));
|
||||
api_vtbl = api_proxy->vptr;
|
||||
|
||||
real_DrawPrimitiveUP = api_vtbl->DrawPrimitiveUP;
|
||||
api_vtbl->DrawPrimitiveUP = my_DrawPrimitiveUP;
|
||||
|
||||
real_SetRenderState = api_vtbl->SetRenderState;
|
||||
api_vtbl->SetRenderState = my_SetRenderState;
|
||||
|
||||
real_Present = api_vtbl->Present;
|
||||
api_vtbl->Present = my_Present;
|
||||
|
||||
*pdev = (IDirect3DDevice8*) api_proxy;
|
||||
|
||||
if (gfx_monitor_check_cb) {
|
||||
execute_monitor_check(api_vtbl, *pdev);
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
static IDirect3D8 *STDCALL my_Direct3DCreate8(UINT sdk_ver)
|
||||
{
|
||||
IDirect3D8 *api;
|
||||
IDirect3D8Vtbl *api_vtbl;
|
||||
struct com_proxy *api_proxy;
|
||||
|
||||
log_info("Direct3DCreate8 hook hit");
|
||||
|
||||
api = real_Direct3DCreate8(sdk_ver);
|
||||
api_proxy = com_proxy_wrap(api, sizeof(*api->lpVtbl));
|
||||
api_vtbl = api_proxy->vptr;
|
||||
|
||||
api_vtbl->CreateDevice = my_CreateDevice;
|
||||
|
||||
return (IDirect3D8 *) api_proxy;
|
||||
}
|
||||
|
||||
static HRESULT STDCALL my_SetRenderState(IDirect3DDevice8* self,
|
||||
D3DRENDERSTATETYPE State, DWORD Value)
|
||||
{
|
||||
if (gfx_fix_iidx_12_fog) {
|
||||
if (State == D3DRS_FOGENABLE) {
|
||||
Value = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if (gfx_fix_iidx_13_lighting) {
|
||||
if (State == D3DRS_LIGHTING) {
|
||||
Value = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return real_SetRenderState(self, State, Value);
|
||||
}
|
||||
|
||||
static HRESULT STDCALL my_DrawPrimitiveUP(
|
||||
IDirect3DDevice8* self, D3DPRIMITIVETYPE primitive_type,
|
||||
UINT primitive_count, const void *data, UINT stride)
|
||||
{
|
||||
/* trap the background video texture to fix the UVs for newer hardware
|
||||
background videos are rendered to a 512x512 texture, but the videos
|
||||
are 312x416 in size (on IIDX RED)
|
||||
the old hardware supported this to render correctly
|
||||
(VMs might work as well) with the UVs set in the code but on newer
|
||||
hardware, the video frame gets stretched to the full 512x512 texture
|
||||
The tex UVs are only set to a subsection of that texture thus just a
|
||||
subsection is rendered (video appears to be stretched)
|
||||
to fix this, we simply set the UVs to apply to the full texture
|
||||
Quad for BG Video (triangle fan!):
|
||||
164/0 ------- 476/0
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
164/416 ------- 476/416
|
||||
Original UVs for the quad
|
||||
0/0,8125 ------- 0,609375/0,812500
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
0/0 ------- 0,609375/0
|
||||
*/
|
||||
if ( gfx_fix_stretched_bg_videos && primitive_type == 6 &&
|
||||
primitive_count == 2 && stride == 28) {
|
||||
|
||||
struct d3d8_vertex* vertices = (struct d3d8_vertex*) data;
|
||||
|
||||
/*
|
||||
log_info("Video Tex: %f/%f %f/%f %f/%f %f/%f",
|
||||
vertices[0].x, vertices[0].y,
|
||||
vertices[1].x, vertices[1].y,
|
||||
vertices[2].x, vertices[2].y,
|
||||
vertices[3].x, vertices[3].y);
|
||||
*/
|
||||
|
||||
/* Fix full screen background videos (e.g. DistorteD intro sequence) */
|
||||
if ( vertices[0].x >= 0.0f && vertices[0].x < 1.0f &&
|
||||
vertices[0].y >= 0.0f && vertices[0].y < 1.0f &&
|
||||
vertices[1].x > 639.0f && vertices[1].x < 641.0f &&
|
||||
vertices[1].y >= 0.0f && vertices[1].y < 1.0f &&
|
||||
vertices[2].x > 639.0f && vertices[2].x < 641.0f &&
|
||||
vertices[2].y > 479.0f && vertices[2].y < 481.0f &&
|
||||
vertices[3].x >= 0.0f && vertices[3].x < 1.0f &&
|
||||
vertices[3].y > 479.0f && vertices[3].y < 481.0f) {
|
||||
/* fix UVs
|
||||
1.0f / 640 or 480 fixes the diagonal seam connecting the two
|
||||
triangles which is visible on some GPUs (why? idk)
|
||||
*/
|
||||
vertices[0].tu = 0.0f + 1.0f / 640;
|
||||
vertices[0].tv = 1.0f;
|
||||
vertices[1].tu = 1.0f;
|
||||
vertices[1].tv = 1.0f;
|
||||
vertices[2].tu = 1.0f;
|
||||
vertices[2].tv = 0.0f + 1.0f / 480;
|
||||
vertices[3].tu = 0.0f + 1.0f / 640;
|
||||
vertices[3].tv = 0.0f + 1.0f / 480;
|
||||
} else
|
||||
|
||||
/* another identifier, because there are other textures with 512x512 size
|
||||
make sure we got the bg video only to not mess up anything else */
|
||||
/* different versions have different themes and position the bg video
|
||||
on slightly different positions (good job...) */
|
||||
if ( /* single */
|
||||
((vertices[0].x >= 164.0f && vertices[0].x <= 168.0f &&
|
||||
float_equal(vertices[0].y, 0.0f, 0.1f)) &&
|
||||
(vertices[1].x >= 472.0f && vertices[1].x <= 476.0f &&
|
||||
float_equal(vertices[1].y, 0.0f, 0.1f)) &&
|
||||
(vertices[2].x >= 472.0f && vertices[2].x <= 476.0f &&
|
||||
float_equal(vertices[2].y, 416.0f, 0.1f)) &&
|
||||
(vertices[3].x >= 164.0f && vertices[3].x <= 168.0f &&
|
||||
float_equal(vertices[3].y, 416.0f, 0.1f))) ||
|
||||
/* double top left */
|
||||
((float_equal(vertices[0].x, 6.0f, 0.1f) &&
|
||||
vertices[0].y >= 24.0f && vertices[0].y <= 28.0f) &&
|
||||
(float_equal(vertices[1].x, 147.0f, 0.1f) &&
|
||||
vertices[1].y >= 24.0f && vertices[1].y <= 28.0f) &&
|
||||
(float_equal(vertices[2].x, 147.0f, 0.1f) &&
|
||||
vertices[2].y >= 212.0f && vertices[2].y <= 216.0f) &&
|
||||
(float_equal(vertices[3].x, 6.0f, 0.1f) &&
|
||||
vertices[3].y >= 212.0f && vertices[3].y <= 216.0f)) ||
|
||||
/* double bottom left */
|
||||
((float_equal(vertices[0].x, 6.0f, 0.1f) &&
|
||||
vertices[0].y >= 216.0f && vertices[0].y <= 220.0f) &&
|
||||
(float_equal(vertices[1].x, 147.0f, 0.1f) &&
|
||||
vertices[1].y >= 216.0f && vertices[1].y <= 220.0f) &&
|
||||
(float_equal(vertices[2].x, 147.0f, 0.1) &&
|
||||
vertices[2].y >= 404.0f && vertices[2].y <= 408.0f) &&
|
||||
(float_equal(vertices[3].x, 6.0f, 0.1f) &&
|
||||
vertices[3].y >= 404.0f && vertices[3].y <= 408.0f)) ||
|
||||
/* double top right */
|
||||
((vertices[0].x >= 493.0f && vertices[0].x <= 494.0f &&
|
||||
vertices[0].y >= 24.0f && vertices[0].y <= 28.0f) &&
|
||||
(vertices[1].x >= 634.0f && vertices[1].x <= 635.0f &&
|
||||
vertices[1].y >= 24.0f && vertices[1].y <= 28.0f) &&
|
||||
(vertices[2].x >= 634.0f && vertices[2].x <= 635.0f &&
|
||||
vertices[2].y >= 212.0f && vertices[2].y <= 216.0f) &&
|
||||
(vertices[3].x >= 493.0f && vertices[3].x <= 494.0f &&
|
||||
vertices[3].y >= 212.0f && vertices[3].y <= 216.0f)) ||
|
||||
/* double bottom right */
|
||||
((vertices[0].x >= 493.0f && vertices[0].x <= 494.0f &&
|
||||
vertices[0].y >= 216.0f && vertices[0].y <= 220.0f) &&
|
||||
(vertices[1].x >= 634.0f && vertices[1].x <= 635.0f &&
|
||||
vertices[1].y >= 216.0f && vertices[1].y <= 220.0f) &&
|
||||
(vertices[2].x >= 634.0f && vertices[2].x <= 635.0f &&
|
||||
vertices[2].y >= 404.0f && vertices[2].y <= 408.0f) &&
|
||||
(vertices[3].x >= 493.0f && vertices[3].x <= 494.0f &&
|
||||
vertices[3].y >= 404.0f && vertices[3].y <= 408.0f)))
|
||||
{
|
||||
/* fix UVs
|
||||
1.0f / 512 fixes the diagonal seam connecting the two triangles
|
||||
which is visible on some GPUs (why? idk)
|
||||
*/
|
||||
vertices[0].tu = 0.0f + 1.0f / 512;
|
||||
vertices[0].tv = 1.0f;
|
||||
vertices[1].tu = 1.0f;
|
||||
vertices[1].tv = 1.0f;
|
||||
vertices[2].tu = 1.0f;
|
||||
vertices[2].tv = 0.0f + 1.0f / 512;
|
||||
vertices[3].tu = 0.0f + 1.0f / 512;
|
||||
vertices[3].tv = 0.0f + 1.0f / 512;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return real_DrawPrimitiveUP(self, primitive_type, primitive_count,
|
||||
data, stride);
|
||||
}
|
||||
|
||||
static HRESULT STDCALL my_Present(IDirect3DDevice8* self,
|
||||
CONST RECT *pSourceRect, CONST RECT *pDestRect,
|
||||
HWND hDestWindowOverride, CONST RGNDATA *pDirtyRegion)
|
||||
{
|
||||
static uint64_t current_time = 0;
|
||||
|
||||
HRESULT hr = real_Present(self, pSourceRect, pDestRect, hDestWindowOverride,
|
||||
pDirtyRegion);
|
||||
|
||||
if (gfx_frame_rate_limit > 0.0f) {
|
||||
if (current_time == 0) {
|
||||
current_time = time_get_counter();
|
||||
} else {
|
||||
uint64_t frame_time = 1000000 / gfx_frame_rate_limit;
|
||||
|
||||
uint64_t dt = time_get_elapsed_us(
|
||||
time_get_counter() - current_time);
|
||||
|
||||
while (dt < frame_time) {
|
||||
/* waste some cpu time by polling
|
||||
because we can't sleep for X us */
|
||||
dt = time_get_elapsed_us(time_get_counter() - current_time);
|
||||
}
|
||||
|
||||
current_time = time_get_counter();
|
||||
}
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
void d3d8_hook_init(void)
|
||||
{
|
||||
hook_table_apply(
|
||||
NULL,
|
||||
"d3d8.dll",
|
||||
d3d8_hook_syms,
|
||||
lengthof(d3d8_hook_syms));
|
||||
|
||||
hook_table_apply(
|
||||
NULL,
|
||||
"user32.dll",
|
||||
d3d8_hook_user32_syms,
|
||||
lengthof(d3d8_hook_user32_syms));
|
||||
|
||||
log_info("Inserted d3d8 graphics hooks");
|
||||
}
|
||||
|
||||
void d3d8_set_windowed(bool framed, int32_t width, int32_t height)
|
||||
{
|
||||
gfx_windowed = true;
|
||||
gfx_window_framed = framed;
|
||||
gfx_window_width = width;
|
||||
gfx_window_height = height;
|
||||
}
|
||||
|
||||
void d3d8_set_frame_rate_limit(float limit)
|
||||
{
|
||||
if (limit < 0.0f) {
|
||||
limit = 0.0f;
|
||||
}
|
||||
|
||||
log_info("Limiting rendering frame rate to %f frames", limit);
|
||||
gfx_frame_rate_limit = limit;
|
||||
}
|
||||
|
||||
void d3d8_enable_monitor_check(d3d8_monitor_check_result_callback_t cb)
|
||||
{
|
||||
log_assert(cb);
|
||||
|
||||
gfx_monitor_check_cb = cb;
|
||||
log_info("Enabled monitor check");
|
||||
}
|
||||
|
||||
void d3d8_iidx_fix_stretched_bg_videos(void)
|
||||
{
|
||||
gfx_fix_stretched_bg_videos = true;
|
||||
log_info("Fixing UVs of stretched BG videos");
|
||||
}
|
||||
|
||||
void d3d8_iidx_fix_12_song_select_bg(void)
|
||||
{
|
||||
gfx_fix_iidx_12_fog = true;
|
||||
log_info("Fixing Happy Sky bugged music select 3D background");
|
||||
}
|
||||
|
||||
void d3d8_iidx_fix_13_song_select_bg(void)
|
||||
{
|
||||
gfx_fix_iidx_13_lighting = true;
|
||||
log_info("Fixing DistorteD music select 3D background");
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
static void execute_monitor_check(IDirect3DDevice8Vtbl* api_vtbl,
|
||||
IDirect3DDevice8* pdev)
|
||||
{
|
||||
log_info("Running monitor check...");
|
||||
|
||||
/* d3d8 does not provide anything like d3d9 with DrawText */
|
||||
|
||||
const uint32_t max_iterations = 60 * 30;
|
||||
const uint32_t skip_frames = 60 * 1;
|
||||
|
||||
uint64_t accu_us = 0;
|
||||
|
||||
double result = 0;
|
||||
uint32_t iterations = 0;
|
||||
|
||||
while (iterations < max_iterations) {
|
||||
iterations++;
|
||||
uint64_t start = time_get_counter();
|
||||
|
||||
api_vtbl->Clear(pdev, 0, NULL, D3DCLEAR_TARGET,
|
||||
D3DCOLOR_XRGB(0xFF, 0xFF, 0xFF), 0.0f, 0);
|
||||
api_vtbl->BeginScene(pdev);
|
||||
|
||||
api_vtbl->EndScene(pdev);
|
||||
api_vtbl->Present(pdev, NULL, NULL, NULL, NULL);
|
||||
|
||||
/* Skip the first inaccurate values */
|
||||
if (iterations > skip_frames) {
|
||||
accu_us += time_get_elapsed_us(time_get_counter() - start);
|
||||
|
||||
result = ((double) (iterations - skip_frames)) /
|
||||
(accu_us / 1000.0 / 1000.0);
|
||||
}
|
||||
}
|
||||
|
||||
log_info("Monitor check done (total iterations %d), refesh rate: %f hz",
|
||||
iterations, result);
|
||||
|
||||
/* Sanity check to ensure people notice that their current refresh rate
|
||||
is way off. */
|
||||
if (result < 55 || result > 65) {
|
||||
log_warning("Monitor check result (%f hz) is not even near the "
|
||||
"intended refresh rate of 60 hz. Fix your setup to ensure a "
|
||||
"constant and as close to as possible 60 hz refresh rate.", result);
|
||||
}
|
||||
|
||||
log_assert(gfx_monitor_check_cb);
|
||||
|
||||
gfx_monitor_check_cb(result);
|
||||
}
|
||||
|
||||
static void calc_win_size_with_framed(HWND hwnd, DWORD x, DWORD y, DWORD width,
|
||||
DWORD height, LPWINDOWPOS wp)
|
||||
{
|
||||
/* taken from dxwnd */
|
||||
RECT rect;
|
||||
DWORD style;
|
||||
int max_x, max_y;
|
||||
HMENU menu;
|
||||
|
||||
rect.left = x;
|
||||
rect.top = y;
|
||||
max_x = width;
|
||||
max_y = height;
|
||||
rect.right = x + max_x;
|
||||
rect.bottom = y + max_y;
|
||||
|
||||
style = GetWindowLong(hwnd, GWL_STYLE);
|
||||
menu = GetMenu(hwnd);
|
||||
AdjustWindowRect(&rect, style, (menu != NULL));
|
||||
|
||||
/* shift down-right so that the border is visible
|
||||
and also update the iPosX,iPosY upper-left coordinates
|
||||
of the client area */
|
||||
|
||||
if (rect.left < 0) {
|
||||
rect.right -= rect.left;
|
||||
rect.left = 0;
|
||||
}
|
||||
|
||||
if (rect.top < 0) {
|
||||
rect.bottom -= rect.top;
|
||||
rect.top = 0;
|
||||
}
|
||||
|
||||
wp->x = rect.left;
|
||||
wp->y = rect.top;
|
||||
wp->cx = rect.right - rect.left;
|
||||
wp->cy = rect.bottom - rect.top;
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
#ifndef IIDXHOOK_D3D8_H
|
||||
#define IIDXHOOK_D3D8_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
/**
|
||||
* Callback function to receive resulting refresh rate of monitor check.
|
||||
*/
|
||||
typedef void (*d3d8_monitor_check_result_callback_t)(double refresh_rate);
|
||||
|
||||
/**
|
||||
* Hook some d3d8 related functions to patch gfx related stuff
|
||||
* like enabling window mode.
|
||||
*
|
||||
* Games using d3d8:
|
||||
* - IIDX: 9th to DD
|
||||
*/
|
||||
void d3d8_hook_init(void);
|
||||
|
||||
/**
|
||||
* Set the game to window mode.
|
||||
*
|
||||
* @param framed True to add a window frame and make the window
|
||||
* movable, resizable, minizable. False for no frame.
|
||||
* @param width Width of the window. -1 to keep original width.
|
||||
* @param height Height of the window. -1 to keep original height.
|
||||
*/
|
||||
void d3d8_set_windowed(bool framed, int32_t width, int32_t height);
|
||||
|
||||
/**
|
||||
* Set a framerate limit for the rendering loop.
|
||||
*
|
||||
* Use this if the game won't sync up properly with vsync enabled.
|
||||
*
|
||||
* @limit Number of frames to limit the rendering loop to
|
||||
*/
|
||||
void d3d8_set_frame_rate_limit(float limit);
|
||||
|
||||
/**
|
||||
* Enable a software a "monitor check" which determines the (avg.) refresh rate
|
||||
* of the game. Ensure that your GPU is outputting a stable refresh rate to
|
||||
* your monitor. Otherwise, the resulting values fluctuate a lot
|
||||
*
|
||||
* @param cb Callback to a function that receives the result of the monitor
|
||||
* check once it completed.
|
||||
*/
|
||||
void d3d8_enable_monitor_check(d3d8_monitor_check_result_callback_t cb);
|
||||
|
||||
/**
|
||||
* Fixes UV coordinates for background videos on
|
||||
* IIDX Red to DD. Use this if you experience the game
|
||||
* stretching the background videos
|
||||
* (see implementation for further details)
|
||||
*/
|
||||
void d3d8_iidx_fix_stretched_bg_videos(void);
|
||||
|
||||
/**
|
||||
* Fixes the 3D background displayed during song select by disabling fog.
|
||||
* On some machines/GPUs the background is rendered completely white which
|
||||
* is caused by buggy fog rendering. Fog is used to hide the rings appearing
|
||||
* but that's quite 'far away' so disabling fog doesn't look bad.
|
||||
*/
|
||||
void d3d8_iidx_fix_12_song_select_bg(void);
|
||||
|
||||
/**
|
||||
* Fixes the 3D background displayed during song select by disabling lighting.
|
||||
* On some machines/GPUs the background is rendered completely black which
|
||||
* is caused by buggy lighting. Disabling (dynamic) lighting sets the whole
|
||||
* scene to ambient lighting which displays everything properly.
|
||||
*/
|
||||
void d3d8_iidx_fix_13_song_select_bg(void);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,907 @@
|
||||
#define LOG_MODULE "d3d9-hook"
|
||||
|
||||
#include <windows.h>
|
||||
#include <d3d9.h>
|
||||
#include <d3dx9core.h>
|
||||
|
||||
#include <math.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "hook/com-proxy.h"
|
||||
#include "hook/table.h"
|
||||
|
||||
#include "iidxhook-util/d3d9.h"
|
||||
|
||||
#include "util/defs.h"
|
||||
#include "util/log.h"
|
||||
#include "util/str.h"
|
||||
#include "util/time.h"
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
struct d3d8_vertex {
|
||||
float x;
|
||||
float y;
|
||||
float z;
|
||||
uint32_t color;
|
||||
uint32_t unknown;
|
||||
float tu;
|
||||
float tv;
|
||||
};
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
/* Device IDs recognised by IIDX <20. Hex chars must be capital letters.
|
||||
|
||||
7146: RV515 [Radeon X1300]
|
||||
95C5: RV620 LE [Radeon HD 3450]
|
||||
|
||||
VEN is always 1002 (ATi) */
|
||||
|
||||
static HWND STDCALL my_CreateWindowExA(
|
||||
DWORD dwExStyle, LPCSTR lpClassName, LPCSTR lpWindowName, DWORD dwStyle,
|
||||
int X, int Y, int nWidth, int nHeight, HWND hWndParent, HMENU hMenu,
|
||||
HINSTANCE hInstance, LPVOID lpParam);
|
||||
|
||||
static BOOL STDCALL my_GetClientRect(HWND hWnd, LPRECT lpRect);
|
||||
|
||||
static HRESULT STDCALL my_CreateDevice(
|
||||
IDirect3D9 *self, UINT adapter, D3DDEVTYPE type, HWND hwnd,
|
||||
DWORD flags, D3DPRESENT_PARAMETERS *pp, IDirect3DDevice9 **pdev);
|
||||
|
||||
static IDirect3D9 *STDCALL my_Direct3DCreate9(UINT sdk_ver);
|
||||
|
||||
static BOOL STDCALL my_EnumDisplayDevicesA(
|
||||
const char *dev_name, DWORD dev_num, DISPLAY_DEVICEA *info,
|
||||
DWORD flags);
|
||||
|
||||
static HRESULT STDCALL my_CreateTexture(IDirect3DDevice9* self, UINT Width,
|
||||
UINT Height, UINT Levels, DWORD Usage, D3DFORMAT Format, D3DPOOL Pool,
|
||||
IDirect3DTexture9** ppTexture, HANDLE* pSharedHandle);
|
||||
|
||||
static HRESULT STDCALL my_SetRenderState(IDirect3DDevice9* self,
|
||||
D3DRENDERSTATETYPE State, DWORD Value);
|
||||
|
||||
static HRESULT STDCALL my_DrawPrimitiveUP(
|
||||
IDirect3DDevice9* self, D3DPRIMITIVETYPE primitive_type,
|
||||
UINT primitive_count, const void *data, UINT stride);
|
||||
|
||||
static HRESULT STDCALL my_Present(IDirect3DDevice9* self,
|
||||
CONST RECT *pSourceRect, CONST RECT *pDestRect,
|
||||
HWND hDestWindowOverride, CONST RGNDATA *pDirtyRegion);
|
||||
|
||||
static HRESULT STDCALL my_BeginScene(IDirect3DDevice9* self);
|
||||
|
||||
static HRESULT STDCALL my_EndScene(IDirect3DDevice9* self);
|
||||
|
||||
static void calc_win_size_with_framed(HWND hwnd, DWORD x, DWORD y,
|
||||
DWORD width, DWORD height, LPWINDOWPOS wp);
|
||||
|
||||
static HWND (STDCALL *real_CreateWindowExA)(
|
||||
DWORD dwExStyle, LPCSTR lpClassName, LPCSTR lpWindowName, DWORD dwStyle,
|
||||
int X, int Y, int nWidth, int nHeight, HWND hWndParent, HMENU hMenu,
|
||||
HINSTANCE hInstance, LPVOID lpParam);
|
||||
|
||||
static BOOL (STDCALL* real_GetClientRect)(HWND hWnd, LPRECT lpRect);
|
||||
|
||||
static IDirect3D9 * (STDCALL *real_Direct3DCreate9)(
|
||||
UINT sdk_ver);
|
||||
|
||||
static BOOL (STDCALL *real_EnumDisplayDevicesA)(
|
||||
const char *dev_name, DWORD dev_num, DISPLAY_DEVICEA *info,
|
||||
DWORD flags);
|
||||
|
||||
static HRESULT (STDCALL *real_CreateTexture)(IDirect3DDevice9* self, UINT Width,
|
||||
UINT Height, UINT Levels, DWORD Usage, D3DFORMAT Format, D3DPOOL Pool,
|
||||
IDirect3DTexture9** ppTexture, HANDLE* pSharedHandle);
|
||||
|
||||
static HRESULT (STDCALL* real_SetRenderState)(IDirect3DDevice9* self,
|
||||
D3DRENDERSTATETYPE State, DWORD Value);
|
||||
|
||||
static HRESULT (STDCALL *real_DrawPrimitiveUP)(
|
||||
IDirect3DDevice9* self, D3DPRIMITIVETYPE primitive_type,
|
||||
UINT primitive_count, const void *data, UINT stride);
|
||||
|
||||
static HRESULT (STDCALL* real_Present)(IDirect3DDevice9* self,
|
||||
CONST RECT *pSourceRect, CONST RECT *pDestRect,
|
||||
HWND hDestWindowOverride, CONST RGNDATA *pDirtyRegion);
|
||||
|
||||
static HRESULT (STDCALL* real_BeginScene)(IDirect3DDevice9* self);
|
||||
|
||||
static HRESULT (STDCALL* real_EndScene)(IDirect3DDevice9* self);
|
||||
|
||||
static HRESULT (STDCALL* real_GetRenderTarget)(IDirect3DDevice9* self, DWORD RenderTargetIndex,
|
||||
IDirect3DSurface9 **ppRenderTarget);
|
||||
|
||||
static HRESULT (STDCALL* real_SetRenderTarget)(IDirect3DDevice9* self, DWORD RenderTargetIndex,
|
||||
IDirect3DSurface9 *pRenderTarget);
|
||||
|
||||
static HRESULT (STDCALL* real_StretchRect)(IDirect3DDevice9* self, IDirect3DSurface9 *pSourceSurface,
|
||||
const RECT *pSourceRect, IDirect3DSurface9 *pDestSurface, const RECT *pDestRect, D3DTEXTUREFILTERTYPE Filter);
|
||||
|
||||
typedef HRESULT WINAPI (*func_D3DXCreateFontA)(struct IDirect3DDevice9 *device,
|
||||
INT height, UINT width, UINT weight, UINT miplevels, BOOL italic,
|
||||
DWORD charset, DWORD precision, DWORD quality, DWORD pitchandfamily,
|
||||
const char *facename, struct ID3DXFont **font);
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
static char d3d9_pci_id[32];
|
||||
static bool d3d9_windowed;
|
||||
static int32_t d3d9_window_width = -1;
|
||||
static int32_t d3d9_window_height = -1;
|
||||
static bool d3d9_window_framed;
|
||||
static d3d9_monitor_check_result_callback_t d3d9_monitor_check_cb;
|
||||
static bool d3d9_fix_iidx_12_fog = false;
|
||||
static bool d3d9_fix_iidx_13_lighting = false;
|
||||
static bool d3d9_nvidia_fix;
|
||||
static bool d3d9_bg_video_fix;
|
||||
static float d3d9_frame_rate_limit = 0.0f;
|
||||
static uint16_t d3d9_scale_back_buffer_width;
|
||||
static uint16_t d3d9_scale_back_buffer_height;
|
||||
|
||||
static D3DTEXTUREFILTERTYPE d3d9_scale_back_buffer_filter;
|
||||
static IDirect3DSurface9* d3d9_original_back_buffer;
|
||||
static IDirect3DSurface9* d3d9_scale_intermediate_render_target;
|
||||
static uint16_t d3d9_original_back_buffer_width;
|
||||
static uint16_t d3d9_original_back_buffer_height;
|
||||
|
||||
static void execute_monitor_check(IDirect3DDevice9* api,
|
||||
IDirect3DDevice9Vtbl* api_vtbl, IDirect3DDevice9* pdev);
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
static const struct hook_symbol d3d9_hook_syms[] = {
|
||||
{
|
||||
.name = "Direct3DCreate9",
|
||||
.patch = my_Direct3DCreate9,
|
||||
.link = (void **) &real_Direct3DCreate9
|
||||
},
|
||||
};
|
||||
|
||||
static const struct hook_symbol d3d9_hook_user32_syms[] = {
|
||||
{
|
||||
.name = "EnumDisplayDevicesA",
|
||||
.patch = my_EnumDisplayDevicesA,
|
||||
.link = (void **) &real_EnumDisplayDevicesA
|
||||
},
|
||||
{
|
||||
.name = "CreateWindowExA",
|
||||
.patch = my_CreateWindowExA,
|
||||
.link = (void **) &real_CreateWindowExA
|
||||
},
|
||||
{
|
||||
.name = "GetClientRect",
|
||||
.patch = my_GetClientRect,
|
||||
.link = (void **) &real_GetClientRect
|
||||
},
|
||||
};
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
static bool float_equal(float a, float b, float eps)
|
||||
{
|
||||
return fabs(a - b) < eps;
|
||||
}
|
||||
|
||||
static HWND STDCALL my_CreateWindowExA(
|
||||
DWORD dwExStyle, LPCSTR lpClassName, LPCSTR lpWindowName, DWORD dwStyle,
|
||||
int X, int Y, int nWidth, int nHeight, HWND hWndParent, HMENU hMenu,
|
||||
HINSTANCE hInstance, LPVOID lpParam)
|
||||
{
|
||||
if (d3d9_windowed && d3d9_window_framed) {
|
||||
/* use a different style */
|
||||
dwStyle |= WS_OVERLAPPEDWINDOW;
|
||||
/* also show mouse cursor */
|
||||
ShowCursor(TRUE);
|
||||
}
|
||||
|
||||
if (d3d9_window_width != -1 && d3d9_window_height != -1) {
|
||||
log_misc("Overriding window size from %dx%d with %dx%d", nWidth,
|
||||
nHeight, d3d9_window_width, d3d9_window_height);
|
||||
|
||||
nWidth = d3d9_window_width;
|
||||
nHeight = d3d9_window_height;
|
||||
}
|
||||
|
||||
HWND hwnd = real_CreateWindowExA(dwExStyle, lpClassName, lpWindowName,
|
||||
dwStyle, X, Y, nWidth, nHeight, hWndParent, hMenu, hInstance,
|
||||
lpParam);
|
||||
|
||||
if (hwnd == INVALID_HANDLE_VALUE) {
|
||||
return hwnd;
|
||||
}
|
||||
|
||||
if (d3d9_windowed && d3d9_window_framed) {
|
||||
/* we have to adjust the window size, because the window needs to be a
|
||||
slightly bigger than the rendering resolution (window caption and
|
||||
stuff is included in the window size) */
|
||||
WINDOWPOS wp;
|
||||
calc_win_size_with_framed(hwnd, X, Y, nWidth, nHeight, &wp);
|
||||
SetWindowPos(hwnd, 0, wp.x, wp.y, wp.cx, wp.cy, 0);
|
||||
X = wp.x;
|
||||
Y = wp.y;
|
||||
nWidth = wp.cx;
|
||||
nHeight = wp.cy;
|
||||
}
|
||||
|
||||
return hwnd;
|
||||
}
|
||||
|
||||
static BOOL STDCALL my_GetClientRect(HWND hWnd, LPRECT lpRect)
|
||||
{
|
||||
/* IIDX 9-13 (at least) use this call to get the size of rectangle for setting the viewport size... */
|
||||
|
||||
/* Always use the original requested back buffer size. If scaling is active, using the scaled
|
||||
values leads to (3D) scenes getting render to a viewport with incorrect sized. */
|
||||
lpRect->left = 0;
|
||||
lpRect->top = 0;
|
||||
lpRect->right = d3d9_original_back_buffer_width;
|
||||
lpRect->bottom = d3d9_original_back_buffer_height;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static HRESULT STDCALL my_CreateDevice(
|
||||
IDirect3D9 *self, UINT adapter, D3DDEVTYPE type, HWND hwnd, DWORD flags,
|
||||
D3DPRESENT_PARAMETERS *pp, IDirect3DDevice9 **pdev)
|
||||
{
|
||||
IDirect3D9 *real = COM_PROXY_UNWRAP(self);
|
||||
IDirect3DDevice9* api;
|
||||
IDirect3DDevice9Vtbl *api_vtbl;
|
||||
struct com_proxy *api_proxy;
|
||||
HRESULT hr;
|
||||
char* error;
|
||||
|
||||
log_misc("CreateDevice parameters: adapter %d, type %d, hwnd %p, flags %lX, pdev %p",
|
||||
adapter, type, hwnd, flags, pdev);
|
||||
|
||||
log_misc("D3D9 presenter parameters: BackBufferWidth %d, BackBufferHeight %d, BackBufferFormat %d, "
|
||||
"BackBufferCount %d, MultiSampleType %d, SwapEffect %d, hDeviceWindow %p, Windowed %d, EnableAutoDepthStencil "
|
||||
"%d, AutoDepthStencilFormat %d, Flags %lX, FullScreen_RefreshRateInHz %d",
|
||||
pp->BackBufferWidth, pp->BackBufferHeight, pp->BackBufferFormat, pp->BackBufferCount, pp->MultiSampleType,
|
||||
pp->SwapEffect, pp->hDeviceWindow, pp->Windowed, pp->EnableAutoDepthStencil, pp->AutoDepthStencilFormat,
|
||||
pp->Flags, pp->FullScreen_RefreshRateInHz);
|
||||
|
||||
/* Fix a long-standing bug in IIDX */
|
||||
if (flags & D3DCREATE_SOFTWARE_VERTEXPROCESSING) {
|
||||
flags &= ~D3DCREATE_PUREDEVICE;
|
||||
}
|
||||
|
||||
if (d3d9_windowed) {
|
||||
log_misc("Window mode");
|
||||
|
||||
pp->Windowed = TRUE;
|
||||
pp->FullScreen_RefreshRateInHz = 0;
|
||||
}
|
||||
|
||||
/* Store these so we can apply scaling further down */
|
||||
d3d9_original_back_buffer_width = pp->BackBufferWidth;
|
||||
d3d9_original_back_buffer_height = pp->BackBufferHeight;
|
||||
|
||||
if (d3d9_scale_back_buffer_width > 0 && d3d9_scale_back_buffer_height > 0) {
|
||||
log_misc("Scale back buffer from %dx%d -> %dx%d", d3d9_original_back_buffer_width,
|
||||
d3d9_original_back_buffer_height, d3d9_scale_back_buffer_width, d3d9_scale_back_buffer_height);
|
||||
|
||||
pp->BackBufferWidth = d3d9_scale_back_buffer_width;
|
||||
pp->BackBufferHeight = d3d9_scale_back_buffer_height;
|
||||
}
|
||||
|
||||
/* Same fix as on D3D8, orz...
|
||||
If we don't do this, some games one certain platforms (e.g. iidx 14/15 on Windows 10).
|
||||
CreateDevice fails with an "invalid call" on either fullscreen or windowed or even both.
|
||||
Also, further reports about textures with green glowing borders are gone as well when applying this */
|
||||
D3DDISPLAYMODE mode;
|
||||
IDirect3D9_GetAdapterDisplayMode(real, adapter, &mode);
|
||||
pp->BackBufferFormat = mode.Format;
|
||||
|
||||
hr = IDirect3D9_CreateDevice(real, adapter, type, hwnd, flags, pp, pdev);
|
||||
|
||||
if (hr != S_OK) {
|
||||
switch (hr) {
|
||||
case D3DERR_DEVICELOST:
|
||||
error = "device lost";
|
||||
break;
|
||||
|
||||
case D3DERR_INVALIDCALL:
|
||||
error = "invalid call";
|
||||
break;
|
||||
|
||||
case D3DERR_NOTAVAILABLE:
|
||||
error = "not available";
|
||||
break;
|
||||
|
||||
case D3DERR_OUTOFVIDEOMEMORY:
|
||||
error = "out of video memory";
|
||||
break;
|
||||
|
||||
default:
|
||||
error = "unknown";
|
||||
break;
|
||||
}
|
||||
|
||||
log_warning("Creating D3D9 device failed: %lX, %s", hr, error);
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
api = *pdev;
|
||||
api_proxy = com_proxy_wrap(api, sizeof(*api->lpVtbl));
|
||||
api_vtbl = api_proxy->vptr;
|
||||
|
||||
real_CreateTexture = api_vtbl->CreateTexture;
|
||||
api_vtbl->CreateTexture = my_CreateTexture;
|
||||
|
||||
real_DrawPrimitiveUP = api_vtbl->DrawPrimitiveUP;
|
||||
api_vtbl->DrawPrimitiveUP = my_DrawPrimitiveUP;
|
||||
|
||||
real_SetRenderState = api_vtbl->SetRenderState;
|
||||
api_vtbl->SetRenderState = my_SetRenderState;
|
||||
|
||||
real_Present = api_vtbl->Present;
|
||||
api_vtbl->Present = my_Present;
|
||||
|
||||
real_BeginScene = api_vtbl->BeginScene;
|
||||
api_vtbl->BeginScene = my_BeginScene;
|
||||
|
||||
real_EndScene = api_vtbl->EndScene;
|
||||
api_vtbl->EndScene = my_EndScene;
|
||||
|
||||
real_SetRenderTarget = api_vtbl->SetRenderTarget;
|
||||
real_GetRenderTarget = api_vtbl->GetRenderTarget;
|
||||
real_StretchRect = api_vtbl->StretchRect;
|
||||
|
||||
*pdev = (IDirect3DDevice9*) api_proxy;
|
||||
|
||||
if (d3d9_scale_back_buffer_width > 0 && d3d9_scale_back_buffer_height > 0) {
|
||||
hr = api_vtbl->CreateRenderTarget(*pdev, d3d9_scale_back_buffer_width, d3d9_scale_back_buffer_height,
|
||||
pp->BackBufferFormat, pp->MultiSampleType, 0, false, &d3d9_scale_intermediate_render_target, NULL);
|
||||
|
||||
if (hr != D3D_OK) {
|
||||
log_warning("Creating intermediate render target for scaling back buffer failed: %ld", hr);
|
||||
return hr;
|
||||
}
|
||||
}
|
||||
|
||||
if (d3d9_monitor_check_cb) {
|
||||
execute_monitor_check(api, api_vtbl, *pdev);
|
||||
}
|
||||
|
||||
/* Avoid returning our scaled values because the application might use them, e.g. calculate
|
||||
sprite positions. */
|
||||
pp->BackBufferWidth = d3d9_original_back_buffer_width;
|
||||
pp->BackBufferHeight = d3d9_original_back_buffer_height;
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
static IDirect3D9 *STDCALL my_Direct3DCreate9(UINT sdk_ver)
|
||||
{
|
||||
IDirect3D9 *api;
|
||||
IDirect3D9Vtbl *api_vtbl;
|
||||
struct com_proxy *api_proxy;
|
||||
|
||||
log_info("Direct3DCreate9 hook hit");
|
||||
|
||||
api = real_Direct3DCreate9(sdk_ver);
|
||||
api_proxy = com_proxy_wrap(api, sizeof(*api->lpVtbl));
|
||||
api_vtbl = api_proxy->vptr;
|
||||
|
||||
api_vtbl->CreateDevice = my_CreateDevice;
|
||||
|
||||
return (IDirect3D9 *) api_proxy;
|
||||
}
|
||||
|
||||
static BOOL STDCALL my_EnumDisplayDevicesA(
|
||||
const char *dev_name, DWORD dev_num, DISPLAY_DEVICEA *info,
|
||||
DWORD flags)
|
||||
{
|
||||
BOOL ok;
|
||||
|
||||
ok = real_EnumDisplayDevicesA(dev_name, dev_num, info, flags);
|
||||
|
||||
if (ok && d3d9_pci_id[0] != '\0') {
|
||||
/* Apparently Konami didn't read the "Not Used" message in the MSDN
|
||||
docs for DISPLAY_DEVICE */
|
||||
log_misc("Replacing device ID %s with %s",
|
||||
info->DeviceID, d3d9_pci_id);
|
||||
|
||||
str_cpy(info->DeviceID, sizeof(info->DeviceID), d3d9_pci_id);
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
static HRESULT STDCALL my_CreateTexture(IDirect3DDevice9* self, UINT Width,
|
||||
UINT Height, UINT Levels, DWORD Usage, D3DFORMAT Format, D3DPOOL Pool,
|
||||
IDirect3DTexture9** ppTexture, HANDLE* pSharedHandle)
|
||||
{
|
||||
/* Fix non ATI cards not working on Gold (and DJT?). There is a check that
|
||||
creates textures when starting the game. This check fails which results
|
||||
in a message box being shown mentioning something about an aep-lib error.
|
||||
Seems like a combination of parameters is not supported by
|
||||
non ATI cards and throws an error
|
||||
(Fix taken from old Gold cracks by ahnada and tau)
|
||||
*/
|
||||
if (d3d9_nvidia_fix && Width == 256 && Height == 256 && Levels == 1 &&
|
||||
Usage == 1 && Format == 25 && Pool == 0 && pSharedHandle == 0) {
|
||||
/* log_misc("CreateTexture: Patching texture format..."); */
|
||||
Format = 21;
|
||||
}
|
||||
|
||||
return real_CreateTexture(self, Width, Height, Levels, Usage, Format, Pool,
|
||||
ppTexture, pSharedHandle);
|
||||
}
|
||||
|
||||
static HRESULT STDCALL my_SetRenderState(IDirect3DDevice9* self,
|
||||
D3DRENDERSTATETYPE State, DWORD Value)
|
||||
{
|
||||
if (d3d9_fix_iidx_12_fog) {
|
||||
if (State == D3DRS_FOGENABLE) {
|
||||
Value = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if (d3d9_fix_iidx_13_lighting) {
|
||||
if (State == D3DRS_LIGHTING) {
|
||||
Value = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return real_SetRenderState(self, State, Value);
|
||||
}
|
||||
|
||||
static HRESULT STDCALL my_DrawPrimitiveUP(
|
||||
IDirect3DDevice9* self, D3DPRIMITIVETYPE primitive_type,
|
||||
UINT primitive_count, const void *data, UINT stride)
|
||||
{
|
||||
/* same code taken from the d3d8 module. but, this just fixes the quad
|
||||
seam issue as there are no reports of streched bg videos */
|
||||
if ( d3d9_bg_video_fix && primitive_type == 6 && primitive_count == 2 &&
|
||||
stride == 28) {
|
||||
|
||||
struct d3d8_vertex* vertices = (struct d3d8_vertex*) data;
|
||||
|
||||
/*
|
||||
log_info("Video Tex: %f/%f %f/%f %f/%f %f/%f",
|
||||
vertices[0].x, vertices[0].y,
|
||||
vertices[1].x, vertices[1].y,
|
||||
vertices[2].x, vertices[2].y,
|
||||
vertices[3].x, vertices[3].y);
|
||||
*/
|
||||
|
||||
/* Fix full screen background videos (e.g. DistorteD intro sequence) */
|
||||
if ( vertices[0].x >= 0.0f && vertices[0].x < 1.0f &&
|
||||
vertices[0].y >= 0.0f && vertices[0].y < 1.0f &&
|
||||
vertices[1].x > 639.0f && vertices[1].x < 641.0f &&
|
||||
vertices[1].y >= 0.0f && vertices[1].y < 1.0f &&
|
||||
vertices[2].x > 639.0f && vertices[2].x < 641.0f &&
|
||||
vertices[2].y > 479.0f && vertices[2].y < 481.0f &&
|
||||
vertices[3].x >= 0.0f && vertices[3].x < 1.0f &&
|
||||
vertices[3].y > 479.0f && vertices[3].y < 481.0f) {
|
||||
/* fix UVs
|
||||
1.0f / 640 or 480 fixes the diagonal seam connecting the two
|
||||
triangles which is visible on some GPUs (why? idk)
|
||||
*/
|
||||
vertices[0].tu = 0.0f + 1.0f / 640;
|
||||
vertices[0].tv = 1.0f;
|
||||
vertices[1].tu = 1.0f;
|
||||
vertices[1].tv = 1.0f;
|
||||
vertices[2].tu = 1.0f;
|
||||
vertices[2].tv = 0.0f + 1.0f / 480;
|
||||
vertices[3].tu = 0.0f + 1.0f / 640;
|
||||
vertices[3].tv = 0.0f + 1.0f / 480;
|
||||
} else
|
||||
|
||||
/* another identifier, because there are other textures with 512x512 size
|
||||
make sure we got the bg video only to not mess up anything else */
|
||||
/* different versions have different themes and position the bg video
|
||||
on slightly different positions (good job...) */
|
||||
if ( /* single */
|
||||
((vertices[0].x >= 164.0f && vertices[0].x <= 168.0f &&
|
||||
float_equal(vertices[0].y, 0.0f, 0.1f)) &&
|
||||
(vertices[1].x >= 472.0f && vertices[1].x <= 476.0f &&
|
||||
float_equal(vertices[1].y, 0.0f, 0.1f)) &&
|
||||
(vertices[2].x >= 472.0f && vertices[2].x <= 476.0f &&
|
||||
float_equal(vertices[2].y, 416.0f, 0.1f)) &&
|
||||
(vertices[3].x >= 164.0f && vertices[3].x <= 168.0f &&
|
||||
float_equal(vertices[3].y, 416.0f, 0.1f))) ||
|
||||
/* double top left */
|
||||
((float_equal(vertices[0].x, 6.0f, 0.1f) &&
|
||||
vertices[0].y >= 24.0f && vertices[0].y <= 28.0f) &&
|
||||
(float_equal(vertices[1].x, 147.0f, 0.1f) &&
|
||||
vertices[1].y >= 24.0f && vertices[1].y <= 28.0f) &&
|
||||
(float_equal(vertices[2].x, 147.0f, 0.1f) &&
|
||||
vertices[2].y >= 212.0f && vertices[2].y <= 216.0f) &&
|
||||
(float_equal(vertices[3].x, 6.0f, 0.1f) &&
|
||||
vertices[3].y >= 212.0f && vertices[3].y <= 216.0f)) ||
|
||||
/* double bottom left */
|
||||
((float_equal(vertices[0].x, 6.0f, 0.1f) &&
|
||||
vertices[0].y >= 216.0f && vertices[0].y <= 220.0f) &&
|
||||
(float_equal(vertices[1].x, 147.0f, 0.1f) &&
|
||||
vertices[1].y >= 216.0f && vertices[1].y <= 220.0f) &&
|
||||
(float_equal(vertices[2].x, 147.0f, 0.1) &&
|
||||
vertices[2].y >= 404.0f && vertices[2].y <= 408.0f) &&
|
||||
(float_equal(vertices[3].x, 6.0f, 0.1f) &&
|
||||
vertices[3].y >= 404.0f && vertices[3].y <= 408.0f)) ||
|
||||
/* double top right */
|
||||
((vertices[0].x >= 493.0f && vertices[0].x <= 494.0f &&
|
||||
vertices[0].y >= 24.0f && vertices[0].y <= 28.0f) &&
|
||||
(vertices[1].x >= 634.0f && vertices[1].x <= 635.0f &&
|
||||
vertices[1].y >= 24.0f && vertices[1].y <= 28.0f) &&
|
||||
(vertices[2].x >= 634.0f && vertices[2].x <= 635.0f &&
|
||||
vertices[2].y >= 212.0f && vertices[2].y <= 216.0f) &&
|
||||
(vertices[3].x >= 493.0f && vertices[3].x <= 494.0f &&
|
||||
vertices[3].y >= 212.0f && vertices[3].y <= 216.0f)) ||
|
||||
/* double bottom right */
|
||||
((vertices[0].x >= 493.0f && vertices[0].x <= 494.0f &&
|
||||
vertices[0].y >= 216.0f && vertices[0].y <= 220.0f) &&
|
||||
(vertices[1].x >= 634.0f && vertices[1].x <= 635.0f &&
|
||||
vertices[1].y >= 216.0f && vertices[1].y <= 220.0f) &&
|
||||
(vertices[2].x >= 634.0f && vertices[2].x <= 635.0f &&
|
||||
vertices[2].y >= 404.0f && vertices[2].y <= 408.0f) &&
|
||||
(vertices[3].x >= 493.0f && vertices[3].x <= 494.0f &&
|
||||
vertices[3].y >= 404.0f && vertices[3].y <= 408.0f)))
|
||||
{
|
||||
/* fix UVs
|
||||
1.0f / 512 fixes the diagonal seam connecting the two triangles
|
||||
which is visible on some GPUs (why? idk)
|
||||
*/
|
||||
vertices[0].tu = 0.0f + 1.0f / 512;
|
||||
vertices[0].tv = 1.0f;
|
||||
vertices[1].tu = 1.0f;
|
||||
vertices[1].tv = 1.0f;
|
||||
vertices[2].tu = 1.0f;
|
||||
vertices[2].tv = 0.0f + 1.0f / 512;
|
||||
vertices[3].tu = 0.0f + 1.0f / 512;
|
||||
vertices[3].tv = 0.0f + 1.0f / 512;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return real_DrawPrimitiveUP(self, primitive_type, primitive_count,
|
||||
data, stride);
|
||||
}
|
||||
|
||||
static HRESULT STDCALL my_Present(IDirect3DDevice9* self,
|
||||
CONST RECT *pSourceRect, CONST RECT *pDestRect,
|
||||
HWND hDestWindowOverride, CONST RGNDATA *pDirtyRegion)
|
||||
{
|
||||
static uint64_t current_time = 0;
|
||||
|
||||
HRESULT hr = real_Present(self, pSourceRect, pDestRect, hDestWindowOverride,
|
||||
pDirtyRegion);
|
||||
|
||||
if (d3d9_frame_rate_limit > 0.0f) {
|
||||
if (current_time == 0) {
|
||||
current_time = time_get_counter();
|
||||
} else {
|
||||
uint64_t frame_time = 1000000 / d3d9_frame_rate_limit;
|
||||
|
||||
uint64_t dt = time_get_elapsed_us(
|
||||
time_get_counter() - current_time);
|
||||
|
||||
while (dt < frame_time) {
|
||||
/* waste some cpu time by polling
|
||||
because we can't sleep for X us */
|
||||
dt = time_get_elapsed_us(time_get_counter() - current_time);
|
||||
}
|
||||
|
||||
current_time = time_get_counter();
|
||||
}
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
static HRESULT STDCALL my_BeginScene(IDirect3DDevice9* self)
|
||||
{
|
||||
HRESULT res;
|
||||
|
||||
if (d3d9_scale_back_buffer_width > 0 && d3d9_scale_back_buffer_height > 0) {
|
||||
res = real_GetRenderTarget(self, 0, &d3d9_original_back_buffer);
|
||||
|
||||
if (res != D3D_OK) {
|
||||
log_warning("Getting back buffer render target failed: %ld", res);
|
||||
return res;
|
||||
}
|
||||
|
||||
res = real_SetRenderTarget(self, 0, d3d9_scale_intermediate_render_target);
|
||||
|
||||
if (res != D3D_OK) {
|
||||
log_warning("Setting intermediate render target failed: %ld", res);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
return real_BeginScene(self);
|
||||
}
|
||||
|
||||
static HRESULT STDCALL my_EndScene(IDirect3DDevice9* self)
|
||||
{
|
||||
HRESULT res = real_EndScene(self);
|
||||
|
||||
if (res == D3D_OK) {
|
||||
if (d3d9_scale_back_buffer_width > 0 && d3d9_scale_back_buffer_height > 0) {
|
||||
res = real_SetRenderTarget(self, 0, d3d9_original_back_buffer);
|
||||
|
||||
if (res != D3D_OK) {
|
||||
log_warning("Setting back back buffer render target failed: %ld", res);
|
||||
return res;
|
||||
}
|
||||
|
||||
RECT rect;
|
||||
rect.left = 0;
|
||||
rect.top = 0;
|
||||
rect.right = d3d9_original_back_buffer_width;
|
||||
rect.bottom = d3d9_original_back_buffer_height;
|
||||
|
||||
/* Must be called outside of begin-end scene block */
|
||||
res = real_StretchRect(self, d3d9_scale_intermediate_render_target, &rect, d3d9_original_back_buffer, NULL,
|
||||
d3d9_scale_back_buffer_filter);
|
||||
|
||||
if (res != D3D_OK) {
|
||||
log_warning("Stretching immediate render target to back buffer failed: %ld", res);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void d3d9_hook_init(void)
|
||||
{
|
||||
hook_table_apply(
|
||||
NULL,
|
||||
"d3d9.dll",
|
||||
d3d9_hook_syms,
|
||||
lengthof(d3d9_hook_syms));
|
||||
|
||||
hook_table_apply(
|
||||
NULL,
|
||||
"user32.dll",
|
||||
d3d9_hook_user32_syms,
|
||||
lengthof(d3d9_hook_user32_syms));
|
||||
|
||||
log_info("Inserted graphics hooks");
|
||||
}
|
||||
|
||||
void d3d9_set_windowed(bool framed, int32_t width, int32_t height)
|
||||
{
|
||||
d3d9_windowed = true;
|
||||
d3d9_window_framed = framed;
|
||||
d3d9_window_width = width;
|
||||
d3d9_window_height = height;
|
||||
}
|
||||
|
||||
void d3d9_set_pci_id(uint16_t vid, uint16_t pid)
|
||||
{
|
||||
str_format(d3d9_pci_id, sizeof(d3d9_pci_id), "PCI\\VEN_%04X&DEV_%04X",
|
||||
vid, pid);
|
||||
}
|
||||
|
||||
void d3d9_set_frame_rate_limit(float limit)
|
||||
{
|
||||
if (limit < 0.0f) {
|
||||
limit = 0.0f;
|
||||
}
|
||||
|
||||
log_info("Limiting rendering frame rate to %f frames", limit);
|
||||
d3d9_frame_rate_limit = limit;
|
||||
}
|
||||
|
||||
void d3d9_enable_monitor_check(d3d9_monitor_check_result_callback_t cb)
|
||||
{
|
||||
log_assert(cb);
|
||||
|
||||
d3d9_monitor_check_cb = cb;
|
||||
log_info("Enabled monitor check");
|
||||
}
|
||||
|
||||
void d3d9_iidx_fix_stretched_bg_videos(void)
|
||||
{
|
||||
/* Same as the seam fix */
|
||||
d3d9_bg_video_fix = true;
|
||||
log_info("Fixing UVs of stretched BG videos");
|
||||
}
|
||||
|
||||
void d3d9_iidx_fix_12_song_select_bg(void)
|
||||
{
|
||||
d3d9_fix_iidx_12_fog = true;
|
||||
log_info("Fixing Happy Sky bugged music select 3D background");
|
||||
}
|
||||
|
||||
void d3d9_iidx_fix_13_song_select_bg(void)
|
||||
{
|
||||
d3d9_fix_iidx_13_lighting = true;
|
||||
log_info("Fixing DistorteD music select 3D background");
|
||||
}
|
||||
|
||||
void d3d9_enable_nvidia_fix(void)
|
||||
{
|
||||
d3d9_nvidia_fix = true;
|
||||
log_info("Enabled NVIDIA fix");
|
||||
}
|
||||
|
||||
void d3d9_bg_video_seams_fix(void)
|
||||
{
|
||||
d3d9_bg_video_fix = true;
|
||||
log_info("Enabled BG video seam fix");
|
||||
}
|
||||
|
||||
void d3d9_scale_back_buffer(uint16_t width, uint16_t height, enum d3d9_back_buffer_scale_filter filter)
|
||||
{
|
||||
d3d9_scale_back_buffer_width = width;
|
||||
d3d9_scale_back_buffer_height = height;
|
||||
|
||||
switch (filter) {
|
||||
case D3D9_BACK_BUFFER_SCALE_FILTER_NONE:
|
||||
d3d9_scale_back_buffer_filter = D3DTEXF_NONE;
|
||||
break;
|
||||
|
||||
case D3D9_BACK_BUFFER_SCALE_FILTER_LINEAR:
|
||||
d3d9_scale_back_buffer_filter = D3DTEXF_LINEAR;
|
||||
break;
|
||||
|
||||
case D3D9_BACK_BUFFER_SCALE_FILTER_POINT:
|
||||
d3d9_scale_back_buffer_filter = D3DTEXF_POINT;
|
||||
break;
|
||||
|
||||
default:
|
||||
log_fatal("Illegal state");
|
||||
break;
|
||||
}
|
||||
|
||||
if (d3d9_scale_back_buffer_width > 0 && d3d9_scale_back_buffer_height > 0) {
|
||||
log_info("Enable scaling of back buffer, target size %dx%d, filter %d", width, height, filter);
|
||||
} else {
|
||||
d3d9_scale_back_buffer_width = 0;
|
||||
d3d9_scale_back_buffer_height = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
static void execute_monitor_check(IDirect3DDevice9* api,
|
||||
IDirect3DDevice9Vtbl* api_vtbl, IDirect3DDevice9* pdev)
|
||||
{
|
||||
log_info("Running monitor check...");
|
||||
|
||||
/* All games include the _24 version anyway, so leave that hardcoded here */
|
||||
HMODULE d3d9 = GetModuleHandleA("d3dx9_24.dll");
|
||||
|
||||
if (d3d9 == NULL) {
|
||||
log_fatal("monitor check: failed to load d3dx9_24.dll");
|
||||
}
|
||||
|
||||
func_D3DXCreateFontA d3dxCreateFontA =
|
||||
(func_D3DXCreateFontA) GetProcAddress(d3d9, "D3DXCreateFontA");
|
||||
|
||||
if (d3dxCreateFontA == NULL) {
|
||||
log_fatal("monitor check: failed to find function D3DXCreateFontA");
|
||||
}
|
||||
|
||||
RECT fRectangle;
|
||||
ID3DXFont* font = NULL;
|
||||
struct com_proxy *font_api_proxy;
|
||||
ID3DXFontVtbl *font_api_vtbl;
|
||||
|
||||
HRESULT hr = d3dxCreateFontA(api, 22, 0, FW_NORMAL, 1, false,
|
||||
DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, ANTIALIASED_QUALITY, FF_DONTCARE,
|
||||
"Arial", &font);
|
||||
|
||||
font_api_proxy = com_proxy_wrap(font, sizeof(*font->lpVtbl));
|
||||
font_api_vtbl = font_api_proxy->vptr;
|
||||
|
||||
font = (ID3DXFont*) font_api_proxy;
|
||||
SetRect(&fRectangle, 20, 20, 640, 480);
|
||||
|
||||
if (hr != S_OK) {
|
||||
log_fatal("monitor check: Creating font failed");
|
||||
}
|
||||
|
||||
if (font == NULL) {
|
||||
log_fatal("monitor check: Loading font failed");
|
||||
}
|
||||
|
||||
const uint32_t max_iterations = 60 * 30;
|
||||
const uint32_t skip_frames = 60 * 1;
|
||||
|
||||
uint64_t accu_us = 0;
|
||||
|
||||
double result = 0;
|
||||
uint32_t iterations = 0;
|
||||
|
||||
char text_buffer[256];
|
||||
|
||||
while (iterations < max_iterations) {
|
||||
sprintf(text_buffer,
|
||||
"Monitor check...\n"
|
||||
"Elapsed iterations: %d/%d\n"
|
||||
"Refresh rate: %f",
|
||||
iterations + 1, max_iterations, result);
|
||||
|
||||
iterations++;
|
||||
uint64_t start = time_get_counter();
|
||||
|
||||
api_vtbl->Clear(pdev, 0, NULL, D3DCLEAR_TARGET,
|
||||
D3DCOLOR_XRGB(0, 0, 0), 0.0f, 0);
|
||||
api_vtbl->BeginScene(pdev);
|
||||
|
||||
font_api_vtbl->DrawTextA(font, NULL, text_buffer, -1, &fRectangle,
|
||||
DT_LEFT, D3DCOLOR_XRGB(0xFF, 0xFF, 0xFF));
|
||||
|
||||
api_vtbl->EndScene(pdev);
|
||||
api_vtbl->Present(pdev, NULL, NULL, NULL, NULL);
|
||||
|
||||
/* Skip the first inaccurate values */
|
||||
if (iterations > skip_frames) {
|
||||
accu_us += time_get_elapsed_us(time_get_counter() - start);
|
||||
|
||||
result = ((double) (iterations - skip_frames)) /
|
||||
(accu_us / 1000.0 / 1000.0);
|
||||
}
|
||||
}
|
||||
|
||||
log_info("Monitor check done (total iterations %d), refesh rate: %f hz",
|
||||
iterations, result);
|
||||
|
||||
/* Sanity check to ensure people notice that their current refresh rate
|
||||
is way off. */
|
||||
if (result < 55 || result > 65) {
|
||||
log_warning("Monitor check result (%f hz) is not even near the "
|
||||
"intended refresh rate of 60 hz. Fix your setup to ensure a "
|
||||
"constant and as close to as possible 60 hz refresh rate.", result);
|
||||
}
|
||||
|
||||
/* Leave results of monitor check on screen for a moment */
|
||||
Sleep(2000);
|
||||
|
||||
log_assert(d3d9_monitor_check_cb);
|
||||
|
||||
d3d9_monitor_check_cb(result);
|
||||
}
|
||||
|
||||
static void calc_win_size_with_framed(HWND hwnd, DWORD x, DWORD y, DWORD width,
|
||||
DWORD height, LPWINDOWPOS wp)
|
||||
{
|
||||
/* taken from dxwnd */
|
||||
RECT rect;
|
||||
DWORD style;
|
||||
int max_x, max_y;
|
||||
HMENU menu;
|
||||
|
||||
rect.left = x;
|
||||
rect.top = y;
|
||||
max_x = width;
|
||||
max_y = height;
|
||||
rect.right = x + max_x;
|
||||
rect.bottom = y + max_y;
|
||||
|
||||
style = GetWindowLong(hwnd, GWL_STYLE);
|
||||
menu = GetMenu(hwnd);
|
||||
AdjustWindowRect(&rect, style, (menu != NULL));
|
||||
|
||||
/* shift down-right so that the border is visible
|
||||
and also update the iPosX,iPosY upper-left coordinates
|
||||
of the client area */
|
||||
|
||||
if (rect.left < 0) {
|
||||
rect.right -= rect.left;
|
||||
rect.left = 0;
|
||||
}
|
||||
|
||||
if (rect.top < 0) {
|
||||
rect.bottom -= rect.top;
|
||||
rect.top = 0;
|
||||
}
|
||||
|
||||
wp->x = rect.left;
|
||||
wp->y = rect.top;
|
||||
wp->cx = rect.right - rect.left;
|
||||
wp->cy = rect.bottom - rect.top;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
#ifndef IIDXHOOK_D3D9_H
|
||||
#define IIDXHOOK_D3D9_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* Filter types for scaling back buffer, e.g. upscaling for higher resolution monitors.
|
||||
*/
|
||||
enum d3d9_back_buffer_scale_filter {
|
||||
D3D9_BACK_BUFFER_SCALE_FILTER_NONE = 0,
|
||||
D3D9_BACK_BUFFER_SCALE_FILTER_LINEAR = 1,
|
||||
D3D9_BACK_BUFFER_SCALE_FILTER_POINT = 2,
|
||||
};
|
||||
|
||||
/**
|
||||
* Callback function to receive resulting refresh rate of monitor check.
|
||||
*/
|
||||
typedef void (*d3d9_monitor_check_result_callback_t)(double refresh_rate);
|
||||
|
||||
/**
|
||||
* Hook some d3d9 functions to patch gfx related stuff
|
||||
* like enabling window mode.
|
||||
*
|
||||
* Games using d3d9:
|
||||
* - IIDX: Gold to Copula
|
||||
*/
|
||||
void d3d9_hook_init(void);
|
||||
|
||||
/**
|
||||
* Set the game to window mode.
|
||||
*
|
||||
* @param framed True to add a window frame and make the window
|
||||
* movable, resizable, minizable. False for no frame.
|
||||
* @param width Width of the window. -1 to keep original width.
|
||||
* @param height Height of the window. -1 to keep original height.
|
||||
*/
|
||||
void d3d9_set_windowed(bool framed, int32_t width, int32_t height);
|
||||
|
||||
/**
|
||||
* Patch the GPU device ID detection to allow running the game with
|
||||
* other GPUs than the ones it is locked to.
|
||||
*
|
||||
* @param vid Vendor ID to patch.
|
||||
* @param pid Product ID to patch.
|
||||
*/
|
||||
void d3d9_set_pci_id(uint16_t vid, uint16_t pid);
|
||||
|
||||
/**
|
||||
* Set a framerate limit for the rendering loop.
|
||||
*
|
||||
* Use this if the game won't sync up properly with vsync enabled.
|
||||
*
|
||||
* @limit Number of frames to limit the rendering loop to
|
||||
*/
|
||||
void d3d9_set_frame_rate_limit(float limit);
|
||||
|
||||
/**
|
||||
* Enable a software a "monitor check" which determines the (avg.) refresh rate
|
||||
* of the game. Ensure that your GPU is outputting a stable refresh rate to
|
||||
* your monitor. Otherwise, the resulting values fluctuate a lot
|
||||
*
|
||||
* @param cb Callback to a function that receives the result of the monitor
|
||||
* check once it completed.
|
||||
*/
|
||||
void d3d9_enable_monitor_check(d3d9_monitor_check_result_callback_t cb);
|
||||
|
||||
/**
|
||||
* Ported from d3d8 module to enable this when using d3d8to9.
|
||||
*
|
||||
* Fixes UV coordinates for background videos on
|
||||
* IIDX Red to DD. Use this if you experience the game
|
||||
* stretching the background videos
|
||||
* (see implementation for further details)
|
||||
*/
|
||||
void d3d9_iidx_fix_stretched_bg_videos(void);
|
||||
|
||||
/**
|
||||
* Ported from d3d8 module to enable this when using d3d8to9.
|
||||
*
|
||||
* Fixes the 3D background displayed during song select by disabling fog.
|
||||
* On some machines/GPUs the background is rendered completely white which
|
||||
* is caused by buggy fog rendering. Fog is used to hide the rings appearing
|
||||
* but that's quite 'far away' so disabling fog doesn't look bad.
|
||||
*/
|
||||
void d3d9_iidx_fix_12_song_select_bg(void);
|
||||
|
||||
/**
|
||||
* Ported from d3d8 module to enable this when using d3d8to9.
|
||||
*
|
||||
* Fixes the 3D background displayed during song select by disabling lighting.
|
||||
* On some machines/GPUs the background is rendered completely black which
|
||||
* is caused by buggy lighting. Disabling (dynamic) lighting sets the whole
|
||||
* scene to ambient lighting which displays everything properly.
|
||||
*/
|
||||
void d3d9_iidx_fix_13_song_select_bg(void);
|
||||
|
||||
/**
|
||||
* Fixes a parameter on CreateTexture calls causing the game to crash with
|
||||
* NVIDIA cards (on GOLD and possible newer)
|
||||
*/
|
||||
void d3d9_enable_nvidia_fix(void);
|
||||
|
||||
/**
|
||||
* Fixes UV coordinates for background videos on
|
||||
* (similar to the d3d8 games). But, this fixes the quad seam issue on 14 to
|
||||
* 17, only.
|
||||
*/
|
||||
void d3d9_bg_video_seams_fix(void);
|
||||
|
||||
/**
|
||||
* Scale the back buffer after a frame got rendered. This allows you to up-/downscale the final frame to display
|
||||
* arbitrary resolutions, e.g. 640x480 -> 1920x1080 to allow displaying the game in the monitor's native resolution.
|
||||
* This avoids over-/underscan (yes, that's still a thing with todays TVs) or leaving the upscaling to monitors/TVs
|
||||
* that are doing a terrible job regarding picture quality or latency wise.
|
||||
*
|
||||
* @param width Target width to scale the frame to. 0 disables scaling entirely.
|
||||
* @param height Target height to scale the frame to. 0 disables scaling entirely.
|
||||
* @param filter Filtering method to use for scaling. Depending on how you scale, this can have an impact on the final
|
||||
* image quality.
|
||||
*/
|
||||
void d3d9_scale_back_buffer(uint16_t width, uint16_t height, enum d3d9_back_buffer_scale_filter filter);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,190 @@
|
||||
#define LOG_MODULE "eamuse-hook"
|
||||
|
||||
#include <windows.h>
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "hook/table.h"
|
||||
|
||||
#include "iidxhook-util/eamuse.h"
|
||||
|
||||
#include "util/defs.h"
|
||||
#include "util/log.h"
|
||||
#include "util/str.h"
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
static unsigned long STDCALL my_inet_addr(const char *cp);
|
||||
static int STDCALL my_connect(SOCKET s, const struct sockaddr* addr,
|
||||
int addrlen);
|
||||
static struct hostent FAR* STDCALL my_gethostbyname(const char *nameB);
|
||||
|
||||
static unsigned long (STDCALL *real_inet_addr)(const char *cp);
|
||||
static int (STDCALL *real_connect)(SOCKET s, const struct sockaddr* addr,
|
||||
int addrlen);
|
||||
static struct hostent FAR* (STDCALL *real_gethostbyname)(const char *nameB);
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
static const struct hook_symbol eamuse_hook_syms[] = {
|
||||
/* WS2_32.DLL's SDK import lib generates ordinal imports, so these ordinals
|
||||
are a frozen aspect of the Win32 ABI. */
|
||||
|
||||
{
|
||||
.name = "inet_addr",
|
||||
.ordinal = 11,
|
||||
.patch = my_inet_addr,
|
||||
.link = (void **) &real_inet_addr
|
||||
},
|
||||
{
|
||||
.name = "connect",
|
||||
.ordinal = 4,
|
||||
.patch = my_connect,
|
||||
.link = (void **) &real_connect
|
||||
},
|
||||
{
|
||||
.name = "gethostbyname",
|
||||
.ordinal = 52,
|
||||
.patch = my_gethostbyname,
|
||||
.link = (void **) &real_gethostbyname
|
||||
},
|
||||
};
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
static struct net_addr eamuse_server_addr;
|
||||
static struct net_addr eamuse_server_addr_resolved;
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
static unsigned long STDCALL my_inet_addr(const char *cp)
|
||||
{
|
||||
char* tmp;
|
||||
|
||||
/* for a stock machine connected to the eamuse router,
|
||||
the game wants to connect to the standard domain
|
||||
services.konami.eamuse.fun
|
||||
depending on the router you got, it will be services.hostname.my.router
|
||||
so we catch that and turn it into any ip we want */
|
||||
|
||||
/* bugfix win10: don't just catch services.konami... because
|
||||
win10 is doing some weird stuff and this call also contains
|
||||
various IP addresses. Always return the server address */
|
||||
tmp = net_addr_to_str(&eamuse_server_addr_resolved);
|
||||
log_misc("my_inet_addr: '%s' -> %s", cp, tmp);
|
||||
free(tmp);
|
||||
|
||||
return eamuse_server_addr_resolved.ipv4.addr;
|
||||
}
|
||||
|
||||
static int STDCALL my_connect(SOCKET s, const struct sockaddr* addr, int addrlen)
|
||||
{
|
||||
char* tmp;
|
||||
|
||||
if (addr->sa_family == AF_INET) {
|
||||
/* check if we got our server's address */
|
||||
struct sockaddr_in* addr_in = (struct sockaddr_in*) addr;
|
||||
|
||||
if (addr_in->sin_addr.S_un.S_addr ==
|
||||
eamuse_server_addr_resolved.ipv4.addr) {
|
||||
tmp = net_addr_to_str(&eamuse_server_addr_resolved);
|
||||
log_misc("Patching port '%d' of %s to %d", ntohs(addr_in->sin_port),
|
||||
tmp, eamuse_server_addr_resolved.ipv4.port);
|
||||
free(tmp);
|
||||
|
||||
addr_in->sin_port = htons(eamuse_server_addr_resolved.ipv4.port);
|
||||
}
|
||||
}
|
||||
|
||||
return real_connect(s, addr, addrlen);
|
||||
}
|
||||
|
||||
static struct hostent FAR* STDCALL my_gethostbyname(const char* name)
|
||||
{
|
||||
char* tmp;
|
||||
|
||||
/* for doc, checkout the other detour of inetaddr above
|
||||
this call is used starting GOLD (not used on pre GOLD) */
|
||||
|
||||
/* bugfix win10: don't just catch services.konami... because
|
||||
win10 is doing some weird stuff and this call also contains
|
||||
various IP addresses. Always return the server address */
|
||||
tmp = net_addr_to_str(&eamuse_server_addr_resolved);
|
||||
log_misc("my_gethostbyname: '%s' to ip %s", name, tmp);
|
||||
free(tmp);
|
||||
|
||||
/* very ugly hack to get this working.
|
||||
don't bother with dynamic allocations and have stuff staticly alloc'd
|
||||
just populate what's touched by the game */
|
||||
static struct hostent ret;
|
||||
static uint32_t addr;
|
||||
static char* arr[2];
|
||||
static bool first = true;
|
||||
|
||||
if (first) {
|
||||
ret.h_length = 4;
|
||||
ret.h_addr_list = (char**) &arr;
|
||||
ret.h_addr_list[0] = (char*) &addr;
|
||||
ret.h_addr_list[1] = NULL;
|
||||
ret.h_aliases = NULL;
|
||||
ret.h_name = NULL;
|
||||
}
|
||||
|
||||
addr = eamuse_server_addr_resolved.ipv4.addr;
|
||||
|
||||
return &ret;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
void eamuse_hook_init(void)
|
||||
{
|
||||
hook_table_apply(
|
||||
NULL,
|
||||
"ws2_32.dll",
|
||||
eamuse_hook_syms,
|
||||
lengthof(eamuse_hook_syms));
|
||||
|
||||
log_info("Inserted eamuse hooks");
|
||||
}
|
||||
|
||||
void eamuse_set_addr(const struct net_addr* addr)
|
||||
{
|
||||
char* tmp_str;
|
||||
char* tmp_str2;
|
||||
|
||||
log_assert(addr);
|
||||
|
||||
memcpy(&eamuse_server_addr, addr, sizeof(struct net_addr));
|
||||
|
||||
tmp_str = net_addr_to_str(&eamuse_server_addr);
|
||||
|
||||
eamuse_server_addr_resolved.type = NET_ADDR_TYPE_IPV4;
|
||||
|
||||
if (!net_resolve_hostname_net_addr(&eamuse_server_addr,
|
||||
&eamuse_server_addr_resolved.ipv4)) {
|
||||
log_fatal("Failed to resolve eamuse server address: %s", tmp_str);
|
||||
free(tmp_str);
|
||||
return;
|
||||
}
|
||||
|
||||
tmp_str2 = net_addr_to_str(&eamuse_server_addr_resolved);
|
||||
|
||||
log_info("Target eamuse server %s resolved to %s", tmp_str, tmp_str2);
|
||||
|
||||
free(tmp_str);
|
||||
free(tmp_str2);
|
||||
}
|
||||
|
||||
void eamuse_check_connection()
|
||||
{
|
||||
if (!net_check_remote_connection(&eamuse_server_addr_resolved, 5000)) {
|
||||
log_info("Target eamuse server reachable.");
|
||||
} else {
|
||||
log_warning("Target eamuse server is not reachable. Game might throw "
|
||||
"network errors/warnings if eamuse is enabled.");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
#ifndef IIDXHOOK_EAMUSE_H
|
||||
#define IIDXHOOK_EAMUSE_H
|
||||
|
||||
#include "util/net.h"
|
||||
|
||||
/**
|
||||
* Hook various calls resolving the service address to connect to
|
||||
* the eamuse server for the old IIDX games (9th to Sirius)
|
||||
*/
|
||||
void eamuse_hook_init(void);
|
||||
|
||||
/**
|
||||
* Set a net_addr to a eamuse server for the game to connect to.
|
||||
*
|
||||
* @param addr net_addr struct with address to the server.
|
||||
*/
|
||||
void eamuse_set_addr(const struct net_addr* addr);
|
||||
|
||||
/**
|
||||
* Check if the target eamuse server is reachable. Reports success/error using
|
||||
* the logger.
|
||||
*/
|
||||
void eamuse_check_connection();
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,85 @@
|
||||
#define LOG_MODULE "effector-hook"
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "hook/table.h"
|
||||
|
||||
#include "iidxhook-util/effector.h"
|
||||
|
||||
#include "util/defs.h"
|
||||
#include "util/log.h"
|
||||
|
||||
static BOOL my_EnableEqualizer(int a1);
|
||||
static BOOL my_GetEqualizerStatus(LPVOID buffer);
|
||||
static BOOL my_SetEqualizerGain(int a1, int a2);
|
||||
static BOOL my_SetGlobalEnvironment(int a1);
|
||||
static BOOL my_SetSpeakerMode(int direct_sound_struct, int a2);
|
||||
|
||||
static const struct hook_symbol effector_hook_syms[] = {
|
||||
{
|
||||
.name = "EnableEqualizer",
|
||||
.patch = my_EnableEqualizer,
|
||||
},
|
||||
{
|
||||
.name = "GetEqualizerStatus",
|
||||
.patch = my_GetEqualizerStatus,
|
||||
},
|
||||
{
|
||||
.name = "SetEqualizerGain",
|
||||
.patch = my_SetEqualizerGain,
|
||||
},
|
||||
{
|
||||
.name = "SetGlobalEnvironment",
|
||||
.patch = my_SetGlobalEnvironment,
|
||||
},
|
||||
{
|
||||
.name = "SetSpeakerMode",
|
||||
.patch = my_SetSpeakerMode,
|
||||
},
|
||||
};
|
||||
|
||||
static BOOL my_EnableEqualizer(int a1)
|
||||
{
|
||||
log_misc("EnableEqualizer");
|
||||
// stub
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL my_GetEqualizerStatus(LPVOID buffer)
|
||||
{
|
||||
log_misc("GetEqualizerStatus");
|
||||
// stub
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL my_SetEqualizerGain(int a1, int a2)
|
||||
{
|
||||
log_misc("SetEqualizerGain");
|
||||
// stub
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL my_SetGlobalEnvironment(int a1)
|
||||
{
|
||||
log_misc("SetGlobalEnvironment");
|
||||
// stub
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL my_SetSpeakerMode(int direct_sound_struct, int a2)
|
||||
{
|
||||
log_misc("SetSpeakerMode");
|
||||
// stub
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void effector_hook_init(void)
|
||||
{
|
||||
hook_table_apply(
|
||||
NULL,
|
||||
"rteffect.dll",
|
||||
effector_hook_syms,
|
||||
lengthof(effector_hook_syms));
|
||||
|
||||
log_info("Inserted rteffect hooks");
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#ifndef IIDXHOOK_EFFECTOR_H
|
||||
#define IIDXHOOK_EFFECTOR_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
/**
|
||||
* Hook rteffects.dll exports (10th to DD)
|
||||
*/
|
||||
void effector_hook_init(void);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,161 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "hook/table.h"
|
||||
|
||||
#include "iidxhook-util/log-server.h"
|
||||
|
||||
#include "imports/avs.h"
|
||||
|
||||
#include "util/defs.h"
|
||||
#include "util/log.h"
|
||||
#include "util/str.h"
|
||||
#include "util/thread.h"
|
||||
|
||||
static int log_thread_proc(void *ctx);
|
||||
static void log_post(char level, const char *module, const char *fmt,
|
||||
va_list ap);
|
||||
static void log_post_misc(const char *module, const char *fmt, ...);
|
||||
static void log_post_info(const char *module, const char *fmt, ...);
|
||||
static void log_post_warning(const char *module, const char *fmt, ...);
|
||||
static void log_post_fatal(const char *module, const char *fmt, ...);
|
||||
|
||||
static int log_thread_id;
|
||||
static HANDLE log_rv_producer;
|
||||
static HANDLE log_rv_consumer;
|
||||
static char log_rv_level;
|
||||
static const char *log_rv_module;
|
||||
static char log_rv_buffer[8192];
|
||||
|
||||
void log_server_init(void)
|
||||
{
|
||||
HANDLE ready;
|
||||
|
||||
log_rv_producer = CreateSemaphore(NULL, 1, 1, NULL);
|
||||
log_rv_consumer = CreateSemaphore(NULL, 0, 1, NULL);
|
||||
ready = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
|
||||
log_to_external(log_post_misc, log_post_info, log_post_warning,
|
||||
log_post_fatal);
|
||||
|
||||
log_thread_id = avs_thread_create(log_thread_proc, ready, 16384, 0);
|
||||
|
||||
if (WaitForSingleObject(ready, INFINITE)) {
|
||||
// can't do any logging here, yet.
|
||||
fprintf(stderr, "ERROR log_server_init: WaitForSingleObject failed: %08x", (unsigned int) GetLastError());
|
||||
return;
|
||||
}
|
||||
|
||||
CloseHandle(ready);
|
||||
|
||||
log_misc("Started log server thread");
|
||||
}
|
||||
|
||||
static int log_thread_proc(void *ctx)
|
||||
{
|
||||
bool run;
|
||||
|
||||
SetEvent((HANDLE) ctx);
|
||||
|
||||
run = true;
|
||||
|
||||
log_body_misc(LOG_MODULE, "Log server thread is running");
|
||||
|
||||
while (run) {
|
||||
|
||||
if (WaitForSingleObject(log_rv_consumer, INFINITE)) {
|
||||
log_fatal("WaitForSingleObject failed: %08x",
|
||||
(unsigned int) GetLastError());
|
||||
}
|
||||
|
||||
switch (log_rv_level) {
|
||||
case '\0':
|
||||
run = false;
|
||||
|
||||
break;
|
||||
|
||||
case 'M':
|
||||
log_body_misc(log_rv_module, "%s", log_rv_buffer);
|
||||
|
||||
break;
|
||||
|
||||
case 'I':
|
||||
log_body_info(log_rv_module, "%s", log_rv_buffer);
|
||||
|
||||
break;
|
||||
|
||||
case 'W':
|
||||
log_body_warning(log_rv_module, "%s", log_rv_buffer);
|
||||
|
||||
break;
|
||||
|
||||
case 'F':
|
||||
log_body_fatal(log_rv_module, "%s", log_rv_buffer);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (!ReleaseSemaphore(log_rv_producer, 1, NULL)) {
|
||||
log_fatal("ReleaseSemaphore failed: %08x",
|
||||
(unsigned int) GetLastError());
|
||||
}
|
||||
}
|
||||
|
||||
log_body_misc(LOG_MODULE, "Log server thread is exiting");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void log_post(char level, const char *module, const char *fmt,
|
||||
va_list ap)
|
||||
{
|
||||
if (WaitForSingleObject(log_rv_producer, INFINITE)) {
|
||||
return;
|
||||
}
|
||||
|
||||
log_rv_level = level;
|
||||
log_rv_module = module;
|
||||
str_vformat(log_rv_buffer, sizeof(log_rv_buffer), fmt, ap);
|
||||
|
||||
ReleaseSemaphore(log_rv_consumer, 1, NULL);
|
||||
}
|
||||
|
||||
#define LOG_POST_IMPL(name, level) \
|
||||
static void name(const char *module, const char *fmt, ...) \
|
||||
{ \
|
||||
va_list ap; \
|
||||
\
|
||||
va_start(ap, fmt); \
|
||||
log_post(level, module, fmt, ap); \
|
||||
va_end(ap); \
|
||||
}
|
||||
|
||||
LOG_POST_IMPL(log_post_misc, 'M')
|
||||
LOG_POST_IMPL(log_post_info, 'I')
|
||||
LOG_POST_IMPL(log_post_warning, 'W')
|
||||
LOG_POST_IMPL(log_post_fatal, 'F')
|
||||
|
||||
void log_server_fini(void)
|
||||
{
|
||||
int thread_result;
|
||||
|
||||
log_misc("Stopping log server thread");
|
||||
|
||||
WaitForSingleObject(log_rv_producer, INFINITE);
|
||||
log_rv_level = '\0';
|
||||
ReleaseSemaphore(log_rv_consumer, 1, NULL);
|
||||
|
||||
avs_thread_join(log_thread_id, &thread_result);
|
||||
avs_thread_destroy(log_thread_id);
|
||||
|
||||
CloseHandle(log_rv_producer);
|
||||
CloseHandle(log_rv_consumer);
|
||||
|
||||
log_rv_producer = NULL;
|
||||
log_rv_consumer = NULL;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
#ifndef IIDXHOOK_LOG_SERVER_H
|
||||
#define IIDXHOOK_LOG_SERVER_H
|
||||
|
||||
/**
|
||||
* Initialize the log server which creates a dedicated thread and a log message queue.
|
||||
*
|
||||
* Log messages are posted to the queue by the application and the background thread writes them to the target output.
|
||||
* Required for newer iidx and pop'n games using the avs log api (e.g. iidxhook4 to 7) due to a quirk of the ezusb
|
||||
* driver. For a full breakdown about the logging on avs (and non avs) based konami games,
|
||||
* see doc/logging-breadkdown-avs.md.
|
||||
*/
|
||||
void log_server_init(void);
|
||||
|
||||
/**
|
||||
* Shut down the log server.
|
||||
*/
|
||||
void log_server_fini(void);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,107 @@
|
||||
#define LOG_MODULE "settings-hook"
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "hook/iohook.h"
|
||||
#include "hook/table.h"
|
||||
|
||||
#include "util/defs.h"
|
||||
#include "util/fs.h"
|
||||
#include "util/log.h"
|
||||
#include "util/str.h"
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
static BOOL WINAPI my_CreateDirectoryA(LPCSTR lpPathName,
|
||||
LPSECURITY_ATTRIBUTES lpSecurityAttributes);
|
||||
|
||||
static BOOL (WINAPI *real_CreateDirectoryA)(LPCSTR lpPathName,
|
||||
LPSECURITY_ATTRIBUTES lpSecurityAttributes);
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
static const struct hook_symbol settings_hook_syms[] = {
|
||||
{
|
||||
.name = "CreateDirectoryA",
|
||||
.patch = my_CreateDirectoryA,
|
||||
.link = (void **) &real_CreateDirectoryA
|
||||
},
|
||||
};
|
||||
|
||||
static bool settings_folders_checked;
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
BOOL WINAPI my_CreateDirectoryA(LPCSTR lpPathName,
|
||||
LPSECURITY_ATTRIBUTES lpSecurityAttributes)
|
||||
{
|
||||
if ( lpPathName != NULL &&
|
||||
(lpPathName[0] == 'd' || lpPathName[0] == 'e' || lpPathName[0] == 'f') &&
|
||||
lpPathName[1] == ':') {
|
||||
char new_path[MAX_PATH];
|
||||
|
||||
strcpy(new_path, lpPathName);
|
||||
new_path[1] = '\\';
|
||||
log_misc("(CreateDir) Remapped settings path %s", new_path);
|
||||
|
||||
return real_CreateDirectoryA(new_path, lpSecurityAttributes);
|
||||
}
|
||||
|
||||
return real_CreateDirectoryA(lpPathName, lpSecurityAttributes);
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
void settings_hook_init(void)
|
||||
{
|
||||
hook_table_apply(
|
||||
NULL,
|
||||
"kernel32.dll",
|
||||
settings_hook_syms,
|
||||
lengthof(settings_hook_syms));
|
||||
|
||||
log_info("Inserted settings hooks");
|
||||
}
|
||||
|
||||
HRESULT settings_hook_dispatch_irp(struct irp *irp)
|
||||
{
|
||||
if (irp->op == IRP_OP_OPEN &&
|
||||
(irp->open_filename[0] == L'd' || irp->open_filename[0] == L'e' ||
|
||||
irp->open_filename[0] == L'f') && irp->open_filename[1] == L':') {
|
||||
char* log_str;
|
||||
|
||||
((wchar_t*) irp->open_filename)[1] = L'\\';
|
||||
|
||||
wstr_narrow(irp->open_filename, &log_str);
|
||||
log_misc("Remapped settings path %s", log_str);
|
||||
free(log_str);
|
||||
|
||||
/* Create local settings folders if not available */
|
||||
if (!settings_folders_checked) {
|
||||
settings_folders_checked = true;
|
||||
|
||||
for (char c = 'd'; c <= 'f'; c++) {
|
||||
char str[3];
|
||||
|
||||
str[0] = c;
|
||||
str[1] = '\\';
|
||||
str[2] = '\0';
|
||||
|
||||
if (!path_exists(str)) {
|
||||
log_misc("Creating local settings folder %s\\", str);
|
||||
CreateDirectoryA(str, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return irp_invoke_next(irp);
|
||||
}
|
||||
|
||||
return irp_invoke_next(irp);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
#ifndef IIDXHOOK_SETTINGS_H
|
||||
#define IIDXHOOK_SETTINGS_H
|
||||
|
||||
#include "hook/iohook.h"
|
||||
|
||||
/**
|
||||
* Remaps the paths for the settings drives d:\, e:\ and f:\
|
||||
* to local folders e\ and f\.
|
||||
* Needed on IIDX 9th to Sirius.
|
||||
*/
|
||||
void settings_hook_init(void);
|
||||
|
||||
/**
|
||||
* iohook dispatch function
|
||||
*/
|
||||
HRESULT settings_hook_dispatch_irp(struct irp *irp);
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user