Bemanitools v5.26 release

This commit is contained in:
icex2
2019-09-27 22:36:50 +02:00
commit cbd7720349
718 changed files with 62731 additions and 0 deletions
+19
View File
@@ -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 \
+103
View File
@@ -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);
}
}
+32
View File
@@ -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
+536
View File
@@ -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);
}
}
+44
View File
@@ -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
+37
View File
@@ -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");
}
+11
View File
@@ -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
+129
View File
@@ -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);
}
}
+23
View File
@@ -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
+235
View File
@@ -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);
}
}
+28
View File
@@ -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
+45
View File
@@ -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);
}
}
+16
View File
@@ -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
+172
View File
@@ -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);
}
+19
View File
@@ -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
+600
View File
@@ -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;
}
+73
View File
@@ -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
+907
View File
@@ -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;
}
+123
View File
@@ -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
+190
View File
@@ -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.");
}
}
+25
View File
@@ -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
+85
View File
@@ -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");
}
+11
View File
@@ -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
+161
View File
@@ -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;
}
+19
View File
@@ -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
+107
View File
@@ -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);
}
+18
View File
@@ -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