mirror of https://github.com/OpenRCT2/OpenRCT2.git
Make it work on Linux
Right now the project is decompiled to the point where it is feasible to
try porting it to another platform. It doesn't work 100% correctly, but
it's nearing this state.
To port it to Linux I mmapped the openrct2.exe into expected places,
disabled two offending calls (RCT2_CALLPROC_EBPSAFE(0x0040701D) and
RCT2_CALLPROC_X(0x006E7FF3…)), replaced memory management routines
with generic ones and removed all the function-pointer calls.
A basic, non-exhaustive check is included to verify that memory is
loaded correctly in place.
That last bit is probably the most intrusive one, but had to be done, as
the calling convention on Linux differs from the one on Windows. It
could possibly be emulated (just like RCT2_CALLFUNC_X) until dependency
on exe is dropped.
It is possible to completely remove calls out to original code by
commenting out contents of RCT2_CALLFUNC_X, right now this will yield
working UI, but no rendering of peeps, rides or rest of world. This can
be used as a benchmark or test platform for correctness of
implementation. The data sections will still be required for now.
Assets are expected to be in specific places, so to launch it, following
needs to satisified:
* $build/data/ has to have contents of $RCT2/Data/
* $build/data/ (same as above) has to have contents of $repo/data/
* $build/ObjData/ has to have contents of $RCT2/ObjData/
* $build/../openrct2.exe has to be $repo/openrct2.exe (as of 976ea0d
)
Keep in mind you can symlink stuff and that filesystems are case
sensitive!
You can copy more of required data to possibly improve your experience.
Pretty much all of this commit will possibly have to be reverted by the
time OpenRCT2 gains independence.
Remember to build with -DDISABLE_NETWORK=ON -DDISABLE_HTTP_TWITCH=ON
This commit is contained in:
parent
a5d85cd15f
commit
1bd8e11c0f
|
@ -27,6 +27,7 @@
|
|||
|
||||
#define RCT2_ADDRESS(address, type) ((type*)(address))
|
||||
#define RCT2_GLOBAL(address, type) (*((type*)(address)))
|
||||
#ifdef _WIN32
|
||||
#define RCT2_CALLPROC(address) (((void(*)())(address))())
|
||||
#define RCT2_CALLFUNC(address, returnType) (((returnType(*)())(address))())
|
||||
|
||||
|
@ -36,6 +37,16 @@
|
|||
#define RCT2_CALLFUNC_4(address, returnType, a1, a2, a3, a4, v1, v2, v3, v4) (((returnType(*)(a1, a2, a3, a4))(address))(v1, v2, v3, v4))
|
||||
#define RCT2_CALLFUNC_5(address, returnType, a1, a2, a3, a4, a5, v1, v2, v3, v4, v5) (((returnType(*)(a1, a2, a3, a4, a5))(address))(v1, v2, v3, v4, v5))
|
||||
#define RCT2_CALLFUNC_6(address, returnType, a1, a2, a3, a4, a5, a6, v1, v2, v3, v4, v5, v6) (((returnType(*)(a1, a2, a3, a4, a5, a6))(address))(v1, v2, v3, v4, v5, v6))
|
||||
#else
|
||||
#define RCT2_CALLPROC(address)
|
||||
#define RCT2_CALLFUNC(address, returnType)
|
||||
#define RCT2_CALLFUNC_1(address, returnType, a1, v1)
|
||||
#define RCT2_CALLFUNC_2(address, returnType, a1, a2, v1, v2)
|
||||
#define RCT2_CALLFUNC_3(address, returnType, a1, a2, a3, v1, v2, v3)
|
||||
#define RCT2_CALLFUNC_4(address, returnType, a1, a2, a3, a4, v1, v2, v3, v4)
|
||||
#define RCT2_CALLFUNC_5(address, returnType, a1, a2, a3, a4, a5, v1, v2, v3, v4, v5)
|
||||
#define RCT2_CALLFUNC_6(address, returnType, a1, a2, a3, a4, a5, a6, v1, v2, v3, v4, v5, v6)
|
||||
#endif // _WIN32
|
||||
|
||||
#define RCT2_CALLPROC_1(address, a1, v1) RCT2_CALLFUNC_1(address, void, a1, v1)
|
||||
#define RCT2_CALLPROC_2(address, a1, a2, v1, v2) RCT2_CALLFUNC_2(address, void, a1, a2, v1, v2)
|
||||
|
|
|
@ -545,9 +545,13 @@ Channel* Mixer::Play(Source& source, int loop, bool deleteondone, bool deletesou
|
|||
|
||||
void Mixer::Stop(Channel& channel)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
Lock();
|
||||
channel.stopping = true;
|
||||
Unlock();
|
||||
#else
|
||||
#warning unimplemented
|
||||
#endif // _WIN32
|
||||
}
|
||||
|
||||
bool Mixer::LoadMusic(int pathid)
|
||||
|
|
|
@ -271,7 +271,11 @@ void sub_689174(sint16* x, sint16* y, sint16 *z)
|
|||
|
||||
void sub_6E7FF3(rct_window *w, rct_viewport *viewport, int x, int y)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
RCT2_CALLPROC_X(0x006E7FF3, 0, 0, 0, x, (int)viewport, (int)w, y);
|
||||
#else
|
||||
STUB();
|
||||
#endif // _WIN32
|
||||
|
||||
// int zoom = 1 << viewport->zoom;
|
||||
// if (w >= RCT2_GLOBAL(RCT2_ADDRESS_NEW_WINDOW_PTR, rct_window*)){
|
||||
|
|
|
@ -2420,7 +2420,11 @@ void textinput_cancel()
|
|||
|
||||
// The following code is only necessary for the old Windows text input dialog. In theory this isn't used anymore, but can
|
||||
// still be triggered via original code paths.
|
||||
#ifdef _WIN32
|
||||
RCT2_CALLPROC_EBPSAFE(0x0040701D);
|
||||
#else
|
||||
log_warning("there should be something called here (0x0040701D)");
|
||||
#endif // _WIN32
|
||||
if (RCT2_GLOBAL(0x009DEB8C, uint8) != 255) {
|
||||
RCT2_CALLPROC_EBPSAFE(0x006EE4E2);
|
||||
w = window_find_by_number(
|
||||
|
|
|
@ -855,7 +855,7 @@ int win1252_to_utf8(utf8string dst, const char *src, int maxBufferLength)
|
|||
MultiByteToWideChar(CP_ACP, 0, src, -1, intermediateBuffer, bufferCount);
|
||||
int result = WideCharToMultiByte(CP_UTF8, 0, intermediateBuffer, -1, dst, maxBufferLength, NULL, NULL);
|
||||
#else
|
||||
STUB();
|
||||
//STUB();
|
||||
// we cannot walk past maxBufferLength, but in case we have still space left
|
||||
// we need one byte for null terminator
|
||||
int result = strnlen(src, maxBufferLength) + 1;
|
||||
|
|
|
@ -40,6 +40,15 @@
|
|||
#include "util/util.h"
|
||||
#include "world/mapgen.h"
|
||||
|
||||
#ifdef __linux__
|
||||
#include <sys/mman.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#endif // __linux__
|
||||
|
||||
int gOpenRCT2StartupAction = STARTUP_ACTION_TITLE;
|
||||
utf8 gOpenRCT2StartupActionPath[512] = { 0 };
|
||||
utf8 gExePath[MAX_PATH];
|
||||
|
@ -175,6 +184,86 @@ bool openrct2_initialise()
|
|||
return false;
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
#define DATA_OFFSET 0x004A4000
|
||||
|
||||
const char *exepath = "../openrct2.exe";
|
||||
int fd = open(exepath, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
log_fatal("failed to open %s, errno = %d", exepath, errno);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// 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
|
||||
//
|
||||
// Since mmap does not provide a way to create a mapping with virtual size,
|
||||
// I resorted to creating a one large map for data and memcpy'ing data where
|
||||
// required.
|
||||
// Another section is needed for .text, as it requires PROT_EXEC flag.
|
||||
|
||||
// TODO: UGLY, UGLY HACK!
|
||||
off_t file_size = 6750208;
|
||||
|
||||
int len = 0x01429000 - 0x8a4000; // 0xB85000, 12079104 bytes or around 11.5MB
|
||||
// section: rw data
|
||||
void *base = mmap((void *)0x8a4000, len, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, 0, 0);
|
||||
log_warning("base = %x, 0x01423b40 >= base == %i, 0x01423b40 < base + len == %i", base, (void *)0x01423b40 >= base, (void *)0x01423b40 < base + len);
|
||||
if (base == MAP_FAILED) {
|
||||
log_warning("errno = %i", errno);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
len = 0x004A3000;
|
||||
// section: text
|
||||
void *base2 = mmap((void *)(0x401000), len, PROT_EXEC | PROT_WRITE | PROT_READ, MAP_PRIVATE, fd, 0x1000);
|
||||
if (base2 != (void *)(0x401000))
|
||||
{
|
||||
log_fatal("mmap failed to get required offset! got %p, expected %p, errno = %d", base2, (void *)(0x401000), errno);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void *fbase = mmap(NULL, file_size, PROT_READ, MAP_PRIVATE, fd, 0);
|
||||
int err = errno;
|
||||
log_warning("mmapped file to %p", fbase);
|
||||
if (fbase == MAP_FAILED)
|
||||
{
|
||||
log_fatal("mmap failed to get required offset! got %p, errno = %d", fbase, err);
|
||||
exit(1);
|
||||
}
|
||||
// .rdata and real part of .data
|
||||
// 0x9e2000 - 0x8a4000 = 0x13e000
|
||||
memcpy(base, fbase + DATA_OFFSET, 0x13e000);
|
||||
#endif // __linux__
|
||||
const uint32 c1 = sawyercoding_calculate_checksum((void *)0x009ACFA4, 128);
|
||||
const uint32 c2 = sawyercoding_calculate_checksum((void *)0x009ACFA4, 720 * 4);
|
||||
const uint32 exp_c1 = 32640;
|
||||
const uint32 exp_c2 = 734400;
|
||||
log_warning("c1 = %u, expected %u, match %d", c1, exp_c1, c1 == exp_c1);
|
||||
log_warning("c1 = %u, expected %u, match %d", c2, exp_c2, c2 == exp_c2);
|
||||
if (c1 != exp_c1 || c2 != exp_c2)
|
||||
{
|
||||
exit(1);
|
||||
}
|
||||
openrct2_set_exe_path();
|
||||
|
||||
config_set_defaults();
|
||||
|
|
|
@ -517,7 +517,6 @@ void platform_show_cursor()
|
|||
|
||||
void platform_get_cursor_position(int *x, int *y)
|
||||
{
|
||||
|
||||
STUB();
|
||||
}
|
||||
|
||||
|
|
15
src/rct2.c
15
src/rct2.c
|
@ -567,7 +567,12 @@ void get_local_time()
|
|||
*/
|
||||
void *rct2_malloc(size_t numBytes)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return RCT2_CALLFUNC_1(0x004068B2, void*, size_t, numBytes);
|
||||
#else
|
||||
//log_warning("call rct's function");
|
||||
return malloc(numBytes);
|
||||
#endif // _WIN32
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -577,7 +582,12 @@ void *rct2_malloc(size_t numBytes)
|
|||
*/
|
||||
void *rct2_realloc(void *block, size_t numBytes)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return RCT2_CALLFUNC_2(0x004068BD, void*, void*, size_t, block, numBytes);
|
||||
#else
|
||||
//log_warning("call rct's function");
|
||||
return realloc(block, numBytes);
|
||||
#endif // _WIN32
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -586,5 +596,10 @@ void *rct2_realloc(void *block, size_t numBytes)
|
|||
*/
|
||||
void rct2_free(void *block)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
RCT2_CALLPROC_1(0x004068CD, void*, block);
|
||||
#else
|
||||
//log_warning("call rct's function");
|
||||
free(block);
|
||||
#endif // _WIN32
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue