diff --git a/CMakeLists.txt b/CMakeLists.txt index 9f2829acb9..aa82549abe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -132,6 +132,9 @@ add_custom_target(g2 DEPENDS ${PROJECT} g2.dat) # Include tests if (WITH_TESTS) enable_testing() + if (UNIX AND (NOT USE_MMAP) AND FORCE32) + include("${ROOT_DIR}/test/testpaint/CMakeLists.txt" NO_POLICY_SCOPE) + endif () include("${ROOT_DIR}/test/tests/CMakeLists.txt" NO_POLICY_SCOPE) endif () diff --git a/src/openrct2/drawing/drawing.c b/src/openrct2/drawing/drawing.c index 0996da84bd..62d8eb0420 100644 --- a/src/openrct2/drawing/drawing.c +++ b/src/openrct2/drawing/drawing.c @@ -39,8 +39,6 @@ uint32 gPickupPeepImage; sint32 gPickupPeepX; sint32 gPickupPeepY; -rct_drawpixelinfo *unk_140E9A8; - /** * 12 elements from 0xF3 are the peep top colour, 12 elements from 0xCA are peep trouser colour * diff --git a/src/openrct2/drawing/drawing.h b/src/openrct2/drawing/drawing.h index 873e0244ea..0d35d988e6 100644 --- a/src/openrct2/drawing/drawing.h +++ b/src/openrct2/drawing/drawing.h @@ -269,8 +269,6 @@ extern bool gTinyFontAntiAliased; extern rct_drawpixelinfo gScreenDPI; extern rct_drawpixelinfo gWindowDPI; -extern rct_drawpixelinfo *unk_140E9A8; - // bool clip_drawpixelinfo(rct_drawpixelinfo *dst, rct_drawpixelinfo *src, sint32 x, sint32 y, sint32 width, sint32 height); void gfx_set_dirty_blocks(sint16 left, sint16 top, sint16 right, sint16 bottom); diff --git a/src/openrct2/ride/Vehicle.h b/src/openrct2/ride/Vehicle.h index e2eeb27851..aad04dd3f6 100644 --- a/src/openrct2/ride/Vehicle.h +++ b/src/openrct2/ride/Vehicle.h @@ -20,6 +20,7 @@ #include "../common.h" #include "../world/map.h" +#pragma pack(push, 1) typedef struct rct_vehicle_colour { uint8 body_colour; uint8 trim_colour; @@ -75,6 +76,7 @@ typedef struct rct_ride_entry_vehicle { sint8* peep_loading_positions; // 0x61 , 0x7B uint16 peep_loading_positions_count; } rct_ride_entry_vehicle; +#pragma pack(pop) typedef struct rct_vehicle { uint8 sprite_identifier; // 0x00 diff --git a/test/testpaint/CMakeLists.txt b/test/testpaint/CMakeLists.txt index 441fe5b5cd..e4ee9d4bfa 100644 --- a/test/testpaint/CMakeLists.txt +++ b/test/testpaint/CMakeLists.txt @@ -4,6 +4,54 @@ if (CMAKE_BINARY_DIR STREQUAL CMAKE_SOURCE_DIR) message(FATAL_ERROR "Building in-source is not supported! Create a build dir and remove ${CMAKE_SOURCE_DIR}/CMakeCache.txt") endif () +set(OPENRCT2_EXE "${ROOT_DIR}/openrct2.exe") +add_custom_command( + OUTPUT openrct2_text + COMMAND dd if="${OPENRCT2_EXE}" of="${CMAKE_BINARY_DIR}/openrct2_text" bs=4096 skip=1 count=1187 + DEPENDS ${OPENRCT2_EXE} +) +add_custom_command( + OUTPUT openrct2_data + COMMAND dd if="${OPENRCT2_EXE}" of="${CMAKE_BINARY_DIR}/openrct2_data" bs=4096 skip=1188 count=318 + COMMAND dd if=/dev/zero of="${CMAKE_BINARY_DIR}/openrct2_data" bs=4096 seek=318 count=2630 conv=notrunc + COMMAND dd if="${OPENRCT2_EXE}" of="${CMAKE_BINARY_DIR}/openrct2_data" bs=4096 skip=1506 seek=2948 count=1 conv=notrunc + DEPENDS ${OPENRCT2_EXE} +) +add_custom_target(segfiles DEPENDS openrct2_text openrct2_data) +if (NOT USE_MMAP) + set(OBJ_FORMAT "elf32-i386") + set(LINKER_SCRIPT "ld_script_i386.xc") + if (APPLE) + set(RCT2_SEGMENT_LINKER_FLAGS "-sectcreate rct2_text __text ${CMAKE_BINARY_DIR}/openrct2_text -sectcreate rct2_data __data ${CMAKE_BINARY_DIR}/openrct2_data -segaddr rct2_data 0x8a4000 -segprot rct2_data rwx rwx -segaddr rct2_text 0x401000 -segprot rct2_text rwx rwx -segaddr __TEXT 0x2000000 -read_only_relocs suppress") + else () + # For Linux we have to use objcopy to wrap regular binaries into a linkable + # format. We use specific section names which are then referenced in a + # bespoke linker script so they can be placed at predefined VMAs. + add_custom_command( + OUTPUT openrct2_text_section.o + COMMAND objcopy --input binary --output ${OBJ_FORMAT} --binary-architecture i386 openrct2_text openrct2_text_section.o --rename-section .data=.rct2_text,contents,alloc,load,readonly,code + DEPENDS segfiles + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + ) + add_custom_command( + OUTPUT openrct2_data_section.o + COMMAND objcopy --input binary --output ${OBJ_FORMAT} --binary-architecture i386 openrct2_data openrct2_data_section.o --rename-section .data=.rct2_data,contents,alloc,load,readonly,data + DEPENDS segfiles + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + ) + add_custom_target(linkable_sections DEPENDS openrct2_text_section.o openrct2_data_section.o) + set_source_files_properties( + openrct2_text_section.o openrct2_data_section.o + PROPERTIES + EXTERNAL_OBJECT true + GENERATED true + ) + # can't use GLOB here, as the files don't exist yet at cmake-time + set(RCT2_SECTIONS "${CMAKE_BINARY_DIR}/openrct2_data_section.o" "${CMAKE_BINARY_DIR}/openrct2_text_section.o") + set(RCT2_SEGMENT_LINKER_FLAGS "-Wl,-T,\"${ROOT_DIR}/distribution/linux/${LINKER_SCRIPT}\"") + endif () +endif () + set(OPENRCT2_SRCPATH "${ROOT_DIR}/src/openrct2") file(GLOB_RECURSE ORCT2_RIDE_SOURCES "${OPENRCT2_SRCPATH}/ride/*/*.cpp") file(GLOB_RECURSE ORCT2_RIDE_DEP_SOURCES "${OPENRCT2_SRCPATH}/ride/ride_data.c" @@ -19,8 +67,13 @@ file(GLOB_RECURSE ORCT2_TESTPAINT_SOURCES "${CMAKE_CURRENT_LIST_DIR}/*.c" "${CMAKE_CURRENT_LIST_DIR}/*.cpp" "${CMAKE_CURRENT_LIST_DIR}/*.h") +# Disable optimizations for addresses.c for all compilers, to allow optimized +# builds without need for -fno-omit-frame-pointer +set_source_files_properties(${CMAKE_CURRENT_LIST_DIR}/addresses.c PROPERTIES COMPILE_FLAGS -O0) + add_executable(testpaint EXCLUDE_FROM_ALL ${ORCT2_RIDE_SOURCES} ${ORCT2_RIDE_DEP_SOURCES} ${ORCT2_TESTPAINT_SOURCES} ${RCT2_SECTIONS}) target_include_directories(testpaint PRIVATE "${ROOT_DIR}/src/") + set_target_properties(testpaint PROPERTIES COMPILE_FLAGS "-DNO_VEHICLES -D__TESTPAINT__ -Wno-unused") set_target_properties(testpaint PROPERTIES LINK_FLAGS ${RCT2_SEGMENT_LINKER_FLAGS}) add_dependencies(testpaint segfiles) diff --git a/test/testpaint/PaintIntercept.cpp b/test/testpaint/PaintIntercept.cpp index a02dd9868a..c5b09f4493 100644 --- a/test/testpaint/PaintIntercept.cpp +++ b/test/testpaint/PaintIntercept.cpp @@ -15,11 +15,11 @@ #pragma endregion +#include "hook.h" #include "PaintIntercept.hpp" #include "FunctionCall.hpp" #include -#include #include #include #include @@ -233,7 +233,7 @@ namespace PaintIntercept { static uint8 InterceptPaint6C(registers *regs) { - if ((regs->ebp & 0x03) != get_current_rotation()) + if ((regs->ebp & 0x03) != RCT2_CurrentRotation) { // Log error log_error("Ebp is different from current rotation"); @@ -273,7 +273,7 @@ namespace PaintIntercept { } static uint8 InterceptPaintFull(uint8 function, registers *regs) { - if ((regs->ebp & 0x03) != get_current_rotation()) { + if ((regs->ebp & 0x03) != RCT2_CurrentRotation) { // Log error log_error("Ebp is different from current rotation"); } diff --git a/test/testpaint/TestPaint.cpp b/test/testpaint/TestPaint.cpp index dbf4586bc1..d341f798b3 100644 --- a/test/testpaint/TestPaint.cpp +++ b/test/testpaint/TestPaint.cpp @@ -17,6 +17,7 @@ #include #include +#include "hook.h" #include "GeneralSupportHeightCall.hpp" #include "Printer.hpp" #include "SegmentSupportHeightCall.hpp" @@ -27,7 +28,6 @@ #include #include #include -#include namespace TestPaint { @@ -47,7 +47,7 @@ namespace TestPaint rct_drawpixelinfo dpi = { 0 }; dpi.zoom_level = 1; - unk_140E9A8 = &dpi; + RCT2_GLOBAL(0x0140E9A8, rct_drawpixelinfo *) = &dpi; gPaintSession.Unk140E9A8 = &dpi; Ride ride = {0}; @@ -59,10 +59,18 @@ namespace TestPaint rideEntry.vehicles[0] = vehicleEntry; gRideList[0] = ride; + RCT2_ADDRESS(0x013628F8, Ride)[0] = ride; + gRideEntries[0] = &rideEntry; g141E9DB = G141E9DB_FLAG_1 | G141E9DB_FLAG_2; gPaintSession.Unk141E9DB = G141E9DB_FLAG_1 | G141E9DB_FLAG_2; + + gCurrentViewportFlags = 0; + RCT2_GLOBAL(0x0141E9E4, uint32) = 0; + + gScenarioTicks = 0; + RCT2_GLOBAL(0x00F663AC, uint32) = 0; } void ResetTunnels() { diff --git a/test/testpaint/TestPaint.hpp b/test/testpaint/TestPaint.hpp index 008e7498df..28663258c4 100644 --- a/test/testpaint/TestPaint.hpp +++ b/test/testpaint/TestPaint.hpp @@ -28,8 +28,32 @@ extern "C" #include } -#define gRideEntries RCT2_ADDRESS(RCT2_ADDRESS_RIDE_ENTRIES, rct_ride_entry*) -#define gCurrentRotation RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_ROTATION, uint8) +#include "addresses.h" + +#define gRideEntries RCT2_ADDRESS(0x009ACFA4, rct_ride_entry*) +#define gSupportSegments RCT2_ADDRESS(0x0141E9B4, support_height) +#define gWoodenSupportsPrependTo RCT2_GLOBAL(0x009DEA58, paint_struct *) +#define gPaintStructs RCT2_ADDRESS(0x00EE788C, paint_entry) +#define g_currently_drawn_item RCT2_GLOBAL(0x009DE578, void*) +#define gEndOfPaintStructArray RCT2_GLOBAL(0x00EE7880, paint_entry *) +#define gPaintSpritePosition RCT2_GLOBAL(0x009DE568, LocationXY16) +#define gPaintInteractionType RCT2_GLOBAL(0x009DE570, uint8) +#define gSupportSegments RCT2_ADDRESS(0x0141E9B4, support_height) +#define gSupport RCT2_GLOBAL(0x0141E9D8, support_height) +#define gWoodenSupportsPrependTo RCT2_GLOBAL(0x009DEA58, paint_struct *) +#define gPaintMapPosition RCT2_GLOBAL(0x009DE574, LocationXY16) +#define gLeftTunnels RCT2_ADDRESS(0x009E3138, tunnel_entry) +#define gLeftTunnelCount RCT2_GLOBAL(0x0141F56A, uint8) +#define gRightTunnels RCT2_ADDRESS(0x009E30B6, tunnel_entry) +#define gRightTunnelCount RCT2_GLOBAL(0x0141F56B, uint8) +#define gVerticalTunnelHeight RCT2_GLOBAL(0x009E323C, uint8) +#define gSurfaceElement RCT2_GLOBAL(0x009E3250, rct_tile_element *) +#define gDidPassSurface RCT2_GLOBAL(0x009DE57C, bool) +#define g141E9DB RCT2_GLOBAL(0x0141E9DB, uint8) +#define gUnk141E9DC RCT2_GLOBAL(0x0141E9DC, uint16) +#define gTrackColours RCT2_ADDRESS(0x00F44198, uint32) + +#define RCT2_CurrentRotation RCT2_GLOBAL(0x0141E9E0, uint8) enum { TEST_SUCCESS, diff --git a/test/testpaint/TestTrack.cpp b/test/testpaint/TestTrack.cpp index 9a4c227149..d16bc9534a 100644 --- a/test/testpaint/TestTrack.cpp +++ b/test/testpaint/TestTrack.cpp @@ -166,6 +166,9 @@ public: Ride *ride, rct_ride_entry *rideEntry ) override { ride->entrance_style = variant; + + Ride * rct2ride = RCT2_ADDRESS(0x013628F8, Ride); + rct2ride->entrance_style = variant; } }; @@ -345,6 +348,7 @@ static uint8 TestTrackElementPaintCalls(uint8 rideType, uint8 trackType, uint8 t for (int currentRotation = 0; currentRotation < 4; currentRotation++) { gCurrentRotation = currentRotation; + RCT2_CurrentRotation = currentRotation; for (int direction = 0; direction < 4; direction++) { RCT2_GLOBAL(0x009DE56A, sint16) = 64; // x RCT2_GLOBAL(0x009DE56E, sint16) = 64; // y diff --git a/test/testpaint/addresses.c b/test/testpaint/addresses.c new file mode 100644 index 0000000000..13ce59817f --- /dev/null +++ b/test/testpaint/addresses.c @@ -0,0 +1,85 @@ +#pragma region Copyright (c) 2014-2017 OpenRCT2 Developers +/***************************************************************************** + * OpenRCT2, an open source clone of Roller Coaster Tycoon 2. + * + * OpenRCT2 is the work of many authors, a full list can be found in contributors.md + * For more information, visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * A full copy of the GNU General Public License can be found in licence.txt + *****************************************************************************/ +#pragma endregion + +#include "addresses.h" + +#if defined(__GNUC__) + #ifdef __clang__ + #define DISABLE_OPT __attribute__((noinline,optnone)) + #else + #define DISABLE_OPT __attribute__((noinline,optimize("O0"))) + #endif // __clang__ +#else +#define DISABLE_OPT +#endif // defined(__GNUC__) + +// This variable serves a purpose of identifying a crash if it has happened inside original code. +// When switching to original code, stack frame pointer is modified and prevents breakpad from providing stack trace. +volatile sint32 _originalAddress = 0; + +sint32 DISABLE_OPT RCT2_CALLPROC_X(sint32 address, sint32 _eax, sint32 _ebx, sint32 _ecx, sint32 _edx, sint32 _esi, sint32 _edi, sint32 _ebp) +{ + sint32 result = 0; + _originalAddress = address; +#if defined(PLATFORM_X86) && !defined(NO_RCT2) + #ifdef _MSC_VER + __asm { + push ebp + push address + mov eax, _eax + mov ebx, _ebx + mov ecx, _ecx + mov edx, _edx + mov esi, _esi + mov edi, _edi + mov ebp, _ebp + call [esp] + lahf + pop ebp + pop ebp + /* Load result with flags */ + mov result, eax + } + #else + __asm__ volatile ( "\ + \n\ + push %%ebx \n\ + push %%ebp \n\ + push %[address] \n\ + mov %[eax], %%eax \n\ + mov %[ebx], %%ebx \n\ + mov %[ecx], %%ecx \n\ + mov %[edx], %%edx \n\ + mov %[esi], %%esi \n\ + mov %[edi], %%edi \n\ + mov %[ebp], %%ebp \n\ + call *(%%esp) \n\ + lahf \n\ + add $4, %%esp \n\ + pop %%ebp \n\ + pop %%ebx \n\ + /* Load result with flags */ \n\ + mov %%eax, %[result] \n\ + " : [address] "+m" (address), [eax] "+m" (_eax), [ebx] "+m" (_ebx), [ecx] "+m" (_ecx), [edx] "+m" (_edx), [esi] "+m" (_esi), [edi] "+m" (_edi), [ebp] "+m" (_ebp), [result] "+m" (result) + : + : "eax","ecx","edx","esi","edi","memory" + ); + #endif +#endif // PLATFORM_X86 + _originalAddress = 0; + // lahf only modifies ah, zero out the rest + return result & 0xFF00; +} diff --git a/test/testpaint/addresses.h b/test/testpaint/addresses.h new file mode 100644 index 0000000000..6d467d7c8a --- /dev/null +++ b/test/testpaint/addresses.h @@ -0,0 +1,69 @@ + +#pragma region Copyright (c) 2014-2017 OpenRCT2 Developers +/***************************************************************************** + * OpenRCT2, an open source clone of Roller Coaster Tycoon 2. + * + * OpenRCT2 is the work of many authors, a full list can be found in contributors.md + * For more information, visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * A full copy of the GNU General Public License can be found in licence.txt + *****************************************************************************/ +#pragma endregion + +#ifndef _ADDRESSES_H_ +#define _ADDRESSES_H_ + +#include + +#ifdef USE_MMAP + #if defined(PLATFORM_64BIT) + #define GOOD_PLACE_FOR_DATA_SEGMENT ((uintptr_t)0x200000000) + #elif defined(PLATFORM_32BIT) + #define GOOD_PLACE_FOR_DATA_SEGMENT ((uintptr_t)0x09000000) + #else + #error "Unknown platform" + #endif +#else + #define GOOD_PLACE_FOR_DATA_SEGMENT ((uintptr_t)0x8a4000) +#endif + +#define RCT2_ADDRESS(address, type) ((type*)(GOOD_PLACE_FOR_DATA_SEGMENT - 0x8a4000 + (address))) +#define RCT2_GLOBAL(address, type) (*((type*)(GOOD_PLACE_FOR_DATA_SEGMENT - 0x8a4000 + (address)))) + +#define RCT2_ADDRESS_RIDE_ENTRIES 0x009ACFA4 +#define RCT2_ADDRESS_CURRENT_ROTATION 0x0141E9E0 +#define RCT2_ADDRESS_SPRITE_LIST 0x010E63BC +#define RCT2_ADDRESS_CURRENT_SUPPORT_SEGMENTS 0x0141E9B4 +#define RCT2_ADDRESS_PAINT_BOUNDBOX_OFFSET_X 0x009DEA52 +#define RCT2_ADDRESS_PAINT_BOUNDBOX_OFFSET_Y 0x009DEA54 +#define RCT2_ADDRESS_PAINT_BOUNDBOX_OFFSET_Z 0x009DEA56 + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** + * Returns the flags register + * + * Flags register is as follows: + * 0bSZ0A_0P0C_0000_0000 + * S = Signed flag + * Z = Zero flag + * C = Carry flag + * A = Adjust flag + * P = Parity flag + * All other bits are undefined. + */ +sint32 RCT2_CALLPROC_X(sint32 address, sint32 _eax, sint32 _ebx, sint32 _ecx, sint32 _edx, sint32 _esi, sint32 _edi, sint32 _ebp); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/test/testpaint/compat.c b/test/testpaint/compat.c index a5331b7621..5098e79849 100644 --- a/test/testpaint/compat.c +++ b/test/testpaint/compat.c @@ -23,21 +23,20 @@ #include #include #include - -#define RCT2_ADDRESS_SPRITE_LIST 0x010E63BC +#include "addresses.h" #define gRideEntries RCT2_ADDRESS(RCT2_ADDRESS_RIDE_ENTRIES, rct_ride_entry*) -#define gCurrentRotation RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_ROTATION, uint8) - -rct_tile_element *gTileElements = (rct_tile_element *) RCT2_ADDRESS_TILE_ELEMENTS; -rct_tile_element **gTileElementTilePointers = (rct_tile_element **) RCT2_ADDRESS_TILE_TILE_ELEMENT_POINTERS; -Ride *gRideList = RCT2_ADDRESS(RCT2_ADDRESS_RIDE_LIST, Ride); +#define gTileElementTilePointers RCT2_ADDRESS(0x013CE9A4, rct_tile_element*) +Ride gRideList[MAX_RIDES]; rct_sprite *sprite_list = RCT2_ADDRESS(RCT2_ADDRESS_SPRITE_LIST, rct_sprite); sint16 gMapSizeUnits; sint16 gMapBaseZ; bool gTrackDesignSaveMode = false; uint8 gTrackDesignSaveRideIndex = 255; uint8 gClipHeight = 255; +uint32 gCurrentViewportFlags; +uint32 gScenarioTicks; +uint8 gCurrentRotation; const LocationXY16 TileDirectionDelta[] = { {-32, 0}, diff --git a/test/testpaint/hook.c b/test/testpaint/hook.c new file mode 100644 index 0000000000..eb71f509e1 --- /dev/null +++ b/test/testpaint/hook.c @@ -0,0 +1,213 @@ +#pragma region Copyright (c) 2014-2017 OpenRCT2 Developers +/***************************************************************************** + * OpenRCT2, an open source clone of Roller Coaster Tycoon 2. + * + * OpenRCT2 is the work of many authors, a full list can be found in contributors.md + * For more information, visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * A full copy of the GNU General Public License can be found in licence.txt + *****************************************************************************/ +#pragma endregion + +#include + +#ifndef NO_RCT2 + +#ifdef _WIN32 + #include +#else + #include +#endif // _WIN32 + +#include "hook.h" + +void* _hookTableAddress = 0; +sint32 _hookTableOffset = 0; +sint32 _maxHooks = 1000; +#define HOOK_BYTE_COUNT (140) + +registers gHookRegisters = {0}; + +// This macro writes a little-endian 4-byte long value into *data +// It is used to avoid type punning. +#define write_address_strictalias(data, addr) \ + *(data + 0) = ((addr) & 0x000000ff) >> 0; \ + *(data + 1) = ((addr) & 0x0000ff00) >> 8; \ + *(data + 2) = ((addr) & 0x00ff0000) >> 16; \ + *(data + 3) = ((addr) & 0xff000000) >> 24; + +static void hookfunc(uintptr_t address, uintptr_t hookAddress, sint32 stacksize) +{ + sint32 i = 0; + uint8 data[HOOK_BYTE_COUNT] = {0}; + + uintptr_t registerAddress = (uintptr_t) &gHookRegisters; + + data[i++] = 0x89; // mov [gHookRegisters], eax + data[i++] = (0b000 << 3) | 0b101; + write_address_strictalias(&data[i], registerAddress); + i += 4; + + data[i++] = 0x89; // mov [gHookRegisters + 4], ebx + data[i++] = (0b011 << 3) | 0b101; + write_address_strictalias(&data[i], registerAddress + 4); + i += 4; + + data[i++] = 0x89; // mov [gHookRegisters + 8], ecx + data[i++] = (0b001 << 3) | 0b101; + write_address_strictalias(&data[i], registerAddress + 8); + i += 4; + + data[i++] = 0x89; // mov [gHookRegisters + 12], edx + data[i++] = (0b010 << 3) | 0b101; + write_address_strictalias(&data[i], registerAddress + 12); + i += 4; + + data[i++] = 0x89; // mov [gHookRegisters + 16], esi + data[i++] = (0b110 << 3) | 0b101; + write_address_strictalias(&data[i], registerAddress + 16); + i += 4; + + data[i++] = 0x89; // mov [gHookRegisters + 20], edi + data[i++] = (0b111 << 3) | 0b101; + write_address_strictalias(&data[i], registerAddress + 20); + i += 4; + + data[i++] = 0x89; // mov [gHookRegisters + 24], ebp + data[i++] = (0b101 << 3) | 0b101; + write_address_strictalias(&data[i], registerAddress + 24); + i += 4; + + // work out distance to nearest 0xC + // (esp - numargs * 4) & 0xC + // move to align - 4 + // save that amount + + // push the registers to be on the stack to access as arguments + data[i++] = 0x68; // push gHookRegisters + write_address_strictalias(&data[i], registerAddress); + i += 4; + + data[i++] = 0xE8; // call + + write_address_strictalias(&data[i], hookAddress - address - i - 4); + i += 4; + + + data[i++] = 0x83; // add esp, 4 + data[i++] = 0xC4; + data[i++] = 0x04; + + data[i++] = 0x25; // and eax,0xff + data[i++] = 0xff; + data[i++] = 0x00; + data[i++] = 0x00; + data[i++] = 0x00; + data[i++] = 0xc1; // shl eax, 8 + data[i++] = 0xe0; + data[i++] = 0x08; + data[i++] = 0x9e; // sahf + data[i++] = 0x9c; // pushf + + data[i++] = 0x8B; // mov eax, [gHookRegisters] + data[i++] = (0b000 << 3) | 0b101; + write_address_strictalias(&data[i], registerAddress); + i += 4; + + data[i++] = 0x8B; // mov ebx, [gHookRegisters + 4] + data[i++] = (0b011 << 3) | 0b101; + write_address_strictalias(&data[i], registerAddress + 4); + i += 4; + + data[i++] = 0x8B; // mov ecx, [gHookRegisters + 8] + data[i++] = (0b001 << 3) | 0b101; + write_address_strictalias(&data[i], registerAddress + 8); + i += 4; + + data[i++] = 0x8B; // mov edx, [gHookRegisters + 12] + data[i++] = (0b010 << 3) | 0b101; + write_address_strictalias(&data[i], registerAddress + 12); + i += 4; + + data[i++] = 0x8B; // mov esi, [gHookRegisters + 16] + data[i++] = (0b110 << 3) | 0b101; + write_address_strictalias(&data[i], registerAddress + 16); + i += 4; + + data[i++] = 0x8B; // mov edi, [gHookRegisters + 20] + data[i++] = (0b111 << 3) | 0b101; + write_address_strictalias(&data[i], registerAddress + 20); + i += 4; + + data[i++] = 0x8B; // mov ebp, [gHookRegisters + 24] + data[i++] = (0b101 << 3) | 0b101; + write_address_strictalias(&data[i], registerAddress + 24); + i += 4; + + data[i++] = 0x9d; // popf + + data[i++] = 0xC3; // retn + +#ifdef _WIN32 + WriteProcessMemory(GetCurrentProcess(), (LPVOID)address, data, i, 0); +#else + // We own the pages with PROT_WRITE | PROT_EXEC, we can simply just memcpy the data + memcpy((void *)address, data, i); +#endif // _WIN32 +} + +void addhook(uintptr_t address, hook_function *function) +{ + if (!_hookTableAddress) { + size_t size = _maxHooks * HOOK_BYTE_COUNT; +#ifdef _WIN32 + _hookTableAddress = VirtualAllocEx(GetCurrentProcess(), NULL, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE); +#else + _hookTableAddress = mmap(NULL, size, PROT_EXEC | PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (_hookTableAddress == MAP_FAILED) + { + perror("mmap"); + exit(1); + } +#endif // _WIN32 + } + if (_hookTableOffset > _maxHooks) { + return; + } + uint32 hookaddress = (uint32)_hookTableAddress + (_hookTableOffset * HOOK_BYTE_COUNT); + uint8 data[9]; + sint32 i = 0; + data[i++] = 0xE9; // jmp + + write_address_strictalias(&data[i], hookaddress - address - i - 4); + i += 4; + + data[i++] = 0xC3; // retn +#ifdef _WIN32 + WriteProcessMemory(GetCurrentProcess(), (LPVOID)address, data, i, 0); +#else + // We own the pages with PROT_WRITE | PROT_EXEC, we can simply just memcpy the data + sint32 err = mprotect((void *)0x401000, 0x8a4000 - 0x401000, PROT_READ | PROT_WRITE); + if (err != 0) + { + perror("mprotect"); + } + + memcpy((void *)address, data, i); + + err = mprotect((void *)0x401000, 0x8a4000 - 0x401000, PROT_READ | PROT_EXEC); + if (err != 0) + { + perror("mprotect"); + } +#endif // _WIN32 + hookfunc(hookaddress, (uintptr_t)function, 0); + _hookTableOffset++; +} + +#endif diff --git a/test/testpaint/hook.h b/test/testpaint/hook.h new file mode 100644 index 0000000000..3b462bd556 --- /dev/null +++ b/test/testpaint/hook.h @@ -0,0 +1,48 @@ +#pragma region Copyright (c) 2014-2017 OpenRCT2 Developers +/***************************************************************************** + * OpenRCT2, an open source clone of Roller Coaster Tycoon 2. + * + * OpenRCT2 is the work of many authors, a full list can be found in contributors.md + * For more information, visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * A full copy of the GNU General Public License can be found in licence.txt + *****************************************************************************/ +#pragma endregion + +#ifndef _HOOK_H_ +#define _HOOK_H_ + +#ifndef NO_RCT2 + +#include + +enum { + X86_FLAG_CARRY = 1 << 0, + X86_FLAG_PARITY = 1 << 2, + + X86_FLAG_ADJUST = 1 << 4, + + X86_FLAG_ZERO = 1 << 6, + X86_FLAG_SIGN = 1 << 7, +}; + +typedef uint8 (hook_function)(registers *regs); + +#ifdef __cplusplus +extern "C" { +#endif + +void addhook(uintptr_t address, hook_function *function); + +#ifdef __cplusplus +} +#endif + +#endif + +#endif