diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..9a45936 --- /dev/null +++ b/Makefile @@ -0,0 +1,275 @@ +# vim: noexpandtab sts=8 sw=8 ts=8 + +# +# Overridable variables +# + +export SHELL := /bin/bash +BUILDDIR ?= build + +# +# Internal variables +# + +export TZ := /usr/share/zoneinfo/Japan + +ifeq ($(USE_CCACHE),yes) +ccache := ccache +else +ccache := +endif + +depdir := $(BUILDDIR)/dep +objdir := $(BUILDDIR)/obj +bindir := $(BUILDDIR)/bin + +toolchain_32 := i686-w64-mingw32- +toolchain_64 := x86_64-w64-mingw32- + +cppflags := -I. \ + -DNDEBUG \ + -D_NEED_FULLVERSION_INFO=1 -D_SECURE_SCL=1 \ + -D_CRT_SECURE_NO_WARNINGS=1 -D_NO_CRT_STDIO_INLINE=1 \ + -D_WINSOCK_DEPRECATED_NO_WARNINGS=1 \ + -DFKG_FORCED_USAGE=1 -DOFFICIAL_BUILD=1 -DBETA=1 -DDEVL=1 + +com_wflags := -Wall -Werror -Wpointer-arith -Wreturn-type \ + -Wwrite-strings -Wswitch -Wcast-align -Wchar-subscripts \ + -Wredundant-decls -Wunreachable-code -Wno-pedantic \ + -Wshadow -Winline -Wno-cast-qual -Wno-multichar \ + -fstrict-aliasing -Wno-unused-function \ + -Warray-bounds=2 -Wno-redundant-decls + +wflags := $(com_wflags) -Wno-strict-prototypes -Wnested-externs \ + -Wno-discarded-qualifiers +wxxflags := $(com_wflags) -Wno-old-style-cast + +cflags := -O3 -pipe -ffunction-sections -fdata-sections \ + -std=gnu11 $(wflags) +cxxflags := -O3 -pipe -ffunction-sections -fdata-sections \ + -std=c++11 $(wxxflags) + +ldflags := -Wl,--gc-sections -static -static-libgcc -lstdc++ \ + -fdiagnostics-color -Werror \ + -Wl,--gc-keep-exported \ + -Wl,--enable-auto-image-base \ + -Wl,--exclude-all-symbols \ + -Wl,--dynamicbase \ + -Wl,--nxcompat \ + -Wl,-s + +# +# The first target that GNU Make encounters becomes the default target. +# Define our ultimate target (`all') here, and also some helpers +# + +all: + +.PHONY: clean + +clean: + rm -rf $(BUILDDIR) + +# +# Pull in module definitions +# + +deps := + +dlls := +exes := +imps := +libs := + +avsdlls := +avsexes := + +include Module.mk + +modules := $(dlls) $(exes) $(libs) $(avsdlls) $(avsexes) + +# +# $1: Bitness +# $2: AVS2 minor version +# $3: Module +# + +optflags_64 += -mfpmath=sse -march=x86-64 \ + -mtune=generic -mabi=ms -malign-data=cacheline \ + -minline-stringops-dynamically -funswitch-loops \ + -funroll-loops -fschedule-insns2 -fsched-pressure \ + -fprefetch-loop-arrays --param prefetch-latency=300 \ + -fsel-sched-pipelining -fselective-scheduling \ + -ftree-vectorize -fbranch-target-load-optimize \ + -flive-range-shrinkage -falign-functions=16 \ + -flto -fno-use-linker-plugin -masm=intel + +optflags_32 += -mfpmath=sse -march=pentium-m -mtune=generic \ + -mabi=ms -malign-data=cacheline \ + -minline-stringops-dynamically -funswitch-loops \ + -funroll-loops -fschedule-insns2 -fsched-pressure \ + -fprefetch-loop-arrays --param prefetch-latency=300 \ + -fsel-sched-pipelining -fselective-scheduling \ + -ftree-vectorize -fbranch-target-load-optimize \ + -flive-range-shrinkage -falign-functions=16 \ + -flto -fno-use-linker-plugin -masm=intel + +cflags_32 := $(optflags_32) +cxxflags_32 := $(optflags_32) +cflags_64 := $(optflags_64) +cxxflags_64 := $(optflags_64) + +define t_moddefs + +cppflags_$3 += $(cppflags) -DBUILD_MODULE=$3 +cflags_$3 += $(cflags) +cxxflags_$3 += $(cxxflags) +ldflags_$3 += $(ldflags) +srcdir_$3 := $3 + +endef + +$(eval $(foreach module,$(modules),$(call t_moddefs,_,_,$(module)))) + +############################################################################## + +define t_bitness + +subdir_$1_indep := indep-$1 +bindir_$1_indep := $(bindir)/$$(subdir_$1_indep) + +$$(bindir_$1_indep): + mkdir -p $$@ + +$$(eval $$(foreach imp,$(imps),$$(call t_import,$1,indep,$$(imp)))) +$$(eval $$(foreach dll,$(dlls),$$(call t_linkdll,$1,indep,$$(dll)))) +$$(eval $$(foreach exe,$(exes),$$(call t_linkexe,$1,indep,$$(exe)))) +$$(eval $$(foreach lib,$(libs),$$(call t_archive,$1,indep,$$(lib)))) + +$$(eval $$(foreach avsver,$$(avsvers_$1),$$(call t_avsver,$1,$$(avsver)))) + +endef + +############################################################################## + +define t_avsver + +subdir_$1_$2 := avs2_$2-$1 +bindir_$1_$2 := $(bindir)/$$(subdir_$1_$2) + +$$(bindir_$1_$2): + mkdir -p $$@ + +$$(eval $$(foreach imp,$(imps),$$(call t_import,$1,$2,$$(imp)))) +$$(eval $$(foreach dll,$(avsdlls),$$(call t_linkdll,$1,$2,$$(dll)))) +$$(eval $$(foreach exe,$(avsexes),$$(call t_linkexe,$1,$2,$$(exe)))) + +endef + +############################################################################## + +define t_compile + +depdir_$1_$2_$3 := $(depdir)/$$(subdir_$1_$2)/$3 +abslib_$1_$2_$3 := $$(libs_$3:%=$$(bindir_$1_indep)/lib%.a) +absdpl_$1_$2_$3 := $$(deplibs_$3:%=$$(bindir_$1_$2)/lib%.a) +objdir_$1_$2_$3 := $(objdir)/$$(subdir_$1_$2)/$3 +obj_$1_$2_$3 := $$(src_$3:%.c=$$(objdir_$1_$2_$3)/%.o) \ + $$(rc_$3:%.rc=$$(objdir_$1_$2_$3)/%_rc.o) \ + $$(srcpp_$3:%.cc=$$(objdir_$1_$2_$3)/%.o) + +deps += $$(src_$3:%.c=$$(depdir_$1_$2_$3)/%.d) \ + $$(srcpp_$3:%.cc=$$(depdir_$1_$2_$3)/%.d) + +$$(depdir_$1_$2_$3): + mkdir -p $$@ + +$$(objdir_$1_$2_$3): + mkdir -p $$@ + +$$(objdir_$1_$2_$3)/%.o: $$(srcdir_$3)/%.c \ + | $$(depdir_$1_$2_$3) $$(objdir_$1_$2_$3) + $(ccache) $$(toolchain_$1)gcc $$(cflags_$3) $$(cflags_$1) $$(cppflags_$3) \ + -MMD -MF $$(depdir_$1_$2_$3)/$$*.d -MT $$@ -MP \ + -DAVS_VERSION=$2 -c -o $$@ $$< + +$$(objdir_$1_$2_$3)/%.o: $$(srcdir_$3)/%.cc \ + | $$(depdir_$1_$2_$3) $$(objdir_$1_$2_$3) + $(ccache) $$(toolchain_$1)g++ $$(cxxflags_$3) $$(cxxflags_$1) $$(cppflags_$3) \ + -MMD -MF $$(depdir_$1_$2_$3)/$$*.d -MT $$@ -MP \ + -DAVS_VERSION=$2 -c -o $$@ $$< + +$$(objdir_$1_$2_$3)/%_rc.o: $$(srcdir_$3)/%.rc \ + | $$(depdir_$1_$2_$3) $$(objdir_$1_$2_$3) + $$(toolchain_$1)windres $$(cppflags_$3) $$< $$@ + +endef + +############################################################################## + +define t_archive + +$(t_compile) + +$$(bindir_$1_$2)/lib$3.a: $$(obj_$1_$2_$3) | $$(bindir_$1_$2) + $$(toolchain_$1)gcc-ar r $$@ $$^ 2> /dev/null + $$(toolchain_$1)gcc-ranlib $$@ + +endef + +############################################################################## + +define t_linkdll + +$(t_compile) + +dll_$1_$2_$3 := $$(bindir_$1_$2)/$3.dll +implib_$1_$2_$3 := $$(bindir_$1_$2)/lib$3.a + +$$(dll_$1_$2_$3) $$(implib_$1_$2_$3): $$(obj_$1_$2_$3) $$(abslib_$1_$2_$3) \ + $$(absdpl_$1_$2_$3) \ + $$(srcdir_$3)/$3.def | $$(bindir_$1_$2) + $(ccache) $$(toolchain_$1)gcc -shared $$(srcdir_$3)/$3.def \ + -o $$(dll_$1_$2_$3) -Wl,--out-implib,$$(implib_$1_$2_$3) \ + $$^ $$(ldflags_$3) $(optflags_$1) + $$(toolchain_$1)strip -s $$(dll_$1_$2_$3) + $$(toolchain_$1)ranlib $$(implib_$1_$2_$3) + +endef + +############################################################################## + +define t_linkexe + +$(t_compile) + +exe_$1_$2_$3 := $$(bindir_$1_$2)/$3.exe + +$$(exe_$1_$2_$3): $$(obj_$1_$2_$3) $$(abslib_$1_$2_$3) $$(absdpl_$1_$2_$3) \ + | $$(bindir_$1_$2) + $(ccache) $$(toolchain_$1)gcc -o $$@ $$^ $$(ldflags_$3) $(optflags_$1) + $$(toolchain_$1)strip -s $$@ + +endef + +############################################################################## + +define t_import + +impdef_$1_$2_$3 ?= imports/import_$1_$2_$3.def + +$$(bindir_$1_$2)/lib$3.a: $$(impdef_$1_$2_$3) | $$(bindir_$1_$2) + $$(toolchain_$1)dlltool -l $$@ -d $$< + +endef + +############################################################################## + +$(eval $(foreach bitness,32 64,$(call t_bitness,$(bitness)))) + +# +# Pull in GCC-generated dependency files +# + +-include $(deps) + diff --git a/Module.mk b/Module.mk new file mode 100644 index 0000000..c4baf77 --- /dev/null +++ b/Module.mk @@ -0,0 +1,34 @@ +cflags += -DWIN32_LEAN_AND_MEAN -DCOBJMACROS -Ipkcs11 -Wno-attributes + +avsvers_32 := 1700 1508 +avsvers_64 := 1700 1509 + +imps += avs avs-ea3 + +include util/Module.mk +include minhook/Module.mk +include popnhax/Module.mk + +# +# Distribution build rules +# + +zipdir := $(BUILDDIR)/zip + +$(zipdir)/: + mkdir -p $@ + +$(BUILDDIR)/popnhax.zip: \ + build/bin/avs2_1508-32/popnhax.dll \ + dist/popnhax/popnhax.xml \ + | $(zipdir)/ + echo ... $@ + zip -j $@ $^ + +$(BUILDDIR)/bemanihax.zip: \ + $(BUILDDIR)/popnhax.zip \ + | $(zipdir)/ + echo ... $@ + zip -9 -q -j $@ $^ + +all: $(BUILDDIR)/bemanihax.zip diff --git a/README.md b/README.md index 436fb8b..17fbcfe 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,11 @@ +[![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/donate?hosted_button_id=WT735CX4UMZ9U) + # popnhax -popnhax + +Arcade game patcher. + +Based on [bemanihax](https://github.com/windyfairy/bemanihax) whose an updated version was included with omnimix v1 + +### Build Instructions + +Should be working out of the box with MSYS2/MinGW32. Just run `make`. diff --git a/clang-format.py b/clang-format.py new file mode 100644 index 0000000..97a7db3 --- /dev/null +++ b/clang-format.py @@ -0,0 +1,30 @@ +import os +import subprocess + +ignore_folders = [".git", "build", "dist", "minhook"] +cpp_extensions = (".cpp", ".cxx", ".c++", ".h++", ".hpp", ".hxx") + +for root, dirs, files in os.walk(".", topdown=True): + dirs[:] = [d for d in dirs if d not in ignore_folders] + + for filename in files: + if filename.endswith(cpp_extensions): + print(filename) + subprocess.check_call(["clang-format", "-i", "-style=file", root + "/" + filename]) + # print(" ".join(["clang-tidy", root + "/" + filename, "--", "-I."])) + subprocess.check_call(["./clang-tidy.sh", root + "/" + filename, "c++11"]) + print() + + +cpp_extensions = (".cc", ".cp", ".c", ".i", ".ii", ".h") + +for root, dirs, files in os.walk(".", topdown=True): + dirs[:] = [d for d in dirs if d not in ignore_folders] + + for filename in files: + if filename.endswith(cpp_extensions): + print(filename) + subprocess.check_call(["clang-format", "-i", "-style=file", root + "/" + filename]) + # print(" ".join(["clang-tidy", root + "/" + filename, "--", "-I."])) + subprocess.check_call(["./clang-tidy.sh", root + "/" + filename, "c11"]) + print() diff --git a/clang-tidy.sh b/clang-tidy.sh new file mode 100644 index 0000000..f0558b1 --- /dev/null +++ b/clang-tidy.sh @@ -0,0 +1,36 @@ +#!/bin/bash +# Run clang-tidy on a source file +TOOLCHAIN_PREFIX=i686-w64-mingw32 + +CLANG_TIDY=clang-tidy +SYSROOT=$($TOOLCHAIN_PREFIX-gcc -print-sysroot)/mingw + +if [ ! -f $SYSROOT/include/windows.h ]; then + SYSROOT=/usr/i686-w64-mingw32 + if [ ! -f $SYSROOT/include/windows.h ]; then + SYSROOT=/usr/local/i686-w64-mingw32 + fi +fi + +CPP_HEADERS="/usr/share/mingw-w64/include/" + +# echo "System root: $SYSROOT" +# echo "C++ headers: $CPP_HEADERS" + +$CLANG_TIDY $1 \ + -- \ + -target i686-w64-mingw32 \ + -std=$2 \ + --sysroot $SYSROOT \ + -isysroot $SYSROOT \ + -I $CPP_HEADERS \ + -I. \ + -Iinclude \ + -Dssize_t=int \ + -DAVS_VERSION=1700 \ + -D_CRT_SECURE_NO_WARNINGS=1 \ + -Di386=1 \ + -D__i386=1 \ + -D__i386__=1 \ + -D_WIN32=1 \ + -DCOBJMACROS diff --git a/dist/popnhax/popnhax.xml b/dist/popnhax/popnhax.xml new file mode 100644 index 0000000..8f27cad --- /dev/null +++ b/dist/popnhax/popnhax.xml @@ -0,0 +1,26 @@ + + + + 1 + + 0 + + 1 + + 0 + + 0 + + 1 + + + 1 + + 0 + + 0 + + 1 + + + diff --git a/imports/avs-ea3.h b/imports/avs-ea3.h new file mode 100644 index 0000000..e12589a --- /dev/null +++ b/imports/avs-ea3.h @@ -0,0 +1,92 @@ +#ifndef IMPORTS_AVS_EA3_H +#define IMPORTS_AVS_EA3_H + +#ifdef __cplusplus +extern "C" { +#endif + +#if !defined(AVS_VERSION) + +#error "Can't build AVS-dependent project using AVS-independent make rules" + +#elif AVS_VERSION == 1509 + +#define ea3_xrpc_apply XE592acd000057 +#define ea3_xrpc_module_register XE592acd000060 +#define ea3_xrpc_new XE592acd000052 +#define ea3_xrpc_destroy XE592acd000007 +#define ea3_boot XE592acd00008c +#define ea3_shutdown XE592acd00005a + +#else + +#error AVS obfuscated import macros have not been declared for this version + +#endif + +#define XRPC_STATUS_SERVER_FAULT_ERROR (-17) +#define XRPC_STATUS_SERVER_RESPONSE_ERROR (-18) + +void ea3_boot(struct property_node *conf); +void ea3_shutdown(void); + +struct xrpc_handle; +struct xrpc_server_handle; + +struct xrpc_status { + int16_t status; + int16_t subcode; + int16_t status_code; + int16_t fault_code; +} __attribute__((gcc_struct, packed)); + +struct xrpc_arg_list { + const char *name; + const char *property_path; + bool omittable; +} __attribute__((gcc_struct, packed)); + +/* + * The following defines the codes used inside the 'status@' attribute + * used in a xrpc response. + */ + +enum ea3_general_status_codes { + XRPC_OK = 0, /* No error */ +}; + +enum xrpc_method_types { + HTTPAC_HTTP10 = 0x1, +}; + +struct xrpc_method { + const char *xrpc_meth_name; + bool(__cdecl *xrpc_cb_init)(void *shmem, char *buffer); + bool(__cdecl *xrpc_cb_sender)(void *shmem, struct property_node *node); + bool(__cdecl *xrpc_cb_receiver)(void *shmem, struct property_node *node); + char crypt_level; + char padding_00; + bool use_xrpc11; + bool use_esign; + bool use_ssl; + char compress_type; + char method_type; + char padding_01; + struct xrpc_arg_list *arg_list; +} __attribute__((gcc_struct, packed)); + +typedef int (*xrpc_apply_exit_callback_t)(void *buffer, struct xrpc_status status, void *param); + +int ea3_xrpc_apply(struct xrpc_handle *handle, const char *name, void *shmem, + xrpc_apply_exit_callback_t cbexit, void *cbexit_data, ...); + +struct xrpc_handle *ea3_xrpc_new(size_t sz_xrpc_buf, const char *xrpc_encoding, uint32_t flags); +void ea3_xrpc_destroy(struct xrpc_handle *handle); +int ea3_xrpc_module_register(const char *xrpc_endpoint, const char *services_url, uint32_t flags, + struct xrpc_method *method_array); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/imports/avs.h b/imports/avs.h new file mode 100644 index 0000000..5a93c94 --- /dev/null +++ b/imports/avs.h @@ -0,0 +1,188 @@ +#ifndef IMPORTS_AVS_H +#define IMPORTS_AVS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +#include + +#if !defined(AVS_VERSION) + +#error "Can't build AVS-dependent project using AVS-independent make rules" + +#elif AVS_VERSION == 1508 || AVS_VERSION == 1509 + +#define avs_thread_delay XCd229cc00012b +#define property_search XCd229cc00012e +#define boot XCd229cc0000aa +#define shutdown XCd229cc00001d +#define property_desc_to_buffer XCd229cc0000fd +#define property_destroy XCd229cc00013c +#define property_read_query_memsize XCd229cc0000ff +#define property_create XCd229cc000126 +#define property_insert_read XCd229cc00009a +#define property_node_create XCd229cc00002c +#define property_node_remove XCd229cc000028 +#define property_node_refer XCd229cc000009 +#define std_setenv XCd229cc000094 +#define avs_fs_open XCd229cc000090 +#define avs_fs_copy XCd229cc0000eb +#define avs_fs_close XCd229cc00011f +#define avs_fs_dump_mountpoint XCd229cc0000e9 +#define avs_fs_mount XCd229cc0000ce +#define avs_fs_fstat XCd229cc0000c3 +#define avs_fs_lstat XCd229cc0000c0 +#define avs_fs_lseek XCd229cc00004d +#define avs_fs_read XCd229cc00010d +#define avs_fs_opendir XCd229cc0000f0 +#define avs_fs_readdir XCd229cc0000bb +#define avs_fs_closedir XCd229cc0000b8 +#define cstream_create XCd229cc000141 +#define cstream_operate XCd229cc00008c +#define cstream_finish XCd229cc000025 +#define cstream_destroy XCd229cc0000e3 +#define property_node_read XCd229cc0000f3 +#define property_node_write XCd229cc00002d +#define property_file_write XCd229cc000052 +#define property_node_traversal XCd229cc000046 +#define property_psmap_export XCd229cc000006 +#define property_psmap_import XCd229cc000005 +#define property_node_name XCd229cc000049 +#define property_node_get_desc XCd229cc000165 +#define property_get_error XCd229cc0000b5 +#define property_node_clone XCd229cc00010a +#define property_query_size XCd229cc000032 +#define property_node_query_stat XCd229cc0000b1 +#define property_node_datasize XCd229cc000083 +#define property_mem_write XCd229cc000033 +#define property_part_write XCd229cc000024 +#define property_node_absolute_path XCd229cc00007c +#define property_node_has XCd229cc00008a +#define property_node_is_array XCd229cc000142 +#define property_node_type XCd229cc000071 +#define property_get_attribute_bool XCd229cc000043 +#define property_node_get_attribute_bool XCd229cc000110 +#define property_node_get_attribute_u32 XCd229cc0000db +#define property_node_get_attribute_s32 XCd229cc00011a +#define property_node_rename XCd229cc0000af +#define property_query_freesize XCd229cc000144 +#define property_clear_error XCd229cc00014b +#define property_lookup_encode XCd229cc0000fc +#define property_unlock_flag XCd229cc000145 +#define property_lock_flag XCd229cc000121 +#define property_set_flag XCd229cc000035 +#define property_part_write_meta XCd229cc00004f +#define property_part_write_meta2 XCd229cc000107 +#define property_read_data XCd229cc0000de +#define property_read_meta XCd229cc00010e +#define property_get_attribute_u32 XCd229cc000148 +#define property_get_attribute_s32 XCd229cc00005f +#define property_get_fingerprint XCd229cc000057 +#define property_node_refdata XCd229cc00009f +#define property_insert_read_with_filename XCd229cc0000cd +#define property_mem_read XCd229cc000039 +#define property_read_query_memsize_long XCd229cc00002b +#define property_clear XCd229cc0000c2 +#define avs_net_add_protocol XCd229cc000156 +#define avs_net_del_protocol XCd229cc00000f +#define avs_net_addrinfobyaddr XCd229cc000040 +#define avs_net_socket XCd229cc000026 +#define avs_net_setsockopt XCd229cc000092 +#define avs_net_getsockopt XCd229cc000084 +#define avs_net_connect XCd229cc000038 +#define avs_net_send XCd229cc00011d +#define avs_net_recv XCd229cc000131 +#define avs_net_pollfds_add XCd229cc00004b +#define avs_net_pollfds_get XCd229cc000105 +#define avs_net_bind XCd229cc00007e +#define avs_net_close XCd229cc00009c +#define avs_net_shutdown XCd229cc0000ac +#define avs_net_get_peername XCd229cc000085 +#define avs_net_get_sockname XCd229cc0000b0 + +#elif AVS_VERSION == 1700 + +#define property_create XCgsqzn0000090 +#define property_insert_read XCgsqzn0000094 +#define property_read_query_memsize XCgsqzn00000b0 +#define property_psmap_import XCgsqzn00000b2 + +#else + +#error AVS obfuscated import macros have not been declared for this version + +#endif + +enum property_type { + PROPERTY_TYPE_VOID = 1, + PROPERTY_TYPE_S8 = 2, + PROPERTY_TYPE_U8 = 3, + PROPERTY_TYPE_S16 = 4, + PROPERTY_TYPE_U16 = 5, + PROPERTY_TYPE_S32 = 6, + PROPERTY_TYPE_U32 = 7, + PROPERTY_TYPE_S64 = 8, + PROPERTY_TYPE_U64 = 9, + PROPERTY_TYPE_BIN = 10, + PROPERTY_TYPE_STR = 11, + PROPERTY_TYPE_FLOAT = 14, + PROPERTY_TYPE_ATTR = 46, + PROPERTY_TYPE_BOOL = 52, +}; + +enum property_node_traversal { + TRAVERSE_PARENT = 0, + TRAVERSE_FIRST_CHILD = 1, + TRAVERSE_FIRST_ATTR = 2, + TRAVERSE_FIRST_SIBLING = 3, + TRAVERSE_NEXT_SIBLING = 4, + TRAVERSE_PREVIOUS_SIBLING = 5, + TRAVERSE_LAST_SIBLING = 6, + TRAVERSE_NEXT_SEARCH_RESULT = 7, + TRAVERSE_PREV_SEARCH_RESULT = 8, +}; + +struct property; +struct property_node; +struct property_psmap; + +typedef int (*avs_reader_t)(uint32_t context, void *bytes, size_t nbytes); + +uint32_t property_read_query_memsize(avs_reader_t reader, uint32_t context, int *nodes, int *total); +struct property *property_create(int flags, void *buffer, uint32_t buffer_size); +int property_insert_read(struct property *prop, struct property_node *node, avs_reader_t reader, + uint32_t context); +int property_psmap_import(struct property *prop, struct property_node *root, void *dest, + const struct property_psmap *psmap); +struct property_node *property_node_create(struct property *prop, struct property_node *parent, + int type, const char *key, ...); +struct property_node *property_search( + struct property *prop, struct property_node *root, const char *path); + +int property_mem_write(struct property *prop, void *bytes, int nbytes); +void *property_desc_to_buffer(struct property *prop); +void property_file_write(struct property *prop, const char *path); +int property_set_flag(struct property *prop, int flags, int mask); +void property_destroy(struct property *prop); + +int property_node_refer( + struct property *prop, + struct property_node *node, + const char *name, + enum property_type type, + void *bytes, + uint32_t nbytes); +struct property_node *property_node_traversal( + struct property_node *node, enum property_node_traversal direction); +void avs_thread_delay(size_t ms, int zero); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/imports/import_32_0_avs.def b/imports/import_32_0_avs.def new file mode 100644 index 0000000..cb2ea9d --- /dev/null +++ b/imports/import_32_0_avs.def @@ -0,0 +1,36 @@ +LIBRARY libavs-win32 + +EXPORTS + avs_boot + avs_net_ctrl + avs_shutdown + avs_thread_create + avs_thread_destroy + avs_thread_exit + avs_thread_join + log_body_fatal + log_body_info + log_body_misc + log_body_warning + log_boot + log_change_level + property_create + property_desc_to_buffer + property_destroy + property_file_write + property_insert_read + property_mem_write + property_read_query_memsize + property_search + property_set_flag + property_node_clone + property_node_create + property_node_name + property_node_refer + property_node_remove + property_node_type + property_node_traversal + property_node_refdata + std_getenv + std_setenv + diff --git a/imports/import_32_1101_avs.def b/imports/import_32_1101_avs.def new file mode 100644 index 0000000..1b63626 --- /dev/null +++ b/imports/import_32_1101_avs.def @@ -0,0 +1,28 @@ +LIBRARY libavs-win32 + +EXPORTS + avs_boot @22 NONAME + avs_net_ctrl @107 NONAME + avs_shutdown @140 NONAME + avs_thread_create @156 NONAME + avs_thread_destroy @158 NONAME + avs_thread_exit @159 NONAME + avs_thread_join @161 NONAME + log_assert_body @196 NONAME + log_body_misc @199 NONAME + log_body_info @198 NONAME + log_body_warning @200 NONAME + log_body_fatal @197 NONAME + property_create @245 NONAME + property_desc_to_buffer @246 NONAME + property_destroy @247 NONAME + property_insert_read @255 NONAME + property_node_create @266 NONAME + property_node_refer @278 NONAME + property_node_remove @279 NONAME + property_psmap_import @288 NONAME + property_psmap_export @287 NONAME + property_read_query_memsize @291 NONAME + property_search @294 NONAME + std_getenv @308 NONAME + std_setenv @322 NONAME diff --git a/imports/import_32_1304_avs.def b/imports/import_32_1304_avs.def new file mode 100644 index 0000000..5a98e5a --- /dev/null +++ b/imports/import_32_1304_avs.def @@ -0,0 +1,27 @@ +LIBRARY libavs-win32 + +EXPORTS + avs_boot @237 NONAME + avs_net_ctrl @15 NONAME + avs_shutdown @333 NONAME + avs_thread_create @183 NONAME + avs_thread_destroy @76 NONAME + avs_thread_exit @147 NONAME + avs_thread_join @92 NONAME + log_body_misc @44 NONAME + log_body_info @339 NONAME + log_body_warning @219 NONAME + log_body_fatal @128 NONAME + property_create @256 NONAME + property_desc_to_buffer @201 NONAME + property_destroy @264 NONAME + property_insert_read @23 NONAME + property_node_create @316 NONAME + property_node_refer @268 NONAME + property_node_remove @129 NONAME + property_psmap_import @102 NONAME + property_psmap_export @110 NONAME + property_read_query_memsize @100 NONAME + property_search @244 NONAME + std_getenv @226 NONAME + std_setenv @114 NONAME diff --git a/imports/import_32_1403_avs.def b/imports/import_32_1403_avs.def new file mode 100644 index 0000000..32d68b1 --- /dev/null +++ b/imports/import_32_1403_avs.def @@ -0,0 +1,24 @@ +LIBRARY libavs-win32 + +EXPORTS + avs_boot @298 NONAME + avs_net_ctrl @100 NONAME + avs_shutdown @299 NONAME + avs_thread_create @6 NONAME + avs_thread_destroy @8 NONAME + avs_thread_join @13 NONAME + log_body_info @363 NONAME + log_body_misc @364 NONAME + log_body_warning @362 NONAME + log_body_fatal @365 NONAME + property_create @129 NONAME + property_desc_to_buffer @131 NONAME + property_destroy @130 NONAME + property_insert_read @133 NONAME + property_node_remove @148 NONAME + property_psmap_import @163 NONAME + property_psmap_export @164 NONAME + property_read_query_memsize @161 NONAME + property_search @146 NONAME + std_getenv @208 NONAME + std_setenv @209 NONAME diff --git a/imports/import_32_1508_avs.def b/imports/import_32_1508_avs.def new file mode 100644 index 0000000..2406cb0 --- /dev/null +++ b/imports/import_32_1508_avs.def @@ -0,0 +1,91 @@ +LIBRARY libavs-win32 + +EXPORTS + XCd229cc00012e + XCd229cc0000aa + XCd229cc00001d + XCd229cc0000fd + XCd229cc00013c + XCd229cc0000ff + XCd229cc000126 + XCd229cc00009a + XCd229cc00002c + XCd229cc000028 + XCd229cc000009 + XCd229cc000094 + XCd229cc000090 + XCd229cc0000eb + XCd229cc00011f + XCd229cc0000e9 + XCd229cc0000ce + XCd229cc0000c3 + XCd229cc0000c0 + XCd229cc00004d + XCd229cc00010d + XCd229cc0000f0 + XCd229cc0000bb + XCd229cc0000b8 + XCd229cc000141 + XCd229cc00008c + XCd229cc000025 + XCd229cc0000e3 + XCd229cc0000f3 + XCd229cc00002d + XCd229cc000052 + XCd229cc000046 + XCd229cc000006 + XCd229cc000005 + XCd229cc000049 + XCd229cc000165 + XCd229cc0000b5 + XCd229cc00010a + XCd229cc000032 + XCd229cc0000b1 + XCd229cc000083 + XCd229cc000033 + XCd229cc000024 + XCd229cc00007c + XCd229cc00008a + XCd229cc000142 + XCd229cc000071 + XCd229cc000043 + XCd229cc000110 + XCd229cc0000db + XCd229cc00011a + XCd229cc0000af + XCd229cc000144 + XCd229cc00014b + XCd229cc0000fc + XCd229cc000145 + XCd229cc000121 + XCd229cc000035 + XCd229cc00004f + XCd229cc000107 + XCd229cc0000de + XCd229cc00010e + XCd229cc000148 + XCd229cc00005f + XCd229cc000057 + XCd229cc00009f + XCd229cc0000cd + XCd229cc000039 + XCd229cc00002b + XCd229cc0000c2 + XCd229cc000156 + XCd229cc00000f + XCd229cc000040 + XCd229cc000026 + XCd229cc000092 + XCd229cc000084 + XCd229cc000038 + XCd229cc00011d + XCd229cc000131 + XCd229cc00004b + XCd229cc000105 + XCd229cc00007e + XCd229cc00009c + XCd229cc0000ac + XCd229cc000085 + XCd229cc0000b0 + + diff --git a/imports/import_32_1601_avs.def b/imports/import_32_1601_avs.def new file mode 100644 index 0000000..e260a8c --- /dev/null +++ b/imports/import_32_1601_avs.def @@ -0,0 +1,28 @@ +LIBRARY libavs-win32 + +EXPORTS + avs_thread_create @5 NONAME + avs_thread_destroy @7 NONAME + avs_thread_exit @11 NONAME + avs_thread_join @12 NONAME + avs_net_ctrl @98 NONAME + property_create @124 NONAME + property_destroy @125 NONAME + property_desc_to_buffer @126 NONAME + property_insert_read @128 NONAME + property_search @141 NONAME + property_node_create @142 NONAME + property_node_remove @143 NONAME + property_node_refer @155 NONAME + property_read_query_memsize @156 NONAME + property_psmap_export @159 NONAME + property_psmap_import @158 NONAME + std_getenv @204 NONAME + std_setenv @205 NONAME + avs_boot @283 NONAME + avs_shutdown @284 NONAME + log_body_fatal @361 NONAME + log_body_warning @362 NONAME + log_body_info @363 NONAME + log_body_misc @364 NONAME + diff --git a/imports/import_32_1603_avs.def b/imports/import_32_1603_avs.def new file mode 100644 index 0000000..98f5fe7 --- /dev/null +++ b/imports/import_32_1603_avs.def @@ -0,0 +1,27 @@ +LIBRARY libavs-win32 + +EXPORTS + avs_thread_create @5 NONAME + avs_thread_destroy @7 NONAME + avs_thread_exit @11 NONAME + avs_thread_join @12 NONAME + avs_net_ctrl @119 NONAME + property_create @145 NONAME + property_destroy @146 NONAME + property_desc_to_buffer @147 NONAME + property_insert_read @149 NONAME + property_search @162 NONAME + property_node_create @163 NONAME + property_node_remove @164 NONAME + property_node_refer @176 NONAME + property_read_query_memsize @177 NONAME + property_psmap_import @179 NONAME + property_psmap_export @180 NONAME + std_getenv @212 NONAME + std_setenv @213 NONAME + avs_boot @298 NONAME + avs_shutdown @299 NONAME + log_body_fatal @379 NONAME + log_body_warning @380 NONAME + log_body_info @381 NONAME + log_body_misc @382 NONAME diff --git a/imports/import_32_1700_avs.def b/imports/import_32_1700_avs.def new file mode 100644 index 0000000..18fefc8 --- /dev/null +++ b/imports/import_32_1700_avs.def @@ -0,0 +1,53 @@ +LIBRARY avs2-core + +EXPORTS + XCgsqzn0000007 + XCgsqzn0000004 + XCgsqzn0000006 + XCgsqzn000000a + XCgsqzn000000b + XCgsqzn0000076 + XCgsqzn0000090 + XCgsqzn0000091 + XCgsqzn0000092 + XCgsqzn0000094 + XCgsqzn00000a1 + XCgsqzn00000a2 + XCgsqzn00000a3 + XCgsqzn00000af + XCgsqzn00000b0 + XCgsqzn00000b2 + XCgsqzn00000b3 + XCgsqzn00000d3 + XCgsqzn00000d4 + XCgsqzn0000129 + XCgsqzn000012a + XCgsqzn000017a + XCgsqzn000017b + XCgsqzn000017c + XCgsqzn000017d + XCgsqzn000004e + XCgsqzn0000055 + XCgsqzn0000065 + XCgsqzn0000068 + XCgsqzn00000d5 + XCgsqzn0000051 + XCgsqzn00000a6 + XCgsqzn0000144 + XCgsqzn0000077 + XCgsqzn0000079 + XCgsqzn000007a + XCgsqzn000007b + XCgsqzn000007c + XCgsqzn000007d + XCgsqzn000007e + XCgsqzn000007f + XCgsqzn0000080 + XCgsqzn0000081 + XCgsqzn0000082 + XCgsqzn0000086 + XCgsqzn000008c + XCgsqzn000008d + XCgsqzn000008e + XCgsqzn000008f + XCgsqzn0000078 diff --git a/imports/import_32_803_avs.def b/imports/import_32_803_avs.def new file mode 100644 index 0000000..a7f59b5 --- /dev/null +++ b/imports/import_32_803_avs.def @@ -0,0 +1,37 @@ +LIBRARY libavs-win32 + +EXPORTS + avs_boot + avs_net_ctrl + avs_shutdown + avs_thread_create + avs_thread_destroy + avs_thread_exit + avs_thread_join + log_body_fatal + log_body_info + log_body_misc + log_body_warning + log_boot + log_change_level + property_create + property_desc_to_buffer + property_destroy + property_file_write + property_insert_read + property_mem_write + property_read_query_memsize + property_search + property_set_flag + property_node_clone + property_node_create + property_node_datasize + property_node_name + property_node_refer + property_node_remove + property_node_type + property_node_traversal + property_node_refdata + std_getenv + std_setenv + diff --git a/imports/import_64_1509_avs-ea3.def b/imports/import_64_1509_avs-ea3.def new file mode 100644 index 0000000..67a9653 --- /dev/null +++ b/imports/import_64_1509_avs-ea3.def @@ -0,0 +1,142 @@ +LIBRARY libavs-win64-ea3 + +EXPORTS + XE592acd000000 + XE592acd000001 + XE592acd000002 + XE592acd000003 + XE592acd000004 + XE592acd000005 + XE592acd000006 + XE592acd000007 + XE592acd000008 + XE592acd000009 + XE592acd00000a + XE592acd00000b + XE592acd00000c + XE592acd00000d + XE592acd000010 + XE592acd000011 + XE592acd000012 + XE592acd000013 + XE592acd000014 + XE592acd000015 + XE592acd000016 + XE592acd000017 + XE592acd000019 + XE592acd00001a + XE592acd00001b + XE592acd00001c + XE592acd00001d + XE592acd00001e + XE592acd00001f + XE592acd000020 + XE592acd000021 + XE592acd000022 + XE592acd000023 + XE592acd000024 + XE592acd000025 + XE592acd000026 + XE592acd000027 + XE592acd000028 + XE592acd000029 + XE592acd00002a + XE592acd00002b + XE592acd00002c + XE592acd00002d + XE592acd00002e + XE592acd00002f + XE592acd000030 + XE592acd000031 + XE592acd000032 + XE592acd000033 + XE592acd000034 + XE592acd000035 + XE592acd000036 + XE592acd000037 + XE592acd000038 + XE592acd00003a + XE592acd00003b + XE592acd00003c + XE592acd00003d + XE592acd00003e + XE592acd00003f + XE592acd000040 + XE592acd000041 + XE592acd000042 + XE592acd000043 + XE592acd000044 + XE592acd000046 + XE592acd000047 + XE592acd000048 + XE592acd000049 + XE592acd00004a + XE592acd00004b + XE592acd00004c + XE592acd00004d + XE592acd00004f + XE592acd000050 + XE592acd000051 + XE592acd000052 + XE592acd000053 + XE592acd000054 + XE592acd000055 + XE592acd000056 + XE592acd000057 + XE592acd000058 + XE592acd000059 + XE592acd00005a + XE592acd00005c + XE592acd00005d + XE592acd00005e + XE592acd00005f + XE592acd000060 + XE592acd000061 + XE592acd000062 + XE592acd000063 + XE592acd000064 + XE592acd000065 + XE592acd000066 + XE592acd000067 + XE592acd000068 + XE592acd000069 + XE592acd00006a + XE592acd00006b + XE592acd00006c + XE592acd00006d + XE592acd00006e + XE592acd00006f + XE592acd000070 + XE592acd000071 + XE592acd000072 + XE592acd000073 + XE592acd000074 + XE592acd000075 + XE592acd000076 + XE592acd000077 + XE592acd000078 + XE592acd000079 + XE592acd00007a + XE592acd00007b + XE592acd00007c + XE592acd00007d + XE592acd00007e + XE592acd00007f + XE592acd000080 + XE592acd000081 + XE592acd000082 + XE592acd000083 + XE592acd000084 + XE592acd000085 + XE592acd000086 + XE592acd000087 + XE592acd000088 + XE592acd000089 + XE592acd00008b + XE592acd00008c + XE592acd00008d + XE592acd00008e + XE592acd00008f + XE592acd000090 + XE592acd000091 + XE592acd000092 diff --git a/imports/import_64_1509_avs.def b/imports/import_64_1509_avs.def new file mode 100644 index 0000000..62ca959 --- /dev/null +++ b/imports/import_64_1509_avs.def @@ -0,0 +1,365 @@ +LIBRARY libavs-win64 + +EXPORTS + XCd229cc000000 + XCd229cc000001 + XCd229cc000002 + XCd229cc000003 + XCd229cc000004 + XCd229cc000005 + XCd229cc000006 + XCd229cc000007 + XCd229cc000009 + XCd229cc00000a + XCd229cc00000b + XCd229cc00000c + XCd229cc00000d + XCd229cc00000e + XCd229cc00000f + XCd229cc000010 + XCd229cc000011 + XCd229cc000012 + XCd229cc000013 + XCd229cc000014 + XCd229cc000015 + XCd229cc000016 + XCd229cc000017 + XCd229cc000018 + XCd229cc000019 + XCd229cc00001a + XCd229cc00001b + XCd229cc00001d + XCd229cc00001e + XCd229cc00001f + XCd229cc000020 + XCd229cc000021 + XCd229cc000022 + XCd229cc000023 + XCd229cc000024 + XCd229cc000025 + XCd229cc000026 + XCd229cc000027 + XCd229cc000028 + XCd229cc000029 + XCd229cc00002a + XCd229cc00002b + XCd229cc00002c + XCd229cc00002d + XCd229cc00002e + XCd229cc00002f + XCd229cc000030 + XCd229cc000031 + XCd229cc000032 + XCd229cc000033 + XCd229cc000034 + XCd229cc000035 + XCd229cc000036 + XCd229cc000037 + XCd229cc000038 + XCd229cc000039 + XCd229cc00003a + XCd229cc00003b + XCd229cc00003c + XCd229cc00003d + XCd229cc00003e + XCd229cc00003f + XCd229cc000040 + XCd229cc000041 + XCd229cc000042 + XCd229cc000043 + XCd229cc000044 + XCd229cc000045 + XCd229cc000046 + XCd229cc000047 + XCd229cc000048 + XCd229cc000049 + XCd229cc00004a + XCd229cc00004b + XCd229cc00004c + XCd229cc00004d + XCd229cc00004e + XCd229cc00004f + XCd229cc000050 + XCd229cc000051 + XCd229cc000052 + XCd229cc000053 + XCd229cc000054 + XCd229cc000055 + XCd229cc000056 + XCd229cc000057 + XCd229cc000058 + XCd229cc000059 + XCd229cc00005a + XCd229cc00005b + XCd229cc00005c + XCd229cc00005d + XCd229cc00005e + XCd229cc00005f + XCd229cc000060 + XCd229cc000061 + XCd229cc000062 + XCd229cc000063 + XCd229cc000064 + XCd229cc000065 + XCd229cc000066 + XCd229cc000067 + XCd229cc000068 + XCd229cc000069 + XCd229cc00006a + XCd229cc00006b + XCd229cc00006c + XCd229cc00006d + XCd229cc00006e + XCd229cc00006f + XCd229cc000070 + XCd229cc000071 + XCd229cc000072 + XCd229cc000073 + XCd229cc000074 + XCd229cc000075 + XCd229cc000076 + XCd229cc000077 + XCd229cc000078 + XCd229cc000079 + XCd229cc00007a + XCd229cc00007b + XCd229cc00007c + XCd229cc00007d + XCd229cc00007e + XCd229cc00007f + XCd229cc000080 + XCd229cc000081 + XCd229cc000082 + XCd229cc000083 + XCd229cc000084 + XCd229cc000085 + XCd229cc000086 + XCd229cc000087 + XCd229cc000088 + XCd229cc000089 + XCd229cc00008a + XCd229cc00008b + XCd229cc00008c + XCd229cc00008d + XCd229cc00008e + XCd229cc00008f + XCd229cc000090 + XCd229cc000091 + XCd229cc000092 + XCd229cc000093 + XCd229cc000094 + XCd229cc000095 + XCd229cc000096 + XCd229cc000097 + XCd229cc000098 + XCd229cc000099 + XCd229cc00009a + XCd229cc00009b + XCd229cc00009c + XCd229cc00009d + XCd229cc00009e + XCd229cc00009f + XCd229cc0000a0 + XCd229cc0000a1 + XCd229cc0000a2 + XCd229cc0000a3 + XCd229cc0000a4 + XCd229cc0000a5 + XCd229cc0000a6 + XCd229cc0000a7 + XCd229cc0000a8 + XCd229cc0000a9 + XCd229cc0000aa + XCd229cc0000ab + XCd229cc0000ac + XCd229cc0000ad + XCd229cc0000ae + XCd229cc0000af + XCd229cc0000b0 + XCd229cc0000b1 + XCd229cc0000b2 + XCd229cc0000b3 + XCd229cc0000b4 + XCd229cc0000b5 + XCd229cc0000b6 + XCd229cc0000b7 + XCd229cc0000b8 + XCd229cc0000b9 + XCd229cc0000ba + XCd229cc0000bb + XCd229cc0000bc + XCd229cc0000bd + XCd229cc0000be + XCd229cc0000bf + XCd229cc0000c0 + XCd229cc0000c1 + XCd229cc0000c2 + XCd229cc0000c3 + XCd229cc0000c4 + XCd229cc0000c5 + XCd229cc0000c6 + XCd229cc0000c7 + XCd229cc0000c8 + XCd229cc0000c9 + XCd229cc0000ca + XCd229cc0000cb + XCd229cc0000cc + XCd229cc0000cd + XCd229cc0000ce + XCd229cc0000cf + XCd229cc0000d0 + XCd229cc0000d1 + XCd229cc0000d2 + XCd229cc0000d3 + XCd229cc0000d4 + XCd229cc0000d5 + XCd229cc0000d6 + XCd229cc0000d7 + XCd229cc0000d8 + XCd229cc0000d9 + XCd229cc0000da + XCd229cc0000db + XCd229cc0000dc + XCd229cc0000dd + XCd229cc0000de + XCd229cc0000df + XCd229cc0000e0 + XCd229cc0000e1 + XCd229cc0000e2 + XCd229cc0000e3 + XCd229cc0000e4 + XCd229cc0000e5 + XCd229cc0000e6 + XCd229cc0000e7 + XCd229cc0000e8 + XCd229cc0000e9 + XCd229cc0000ea + XCd229cc0000eb + XCd229cc0000ec + XCd229cc0000ed + XCd229cc0000ee + XCd229cc0000ef + XCd229cc0000f0 + XCd229cc0000f1 + XCd229cc0000f2 + XCd229cc0000f3 + XCd229cc0000f4 + XCd229cc0000f6 + XCd229cc0000f7 + XCd229cc0000f8 + XCd229cc0000f9 + XCd229cc0000fa + XCd229cc0000fb + XCd229cc0000fc + XCd229cc0000fd + XCd229cc0000fe + XCd229cc0000ff + XCd229cc000100 + XCd229cc000102 + XCd229cc000103 + XCd229cc000104 + XCd229cc000105 + XCd229cc000106 + XCd229cc000107 + XCd229cc000108 + XCd229cc000109 + XCd229cc00010a + XCd229cc00010b + XCd229cc00010c + XCd229cc00010d + XCd229cc00010e + XCd229cc00010f + XCd229cc000110 + XCd229cc000111 + XCd229cc000112 + XCd229cc000113 + XCd229cc000114 + XCd229cc000115 + XCd229cc000116 + XCd229cc000117 + XCd229cc000118 + XCd229cc000119 + XCd229cc00011a + XCd229cc00011b + XCd229cc00011c + XCd229cc00011d + XCd229cc00011e + XCd229cc00011f + XCd229cc000120 + XCd229cc000121 + XCd229cc000122 + XCd229cc000123 + XCd229cc000124 + XCd229cc000125 + XCd229cc000126 + XCd229cc000127 + XCd229cc000128 + XCd229cc000129 + XCd229cc00012a + XCd229cc00012b + XCd229cc00012c + XCd229cc00012d + XCd229cc00012e + XCd229cc00012f + XCd229cc000130 + XCd229cc000131 + XCd229cc000132 + XCd229cc000133 + XCd229cc000134 + XCd229cc000135 + XCd229cc000136 + XCd229cc000137 + XCd229cc000138 + XCd229cc000139 + XCd229cc00013a + XCd229cc00013b + XCd229cc00013c + XCd229cc00013d + XCd229cc00013e + XCd229cc00013f + XCd229cc000140 + XCd229cc000141 + XCd229cc000142 + XCd229cc000143 + XCd229cc000144 + XCd229cc000145 + XCd229cc000146 + XCd229cc000147 + XCd229cc000148 + XCd229cc000149 + XCd229cc00014b + XCd229cc00014c + XCd229cc00014d + XCd229cc00014e + XCd229cc00014f + XCd229cc000150 + XCd229cc000151 + XCd229cc000152 + XCd229cc000153 + XCd229cc000154 + XCd229cc000155 + XCd229cc000156 + XCd229cc000157 + XCd229cc000158 + XCd229cc000159 + XCd229cc00015a + XCd229cc00015b + XCd229cc00015c + XCd229cc00015d + XCd229cc00015e + XCd229cc00015f + XCd229cc000160 + XCd229cc000161 + XCd229cc000162 + XCd229cc000163 + XCd229cc000164 + XCd229cc000165 + XCd229cc000166 + XCd229cc000167 + XCd229cc000168 + XCd229cc000169 + XCd229cc00016a + XCd229cc00016b + XCd229cc00016c + XCd229cc000170 + XCd229cc000171 diff --git a/imports/import_64_1700_avs.def b/imports/import_64_1700_avs.def new file mode 100644 index 0000000..18fefc8 --- /dev/null +++ b/imports/import_64_1700_avs.def @@ -0,0 +1,53 @@ +LIBRARY avs2-core + +EXPORTS + XCgsqzn0000007 + XCgsqzn0000004 + XCgsqzn0000006 + XCgsqzn000000a + XCgsqzn000000b + XCgsqzn0000076 + XCgsqzn0000090 + XCgsqzn0000091 + XCgsqzn0000092 + XCgsqzn0000094 + XCgsqzn00000a1 + XCgsqzn00000a2 + XCgsqzn00000a3 + XCgsqzn00000af + XCgsqzn00000b0 + XCgsqzn00000b2 + XCgsqzn00000b3 + XCgsqzn00000d3 + XCgsqzn00000d4 + XCgsqzn0000129 + XCgsqzn000012a + XCgsqzn000017a + XCgsqzn000017b + XCgsqzn000017c + XCgsqzn000017d + XCgsqzn000004e + XCgsqzn0000055 + XCgsqzn0000065 + XCgsqzn0000068 + XCgsqzn00000d5 + XCgsqzn0000051 + XCgsqzn00000a6 + XCgsqzn0000144 + XCgsqzn0000077 + XCgsqzn0000079 + XCgsqzn000007a + XCgsqzn000007b + XCgsqzn000007c + XCgsqzn000007d + XCgsqzn000007e + XCgsqzn000007f + XCgsqzn0000080 + XCgsqzn0000081 + XCgsqzn0000082 + XCgsqzn0000086 + XCgsqzn000008c + XCgsqzn000008d + XCgsqzn000008e + XCgsqzn000008f + XCgsqzn0000078 diff --git a/minhook/LICENSE.txt b/minhook/LICENSE.txt new file mode 100644 index 0000000..74dea27 --- /dev/null +++ b/minhook/LICENSE.txt @@ -0,0 +1,81 @@ +MinHook - The Minimalistic API Hooking Library for x64/x86 +Copyright (C) 2009-2017 Tsuda Kageyu. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +================================================================================ +Portions of this software are Copyright (c) 2008-2009, Vyacheslav Patkov. +================================================================================ +Hacker Disassembler Engine 32 C +Copyright (c) 2008-2009, Vyacheslav Patkov. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +------------------------------------------------------------------------------- +Hacker Disassembler Engine 64 C +Copyright (c) 2008-2009, Vyacheslav Patkov. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/minhook/Module.mk b/minhook/Module.mk new file mode 100644 index 0000000..0380232 --- /dev/null +++ b/minhook/Module.mk @@ -0,0 +1,8 @@ +libs += minhook + +src_minhook := \ + hook.c \ + buffer.c \ + trampoline.c \ + hde32.c \ + hde64.c \ diff --git a/minhook/buffer.c b/minhook/buffer.c new file mode 100644 index 0000000..8f9fbce --- /dev/null +++ b/minhook/buffer.c @@ -0,0 +1,312 @@ +/* + * MinHook - The Minimalistic API Hooking Library for x64/x86 + * Copyright (C) 2009-2017 Tsuda Kageyu. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include "buffer.h" + +// Size of each memory block. (= page size of VirtualAlloc) +#define MEMORY_BLOCK_SIZE 0x1000 + +// Max range for seeking a memory block. (= 1024MB) +#define MAX_MEMORY_RANGE 0x40000000 + +// Memory protection flags to check the executable address. +#define PAGE_EXECUTE_FLAGS \ + (PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY) + +// Memory slot. +typedef struct _MEMORY_SLOT +{ + union + { + struct _MEMORY_SLOT *pNext; + UINT8 buffer[MEMORY_SLOT_SIZE]; + }; +} MEMORY_SLOT, *PMEMORY_SLOT; + +// Memory block info. Placed at the head of each block. +typedef struct _MEMORY_BLOCK +{ + struct _MEMORY_BLOCK *pNext; + PMEMORY_SLOT pFree; // First element of the free slot list. + UINT usedCount; +} MEMORY_BLOCK, *PMEMORY_BLOCK; + +//------------------------------------------------------------------------- +// Global Variables: +//------------------------------------------------------------------------- + +// First element of the memory block list. +PMEMORY_BLOCK g_pMemoryBlocks; + +//------------------------------------------------------------------------- +VOID InitializeBuffer(VOID) +{ + // Nothing to do for now. +} + +//------------------------------------------------------------------------- +VOID UninitializeBuffer(VOID) +{ + PMEMORY_BLOCK pBlock = g_pMemoryBlocks; + g_pMemoryBlocks = NULL; + + while (pBlock) + { + PMEMORY_BLOCK pNext = pBlock->pNext; + VirtualFree(pBlock, 0, MEM_RELEASE); + pBlock = pNext; + } +} + +//------------------------------------------------------------------------- +#if defined(_M_X64) || defined(__x86_64__) +static LPVOID FindPrevFreeRegion(LPVOID pAddress, LPVOID pMinAddr, DWORD dwAllocationGranularity) +{ + ULONG_PTR tryAddr = (ULONG_PTR)pAddress; + + // Round down to the allocation granularity. + tryAddr -= tryAddr % dwAllocationGranularity; + + // Start from the previous allocation granularity multiply. + tryAddr -= dwAllocationGranularity; + + while (tryAddr >= (ULONG_PTR)pMinAddr) + { + MEMORY_BASIC_INFORMATION mbi; + if (VirtualQuery((LPVOID)tryAddr, &mbi, sizeof(mbi)) == 0) + break; + + if (mbi.State == MEM_FREE) + return (LPVOID)tryAddr; + + if ((ULONG_PTR)mbi.AllocationBase < dwAllocationGranularity) + break; + + tryAddr = (ULONG_PTR)mbi.AllocationBase - dwAllocationGranularity; + } + + return NULL; +} +#endif + +//------------------------------------------------------------------------- +#if defined(_M_X64) || defined(__x86_64__) +static LPVOID FindNextFreeRegion(LPVOID pAddress, LPVOID pMaxAddr, DWORD dwAllocationGranularity) +{ + ULONG_PTR tryAddr = (ULONG_PTR)pAddress; + + // Round down to the allocation granularity. + tryAddr -= tryAddr % dwAllocationGranularity; + + // Start from the next allocation granularity multiply. + tryAddr += dwAllocationGranularity; + + while (tryAddr <= (ULONG_PTR)pMaxAddr) + { + MEMORY_BASIC_INFORMATION mbi; + if (VirtualQuery((LPVOID)tryAddr, &mbi, sizeof(mbi)) == 0) + break; + + if (mbi.State == MEM_FREE) + return (LPVOID)tryAddr; + + tryAddr = (ULONG_PTR)mbi.BaseAddress + mbi.RegionSize; + + // Round up to the next allocation granularity. + tryAddr += dwAllocationGranularity - 1; + tryAddr -= tryAddr % dwAllocationGranularity; + } + + return NULL; +} +#endif + +//------------------------------------------------------------------------- +static PMEMORY_BLOCK GetMemoryBlock(LPVOID pOrigin) +{ + PMEMORY_BLOCK pBlock; +#if defined(_M_X64) || defined(__x86_64__) + ULONG_PTR minAddr; + ULONG_PTR maxAddr; + + SYSTEM_INFO si; + GetSystemInfo(&si); + minAddr = (ULONG_PTR)si.lpMinimumApplicationAddress; + maxAddr = (ULONG_PTR)si.lpMaximumApplicationAddress; + + // pOrigin ± 512MB + if ((ULONG_PTR)pOrigin > MAX_MEMORY_RANGE && minAddr < (ULONG_PTR)pOrigin - MAX_MEMORY_RANGE) + minAddr = (ULONG_PTR)pOrigin - MAX_MEMORY_RANGE; + + if (maxAddr > (ULONG_PTR)pOrigin + MAX_MEMORY_RANGE) + maxAddr = (ULONG_PTR)pOrigin + MAX_MEMORY_RANGE; + + // Make room for MEMORY_BLOCK_SIZE bytes. + maxAddr -= MEMORY_BLOCK_SIZE - 1; +#endif + + // Look the registered blocks for a reachable one. + for (pBlock = g_pMemoryBlocks; pBlock != NULL; pBlock = pBlock->pNext) + { +#if defined(_M_X64) || defined(__x86_64__) + // Ignore the blocks too far. + if ((ULONG_PTR)pBlock < minAddr || (ULONG_PTR)pBlock >= maxAddr) + continue; +#endif + // The block has at least one unused slot. + if (pBlock->pFree != NULL) + return pBlock; + } + +#if defined(_M_X64) || defined(__x86_64__) + // Alloc a new block above if not found. + { + LPVOID pAlloc = pOrigin; + while ((ULONG_PTR)pAlloc >= minAddr) + { + pAlloc = FindPrevFreeRegion(pAlloc, (LPVOID)minAddr, si.dwAllocationGranularity); + if (pAlloc == NULL) + break; + + pBlock = (PMEMORY_BLOCK)VirtualAlloc( + pAlloc, MEMORY_BLOCK_SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); + if (pBlock != NULL) + break; + } + } + + // Alloc a new block below if not found. + if (pBlock == NULL) + { + LPVOID pAlloc = pOrigin; + while ((ULONG_PTR)pAlloc <= maxAddr) + { + pAlloc = FindNextFreeRegion(pAlloc, (LPVOID)maxAddr, si.dwAllocationGranularity); + if (pAlloc == NULL) + break; + + pBlock = (PMEMORY_BLOCK)VirtualAlloc( + pAlloc, MEMORY_BLOCK_SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); + if (pBlock != NULL) + break; + } + } +#else + // In x86 mode, a memory block can be placed anywhere. + pBlock = (PMEMORY_BLOCK)VirtualAlloc( + NULL, MEMORY_BLOCK_SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); +#endif + + if (pBlock != NULL) + { + // Build a linked list of all the slots. + PMEMORY_SLOT pSlot = (PMEMORY_SLOT)pBlock + 1; + pBlock->pFree = NULL; + pBlock->usedCount = 0; + do + { + pSlot->pNext = pBlock->pFree; + pBlock->pFree = pSlot; + pSlot++; + } while ((ULONG_PTR)pSlot - (ULONG_PTR)pBlock <= MEMORY_BLOCK_SIZE - MEMORY_SLOT_SIZE); + + pBlock->pNext = g_pMemoryBlocks; + g_pMemoryBlocks = pBlock; + } + + return pBlock; +} + +//------------------------------------------------------------------------- +LPVOID AllocateBuffer(LPVOID pOrigin) +{ + PMEMORY_SLOT pSlot; + PMEMORY_BLOCK pBlock = GetMemoryBlock(pOrigin); + if (pBlock == NULL) + return NULL; + + // Remove an unused slot from the list. + pSlot = pBlock->pFree; + pBlock->pFree = pSlot->pNext; + pBlock->usedCount++; +#ifdef _DEBUG + // Fill the slot with INT3 for debugging. + memset(pSlot, 0xCC, sizeof(MEMORY_SLOT)); +#endif + return pSlot; +} + +//------------------------------------------------------------------------- +VOID FreeBuffer(LPVOID pBuffer) +{ + PMEMORY_BLOCK pBlock = g_pMemoryBlocks; + PMEMORY_BLOCK pPrev = NULL; + ULONG_PTR pTargetBlock = ((ULONG_PTR)pBuffer / MEMORY_BLOCK_SIZE) * MEMORY_BLOCK_SIZE; + + while (pBlock != NULL) + { + if ((ULONG_PTR)pBlock == pTargetBlock) + { + PMEMORY_SLOT pSlot = (PMEMORY_SLOT)pBuffer; +#ifdef _DEBUG + // Clear the released slot for debugging. + memset(pSlot, 0x00, sizeof(*pSlot)); +#endif + // Restore the released slot to the list. + pSlot->pNext = pBlock->pFree; + pBlock->pFree = pSlot; + pBlock->usedCount--; + + // Free if unused. + if (pBlock->usedCount == 0) + { + if (pPrev) + pPrev->pNext = pBlock->pNext; + else + g_pMemoryBlocks = pBlock->pNext; + + VirtualFree(pBlock, 0, MEM_RELEASE); + } + + break; + } + + pPrev = pBlock; + pBlock = pBlock->pNext; + } +} + +//------------------------------------------------------------------------- +BOOL IsExecutableAddress(LPVOID pAddress) +{ + MEMORY_BASIC_INFORMATION mi; + VirtualQuery(pAddress, &mi, sizeof(mi)); + + return (mi.State == MEM_COMMIT && (mi.Protect & PAGE_EXECUTE_FLAGS)); +} diff --git a/minhook/buffer.h b/minhook/buffer.h new file mode 100644 index 0000000..204d551 --- /dev/null +++ b/minhook/buffer.h @@ -0,0 +1,42 @@ +/* + * MinHook - The Minimalistic API Hooking Library for x64/x86 + * Copyright (C) 2009-2017 Tsuda Kageyu. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +// Size of each memory slot. +#if defined(_M_X64) || defined(__x86_64__) + #define MEMORY_SLOT_SIZE 64 +#else + #define MEMORY_SLOT_SIZE 32 +#endif + +VOID InitializeBuffer(VOID); +VOID UninitializeBuffer(VOID); +LPVOID AllocateBuffer(LPVOID pOrigin); +VOID FreeBuffer(LPVOID pBuffer); +BOOL IsExecutableAddress(LPVOID pAddress); diff --git a/minhook/hde32.c b/minhook/hde32.c new file mode 100644 index 0000000..08fa25b --- /dev/null +++ b/minhook/hde32.c @@ -0,0 +1,326 @@ +/* + * Hacker Disassembler Engine 32 C + * Copyright (c) 2008-2009, Vyacheslav Patkov. + * All rights reserved. + * + */ + +#if defined(_M_IX86) || defined(__i386__) + +#include "hde32.h" +#include "table32.h" + +unsigned int hde32_disasm(const void *code, hde32s *hs) +{ + uint8_t x, c, *p = (uint8_t *)code, cflags, opcode, pref = 0; + uint8_t *ht = hde32_table, m_mod, m_reg, m_rm, disp_size = 0; + + // Avoid using memset to reduce the footprint. +#ifndef _MSC_VER + memset((LPBYTE)hs, 0, sizeof(hde32s)); +#else + __stosb((LPBYTE)hs, 0, sizeof(hde32s)); +#endif + + for (x = 16; x; x--) + switch (c = *p++) { + case 0xf3: + hs->p_rep = c; + pref |= PRE_F3; + break; + case 0xf2: + hs->p_rep = c; + pref |= PRE_F2; + break; + case 0xf0: + hs->p_lock = c; + pref |= PRE_LOCK; + break; + case 0x26: case 0x2e: case 0x36: + case 0x3e: case 0x64: case 0x65: + hs->p_seg = c; + pref |= PRE_SEG; + break; + case 0x66: + hs->p_66 = c; + pref |= PRE_66; + break; + case 0x67: + hs->p_67 = c; + pref |= PRE_67; + break; + default: + goto pref_done; + } + pref_done: + + hs->flags = (uint32_t)pref << 23; + + if (!pref) + pref |= PRE_NONE; + + if ((hs->opcode = c) == 0x0f) { + hs->opcode2 = c = *p++; + ht += DELTA_OPCODES; + } else if (c >= 0xa0 && c <= 0xa3) { + if (pref & PRE_67) + pref |= PRE_66; + else + pref &= ~PRE_66; + } + + opcode = c; + cflags = ht[ht[opcode / 4] + (opcode % 4)]; + + if (cflags == C_ERROR) { + hs->flags |= F_ERROR | F_ERROR_OPCODE; + cflags = 0; + if ((opcode & -3) == 0x24) + cflags++; + } + + x = 0; + if (cflags & C_GROUP) { + uint16_t t; + t = *(uint16_t *)(ht + (cflags & 0x7f)); + cflags = (uint8_t)t; + x = (uint8_t)(t >> 8); + } + + if (hs->opcode2) { + ht = hde32_table + DELTA_PREFIXES; + if (ht[ht[opcode / 4] + (opcode % 4)] & pref) + hs->flags |= F_ERROR | F_ERROR_OPCODE; + } + + if (cflags & C_MODRM) { + hs->flags |= F_MODRM; + hs->modrm = c = *p++; + hs->modrm_mod = m_mod = c >> 6; + hs->modrm_rm = m_rm = c & 7; + hs->modrm_reg = m_reg = (c & 0x3f) >> 3; + + if (x && ((x << m_reg) & 0x80)) + hs->flags |= F_ERROR | F_ERROR_OPCODE; + + if (!hs->opcode2 && opcode >= 0xd9 && opcode <= 0xdf) { + uint8_t t = opcode - 0xd9; + if (m_mod == 3) { + ht = hde32_table + DELTA_FPU_MODRM + t*8; + t = ht[m_reg] << m_rm; + } else { + ht = hde32_table + DELTA_FPU_REG; + t = ht[t] << m_reg; + } + if (t & 0x80) + hs->flags |= F_ERROR | F_ERROR_OPCODE; + } + + if (pref & PRE_LOCK) { + if (m_mod == 3) { + hs->flags |= F_ERROR | F_ERROR_LOCK; + } else { + uint8_t *table_end, op = opcode; + if (hs->opcode2) { + ht = hde32_table + DELTA_OP2_LOCK_OK; + table_end = ht + DELTA_OP_ONLY_MEM - DELTA_OP2_LOCK_OK; + } else { + ht = hde32_table + DELTA_OP_LOCK_OK; + table_end = ht + DELTA_OP2_LOCK_OK - DELTA_OP_LOCK_OK; + op &= -2; + } + for (; ht != table_end; ht++) + if (*ht++ == op) { + if (!((*ht << m_reg) & 0x80)) + goto no_lock_error; + else + break; + } + hs->flags |= F_ERROR | F_ERROR_LOCK; + no_lock_error: + ; + } + } + + if (hs->opcode2) { + switch (opcode) { + case 0x20: case 0x22: + m_mod = 3; + if (m_reg > 4 || m_reg == 1) + goto error_operand; + else + goto no_error_operand; + case 0x21: case 0x23: + m_mod = 3; + if (m_reg == 4 || m_reg == 5) + goto error_operand; + else + goto no_error_operand; + } + } else { + switch (opcode) { + case 0x8c: + if (m_reg > 5) + goto error_operand; + else + goto no_error_operand; + case 0x8e: + if (m_reg == 1 || m_reg > 5) + goto error_operand; + else + goto no_error_operand; + } + } + + if (m_mod == 3) { + uint8_t *table_end; + if (hs->opcode2) { + ht = hde32_table + DELTA_OP2_ONLY_MEM; + table_end = ht + sizeof(hde32_table) - DELTA_OP2_ONLY_MEM; + } else { + ht = hde32_table + DELTA_OP_ONLY_MEM; + table_end = ht + DELTA_OP2_ONLY_MEM - DELTA_OP_ONLY_MEM; + } + for (; ht != table_end; ht += 2) + if (*ht++ == opcode) { + if (*ht++ & pref && !((*ht << m_reg) & 0x80)) + goto error_operand; + else + break; + } + goto no_error_operand; + } else if (hs->opcode2) { + switch (opcode) { + case 0x50: case 0xd7: case 0xf7: + if (pref & (PRE_NONE | PRE_66)) + goto error_operand; + break; + case 0xd6: + if (pref & (PRE_F2 | PRE_F3)) + goto error_operand; + break; + case 0xc5: + goto error_operand; + } + goto no_error_operand; + } else + goto no_error_operand; + + error_operand: + hs->flags |= F_ERROR | F_ERROR_OPERAND; + no_error_operand: + + c = *p++; + if (m_reg <= 1) { + if (opcode == 0xf6) + cflags |= C_IMM8; + else if (opcode == 0xf7) + cflags |= C_IMM_P66; + } + + switch (m_mod) { + case 0: + if (pref & PRE_67) { + if (m_rm == 6) + disp_size = 2; + } else + if (m_rm == 5) + disp_size = 4; + break; + case 1: + disp_size = 1; + break; + case 2: + disp_size = 2; + if (!(pref & PRE_67)) + disp_size <<= 1; + } + + if (m_mod != 3 && m_rm == 4 && !(pref & PRE_67)) { + hs->flags |= F_SIB; + p++; + hs->sib = c; + hs->sib_scale = c >> 6; + hs->sib_index = (c & 0x3f) >> 3; + if ((hs->sib_base = c & 7) == 5 && !(m_mod & 1)) + disp_size = 4; + } + + p--; + switch (disp_size) { + case 1: + hs->flags |= F_DISP8; + hs->disp.disp8 = *p; + break; + case 2: + hs->flags |= F_DISP16; + hs->disp.disp16 = *(uint16_t *)p; + break; + case 4: + hs->flags |= F_DISP32; + hs->disp.disp32 = *(uint32_t *)p; + } + p += disp_size; + } else if (pref & PRE_LOCK) + hs->flags |= F_ERROR | F_ERROR_LOCK; + + if (cflags & C_IMM_P66) { + if (cflags & C_REL32) { + if (pref & PRE_66) { + hs->flags |= F_IMM16 | F_RELATIVE; + hs->imm.imm16 = *(uint16_t *)p; + p += 2; + goto disasm_done; + } + goto rel32_ok; + } + if (pref & PRE_66) { + hs->flags |= F_IMM16; + hs->imm.imm16 = *(uint16_t *)p; + p += 2; + } else { + hs->flags |= F_IMM32; + hs->imm.imm32 = *(uint32_t *)p; + p += 4; + } + } + + if (cflags & C_IMM16) { + if (hs->flags & F_IMM32) { + hs->flags |= F_IMM16; + hs->disp.disp16 = *(uint16_t *)p; + } else if (hs->flags & F_IMM16) { + hs->flags |= F_2IMM16; + hs->disp.disp16 = *(uint16_t *)p; + } else { + hs->flags |= F_IMM16; + hs->imm.imm16 = *(uint16_t *)p; + } + p += 2; + } + if (cflags & C_IMM8) { + hs->flags |= F_IMM8; + hs->imm.imm8 = *p++; + } + + if (cflags & C_REL32) { + rel32_ok: + hs->flags |= F_IMM32 | F_RELATIVE; + hs->imm.imm32 = *(uint32_t *)p; + p += 4; + } else if (cflags & C_REL8) { + hs->flags |= F_IMM8 | F_RELATIVE; + hs->imm.imm8 = *p++; + } + + disasm_done: + + if ((hs->len = (uint8_t)(p-(uint8_t *)code)) > 15) { + hs->flags |= F_ERROR | F_ERROR_LENGTH; + hs->len = 15; + } + + return (unsigned int)hs->len; +} + +#endif // defined(_M_IX86) || defined(__i386__) diff --git a/minhook/hde32.h b/minhook/hde32.h new file mode 100644 index 0000000..1112450 --- /dev/null +++ b/minhook/hde32.h @@ -0,0 +1,105 @@ +/* + * Hacker Disassembler Engine 32 + * Copyright (c) 2006-2009, Vyacheslav Patkov. + * All rights reserved. + * + * hde32.h: C/C++ header file + * + */ + +#ifndef _HDE32_H_ +#define _HDE32_H_ + +/* stdint.h - C99 standard header + * http://en.wikipedia.org/wiki/stdint.h + * + * if your compiler doesn't contain "stdint.h" header (for + * example, Microsoft Visual C++), you can download file: + * http://www.azillionmonkeys.com/qed/pstdint.h + * and change next line to: + * #include "pstdint.h" + */ +#include "pstdint.h" + +#define F_MODRM 0x00000001 +#define F_SIB 0x00000002 +#define F_IMM8 0x00000004 +#define F_IMM16 0x00000008 +#define F_IMM32 0x00000010 +#define F_DISP8 0x00000020 +#define F_DISP16 0x00000040 +#define F_DISP32 0x00000080 +#define F_RELATIVE 0x00000100 +#define F_2IMM16 0x00000800 +#define F_ERROR 0x00001000 +#define F_ERROR_OPCODE 0x00002000 +#define F_ERROR_LENGTH 0x00004000 +#define F_ERROR_LOCK 0x00008000 +#define F_ERROR_OPERAND 0x00010000 +#define F_PREFIX_REPNZ 0x01000000 +#define F_PREFIX_REPX 0x02000000 +#define F_PREFIX_REP 0x03000000 +#define F_PREFIX_66 0x04000000 +#define F_PREFIX_67 0x08000000 +#define F_PREFIX_LOCK 0x10000000 +#define F_PREFIX_SEG 0x20000000 +#define F_PREFIX_ANY 0x3f000000 + +#define PREFIX_SEGMENT_CS 0x2e +#define PREFIX_SEGMENT_SS 0x36 +#define PREFIX_SEGMENT_DS 0x3e +#define PREFIX_SEGMENT_ES 0x26 +#define PREFIX_SEGMENT_FS 0x64 +#define PREFIX_SEGMENT_GS 0x65 +#define PREFIX_LOCK 0xf0 +#define PREFIX_REPNZ 0xf2 +#define PREFIX_REPX 0xf3 +#define PREFIX_OPERAND_SIZE 0x66 +#define PREFIX_ADDRESS_SIZE 0x67 + +#pragma pack(push,1) + +typedef struct { + uint8_t len; + uint8_t p_rep; + uint8_t p_lock; + uint8_t p_seg; + uint8_t p_66; + uint8_t p_67; + uint8_t opcode; + uint8_t opcode2; + uint8_t modrm; + uint8_t modrm_mod; + uint8_t modrm_reg; + uint8_t modrm_rm; + uint8_t sib; + uint8_t sib_scale; + uint8_t sib_index; + uint8_t sib_base; + union { + uint8_t imm8; + uint16_t imm16; + uint32_t imm32; + } imm; + union { + uint8_t disp8; + uint16_t disp16; + uint32_t disp32; + } disp; + uint32_t flags; +} hde32s; + +#pragma pack(pop) + +#ifdef __cplusplus +extern "C" { +#endif + +/* __cdecl */ +unsigned int hde32_disasm(const void *code, hde32s *hs); + +#ifdef __cplusplus +} +#endif + +#endif /* _HDE32_H_ */ diff --git a/minhook/hde64.c b/minhook/hde64.c new file mode 100644 index 0000000..c23e2fc --- /dev/null +++ b/minhook/hde64.c @@ -0,0 +1,337 @@ +/* + * Hacker Disassembler Engine 64 C + * Copyright (c) 2008-2009, Vyacheslav Patkov. + * All rights reserved. + * + */ + +#if defined(_M_X64) || defined(__x86_64__) + +#include "hde64.h" +#include "table64.h" + +unsigned int hde64_disasm(const void *code, hde64s *hs) +{ + uint8_t x, c, *p = (uint8_t *)code, cflags, opcode, pref = 0; + uint8_t *ht = hde64_table, m_mod, m_reg, m_rm, disp_size = 0; + uint8_t op64 = 0; + + // Avoid using memset to reduce the footprint. +#ifndef _MSC_VER + memset((LPBYTE)hs, 0, sizeof(hde64s)); +#else + __stosb((LPBYTE)hs, 0, sizeof(hde64s)); +#endif + + for (x = 16; x; x--) + switch (c = *p++) { + case 0xf3: + hs->p_rep = c; + pref |= PRE_F3; + break; + case 0xf2: + hs->p_rep = c; + pref |= PRE_F2; + break; + case 0xf0: + hs->p_lock = c; + pref |= PRE_LOCK; + break; + case 0x26: case 0x2e: case 0x36: + case 0x3e: case 0x64: case 0x65: + hs->p_seg = c; + pref |= PRE_SEG; + break; + case 0x66: + hs->p_66 = c; + pref |= PRE_66; + break; + case 0x67: + hs->p_67 = c; + pref |= PRE_67; + break; + default: + goto pref_done; + } + pref_done: + + hs->flags = (uint32_t)pref << 23; + + if (!pref) + pref |= PRE_NONE; + + if ((c & 0xf0) == 0x40) { + hs->flags |= F_PREFIX_REX; + if ((hs->rex_w = (c & 0xf) >> 3) && (*p & 0xf8) == 0xb8) + op64++; + hs->rex_r = (c & 7) >> 2; + hs->rex_x = (c & 3) >> 1; + hs->rex_b = c & 1; + if (((c = *p++) & 0xf0) == 0x40) { + opcode = c; + goto error_opcode; + } + } + + if ((hs->opcode = c) == 0x0f) { + hs->opcode2 = c = *p++; + ht += DELTA_OPCODES; + } else if (c >= 0xa0 && c <= 0xa3) { + op64++; + if (pref & PRE_67) + pref |= PRE_66; + else + pref &= ~PRE_66; + } + + opcode = c; + cflags = ht[ht[opcode / 4] + (opcode % 4)]; + + if (cflags == C_ERROR) { + error_opcode: + hs->flags |= F_ERROR | F_ERROR_OPCODE; + cflags = 0; + if ((opcode & -3) == 0x24) + cflags++; + } + + x = 0; + if (cflags & C_GROUP) { + uint16_t t; + t = *(uint16_t *)(ht + (cflags & 0x7f)); + cflags = (uint8_t)t; + x = (uint8_t)(t >> 8); + } + + if (hs->opcode2) { + ht = hde64_table + DELTA_PREFIXES; + if (ht[ht[opcode / 4] + (opcode % 4)] & pref) + hs->flags |= F_ERROR | F_ERROR_OPCODE; + } + + if (cflags & C_MODRM) { + hs->flags |= F_MODRM; + hs->modrm = c = *p++; + hs->modrm_mod = m_mod = c >> 6; + hs->modrm_rm = m_rm = c & 7; + hs->modrm_reg = m_reg = (c & 0x3f) >> 3; + + if (x && ((x << m_reg) & 0x80)) + hs->flags |= F_ERROR | F_ERROR_OPCODE; + + if (!hs->opcode2 && opcode >= 0xd9 && opcode <= 0xdf) { + uint8_t t = opcode - 0xd9; + if (m_mod == 3) { + ht = hde64_table + DELTA_FPU_MODRM + t*8; + t = ht[m_reg] << m_rm; + } else { + ht = hde64_table + DELTA_FPU_REG; + t = ht[t] << m_reg; + } + if (t & 0x80) + hs->flags |= F_ERROR | F_ERROR_OPCODE; + } + + if (pref & PRE_LOCK) { + if (m_mod == 3) { + hs->flags |= F_ERROR | F_ERROR_LOCK; + } else { + uint8_t *table_end, op = opcode; + if (hs->opcode2) { + ht = hde64_table + DELTA_OP2_LOCK_OK; + table_end = ht + DELTA_OP_ONLY_MEM - DELTA_OP2_LOCK_OK; + } else { + ht = hde64_table + DELTA_OP_LOCK_OK; + table_end = ht + DELTA_OP2_LOCK_OK - DELTA_OP_LOCK_OK; + op &= -2; + } + for (; ht != table_end; ht++) + if (*ht++ == op) { + if (!((*ht << m_reg) & 0x80)) + goto no_lock_error; + else + break; + } + hs->flags |= F_ERROR | F_ERROR_LOCK; + no_lock_error: + ; + } + } + + if (hs->opcode2) { + switch (opcode) { + case 0x20: case 0x22: + m_mod = 3; + if (m_reg > 4 || m_reg == 1) + goto error_operand; + else + goto no_error_operand; + case 0x21: case 0x23: + m_mod = 3; + if (m_reg == 4 || m_reg == 5) + goto error_operand; + else + goto no_error_operand; + } + } else { + switch (opcode) { + case 0x8c: + if (m_reg > 5) + goto error_operand; + else + goto no_error_operand; + case 0x8e: + if (m_reg == 1 || m_reg > 5) + goto error_operand; + else + goto no_error_operand; + } + } + + if (m_mod == 3) { + uint8_t *table_end; + if (hs->opcode2) { + ht = hde64_table + DELTA_OP2_ONLY_MEM; + table_end = ht + sizeof(hde64_table) - DELTA_OP2_ONLY_MEM; + } else { + ht = hde64_table + DELTA_OP_ONLY_MEM; + table_end = ht + DELTA_OP2_ONLY_MEM - DELTA_OP_ONLY_MEM; + } + for (; ht != table_end; ht += 2) + if (*ht++ == opcode) { + if (*ht++ & pref && !((*ht << m_reg) & 0x80)) + goto error_operand; + else + break; + } + goto no_error_operand; + } else if (hs->opcode2) { + switch (opcode) { + case 0x50: case 0xd7: case 0xf7: + if (pref & (PRE_NONE | PRE_66)) + goto error_operand; + break; + case 0xd6: + if (pref & (PRE_F2 | PRE_F3)) + goto error_operand; + break; + case 0xc5: + goto error_operand; + } + goto no_error_operand; + } else + goto no_error_operand; + + error_operand: + hs->flags |= F_ERROR | F_ERROR_OPERAND; + no_error_operand: + + c = *p++; + if (m_reg <= 1) { + if (opcode == 0xf6) + cflags |= C_IMM8; + else if (opcode == 0xf7) + cflags |= C_IMM_P66; + } + + switch (m_mod) { + case 0: + if (pref & PRE_67) { + if (m_rm == 6) + disp_size = 2; + } else + if (m_rm == 5) + disp_size = 4; + break; + case 1: + disp_size = 1; + break; + case 2: + disp_size = 2; + if (!(pref & PRE_67)) + disp_size <<= 1; + } + + if (m_mod != 3 && m_rm == 4) { + hs->flags |= F_SIB; + p++; + hs->sib = c; + hs->sib_scale = c >> 6; + hs->sib_index = (c & 0x3f) >> 3; + if ((hs->sib_base = c & 7) == 5 && !(m_mod & 1)) + disp_size = 4; + } + + p--; + switch (disp_size) { + case 1: + hs->flags |= F_DISP8; + hs->disp.disp8 = *p; + break; + case 2: + hs->flags |= F_DISP16; + hs->disp.disp16 = *(uint16_t *)p; + break; + case 4: + hs->flags |= F_DISP32; + hs->disp.disp32 = *(uint32_t *)p; + } + p += disp_size; + } else if (pref & PRE_LOCK) + hs->flags |= F_ERROR | F_ERROR_LOCK; + + if (cflags & C_IMM_P66) { + if (cflags & C_REL32) { + if (pref & PRE_66) { + hs->flags |= F_IMM16 | F_RELATIVE; + hs->imm.imm16 = *(uint16_t *)p; + p += 2; + goto disasm_done; + } + goto rel32_ok; + } + if (op64) { + hs->flags |= F_IMM64; + hs->imm.imm64 = *(uint64_t *)p; + p += 8; + } else if (!(pref & PRE_66)) { + hs->flags |= F_IMM32; + hs->imm.imm32 = *(uint32_t *)p; + p += 4; + } else + goto imm16_ok; + } + + + if (cflags & C_IMM16) { + imm16_ok: + hs->flags |= F_IMM16; + hs->imm.imm16 = *(uint16_t *)p; + p += 2; + } + if (cflags & C_IMM8) { + hs->flags |= F_IMM8; + hs->imm.imm8 = *p++; + } + + if (cflags & C_REL32) { + rel32_ok: + hs->flags |= F_IMM32 | F_RELATIVE; + hs->imm.imm32 = *(uint32_t *)p; + p += 4; + } else if (cflags & C_REL8) { + hs->flags |= F_IMM8 | F_RELATIVE; + hs->imm.imm8 = *p++; + } + + disasm_done: + + if ((hs->len = (uint8_t)(p-(uint8_t *)code)) > 15) { + hs->flags |= F_ERROR | F_ERROR_LENGTH; + hs->len = 15; + } + + return (unsigned int)hs->len; +} + +#endif // defined(_M_X64) || defined(__x86_64__) diff --git a/minhook/hde64.h b/minhook/hde64.h new file mode 100644 index 0000000..ecbf4df --- /dev/null +++ b/minhook/hde64.h @@ -0,0 +1,112 @@ +/* + * Hacker Disassembler Engine 64 + * Copyright (c) 2008-2009, Vyacheslav Patkov. + * All rights reserved. + * + * hde64.h: C/C++ header file + * + */ + +#ifndef _HDE64_H_ +#define _HDE64_H_ + +/* stdint.h - C99 standard header + * http://en.wikipedia.org/wiki/stdint.h + * + * if your compiler doesn't contain "stdint.h" header (for + * example, Microsoft Visual C++), you can download file: + * http://www.azillionmonkeys.com/qed/pstdint.h + * and change next line to: + * #include "pstdint.h" + */ +#include "pstdint.h" + +#define F_MODRM 0x00000001 +#define F_SIB 0x00000002 +#define F_IMM8 0x00000004 +#define F_IMM16 0x00000008 +#define F_IMM32 0x00000010 +#define F_IMM64 0x00000020 +#define F_DISP8 0x00000040 +#define F_DISP16 0x00000080 +#define F_DISP32 0x00000100 +#define F_RELATIVE 0x00000200 +#define F_ERROR 0x00001000 +#define F_ERROR_OPCODE 0x00002000 +#define F_ERROR_LENGTH 0x00004000 +#define F_ERROR_LOCK 0x00008000 +#define F_ERROR_OPERAND 0x00010000 +#define F_PREFIX_REPNZ 0x01000000 +#define F_PREFIX_REPX 0x02000000 +#define F_PREFIX_REP 0x03000000 +#define F_PREFIX_66 0x04000000 +#define F_PREFIX_67 0x08000000 +#define F_PREFIX_LOCK 0x10000000 +#define F_PREFIX_SEG 0x20000000 +#define F_PREFIX_REX 0x40000000 +#define F_PREFIX_ANY 0x7f000000 + +#define PREFIX_SEGMENT_CS 0x2e +#define PREFIX_SEGMENT_SS 0x36 +#define PREFIX_SEGMENT_DS 0x3e +#define PREFIX_SEGMENT_ES 0x26 +#define PREFIX_SEGMENT_FS 0x64 +#define PREFIX_SEGMENT_GS 0x65 +#define PREFIX_LOCK 0xf0 +#define PREFIX_REPNZ 0xf2 +#define PREFIX_REPX 0xf3 +#define PREFIX_OPERAND_SIZE 0x66 +#define PREFIX_ADDRESS_SIZE 0x67 + +#pragma pack(push,1) + +typedef struct { + uint8_t len; + uint8_t p_rep; + uint8_t p_lock; + uint8_t p_seg; + uint8_t p_66; + uint8_t p_67; + uint8_t rex; + uint8_t rex_w; + uint8_t rex_r; + uint8_t rex_x; + uint8_t rex_b; + uint8_t opcode; + uint8_t opcode2; + uint8_t modrm; + uint8_t modrm_mod; + uint8_t modrm_reg; + uint8_t modrm_rm; + uint8_t sib; + uint8_t sib_scale; + uint8_t sib_index; + uint8_t sib_base; + union { + uint8_t imm8; + uint16_t imm16; + uint32_t imm32; + uint64_t imm64; + } imm; + union { + uint8_t disp8; + uint16_t disp16; + uint32_t disp32; + } disp; + uint32_t flags; +} hde64s; + +#pragma pack(pop) + +#ifdef __cplusplus +extern "C" { +#endif + +/* __cdecl */ +unsigned int hde64_disasm(const void *code, hde64s *hs); + +#ifdef __cplusplus +} +#endif + +#endif /* _HDE64_H_ */ diff --git a/minhook/hook.c b/minhook/hook.c new file mode 100644 index 0000000..8250d8f --- /dev/null +++ b/minhook/hook.c @@ -0,0 +1,889 @@ +/* + * MinHook - The Minimalistic API Hooking Library for x64/x86 + * Copyright (C) 2009-2017 Tsuda Kageyu. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include "include/MinHook.h" +#include "buffer.h" +#include "trampoline.h" + +#ifndef ARRAYSIZE + #define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0])) +#endif + +// Initial capacity of the HOOK_ENTRY buffer. +#define INITIAL_HOOK_CAPACITY 32 + +// Initial capacity of the thread IDs buffer. +#define INITIAL_THREAD_CAPACITY 128 + +// Special hook position values. +#define INVALID_HOOK_POS UINT_MAX +#define ALL_HOOKS_POS UINT_MAX + +// Freeze() action argument defines. +#define ACTION_DISABLE 0 +#define ACTION_ENABLE 1 +#define ACTION_APPLY_QUEUED 2 + +// Thread access rights for suspending/resuming threads. +#define THREAD_ACCESS \ + (THREAD_SUSPEND_RESUME | THREAD_GET_CONTEXT | THREAD_QUERY_INFORMATION | THREAD_SET_CONTEXT) + +// Hook information. +typedef struct _HOOK_ENTRY +{ + LPVOID pTarget; // Address of the target function. + LPVOID pDetour; // Address of the detour or relay function. + LPVOID pTrampoline; // Address of the trampoline function. + UINT8 backup[8]; // Original prologue of the target function. + + UINT8 patchAbove : 1; // Uses the hot patch area. + UINT8 isEnabled : 1; // Enabled. + UINT8 queueEnable : 1; // Queued for enabling/disabling when != isEnabled. + + UINT nIP : 4; // Count of the instruction boundaries. + UINT8 oldIPs[8]; // Instruction boundaries of the target function. + UINT8 newIPs[8]; // Instruction boundaries of the trampoline function. +} HOOK_ENTRY, *PHOOK_ENTRY; + +// Suspended threads for Freeze()/Unfreeze(). +typedef struct _FROZEN_THREADS +{ + LPDWORD pItems; // Data heap + UINT capacity; // Size of allocated data heap, items + UINT size; // Actual number of data items +} FROZEN_THREADS, *PFROZEN_THREADS; + +//------------------------------------------------------------------------- +// Global Variables: +//------------------------------------------------------------------------- + +// Spin lock flag for EnterSpinLock()/LeaveSpinLock(). +volatile LONG g_isLocked = FALSE; + +// Private heap handle. If not NULL, this library is initialized. +HANDLE g_hHeap = NULL; + +// Hook entries. +struct +{ + PHOOK_ENTRY pItems; // Data heap + UINT capacity; // Size of allocated data heap, items + UINT size; // Actual number of data items +} g_hooks; + +//------------------------------------------------------------------------- +// Returns INVALID_HOOK_POS if not found. +static UINT FindHookEntry(LPVOID pTarget) +{ + UINT i; + for (i = 0; i < g_hooks.size; ++i) + { + if ((ULONG_PTR)pTarget == (ULONG_PTR)g_hooks.pItems[i].pTarget) + return i; + } + + return INVALID_HOOK_POS; +} + +//------------------------------------------------------------------------- +static PHOOK_ENTRY AddHookEntry() +{ + if (g_hooks.pItems == NULL) + { + g_hooks.capacity = INITIAL_HOOK_CAPACITY; + g_hooks.pItems = (PHOOK_ENTRY)HeapAlloc( + g_hHeap, 0, g_hooks.capacity * sizeof(HOOK_ENTRY)); + if (g_hooks.pItems == NULL) + return NULL; + } + else if (g_hooks.size >= g_hooks.capacity) + { + PHOOK_ENTRY p = (PHOOK_ENTRY)HeapReAlloc( + g_hHeap, 0, g_hooks.pItems, (g_hooks.capacity * 2) * sizeof(HOOK_ENTRY)); + if (p == NULL) + return NULL; + + g_hooks.capacity *= 2; + g_hooks.pItems = p; + } + + return &g_hooks.pItems[g_hooks.size++]; +} + +//------------------------------------------------------------------------- +static void DeleteHookEntry(UINT pos) +{ + if (pos < g_hooks.size - 1) + g_hooks.pItems[pos] = g_hooks.pItems[g_hooks.size - 1]; + + g_hooks.size--; + + if (g_hooks.capacity / 2 >= INITIAL_HOOK_CAPACITY && g_hooks.capacity / 2 >= g_hooks.size) + { + PHOOK_ENTRY p = (PHOOK_ENTRY)HeapReAlloc( + g_hHeap, 0, g_hooks.pItems, (g_hooks.capacity / 2) * sizeof(HOOK_ENTRY)); + if (p == NULL) + return; + + g_hooks.capacity /= 2; + g_hooks.pItems = p; + } +} + +//------------------------------------------------------------------------- +static DWORD_PTR FindOldIP(PHOOK_ENTRY pHook, DWORD_PTR ip) +{ + UINT i; + + if (pHook->patchAbove && ip == ((DWORD_PTR)pHook->pTarget - sizeof(JMP_REL))) + return (DWORD_PTR)pHook->pTarget; + + for (i = 0; i < pHook->nIP; ++i) + { + if (ip == ((DWORD_PTR)pHook->pTrampoline + pHook->newIPs[i])) + return (DWORD_PTR)pHook->pTarget + pHook->oldIPs[i]; + } + +#if defined(_M_X64) || defined(__x86_64__) + // Check relay function. + if (ip == (DWORD_PTR)pHook->pDetour) + return (DWORD_PTR)pHook->pTarget; +#endif + + return 0; +} + +//------------------------------------------------------------------------- +static DWORD_PTR FindNewIP(PHOOK_ENTRY pHook, DWORD_PTR ip) +{ + UINT i; + for (i = 0; i < pHook->nIP; ++i) + { + if (ip == ((DWORD_PTR)pHook->pTarget + pHook->oldIPs[i])) + return (DWORD_PTR)pHook->pTrampoline + pHook->newIPs[i]; + } + + return 0; +} + +//------------------------------------------------------------------------- +static void ProcessThreadIPs(HANDLE hThread, UINT pos, UINT action) +{ + // If the thread suspended in the overwritten area, + // move IP to the proper address. + + CONTEXT c; +#if defined(_M_X64) || defined(__x86_64__) + DWORD64 *pIP = &c.Rip; +#else + DWORD *pIP = &c.Eip; +#endif + UINT count; + + c.ContextFlags = CONTEXT_CONTROL; + if (!GetThreadContext(hThread, &c)) + return; + + if (pos == ALL_HOOKS_POS) + { + pos = 0; + count = g_hooks.size; + } + else + { + count = pos + 1; + } + + for (; pos < count; ++pos) + { + PHOOK_ENTRY pHook = &g_hooks.pItems[pos]; + BOOL enable; + DWORD_PTR ip; + + switch (action) + { + case ACTION_DISABLE: + enable = FALSE; + break; + + case ACTION_ENABLE: + enable = TRUE; + break; + + default: // ACTION_APPLY_QUEUED + enable = pHook->queueEnable; + break; + } + if (pHook->isEnabled == enable) + continue; + + if (enable) + ip = FindNewIP(pHook, *pIP); + else + ip = FindOldIP(pHook, *pIP); + + if (ip != 0) + { + *pIP = ip; + SetThreadContext(hThread, &c); + } + } +} + +//------------------------------------------------------------------------- +static VOID EnumerateThreads(PFROZEN_THREADS pThreads) +{ + HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); + if (hSnapshot != INVALID_HANDLE_VALUE) + { + THREADENTRY32 te; + te.dwSize = sizeof(THREADENTRY32); + if (Thread32First(hSnapshot, &te)) + { + do + { + if (te.dwSize >= (FIELD_OFFSET(THREADENTRY32, th32OwnerProcessID) + sizeof(DWORD)) + && te.th32OwnerProcessID == GetCurrentProcessId() + && te.th32ThreadID != GetCurrentThreadId()) + { + if (pThreads->pItems == NULL) + { + pThreads->capacity = INITIAL_THREAD_CAPACITY; + pThreads->pItems + = (LPDWORD)HeapAlloc(g_hHeap, 0, pThreads->capacity * sizeof(DWORD)); + if (pThreads->pItems == NULL) + break; + } + else if (pThreads->size >= pThreads->capacity) + { + LPDWORD p = (LPDWORD)HeapReAlloc( + g_hHeap, 0, pThreads->pItems, (pThreads->capacity * 2) * sizeof(DWORD)); + if (p == NULL) + break; + + pThreads->capacity *= 2; + pThreads->pItems = p; + } + pThreads->pItems[pThreads->size++] = te.th32ThreadID; + } + + te.dwSize = sizeof(THREADENTRY32); + } while (Thread32Next(hSnapshot, &te)); + } + CloseHandle(hSnapshot); + } +} + +//------------------------------------------------------------------------- +static VOID Freeze(PFROZEN_THREADS pThreads, UINT pos, UINT action) +{ + pThreads->pItems = NULL; + pThreads->capacity = 0; + pThreads->size = 0; + EnumerateThreads(pThreads); + + if (pThreads->pItems != NULL) + { + UINT i; + for (i = 0; i < pThreads->size; ++i) + { + HANDLE hThread = OpenThread(THREAD_ACCESS, FALSE, pThreads->pItems[i]); + if (hThread != NULL) + { + SuspendThread(hThread); + ProcessThreadIPs(hThread, pos, action); + CloseHandle(hThread); + } + } + } +} + +//------------------------------------------------------------------------- +static VOID Unfreeze(PFROZEN_THREADS pThreads) +{ + if (pThreads->pItems != NULL) + { + UINT i; + for (i = 0; i < pThreads->size; ++i) + { + HANDLE hThread = OpenThread(THREAD_ACCESS, FALSE, pThreads->pItems[i]); + if (hThread != NULL) + { + ResumeThread(hThread); + CloseHandle(hThread); + } + } + + HeapFree(g_hHeap, 0, pThreads->pItems); + } +} + +//------------------------------------------------------------------------- +static MH_STATUS EnableHookLL(UINT pos, BOOL enable) +{ + PHOOK_ENTRY pHook = &g_hooks.pItems[pos]; + DWORD oldProtect; + SIZE_T patchSize = sizeof(JMP_REL); + LPBYTE pPatchTarget = (LPBYTE)pHook->pTarget; + + if (pHook->patchAbove) + { + pPatchTarget -= sizeof(JMP_REL); + patchSize += sizeof(JMP_REL_SHORT); + } + + if (!VirtualProtect(pPatchTarget, patchSize, PAGE_EXECUTE_READWRITE, &oldProtect)) + return MH_ERROR_MEMORY_PROTECT; + + if (enable) + { + PJMP_REL pJmp = (PJMP_REL)pPatchTarget; + pJmp->opcode = 0xE9; + pJmp->operand = (UINT32)((LPBYTE)pHook->pDetour - (pPatchTarget + sizeof(JMP_REL))); + + if (pHook->patchAbove) + { + PJMP_REL_SHORT pShortJmp = (PJMP_REL_SHORT)pHook->pTarget; + pShortJmp->opcode = 0xEB; + pShortJmp->operand = (UINT8)(0 - (sizeof(JMP_REL_SHORT) + sizeof(JMP_REL))); + } + } + else + { + if (pHook->patchAbove) + memcpy(pPatchTarget, pHook->backup, sizeof(JMP_REL) + sizeof(JMP_REL_SHORT)); + else + memcpy(pPatchTarget, pHook->backup, sizeof(JMP_REL)); + } + + VirtualProtect(pPatchTarget, patchSize, oldProtect, &oldProtect); + + // Just-in-case measure. + FlushInstructionCache(GetCurrentProcess(), pPatchTarget, patchSize); + + pHook->isEnabled = enable; + pHook->queueEnable = enable; + + return MH_OK; +} + +//------------------------------------------------------------------------- +static MH_STATUS EnableAllHooksLL(BOOL enable) +{ + MH_STATUS status = MH_OK; + UINT i, first = INVALID_HOOK_POS; + + for (i = 0; i < g_hooks.size; ++i) + { + if (g_hooks.pItems[i].isEnabled != enable) + { + first = i; + break; + } + } + + if (first != INVALID_HOOK_POS) + { + FROZEN_THREADS threads; + Freeze(&threads, ALL_HOOKS_POS, enable ? ACTION_ENABLE : ACTION_DISABLE); + + for (i = first; i < g_hooks.size; ++i) + { + if (g_hooks.pItems[i].isEnabled != enable) + { + status = EnableHookLL(i, enable); + if (status != MH_OK) + break; + } + } + + Unfreeze(&threads); + } + + return status; +} + +//------------------------------------------------------------------------- +static VOID EnterSpinLock(VOID) +{ + SIZE_T spinCount = 0; + + // Wait until the flag is FALSE. + while (InterlockedCompareExchange(&g_isLocked, TRUE, FALSE) != FALSE) + { + // No need to generate a memory barrier here, since InterlockedCompareExchange() + // generates a full memory barrier itself. + + // Prevent the loop from being too busy. + if (spinCount < 32) + Sleep(0); + else + Sleep(1); + + spinCount++; + } +} + +//------------------------------------------------------------------------- +static VOID LeaveSpinLock(VOID) +{ + // No need to generate a memory barrier here, since InterlockedExchange() + // generates a full memory barrier itself. + + InterlockedExchange(&g_isLocked, FALSE); +} + +//------------------------------------------------------------------------- +MH_STATUS WINAPI MH_Initialize(VOID) +{ + MH_STATUS status = MH_OK; + + EnterSpinLock(); + + if (g_hHeap == NULL) + { + g_hHeap = HeapCreate(0, 0, 0); + if (g_hHeap != NULL) + { + // Initialize the internal function buffer. + InitializeBuffer(); + } + else + { + status = MH_ERROR_MEMORY_ALLOC; + } + } + else + { + status = MH_ERROR_ALREADY_INITIALIZED; + } + + LeaveSpinLock(); + + return status; +} + +//------------------------------------------------------------------------- +MH_STATUS WINAPI MH_Uninitialize(VOID) +{ + MH_STATUS status = MH_OK; + + EnterSpinLock(); + + if (g_hHeap != NULL) + { + status = EnableAllHooksLL(FALSE); + if (status == MH_OK) + { + // Free the internal function buffer. + + // HeapFree is actually not required, but some tools detect a false + // memory leak without HeapFree. + + UninitializeBuffer(); + + HeapFree(g_hHeap, 0, g_hooks.pItems); + HeapDestroy(g_hHeap); + + g_hHeap = NULL; + + g_hooks.pItems = NULL; + g_hooks.capacity = 0; + g_hooks.size = 0; + } + } + else + { + status = MH_ERROR_NOT_INITIALIZED; + } + + LeaveSpinLock(); + + return status; +} + +//------------------------------------------------------------------------- +MH_STATUS WINAPI MH_CreateHook(LPVOID pTarget, LPVOID pDetour, LPVOID *ppOriginal) +{ + MH_STATUS status = MH_OK; + + EnterSpinLock(); + + if (g_hHeap != NULL) + { + if (IsExecutableAddress(pTarget) && IsExecutableAddress(pDetour)) + { + UINT pos = FindHookEntry(pTarget); + if (pos == INVALID_HOOK_POS) + { + LPVOID pBuffer = AllocateBuffer(pTarget); + if (pBuffer != NULL) + { + TRAMPOLINE ct; + + ct.pTarget = pTarget; + ct.pDetour = pDetour; + ct.pTrampoline = pBuffer; + if (CreateTrampolineFunction(&ct)) + { + PHOOK_ENTRY pHook = AddHookEntry(); + if (pHook != NULL) + { + pHook->pTarget = ct.pTarget; +#if defined(_M_X64) || defined(__x86_64__) + pHook->pDetour = ct.pRelay; +#else + pHook->pDetour = ct.pDetour; +#endif + pHook->pTrampoline = ct.pTrampoline; + pHook->patchAbove = ct.patchAbove; + pHook->isEnabled = FALSE; + pHook->queueEnable = FALSE; + pHook->nIP = ct.nIP; + memcpy(pHook->oldIPs, ct.oldIPs, ARRAYSIZE(ct.oldIPs)); + memcpy(pHook->newIPs, ct.newIPs, ARRAYSIZE(ct.newIPs)); + + // Back up the target function. + + if (ct.patchAbove) + { + memcpy( + pHook->backup, + (LPBYTE)pTarget - sizeof(JMP_REL), + sizeof(JMP_REL) + sizeof(JMP_REL_SHORT)); + } + else + { + memcpy(pHook->backup, pTarget, sizeof(JMP_REL)); + } + + if (ppOriginal != NULL) + *ppOriginal = pHook->pTrampoline; + } + else + { + status = MH_ERROR_MEMORY_ALLOC; + } + } + else + { + status = MH_ERROR_UNSUPPORTED_FUNCTION; + } + + if (status != MH_OK) + { + FreeBuffer(pBuffer); + } + } + else + { + status = MH_ERROR_MEMORY_ALLOC; + } + } + else + { + status = MH_ERROR_ALREADY_CREATED; + } + } + else + { + status = MH_ERROR_NOT_EXECUTABLE; + } + } + else + { + status = MH_ERROR_NOT_INITIALIZED; + } + + LeaveSpinLock(); + + return status; +} + +//------------------------------------------------------------------------- +MH_STATUS WINAPI MH_RemoveHook(LPVOID pTarget) +{ + MH_STATUS status = MH_OK; + + EnterSpinLock(); + + if (g_hHeap != NULL) + { + UINT pos = FindHookEntry(pTarget); + if (pos != INVALID_HOOK_POS) + { + if (g_hooks.pItems[pos].isEnabled) + { + FROZEN_THREADS threads; + Freeze(&threads, pos, ACTION_DISABLE); + + status = EnableHookLL(pos, FALSE); + + Unfreeze(&threads); + } + + if (status == MH_OK) + { + FreeBuffer(g_hooks.pItems[pos].pTrampoline); + DeleteHookEntry(pos); + } + } + else + { + status = MH_ERROR_NOT_CREATED; + } + } + else + { + status = MH_ERROR_NOT_INITIALIZED; + } + + LeaveSpinLock(); + + return status; +} + +//------------------------------------------------------------------------- +static MH_STATUS EnableHook(LPVOID pTarget, BOOL enable) +{ + MH_STATUS status = MH_OK; + + EnterSpinLock(); + + if (g_hHeap != NULL) + { + if (pTarget == MH_ALL_HOOKS) + { + status = EnableAllHooksLL(enable); + } + else + { + FROZEN_THREADS threads; + UINT pos = FindHookEntry(pTarget); + if (pos != INVALID_HOOK_POS) + { + if (g_hooks.pItems[pos].isEnabled != enable) + { + Freeze(&threads, pos, ACTION_ENABLE); + + status = EnableHookLL(pos, enable); + + Unfreeze(&threads); + } + else + { + status = enable ? MH_ERROR_ENABLED : MH_ERROR_DISABLED; + } + } + else + { + status = MH_ERROR_NOT_CREATED; + } + } + } + else + { + status = MH_ERROR_NOT_INITIALIZED; + } + + LeaveSpinLock(); + + return status; +} + +//------------------------------------------------------------------------- +MH_STATUS WINAPI MH_EnableHook(LPVOID pTarget) +{ + return EnableHook(pTarget, TRUE); +} + +//------------------------------------------------------------------------- +MH_STATUS WINAPI MH_DisableHook(LPVOID pTarget) +{ + return EnableHook(pTarget, FALSE); +} + +//------------------------------------------------------------------------- +static MH_STATUS QueueHook(LPVOID pTarget, BOOL queueEnable) +{ + MH_STATUS status = MH_OK; + + EnterSpinLock(); + + if (g_hHeap != NULL) + { + if (pTarget == MH_ALL_HOOKS) + { + UINT i; + for (i = 0; i < g_hooks.size; ++i) + g_hooks.pItems[i].queueEnable = queueEnable; + } + else + { + UINT pos = FindHookEntry(pTarget); + if (pos != INVALID_HOOK_POS) + { + g_hooks.pItems[pos].queueEnable = queueEnable; + } + else + { + status = MH_ERROR_NOT_CREATED; + } + } + } + else + { + status = MH_ERROR_NOT_INITIALIZED; + } + + LeaveSpinLock(); + + return status; +} + +//------------------------------------------------------------------------- +MH_STATUS WINAPI MH_QueueEnableHook(LPVOID pTarget) +{ + return QueueHook(pTarget, TRUE); +} + +//------------------------------------------------------------------------- +MH_STATUS WINAPI MH_QueueDisableHook(LPVOID pTarget) +{ + return QueueHook(pTarget, FALSE); +} + +//------------------------------------------------------------------------- +MH_STATUS WINAPI MH_ApplyQueued(VOID) +{ + MH_STATUS status = MH_OK; + UINT i, first = INVALID_HOOK_POS; + + EnterSpinLock(); + + if (g_hHeap != NULL) + { + for (i = 0; i < g_hooks.size; ++i) + { + if (g_hooks.pItems[i].isEnabled != g_hooks.pItems[i].queueEnable) + { + first = i; + break; + } + } + + if (first != INVALID_HOOK_POS) + { + FROZEN_THREADS threads; + Freeze(&threads, ALL_HOOKS_POS, ACTION_APPLY_QUEUED); + + for (i = first; i < g_hooks.size; ++i) + { + PHOOK_ENTRY pHook = &g_hooks.pItems[i]; + if (pHook->isEnabled != pHook->queueEnable) + { + status = EnableHookLL(i, pHook->queueEnable); + if (status != MH_OK) + break; + } + } + + Unfreeze(&threads); + } + } + else + { + status = MH_ERROR_NOT_INITIALIZED; + } + + LeaveSpinLock(); + + return status; +} + +//------------------------------------------------------------------------- +MH_STATUS WINAPI MH_CreateHookApiEx( + LPCWSTR pszModule, LPCSTR pszProcName, LPVOID pDetour, + LPVOID *ppOriginal, LPVOID *ppTarget) +{ + HMODULE hModule; + LPVOID pTarget; + + hModule = GetModuleHandleW(pszModule); + if (hModule == NULL) + return MH_ERROR_MODULE_NOT_FOUND; + + pTarget = (LPVOID)GetProcAddress(hModule, pszProcName); + if (pTarget == NULL) + return MH_ERROR_FUNCTION_NOT_FOUND; + + if(ppTarget != NULL) + *ppTarget = pTarget; + + return MH_CreateHook(pTarget, pDetour, ppOriginal); +} + +//------------------------------------------------------------------------- +MH_STATUS WINAPI MH_CreateHookApi( + LPCWSTR pszModule, LPCSTR pszProcName, LPVOID pDetour, LPVOID *ppOriginal) +{ + return MH_CreateHookApiEx(pszModule, pszProcName, pDetour, ppOriginal, NULL); +} + +//------------------------------------------------------------------------- +const char * WINAPI MH_StatusToString(MH_STATUS status) +{ +#define MH_ST2STR(x) \ + case x: \ + return #x; + + switch (status) { + MH_ST2STR(MH_UNKNOWN) + MH_ST2STR(MH_OK) + MH_ST2STR(MH_ERROR_ALREADY_INITIALIZED) + MH_ST2STR(MH_ERROR_NOT_INITIALIZED) + MH_ST2STR(MH_ERROR_ALREADY_CREATED) + MH_ST2STR(MH_ERROR_NOT_CREATED) + MH_ST2STR(MH_ERROR_ENABLED) + MH_ST2STR(MH_ERROR_DISABLED) + MH_ST2STR(MH_ERROR_NOT_EXECUTABLE) + MH_ST2STR(MH_ERROR_UNSUPPORTED_FUNCTION) + MH_ST2STR(MH_ERROR_MEMORY_ALLOC) + MH_ST2STR(MH_ERROR_MEMORY_PROTECT) + MH_ST2STR(MH_ERROR_MODULE_NOT_FOUND) + MH_ST2STR(MH_ERROR_FUNCTION_NOT_FOUND) + } + +#undef MH_ST2STR + + return "(unknown)"; +} diff --git a/minhook/include/MinHook.h b/minhook/include/MinHook.h new file mode 100644 index 0000000..15c0a87 --- /dev/null +++ b/minhook/include/MinHook.h @@ -0,0 +1,186 @@ +/* + * MinHook - The Minimalistic API Hooking Library for x64/x86 + * Copyright (C) 2009-2017 Tsuda Kageyu. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if !(defined _M_IX86) && !(defined _M_X64) && !(defined __i386__) && !(defined __x86_64__) + #error MinHook supports only x86 and x64 systems. +#endif + +#include + +// MinHook Error Codes. +typedef enum MH_STATUS +{ + // Unknown error. Should not be returned. + MH_UNKNOWN = -1, + + // Successful. + MH_OK = 0, + + // MinHook is already initialized. + MH_ERROR_ALREADY_INITIALIZED, + + // MinHook is not initialized yet, or already uninitialized. + MH_ERROR_NOT_INITIALIZED, + + // The hook for the specified target function is already created. + MH_ERROR_ALREADY_CREATED, + + // The hook for the specified target function is not created yet. + MH_ERROR_NOT_CREATED, + + // The hook for the specified target function is already enabled. + MH_ERROR_ENABLED, + + // The hook for the specified target function is not enabled yet, or already + // disabled. + MH_ERROR_DISABLED, + + // The specified pointer is invalid. It points the address of non-allocated + // and/or non-executable region. + MH_ERROR_NOT_EXECUTABLE, + + // The specified target function cannot be hooked. + MH_ERROR_UNSUPPORTED_FUNCTION, + + // Failed to allocate memory. + MH_ERROR_MEMORY_ALLOC, + + // Failed to change the memory protection. + MH_ERROR_MEMORY_PROTECT, + + // The specified module is not loaded. + MH_ERROR_MODULE_NOT_FOUND, + + // The specified function is not found. + MH_ERROR_FUNCTION_NOT_FOUND +} +MH_STATUS; + +// Can be passed as a parameter to MH_EnableHook, MH_DisableHook, +// MH_QueueEnableHook or MH_QueueDisableHook. +#define MH_ALL_HOOKS NULL + +#ifdef __cplusplus +extern "C" { +#endif + + // Initialize the MinHook library. You must call this function EXACTLY ONCE + // at the beginning of your program. + MH_STATUS WINAPI MH_Initialize(VOID); + + // Uninitialize the MinHook library. You must call this function EXACTLY + // ONCE at the end of your program. + MH_STATUS WINAPI MH_Uninitialize(VOID); + + // Creates a Hook for the specified target function, in disabled state. + // Parameters: + // pTarget [in] A pointer to the target function, which will be + // overridden by the detour function. + // pDetour [in] A pointer to the detour function, which will override + // the target function. + // ppOriginal [out] A pointer to the trampoline function, which will be + // used to call the original target function. + // This parameter can be NULL. + MH_STATUS WINAPI MH_CreateHook(LPVOID pTarget, LPVOID pDetour, LPVOID *ppOriginal); + + // Creates a Hook for the specified API function, in disabled state. + // Parameters: + // pszModule [in] A pointer to the loaded module name which contains the + // target function. + // pszTarget [in] A pointer to the target function name, which will be + // overridden by the detour function. + // pDetour [in] A pointer to the detour function, which will override + // the target function. + // ppOriginal [out] A pointer to the trampoline function, which will be + // used to call the original target function. + // This parameter can be NULL. + MH_STATUS WINAPI MH_CreateHookApi( + LPCWSTR pszModule, LPCSTR pszProcName, LPVOID pDetour, LPVOID *ppOriginal); + + // Creates a Hook for the specified API function, in disabled state. + // Parameters: + // pszModule [in] A pointer to the loaded module name which contains the + // target function. + // pszTarget [in] A pointer to the target function name, which will be + // overridden by the detour function. + // pDetour [in] A pointer to the detour function, which will override + // the target function. + // ppOriginal [out] A pointer to the trampoline function, which will be + // used to call the original target function. + // This parameter can be NULL. + // ppTarget [out] A pointer to the target function, which will be used + // with other functions. + // This parameter can be NULL. + MH_STATUS WINAPI MH_CreateHookApiEx( + LPCWSTR pszModule, LPCSTR pszProcName, LPVOID pDetour, LPVOID *ppOriginal, LPVOID *ppTarget); + + // Removes an already created hook. + // Parameters: + // pTarget [in] A pointer to the target function. + MH_STATUS WINAPI MH_RemoveHook(LPVOID pTarget); + + // Enables an already created hook. + // Parameters: + // pTarget [in] A pointer to the target function. + // If this parameter is MH_ALL_HOOKS, all created hooks are + // enabled in one go. + MH_STATUS WINAPI MH_EnableHook(LPVOID pTarget); + + // Disables an already created hook. + // Parameters: + // pTarget [in] A pointer to the target function. + // If this parameter is MH_ALL_HOOKS, all created hooks are + // disabled in one go. + MH_STATUS WINAPI MH_DisableHook(LPVOID pTarget); + + // Queues to enable an already created hook. + // Parameters: + // pTarget [in] A pointer to the target function. + // If this parameter is MH_ALL_HOOKS, all created hooks are + // queued to be enabled. + MH_STATUS WINAPI MH_QueueEnableHook(LPVOID pTarget); + + // Queues to disable an already created hook. + // Parameters: + // pTarget [in] A pointer to the target function. + // If this parameter is MH_ALL_HOOKS, all created hooks are + // queued to be disabled. + MH_STATUS WINAPI MH_QueueDisableHook(LPVOID pTarget); + + // Applies all queued changes in one go. + MH_STATUS WINAPI MH_ApplyQueued(VOID); + + // Translates the MH_STATUS to its name as a string. + const char * WINAPI MH_StatusToString(MH_STATUS status); + +#ifdef __cplusplus +} +#endif + diff --git a/minhook/pstdint.h b/minhook/pstdint.h new file mode 100644 index 0000000..84d82a0 --- /dev/null +++ b/minhook/pstdint.h @@ -0,0 +1,39 @@ +/* + * MinHook - The Minimalistic API Hooking Library for x64/x86 + * Copyright (C) 2009-2017 Tsuda Kageyu. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include + +// Integer types for HDE. +typedef INT8 int8_t; +typedef INT16 int16_t; +typedef INT32 int32_t; +typedef INT64 int64_t; +typedef UINT8 uint8_t; +typedef UINT16 uint16_t; +typedef UINT32 uint32_t; +typedef UINT64 uint64_t; diff --git a/minhook/table32.h b/minhook/table32.h new file mode 100644 index 0000000..7b3e12e --- /dev/null +++ b/minhook/table32.h @@ -0,0 +1,73 @@ +/* + * Hacker Disassembler Engine 32 C + * Copyright (c) 2008-2009, Vyacheslav Patkov. + * All rights reserved. + * + */ + +#define C_NONE 0x00 +#define C_MODRM 0x01 +#define C_IMM8 0x02 +#define C_IMM16 0x04 +#define C_IMM_P66 0x10 +#define C_REL8 0x20 +#define C_REL32 0x40 +#define C_GROUP 0x80 +#define C_ERROR 0xff + +#define PRE_ANY 0x00 +#define PRE_NONE 0x01 +#define PRE_F2 0x02 +#define PRE_F3 0x04 +#define PRE_66 0x08 +#define PRE_67 0x10 +#define PRE_LOCK 0x20 +#define PRE_SEG 0x40 +#define PRE_ALL 0xff + +#define DELTA_OPCODES 0x4a +#define DELTA_FPU_REG 0xf1 +#define DELTA_FPU_MODRM 0xf8 +#define DELTA_PREFIXES 0x130 +#define DELTA_OP_LOCK_OK 0x1a1 +#define DELTA_OP2_LOCK_OK 0x1b9 +#define DELTA_OP_ONLY_MEM 0x1cb +#define DELTA_OP2_ONLY_MEM 0x1da + +unsigned char hde32_table[] = { + 0xa3,0xa8,0xa3,0xa8,0xa3,0xa8,0xa3,0xa8,0xa3,0xa8,0xa3,0xa8,0xa3,0xa8,0xa3, + 0xa8,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xac,0xaa,0xb2,0xaa,0x9f,0x9f, + 0x9f,0x9f,0xb5,0xa3,0xa3,0xa4,0xaa,0xaa,0xba,0xaa,0x96,0xaa,0xa8,0xaa,0xc3, + 0xc3,0x96,0x96,0xb7,0xae,0xd6,0xbd,0xa3,0xc5,0xa3,0xa3,0x9f,0xc3,0x9c,0xaa, + 0xaa,0xac,0xaa,0xbf,0x03,0x7f,0x11,0x7f,0x01,0x7f,0x01,0x3f,0x01,0x01,0x90, + 0x82,0x7d,0x97,0x59,0x59,0x59,0x59,0x59,0x7f,0x59,0x59,0x60,0x7d,0x7f,0x7f, + 0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x9a,0x88,0x7d, + 0x59,0x50,0x50,0x50,0x50,0x59,0x59,0x59,0x59,0x61,0x94,0x61,0x9e,0x59,0x59, + 0x85,0x59,0x92,0xa3,0x60,0x60,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59, + 0x59,0x59,0x9f,0x01,0x03,0x01,0x04,0x03,0xd5,0x03,0xcc,0x01,0xbc,0x03,0xf0, + 0x10,0x10,0x10,0x10,0x50,0x50,0x50,0x50,0x14,0x20,0x20,0x20,0x20,0x01,0x01, + 0x01,0x01,0xc4,0x02,0x10,0x00,0x00,0x00,0x00,0x01,0x01,0xc0,0xc2,0x10,0x11, + 0x02,0x03,0x11,0x03,0x03,0x04,0x00,0x00,0x14,0x00,0x02,0x00,0x00,0xc6,0xc8, + 0x02,0x02,0x02,0x02,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0xff,0xca, + 0x01,0x01,0x01,0x00,0x06,0x00,0x04,0x00,0xc0,0xc2,0x01,0x01,0x03,0x01,0xff, + 0xff,0x01,0x00,0x03,0xc4,0xc4,0xc6,0x03,0x01,0x01,0x01,0xff,0x03,0x03,0x03, + 0xc8,0x40,0x00,0x0a,0x00,0x04,0x00,0x00,0x00,0x00,0x7f,0x00,0x33,0x01,0x00, + 0x00,0x00,0x00,0x00,0x00,0xff,0xbf,0xff,0xff,0x00,0x00,0x00,0x00,0x07,0x00, + 0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0xff,0xff,0x00,0x00,0x00,0xbf,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x7f,0x00,0x00,0xff,0x4a,0x4a,0x4a,0x4a,0x4b,0x52,0x4a,0x4a,0x4a,0x4a,0x4f, + 0x4c,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a,0x55,0x45,0x40,0x4a,0x4a,0x4a, + 0x45,0x59,0x4d,0x46,0x4a,0x5d,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a, + 0x4a,0x4a,0x4a,0x4a,0x4a,0x61,0x63,0x67,0x4e,0x4a,0x4a,0x6b,0x6d,0x4a,0x4a, + 0x45,0x6d,0x4a,0x4a,0x44,0x45,0x4a,0x4a,0x00,0x00,0x00,0x02,0x0d,0x06,0x06, + 0x06,0x06,0x0e,0x00,0x00,0x00,0x00,0x06,0x06,0x06,0x00,0x06,0x06,0x02,0x06, + 0x00,0x0a,0x0a,0x07,0x07,0x06,0x02,0x05,0x05,0x02,0x02,0x00,0x00,0x04,0x04, + 0x04,0x04,0x00,0x00,0x00,0x0e,0x05,0x06,0x06,0x06,0x01,0x06,0x00,0x00,0x08, + 0x00,0x10,0x00,0x18,0x00,0x20,0x00,0x28,0x00,0x30,0x00,0x80,0x01,0x82,0x01, + 0x86,0x00,0xf6,0xcf,0xfe,0x3f,0xab,0x00,0xb0,0x00,0xb1,0x00,0xb3,0x00,0xba, + 0xf8,0xbb,0x00,0xc0,0x00,0xc1,0x00,0xc7,0xbf,0x62,0xff,0x00,0x8d,0xff,0x00, + 0xc4,0xff,0x00,0xc5,0xff,0x00,0xff,0xff,0xeb,0x01,0xff,0x0e,0x12,0x08,0x00, + 0x13,0x09,0x00,0x16,0x08,0x00,0x17,0x09,0x00,0x2b,0x09,0x00,0xae,0xff,0x07, + 0xb2,0xff,0x00,0xb4,0xff,0x00,0xb5,0xff,0x00,0xc3,0x01,0x00,0xc7,0xff,0xbf, + 0xe7,0x08,0x00,0xf0,0x02,0x00 +}; diff --git a/minhook/table64.h b/minhook/table64.h new file mode 100644 index 0000000..01d4541 --- /dev/null +++ b/minhook/table64.h @@ -0,0 +1,74 @@ +/* + * Hacker Disassembler Engine 64 C + * Copyright (c) 2008-2009, Vyacheslav Patkov. + * All rights reserved. + * + */ + +#define C_NONE 0x00 +#define C_MODRM 0x01 +#define C_IMM8 0x02 +#define C_IMM16 0x04 +#define C_IMM_P66 0x10 +#define C_REL8 0x20 +#define C_REL32 0x40 +#define C_GROUP 0x80 +#define C_ERROR 0xff + +#define PRE_ANY 0x00 +#define PRE_NONE 0x01 +#define PRE_F2 0x02 +#define PRE_F3 0x04 +#define PRE_66 0x08 +#define PRE_67 0x10 +#define PRE_LOCK 0x20 +#define PRE_SEG 0x40 +#define PRE_ALL 0xff + +#define DELTA_OPCODES 0x4a +#define DELTA_FPU_REG 0xfd +#define DELTA_FPU_MODRM 0x104 +#define DELTA_PREFIXES 0x13c +#define DELTA_OP_LOCK_OK 0x1ae +#define DELTA_OP2_LOCK_OK 0x1c6 +#define DELTA_OP_ONLY_MEM 0x1d8 +#define DELTA_OP2_ONLY_MEM 0x1e7 + +unsigned char hde64_table[] = { + 0xa5,0xaa,0xa5,0xb8,0xa5,0xaa,0xa5,0xaa,0xa5,0xb8,0xa5,0xb8,0xa5,0xb8,0xa5, + 0xb8,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xac,0xc0,0xcc,0xc0,0xa1,0xa1, + 0xa1,0xa1,0xb1,0xa5,0xa5,0xa6,0xc0,0xc0,0xd7,0xda,0xe0,0xc0,0xe4,0xc0,0xea, + 0xea,0xe0,0xe0,0x98,0xc8,0xee,0xf1,0xa5,0xd3,0xa5,0xa5,0xa1,0xea,0x9e,0xc0, + 0xc0,0xc2,0xc0,0xe6,0x03,0x7f,0x11,0x7f,0x01,0x7f,0x01,0x3f,0x01,0x01,0xab, + 0x8b,0x90,0x64,0x5b,0x5b,0x5b,0x5b,0x5b,0x92,0x5b,0x5b,0x76,0x90,0x92,0x92, + 0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x6a,0x73,0x90, + 0x5b,0x52,0x52,0x52,0x52,0x5b,0x5b,0x5b,0x5b,0x77,0x7c,0x77,0x85,0x5b,0x5b, + 0x70,0x5b,0x7a,0xaf,0x76,0x76,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b, + 0x5b,0x5b,0x86,0x01,0x03,0x01,0x04,0x03,0xd5,0x03,0xd5,0x03,0xcc,0x01,0xbc, + 0x03,0xf0,0x03,0x03,0x04,0x00,0x50,0x50,0x50,0x50,0xff,0x20,0x20,0x20,0x20, + 0x01,0x01,0x01,0x01,0xc4,0x02,0x10,0xff,0xff,0xff,0x01,0x00,0x03,0x11,0xff, + 0x03,0xc4,0xc6,0xc8,0x02,0x10,0x00,0xff,0xcc,0x01,0x01,0x01,0x00,0x00,0x00, + 0x00,0x01,0x01,0x03,0x01,0xff,0xff,0xc0,0xc2,0x10,0x11,0x02,0x03,0x01,0x01, + 0x01,0xff,0xff,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0xff,0xff,0xff,0x10, + 0x10,0x10,0x10,0x02,0x10,0x00,0x00,0xc6,0xc8,0x02,0x02,0x02,0x02,0x06,0x00, + 0x04,0x00,0x02,0xff,0x00,0xc0,0xc2,0x01,0x01,0x03,0x03,0x03,0xca,0x40,0x00, + 0x0a,0x00,0x04,0x00,0x00,0x00,0x00,0x7f,0x00,0x33,0x01,0x00,0x00,0x00,0x00, + 0x00,0x00,0xff,0xbf,0xff,0xff,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0xff,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff, + 0x00,0x00,0x00,0xbf,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7f,0x00,0x00, + 0xff,0x40,0x40,0x40,0x40,0x41,0x49,0x40,0x40,0x40,0x40,0x4c,0x42,0x40,0x40, + 0x40,0x40,0x40,0x40,0x40,0x40,0x4f,0x44,0x53,0x40,0x40,0x40,0x44,0x57,0x43, + 0x5c,0x40,0x60,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40, + 0x40,0x40,0x64,0x66,0x6e,0x6b,0x40,0x40,0x6a,0x46,0x40,0x40,0x44,0x46,0x40, + 0x40,0x5b,0x44,0x40,0x40,0x00,0x00,0x00,0x00,0x06,0x06,0x06,0x06,0x01,0x06, + 0x06,0x02,0x06,0x06,0x00,0x06,0x00,0x0a,0x0a,0x00,0x00,0x00,0x02,0x07,0x07, + 0x06,0x02,0x0d,0x06,0x06,0x06,0x0e,0x05,0x05,0x02,0x02,0x00,0x00,0x04,0x04, + 0x04,0x04,0x05,0x06,0x06,0x06,0x00,0x00,0x00,0x0e,0x00,0x00,0x08,0x00,0x10, + 0x00,0x18,0x00,0x20,0x00,0x28,0x00,0x30,0x00,0x80,0x01,0x82,0x01,0x86,0x00, + 0xf6,0xcf,0xfe,0x3f,0xab,0x00,0xb0,0x00,0xb1,0x00,0xb3,0x00,0xba,0xf8,0xbb, + 0x00,0xc0,0x00,0xc1,0x00,0xc7,0xbf,0x62,0xff,0x00,0x8d,0xff,0x00,0xc4,0xff, + 0x00,0xc5,0xff,0x00,0xff,0xff,0xeb,0x01,0xff,0x0e,0x12,0x08,0x00,0x13,0x09, + 0x00,0x16,0x08,0x00,0x17,0x09,0x00,0x2b,0x09,0x00,0xae,0xff,0x07,0xb2,0xff, + 0x00,0xb4,0xff,0x00,0xb5,0xff,0x00,0xc3,0x01,0x00,0xc7,0xff,0xbf,0xe7,0x08, + 0x00,0xf0,0x02,0x00 +}; diff --git a/minhook/trampoline.c b/minhook/trampoline.c new file mode 100644 index 0000000..b707411 --- /dev/null +++ b/minhook/trampoline.c @@ -0,0 +1,316 @@ +/* + * MinHook - The Minimalistic API Hooking Library for x64/x86 + * Copyright (C) 2009-2017 Tsuda Kageyu. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#ifndef ARRAYSIZE + #define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0])) +#endif + +#if defined(_M_X64) || defined(__x86_64__) + #include "hde64.h" + typedef hde64s HDE; + #define HDE_DISASM(code, hs) hde64_disasm(code, hs) +#else + #include "hde32.h" + typedef hde32s HDE; + #define HDE_DISASM(code, hs) hde32_disasm(code, hs) +#endif + +#include "trampoline.h" +#include "buffer.h" + +// Maximum size of a trampoline function. +#if defined(_M_X64) || defined(__x86_64__) + #define TRAMPOLINE_MAX_SIZE (MEMORY_SLOT_SIZE - sizeof(JMP_ABS)) +#else + #define TRAMPOLINE_MAX_SIZE MEMORY_SLOT_SIZE +#endif + +//------------------------------------------------------------------------- +static BOOL IsCodePadding(LPBYTE pInst, UINT size) +{ + UINT i; + + if (pInst[0] != 0x00 && pInst[0] != 0x90 && pInst[0] != 0xCC) + return FALSE; + + for (i = 1; i < size; ++i) + { + if (pInst[i] != pInst[0]) + return FALSE; + } + return TRUE; +} + +//------------------------------------------------------------------------- +BOOL CreateTrampolineFunction(PTRAMPOLINE ct) +{ +#if defined(_M_X64) || defined(__x86_64__) + CALL_ABS call = { + 0xFF, 0x15, 0x00000002, // FF15 00000002: CALL [RIP+8] + 0xEB, 0x08, // EB 08: JMP +10 + 0x0000000000000000ULL // Absolute destination address + }; + JMP_ABS jmp = { + 0xFF, 0x25, 0x00000000, // FF25 00000000: JMP [RIP+6] + 0x0000000000000000ULL // Absolute destination address + }; + JCC_ABS jcc = { + 0x70, 0x0E, // 7* 0E: J** +16 + 0xFF, 0x25, 0x00000000, // FF25 00000000: JMP [RIP+6] + 0x0000000000000000ULL // Absolute destination address + }; +#else + CALL_REL call = { + 0xE8, // E8 xxxxxxxx: CALL +5+xxxxxxxx + 0x00000000 // Relative destination address + }; + JMP_REL jmp = { + 0xE9, // E9 xxxxxxxx: JMP +5+xxxxxxxx + 0x00000000 // Relative destination address + }; + JCC_REL jcc = { + 0x0F, 0x80, // 0F8* xxxxxxxx: J** +6+xxxxxxxx + 0x00000000 // Relative destination address + }; +#endif + + UINT8 oldPos = 0; + UINT8 newPos = 0; + ULONG_PTR jmpDest = 0; // Destination address of an internal jump. + BOOL finished = FALSE; // Is the function completed? +#if defined(_M_X64) || defined(__x86_64__) + UINT8 instBuf[16]; +#endif + + ct->patchAbove = FALSE; + ct->nIP = 0; + + do + { + HDE hs; + UINT copySize; + LPVOID pCopySrc; + ULONG_PTR pOldInst = (ULONG_PTR)ct->pTarget + oldPos; + ULONG_PTR pNewInst = (ULONG_PTR)ct->pTrampoline + newPos; + + copySize = HDE_DISASM((LPVOID)pOldInst, &hs); + if (hs.flags & F_ERROR) + return FALSE; + + pCopySrc = (LPVOID)pOldInst; + if (oldPos >= sizeof(JMP_REL)) + { + // The trampoline function is long enough. + // Complete the function with the jump to the target function. +#if defined(_M_X64) || defined(__x86_64__) + jmp.address = pOldInst; +#else + jmp.operand = (UINT32)(pOldInst - (pNewInst + sizeof(jmp))); +#endif + pCopySrc = &jmp; + copySize = sizeof(jmp); + + finished = TRUE; + } +#if defined(_M_X64) || defined(__x86_64__) + else if ((hs.modrm & 0xC7) == 0x05) + { + // Instructions using RIP relative addressing. (ModR/M = 00???101B) + + // Modify the RIP relative address. + PUINT32 pRelAddr; + + // Avoid using memcpy to reduce the footprint. +#ifndef _MSC_VER + memcpy(instBuf, (LPBYTE)pOldInst, copySize); +#else + __movsb(instBuf, (LPBYTE)pOldInst, copySize); +#endif + pCopySrc = instBuf; + + // Relative address is stored at (instruction length - immediate value length - 4). + pRelAddr = (PUINT32)(instBuf + hs.len - ((hs.flags & 0x3C) >> 2) - 4); + *pRelAddr + = (UINT32)((pOldInst + hs.len + (INT32)hs.disp.disp32) - (pNewInst + hs.len)); + + // Complete the function if JMP (FF /4). + if (hs.opcode == 0xFF && hs.modrm_reg == 4) + finished = TRUE; + } +#endif + else if (hs.opcode == 0xE8) + { + // Direct relative CALL + ULONG_PTR dest = pOldInst + hs.len + (INT32)hs.imm.imm32; +#if defined(_M_X64) || defined(__x86_64__) + call.address = dest; +#else + call.operand = (UINT32)(dest - (pNewInst + sizeof(call))); +#endif + pCopySrc = &call; + copySize = sizeof(call); + } + else if ((hs.opcode & 0xFD) == 0xE9) + { + // Direct relative JMP (EB or E9) + ULONG_PTR dest = pOldInst + hs.len; + + if (hs.opcode == 0xEB) // isShort jmp + dest += (INT8)hs.imm.imm8; + else + dest += (INT32)hs.imm.imm32; + + // Simply copy an internal jump. + if ((ULONG_PTR)ct->pTarget <= dest + && dest < ((ULONG_PTR)ct->pTarget + sizeof(JMP_REL))) + { + if (jmpDest < dest) + jmpDest = dest; + } + else + { +#if defined(_M_X64) || defined(__x86_64__) + jmp.address = dest; +#else + jmp.operand = (UINT32)(dest - (pNewInst + sizeof(jmp))); +#endif + pCopySrc = &jmp; + copySize = sizeof(jmp); + + // Exit the function If it is not in the branch + finished = (pOldInst >= jmpDest); + } + } + else if ((hs.opcode & 0xF0) == 0x70 + || (hs.opcode & 0xFC) == 0xE0 + || (hs.opcode2 & 0xF0) == 0x80) + { + // Direct relative Jcc + ULONG_PTR dest = pOldInst + hs.len; + + if ((hs.opcode & 0xF0) == 0x70 // Jcc + || (hs.opcode & 0xFC) == 0xE0) // LOOPNZ/LOOPZ/LOOP/JECXZ + dest += (INT8)hs.imm.imm8; + else + dest += (INT32)hs.imm.imm32; + + // Simply copy an internal jump. + if ((ULONG_PTR)ct->pTarget <= dest + && dest < ((ULONG_PTR)ct->pTarget + sizeof(JMP_REL))) + { + if (jmpDest < dest) + jmpDest = dest; + } + else if ((hs.opcode & 0xFC) == 0xE0) + { + // LOOPNZ/LOOPZ/LOOP/JCXZ/JECXZ to the outside are not supported. + return FALSE; + } + else + { + UINT8 cond = ((hs.opcode != 0x0F ? hs.opcode : hs.opcode2) & 0x0F); +#if defined(_M_X64) || defined(__x86_64__) + // Invert the condition in x64 mode to simplify the conditional jump logic. + jcc.opcode = 0x71 ^ cond; + jcc.address = dest; +#else + jcc.opcode1 = 0x80 | cond; + jcc.operand = (UINT32)(dest - (pNewInst + sizeof(jcc))); +#endif + pCopySrc = &jcc; + copySize = sizeof(jcc); + } + } + else if ((hs.opcode & 0xFE) == 0xC2) + { + // RET (C2 or C3) + + // Complete the function if not in a branch. + finished = (pOldInst >= jmpDest); + } + + // Can't alter the instruction length in a branch. + if (pOldInst < jmpDest && copySize != hs.len) + return FALSE; + + // Trampoline function is too large. + if ((newPos + copySize) > TRAMPOLINE_MAX_SIZE) + return FALSE; + + // Trampoline function has too many instructions. + if (ct->nIP >= ARRAYSIZE(ct->oldIPs)) + return FALSE; + + ct->oldIPs[ct->nIP] = oldPos; + ct->newIPs[ct->nIP] = newPos; + ct->nIP++; + + // Avoid using memcpy to reduce the footprint. +#ifndef _MSC_VER + memcpy((LPBYTE)ct->pTrampoline + newPos, pCopySrc, copySize); +#else + __movsb((LPBYTE)ct->pTrampoline + newPos, pCopySrc, copySize); +#endif + newPos += copySize; + oldPos += hs.len; + } + while (!finished); + + // Is there enough place for a long jump? + if (oldPos < sizeof(JMP_REL) + && !IsCodePadding((LPBYTE)ct->pTarget + oldPos, sizeof(JMP_REL) - oldPos)) + { + // Is there enough place for a short jump? + if (oldPos < sizeof(JMP_REL_SHORT) + && !IsCodePadding((LPBYTE)ct->pTarget + oldPos, sizeof(JMP_REL_SHORT) - oldPos)) + { + return FALSE; + } + + // Can we place the long jump above the function? + if (!IsExecutableAddress((LPBYTE)ct->pTarget - sizeof(JMP_REL))) + return FALSE; + + if (!IsCodePadding((LPBYTE)ct->pTarget - sizeof(JMP_REL), sizeof(JMP_REL))) + return FALSE; + + ct->patchAbove = TRUE; + } + +#if defined(_M_X64) || defined(__x86_64__) + // Create a relay function. + jmp.address = (ULONG_PTR)ct->pDetour; + + ct->pRelay = (LPBYTE)ct->pTrampoline + newPos; + memcpy(ct->pRelay, &jmp, sizeof(jmp)); +#endif + + return TRUE; +} diff --git a/minhook/trampoline.h b/minhook/trampoline.h new file mode 100644 index 0000000..bdffdac --- /dev/null +++ b/minhook/trampoline.h @@ -0,0 +1,105 @@ +/* + * MinHook - The Minimalistic API Hooking Library for x64/x86 + * Copyright (C) 2009-2017 Tsuda Kageyu. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#pragma pack(push, 1) + +// Structs for writing x86/x64 instructions. + +// 8-bit relative jump. +typedef struct _JMP_REL_SHORT +{ + UINT8 opcode; // EB xx: JMP +2+xx + UINT8 operand; +} JMP_REL_SHORT, *PJMP_REL_SHORT; + +// 32-bit direct relative jump/call. +typedef struct _JMP_REL +{ + UINT8 opcode; // E9/E8 xxxxxxxx: JMP/CALL +5+xxxxxxxx + UINT32 operand; // Relative destination address +} JMP_REL, *PJMP_REL, CALL_REL; + +// 64-bit indirect absolute jump. +typedef struct _JMP_ABS +{ + UINT8 opcode0; // FF25 00000000: JMP [+6] + UINT8 opcode1; + UINT32 dummy; + UINT64 address; // Absolute destination address +} JMP_ABS, *PJMP_ABS; + +// 64-bit indirect absolute call. +typedef struct _CALL_ABS +{ + UINT8 opcode0; // FF15 00000002: CALL [+6] + UINT8 opcode1; + UINT32 dummy0; + UINT8 dummy1; // EB 08: JMP +10 + UINT8 dummy2; + UINT64 address; // Absolute destination address +} CALL_ABS; + +// 32-bit direct relative conditional jumps. +typedef struct _JCC_REL +{ + UINT8 opcode0; // 0F8* xxxxxxxx: J** +6+xxxxxxxx + UINT8 opcode1; + UINT32 operand; // Relative destination address +} JCC_REL; + +// 64bit indirect absolute conditional jumps that x64 lacks. +typedef struct _JCC_ABS +{ + UINT8 opcode; // 7* 0E: J** +16 + UINT8 dummy0; + UINT8 dummy1; // FF25 00000000: JMP [+6] + UINT8 dummy2; + UINT32 dummy3; + UINT64 address; // Absolute destination address +} JCC_ABS; + +#pragma pack(pop) + +typedef struct _TRAMPOLINE +{ + LPVOID pTarget; // [In] Address of the target function. + LPVOID pDetour; // [In] Address of the detour function. + LPVOID pTrampoline; // [In] Buffer address for the trampoline and relay function. + +#if defined(_M_X64) || defined(__x86_64__) + LPVOID pRelay; // [Out] Address of the relay function. +#endif + BOOL patchAbove; // [Out] Should use the hot patch area? + UINT nIP; // [Out] Number of the instruction boundaries. + UINT8 oldIPs[8]; // [Out] Instruction boundaries of the target function. + UINT8 newIPs[8]; // [Out] Instruction boundaries of the trampoline function. +} TRAMPOLINE, *PTRAMPOLINE; + +BOOL CreateTrampolineFunction(PTRAMPOLINE ct); diff --git a/popnhax/Module.mk b/popnhax/Module.mk new file mode 100644 index 0000000..3d64988 --- /dev/null +++ b/popnhax/Module.mk @@ -0,0 +1,16 @@ +avsdlls += popnhax + +deplibs_popnhax := \ + avs \ + +ldflags_popnhax := \ + -lwinmm -lpsapi + +libs_popnhax := \ + util \ + minhook + +srcpp_popnhax := \ + dllmain.cc \ + loader.cc \ + SearchFile.cc diff --git a/popnhax/SearchFile.cc b/popnhax/SearchFile.cc new file mode 100644 index 0000000..71c986c --- /dev/null +++ b/popnhax/SearchFile.cc @@ -0,0 +1,49 @@ + +#include +#include "SearchFile.h" + +vector SearchFile::getResult() +{ + auto t = result; + result.clear(); + return t; +} + +bool SearchFile::search(const char *path, const char *ext, bool subfolders_only) +{ + HANDLE hFile; + char buffer[MAX_PATH]={0,}; + WIN32_FIND_DATA pNextInfo; + string t; + + sprintf(buffer,"%s\\*.*",path); + hFile = FindFirstFileA(buffer,&pNextInfo); + if(!hFile){ + return false; + } + + while(FindNextFileA(hFile,&pNextInfo)) + { + if(pNextInfo.cFileName[0] == '.')// . and .. + continue; + + if(pNextInfo.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY && subfolders_only) + { + ZeroMemory(buffer,MAX_PATH); + sprintf(buffer,"%s\\%s",path,pNextInfo.cFileName); + search(buffer,ext,false); + } + if (!subfolders_only) + { + t.assign(path); + t+='\\'; + t.append(pNextInfo.cFileName); + if(t.substr(t.size()-strlen(ext))==ext) + { + result.push_back(t); + } + } + } + return true; +} + diff --git a/popnhax/SearchFile.h b/popnhax/SearchFile.h new file mode 100644 index 0000000..1775e3c --- /dev/null +++ b/popnhax/SearchFile.h @@ -0,0 +1,14 @@ +#ifndef __SEARCHFILE_H_ +#define __SEARCHFILE_H_ +#include +#include +using namespace std; +class SearchFile +{ +private: + vector result; +public: + vector getResult(); + bool search(const char *path,const char *ext,bool subfolders_only); +}; +#endif diff --git a/popnhax/config.h b/popnhax/config.h new file mode 100644 index 0000000..af632f4 --- /dev/null +++ b/popnhax/config.h @@ -0,0 +1,21 @@ +#ifndef __POPNHAX_CONFIG__ +#define __POPNHAX_CONFIG__ + +#include + +struct popnhax_config { + bool force_unlocks; + bool unset_volume; + bool event_mode; + bool remove_timer; + bool freeze_timer; + bool skip_tutorials; + + bool patch_db; + bool disable_expansions; + bool disable_redirection; + bool patch_xml_auto; + char patch_xml_filename[MAX_PATH]; +}; + +#endif diff --git a/popnhax/dllmain.cc b/popnhax/dllmain.cc new file mode 100644 index 0000000..98a00f8 --- /dev/null +++ b/popnhax/dllmain.cc @@ -0,0 +1,1078 @@ +// clang-format off +#include +#include +// clang-format on + +#include + +#include +#include +#include + +#include "util/fuzzy_search.h" + +#include "minhook/hde32.h" +#include "minhook/include/MinHook.h" + +#include "popnhax/config.h" +#include "util/patch.h" +#include "util/xmlprop.hpp" +#include "xmlhelper.h" + +#include "tableinfo.h" +#include "loader.h" + +#include "SearchFile.h" + +uint8_t *add_string(uint8_t *input); + +struct popnhax_config config = {}; + +PSMAP_BEGIN(config_psmap, static) +PSMAP_MEMBER_REQ(PSMAP_PROPERTY_TYPE_BOOL, struct popnhax_config, force_unlocks, + "/popnhax/force_unlocks") +PSMAP_MEMBER_REQ(PSMAP_PROPERTY_TYPE_BOOL, struct popnhax_config, unset_volume, + "/popnhax/unset_volume") +PSMAP_MEMBER_REQ(PSMAP_PROPERTY_TYPE_BOOL, struct popnhax_config, event_mode, "/popnhax/event_mode") +PSMAP_MEMBER_REQ(PSMAP_PROPERTY_TYPE_BOOL, struct popnhax_config, remove_timer, + "/popnhax/remove_timer") +PSMAP_MEMBER_REQ(PSMAP_PROPERTY_TYPE_BOOL, struct popnhax_config, freeze_timer, + "/popnhax/freeze_timer") +PSMAP_MEMBER_REQ(PSMAP_PROPERTY_TYPE_BOOL, struct popnhax_config, skip_tutorials, + "/popnhax/skip_tutorials") +PSMAP_MEMBER_REQ(PSMAP_PROPERTY_TYPE_BOOL, struct popnhax_config, patch_db, + "/popnhax/patch_db") +PSMAP_MEMBER_REQ(PSMAP_PROPERTY_TYPE_BOOL, struct popnhax_config, disable_expansions, + "/popnhax/disable_expansions") +PSMAP_MEMBER_REQ(PSMAP_PROPERTY_TYPE_BOOL, struct popnhax_config, disable_redirection, + "/popnhax/disable_redirection") +PSMAP_MEMBER_REQ(PSMAP_PROPERTY_TYPE_BOOL, struct popnhax_config, patch_xml_auto, + "/popnhax/patch_xml_auto") +PSMAP_MEMBER_REQ(PSMAP_PROPERTY_TYPE_STR, struct popnhax_config, patch_xml_filename, + "/popnhax/patch_xml_filename") +PSMAP_END + +enum BufferIndexes { + MUSIC_TABLE_IDX = 0, + CHART_TABLE_IDX, + STYLE_TABLE_IDX, + FLAVOR_TABLE_IDX, + CHARA_TABLE_IDX, +}; + +typedef struct { + uint64_t type; + uint64_t method; + uint64_t offset; + uint64_t expectedValue; + uint64_t size; +} UpdateOtherEntry; + +typedef struct { + uint64_t type; + uint64_t offset; + uint64_t size; +} UpdateBufferEntry; + +typedef struct { + uint64_t method; + uint64_t offset; +} HookEntry; + +const size_t LIMIT_TABLE_SIZE = 5; +uint64_t limit_table[LIMIT_TABLE_SIZE] = {0}; +uint64_t new_limit_table[LIMIT_TABLE_SIZE] = {0}; +uint64_t buffer_addrs[LIMIT_TABLE_SIZE] = {0}; + +uint8_t *new_buffer_addrs[LIMIT_TABLE_SIZE] = {NULL}; + +std::vector buffer_offsets; +std::vector other_offsets; +std::vector hook_offsets; + +void (*real_omnimix_patch_jbx)(); +void omnimix_patch_jbx() { + __asm("mov al, 'X'\n"); + __asm("mov byte [edi+4], al\n"); + real_omnimix_patch_jbx(); +} + +void (*real_check_music_idx)(); +extern "C" void check_music_idx(); + +extern "C" int8_t check_music_idx_handler(int32_t music_idx, int32_t chart_idx, int32_t result) { + int8_t override_flag = get_chart_type_override(new_buffer_addrs[MUSIC_TABLE_IDX], music_idx & 0xffff, chart_idx & 0x0f); + + printf("music_idx: %d, result: %d, override_flag: %d\n", music_idx & 0xffff, result, override_flag); + + if (override_flag != -1) { + return override_flag; + } + + return (music_idx & 0xffff) > limit_table[CHART_TABLE_IDX] ? 1 : result; +} + +asm( +".global _check_music_idx\n" +"_check_music_idx:\n" +" call [_real_check_music_idx]\n" +" movzx eax, al\n" +" push eax\n" +" push ebx\n" +" push esi\n" +" call _check_music_idx_handler\n" +" add esp, 12\n" +" ret\n" +); + +void (*real_check_music_idx_usaneko)(); +extern "C" void check_music_idx_usaneko(); +extern "C" int8_t check_music_idx_handler_usaneko(int32_t result, int32_t chart_idx, int32_t music_idx) { + return check_music_idx_handler(music_idx, chart_idx, result); +} + +asm( +".global _check_music_idx_usaneko\n" +"_check_music_idx_usaneko:\n" +" push edi\n" +" push ebx\n" +" cmp eax, 0x18\n" +" setl al\n" +" movzx eax, al\n" +" push eax\n" +" call _check_music_idx_handler_usaneko\n" +" add esp, 12\n" +" jmp [_real_check_music_idx_usaneko]\n" +); + +void patch_string(const char *input_string, const char *new_string) { + DWORD dllSize = 0; + char *data = getDllData("popn22.dll", &dllSize); + + while (1) { + fuzzy_search_task task; + + FUZZY_START(task, 1) + FUZZY_CODE(task, 0, input_string, strlen(input_string)) + + int64_t pattern_offset = find_block(data, dllSize, &task, 0); + if (pattern_offset == -1) { + break; + } + + uint64_t patch_addr = (int64_t)data + pattern_offset; + char *new_string_buff = (char*)calloc(strlen(new_string) + 1, sizeof(char)); + memcpy(new_string_buff, new_string, strlen(new_string)); + patch_memory(patch_addr, new_string_buff, strlen(new_string) + 1); + free(new_string_buff); + } +} + +char *parse_patchdb(const char *input_filename, char *base_data) { + const char *folder = "data_mods\\"; + char *input_filepath = (char*)calloc(strlen(input_filename) + strlen(folder) + 1, sizeof(char)); + + sprintf(input_filepath, "%s%s", folder, input_filename); + + property* config_xml = load_prop_file(input_filepath); + + free(input_filepath); + + char *target = (char*)calloc(64, sizeof(char)); + property_node_refer(config_xml, property_search(config_xml, NULL, "/patches"), "target@", PROPERTY_TYPE_ATTR, target, 64); + + READ_U32(config_xml, NULL, "/patches/limits/music", limit_music) + READ_U32(config_xml, NULL, "/patches/limits/chart", limit_chart) + READ_U32(config_xml, NULL, "/patches/limits/style", limit_style) + READ_U32(config_xml, NULL, "/patches/limits/flavor", limit_flavor) + READ_U32(config_xml, NULL, "/patches/limits/chara", limit_chara) + + limit_table[MUSIC_TABLE_IDX] = limit_music; + limit_table[CHART_TABLE_IDX] = limit_chart; + limit_table[STYLE_TABLE_IDX] = limit_style; + limit_table[FLAVOR_TABLE_IDX] = limit_flavor; + limit_table[CHARA_TABLE_IDX] = limit_chara; + + READ_HEX(config_xml, NULL, "/patches/buffer_base_addrs/music", buffer_addrs[MUSIC_TABLE_IDX]) + buffer_addrs[MUSIC_TABLE_IDX] = buffer_addrs[MUSIC_TABLE_IDX] > 0 ? (uint64_t)base_data + (buffer_addrs[MUSIC_TABLE_IDX] - 0x10000000) : buffer_addrs[MUSIC_TABLE_IDX]; + + READ_HEX(config_xml, NULL, "/patches/buffer_base_addrs/chart", buffer_addrs[CHART_TABLE_IDX]) + buffer_addrs[CHART_TABLE_IDX] = buffer_addrs[CHART_TABLE_IDX] > 0 ? (uint64_t)base_data + (buffer_addrs[CHART_TABLE_IDX] - 0x10000000) : buffer_addrs[CHART_TABLE_IDX]; + + READ_HEX(config_xml, NULL, "/patches/buffer_base_addrs/style", buffer_addrs[STYLE_TABLE_IDX]) + buffer_addrs[STYLE_TABLE_IDX] = buffer_addrs[STYLE_TABLE_IDX] > 0 ? (uint64_t)base_data + (buffer_addrs[STYLE_TABLE_IDX] - 0x10000000) : buffer_addrs[STYLE_TABLE_IDX]; + + READ_HEX(config_xml, NULL, "/patches/buffer_base_addrs/flavor", buffer_addrs[FLAVOR_TABLE_IDX]) + buffer_addrs[FLAVOR_TABLE_IDX] = buffer_addrs[FLAVOR_TABLE_IDX] > 0 ? (uint64_t)base_data + (buffer_addrs[FLAVOR_TABLE_IDX] - 0x10000000) : buffer_addrs[FLAVOR_TABLE_IDX]; + + READ_HEX(config_xml, NULL, "/patches/buffer_base_addrs/chara", buffer_addrs[CHARA_TABLE_IDX]) + buffer_addrs[CHARA_TABLE_IDX] = buffer_addrs[CHARA_TABLE_IDX] > 0 ? (uint64_t)base_data + (buffer_addrs[CHARA_TABLE_IDX] - 0x10000000) : buffer_addrs[CHARA_TABLE_IDX]; + + printf("limit music: %lld\n", limit_table[MUSIC_TABLE_IDX]); + printf("limit chart: %lld\n", limit_table[CHART_TABLE_IDX]); + printf("limit style: %lld\n", limit_table[STYLE_TABLE_IDX]); + printf("limit flavor: %lld\n", limit_table[FLAVOR_TABLE_IDX]); + printf("limit chara: %lld\n", limit_table[CHARA_TABLE_IDX]); + + printf("buffer music: %llx\n", buffer_addrs[MUSIC_TABLE_IDX]); + printf("buffer chart: %llx\n", buffer_addrs[CHART_TABLE_IDX]); + printf("buffer style: %llx\n", buffer_addrs[STYLE_TABLE_IDX]); + printf("buffer flavor: %llx\n", buffer_addrs[FLAVOR_TABLE_IDX]); + printf("buffer chara: %llx\n", buffer_addrs[CHARA_TABLE_IDX]); + + const char *types[LIMIT_TABLE_SIZE] = {"music", "chart", "style", "flavor", "chara"}; + + // Read buffers_patch_addrs + for (size_t i = 0; i < LIMIT_TABLE_SIZE; i++) { + const char strbase[] = "/patches/buffers_patch_addrs"; + size_t search_str_len = strlen(strbase) + 1 + strlen(types[i]) + 1; + char *search_str = (char*)calloc(search_str_len, sizeof(char)); + + if (!search_str) { + printf("Couldn't create buffer of size %d\n", search_str_len); + exit(1); + } + + sprintf(search_str, "%s/%s", strbase, types[i]); + printf("search_str: %s\n", search_str); + + property_node* prop = NULL; + if ((prop = property_search(config_xml, NULL, search_str))) { + for (; prop != NULL; prop = property_node_traversal(prop, TRAVERSE_NEXT_SEARCH_RESULT)) { + uint64_t offset = 0; + READ_HEX(config_xml, prop, "", offset) + + printf("offset ptr: %llx\n", offset); + + UpdateBufferEntry *buffer_offset = new UpdateBufferEntry(); + buffer_offset->type = i; + buffer_offset->offset = offset; + buffer_offsets.push_back(buffer_offset); + } + } + } + + // Read other_patches + for (size_t i = 0; i < LIMIT_TABLE_SIZE; i++) { + const char strbase[] = "/patches/other_patches"; + size_t search_str_len = strlen(strbase) + 1 + strlen(types[i]) + 1; + char *search_str = (char*)calloc(search_str_len, sizeof(char)); + + if (!search_str) { + printf("Couldn't create buffer of size %d\n", search_str_len); + exit(1); + } + + sprintf(search_str, "%s/%s", strbase, types[i]); + printf("search_str: %s\n", search_str); + + property_node* prop = NULL; + if ((prop = property_search(config_xml, NULL, search_str))) { + for (; prop != NULL; prop = property_node_traversal(prop, TRAVERSE_NEXT_SEARCH_RESULT)) { + char methodStr[256] = {}; + property_node_refer(config_xml, prop, "method@", PROPERTY_TYPE_ATTR, methodStr, sizeof(methodStr)); + uint32_t method = atoi(methodStr); + + char expectedStr[256] = {}; + property_node_refer(config_xml, prop, "expected@", PROPERTY_TYPE_ATTR, expectedStr, sizeof(expectedStr)); + uint64_t expected = strtol((const char*)expectedStr, NULL, 16); + + char sizeStr[256] = {}; + property_node_refer(config_xml, prop, "size@", PROPERTY_TYPE_ATTR, sizeStr, sizeof(sizeStr)); + uint64_t size = strtol((const char*)sizeStr, NULL, 16); + + if (size == 0) { + // I can't think of any patches that require a different size than 4 bytes + size = 4; + } + + uint64_t offset = 0; + READ_HEX(config_xml, prop, "", offset) + + printf("offset ptr: %llx, method = %d, expected = %llx, size = %lld\n", offset, method, expected, size); + + UpdateOtherEntry *other_offset = new UpdateOtherEntry(); + other_offset->type = i; + other_offset->method = method; + other_offset->offset = offset; + other_offset->expectedValue = expected; + other_offset->size = size; + other_offsets.push_back(other_offset); + } + } + } + + // Read hook_addrs + { + property_node* prop = NULL; + if ((prop = property_search(config_xml, NULL, "/patches/hook_addrs/offset"))) { + for (; prop != NULL; prop = property_node_traversal(prop, TRAVERSE_NEXT_SEARCH_RESULT)) { + char methodStr[256] = {}; + property_node_refer(config_xml, prop, "method@", PROPERTY_TYPE_ATTR, methodStr, sizeof(methodStr)); + uint32_t method = atoi(methodStr); + + uint64_t offset = 0; + READ_HEX(config_xml, prop, "", offset) + + printf("offset ptr: %llx\n", offset); + + HookEntry *hook_offset = new HookEntry(); + hook_offset->offset = offset; + hook_offset->method = method; + hook_offsets.push_back(hook_offset); + } + } + } + + return target; +} + +static bool patch_database(bool force_unlocks) { + DWORD dllSize = 0; + char *data = getDllData("popn22.dll", &dllSize); + + { + fuzzy_search_task task; + + FUZZY_START(task, 1) + FUZZY_CODE(task, 0, "\x8D\x44\x24\x10\x88\x4C\x24\x10\x88\x5C\x24\x11\x8D\x50\x01", 15) + + int64_t pattern_offset = find_block(data, dllSize, &task, 0); + if (pattern_offset != -1) { + uint64_t patch_addr = (int64_t)data + pattern_offset; + MH_CreateHook((LPVOID)patch_addr, (LPVOID)omnimix_patch_jbx, + (void **)&real_omnimix_patch_jbx); + + printf("popnhax: Patched X rev for omnimix\n"); + } else { + printf("popnhax: Couldn't find rev patch\n"); + } + } + + char *target; + + if (config.patch_xml_auto) { + const char *filename = NULL; + SearchFile s; + uint8_t *datecode = NULL; + bool found = false; + + printf("pophax: auto detect patch file\n"); + property *config_xml = load_prop_file("prop/ea3-config.xml"); + READ_STR_OPT(config_xml, property_search(config_xml, NULL, "/ea3/soft"), "ext", datecode) + free(config_xml); + + if (datecode == NULL) { + printf("popnhax: (patch_xml_auto) failed to retrieve datecode from ea3-config. Please disable patch_xml_auto option and use patch_xml_filename to specify which file should be used.\n"); + return false; + } + + printf("popnhax: (patch_xml_auto) datecode from ea3-config : %s\n", datecode); + printf("popnhax: (patch_xml_auto) XML patch files search...\n"); + s.search("data_mods", "xml", false); + auto result = s.getResult(); + + printf("popnhax: (patch_xml_auto) found %d xml files in data_mods\n",result.size()); + for (uint16_t i=0; ioffset == 0 || buffer_addrs[buffer_offsets[i]->type] == 0) { + continue; + } + + // buffer_base_offsets is required because it will point to the beginning of all of the buffers to calculate offsets + uint64_t patch_addr = (int64_t)data + (buffer_offsets[i]->offset - 0x10000000); + uint64_t cur_addr = *(int32_t*)patch_addr; + + uint64_t mem_addr = (uint64_t)new_buffer_addrs[buffer_offsets[i]->type] + (cur_addr - buffer_addrs[buffer_offsets[i]->type]); + + printf("Patching %llx -> %llx @ %llx (%llx - %llx = %llx)\n", cur_addr, mem_addr, buffer_offsets[i]->offset, cur_addr, buffer_addrs[buffer_offsets[i]->type], cur_addr - buffer_addrs[buffer_offsets[i]->type]); + + patch_memory(patch_addr, (char*)&mem_addr, 4); + } + + printf("popnhax: patched memory db locations\n"); + + if (config.disable_expansions) { + printf("Expansion-related code is disabled, buffer size and related patches will not be applied"); + return true; + } + + for (size_t i = 0; i < other_offsets.size(); i++) { + // Get current limit so it can be used for later calculations + uint32_t cur_limit = limit_table[other_offsets[i]->type]; + uint32_t limit_diff = cur_limit < new_limit_table[other_offsets[i]->type] + ? new_limit_table[other_offsets[i]->type] - cur_limit + : 0; + + if (limit_diff != 0) + { + uint64_t cur_value = 0; + + if (other_offsets[i]->size == 1) { + cur_value = *(uint8_t*)(data + (other_offsets[i]->offset - 0x10000000)); + } else if (other_offsets[i]->size == 2) { + cur_value = *(uint16_t*)(data + (other_offsets[i]->offset - 0x10000000)); + } else if (other_offsets[i]->size == 4) { + cur_value = *(uint32_t*)(data + (other_offsets[i]->offset - 0x10000000)); + } else if (other_offsets[i]->size == 8) { + cur_value = *(uint64_t*)(data + (other_offsets[i]->offset - 0x10000000)); + } + + if (other_offsets[i]->method != 12 && cur_value != other_offsets[i]->expectedValue) { + printf("ERROR! Expected %llx, found %llx @ %llx!\n", other_offsets[i]->expectedValue, cur_value, other_offsets[i]->offset); + exit(1); + } + + uint64_t value = cur_value; + uint32_t patch_size = 4; + switch (other_offsets[i]->method) { + // Add limit_diff to value + case 0: + value = cur_value + limit_diff; + break; + + // Add limit_diff * 4 to value + case 1: + value = cur_value + (limit_diff * 4); + break; + + // Add limit diff * 6 * 4 (number of charts * 4?) + case 2: + value = cur_value + ((limit_diff * 6) * 4); + break; + + // Add limit_diff * 6 + case 3: + value = cur_value + (limit_diff * 6); + break; + + // Add limit_diff * 0x90 + case 4: + value = cur_value + (limit_diff * 0x90); + break; + + // Add limit_diff * 0x48 + case 5: + value = cur_value + (limit_diff * 0x48); + break; + + // Add limit_diff * 0x120 + case 6: + value = cur_value + (limit_diff * 0x120); + break; + + // Add limit_diff * 0x440 + case 7: + value = cur_value + (limit_diff * 0x440); + break; + + // Add limit_diff * 0x0c + case 8: + value = cur_value + (limit_diff * 0x0c); + break; + + // Add limit_diff * 0x3d0 + case 9: + value = cur_value + (limit_diff * 0x3d0); + break; + + // Add (limit_diff * 0x3d0) + (limit_diff * 0x9c) + case 10: + value = cur_value + (limit_diff * 0x3d0) + (limit_diff * 0x9c); + break; + + // Add (limit_diff * 0x3d0) + (limit_diff * 0x9c) + (limit_diff * 0x2a0) + case 11: + value = cur_value + (limit_diff * 0x3d0) + (limit_diff * 0x9c) + (limit_diff * 0x2a0); + break; + + // NOP + case 12: + value = 0x9090909090909090; + patch_size = other_offsets[i]->size; + break; + + default: + printf("Unknown command: %lld\n", other_offsets[i]->method); + break; + } + + if (value != cur_value) { + uint64_t patch_addr = (int64_t)data + (other_offsets[i]->offset - 0x10000000); + patch_memory(patch_addr, (char*)&value, patch_size); + + printf("Patched %llx: %llx -> %llx\n", other_offsets[i]->offset, cur_value, value); + } + } + } + + HMODULE _moduleBase = GetModuleHandle("popn22.dll"); + for (size_t i = 0; i < hook_offsets.size(); i++) { + switch (hook_offsets[i]->method) { + case 0: { + // Peace hook + printf("Hooking %llx %p\n", hook_offsets[i]->offset, _moduleBase); + MH_CreateHook((void*)((uint8_t*)_moduleBase + (hook_offsets[i]->offset - 0x10000000)), (void *)&check_music_idx, (void **)&real_check_music_idx); + break; + } + + case 1: { + // Usaneko hook + printf("Hooking %llx %p\n", hook_offsets[i]->offset, _moduleBase); + uint8_t nops[] = { 0x90, 0x90, 0x90, 0x90, 0x90, 0x90 }; + patch_memory((uint64_t)((uint8_t*)_moduleBase + (hook_offsets[i]->offset - 0x10000000) - 6), (char *)&nops, 6); + MH_CreateHook((void*)((uint8_t*)_moduleBase + (hook_offsets[i]->offset - 0x10000000)), (void *)&check_music_idx_usaneko, (void **)&real_check_music_idx_usaneko); + break; + } + + default: + printf("Unknown hook command: %lld\n", hook_offsets[i]->method); + break; + } + } + + printf("popnhax: patched limit-related code\n"); + + return true; +} + +static bool patch_unset_volume() { + DWORD dllSize = 0; + char *data = getDllData("popn22.dll", &dllSize); + + int64_t first_loc = 0; + + { + fuzzy_search_task task; + + FUZZY_START(task, 1) + FUZZY_CODE(task, 0, "\x04\x00\x81\xC4\x00\x01\x00\x00\xC3\xCC", 10) + + first_loc = find_block(data, dllSize, &task, 0); + if (first_loc == -1) { + return false; + } + } + + { + fuzzy_search_task task; + + FUZZY_START(task, 1) + FUZZY_CODE(task, 0, "\x83", 1) + + int64_t pattern_offset = find_block(data, 0x10, &task, first_loc); + if (pattern_offset == -1) { + return false; + } + + uint64_t patch_addr = (int64_t)data + pattern_offset; + patch_memory(patch_addr, (char *)"\xC3", 1); + } + + printf("popnhax: windows volume untouched\n"); + + return true; +} + +static bool patch_event_mode() { + DWORD dllSize = 0; + char *data = getDllData("popn22.dll", &dllSize); + + { + fuzzy_search_task task; + + FUZZY_START(task, 1) + FUZZY_CODE(task, 0, + "\x8B\x00\xC3\xCC\xCC\xCC\xCC\xCC\xCC\xCC\xCC\xCC\xCC\xCC" + "\xCC\xCC\xC7", + 17) + + int64_t pattern_offset = find_block(data, dllSize, &task, 0); + if (pattern_offset == -1) { + return false; + } + + uint64_t patch_addr = (int64_t)data + pattern_offset; + patch_memory(patch_addr, (char *)"\x31\xC0\x40\xC3", 4); + } + + printf("popnhax: event mode forced\n"); + + return true; +} + +static bool patch_remove_timer() { + DWORD dllSize = 0; + char *data = getDllData("popn22.dll", &dllSize); + + int64_t first_loc = 0; + + { + fuzzy_search_task task; + + FUZZY_START(task, 1) + FUZZY_CODE(task, 0, "\x8B\xAC\x24\x68\x01", 5) + + first_loc = find_block(data, dllSize, &task, 0); + if (first_loc == -1) { + return false; + } + } + + { + fuzzy_search_task task; + + FUZZY_START(task, 1) + FUZZY_CODE(task, 0, "\x0F", 1) + + int64_t pattern_offset = find_block(data, 0x15, &task, first_loc); + if (pattern_offset == -1) { + return false; + } + + uint64_t patch_addr = (int64_t)data + pattern_offset; + patch_memory(patch_addr, (char *)"\x90\xE9", 2); + } + + printf("popnhax: timer removed\n"); + + return true; +} + +static bool patch_freeze_timer() { + DWORD dllSize = 0; + char *data = getDllData("popn22.dll", &dllSize); + + { + fuzzy_search_task task; + + FUZZY_START(task, 1) + FUZZY_CODE(task, 0, "\xC7\x45\x38\x09\x00\x00\x00", 7) + + int64_t pattern_offset = find_block(data, dllSize, &task, 0); + if (pattern_offset == -1) { + return false; + } + + uint64_t patch_addr = (int64_t)data + pattern_offset; + patch_memory(patch_addr, (char *)"\x90\x90\x90\x90\x90\x90\x90", 7); + } + + printf("popnhax: timer frozen at 10 seconds remaining\n"); + + return true; +} + +static bool patch_skip_tutorials() { + DWORD dllSize = 0; + char *data = getDllData("popn22.dll", &dllSize); + + int64_t first_loc = 0; + + { + fuzzy_search_task task; + + FUZZY_START(task, 1) + FUZZY_CODE(task, 0, "\xFD\xFF\x5E\xC2\x04\x00\xE8", 7) + + first_loc = find_block(data, dllSize, &task, 0); + if (first_loc == -1) { + return false; + } + } + + { + fuzzy_search_task task; + + FUZZY_START(task, 1) + FUZZY_CODE(task, 0, "\x74", 1) + + int64_t pattern_offset = find_block(data, 0x10, &task, first_loc); + if (pattern_offset == -1) { + return false; + } + + uint64_t patch_addr = (int64_t)data + pattern_offset; + patch_memory(patch_addr, (char *)"\xEB", 1); + } + + { + fuzzy_search_task task; + + FUZZY_START(task, 1) + FUZZY_CODE(task, 0, "\x66\x85\xC0\x75\x5E\x6A", 6) + + int64_t pattern_offset = find_block(data, dllSize, &task, 0); + if (pattern_offset == -1) { + return false; + } + + uint64_t patch_addr = (int64_t)data + pattern_offset; + patch_memory(patch_addr, (char *)"\x66\x85\xC0\xEB\x5E\x6A", 6); + } + + { + fuzzy_search_task task; + + FUZZY_START(task, 1) + FUZZY_CODE(task, 0, "\x00\x5F\x5E\x66\x83\xF8\x01\x75", 8) + + int64_t pattern_offset = find_block(data, dllSize, &task, 0); + if (pattern_offset == -1) { + return false; + } + + uint64_t patch_addr = (int64_t)data + pattern_offset; + patch_memory(patch_addr, (char *)"\x00\x5F\x5E\x66\x83\xF8\x01\xEB", 8); + } + + printf("popnhax: menu and long note tutorials skipped\n"); + + return true; +} + +bool force_unlock_deco_parts() { + DWORD dllSize = 0; + char *data = getDllData("popn22.dll", &dllSize); + + { + fuzzy_search_task task; + + FUZZY_START(task, 1) + FUZZY_CODE(task, 0, "\x83\xC4\x04\x83\x38\x00\x75\x22", 8) + + int64_t pattern_offset = find_block(data, dllSize, &task, 0); + if (pattern_offset == -1) { + printf("popnhax: couldn't unlock deco parts\n"); + return false; + } + + uint64_t patch_addr = (int64_t)data + pattern_offset; + patch_memory(patch_addr, (char *)"\x83\xC4\x04\x83\x38\x00\x90\x90", 8); + } + + printf("popnhax: unlocked deco parts\n"); + + return true; +} + +bool force_unlock_songs() { + DWORD dllSize = 0; + char *data = getDllData("popn22.dll", &dllSize); + + int music_unlocks = 0, chart_unlocks = 0; + + { + fuzzy_search_task task; + + FUZZY_START(task, 1) + FUZZY_CODE(task, 0, "\x69\xC0\xAC\x00\x00\x00\x8B\x80", 8) // 0xac here is the size of music_entry. May change in the future + + int64_t pattern_offset = find_block(data, dllSize, &task, 0); + if (pattern_offset == -1) { + printf("popnhax: couldn't unlock songs and charts\n"); + return false; + } + + uint32_t buffer_offset = *(uint32_t*)((int64_t)data + pattern_offset + task.blocks[0].data[0].length); + buffer_offset -= 0x1c; // The difference between music_entry.mask and music_entry.fw_genre_ptr to put it at the beginning of the entry + music_entry *entry = (music_entry*)buffer_offset; + + for (int32_t i = 0; ; i++) { + // Detect end of table + // Kind of iffy but I think it should work + if (entry->charts[6] != 0 && entry->hold_flags[7] != 0) { + // These should *probably* always be 0 but won't be after the table ends + break; + } + + if ((entry->mask & 0x08000000) != 0) { + printf("[%04d] Unlocking %s\n", i, entry->title_ptr); + music_unlocks++; + } + + if ((entry->mask & 0x00000080) != 0) { + printf("[%04d] Unlocking charts for %s\n", i, entry->title_ptr); + chart_unlocks++; + } + + if ((entry->mask & 0x08000080) != 0) { + uint32_t new_mask = entry->mask & ~0x08000080; + patch_memory((uint64_t)&entry->mask, (char *)&new_mask, 4); + } + + entry++; // Move to the next music entry + } + } + + printf("popnhax: unlocked %d songs and %d charts\n", music_unlocks, chart_unlocks); + + return true; +} + +bool force_unlock_charas() { + DWORD dllSize = 0; + char *data = getDllData("popn22.dll", &dllSize); + + int chara_unlocks = 0; + + { + fuzzy_search_task task; + + FUZZY_START(task, 1) + FUZZY_CODE(task, 0, "\x98\x6B\xC0\x4C\x8B\x80", 6) // 0x4c here is the size of character_entry. May change in the future + + int64_t pattern_offset = find_block(data, dllSize, &task, 0); + if (pattern_offset == -1) { + printf("popnhax: couldn't unlock characters\n"); + return false; + } + + uint32_t buffer_offset = *(uint32_t*)((int64_t)data + pattern_offset + task.blocks[0].data[0].length); + buffer_offset -= 0x48; // The difference between character_entry.game_version and character_entry.chara_id_ptr to put it at the beginning of the entry + character_entry *entry = (character_entry*)buffer_offset; + + for (int32_t i = 0; ; i++) { + // Detect end of table + // Kind of iffy but I think it should work + if (entry->_pad1[0] != 0 || entry->_pad2[0] != 0 || entry->_pad2[1] != 0 || entry->_pad2[2] != 0) { + // These should *probably* always be 0 but won't be after the table ends + break; + } + + uint32_t new_flags = entry->flags & ~3; + + if (new_flags != entry->flags && entry->disp_name_ptr != NULL && strlen((char*)entry->disp_name_ptr) > 0) { + printf("Unlocking [%04d] %s... %08x -> %08x\n", i, entry->disp_name_ptr, entry->flags, new_flags); + patch_memory((uint64_t)&entry->flags, (char *)&new_flags, sizeof(uint32_t)); + + if ((entry->flavor_idx == 0 || entry->flavor_idx == -1)) { + int flavor_idx = 1; + patch_memory((uint64_t)&entry->flavor_idx, (char *)&flavor_idx, sizeof(uint32_t)); + printf("Setting default flavor for chara id %d\n", i); + } + + chara_unlocks++; + } + + entry++; // Move to the next character entry + } + } + + printf("popnhax: unlocked %d characters\n", chara_unlocks); + + return true; +} + +static bool patch_unlocks_offline() { + DWORD dllSize = 0; + char *data = getDllData("popn22.dll", &dllSize); + + int64_t first_loc = 0; + + { + fuzzy_search_task task; + + FUZZY_START(task, 1) + FUZZY_CODE(task, 0, "\x8B\xE5\x5D\xC2\x04\x00\xC3", 7) + + first_loc = find_block(data, dllSize, &task, 0); + if (first_loc == -1) { + printf("Couldn't find unlock loc 1\n"); + return false; + } + } + + int64_t second_loc = 0; + { + fuzzy_search_task task; + + FUZZY_START(task, 1) + FUZZY_CODE(task, 0, "\x00\x00\x50\x8B", 4) + + second_loc = find_block(data, 0x50, &task, first_loc); + if (first_loc == -1) { + printf("Couldn't find unlock loc 2\n"); + return false; + } + } + + { + fuzzy_search_task task; + + FUZZY_START(task, 1) + FUZZY_CODE(task, 0, "\x00\x00\x84\xC0\x74", 5) + + int64_t pattern_offset = find_block(data, 0x10, &task, second_loc); + if (pattern_offset == -1) { + printf("Couldn't find first song unlock\n"); + return false; + } + + uint64_t patch_addr = (int64_t)data + pattern_offset; + patch_memory(patch_addr, (char *)"\x00\x00\x84\xC0\x90\x90", 6); + } + + { + fuzzy_search_task task; + + FUZZY_START(task, 1) + FUZZY_CODE(task, 0, "\xFF\xFF\xA9\x06\x00\x00\x68\x74", 8) + + int64_t pattern_offset = find_block(data, 0xDD, &task, second_loc); + if (pattern_offset == -1) { + printf("Couldn't find second song unlock\n"); + return false; + } + uint64_t patch_addr = (int64_t)data + pattern_offset; + patch_memory(patch_addr, (char *)"\xFF\xFF\xA9\x06\x00\x00\x68\xEB", 8); + + printf("popnhax: songs unlocked for offline\n"); + } + + { + fuzzy_search_task task; + + FUZZY_START(task, 1) + FUZZY_CODE(task, 0, "\xA9\x50\x01\x00\x00\x74", 6) + + int64_t pattern_offset = find_block_back(data, 0x50000, &task, second_loc); + if (pattern_offset == -1) { + printf("Couldn't find character unlock\n"); + return false; + } + uint64_t patch_addr = (int64_t)data + pattern_offset; + patch_memory(patch_addr, (char *)"\xA9\x50\x01\x00\x00\xEB", 6); + + printf("popnhax: characters unlocked for offline\n"); + } + + return true; +} + +BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { + switch (ul_reason_for_call) { + case DLL_PROCESS_ATTACH: { + if (MH_Initialize() != MH_OK) { + printf("Failed to initialize minhook\n"); + exit(1); + return TRUE; + } + + _load_config("popnhax.xml", &config, config_psmap); + + if (config.unset_volume) { + patch_unset_volume(); + } + + if (config.event_mode) { + patch_event_mode(); + } + + if (config.remove_timer) { + patch_remove_timer(); + } + + if (config.freeze_timer) { + patch_freeze_timer(); + } + + if (config.skip_tutorials) { + patch_skip_tutorials(); + } + + if (config.patch_db) { + patch_database(config.force_unlocks); + } + + if (config.force_unlocks) { + if (!config.patch_db) { + // Only unlock using these methods if it's not done directly through the database hooks + force_unlock_songs(); + force_unlock_charas(); + } + + patch_unlocks_offline(); + force_unlock_deco_parts(); + } + + MH_EnableHook(MH_ALL_HOOKS); + + break; + } + + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + break; + } + + return TRUE; +} diff --git a/popnhax/loader.cc b/popnhax/loader.cc new file mode 100644 index 0000000..ae5e924 --- /dev/null +++ b/popnhax/loader.cc @@ -0,0 +1,1282 @@ +#include +#include +#include +#include + +#include "imports/avs.h" +#include "util/patch.h" +#include "xmlhelper.h" + +#include "tableinfo.h" +#include "loader.h" +#include +#include +#include + +#include "SearchFile.h" + +#define RET_COMPARE_INDEX(ptr, val, idx) \ + if (strcmp((char *)ptr, val) == 0) { \ + return idx; \ + } + +struct property *load_prop_file(const char *filename); + +bool file_exists(const char *filename); + +uint8_t *find_string(uint8_t *input, size_t len); +uint8_t *add_string(uint8_t *input); + +void add_chart_type_flag(uint32_t cur_idx, int8_t flag); +uint32_t get_lapis_shape_id(uint8_t *lapis_shape_ptr); +uint32_t get_lapis_color_id(uint8_t *lapis_color_ptr); +uint16_t get_chara_idx(const uint8_t *chara_id); +fontstyle_entry *get_fontstyle(int32_t cur_idx); +character_entry *get_chara(int32_t cur_idx); +music_entry *get_music(int32_t cur_idx); +chart_entry *get_chart(int32_t cur_idx); + +uint32_t add_style(int32_t cur_idx, uint32_t fontface, uint32_t color, uint32_t height, + uint32_t width); + +uint32_t add_flavor(int32_t cur_idx, uint8_t *phrase1, uint8_t *phrase2, uint8_t *phrase3, + uint8_t *phrase4, uint8_t *phrase5, uint8_t *phrase6, uint8_t *birthday, + uint8_t chara1_birth_month, uint8_t chara2_birth_month, + uint8_t chara3_birth_month, uint8_t chara1_birth_date, + uint8_t chara2_birth_date, uint8_t chara3_birth_date, uint16_t style1, + bool style2_flag, uint16_t style3, uint32_t fontstyle_fontface, + uint32_t fontstyle_color, uint32_t fontstyle_height, uint32_t fontstyle_width); + +uint32_t add_chara(int32_t cur_idx, uint8_t *chara_id_ptr, uint32_t flags, uint8_t *folder_ptr, + uint8_t *gg_ptr, uint8_t *cs_ptr, uint8_t *icon1_ptr, uint8_t *icon2_ptr, + uint16_t chara_xw, uint16_t chara_yh, uint32_t display_flags, int16_t flavor_idx, + uint8_t chara_variation_num, uint8_t *sort_name_ptr, uint8_t *disp_name_ptr, + uint32_t file_type, uint32_t lapis_shape, uint8_t lapis_color, uint8_t *ha_ptr, + uint8_t *catchtext_ptr, int16_t win2_trigger, uint32_t game_version); + +uint32_t add_music(int32_t cur_idx, uint8_t *fw_genre_ptr, uint8_t *fw_title_ptr, + uint8_t *fw_artist_ptr, uint8_t *genre_ptr, uint8_t *title_ptr, + uint8_t *artist_ptr, uint16_t chara1, uint16_t chara2, uint32_t mask, + uint32_t folder, uint32_t cs_version, uint32_t categories, uint8_t *diffs, + uint16_t *charts, uint8_t *ha_ptr, uint32_t chara_x, uint32_t chara_y, + uint16_t *unk1, uint16_t *display_bpm, uint8_t *hold_flags, bool allow_resize); + +uint32_t add_chart(uint32_t cur_idx, uint8_t *folder, uint8_t *filename, int32_t audio_param1, + int32_t audio_param2, int32_t audio_param3, int32_t audio_param4, + uint32_t file_type, uint16_t used_keys, bool override_idx); + +void parse_charadb(const char *input_filename, const char *target); +void parse_musicdb(const char *input_filename, const char *target); + +std::map chart_type_overrides; + +uint32_t flavor_limit = 0; + +uint8_t *filler_str = add_string((uint8_t *)"\x81\x5d\x00"); + +const uint32_t string_table_growth_size = 0x100000; +uint32_t string_table_size = 0; +uint32_t string_table_idx = 0; +uint8_t *string_table = NULL; + +const uint32_t fontstyle_table_growth_size = 50; +uint32_t fontstyle_table_size = 0; +int32_t fontmax_style_id = -1; +uint8_t *fontstyle_table = NULL; + +const uint32_t flavor_table_growth_size = 50; +uint32_t flavor_table_size = 0; +int32_t max_flavor_id = -1; +uint8_t *flavor_table = NULL; + +std::map used_chara_id; +const uint32_t chara_table_growth_size = 50; +uint32_t chara_table_size = 0; +int32_t max_chara_id = -1; +uint8_t *chara_table = NULL; +static uint8_t chara_table_nullptr[1] = {}; + +std::map used_music_id; +const uint32_t music_table_growth_size = 50; +uint32_t music_table_size = 0; +int32_t max_music_id = -1; +uint8_t *music_table = NULL; + +const uint32_t chart_table_growth_size = 50; +uint32_t chart_table_size = 0; +int32_t max_chart_id = -1; +uint8_t *chart_table = NULL; + +bool file_exists(const char *filename) { + FILE *test = fopen(filename, "rb"); + + if (!test) { + return false; + } + + fclose(test); + return true; +} + +uint8_t *find_string(uint8_t *input, size_t len) { + for (uint32_t i = 0; i < string_table_size - 1; i++) { + if (memcmp(string_table + i, input, len) == 0 && string_table[i + len + 1] == 0) { + return string_table + i; + } + } + + return nullptr; +} + +uint8_t *add_string(uint8_t *input) { + // Try to find string in string_table. + // If it exists, return that address. + // Otherwise add it to the table. + // If the string can't fit into the table, grow the table before adding it. + + // Note for future self/other people: + // The code doesn't really benefit from compressing the duplicate strings + // in the string table and it only slows it down greatly. + // The full chara and music database uses less than 1mb total so it's not an issue. + if (input == NULL) { + return NULL; + } + + uint32_t len = strlen((char *)input); + + if (len == 0) { + return NULL; + } + + if (string_table == NULL || string_table_idx + len + 1 >= string_table_size) { + // Realloc array to hold more strings + uint32_t new_string_table_size = string_table_idx + len + 1 + string_table_growth_size; + uint8_t *string_table_new = (uint8_t *)realloc(string_table, new_string_table_size + 1); + + if (string_table_new == NULL) { + printf("Couldn't relloc array from %d bytes to %d bytes, exiting...\n", + string_table_size, string_table_size + string_table_growth_size); + exit(1); + } + + // Zero out new section + memset(string_table_new + string_table_size, 0, new_string_table_size - string_table_size); + + string_table = string_table_new; + string_table_size = new_string_table_size; + } + + uint8_t *output = string_table + string_table_idx; + memcpy(string_table + string_table_idx, input, len); + string_table[string_table_idx + len + 1] = 0; + string_table_idx += len + 1; + + return output; +} + +void add_chart_type_flag(uint32_t cur_idx, int8_t flag) { chart_type_overrides[cur_idx] = flag; } + +int8_t get_chart_type_override(uint8_t *buffer, uint32_t music_idx, uint32_t chart_idx) { + music_entry *m = (music_entry *)buffer; + uint32_t idx = m[music_idx].charts[chart_idx]; + + if (chart_type_overrides.find(idx) == chart_type_overrides.end()) { + printf("Couldn't find chart\n"); + return -1; + } + + printf("Found chart: %d\n", chart_type_overrides[idx]); + if (chart_type_overrides[idx] == 1) { + // Uses new chart type + return 0; + } else if (chart_type_overrides[idx] == 0) { + // Does not use new chart + return 1; + } + + return -1; +} + +uint32_t get_lapis_shape_id(uint8_t *lapis_shape_ptr) { + if (!lapis_shape_ptr) { + return 0; + } + + RET_COMPARE_INDEX(lapis_shape_ptr, "dia", 1) + RET_COMPARE_INDEX(lapis_shape_ptr, "tear", 2) + RET_COMPARE_INDEX(lapis_shape_ptr, "heart", 3) + RET_COMPARE_INDEX(lapis_shape_ptr, "squ", 4) + + return 0; +} + +uint32_t get_lapis_color_id(uint8_t *lapis_color_ptr) { + if (!lapis_color_ptr) { + return 0; + } + + RET_COMPARE_INDEX(lapis_color_ptr, "blue", 1) + RET_COMPARE_INDEX(lapis_color_ptr, "pink", 2) + RET_COMPARE_INDEX(lapis_color_ptr, "red", 3) + RET_COMPARE_INDEX(lapis_color_ptr, "green", 4) + RET_COMPARE_INDEX(lapis_color_ptr, "normal", 5) + RET_COMPARE_INDEX(lapis_color_ptr, "yellow", 6) + RET_COMPARE_INDEX(lapis_color_ptr, "purple", 7) + RET_COMPARE_INDEX(lapis_color_ptr, "black", 8) + + return 0; +} + +uint16_t get_chara_idx(const uint8_t *chara_id) { + if (!chara_id || strlen((char *)chara_id) == 0) { + return 0; + } + + // This table is limited so keep it as small as possible + for (int32_t i = 0; i <= max_chara_id; i++) { + character_entry *cur = (character_entry *)chara_table + i; + + if (chara_id != NULL && cur->chara_id_ptr != NULL && + strlen((char *)cur->chara_id_ptr) == strlen((char *)chara_id) && + strcmp((char *)cur->chara_id_ptr, (char *)chara_id) == 0) { + return i; + } + } + + return 0; +} + +uint32_t chart_label_to_idx(uint8_t *chart_label) { + if (!chart_label) { + return 1; + } + + RET_COMPARE_INDEX(chart_label, "ep", 0) + RET_COMPARE_INDEX(chart_label, "np", 1) + RET_COMPARE_INDEX(chart_label, "hp", 2) + RET_COMPARE_INDEX(chart_label, "op", 3) + RET_COMPARE_INDEX(chart_label, "bp_n", 4) + RET_COMPARE_INDEX(chart_label, "bn", 4) + RET_COMPARE_INDEX(chart_label, "bp_h", 5) + RET_COMPARE_INDEX(chart_label, "bh", 5) + + RET_COMPARE_INDEX(chart_label, "0", 0) + RET_COMPARE_INDEX(chart_label, "1", 1) + RET_COMPARE_INDEX(chart_label, "2", 2) + RET_COMPARE_INDEX(chart_label, "3", 3) + RET_COMPARE_INDEX(chart_label, "4", 4) + RET_COMPARE_INDEX(chart_label, "5", 5) + + return 1; +} + +fontstyle_entry *get_fontstyle(int32_t cur_idx) { + if (cur_idx < 11) { + return NULL; + } + + cur_idx -= 11; + if (cur_idx > fontmax_style_id) { + return NULL; + } + + return (fontstyle_entry *)fontstyle_table + cur_idx; +} + +character_entry *get_chara(int32_t cur_idx) { + if (used_chara_id.find(cur_idx) == used_chara_id.end()) { + return NULL; + } + + return (character_entry *)chara_table + cur_idx; +} + +music_entry *get_music(int32_t cur_idx) { + if (used_music_id.find(cur_idx) == used_music_id.end()) { + return NULL; + } + + return (music_entry *)music_table + cur_idx; +} + +chart_entry *get_chart(int32_t cur_idx) { + if (cur_idx > max_chart_id) { + return NULL; + } + + return (chart_entry *)chart_table + cur_idx; +} + +uint32_t add_style(int32_t cur_idx, uint32_t fontface, uint32_t color, uint32_t height, + uint32_t width) { + if (fontstyle_table == NULL || cur_idx + 1 >= (int)fontstyle_table_size) { + // Realloc array to hold more styles + uint32_t new_style_table_size = cur_idx + 1 + fontstyle_table_growth_size; + uint8_t *style_table_new = (uint8_t *)realloc(fontstyle_table, (new_style_table_size + 1) * + sizeof(fontstyle_entry)); + + if (style_table_new == NULL) { + printf("Couldn't relloc array from %d bytes to %d bytes, exiting...\n", + fontstyle_table_size, fontstyle_table_size + fontstyle_table_growth_size); + exit(1); + } + + // Zero out new section + memset(style_table_new + fontstyle_table_size * sizeof(fontstyle_entry), 0, + (new_style_table_size - fontstyle_table_size) * sizeof(fontstyle_entry)); + + fontstyle_table = style_table_new; + fontstyle_table_size = new_style_table_size; + } + + // This table is limited so keep it as small as possible + for (int32_t i = 0; i <= fontmax_style_id; i++) { + fontstyle_entry *cur = (fontstyle_entry *)fontstyle_table + i; + + if (cur->fontface == fontface && cur->color == color && cur->height == height && + cur->width == width) { + return i; + } + } + + fontstyle_entry *ptr = (fontstyle_entry *)fontstyle_table + cur_idx; + memset(ptr, 0, sizeof(fontstyle_entry)); + ptr->fontface = fontface; + ptr->color = color; + ptr->height = height; + ptr->width = width; + + if (cur_idx > fontmax_style_id) { + fontmax_style_id = cur_idx; + } + + return cur_idx; +} + +uint32_t add_flavor(int32_t cur_idx, uint8_t *phrase1, uint8_t *phrase2, uint8_t *phrase3, + uint8_t *phrase4, uint8_t *phrase5, uint8_t *phrase6, uint8_t *birthday, + uint8_t chara1_birth_month, uint8_t chara2_birth_month, + uint8_t chara3_birth_month, uint8_t chara1_birth_date, + uint8_t chara2_birth_date, uint8_t chara3_birth_date, uint16_t style1, + bool style2_flag, uint16_t style3, uint32_t fontstyle_fontface, + uint32_t fontstyle_color, uint32_t fontstyle_height, uint32_t fontstyle_width) { + if (flavor_table == NULL || cur_idx + 1 >= (int)flavor_table_size) { + // Realloc array to hold more flavors + uint32_t new_flavor_table_size = cur_idx + 1 + flavor_table_growth_size; + uint8_t *flavor_table_new = + (uint8_t *)realloc(flavor_table, (new_flavor_table_size + 1) * sizeof(flavor_entry)); + + if (flavor_table_new == NULL) { + printf("Couldn't relloc array from %d bytes to %d bytes, exiting...\n", + flavor_table_size, flavor_table_size + flavor_table_growth_size); + exit(1); + } + + // Zero out new section + memset(flavor_table_new + flavor_table_size * sizeof(flavor_entry), 0, + (new_flavor_table_size - flavor_table_size) * sizeof(flavor_entry)); + + flavor_table = flavor_table_new; + flavor_table_size = new_flavor_table_size; + } + + // This table is limited so keep it as small as possible + for (int32_t i = 0; i < cur_idx; i++) { + flavor_entry *cur = (flavor_entry *)flavor_table + i; + + fontstyle_entry *fs = get_fontstyle(cur->style2); + + if (phrase1 != NULL && cur->phrase1 != NULL && + strlen((char *)cur->phrase1) == strlen((char *)phrase1) && + strcmp((char *)cur->phrase1, (char *)phrase1) == 0 && phrase2 != NULL && + cur->phrase2 != NULL && strlen((char *)cur->phrase2) == strlen((char *)phrase2) && + strcmp((char *)cur->phrase2, (char *)phrase2) == 0 && phrase3 != NULL && + cur->phrase3 != NULL && strlen((char *)cur->phrase3) == strlen((char *)phrase3) && + strcmp((char *)cur->phrase3, (char *)phrase3) == 0 && phrase4 != NULL && + cur->phrase4 != NULL && strlen((char *)cur->phrase4) == strlen((char *)phrase4) && + strcmp((char *)cur->phrase4, (char *)phrase4) == 0 && phrase5 != NULL && + cur->phrase5 != NULL && strlen((char *)cur->phrase5) == strlen((char *)phrase5) && + strcmp((char *)cur->phrase5, (char *)phrase5) == 0 && phrase6 != NULL && + cur->phrase6 != NULL && strlen((char *)cur->phrase6) == strlen((char *)phrase6) && + strcmp((char *)cur->phrase6, (char *)phrase6) == 0 && birthday != NULL && + cur->birthday_ptr != NULL && + strlen((char *)cur->birthday_ptr) == strlen((char *)birthday) && + strcmp((char *)cur->birthday_ptr, (char *)birthday) == 0 && + cur->chara1_birth_month == chara1_birth_month && + cur->chara2_birth_month == chara2_birth_month && + cur->chara3_birth_month == chara3_birth_month && + cur->chara1_birth_date == chara1_birth_date && + cur->chara2_birth_date == chara2_birth_date && + cur->chara3_birth_date == chara3_birth_date && cur->style1 == style1 && + cur->style3 == style3 && + (fs == NULL // TODO: Would a NULL fontstyle actually be an error? + || (fs != NULL && fs->fontface == fontstyle_fontface && fs->color == fontstyle_color && + fs->height == fontstyle_height && fs->width == fontstyle_width))) { + return i; + } + } + + uint16_t style2 = add_style(fontmax_style_id + 1, fontstyle_fontface, fontstyle_color, + fontstyle_height, fontstyle_width) + + 11; + + flavor_entry *ptr = (flavor_entry *)flavor_table + cur_idx; + memset(ptr, 0, sizeof(flavor_entry)); + if (phrase1) { + memcpy(ptr->phrase1, phrase1, sizeof(ptr->phrase1) - 1); + } + + if (phrase2) { + memcpy(ptr->phrase2, phrase2, sizeof(ptr->phrase2) - 1); + } + + if (phrase3) { + memcpy(ptr->phrase3, phrase3, sizeof(ptr->phrase3) - 1); + } + + if (phrase4) { + memcpy(ptr->phrase4, phrase4, sizeof(ptr->phrase4) - 1); + } + + if (phrase5) { + memcpy(ptr->phrase5, phrase5, sizeof(ptr->phrase5) - 1); + } + + if (phrase6) { + memcpy(ptr->phrase6, phrase6, sizeof(ptr->phrase6) - 1); + } + + ptr->birthday_ptr = birthday; + ptr->chara1_birth_month = chara1_birth_month; + ptr->chara2_birth_month = chara2_birth_month; + ptr->chara3_birth_month = chara3_birth_month; + ptr->chara1_birth_date = chara1_birth_date; + ptr->chara2_birth_date = chara2_birth_date; + ptr->chara3_birth_date = chara3_birth_date; + ptr->style1 = style1; + ptr->style2 = style2; + ptr->style3 = style3; + + if (cur_idx > max_flavor_id) { + max_flavor_id = cur_idx; + } + + return cur_idx; +} + +uint32_t add_chara(int32_t cur_idx, uint8_t *chara_id_ptr, uint32_t flags, uint8_t *folder_ptr, + uint8_t *gg_ptr, uint8_t *cs_ptr, uint8_t *icon1_ptr, uint8_t *icon2_ptr, + uint16_t chara_xw, uint16_t chara_yh, uint32_t display_flags, int16_t flavor_idx, + uint8_t chara_variation_num, uint8_t *sort_name_ptr, uint8_t *disp_name_ptr, + uint32_t file_type, uint32_t lapis_shape, uint8_t lapis_color, uint8_t *ha_ptr, + uint8_t *catchtext_ptr, int16_t win2_trigger, uint32_t game_version) { + if (chara_table == NULL || cur_idx + 1 >= (int32_t)chara_table_size) { + // Realloc array to hold more charas + uint32_t new_chara_table_size = cur_idx + 1 + chara_table_growth_size; + uint8_t *chara_table_new = + (uint8_t *)realloc(chara_table, (new_chara_table_size + 1) * sizeof(character_entry)); + + if (chara_table_new == NULL) { + printf("Couldn't relloc array from %d bytes to %d bytes, exiting...\n", + chara_table_size, chara_table_size + chara_table_growth_size); + exit(1); + } + + // Zero out new section + memset(chara_table_new + chara_table_size * sizeof(character_entry), 0, + (new_chara_table_size - chara_table_size) * sizeof(character_entry)); + + // Initialize new entries to sane defaults + for (uint32_t i = chara_table_size; i < new_chara_table_size; i++) { + character_entry *chara_entry = (character_entry *)chara_table_new + i; + memset(chara_entry, 0, sizeof(*chara_entry)); + + chara_entry->chara_id_ptr = add_string((uint8_t *)"bamb_1a"); + chara_entry->flags = 0x31; + chara_entry->folder_ptr = add_string((uint8_t *)"22"); + chara_entry->gg_ptr = add_string((uint8_t *)"gg_bamb_1a"); + chara_entry->cs_ptr = add_string((uint8_t *)"cs_bamb_1a"); + chara_entry->icon1_ptr = add_string((uint8_t *)"cs_a"); + chara_entry->icon2_ptr = add_string((uint8_t *)"cs_b"); + chara_entry->chara_xw = 240; + chara_entry->chara_yh = 220; + chara_entry->file_type = 53; + chara_entry->lapis_shape = get_lapis_shape_id((uint8_t *)"dia"); + chara_entry->lapis_color = get_lapis_color_id((uint8_t *)"yellow"); + chara_entry->win2_trigger = -1; + } + + chara_table = chara_table_new; + chara_table_size = new_chara_table_size; + } + + character_entry *ptr = (character_entry *)chara_table + cur_idx; + ptr->chara_id_ptr = chara_id_ptr; + ptr->flags = flags; + ptr->folder_ptr = folder_ptr; + ptr->gg_ptr = gg_ptr; + ptr->cs_ptr = cs_ptr; + ptr->icon1_ptr = icon1_ptr; + ptr->icon2_ptr = icon2_ptr; + ptr->chara_xw = chara_xw; + ptr->chara_yh = chara_yh; + ptr->display_flags = display_flags; + ptr->flavor_idx = flavor_idx; + ptr->chara_variation_num = chara_variation_num; + ptr->sort_name_ptr = sort_name_ptr; + ptr->disp_name_ptr = disp_name_ptr; + ptr->file_type = file_type; + ptr->lapis_shape = lapis_shape; + ptr->lapis_color = lapis_color; + ptr->ha_ptr = ha_ptr; + ptr->catchtext_ptr = catchtext_ptr ? catchtext_ptr : chara_table_nullptr; + ptr->win2_trigger = win2_trigger; + ptr->game_version = game_version; + + if (cur_idx > max_chara_id) { + max_chara_id = cur_idx; + } + + used_chara_id[cur_idx] = true; + + return cur_idx; +} + +uint32_t add_music(int32_t cur_idx, uint8_t *fw_genre_ptr, uint8_t *fw_title_ptr, + uint8_t *fw_artist_ptr, uint8_t *genre_ptr, uint8_t *title_ptr, + uint8_t *artist_ptr, uint16_t chara1, uint16_t chara2, uint32_t mask, + uint32_t folder, uint32_t cs_version, uint32_t categories, uint8_t *diffs, + uint16_t *charts, uint8_t *ha_ptr, uint32_t chara_x, uint32_t chara_y, + uint16_t *unk1, uint16_t *display_bpm, uint8_t *hold_flags, bool allow_resize) { + if (allow_resize && (music_table == NULL || cur_idx + 1 >= (int32_t)music_table_size)) { + // Realloc array to hold more musics + uint32_t new_music_table_size = cur_idx + 1 + music_table_growth_size; + uint8_t *music_table_new = + (uint8_t *)realloc(music_table, (new_music_table_size + 1) * sizeof(music_entry)); + + if (music_table_new == NULL) { + printf("Couldn't relloc array from %d bytes to %d bytes, exiting...\n", + music_table_size, music_table_size + music_table_growth_size); + exit(1); + } + + // Initialize new entries to sane defaults + for (uint32_t i = music_table_size; i < new_music_table_size; i++) { + music_entry *e = (music_entry *)music_table_new + i; + memset(e, 0, sizeof(*e)); + + e->fw_genre_ptr = filler_str; + e->fw_title_ptr = filler_str; + e->fw_artist_ptr = filler_str; + e->genre_ptr = filler_str; + e->title_ptr = filler_str; + e->artist_ptr = filler_str; + e->mask = 0x80000000; + e->folder = 25; + memset((char *)e->diffs, 1, sizeof(e->diffs)); + } + + music_table = music_table_new; + music_table_size = new_music_table_size; + } + + music_entry *ptr = (music_entry *)music_table + cur_idx; + ptr->fw_genre_ptr = fw_genre_ptr; + ptr->fw_title_ptr = fw_title_ptr; + ptr->fw_artist_ptr = fw_artist_ptr; + ptr->genre_ptr = genre_ptr; + ptr->title_ptr = title_ptr; + ptr->artist_ptr = artist_ptr; + ptr->chara1 = chara1; + ptr->chara2 = chara2; + ptr->mask = mask; + ptr->folder = folder; + ptr->cs_version = cs_version; + ptr->categories = categories; + memcpy(ptr->diffs, (char *)diffs, sizeof(uint8_t) * 6); + memcpy(ptr->charts, charts, sizeof(uint16_t) * 7); + ptr->ha_ptr = ha_ptr; + ptr->chara_x = chara_x; + ptr->chara_y = chara_y; + memcpy(ptr->unk1, unk1, sizeof(uint16_t) * 32); + memcpy(ptr->display_bpm, display_bpm, sizeof(uint16_t) * 12); + memcpy(ptr->hold_flags, hold_flags, sizeof(uint8_t) * 8); + + if (cur_idx > max_music_id) { + max_music_id = cur_idx; + } + + used_music_id[cur_idx] = true; + + return cur_idx; +} + +uint32_t add_chart(int32_t cur_idx, uint8_t *folder, uint8_t *filename, int32_t audio_param1, + int32_t audio_param2, int32_t audio_param3, int32_t audio_param4, + uint32_t file_type, uint16_t used_keys, bool override_idx) { + if (chart_table == NULL || cur_idx + 1 >= (int32_t)chart_table_size) { + // Realloc array to hold more charts + uint32_t new_chart_table_size = cur_idx + 1 + chart_table_growth_size; + uint8_t *chart_table_new = + (uint8_t *)realloc(chart_table, (new_chart_table_size + 1) * sizeof(chart_entry)); + + if (chart_table_new == NULL) { + printf("Couldn't relloc array from %d bytes to %d bytes, exiting...\n", + chart_table_size, chart_table_size + chart_table_growth_size); + exit(1); + } + + // Initialize new entries to sane defaults + for (uint32_t i = chart_table_size; i < new_chart_table_size; i++) { + chart_entry *e = (chart_entry *)chart_table_new + i; + memset(e, 0, sizeof(*e)); + } + + chart_table = chart_table_new; + chart_table_size = new_chart_table_size; + } + + // This table is limited so keep it as small as possible + for (int32_t i = 0; !override_idx && i <= max_chart_id; i++) { + chart_entry *cur = (chart_entry *)chart_table + i; + + if (folder != NULL && cur->folder_ptr != NULL && + strlen((char *)cur->folder_ptr) == strlen((char *)folder) && + strcmp((char *)cur->folder_ptr, (char *)folder) == 0 && filename != NULL && + cur->filename_ptr != NULL && + strlen((char *)cur->filename_ptr) == strlen((char *)filename) && + strcmp((char *)cur->filename_ptr, (char *)filename) == 0 && + cur->audio_param1 == audio_param1 && cur->audio_param2 == audio_param2 && + cur->audio_param3 == audio_param3 && cur->audio_param4 == audio_param4 && + cur->file_type == file_type && cur->used_keys == used_keys) { + return i; + } + } + + chart_entry *ptr = (chart_entry *)chart_table + cur_idx; + ptr->folder_ptr = folder; + ptr->filename_ptr = filename; + ptr->audio_param1 = audio_param1; + ptr->audio_param2 = audio_param2; + ptr->audio_param3 = audio_param3; + ptr->audio_param4 = audio_param4; + ptr->file_type = file_type; + ptr->used_keys = used_keys; + + if (cur_idx > max_chart_id) { + max_chart_id = cur_idx; + } + + return cur_idx; +} + +void parse_charadb(const char *input_filename, const char *target) { + if (!file_exists(input_filename)) { + printf("Couldn't find %s, skipping...\n", input_filename); + return; + } + + property *config_xml = load_prop_file(input_filename); + + if (target && strlen(target) > 0) { + char beforeTarget[64] = {}; + property_node_refer(config_xml, property_search(config_xml, NULL, "/database"), "before@", + PROPERTY_TYPE_ATTR, beforeTarget, 64); + + if (strlen(beforeTarget) > 0 && atoi(target) >= atoi(beforeTarget)) { + printf("Currently loading %s, found database that is only valid before %s, skipping %s...\n", beforeTarget, target, input_filename); + return; + } + + char afterTarget[64] = {}; + property_node_refer(config_xml, property_search(config_xml, NULL, "/database"), "after@", + PROPERTY_TYPE_ATTR, afterTarget, 64); + if (strlen(afterTarget) > 0 && atoi(target) < atoi(afterTarget)) { + printf("Currently loading %s, found database that is only valid after %s, skipping %s...\n", beforeTarget, target, input_filename); + return; + } + } + + property_node *prop = NULL; + if ((prop = property_search(config_xml, NULL, "/database/chara"))) { + // Iterate over all charas in /database + for (; prop != NULL; prop = property_node_traversal(prop, TRAVERSE_NEXT_SEARCH_RESULT)) { + char idxStr[256] = {}; + property_node_refer(config_xml, prop, "id@", PROPERTY_TYPE_ATTR, idxStr, + sizeof(idxStr)); + uint32_t idx = atoi(idxStr); + + // Get an existing music entry in memory + // If it exists, return the existing entry + // If it doesn't exist, create a new entry in memory + // Update the data in-place and make all parameters optional + character_entry *c = get_chara(idx); + bool is_fresh = c == NULL; + + if (is_fresh) { + // Default character entry + // TODO: Is there a better way I can make it force a new entry without having to + // define everything like this? + add_chara(idx, add_string((uint8_t *)"bamb_1a"), 0x31, add_string((uint8_t *)"22"), + add_string((uint8_t *)"gg_bamb_1a"), add_string((uint8_t *)"cs_bamb_1a"), + add_string((uint8_t *)"cs_a"), add_string((uint8_t *)"cs_b"), 240, 220, 0, + 0, 0, NULL, NULL, 53, get_lapis_shape_id((uint8_t *)"dia"), + get_lapis_color_id((uint8_t *)"yellow"), NULL, NULL, -1, 0); + + c = get_chara(idx); + } + + DWORD old_prot; + VirtualProtect((LPVOID)c, sizeof(character_entry), PAGE_EXECUTE_READWRITE, &old_prot); + + // Save to character table + READ_STR_OPT(config_xml, prop, "chara_id", c->chara_id_ptr) + READ_U32_OPT(config_xml, prop, "flags", c->flags) + READ_STR_OPT(config_xml, prop, "folder", c->folder_ptr) + READ_STR_OPT(config_xml, prop, "gg", c->gg_ptr) + READ_STR_OPT(config_xml, prop, "cs", c->cs_ptr) + READ_STR_OPT(config_xml, prop, "icon1", c->icon1_ptr) + READ_STR_OPT(config_xml, prop, "icon2", c->icon2_ptr) + READ_U16_OPT(config_xml, prop, "chara_xw", c->chara_xw) + READ_U16_OPT(config_xml, prop, "chara_yh", c->chara_yh) + READ_U32_OPT(config_xml, prop, "display_flags", c->display_flags) + READ_U8_OPT(config_xml, prop, "chara_variation_num", c->chara_variation_num) + READ_STR_OPT(config_xml, prop, "sort_name", c->sort_name_ptr) + READ_STR_OPT(config_xml, prop, "disp_name", c->disp_name_ptr) + READ_U32_OPT(config_xml, prop, "file_type", c->file_type) + READ_LAPIS_SHAPE_OPT(config_xml, prop, "lapis_shape", c->lapis_shape) + READ_LAPIS_COLOR_OPT(config_xml, prop, "lapis_color", c->lapis_color) + READ_STR_OPT(config_xml, prop, "ha", c->ha_ptr) + READ_STR_OPT(config_xml, prop, "catchtext", c->catchtext_ptr) + READ_S16_OPT(config_xml, prop, "win2_trigger", c->win2_trigger) + READ_U32_OPT(config_xml, prop, "game_version", c->game_version) + + property_node *prop_flavor = NULL; + int32_t flavor_idx = -1; + if ((prop_flavor = property_search(config_xml, prop, "/flavor"))) { + // Save to flavor table + READ_STR(config_xml, prop_flavor, "phrase1", phrase1, phrase1_ptr) + READ_STR(config_xml, prop_flavor, "phrase2", phrase2, phrase2_ptr) + READ_STR(config_xml, prop_flavor, "phrase3", phrase3, phrase3_ptr) + READ_STR(config_xml, prop_flavor, "phrase4", phrase4, phrase4_ptr) + READ_STR(config_xml, prop_flavor, "phrase5", phrase5, phrase5_ptr) + READ_STR(config_xml, prop_flavor, "phrase6", phrase6, phrase6_ptr) + READ_STR(config_xml, prop_flavor, "birthday", birthday, birthday_ptr) + READ_U8(config_xml, prop_flavor, "chara1_birth_month", chara1_birth_month) + READ_U8(config_xml, prop_flavor, "chara2_birth_month", chara2_birth_month) + READ_U8(config_xml, prop_flavor, "chara3_birth_month", chara3_birth_month) + READ_U8(config_xml, prop_flavor, "chara1_birth_date", chara1_birth_date) + READ_U8(config_xml, prop_flavor, "chara2_birth_date", chara2_birth_date) + READ_U8(config_xml, prop_flavor, "chara3_birth_date", chara3_birth_date) + READ_U16(config_xml, prop_flavor, "style1", style1) + READ_U16(config_xml, prop_flavor, "style3", style3) + + property_node *prop_flavor_style = NULL; + bool style2_flag = false; + uint32_t fontface = 0, color = 0, height = 0, width = 0; + if ((prop_flavor_style = property_search(config_xml, prop_flavor, "/style2"))) { + // Save to style table + READ_U32_OPT(config_xml, prop_flavor_style, "fontface", fontface) + READ_U32_OPT(config_xml, prop_flavor_style, "color", color) + READ_U32_OPT(config_xml, prop_flavor_style, "height", height) + READ_U32_OPT(config_xml, prop_flavor_style, "width", width) + + style2_flag = true; + + // printf("style[%d] idx[%d]\n", style_table_ptr, style2_idx); + } + + flavor_idx = add_flavor( + max_flavor_id + 1, phrase1_ptr, phrase2_ptr, phrase3_ptr, phrase4_ptr, + phrase5_ptr, phrase6_ptr, birthday_ptr, chara1_birth_month, chara2_birth_month, + chara3_birth_month, chara1_birth_date, chara2_birth_date, chara3_birth_date, + style1, style2_flag, style3, fontface, color, height, width); + + // printf("flavor[%d] idx[%d]\n", max_flavor_id, flavor_idx); + + if (flavor_idx > (int32_t)flavor_limit) { + // This is a hack because I don't feel like finding the proper buffers to patch + // for this + printf("WARNING: Overflowed flavor text table limit of %d, defaulting to index " + "0 instead of %d\n", + flavor_limit, flavor_idx); + flavor_idx = -1; + } + + c->flavor_idx = flavor_idx; + } + + if ((c->flavor_idx == 0 || c->flavor_idx == -1)) { + c->flavor_idx = 1; + printf("Setting default flavor for chara id %d\n", idx); + } + + VirtualProtect((LPVOID)c, sizeof(character_entry), old_prot, &old_prot); + } + } + + free(config_xml); +} + +void parse_musicdb(const char *input_filename, const char *target) { + if (!file_exists(input_filename)) { + printf("Couldn't find %s, skipping...\n", input_filename); + return; + } + + property *config_xml = load_prop_file(input_filename); + + if (target && strlen(target) > 0) { + char beforeTarget[64] = {}; + property_node_refer(config_xml, property_search(config_xml, NULL, "/database"), "before@", + PROPERTY_TYPE_ATTR, beforeTarget, 64); + + if (strlen(beforeTarget) > 0 && atoi(target) >= atoi(beforeTarget)) { + printf("Currently loading %s, found database that is only valid before %s, skipping %s...\n", beforeTarget, target, input_filename); + return; + } + + char afterTarget[64] = {}; + property_node_refer(config_xml, property_search(config_xml, NULL, "/database"), "after@", + PROPERTY_TYPE_ATTR, afterTarget, 64); + if (strlen(afterTarget) > 0 && atoi(target) < atoi(afterTarget)) { + printf("Currently loading %s, found database that is only valid after %s, skipping %s...\n", beforeTarget, target, input_filename); + return; + } + } + + property_node *prop = NULL; + if ((prop = property_search(config_xml, NULL, "/database/music"))) { + // Iterate over all musics in /database + for (; prop != NULL; prop = property_node_traversal(prop, TRAVERSE_NEXT_SEARCH_RESULT)) { + char idxStr[256] = {}; + property_node_refer(config_xml, prop, "id@", PROPERTY_TYPE_ATTR, idxStr, + sizeof(idxStr)); + uint32_t idx = atoi(idxStr); + + // Get an existing music entry in memory + // If it exists, return the existing entry + // If it doesn't exist, create a new entry in memory + // Update the data in-place and make all parameters optional + music_entry *m = get_music(idx); + bool is_fresh = m == NULL; + + if (is_fresh) { + // Default music entry + static const uint8_t diffs[6] = {0}; + static const uint16_t charts[7] = {0}; + static const uint16_t unk1[32] = {0}; + static const uint16_t display_bpm[12] = {0}; + static const uint8_t hold_flags[8] = {0}; + + // TODO: Is there a better way I can make it force a new entry without having to + // define everything like this? + add_music(idx, filler_str, filler_str, filler_str, filler_str, filler_str, + filler_str, 0, 0, 0x80000000, 25, 0, 0, (uint8_t *)&diffs[0], + (uint16_t *)&charts[0], NULL, 0, 0, (uint16_t *)&unk1[0], + (uint16_t *)&display_bpm[0], (uint8_t *)&hold_flags[0], true); + + m = get_music(idx); + } + + DWORD old_prot; + VirtualProtect((LPVOID)m, sizeof(music_entry), PAGE_EXECUTE_READWRITE, &old_prot); + + READ_STR_OPT(config_xml, prop, "fw_genre", m->fw_genre_ptr) + READ_STR_OPT(config_xml, prop, "fw_title", m->fw_title_ptr) + READ_STR_OPT(config_xml, prop, "fw_artist", m->fw_artist_ptr) + READ_STR_OPT(config_xml, prop, "genre", m->genre_ptr) + READ_STR_OPT(config_xml, prop, "title", m->title_ptr) + READ_STR_OPT(config_xml, prop, "artist", m->artist_ptr) + READ_CHARA_OPT(config_xml, prop, "chara1", m->chara1) + READ_CHARA_OPT(config_xml, prop, "chara2", m->chara2) + + uint32_t orig_mask = m->mask; + READ_U32_OPT(config_xml, prop, "mask", m->mask) + + READ_U32_OPT(config_xml, prop, "folder", m->folder) + READ_U32_OPT(config_xml, prop, "cs_version", m->cs_version) + READ_U32_OPT(config_xml, prop, "categories", m->categories) + READ_STR_OPT(config_xml, prop, "ha", m->ha_ptr) + READ_U32_OPT(config_xml, prop, "chara_x", m->chara_x) + READ_U32_OPT(config_xml, prop, "chara_y", m->chara_y) + READ_U16_ARR_OPT(config_xml, prop, "unk1", m->unk1, 32) + READ_U16_ARR_OPT(config_xml, prop, "display_bpm", m->display_bpm, 16) + + property_node *prop_chart = NULL; + int32_t default_chart_idx = -1; + const uint32_t CHART_MASKS[] = {0x00080000, 0, 0x01000000, + 0x02000000, 0, 0x04000000}; + + // Copy old chart flags from the previous mask + for (size_t i = 0; i < 6; i++) { + if ((orig_mask & CHART_MASKS[i]) != 0) { + m->mask |= CHART_MASKS[i]; + } + } + + if ((prop_chart = property_search(config_xml, prop, "charts/chart"))) { + for (; prop_chart != NULL; prop_chart = property_node_traversal( + prop_chart, TRAVERSE_NEXT_SEARCH_RESULT)) { + char chartIdxStr[256] = {}; + property_node_refer(config_xml, prop_chart, "idx@", PROPERTY_TYPE_ATTR, + chartIdxStr, sizeof(chartIdxStr)); + uint32_t chart_idx = chart_label_to_idx((uint8_t *)chartIdxStr); + + chart_entry *c2 = get_chart(m->charts[chart_idx]); + if (is_fresh || c2 == NULL) { + m->charts[chart_idx] = add_chart(max_chart_id + 1, filler_str, filler_str, + 0, 0, 0, 0, 0, 0, true); + } else { + m->charts[chart_idx] = + add_chart(max_chart_id + 1, c2->folder_ptr, c2->filename_ptr, + c2->audio_param1, c2->audio_param2, c2->audio_param3, + c2->audio_param4, c2->file_type, c2->used_keys, true); + } + + chart_entry *c = get_chart(m->charts[chart_idx]); + if (!c) { + printf("Couldn't find chart entry!\n"); + exit(1); + } + + DWORD old_prot_chart; + VirtualProtect((LPVOID)c, sizeof(chart_entry), PAGE_EXECUTE_READWRITE, &old_prot_chart); + + READ_STR_OPT(config_xml, prop_chart, "folder", c->folder_ptr) + READ_STR_OPT(config_xml, prop_chart, "filename", c->filename_ptr) + READ_S32_OPT(config_xml, prop_chart, "audio_param1", c->audio_param1) + READ_S32_OPT(config_xml, prop_chart, "audio_param2", c->audio_param2) + READ_S32_OPT(config_xml, prop_chart, "audio_param3", c->audio_param3) + READ_S32_OPT(config_xml, prop_chart, "audio_param4", c->audio_param4) + READ_U32_OPT(config_xml, prop_chart, "file_type", c->file_type) + READ_U16_OPT(config_xml, prop_chart, "used_keys", c->used_keys) + + READ_U8_OPT(config_xml, prop_chart, "diff", m->diffs[chart_idx]) + READ_U8_OPT(config_xml, prop_chart, "hold_flag", m->hold_flags[chart_idx]) + + if (property_search(config_xml, prop_chart, "force_new_chart_format")) { + READ_U32(config_xml, prop_chart, "force_new_chart_format", + force_new_chart_format) + add_chart_type_flag(m->charts[chart_idx], force_new_chart_format); + } else { + add_chart_type_flag(m->charts[chart_idx], m->hold_flags[chart_idx] == 1); + } + + m->mask |= CHART_MASKS[chart_idx]; + + if (chart_idx == 1) { + default_chart_idx = m->charts[chart_idx]; + } + + VirtualProtect((LPVOID)c, sizeof(chart_entry), old_prot_chart, &old_prot_chart); + } + } + + // Make sure that all entries in the charts array have a value. + if (m->charts[1] == 0 && default_chart_idx == -1) { + // A normal chart wasn't specified and one didn't originally exist. + // This will probably lead to the preview music not being the right preview. + for (int i = 0; i < 6; i++) { + if (m->charts[i] != 0) { + m->charts[1] = m->charts[i]; + break; + } + } + } + + VirtualProtect((LPVOID)m, sizeof(music_entry), old_prot, &old_prot); + } + } + + free(config_xml); +} + +void load_databases(const char *target_datecode) { + + SearchFile s; + printf("XML db files search...\n"); + s.search("data_mods", "xml", true); + auto result = s.getResult(); + + // Character databases must be loaded before music databases because the music databases could reference modified/new characters + for(uint16_t i=0;i fontstyle_table_size) { + fontstyle_table_size = style_size; + } + + if (flavor_size > flavor_table_size) { + flavor_table_size = flavor_size; + } + + if (chart_size > chart_table_size) { + chart_table_size = chart_size; + } + + if (music_size > music_table_size) { + music_table_size = music_size; + } + + if (chara_size > chara_table_size) { + chara_table_size = chara_size; + } + + flavor_limit = flavor_size; + + fontstyle_table = (uint8_t *)calloc(fontstyle_table_size, sizeof(fontstyle_entry)); + flavor_table = (uint8_t *)calloc(flavor_table_size, sizeof(flavor_entry)); + chara_table = (uint8_t *)calloc(chara_table_size, sizeof(character_entry)); + music_table = (uint8_t *)calloc(music_table_size, sizeof(music_entry)); + chart_table = (uint8_t *)calloc(chart_table_size * 25, sizeof(chart_entry)); + + // Copy style table for use in the flavor table + printf("Copying style table\n"); + for (uint32_t idx = 0; idx < style_size; idx++) { + fontstyle_entry *cur = (fontstyle_entry *)orig_style_data + idx; + + add_style(idx, cur->fontface, cur->color, cur->height, cur->width); + } + + // Copy flavor table for use in the character database + printf("Copying flavor table\n"); + for (uint32_t idx = 0; idx < flavor_size; idx++) { + flavor_entry *cur = (flavor_entry *)orig_flavor_data + idx; + + fontstyle_entry *cur_style = (fontstyle_entry *)orig_style_data + (cur->style2 - 11); + + add_flavor( + idx, cur->phrase1, cur->phrase2, cur->phrase3, cur->phrase4, cur->phrase5, cur->phrase6, + cur->birthday_ptr != NULL + ? add_string(cur->birthday_ptr) + : NULL, + cur->chara1_birth_month, cur->chara2_birth_month, cur->chara3_birth_month, + cur->chara1_birth_date, cur->chara2_birth_date, cur->chara3_birth_date, cur->style1, + true, cur->style3, cur_style->fontface, cur_style->color, cur_style->height, + cur_style->width); + } + + // Copy character database for use in the music database + printf("Copying character database\n"); + for (uint32_t idx = 0; idx < chara_size; idx++) { + character_entry *cur = (character_entry *)orig_chara_data + idx; + add_chara( + idx, + cur->chara_id_ptr != NULL + ? add_string(cur->chara_id_ptr) + : NULL, + cur->flags, + cur->folder_ptr != NULL + ? add_string(cur->folder_ptr) + : NULL, + cur->gg_ptr != NULL + ? add_string(cur->gg_ptr) + : NULL, + cur->cs_ptr != NULL + ? add_string(cur->cs_ptr) + : NULL, + cur->icon1_ptr != NULL + ? add_string(cur->icon1_ptr) + : NULL, + cur->icon2_ptr != NULL + ? add_string(cur->icon2_ptr) + : NULL, + cur->chara_xw, cur->chara_yh, cur->display_flags, cur->flavor_idx, + cur->chara_variation_num, + cur->sort_name_ptr != NULL + ? add_string(cur->sort_name_ptr) + : NULL, + cur->disp_name_ptr != NULL + ? add_string(cur->disp_name_ptr) + : NULL, + cur->file_type, cur->lapis_shape, cur->lapis_color, + cur->ha_ptr != NULL + ? add_string(cur->ha_ptr) + : NULL, + cur->catchtext_ptr != NULL + ? add_string(cur->catchtext_ptr) + : NULL, + cur->win2_trigger, cur->game_version); + } + + // Copy music database + printf("Copying music database\n"); + for (uint32_t idx = 0; idx < music_size; idx++) { + music_entry *cur = (music_entry *)orig_music_data + idx; + + uint16_t charts[7] = {0}; + for (int i = 0; i < 7; i++) { + chart_entry *cur_chart = (chart_entry *)orig_chart_data + cur->charts[i]; + charts[i] = add_chart(cur->charts[i], + cur_chart->folder_ptr != NULL + ? add_string(cur_chart->folder_ptr) + : NULL, + cur_chart->filename_ptr != NULL + ? add_string(cur_chart->filename_ptr) + : NULL, + cur_chart->audio_param1, cur_chart->audio_param2, + cur_chart->audio_param3, cur_chart->audio_param4, + cur_chart->file_type, cur_chart->used_keys, true); + } + + add_music( + idx, + cur->fw_genre_ptr != NULL + ? add_string(cur->fw_genre_ptr) + : NULL, + cur->fw_title_ptr != NULL + ? add_string(cur->fw_title_ptr) + : NULL, + cur->fw_artist_ptr != NULL + ? add_string(cur->fw_artist_ptr) + : NULL, + cur->genre_ptr != NULL + ? add_string(cur->genre_ptr) + : NULL, + cur->title_ptr != NULL + ? add_string(cur->title_ptr) + : NULL, + cur->artist_ptr != NULL + ? add_string(cur->artist_ptr) + : NULL, + cur->chara1, cur->chara2, cur->mask, cur->folder, cur->cs_version, cur->categories, + cur->diffs, charts, + cur->ha_ptr != NULL + ? add_string(cur->ha_ptr) + : NULL, + cur->chara_x, cur->chara_y, cur->unk1, cur->display_bpm, cur->hold_flags, true); + } + + load_databases((const char *)target_datecode); + + // Add some filler charts to fix some bugs (hack) + for (int i = 0; i < 10; i++) { + add_chart(max_chart_id + i + 1, NULL, NULL, 0, 0, 0, 0, 0, 0, true); + } + + // Add one extra song as padding + { + if (max_music_id > (int64_t)music_size) { + static const uint8_t diffs[6] = {0}; + static const uint16_t charts[7] = {0}; + static const uint16_t unk1[32] = {0}; + static const uint16_t display_bpm[12] = {0}; + static const uint8_t hold_flags[8] = {0}; + + add_music(max_music_id + 1, filler_str, filler_str, filler_str, filler_str, filler_str, + filler_str, 0, 0, 0x80000000, 25, 0, 0, (uint8_t *)&diffs[0], + (uint16_t *)&charts[0], NULL, 0, 0, (uint16_t *)&unk1[0], + (uint16_t *)&display_bpm[0], (uint8_t *)&hold_flags[0], true); + } + + if (max_chara_id > (int64_t)chara_size) { + add_chara(max_chara_id + 1, add_string((uint8_t *)"bamb_1a"), 0x31, add_string((uint8_t *)"22"), + add_string((uint8_t *)"gg_bamb_1a"), add_string((uint8_t *)"cs_bamb_1a"), + add_string((uint8_t *)"cs_a"), add_string((uint8_t *)"cs_b"), 240, 220, 0, + 0, 0, NULL, NULL, 53, get_lapis_shape_id((uint8_t *)"dia"), + get_lapis_color_id((uint8_t *)"yellow"), NULL, NULL, -1, 0); + } + } + + if (!is_expansion_allowed) { + max_music_id = music_size; + max_chara_id = chara_size; + fontmax_style_id = style_size; + max_flavor_id = flavor_size; + max_chart_id = chart_size; + } + + if (is_redirection_allowed) { + *new_music_table = music_table; + *new_chara_table = chara_table; + *new_style_table = fontstyle_table; + *new_flavor_table = flavor_table; + *new_chart_table = chart_table; + } else { + // Copy new buffer data over top original buffers + // EXPERIMENTAL! + // Not really a supported feature, but I added it just in case someone wants to replace data + // in-place + patch_memory((uint64_t)orig_music_data, (char *)music_table, + sizeof(music_entry) * music_size); + patch_memory((uint64_t)orig_chart_data, (char *)chart_table, + sizeof(chart_entry) * chart_size); + patch_memory((uint64_t)orig_style_data, (char *)fontstyle_table, + sizeof(fontstyle_entry) * style_size); + patch_memory((uint64_t)orig_flavor_data, (char *)flavor_table, + sizeof(flavor_entry) * flavor_size); + patch_memory((uint64_t)orig_chara_data, (char *)chara_table, + sizeof(character_entry) * chara_size); + + *new_music_table = (uint8_t *)orig_music_data; + *new_chara_table = (uint8_t *)orig_chara_data; + *new_style_table = (uint8_t *)orig_style_data; + *new_flavor_table = (uint8_t *)orig_flavor_data; + *new_chart_table = (uint8_t *)orig_chart_data; + } + + *new_music_size = max_music_id; + *new_chara_size = max_chara_id; + *new_style_size = fontmax_style_id; + *new_flavor_size = max_flavor_id; + *new_chart_size = max_chart_id; + + if (force_unlocks) { + music_entry *m = (music_entry *)*new_music_table; + for (uint64_t i = 0; i < *new_music_size; i++) { + uint32_t new_mask = m[i].mask & ~0x8000080; + + if (m[i].title_ptr != NULL && new_mask != m[i].mask) { + printf("Unlocking [%04lld] %s... %08x -> %08x\n", i, m[i].title_ptr, m[i].mask, + new_mask); + patch_memory((uint64_t)&m[i].mask, (char *)&new_mask, sizeof(uint32_t)); + } + } + + character_entry *c = (character_entry *)*new_chara_table; + for (uint64_t i = 0; i < *new_chara_size; i++) { + uint32_t new_flags = c[i].flags & ~3; + + if (new_flags != c[i].flags && c[i].disp_name_ptr != NULL && strlen((char*)c[i].disp_name_ptr) > 0) { + printf("Unlocking [%04lld] %s... %08x -> %08x\n", i, c[i].disp_name_ptr, c[i].flags, new_flags); + patch_memory((uint64_t)&c[i].flags, (char *)&new_flags, sizeof(uint32_t)); + + if ((c[i].flavor_idx == 0 || c[i].flavor_idx == -1)) { + int flavor_idx = 1; + patch_memory((uint64_t)&c[i].flavor_idx, (char *)&flavor_idx, sizeof(uint32_t)); + printf("Setting default flavor for chara id %lld\n", i); + } + } + } + } +} diff --git a/popnhax/loader.h b/popnhax/loader.h new file mode 100644 index 0000000..4d91185 --- /dev/null +++ b/popnhax/loader.h @@ -0,0 +1,18 @@ +#ifndef __LOADER_H__ +#define __LOADER_H__ + +#include + +int8_t get_chart_type_override(uint8_t *, uint32_t, uint32_t); + +void musichax_core_init(bool force_unlocks, bool is_expansion_allowed, bool is_redirection_allowed, + char *target_datecode, char *base_data, uint64_t music_size, + uint64_t *new_music_size, char *orig_music_data, uint8_t **new_music_table, + uint64_t chart_size, uint64_t *new_chart_size, char *orig_chart_data, + uint8_t **new_chart_table, uint64_t style_size, uint64_t *new_style_size, + char *orig_style_data, uint8_t **new_style_table, uint64_t flavor_size, + uint64_t *new_flavor_size, char *orig_flavor_data, + uint8_t **new_flavor_table, uint64_t chara_size, uint64_t *new_chara_size, + char *orig_chara_data, uint8_t **new_chara_table); + +#endif diff --git a/popnhax/popnhax.def b/popnhax/popnhax.def new file mode 100644 index 0000000..11e417a --- /dev/null +++ b/popnhax/popnhax.def @@ -0,0 +1,2 @@ +LIBRARY popnhax +EXPORTS diff --git a/popnhax/tableinfo.h b/popnhax/tableinfo.h new file mode 100644 index 0000000..3eea927 --- /dev/null +++ b/popnhax/tableinfo.h @@ -0,0 +1,95 @@ +#ifndef __TABLEINFO_H__ +#define __TABLEINFO_H__ + +#include + +typedef struct { + uint8_t *folder_ptr; + uint8_t *filename_ptr; + int32_t audio_param1; + int32_t audio_param2; + int32_t audio_param3; + int32_t audio_param4; + uint32_t file_type; + uint16_t used_keys; + uint8_t pad[2]; +} chart_entry; + +typedef struct { + uint32_t fontface; + uint32_t color; + uint32_t height; + uint32_t width; +} fontstyle_entry; + +typedef struct { + uint8_t phrase1[13]; + uint8_t phrase2[13]; + uint8_t phrase3[13]; + uint8_t phrase4[13]; + uint8_t phrase5[13]; + uint8_t phrase6[13]; + uint8_t _pad1[2]; + uint8_t *birthday_ptr; + uint8_t chara1_birth_month; + uint8_t chara2_birth_month; + uint8_t chara3_birth_month; + uint8_t chara1_birth_date; + uint8_t chara2_birth_date; + uint8_t chara3_birth_date; + uint16_t style1; + uint16_t style2; + uint16_t style3; +} flavor_entry; + +typedef struct { + uint8_t *chara_id_ptr; + uint32_t flags; + uint8_t *folder_ptr; + uint8_t *gg_ptr; + uint8_t *cs_ptr; + uint8_t *icon1_ptr; + uint8_t *icon2_ptr; + uint16_t chara_xw; + uint16_t chara_yh; + uint32_t display_flags; + int16_t flavor_idx; + uint8_t chara_variation_num; + uint8_t _pad1[1]; + uint8_t *sort_name_ptr; + uint8_t *disp_name_ptr; + uint32_t file_type; + uint32_t lapis_shape; + uint8_t lapis_color; + uint8_t _pad2[3]; + uint8_t *ha_ptr; + uint8_t *catchtext_ptr; + int16_t win2_trigger; + uint8_t _pad3[2]; + uint32_t game_version; +} character_entry; + +typedef struct { + uint8_t *fw_genre_ptr; + uint8_t *fw_title_ptr; + uint8_t *fw_artist_ptr; + uint8_t *genre_ptr; + uint8_t *title_ptr; + uint8_t *artist_ptr; + uint16_t chara1; + uint16_t chara2; + uint32_t mask; + uint32_t folder; + uint32_t cs_version; + uint32_t categories; + uint8_t diffs[6]; + uint16_t charts[7]; + uint8_t *ha_ptr; + uint32_t chara_x; + uint32_t chara_y; + uint16_t unk1[32]; + uint16_t display_bpm[12]; + uint8_t hold_flags[8]; +} music_entry; + +#endif \ No newline at end of file diff --git a/popnhax/xmlhelper.h b/popnhax/xmlhelper.h new file mode 100644 index 0000000..e590c12 --- /dev/null +++ b/popnhax/xmlhelper.h @@ -0,0 +1,134 @@ +#define READ_HEX(_xml, _prop, _prop_name, _var_output) \ + {unsigned char _temp[256] = {}; \ + property_node_refer(_xml, _prop, _prop_name, PROPERTY_TYPE_STR, _temp, sizeof(_temp)); \ + _var_output = strtol((const char*)_temp, NULL, 16); \ + } \ + +#define READ_STR_RAW(_xml, _prop, _prop_name, _var_name) \ + unsigned char _var_name[256] = {}; \ + property_node_refer(_xml, _prop, _prop_name, PROPERTY_TYPE_STR, _var_name, sizeof(_var_name)); \ + +#define READ_STR(_xml, _prop, _prop_name, _var_name, _var_output) \ + unsigned char _var_name[256] = {}; \ + property_node_refer(_xml, _prop, _prop_name, PROPERTY_TYPE_STR, _var_name, sizeof(_var_name)); \ + uint8_t *_var_output = add_string(_var_name); + +#define READ_U8_ARR(_xml, _prop, _prop_name, _var_name, _elm_cnt) \ + uint8_t _var_name[_elm_cnt] = {}; \ + property_node_refer(_xml, _prop, _prop_name, PROPERTY_TYPE_U8, (char*)&_var_name, sizeof(_var_name[0]) * _elm_cnt); + +#define READ_U8(_xml, _prop, _prop_name, _var_name) \ + uint8_t _var_name = 0; \ + property_node_refer(_xml, _prop, _prop_name, PROPERTY_TYPE_U8, (char*)&_var_name, sizeof(_var_name)); + +#define READ_S8(_xml, _prop, _prop_name, _var_name) \ + int8_t _var_name = 0; \ + property_node_refer(_xml, _prop, _prop_name, PROPERTY_TYPE_U8, (char*)&_var_name, sizeof(_var_name)); + +#define READ_U16_ARR(_xml, _prop, _prop_name, _var_name, _elm_cnt) \ + uint16_t _var_name[_elm_cnt] = {}; \ + property_node_refer(_xml, _prop, _prop_name, PROPERTY_TYPE_U16, (char*)&_var_name, sizeof(_var_name[0]) * _elm_cnt); + +#define READ_U16(_xml, _prop, _prop_name, _var_name) \ + uint16_t _var_name = 0; \ + property_node_refer(_xml, _prop, _prop_name, PROPERTY_TYPE_U16, (char*)&_var_name, sizeof(_var_name)); + +#define READ_S16(_xml, _prop, _prop_name, _var_name) \ + int16_t _var_name = 0; \ + property_node_refer(_xml, _prop, _prop_name, PROPERTY_TYPE_S16, (char*)&_var_name, sizeof(_var_name)); + +#define READ_U32(_xml, _prop, _prop_name, _var_name) \ + uint32_t _var_name = 0; \ + property_node_refer(_xml, _prop, _prop_name, PROPERTY_TYPE_U32, (char*)&_var_name, sizeof(_var_name)); + +#define READ_S32(_xml, _prop, _prop_name, _var_name) \ + uint32_t _var_name = 0; \ + property_node_refer(_xml, _prop, _prop_name, PROPERTY_TYPE_S32, (char*)&_var_name, sizeof(_var_name)); + + +// Optionals: Update _var_output if _prop_name exists +#define READ_STR_OPT(_xml, _prop, _prop_name, _var_output) \ +{ \ + if (property_search(_xml, _prop, _prop_name)) { \ + unsigned char _var_tempname[256] = {}; \ + property_node_refer(_xml, _prop, _prop_name, PROPERTY_TYPE_STR, _var_tempname, sizeof(_var_tempname)); \ + _var_output = add_string(_var_tempname); \ + } \ +} + +#define READ_U32_OPT(_xml, _prop, _prop_name, _var_output) \ +{ \ + if (property_search(_xml, _prop, _prop_name)) { \ + property_node_refer(_xml, _prop, _prop_name, PROPERTY_TYPE_U32, (char*)&_var_output, sizeof(_var_output)); \ + } \ +} + +#define READ_S32_OPT(_xml, _prop, _prop_name, _var_output) \ +{ \ + if (property_search(_xml, _prop, _prop_name)) { \ + property_node_refer(_xml, _prop, _prop_name, PROPERTY_TYPE_S32, (char*)&_var_output, sizeof(_var_output)); \ + } \ +} + +#define READ_U16_OPT(_xml, _prop, _prop_name, _var_output) \ +{ \ + if (property_search(_xml, _prop, _prop_name)) { \ + property_node_refer(_xml, _prop, _prop_name, PROPERTY_TYPE_U16, (char*)&_var_output, sizeof(_var_output)); \ + } \ +} + +#define READ_S16_OPT(_xml, _prop, _prop_name, _var_output) \ +{ \ + if (property_search(_xml, _prop, _prop_name)) { \ + property_node_refer(_xml, _prop, _prop_name, PROPERTY_TYPE_S16, (char*)&_var_output, sizeof(_var_output)); \ + } \ +} + +#define READ_U8_OPT(_xml, _prop, _prop_name, _var_output) \ +{ \ + if (property_search(_xml, _prop, _prop_name)) { \ + property_node_refer(_xml, _prop, _prop_name, PROPERTY_TYPE_U8, (char*)&_var_output, sizeof(_var_output)); \ + } \ +} + +#define READ_U8_ARR_OPT(_xml, _prop, _prop_name, _var_output, _elm_cnt) \ +{ \ + if (property_search(_xml, _prop, _prop_name)) { \ + property_node_refer(_xml, _prop, _prop_name, PROPERTY_TYPE_U8, (char*)&_var_output, sizeof(_var_output[0]) * _elm_cnt); \ + } \ +} + +#define READ_U16_ARR_OPT(_xml, _prop, _prop_name, _var_output, _elm_cnt) \ +{ \ + if (property_search(_xml, _prop, _prop_name)) { \ + property_node_refer(_xml, _prop, _prop_name, PROPERTY_TYPE_U16, (char*)&_var_output, sizeof(_var_output[0]) * _elm_cnt); \ + } \ +} + + +#define READ_CHARA_OPT(_xml, _prop, _prop_name, _var_output) \ +{ \ + if (property_search(_xml, _prop, _prop_name)) { \ + unsigned char _var_tempname[256] = {}; \ + property_node_refer(_xml, _prop, _prop_name, PROPERTY_TYPE_STR, _var_tempname, sizeof(_var_tempname)); \ + _var_output = get_chara_idx(_var_tempname); \ + } \ +} + +#define READ_LAPIS_SHAPE_OPT(_xml, _prop, _prop_name, _var_output) \ +{ \ + if (property_search(_xml, _prop, _prop_name)) { \ + unsigned char _var_tempname[256] = {}; \ + property_node_refer(_xml, _prop, _prop_name, PROPERTY_TYPE_STR, _var_tempname, sizeof(_var_tempname)); \ + _var_output = get_lapis_shape_id(_var_tempname); \ + } \ +} + +#define READ_LAPIS_COLOR_OPT(_xml, _prop, _prop_name, _var_output) \ +{ \ + if (property_search(_xml, _prop, _prop_name)) { \ + unsigned char _var_tempname[256] = {}; \ + property_node_refer(_xml, _prop, _prop_name, PROPERTY_TYPE_STR, _var_tempname, sizeof(_var_tempname)); \ + _var_output = get_lapis_color_id(_var_tempname); \ + } \ +} diff --git a/util/Module.mk b/util/Module.mk new file mode 100644 index 0000000..6b43d28 --- /dev/null +++ b/util/Module.mk @@ -0,0 +1,6 @@ +libs += util + +srcpp_util := \ + fuzzy_search.cc \ + cmdline.cc \ + patch.cc \ diff --git a/util/cmdline.cc b/util/cmdline.cc new file mode 100644 index 0000000..1dcd82e --- /dev/null +++ b/util/cmdline.cc @@ -0,0 +1,95 @@ +#include +#include +#include + +#include + +void *xmalloc(size_t nbytes) { + void *mem; + + mem = malloc(nbytes); + + if (mem == NULL) { + return NULL; + } + + return mem; +} + +void *xrealloc(void *mem, size_t nbytes) { + void *newmem; + + newmem = realloc(mem, nbytes); + + if (newmem == NULL) { + return NULL; + } + + return newmem; +} + +static void push_argv(int *argc, char ***argv, const char *begin, const char *end) { + size_t nchars; + char *str; + + (*argc)++; + *argv = (char **)xrealloc(*argv, *argc * sizeof(char **)); + + nchars = end - begin; + str = (char *)xmalloc(nchars + 1); + memcpy(str, begin, nchars); + str[nchars] = '\0'; + + (*argv)[*argc - 1] = str; +} + +void args_recover(int *argc_out, char ***argv_out) { + int argc; + char **argv; + char *begin; + char *pos; + bool quote; + + argc = 0; + argv = NULL; + quote = false; + + for (begin = pos = GetCommandLine(); *pos; pos++) { + switch (*pos) { + case '"': + if (!quote) { + quote = true; + begin = pos + 1; + } else { + push_argv(&argc, &argv, begin, pos); + + quote = false; + begin = NULL; + } + + break; + + case ' ': + if (!quote && begin != NULL) { + push_argv(&argc, &argv, begin, pos); + begin = NULL; + } + + break; + + default: + if (begin == NULL) { + begin = pos; + } + + break; + } + } + + if (begin != NULL && !quote) { + push_argv(&argc, &argv, begin, pos); + } + + *argc_out = argc; + *argv_out = argv; +} diff --git a/util/cmdline.h b/util/cmdline.h new file mode 100644 index 0000000..e2bea03 --- /dev/null +++ b/util/cmdline.h @@ -0,0 +1,7 @@ +#ifndef UTIL_CMDLINE_H +#define UTIL_CMDLINE_H + +void args_recover(int *argc, char ***argv); +void args_free(int argc, char **argv); + +#endif \ No newline at end of file diff --git a/util/fuzzy_search.cc b/util/fuzzy_search.cc new file mode 100644 index 0000000..464ce14 --- /dev/null +++ b/util/fuzzy_search.cc @@ -0,0 +1,62 @@ +#include +#include + +#include "util/fuzzy_search.h" + +int find_block_core(char *haystack, size_t haystack_size, fuzzy_search_task *needle, + size_t orig_offset, int dir) { + size_t offset = orig_offset; + haystack_size += orig_offset; + while (offset + 1 < orig_offset + haystack_size) { + size_t offset_temp = offset; + int found = 1; + + if (needle->count <= 0) { + found = 0; + } + + for (int i = 0; i < needle->count; i++) { + int subfound = -1; + + if (needle->blocks[i].type == 1) { + offset_temp += needle->blocks[i].length; + continue; + } + + for (int j = 0; j < needle->blocks[i].count; j++) { + if (haystack + offset_temp + needle->blocks[i].data[j].length < + haystack + haystack_size && + memcmp(haystack + offset_temp, needle->blocks[i].data[j].block, + needle->blocks[i].data[j].length) == 0) { + subfound = j; + break; + } + } + + if (subfound == -1) { + found = 0; + break; + } + + offset_temp += needle->blocks[i].data[subfound].length; + } + + if (found == 1) { + return offset; + } else { + offset += dir; + } + } + + return -1; +} + +int find_block(char *haystack, size_t haystack_size, fuzzy_search_task *needle, + size_t orig_offset) { + return find_block_core(haystack, haystack_size, needle, orig_offset, 1); +} + +int find_block_back(char *haystack, size_t haystack_size, fuzzy_search_task *needle, + size_t orig_offset) { + return find_block_core(haystack, haystack_size, needle, orig_offset, -1); +} \ No newline at end of file diff --git a/util/fuzzy_search.h b/util/fuzzy_search.h new file mode 100644 index 0000000..ac54628 --- /dev/null +++ b/util/fuzzy_search.h @@ -0,0 +1,53 @@ +#ifndef __FUZZY_SEARCH_H__ +#define __FUZZY_SEARCH_H__ + +#include + +#define FUZZY_START(task, _count) \ + { \ + task.count = _count; \ + task.blocks = (fuzzy_search_block *)calloc(task.count, sizeof(fuzzy_search_block)); \ + } + +#define FUZZY_CODE(task, id, code, len) \ + { \ + task.blocks[id].count = 1; \ + task.blocks[id].data[0].type = 0; \ + task.blocks[id].data[0].length = len; \ + task.blocks[id].data[0].block = \ + (char *)calloc(task.blocks[id].data[0].length, sizeof(char)); \ + memcpy(task.blocks[id].data[0].block, code, task.blocks[id].data[0].length); \ + } + +#define FUZZY_WILDCARD(task, id, len) \ + { \ + task.blocks[id].count = 1; \ + task.blocks[id].type = 1; \ + task.blocks[id].length = len; \ + task.blocks[id].data[0].length = len; \ + } + +#define FUZZY_SIZE(task, id) (task.blocks[id].data[0].length) + +typedef struct fuzzy_search_block_data { + int type; + int length; + char *block; +} fuzzy_search_block_data; + +typedef struct fuzzy_search_block { + int type; + int length; + int count; + fuzzy_search_block_data data[0x10]; +} fuzzy_search_block; + +typedef struct fuzzy_search_task { + int count; + fuzzy_search_block *blocks; +} fuzzy_search_task; + +int find_block(char *haystack, size_t haystack_size, fuzzy_search_task *needle, size_t offset); +int find_block_back(char *haystack, size_t haystack_size, fuzzy_search_task *needle, size_t offset); + +#endif diff --git a/util/patch.cc b/util/patch.cc new file mode 100644 index 0000000..5dd7533 --- /dev/null +++ b/util/patch.cc @@ -0,0 +1,27 @@ +// clang-format off +#include +#include +// clang-format on + +#include "patch.h" + +void patch_memory(uint64_t patch_addr, char *data, size_t len) { + DWORD old_prot; + VirtualProtect((LPVOID)patch_addr, len, PAGE_EXECUTE_READWRITE, &old_prot); + memcpy((LPVOID)patch_addr, data, len); + VirtualProtect((LPVOID)patch_addr, len, old_prot, &old_prot); +} + +char *getDllData(const char *dllFilename, DWORD *dllSize) { + HMODULE _moduleBase = GetModuleHandle(dllFilename); + MODULEINFO module_info; + + memset(&module_info, 0, sizeof(module_info)); + if (!GetModuleInformation(GetCurrentProcess(), _moduleBase, &module_info, + sizeof(module_info))) { + return NULL; + } + + *dllSize = module_info.SizeOfImage; + return (char *)module_info.lpBaseOfDll; +} diff --git a/util/patch.h b/util/patch.h new file mode 100644 index 0000000..6c16572 --- /dev/null +++ b/util/patch.h @@ -0,0 +1,13 @@ +#ifndef __PATCH_H__ +#define __PATCH_H__ + +#include +#include +#include + +typedef unsigned long DWORD; + +void patch_memory(uint64_t patch_addr, char *data, size_t len); +char *getDllData(const char *dllFilename, DWORD *dllSize); + +#endif \ No newline at end of file diff --git a/util/xmlprop.hpp b/util/xmlprop.hpp new file mode 100644 index 0000000..f988089 --- /dev/null +++ b/util/xmlprop.hpp @@ -0,0 +1,100 @@ +#ifndef __XML_H__ +#define __XML_H__ + +#include + +#include "imports/avs.h" + +struct property_psmap { + uint8_t type; + uint8_t has_default; + uint16_t field_offset; + uint32_t member_width; + const char *path; + uintptr_t default_value; +} __attribute__((packed)); + +enum psmap_property_type { + PSMAP_PROPERTY_TYPE_S8 = 0x02, + PSMAP_PROPERTY_TYPE_U8 = 0x03, + PSMAP_PROPERTY_TYPE_S16 = 0x04, + PSMAP_PROPERTY_TYPE_U16 = 0x05, + PSMAP_PROPERTY_TYPE_S32 = 0x06, + PSMAP_PROPERTY_TYPE_U32 = 0x07, + PSMAP_PROPERTY_TYPE_S64 = 0x08, + PSMAP_PROPERTY_TYPE_U64 = 0x09, + PSMAP_PROPERTY_TYPE_STR = 0x0A, + PSMAP_PROPERTY_TYPE_FLOAT = 0x0D, + PSMAP_PROPERTY_TYPE_ATTR = 0x2D, + PSMAP_PROPERTY_TYPE_BOOL = 0x32, +}; + +#ifndef __offsetof +#define __offsetof(_s, _f) __builtin_offsetof(_s, _f) +#endif + +#ifndef __fieldwidth +#define __fieldwidth(_s, _f) sizeof((((_s *)NULL)->_f)) +#endif + +#define PSMAP_BEGIN(_name, _type) const _type struct property_psmap _name[] = { +#define PSMAP_MEMBER_REQ(_type, _struct, _member, _path) \ + {_type, false, __offsetof(_struct, _member), __fieldwidth(_struct, _member), _path, 0}, +#define PSMAP_MEMBER_OPT(_type, _struct, _member, _path, _def) \ + {_type, true, __offsetof(_struct, _member), __fieldwidth(_struct, _member), \ + _path, (uintptr_t)_def}, +#define PSMAP_END \ + { 0xff, false, 0, 0, "NULL", 0 } \ + } \ + ; + +int reader_callback(uint32_t context, void *bytes, size_t nbytes) { + return fread(bytes, 1, nbytes, (FILE *)TlsGetValue(context)); +} + +struct property *load_prop_file(const char *filename) { + FILE *file = fopen(filename, "rb"); + DWORD tlsIndex = TlsAlloc(); + + TlsSetValue(tlsIndex, (void *)file); + + if (!file) { + printf("Could not open config file: %s\n", filename); + return nullptr; + } + + const size_t size = property_read_query_memsize(reader_callback, tlsIndex, NULL, NULL); + rewind(file); + + struct property_node *buffer = + (struct property_node *)calloc(size + 0x10000, sizeof(unsigned char)); + struct property *config_data = property_create(0x17, buffer, size + 0x10000); + + if (size > 0) { + const int ret = property_insert_read(config_data, 0, reader_callback, tlsIndex); + + if (ret) { + fclose(file); + } else { + printf("Could not load %s\n", filename); + exit(-6); + } + } + + return config_data; +} + +void _load_config(const char *filename, void *dest, const struct property_psmap *psmap) { + struct property *config_xml = load_prop_file(filename); + + if (!config_xml) { + printf("Couldn't load xml file: %s\n", filename); + return; + } + + if (!(property_psmap_import(config_xml, nullptr, dest, psmap))) { + printf("Couldn't parse psmap\n"); + } +} + +#endif \ No newline at end of file