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)\