diff --git a/openrct2.vcxproj b/openrct2.vcxproj index 0f0b64b570..bd40bc245e 100644 --- a/openrct2.vcxproj +++ b/openrct2.vcxproj @@ -228,6 +228,7 @@ + @@ -544,6 +545,7 @@ + diff --git a/src/hook.h b/src/hook.h index c4dbdf5758..d4cee25f61 100644 --- a/src/hook.h +++ b/src/hook.h @@ -19,6 +19,8 @@ #ifndef NO_RCT2 +#include "common.h" + enum { X86_FLAG_CARRY = 1 << 0, X86_FLAG_PARITY = 1 << 2, diff --git a/src/openrct2.c b/src/openrct2.c index 9af19a5ffb..f18253ae99 100644 --- a/src/openrct2.c +++ b/src/openrct2.c @@ -19,7 +19,6 @@ #include "config.h" #include "editor.h" #include "game.h" -#include "hook.h" #include "interface/chat.h" #include "interface/themes.h" #include "interface/window.h" @@ -32,31 +31,15 @@ #include "openrct2.h" #include "platform/crash.h" #include "platform/platform.h" +#include "rct2/interop.h" #include "ride/ride.h" #include "title.h" -#include "util/sawyercoding.h" #include "util/util.h" #include "version.h" #include "world/mapgen.h" -#if defined(__unix__) || defined(__MACOSX__) -#include -#include -#include -#include -#include -#include -#endif // defined(__unix__) || defined(__MACOSX__) - int gExitCode; -#if defined(USE_MMAP) && (defined(__unix__) || defined(__MACOSX__)) && !defined(NO_RCT2) - static int fdData = -1; -#endif -#if defined(__unix__) && !defined(NO_RCT2) - static char * segments = (char *)(GOOD_PLACE_FOR_DATA_SEGMENT); -#endif - int gOpenRCT2StartupAction = STARTUP_ACTION_TITLE; utf8 gOpenRCT2StartupActionPath[512] = { 0 }; utf8 gExePath[MAX_PATH]; @@ -83,7 +66,6 @@ int _finished; static rct_xyz16 _spritelocations1[MAX_SPRITES], _spritelocations2[MAX_SPRITES]; static void openrct2_loop(); -static void openrct2_setup_rct2_hooks(); void openrct2_write_full_version_info(utf8 *buffer, size_t bufferSize) { @@ -206,7 +188,7 @@ bool openrct2_initialise() crash_init(); - if (!openrct2_setup_rct2_segment()) { + if (!rct2_interop_setup_segment()) { log_fatal("Unable to load RCT2 data sector"); return false; } @@ -263,7 +245,7 @@ bool openrct2_initialise() title_sequences_set_default(); title_sequences_load_presets(); - openrct2_setup_rct2_hooks(); + rct2_interop_setup_hooks(); if (!rct2_init()) return false; @@ -357,10 +339,7 @@ void openrct2_dispose() #ifndef DISABLE_NETWORK EVP_MD_CTX_destroy(gHashCTX); #endif // DISABLE_NETWORK -#if defined(USE_MMAP) && (defined(__unix__) || defined(__MACOSX__)) && !defined(NO_RCT2) - munmap(segments, 12079104); - close(fdData); -#endif + rct2_interop_dispose(); platform_free(); } @@ -491,173 +470,3 @@ void openrct2_reset_object_tween_locations() _spritelocations1[i].z = _spritelocations2[i].z = get_sprite(i)->unknown.z; } } - -static void openrct2_get_segment_data_path(char * buffer, size_t bufferSize) -{ - platform_get_exe_path(buffer, bufferSize); - safe_strcat_path(buffer, "openrct2_data", bufferSize); -} - -/** - * Loads RCT2's data model and remaps the addresses. - * @returns true if the data integrity check succeeded, otherwise false. - */ -bool openrct2_setup_rct2_segment() -{ - // OpenRCT2 on Linux and macOS is wired to have the original Windows PE sections loaded - // necessary. Windows does not need to do this as OpenRCT2 runs as a DLL loaded from the Windows PE. - int len = 0x01429000 - 0x8a4000; // 0xB85000, 12079104 bytes or around 11.5MB - int err = 0; - // in some configurations err and len may be unused - UNUSED(err); - UNUSED(len); -#if defined(USE_MMAP) && (defined(__unix__) || defined(__MACOSX__)) && !defined(NO_RCT2) - #define RDATA_OFFSET 0x004A4000 - #define DATASEG_OFFSET 0x005E2000 - - // Using PE-bear I was able to figure out all the needed addresses to be filled. - // There are three sections to be loaded: .rdata, .data and .text, plus another - // one to be mapped: DATASEG. - // Out of the three, two can simply be mmapped into memory, while the third one, - // .data has a virtual size which is much completely different to its file size - // (even when taking page-alignment into consideration) - // - // The sections are as follows (dump from gdb) - // [0] 0x401000->0x6f7000 at 0x00001000: .text ALLOC LOAD READONLY CODE HAS_CONTENTS - // [1] 0x6f7000->0x8a325d at 0x002f7000: CODESEG ALLOC LOAD READONLY CODE HAS_CONTENTS - // [2] 0x8a4000->0x9a5894 at 0x004a4000: .rdata ALLOC LOAD DATA HAS_CONTENTS - // [3] 0x9a6000->0x9e2000 at 0x005a6000: .data ALLOC LOAD DATA HAS_CONTENTS - // [4] 0x1428000->0x14282bc at 0x005e2000: DATASEG ALLOC LOAD DATA HAS_CONTENTS - // [5] 0x1429000->0x1452000 at 0x005e3000: .cms_t ALLOC LOAD READONLY CODE HAS_CONTENTS - // [6] 0x1452000->0x14aaf3e at 0x0060c000: .cms_d ALLOC LOAD DATA HAS_CONTENTS - // [7] 0x14ab000->0x14ac58a at 0x00665000: .idata ALLOC LOAD READONLY DATA HAS_CONTENTS - // [8] 0x14ad000->0x14b512f at 0x00667000: .rsrc ALLOC LOAD DATA HAS_CONTENTS - // - // .data section, however, has virtual size of 0xA81C3C, and so - // 0x9a6000 + 0xA81C3C = 0x1427C3C, which after alignment to page size becomes - // 0x1428000, which can be seen as next section, DATASEG - // - // The data is now loaded into memory with a linker script, which proves to - // be more reliable, as mallocs that happen before we reach segment setup - // could have already taken the space we need. - - // TODO: UGLY, UGLY HACK! - //off_t file_size = 6750208; - - utf8 segmentDataPath[MAX_PATH]; - openrct2_get_segment_data_path(segmentDataPath, sizeof(segmentDataPath)); - fdData = open(segmentDataPath, O_RDONLY); - if (fdData < 0) - { - log_fatal("failed to load openrct2_data"); - exit(1); - } - log_warning("%p", GOOD_PLACE_FOR_DATA_SEGMENT); - segments = mmap((void *)(GOOD_PLACE_FOR_DATA_SEGMENT), len, PROT_EXEC | PROT_READ | PROT_WRITE, MAP_PRIVATE, fdData, 0); - log_warning("%p", segments); - if ((uintptr_t)segments != GOOD_PLACE_FOR_DATA_SEGMENT) { - perror("mmap"); - return false; - } -#endif // defined(USE_MMAP) && (defined(__unix__) || defined(__MACOSX__)) - -#if defined(__unix__) && !defined(NO_RCT2) - int pageSize = getpagesize(); - int numPages = (len + pageSize - 1) / pageSize; - unsigned char *dummy = malloc(numPages); - - err = mincore((void *)segments, len, dummy); - bool pagesMissing = false; - if (err != 0) - { - err = errno; -#ifdef __LINUX__ - // On Linux ENOMEM means all requested range is unmapped - if (err != ENOMEM) - { - pagesMissing = true; - perror("mincore"); - } -#else - pagesMissing = true; - perror("mincore"); -#endif // __LINUX__ - } else { - for (int i = 0; i < numPages; i++) - { - if (dummy[i] != 1) - { - pagesMissing = true; - void *start = (void *)segments + i * pageSize; - void *end = (void *)segments + (i + 1) * pageSize - 1; - log_warning("required page %p - %p is not in memory!", start, end); - } - } - } - free(dummy); - if (pagesMissing) - { - log_error("At least one of required pages was not found in memory. This can cause segfaults later on."); - } -#if !defined(USE_MMAP) - // section: text - err = mprotect((void *)0x401000, 0x8a4000 - 0x401000, PROT_READ | PROT_EXEC | PROT_WRITE); - if (err != 0) - { - perror("mprotect"); - } -#endif // !defined(USE_MMAP) - // section: rw data - err = mprotect((void *)segments, 0x01429000 - 0x8a4000, PROT_READ | PROT_WRITE); - if (err != 0) - { - perror("mprotect"); - } -#endif // defined(__unix__) - -#if defined(USE_MMAP) && defined(__WINDOWS__) - segments = VirtualAlloc((void *)(GOOD_PLACE_FOR_DATA_SEGMENT), len, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); - if ((uintptr_t)segments != GOOD_PLACE_FOR_DATA_SEGMENT) { - log_error("VirtualAlloc, segments = %p, GetLastError = 0x%x", segments, GetLastError()); - return false; - } - - utf8 segmentDataPath[MAX_PATH]; - openrct2_get_segment_data_path(segmentDataPath, sizeof(segmentDataPath)); - SDL_RWops * rw = SDL_RWFromFile(segmentDataPath, "rb"); - if (rw == NULL) - { - log_error("failed to load file"); - return false; - } - if (SDL_RWread(rw, segments, len, 1) != 1) { - log_error("Unable to read chunk header!"); - return false; - } - SDL_RWclose(rw); -#endif // defined(USE_MMAP) && defined(__WINDOWS__) - -#if !defined(NO_RCT2) && defined(USE_MMAP) - // Check that the expected data is at various addresses. - // Start at 0x9a6000, which is start of .data, to skip the region containing addresses to DLL - // calls, which can be changed by windows/wine loader. - const uint32 c1 = sawyercoding_calculate_checksum((const uint8*)(segments + (uintptr_t)(0x009A6000 - 0x8a4000)), 0x009E0000 - 0x009A6000); - const uint32 c2 = sawyercoding_calculate_checksum((const uint8*)(segments + (uintptr_t)(0x01428000 - 0x8a4000)), 0x014282BC - 0x01428000); - const uint32 exp_c1 = 10114815; - const uint32 exp_c2 = 23564; - if (c1 != exp_c1 || c2 != exp_c2) { - log_warning("c1 = %u, expected %u, match %d", c1, exp_c1, c1 == exp_c1); - log_warning("c2 = %u, expected %u, match %d", c2, exp_c2, c2 == exp_c2); - return false; - } -#endif - return true; -} - -/** - * Setup hooks to allow RCT2 to call OpenRCT2 functions instead. - */ -static void openrct2_setup_rct2_hooks() -{ - // None for now -} diff --git a/src/openrct2.h b/src/openrct2.h index 67eb264628..8da27fe06a 100644 --- a/src/openrct2.h +++ b/src/openrct2.h @@ -61,7 +61,6 @@ void openrct2_launch(); void openrct2_dispose(); void openrct2_finish(); void openrct2_reset_object_tween_locations(); -bool openrct2_setup_rct2_segment(); int cmdline_run(const char **argv, int argc); diff --git a/src/rct2/interop.c b/src/rct2/interop.c new file mode 100644 index 0000000000..d8b952a3fb --- /dev/null +++ b/src/rct2/interop.c @@ -0,0 +1,223 @@ +#pragma region Copyright (c) 2014-2016 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 "../common.h" + +#if defined(__WINDOWS__) + #define WIN32_LEAN_AND_MEAN + #include +#endif + +#if defined(__unix__) || defined(__MACOSX__) + #include + #include + #include + #include + #include + #include +#endif // defined(__unix__) || defined(__MACOSX__) + +#include "../addresses.h" +#include "../hook.h" +#include "../openrct2.h" +#include "../util/sawyercoding.h" +#include "../util/util.h" +#include "interop.h" + +#if defined(USE_MMAP) && (defined(__unix__) || defined(__MACOSX__)) && !defined(NO_RCT2) + static int fdData = -1; +#endif +#if !defined(NO_RCT2) + static char * segments = (char *)(GOOD_PLACE_FOR_DATA_SEGMENT); +#endif + +static void rct2_interop_get_segment_data_path(char * buffer, size_t bufferSize) +{ + platform_get_exe_path(buffer, bufferSize); + safe_strcat_path(buffer, "openrct2_data", bufferSize); +} + +/** + * Loads RCT2's data model and remaps the addresses. + * @returns true if the data integrity check succeeded, otherwise false. + */ +bool rct2_interop_setup_segment() +{ + // OpenRCT2 on Linux and macOS is wired to have the original Windows PE sections loaded + // necessary. Windows does not need to do this as OpenRCT2 runs as a DLL loaded from the Windows PE. + int len = 0x01429000 - 0x8a4000; // 0xB85000, 12079104 bytes or around 11.5MB + int err = 0; + // in some configurations err and len may be unused + UNUSED(err); + UNUSED(len); +#if defined(USE_MMAP) && (defined(__unix__) || defined(__MACOSX__)) && !defined(NO_RCT2) + #define RDATA_OFFSET 0x004A4000 + #define DATASEG_OFFSET 0x005E2000 + + // Using PE-bear I was able to figure out all the needed addresses to be filled. + // There are three sections to be loaded: .rdata, .data and .text, plus another + // one to be mapped: DATASEG. + // Out of the three, two can simply be mmapped into memory, while the third one, + // .data has a virtual size which is much completely different to its file size + // (even when taking page-alignment into consideration) + // + // The sections are as follows (dump from gdb) + // [0] 0x401000->0x6f7000 at 0x00001000: .text ALLOC LOAD READONLY CODE HAS_CONTENTS + // [1] 0x6f7000->0x8a325d at 0x002f7000: CODESEG ALLOC LOAD READONLY CODE HAS_CONTENTS + // [2] 0x8a4000->0x9a5894 at 0x004a4000: .rdata ALLOC LOAD DATA HAS_CONTENTS + // [3] 0x9a6000->0x9e2000 at 0x005a6000: .data ALLOC LOAD DATA HAS_CONTENTS + // [4] 0x1428000->0x14282bc at 0x005e2000: DATASEG ALLOC LOAD DATA HAS_CONTENTS + // [5] 0x1429000->0x1452000 at 0x005e3000: .cms_t ALLOC LOAD READONLY CODE HAS_CONTENTS + // [6] 0x1452000->0x14aaf3e at 0x0060c000: .cms_d ALLOC LOAD DATA HAS_CONTENTS + // [7] 0x14ab000->0x14ac58a at 0x00665000: .idata ALLOC LOAD READONLY DATA HAS_CONTENTS + // [8] 0x14ad000->0x14b512f at 0x00667000: .rsrc ALLOC LOAD DATA HAS_CONTENTS + // + // .data section, however, has virtual size of 0xA81C3C, and so + // 0x9a6000 + 0xA81C3C = 0x1427C3C, which after alignment to page size becomes + // 0x1428000, which can be seen as next section, DATASEG + // + // The data is now loaded into memory with a linker script, which proves to + // be more reliable, as mallocs that happen before we reach segment setup + // could have already taken the space we need. + + // TODO: UGLY, UGLY HACK! + //off_t file_size = 6750208; + + utf8 segmentDataPath[MAX_PATH]; + rct2_interop_get_segment_data_path(segmentDataPath, sizeof(segmentDataPath)); + fdData = open(segmentDataPath, O_RDONLY); + if (fdData < 0) + { + log_fatal("failed to load openrct2_data"); + exit(1); + } + log_warning("%p", GOOD_PLACE_FOR_DATA_SEGMENT); + segments = mmap((void *)(GOOD_PLACE_FOR_DATA_SEGMENT), len, PROT_EXEC | PROT_READ | PROT_WRITE, MAP_PRIVATE, fdData, 0); + log_warning("%p", segments); + if ((uintptr_t)segments != GOOD_PLACE_FOR_DATA_SEGMENT) { + perror("mmap"); + return false; + } +#endif // defined(USE_MMAP) && (defined(__unix__) || defined(__MACOSX__)) + +#if defined(__unix__) && !defined(NO_RCT2) + int pageSize = getpagesize(); + int numPages = (len + pageSize - 1) / pageSize; + unsigned char *dummy = malloc(numPages); + + err = mincore((void *)segments, len, dummy); + bool pagesMissing = false; + if (err != 0) + { + err = errno; +#ifdef __LINUX__ + // On Linux ENOMEM means all requested range is unmapped + if (err != ENOMEM) + { + pagesMissing = true; + perror("mincore"); + } +#else + pagesMissing = true; + perror("mincore"); +#endif // __LINUX__ + } else { + for (int i = 0; i < numPages; i++) + { + if (dummy[i] != 1) + { + pagesMissing = true; + void *start = (void *)segments + i * pageSize; + void *end = (void *)segments + (i + 1) * pageSize - 1; + log_warning("required page %p - %p is not in memory!", start, end); + } + } + } + free(dummy); + if (pagesMissing) + { + log_error("At least one of required pages was not found in memory. This can cause segfaults later on."); + } +#if !defined(USE_MMAP) + // section: text + err = mprotect((void *)0x401000, 0x8a4000 - 0x401000, PROT_READ | PROT_EXEC | PROT_WRITE); + if (err != 0) + { + perror("mprotect"); + } +#endif // !defined(USE_MMAP) + // section: rw data + err = mprotect((void *)segments, 0x01429000 - 0x8a4000, PROT_READ | PROT_WRITE); + if (err != 0) + { + perror("mprotect"); + } +#endif // defined(__unix__) + +#if defined(USE_MMAP) && defined(__WINDOWS__) + segments = VirtualAlloc((void *)(GOOD_PLACE_FOR_DATA_SEGMENT), len, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); + if ((uintptr_t)segments != GOOD_PLACE_FOR_DATA_SEGMENT) { + log_error("VirtualAlloc, segments = %p, GetLastError = 0x%x", segments, GetLastError()); + return false; + } + + utf8 segmentDataPath[MAX_PATH]; + rct2_interop_get_segment_data_path(segmentDataPath, sizeof(segmentDataPath)); + SDL_RWops * rw = SDL_RWFromFile(segmentDataPath, "rb"); + if (rw == NULL) + { + log_error("failed to load file"); + return false; + } + if (SDL_RWread(rw, segments, len, 1) != 1) { + log_error("Unable to read chunk header!"); + return false; + } + SDL_RWclose(rw); +#endif // defined(USE_MMAP) && defined(__WINDOWS__) + +#if !defined(NO_RCT2) && defined(USE_MMAP) + // Check that the expected data is at various addresses. + // Start at 0x9a6000, which is start of .data, to skip the region containing addresses to DLL + // calls, which can be changed by windows/wine loader. + const uint32 c1 = sawyercoding_calculate_checksum((const uint8*)(segments + (uintptr_t)(0x009A6000 - 0x8a4000)), 0x009E0000 - 0x009A6000); + const uint32 c2 = sawyercoding_calculate_checksum((const uint8*)(segments + (uintptr_t)(0x01428000 - 0x8a4000)), 0x014282BC - 0x01428000); + const uint32 exp_c1 = 10114815; + const uint32 exp_c2 = 23564; + if (c1 != exp_c1 || c2 != exp_c2) { + log_warning("c1 = %u, expected %u, match %d", c1, exp_c1, c1 == exp_c1); + log_warning("c2 = %u, expected %u, match %d", c2, exp_c2, c2 == exp_c2); + return false; + } +#endif + return true; +} + +/** + * Setup hooks to allow RCT2 to call OpenRCT2 functions instead. + */ +void rct2_interop_setup_hooks() +{ + // None for now +} + +void rct2_interop_dispose() +{ +#if defined(USE_MMAP) && (defined(__unix__) || defined(__MACOSX__)) && !defined(NO_RCT2) + munmap(segments, 12079104); + close(fdData); +#endif +} diff --git a/src/rct2/interop.h b/src/rct2/interop.h new file mode 100644 index 0000000000..5f7c598cd7 --- /dev/null +++ b/src/rct2/interop.h @@ -0,0 +1,24 @@ +#pragma region Copyright (c) 2014-2016 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 _RCT2_INTEROP_H_ +#define _RCT2_INTEROP_H_ + +bool rct2_interop_setup_segment(); +void rct2_interop_setup_hooks(); +void rct2_interop_dispose(); + +#endif diff --git a/test/testpaint/testpaint.vcxproj b/test/testpaint/testpaint.vcxproj index 047bd39467..c0191e40cf 100644 --- a/test/testpaint/testpaint.vcxproj +++ b/test/testpaint/testpaint.vcxproj @@ -17,7 +17,6 @@ {57E60BA1-FB76-4316-909E-C1449C142327} testpaint - DynamicLibrary @@ -30,18 +29,6 @@ true MultiByte - - - - - - - - - - - - $(SolutionDir)bin\testpaint\ $(SolutionDir)obj\$(ProjectName)\$(Configuration)\