Version 1.08 with public source code release.

This commit is contained in:
icex2
2020-10-03 20:56:55 +02:00
parent 656148121d
commit 762bf140c0
555 changed files with 55502 additions and 3 deletions
+250
View File
@@ -0,0 +1,250 @@
/**
* Hook library for Pump It Up: Exceed 2
*/
#define LOG_MODULE "x2hook"
#include <string.h>
#include "capnhook/hook/lib-main.h"
#include "capnhook/hooklib/redir.h"
#include "hook/core/piu-utils.h"
#include "hook/patch/gfx.h"
#include "hook/patch/hook-mon.h"
#include "hook/patch/main-loop.h"
#include "hook/patch/microdog34.h"
#include "hook/patch/piuio.h"
#include "hook/patch/piuio-exit.h"
#include "hook/patch/redir.h"
#include "hook/patch/sigsegv.h"
#include "hook/patch/sound.h"
#include "hook/patch/usb-emu.h"
#include "hook/patch/usb-init-fix.h"
#include "util/fs.h"
#include "util/glibc.h"
#include "util/patch.h"
#include "util/str.h"
#include "util/sys-info.h"
#include "options.h"
#include "unlock.h"
/* Compiled binary data from data folder. Symbols are defined by compiler */
extern const uint8_t _binary_x2hook_dog_key_start[];
extern const uint8_t _binary_x2hook_dog_key_end[];
static void x2hook_bootstrapping(int argc, char** argv, struct x2hook_options* options, char** game_data_path)
{
log_assert(argc >= 0);
log_assert(argv);
log_assert(options);
log_assert(game_data_path);
if (hook_core_piu_block_non_piu_process_recursion(argc, argv)) {
return;
}
hook_core_piu_log_init(argc, argv);
if (!x2hook_options_init(argc, argv, options)) {
exit(0);
}
hook_core_piu_log_info(argc, argv);
*game_data_path = hook_core_piu_utils_get_and_verify_game_data_path(argc, argv);
hook_core_piu_utils_verify_root_user();
log_info("Initializing, piu game data path: %s", *game_data_path);
}
static void x2hook_patch_fs_init(struct x2hook_options* options)
{
log_assert(options);
patch_hook_mon_init(
options->patch.hook_mon.io,
options->patch.hook_mon.file,
options->patch.hook_mon.fs,
options->patch.hook_mon.usb,
options->patch.hook_mon.open);
patch_redir_init();
}
static void x2hook_fs_redir_settings(struct x2hook_options* options)
{
log_assert(options);
if (options->game.settings) {
if (!util_fs_path_exists(options->game.settings)) {
log_info("Settings path %s does not exist, creating", options->game.settings);
if (!util_fs_mkdir(options->game.settings)) {
log_error("Creating directory %s failed", options->game.settings);
}
}
char* abs_path = util_fs_get_abs_path(options->game.settings);
cnh_redir_add("/SETTINGS", abs_path);
free(abs_path);
} else {
log_warn("No settings path specified");
}
}
static void x2hook_fs_redir_script(struct x2hook_options* options, const char* game_data_path)
{
log_assert(options);
log_assert(game_data_path);
char* path_tmp;
path_tmp = util_str_merge(game_data_path, "/SCRIPT");
cnh_redir_add("/SCRIPT", path_tmp);
free(path_tmp);
}
static void x2hook_fs_redir_usb()
{
/* Needed on newer kernels */
cnh_redir_add("/proc/bus/usb/devices", "/sys/kernel/debug/usb/devices");
}
static void x2hook_fs_redirs_init(struct x2hook_options* options, const char* game_data_path)
{
log_assert(options);
log_assert(game_data_path);
/* Expecting all assets in the piu game (asset) folder. redirects for files
that are not in there by default for reasons */
x2hook_fs_redir_settings(options);
x2hook_fs_redir_script(options, game_data_path);
x2hook_fs_redir_usb();
}
static void x2hook_patch_gfx_init(struct x2hook_options* options)
{
log_assert(options);
patch_gfx_init();
if (options->patch.gfx.windowed) {
patch_gfx_force_window_mode();
}
}
static void x2hook_patch_game_init(struct x2hook_options* options)
{
log_assert(options);
if (options->game.force_unlock) {
hook_x2_unlock_init();
}
/* return from StopAllEffects to avoid killing the sound thread */
log_warn("Applying memory patch only valid for version 102");
util_patch_write_memory_byte(0x08074CE0, 0xC3);
}
static void x2hook_patch_main_loop_init(struct x2hook_options* options)
{
log_assert(options);
patch_main_loop_init(true, options->patch.main_loop.disable_built_in_inputs, false);
if (options->patch.main_loop.x11_input_handler_api_lib) {
char* abs_path_x11_input_hook_lib = util_fs_get_abs_path(options->patch.main_loop.x11_input_handler_api_lib);
patch_main_loop_add_x11_input_handler(abs_path_x11_input_hook_lib);
free(abs_path_x11_input_hook_lib);
}
}
static void x2hook_patch_sound_init(struct x2hook_options* options)
{
log_assert(options);
patch_sound_init(options->patch.sound.device);
}
static void x2hook_patch_sigsegv_init(struct x2hook_options* options)
{
log_assert(options);
patch_sigsegv_init(options->patch.sigsegv.halt_on_segv);
}
static void x2hook_patch_dongle_init()
{
patch_microdog34_init((const uint8_t*) _binary_x2hook_dog_key_start,
((uintptr_t) &_binary_x2hook_dog_key_end - (uintptr_t) &_binary_x2hook_dog_key_start));
}
static void x2hook_patch_piuio_init(struct x2hook_options* options)
{
log_assert(options);
/* Hook before IO emulation */
if (options->patch.piuio.exit_test_serv) {
patch_piuio_exit_init();
}
patch_usb_init_fix_init();
patch_usb_emu_init();
if (options->patch.piuio.api_lib) {
char* abs_path_iolib = util_fs_get_abs_path(options->patch.piuio.api_lib);
patch_piuio_init(abs_path_iolib);
free(abs_path_iolib);
}
}
void x2hook_constructor(void)
{
/* Nothing here */
}
void x2hook_destructor(void)
{
/* Nothing here */
}
void x2hook_trap_before_main(int argc, char** argv)
{
log_assert(argc >= 0);
log_assert(argv);
char* game_data_path;
struct x2hook_options options;
x2hook_bootstrapping(argc, argv, &options, &game_data_path);
x2hook_patch_fs_init(&options);
x2hook_fs_redirs_init(&options, game_data_path);
x2hook_patch_gfx_init(&options);
x2hook_patch_game_init(&options);
x2hook_patch_main_loop_init(&options);
x2hook_patch_sound_init(&options);
x2hook_patch_sigsegv_init(&options);
x2hook_patch_dongle_init();
x2hook_patch_piuio_init(&options);
free(game_data_path);
log_info("Hooking finished");
}
void x2hook_trap_after_main(void)
{
patch_piuio_shutdown();
}
LIB_MAIN_CONSTRUCTOR(x2hook_constructor);
LIB_MAIN_DESTRUCTOR(x2hook_destructor);
LIB_MAIN_TRAP_MAIN(x2hook_trap_before_main, x2hook_trap_after_main);
+38
View File
@@ -0,0 +1,38 @@
/**
* Module to fix weird OpenGL error that only appeared on Exceed 2 thus far.
*
* If OpenGL is not forced into single threaded mode, the backend will run in
* multi-threaded mode for some reason and crash in __glDispatchCheckMultithreaded
* when the game calls glXChooseVisual in its GFX init function.
*
* The fix is quite simple (luckily) as this seems that Exceed 2 is not the only
* game that requires the environment variable __GL_SINGLETHREADED to be set.
*/
#include <stdio.h>
#include <string.h>
#include "capnhook/hook/lib.h"
typedef char* (*getenv_t) (const char* name);
static getenv_t _x2hook_ogl_fix_real_getenv;
char* getenv(const char* name)
{
if (!_x2hook_ogl_fix_real_getenv) {
_x2hook_ogl_fix_real_getenv = (getenv_t) cnh_lib_get_func_addr("getenv");
}
char* ret = _x2hook_ogl_fix_real_getenv(name);
if (!strcmp(name, "__GL_SINGLETHREADED")) {
if (ret == NULL || strcmp(name, "1") != 0) {
printf("!!! x2hook: __GL_SINGLETHREADED not set or enabled, force patching OpenGL to single threaded to "
"avoid crash !!!\n");
return "1";
}
}
return ret;
}
+183
View File
@@ -0,0 +1,183 @@
#include "hook/x2/options.h"
#include "util/options.h"
#define X2HOOK_OPTIONS_STR_GAME_FORCE_UNLOCK "game.force_unlock"
#define X2HOOK_OPTIONS_STR_GAME_SETTINGS "game.settings"
#define X2HOOK_OPTIONS_STR_PATCH_GFX_WINDOWED "patch.gfx.windowed"
#define X2HOOK_OPTIONS_STR_PATCH_HOOK_MON_FILE "patch.hook_mon.file"
#define X2HOOK_OPTIONS_STR_PATCH_HOOK_MON_FS "patch.hook_mon.fs"
#define X2HOOK_OPTIONS_STR_PATCH_HOOK_MON_IO "patch.hook_mon.io"
#define X2HOOK_OPTIONS_STR_PATCH_HOOK_MON_OPEN "patch.hook_mon.open"
#define X2HOOK_OPTIONS_STR_PATCH_HOOK_MON_USB "patch.hook_mon.usb"
#define X2HOOK_OPTIONS_STR_PATCH_HOOK_MAIN_LOOP_DISABLE_BUILT_IN_INPUTS "patch.hook_main_loop.disable_built_in_inputs"
#define X2HOOK_OPTIONS_STR_PATCH_HOOK_MAIN_LOOP_X11_INPUT_HANDLER "patch_hook_main_loop.x11_input_handler"
#define X2HOOK_OPTIONS_STR_PATCH_PIUIO_EMU_LIB "patch.piuio.emu_lib"
#define X2HOOK_OPTIONS_STR_PATCH_PIUIO_EXIT_TEST_SERV "patch.piuio_exit.test_serv"
#define X2HOOK_OPTIONS_STR_PATCH_SOUND_DEVICE "patch.sound.device"
#define X2HOOK_OPTIONS_STR_PATCH_SIGSEGV_HALT_ON_SEGV "patch.sigsegv.halt_on_segv"
#define X2HOOK_OPTIONS_STR_PATCH_UTIL_LOG_FILE "util.log.file"
#define X2HOOK_OPTIONS_STR_PATCH_UTIL_LOG_LEVEL "util.log.level"
static const struct util_options_def x2hook_options_def[] = {
{
.name = X2HOOK_OPTIONS_STR_GAME_FORCE_UNLOCK,
.description = "Force unlock the game. Modifies the current and any new PIUEXCEED.INI settings file permanently",
.param = 'z',
.type = UTIL_OPTIONS_TYPE_BOOL,
.default_value.b = false,
},
{
.name = X2HOOK_OPTIONS_STR_GAME_SETTINGS,
.description = "Path to game settings (SETTINGS) folder",
.param = 's',
.type = UTIL_OPTIONS_TYPE_STR,
.default_value.str = "./save",
},
{
.name = X2HOOK_OPTIONS_STR_PATCH_GFX_WINDOWED,
.description = "Force game into window mode",
.param = 'w',
.type = UTIL_OPTIONS_TYPE_BOOL,
.default_value.b = false,
},
{
.name = X2HOOK_OPTIONS_STR_PATCH_HOOK_MON_FILE,
.description = "Enable file call monitoring",
.param = 'f',
.type = UTIL_OPTIONS_TYPE_BOOL,
.default_value.b = false,
},
{
.name = X2HOOK_OPTIONS_STR_PATCH_HOOK_MON_FS,
.description = "Enable file system related call monitoring",
.param = 'g',
.type = UTIL_OPTIONS_TYPE_BOOL,
.default_value.b = false,
},
{
.name = X2HOOK_OPTIONS_STR_PATCH_HOOK_MON_IO,
.description = "Enable IO syscall monitoring",
.param = 'i',
.type = UTIL_OPTIONS_TYPE_BOOL,
.default_value.b = false,
},
{
.name = X2HOOK_OPTIONS_STR_PATCH_HOOK_MON_OPEN,
.description = "Enable any file/IO open call monitoring",
.param = 'j',
.type = UTIL_OPTIONS_TYPE_BOOL,
.default_value.b = false,
},
{
.name = X2HOOK_OPTIONS_STR_PATCH_HOOK_MON_USB,
.description = "Enable libusb call monitoring",
.param = 'u',
.type = UTIL_OPTIONS_TYPE_BOOL,
.default_value.b = false,
},
{
.name = X2HOOK_OPTIONS_STR_PATCH_HOOK_MAIN_LOOP_DISABLE_BUILT_IN_INPUTS,
.description = "Disable the built in keyboard inputs for test (F1), service (F2) and clear (F3)",
.param = 'k',
.type = UTIL_OPTIONS_TYPE_BOOL,
.default_value.b = false,
},
{
.name = X2HOOK_OPTIONS_STR_PATCH_HOOK_MAIN_LOOP_X11_INPUT_HANDLER,
.description = "Path to a library implementing the x11-input-handler api to capture X11 keyboard inputs",
.param = 'q',
.type = UTIL_OPTIONS_TYPE_STR,
.default_value.str = NULL,
},
{
.name = X2HOOK_OPTIONS_STR_PATCH_PIUIO_EMU_LIB,
.description = "Path to library implementing the piuio api for piuio emulation",
.param = 'p',
.type = UTIL_OPTIONS_TYPE_STR,
.default_value.str = NULL,
},
{
.name = X2HOOK_OPTIONS_STR_PATCH_PIUIO_EXIT_TEST_SERV,
.description = "Enable game exit on Test + Service",
.param = 'e',
.type = UTIL_OPTIONS_TYPE_BOOL,
.default_value.b = false,
},
{
.name = X2HOOK_OPTIONS_STR_PATCH_SOUND_DEVICE,
.description = "Select the sound device to open on snd_pcm_open",
.param = 'a',
.type = UTIL_OPTIONS_TYPE_STR,
.default_value.str = "dmix",
},
{
.name = X2HOOK_OPTIONS_STR_PATCH_SIGSEGV_HALT_ON_SEGV,
.description = "Halt on sigsegv to attach a debugger to the process",
.param = 'd',
.type = UTIL_OPTIONS_TYPE_BOOL,
.default_value.str = false,
},
{
.name = X2HOOK_OPTIONS_STR_PATCH_UTIL_LOG_FILE,
.description = "Print the log output to the specified file",
.param = 'o',
.type = UTIL_OPTIONS_TYPE_STR,
.default_value.str = "pumptools.log",
},
{
.name = X2HOOK_OPTIONS_STR_PATCH_UTIL_LOG_LEVEL,
.description = "Set the log level (0-4)",
.param = 'l',
.type = UTIL_OPTIONS_TYPE_INT,
.default_value.i = LOG_LEVEL_DEBUG,
},
};
static const struct util_options_defs x2hook_options_defs = {
.usage_header =
"Pumptools x2hook for Pump It Up: Exceed 2, build " __DATE__ " " __TIME__ ", gitrev " STRINGIFY(GITREV) "\n"
"Usage: LD_PRELOAD=./x2hook.so [game exec] [game data path] <options>",
.usage_param = 'h',
.defs = x2hook_options_def,
.ndefs = lengthof(x2hook_options_def)
};
bool x2hook_options_init(int argc, char** argv, struct x2hook_options* options)
{
log_assert(argv);
log_assert(options);
struct util_options_opts* options_opt;
util_options_init(argc, argv);
options_opt = util_options_get(&x2hook_options_defs);
if (!options_opt) {
return false;
}
options->game.force_unlock = util_options_get_bool(options_opt, X2HOOK_OPTIONS_STR_GAME_FORCE_UNLOCK);
options->game.settings = util_options_get_str(options_opt, X2HOOK_OPTIONS_STR_GAME_SETTINGS);
options->patch.gfx.windowed = util_options_get_bool(options_opt, X2HOOK_OPTIONS_STR_PATCH_GFX_WINDOWED);
options->patch.hook_mon.file = util_options_get_bool(options_opt, X2HOOK_OPTIONS_STR_PATCH_HOOK_MON_FILE);
options->patch.hook_mon.fs = util_options_get_bool(options_opt, X2HOOK_OPTIONS_STR_PATCH_HOOK_MON_FS);
options->patch.hook_mon.io = util_options_get_bool(options_opt, X2HOOK_OPTIONS_STR_PATCH_HOOK_MON_IO);
options->patch.hook_mon.open = util_options_get_bool(options_opt, X2HOOK_OPTIONS_STR_PATCH_HOOK_MON_OPEN);
options->patch.hook_mon.usb = util_options_get_bool(options_opt, X2HOOK_OPTIONS_STR_PATCH_HOOK_MON_USB);
options->patch.main_loop.disable_built_in_inputs = util_options_get_bool(options_opt,
X2HOOK_OPTIONS_STR_PATCH_HOOK_MAIN_LOOP_DISABLE_BUILT_IN_INPUTS);
options->patch.main_loop.x11_input_handler_api_lib =
util_options_get_str(options_opt, X2HOOK_OPTIONS_STR_PATCH_HOOK_MAIN_LOOP_X11_INPUT_HANDLER);
options->patch.piuio.api_lib = util_options_get_str(options_opt, X2HOOK_OPTIONS_STR_PATCH_PIUIO_EMU_LIB);
options->patch.piuio.exit_test_serv =
util_options_get_bool(options_opt, X2HOOK_OPTIONS_STR_PATCH_PIUIO_EXIT_TEST_SERV);
options->patch.sound.device = util_options_get_str(options_opt, X2HOOK_OPTIONS_STR_PATCH_SOUND_DEVICE);
options->patch.sigsegv.halt_on_segv =
util_options_get_bool(options_opt, X2HOOK_OPTIONS_STR_PATCH_SIGSEGV_HALT_ON_SEGV);
options->log.file = util_options_get_str(options_opt, X2HOOK_OPTIONS_STR_PATCH_UTIL_LOG_FILE);
options->log.level =
(enum util_log_level) util_options_get_int(options_opt, X2HOOK_OPTIONS_STR_PATCH_UTIL_LOG_LEVEL);
return true;
}
+55
View File
@@ -0,0 +1,55 @@
#ifndef X2HOOK_OPTIONS_H
#define X2HOOK_OPTIONS_H
#include <stdbool.h>
#include <stdint.h>
#include "util/log.h"
struct x2hook_options {
struct game {
bool force_unlock;
const char* settings;
} game;
struct patch {
struct gfx {
bool windowed;
} gfx;
struct hook_mon {
bool file;
bool fs;
bool io;
bool open;
bool usb;
} hook_mon;
struct main_loop {
bool disable_built_in_inputs;
const char* x11_input_handler_api_lib;
} main_loop;
struct piuio {
const char* api_lib;
bool exit_test_serv;
} piuio;
struct sound {
const char* device;
} sound;
struct sigsegv {
bool halt_on_segv;
} sigsegv;
} patch;
struct log {
const char* file;
enum util_log_level level;
} log;
};
bool x2hook_options_init(int argc, char** argv, struct x2hook_options* options);
#endif
+81
View File
@@ -0,0 +1,81 @@
#define LOG_MODULE "hook-x2-unlock"
#include <assert.h>
#include <string.h>
#include "asset/x2/lib/settings.h"
#include "capnhook/hook/filehook.h"
#include "util/log.h"
#include "util/str.h"
static FILE* _hook_x2_unlock_settings_file = NULL;
static enum cnh_result _hook_x2_unlock_filehook(struct cnh_filehook_irp *irp);
void hook_x2_unlock_init(void)
{
cnh_filehook_push_handler(_hook_x2_unlock_filehook);
log_info("Initialized");
}
static enum cnh_result _hook_x2_unlock_filehook(struct cnh_filehook_irp *irp)
{
enum cnh_result result;
assert(irp);
if (irp->op != CNH_FILEHOOK_IRP_OP_OPEN && irp->file != _hook_x2_unlock_settings_file) {
return cnh_filehook_invoke_next(irp);
}
switch (irp->op) {
case CNH_FILEHOOK_IRP_OP_OPEN:
{
/* Let it open the real file */
result = cnh_filehook_invoke_next(irp);
if (util_str_ends_with(irp->open_filename, ASSET_X2_SETTINGS_FILE_NAME)) {
if (result == CNH_RESULT_SUCCESS) {
log_debug("Intercepting settings file open");
_hook_x2_unlock_settings_file = irp->file;
}
}
break;
}
case CNH_FILEHOOK_IRP_OP_READ:
{
/* Let it read the real file */
result = cnh_filehook_invoke_next(irp);
if (result == CNH_RESULT_SUCCESS) {
struct asset_x2_settings* settings = (struct asset_x2_settings*) irp->read.bytes;
asset_x2_settings_unlock_all(settings);
log_debug("Applied unlock patch to loaded settings (buffer size %d)", irp->read.pos);
}
break;
}
case CNH_FILEHOOK_IRP_OP_CLOSE:
{
_hook_x2_unlock_settings_file = NULL;
result = cnh_filehook_invoke_next(irp);
break;
}
default:
result = cnh_filehook_invoke_next(irp);
break;
}
return result;
}
+12
View File
@@ -0,0 +1,12 @@
/**
* Patch module to force unlock all permanently unlockable songs and additional difficulties in the game.
*/
#ifndef HOOK_X2_UNLOCK_H
#define HOOK_X2_UNLOCK_H
/**
* Initialize the patch module force unlocking all locked game content.
*/
void hook_x2_unlock_init(void);
#endif
BIN
View File
Binary file not shown.