Linux platform file

Add bulk of the platform file for linux, change the compilation target
from shared library to executable for linux, provide necessary changes
to make it compile & load the stub of a process.

Make sure functions are marked as stubs where needed, and implementation
is expected.
This commit is contained in:
Michał Janiszewski 2015-08-04 22:39:44 +02:00
parent 46a4b47715
commit 4ce0e3a8b2
15 changed files with 864 additions and 72 deletions

View File

@ -36,8 +36,8 @@ if (DISABLE_NETWORK)
add_definitions(-DDISABLE_NETWORK)
endif (DISABLE_NETWORK)
set(ORCTLIBS_INCLUDE /usr/local/cross-tools/orctlibs/include)
set(JANSSON_INCLUDE /usr/local/cross-tools/orctlibs/include/jansson)
set(ORCTLIBS_INCLUDE /usr/local/cross-tools/orctlibs/include)
set(JANSSON_INCLUDE /usr/local/cross-tools/orctlibs/include/jansson)
set(ORCTLIBS_LIB_DIR /usr/local/cross-tools/orctlibs/lib)
set(ORCTLIBS_LIB jansson curl ssl crypto)
@ -50,8 +50,8 @@ file(GLOB_RECURSE ORCT2_SOURCES "src/*.c" "src/*.cpp" "lib/*.c")
if (UNIX)
# force 32bit build for now and set necessary flags to compile code as is
set(CMAKE_C_FLAGS "-m32 -masm=intel -std=gnu99")
set(CMAKE_CXX_FLAGS "-m32 -std=gnu++11 -masm=intel")
set(CMAKE_C_FLAGS "-m32 -masm=intel -fvar-tracking-assignments -std=gnu99")
set(CMAKE_CXX_FLAGS "-m32 -std=gnu++11 -fvar-tracking-assignments -masm=intel")
set(LIB32 /usr/lib32)
set(CMAKE_SHARED_LINKER_FLAGS "-m32 -Wl,-melf_i386 -L${LIB32} -lSDL2_ttf")
set(CMAKE_EXE_LINKER_FLAGS ${CMAKE_SHARED_LINKER_FLAGS})
@ -62,11 +62,15 @@ if (UNIX)
INCLUDE_DIRECTORIES(${SDL2_INCLUDE_DIRS})
endif (UNIX)
INCLUDE_DIRECTORIES(${ORCTLIBS_INCLUDE} ${JANSSON_INCLUDE})
LINK_DIRECTORIES(${SDL2_LIBRARY_DIRS} ${ORCTLIBS_LIB_DIR})
if (WIN32)
# build as library for now, replace with add_executable
add_library(${PROJECT} SHARED ${ORCT2_SOURCES})
else (WIN32)
add_executable(${PROJECT} ${ORCT2_SOURCES})
endif (WIN32)
# build as library for now, replace with add_executable
add_library(${PROJECT} SHARED ${ORCT2_SOURCES})
INCLUDE_DIRECTORIES(${ORCTLIBS_INCLUDE} ${JANSSON_INCLUDE})
LINK_DIRECTORIES(${LINK_DIRECTORIES} ${LIB32} ${SDL2_LIBRARY_DIRS} ${ORCTLIBS_LIB_DIR})
# install into ${CMAKE_INSTALL_PREFIX}/bin/
#install (TARGETS ${PROJECT} DESTINATION bin)
@ -79,4 +83,3 @@ TARGET_LINK_LIBRARIES(${PROJECT} ${SDL2_LIBRARIES} ${ORCTLIBS_LIB})
if (WIN32)
target_link_libraries(${PROJECT} winmm.lib -limm32 -lversion -ldsound ws2_32)
endif (WIN32)

View File

@ -87,7 +87,7 @@ elif [[ `uname` == "Linux" ]]; then
apt-cache search libsdl2
apt-cache policy libsdl2-dev:i386
apt-cache policy libsdl2-dev
sudo apt-get install -y --force-yes binutils-mingw-w64-i686 gcc-mingw-w64-i686 g++-mingw-w64-i686 cmake libsdl2-dev:i386 libsdl2-ttf-dev:i386 gcc-4.8 pkg-config:i386 g++-4.8-multilib gcc-4.8-multilib libjansson-dev:i386 libspeex-dev:i386 libspeexdsp-dev:i386
sudo apt-get install -y --force-yes binutils-mingw-w64-i686 gcc-mingw-w64-i686 g++-mingw-w64-i686 cmake libsdl2-dev:i386 libsdl2-ttf-dev:i386 gcc-4.8 pkg-config:i386 g++-4.8-multilib gcc-4.8-multilib libjansson-dev:i386 libspeex-dev:i386 libspeexdsp-dev:i386 libcurl4-openssl-dev:i386 libcrypto++-dev:i386
export CC=gcc-4.8
export CXX=g++-4.8
fi

View File

@ -8,12 +8,12 @@
* 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.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*****************************************************************************/
@ -556,7 +556,8 @@ int sound_channel_load_file2(int channel, const char* filename, int offset)
if (sound_channel_is_playing(channel)) {
sound_channel_stop(channel);
}
if (SUCCEEDED(sound_channel_load_file(channel, filename, offset))) {
int load_result = sound_channel_load_file(channel, filename, offset);
if (load_result >= 0) {
RCT2_ADDRESS(RCT2_ADDRESS_SOUND_CHANNEL_LIST, rct_sound_channel)[channel].var_4 = 0;
return 1;
}
@ -961,7 +962,7 @@ int sound_play(rct_sound* sound, int looping, int volume, int pan, int frequency
} else {
playflags = 0;
}
if (SUCCEEDED(sound->dsbuffer->lpVtbl->Play(sound->dsbuffer, 0, 0, playflags)))
if (SUCCEEDED(sound->dsbuffer->lpVtbl->Play(sound->dsbuffer, 0, 0, playflags)))
return 1;
}
@ -1497,7 +1498,7 @@ int dsound_create_primary_buffer(int a, int device, int channels, int samples, i
if (FAILED(DirectSoundCreate(&dsdevice->guid, &RCT2_GLOBAL(RCT2_ADDRESS_DIRECTSOUND, LPDIRECTSOUND), 0))) {
return 0;
}
if (FAILED(RCT2_GLOBAL(RCT2_ADDRESS_DIRECTSOUND, LPDIRECTSOUND)->lpVtbl->SetCooperativeLevel(RCT2_GLOBAL(RCT2_ADDRESS_DIRECTSOUND, LPDIRECTSOUND), windows_get_window_handle(), DSSCL_NORMAL)) ||
if (FAILED(RCT2_GLOBAL(RCT2_ADDRESS_DIRECTSOUND, LPDIRECTSOUND)->lpVtbl->SetCooperativeLevel(RCT2_GLOBAL(RCT2_ADDRESS_DIRECTSOUND, LPDIRECTSOUND), windows_get_window_handle(), DSSCL_NORMAL)) ||
FAILED(RCT2_GLOBAL(RCT2_ADDRESS_DIRECTSOUND, LPDIRECTSOUND)->lpVtbl->CreateSoundBuffer(RCT2_GLOBAL(RCT2_ADDRESS_DIRECTSOUND, LPDIRECTSOUND), &bufferdesc, &RCT2_GLOBAL(0x009E2BA8, LPDIRECTSOUNDBUFFER), 0))) {
RCT2_GLOBAL(RCT2_ADDRESS_DIRECTSOUND, LPDIRECTSOUND)->lpVtbl->Release(RCT2_GLOBAL(RCT2_ADDRESS_DIRECTSOUND, LPDIRECTSOUND));
RCT2_GLOBAL(RCT2_ADDRESS_DIRECTSOUND, LPDIRECTSOUND) = 0;

View File

@ -79,4 +79,4 @@ void diagnostic_log_with_location(int diagnosticLevel, const char *file, const c
// Line terminator
fprintf(stream, "\n");
}
}

View File

@ -57,4 +57,4 @@ void diagnostic_log_with_location(int diagnosticLevel, const char *file, const c
#endif
#endif
#endif

View File

@ -8,12 +8,12 @@
* 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.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*****************************************************************************/
@ -22,6 +22,7 @@
#include "../localisation/localisation.h"
#include "../sprites.h"
#include "../world/map.h"
#include "../platform/platform.h"
#include "drawing.h"
static int ttf_get_string_width(const utf8 *text);
@ -353,7 +354,7 @@ void gfx_draw_string_centred(rct_drawpixelinfo *dpi, int format, int x, int y, i
}
/**
*
*
* rct2: 0x006C1E53
* dpi (edi)
* args (esi)
@ -407,7 +408,7 @@ int gfx_draw_string_centred_wrapped(rct_drawpixelinfo *dpi, void *args, int x, i
}
/**
*
*
* rct2: 0x006C2105
* dpi (edi)
* args (esi)
@ -441,7 +442,7 @@ int gfx_draw_string_left_wrapped(rct_drawpixelinfo *dpi, void *args, int x, int
gfx_draw_string(dpi, buffer, 0xFE, x, lineY);
buffer = get_string_end(buffer) + 1;
lineY += lineHeight;
}
}
return lineY - y;
}
@ -492,7 +493,7 @@ void colour_char(uint8 colour, uint16* current_font_flags, uint8* palette_pointe
if (!(*current_font_flags & 2)) {
eax = eax & 0x0FF0000FF;
}
// Adjust text palette. Store current colour?
// Adjust text palette. Store current colour?
palette_pointer[1] = eax & 0xFF;
palette_pointer[2] = (eax >> 8) & 0xFF;
palette_pointer[3] = (eax >> 16) & 0xFF;
@ -512,7 +513,7 @@ void colour_char_window(uint8 colour, uint16* current_font_flags,uint8* palette_
if (*current_font_flags & 2) {
eax |= 0x0A0A00;
}
//Adjust text palette. Store current colour?
//Adjust text palette. Store current colour?
palette_pointer[1] = eax & 0xFF;
palette_pointer[2] = (eax >> 8) & 0xFF;
palette_pointer[3] = (eax >> 16) & 0xFF;
@ -521,7 +522,7 @@ void colour_char_window(uint8 colour, uint16* current_font_flags,uint8* palette_
}
/**
*
*
* rct2: 0x00682702
* dpi (edi)
* buffer (esi)
@ -712,7 +713,7 @@ void gfx_draw_string_centred_wrapped_partial(rct_drawpixelinfo *dpi, int x, int
lineY = y - ((numLines * lineHeight) / 2);
for (int line = 0; line <= numLines; line++) {
int halfWidth = gfx_get_string_width(buffer) / 2;
utf8 *ch = buffer;
utf8 *nextCh;
int codepoint;
@ -735,7 +736,7 @@ void gfx_draw_string_centred_wrapped_partial(rct_drawpixelinfo *dpi, int x, int
buffer = get_string_end(buffer) + 1;
lineY += lineHeight;
}
}
}
static uint32 _ttf_surface_cache_hash(TTF_Font *font, const utf8 *text)
@ -884,7 +885,7 @@ bool ttf_initialise()
for (int i = 0; i < 4; i++) {
TTFFontDescriptor *fontDesc = &(gCurrentTTFFontSet->size[i]);
utf8 fontPath[MAX_PATH] = "C:\\Windows\\Fonts\\";
strcat(fontPath, fontDesc->filename);
@ -1000,7 +1001,7 @@ static void ttf_draw_string_raw_ttf(rct_drawpixelinfo *dpi, const utf8 *text, te
return;
}
}
int fontSize = font_get_size_from_sprite_base(info->font_sprite_base);
int drawX = info->x + fontDesc->offset_x;
int drawY = info->y + fontDesc->offset_y;

View File

@ -856,7 +856,12 @@ int win1252_to_utf8(utf8string dst, const char *src, int maxBufferLength)
int result = WideCharToMultiByte(CP_UTF8, 0, intermediateBuffer, -1, dst, maxBufferLength, NULL, NULL);
#else
STUB();
int result = 0;
// 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;
result = min(result, maxBufferLength);
strncpy(dst, src, maxBufferLength);
dst[maxBufferLength - 1] = '\0';
#endif // _WIN32
if (heapBuffer != NULL) {

View File

@ -56,7 +56,7 @@ int object_load_file(int groupIndex, const rct_object_entry *entry, int* chunkSi
{
uint8 objectType;
rct_object_entry openedEntry;
char path[260];
char path[MAX_PATH];
SDL_RWops* rw;
subsitute_path(path, RCT2_ADDRESS(RCT2_ADDRESS_OBJECT_DATA_PATH, char), (char*)installedObject + 16);
@ -259,7 +259,7 @@ int object_load_packed(SDL_RWops* rw)
}
if (entryGroupIndex == object_entry_group_counts[type]){
// This should never occur. Objects are not loaded before installing a
// This should never occur. Objects are not loaded before installing a
// packed object. So there is only one object loaded at this point.
log_error("Too many objects of the same type loaded.");
rct2_free(chunk);
@ -285,7 +285,7 @@ int object_load_packed(SDL_RWops* rw)
}
// Convert the entry name to a upper case path name
char path[260];
char path[MAX_PATH];
char objectPath[9] = { 0 };
for (int i = 0; i < 8; ++i){
if (entry.name[i] != ' ')
@ -400,7 +400,7 @@ int object_calculate_checksum(const rct_object_entry *entry, const char *data, i
int object_chunk_load_image_directory(uint8_t** chunk)
{
int image_start_no = RCT2_GLOBAL(RCT2_ADDRESS_TOTAL_NO_IMAGES, uint32_t);
// First dword of chunk is no_images
int no_images = *((uint32_t*)(*chunk));
*chunk += 4;
@ -754,7 +754,7 @@ int paint_ride_entry(int flags, int ebx, int ecx, int edx, rct_drawpixelinfo* dp
return flags;
}
else
{
{
rct_window* w = (rct_window*)esi;
int width = w->x + w->width - x - 4;
@ -1415,7 +1415,7 @@ int paint_water_entry(int flags, int ebx, int ecx, int edx, rct_drawpixelinfo* d
return 0;
}
else if ((flags & 0xFF) == 3){
if (!((flags >> 8) & 0xFF))
if (!((flags >> 8) & 0xFF))
gfx_draw_string_centred(dpi, 3326, ecx, edx, 0, (void*)esi);
}
return flags;
@ -1467,7 +1467,7 @@ int object_paint(int type, int eax, int ebx, int ecx, int edx, int esi, int edi,
switch (type)
{
case OBJECT_TYPE_RIDE:
return paint_ride_entry(eax, ebx, ecx, edx, (rct_drawpixelinfo*)edi, esi, ebp);
return paint_ride_entry(eax, ebx, ecx, edx, (rct_drawpixelinfo*)edi, esi, ebp);
case OBJECT_TYPE_SMALL_SCENERY:
return paint_small_scenery(eax, ebx, ecx, edx, (rct_drawpixelinfo*)edi, esi, ebp);
case OBJECT_TYPE_LARGE_SCENERY:
@ -1477,7 +1477,7 @@ int object_paint(int type, int eax, int ebx, int ecx, int edx, int esi, int edi,
case OBJECT_TYPE_BANNERS:
return paint_banner(eax, ebx, ecx, edx, (rct_drawpixelinfo*)edi, esi, ebp);
case OBJECT_TYPE_PATHS:
return paint_path_entry(eax, ebx, ecx, edx, (rct_drawpixelinfo*)edi, esi, ebp);
return paint_path_entry(eax, ebx, ecx, edx, (rct_drawpixelinfo*)edi, esi, ebp);
case OBJECT_TYPE_PATH_BITS:
return paint_path_bit(eax, ebx, ecx, edx, (rct_drawpixelinfo*)edi, esi, ebp);
case OBJECT_TYPE_SCENERY_SETS:
@ -1503,14 +1503,14 @@ int object_get_scenario_text(rct_object_entry *entry)
rct_object_entry *installedObject = RCT2_GLOBAL(RCT2_ADDRESS_INSTALLED_OBJECT_LIST, rct_object_entry*);
installedObject = object_list_find(entry);
if (installedObject == NULL){
log_error("Object not found: %.8s", entry->name);
RCT2_GLOBAL(0x00F42BD9, uint8) = 0;
return 0;
}
char path[260];
char path[MAX_PATH];
char *objectPath = (char*)installedObject + 16;
subsitute_path(path, RCT2_ADDRESS(RCT2_ADDRESS_OBJECT_DATA_PATH, char), objectPath);
@ -1571,7 +1571,7 @@ int object_get_scenario_text(rct_object_entry *entry)
memcpy(gTempObjectLoadName, openedEntry.name, 8);
// Not used??
RCT2_GLOBAL(0x009ADAFD, uint8) = 1;
object_paint(openedEntry.flags & 0x0F, 0, 0, 0, 0, (int)chunk, 0, 0);
object_paint(openedEntry.flags & 0x0F, 0, 0, 0, 0, (int)chunk, 0, 0);
// Tell text to be loaded into normal address
RCT2_GLOBAL(0x009ADAFC, uint8) = 0;
// Not used??
@ -1630,7 +1630,7 @@ rct_object_entry *object_get_next(rct_object_entry *entry)
// Skip theme objects
pos += *pos * 16 + 1;
// Skip
// Skip
pos += 4;
return (rct_object_entry*)pos;

View File

@ -8,12 +8,12 @@
* 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.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*****************************************************************************/
@ -75,7 +75,7 @@ extern int object_entry_group_counts[];
extern int object_entry_group_encoding[];
typedef struct {
uint8 **chunks;
uint8 **chunks;
rct_object_entry_extended *entries;
} rct_object_entry_group;
@ -122,4 +122,4 @@ char *object_get_name(rct_object_entry *entry);
rct_object_filters *get_object_filter(int index);
#endif
#endif

View File

@ -8,12 +8,12 @@
* 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.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*****************************************************************************/
@ -115,7 +115,7 @@ static void object_list_sort()
objectBuffer = &RCT2_GLOBAL(RCT2_ADDRESS_INSTALLED_OBJECT_LIST, rct_object_entry*);
numObjects = RCT2_GLOBAL(RCT2_ADDRESS_OBJECT_LIST_NO_ITEMS, sint32);
copied = calloc(numObjects, sizeof(uint8));
// Get buffer size
entry = *objectBuffer;
for (i = 0; i < numObjects; i++)
@ -166,7 +166,7 @@ static void object_list_sort()
}
/**
*
*
* rct2: 0x006A93CD
*/
static void object_list_examine()
@ -237,7 +237,7 @@ static int object_list_query_directory(int *outTotalFiles, uint64 *outTotalFileS
}
/**
*
*
* rct2: 0x006A8B40
*/
void object_list_load()
@ -291,17 +291,17 @@ void object_list_load()
free(_installedObjectFilters);
_installedObjectFilters = NULL;
}
enumFileHandle = platform_enumerate_files_begin(RCT2_ADDRESS(RCT2_ADDRESS_OBJECT_DATA_PATH, char));
if (enumFileHandle != INVALID_HANDLE) {
uint32 installed_buffer_size = 0x1000;
while (platform_enumerate_files_next(enumFileHandle, &enumFileInfo)) {
fileCount++;
if ((installed_buffer_size - current_item_offset) <= 2842){
installed_buffer_size += 0x1000;
RCT2_GLOBAL(RCT2_ADDRESS_INSTALLED_OBJECT_LIST, void*) = rct2_realloc(RCT2_GLOBAL(RCT2_ADDRESS_INSTALLED_OBJECT_LIST, void*), installed_buffer_size);
RCT2_GLOBAL(RCT2_ADDRESS_INSTALLED_OBJECT_LIST, void*) = rct2_realloc(RCT2_GLOBAL(RCT2_ADDRESS_INSTALLED_OBJECT_LIST, void*), installed_buffer_size);
if (RCT2_GLOBAL(RCT2_ADDRESS_INSTALLED_OBJECT_LIST, int) == -1){
log_error("Failed to allocate memory for object list");
rct2_exit_reason(835, 3162);
@ -355,6 +355,11 @@ static int object_list_cache_load(int totalFiles, uint64 totalFileSize, int file
rct_plugin_header pluginHeader;
uint32 filterVersion = 0;
#ifndef _WIN32
// TODO: remove me!
log_error("this is to be removed after testing/implementation is done");
return 0;
#endif
log_verbose("loading object list cache (plugin.dat)");
get_plugin_path(path);
@ -391,7 +396,7 @@ static int object_list_cache_load(int totalFiles, uint64 totalFileSize, int file
free(_installedObjectFilters);
_installedObjectFilters = malloc(sizeof(rct_object_filters) * pluginHeader.object_list_no_items);
if (SDL_RWread(file, _installedObjectFilters, sizeof(rct_object_filters) * pluginHeader.object_list_no_items, 1) == 1) {
SDL_RWclose(file);
reset_loaded_objects();
object_list_examine();
@ -431,7 +436,7 @@ static int object_list_cache_save(int fileCount, uint64 totalFileSize, int fileD
SDL_RWops *file;
rct_plugin_header pluginHeader;
uint32 filterVersion = FILTER_VERSION;
log_verbose("saving object list cache (plugin.dat)");
pluginHeader.total_files = fileCount | 0x01000000;
@ -519,7 +524,7 @@ void set_load_objects_fail_reason(){
}
/**
*
*
* rct2: 0x006AA0C6
*/
int object_read_and_load_entries(SDL_RWops* rw)
@ -557,7 +562,7 @@ int object_read_and_load_entries(SDL_RWops* rw)
}
}
free(entries);
free(entries);
if (load_fail){
object_unload_all();
RCT2_GLOBAL(0x14241BC, uint32) = 0;
@ -571,7 +576,7 @@ int object_read_and_load_entries(SDL_RWops* rw)
/**
*
*
* rct2: 0x006A9CE8
*/
void object_unload_all()
@ -631,7 +636,7 @@ void object_list_create_hash_table()
// Set hash table slot
_installedObjectHashTable[index] = installedObject;
// Next installed object
installedObject = object_get_next(installedObject);
}
@ -645,8 +650,8 @@ int find_object_in_entry_group(rct_object_entry* entry, uint8* entry_type, uint8
*entry_type = entry->flags & 0xF;
rct_object_entry_group entry_group = object_entry_groups[*entry_type];
for (*entry_index = 0;
*entry_index < object_entry_group_counts[*entry_type];
for (*entry_index = 0;
*entry_index < object_entry_group_counts[*entry_type];
++(*entry_index),
entry_group.chunks++,
entry_group.entries++){

View File

@ -135,8 +135,19 @@ static void openrct2_set_exe_path()
if (bytesRead == -1) {
log_fatal("failed to read /proc/self/exe");
}
exePath[MAX_PATH - 1] = '\0';
strncpy(gExePath, exePath, MAX_PATH);
exePath[bytesRead] = '\0';
log_verbose("######################################## Setting exe path to %s", exePath);
char *exeDelimiter = strrchr(exePath, platform_get_path_separator());
if (exeDelimiter == NULL)
{
log_error("should never happen here");
gExePath[0] = '\0';
return;
}
int exeDelimiterIndex = (int)(exeDelimiter - exePath);
strncpy(gExePath, exePath, exeDelimiterIndex + 1);
gExePath[exeDelimiterIndex] = '\0';
#endif // _WIN32
}

759
src/platform/linux.c Normal file
View File

@ -0,0 +1,759 @@
/*****************************************************************************
* Copyright (c) 2014 Ted John
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
*
* This file is part of 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.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*****************************************************************************/
#ifdef __linux
#include <libgen.h>
#include <SDL_syswm.h>
#include <sys/stat.h>
#include <errno.h>
#include <pwd.h>
#include "../addresses.h"
#include "../cmdline.h"
#include "../openrct2.h"
#include "../localisation/language.h"
#include "../localisation/currency.h"
#include "../config.h"
#include "platform.h"
#include <dirent.h>
// The name of the mutex used to prevent multiple instances of the game from running
#define SINGLE_INSTANCE_MUTEX_NAME "RollerCoaster Tycoon 2_GSKMUTEX"
/**
* The function that is called directly from the host application (rct2.exe)'s WinMain. This will be removed when OpenRCT2 can
* be built as a stand alone application.
*/
int main(int argc, const char **argv)
{
//RCT2_GLOBAL(RCT2_ADDRESS_HINSTANCE, HINSTANCE) = hInstance;
//RCT2_GLOBAL(RCT2_ADDRESS_CMDLINE, LPSTR) = lpCmdLine;
STUB();
int run_game = cmdline_run(argv, argc);
if (run_game == 1)
{
openrct2_launch();
}
exit(gExitCode);
return gExitCode;
}
char platform_get_path_separator()
{
return '/';
}
bool platform_file_exists(const utf8 *path)
{
wchar_t *wPath = utf8_to_widechar(path);
int len = min(MAX_PATH, utf8_length(path));
char buffer[MAX_PATH];
wcstombs(buffer, wPath, len);
buffer[len] = '\0';
free(wPath);
int exists = access(buffer, F_OK) != -1;
log_warning("file '%s' exists = %i", buffer, exists);
return exists;
}
bool platform_directory_exists(const utf8 *path)
{
wchar_t *wPath = utf8_to_widechar(path);
int len = min(MAX_PATH, utf8_length(path));
char buffer[MAX_PATH];
wcstombs(buffer, wPath, len);
buffer[len] = '\0';
free(wPath);
struct stat dirinfo;
int result = stat(buffer, &dirinfo);
log_verbose("checking dir %s, result = %d, is_dir = %d", buffer, result, S_ISDIR(dirinfo.st_mode));
if ((result != 0) || !S_ISDIR(dirinfo.st_mode))
{
return 0;
}
return 1;
}
bool platform_original_game_data_exists(const utf8 *path)
{
wchar_t *wPath = utf8_to_widechar(path);
int len = min(MAX_PATH, utf8_length(path));
char buffer[MAX_PATH];
wcstombs(buffer, wPath, len);
buffer[len] = '\0';
free(wPath);
char checkPath[MAX_PATH];
sprintf(checkPath, "%s%c%s%c%s", buffer, platform_get_path_separator(), "data", platform_get_path_separator(), "g1.dat");
return platform_file_exists(checkPath);
}
mode_t getumask()
{
mode_t mask = umask(0);
umask(mask);
return 0777 & ~mask; // Keep in mind 0777 is octal
}
bool platform_ensure_directory_exists(const utf8 *path)
{
mode_t mask = getumask();
wchar_t *wPath = utf8_to_widechar(path);
int len = min(MAX_PATH, utf8_length(path));
char buffer[MAX_PATH];
wcstombs(buffer, wPath, len);
buffer[len - 1] = '\0';
free(wPath);
log_verbose("%s", buffer);
const int result = mkdir(buffer, mask);
if (result == 0 || (result == -1 && errno == EEXIST))
return true;
return false;
}
bool platform_directory_delete(const utf8 *path)
{
STUB();
return 1;
}
bool platform_lock_single_instance()
{
STUB();
return 1;
}
typedef struct {
char active;
char pattern[MAX_PATH];
struct dirent **fileListTemp;
char **paths;
int cnt;
int handle;
void* data;
} enumerate_file_info;
static enumerate_file_info _enumerateFileInfoList[8] = { 0 };
char *g_file_pattern;
static int winfilter(const struct dirent *d)
{
int entry_length = strnlen(d->d_name, MAX_PATH);
char *name_upper = malloc(entry_length + 1);
if (name_upper == NULL)
{
log_error("out of memory");
return 0;
}
for (int i = 0; i < entry_length; i++)
{
name_upper[i] = (char)toupper(d->d_name[i]);
}
name_upper[entry_length] = '\0';
bool match = strstr(name_upper, g_file_pattern) != NULL;
//log_warning("trying matching filename %s, result = %d", name_upper, match);
free(name_upper);
return match;
}
int platform_enumerate_files_begin(const utf8 *pattern)
{
int i;
enumerate_file_info *enumFileInfo;
wchar_t *wpattern = utf8_to_widechar(pattern);
int length = min(utf8_length(pattern), MAX_PATH);
char *npattern = malloc(length);
int converted;
converted = wcstombs(npattern, wpattern, length);
npattern[length - 1] = '\0';
if (converted == MAX_PATH) {
log_warning("truncated string %s", npattern);
}
log_warning("begin file search, pattern: %s", npattern);
char *file_name = strrchr(npattern, platform_get_path_separator());
char *dir_name;
if (file_name != NULL)
{
dir_name = strndup(npattern, file_name - npattern);
file_name = &file_name[1];
} else {
file_name = npattern;
dir_name = strdup(".");
}
char *smatch = strchr(file_name, '*');
if ((smatch != file_name) && (smatch != NULL))
{
log_error("Sorry, can only match '*' at start of filename.");
return -1;
} else {
// '*' found
if (smatch != NULL)
{
// some boundary checking needed
// skip the '*'
smatch = &smatch[1];
char *match2 = strchr(&smatch[1], '*');
if (match2 != NULL)
{
log_error("Sorry, can only match one '*' wildcard.");
return -1;
}
} else {
// '*' not found
smatch = file_name;
}
}
char *qmatch = strchr(file_name, '?');
if ((qmatch != &npattern[length - 1]) && (qmatch != NULL))
{
log_error("Sorry, can only match '?' at end of filename.");
return -1;
} else {
qmatch = &npattern[length];
}
int pattern_length = qmatch - smatch;
g_file_pattern = strndup(smatch, pattern_length);
for (int j = 0; j < pattern_length; j++)
{
g_file_pattern[j] = (char)toupper(g_file_pattern[j]);
}
log_warning("looking for file matching %s", g_file_pattern);
int cnt;
for (i = 0; i < countof(_enumerateFileInfoList); i++) {
enumFileInfo = &_enumerateFileInfoList[i];
if (!enumFileInfo->active) {
strncpy(enumFileInfo->pattern, npattern, length);
cnt = scandir(dir_name, &enumFileInfo->fileListTemp, winfilter, alphasort);
if (cnt < 0)
{
break;
}
log_warning("found %d files matching in dir '%s'", cnt, dir_name);
enumFileInfo->cnt = cnt;
enumFileInfo->paths = malloc(cnt * sizeof(char *));
char **paths = enumFileInfo->paths;
// 256 is size of dirent.d_name
const int buf_len = min(MAX_PATH, 256);
const int dir_name_len = strnlen(dir_name, MAX_PATH);
char separator[] = {platform_get_path_separator(), 0};
for (int idx = 0; idx < cnt; idx++)
{
struct dirent *d = enumFileInfo->fileListTemp[idx];
const int entry_len = strnlen(d->d_name, MAX_PATH);
// 1 for separator, 1 for trailing null
paths[idx] = malloc(sizeof(char) * min(MAX_PATH, entry_len + dir_name_len + 2));
paths[idx][0] = '\0';
log_verbose("dir_name: %s", dir_name);
strncat(paths[idx], dir_name, MAX_PATH);
strncat(paths[idx], separator, MAX_PATH);
strncat(paths[idx], d->d_name, MAX_PATH);
log_verbose("paths[%d] = %s", idx, paths[idx]);
}
enumFileInfo->handle = 0;
enumFileInfo->active = 1;
free(dir_name);
free(g_file_pattern);
g_file_pattern = NULL;
free(wpattern);
free(npattern);
return i;
}
}
free(dir_name);
free(g_file_pattern);
g_file_pattern = NULL;
free(wpattern);
free(npattern);
return -1;
}
bool platform_enumerate_files_next(int handle, file_info *outFileInfo)
{
bool result;
enumerate_file_info *enumFileInfo;
enumFileInfo = &_enumerateFileInfoList[handle];
log_verbose("handle = %d", handle);
if (enumFileInfo->handle < enumFileInfo->cnt) {
result = true;
} else {
result = false;
}
if (result) {
int entryIdx = enumFileInfo->handle++;
struct stat fileInfo;
log_verbose("trying handle %d", entryIdx);
char *fileName = enumFileInfo->paths[entryIdx];
int statRes;
statRes = stat(fileName, &fileInfo);
if (statRes == -1) {
log_error("failed to stat file '%s'! errno = %i", fileName, errno);
return 0;
}
outFileInfo->path = basename(fileName);
outFileInfo->size = fileInfo.st_size;
outFileInfo->last_modified = fileInfo.st_mtime;
return 1;
} else {
return 0;
}
}
void platform_enumerate_files_end(int handle)
{
int i;
enumerate_file_info *enumFileInfo;
enumFileInfo = &_enumerateFileInfoList[handle];
int cnt = enumFileInfo->cnt;
for (i = 0; i < cnt; i++) {
free(enumFileInfo->fileListTemp[i]);
free(enumFileInfo->paths[i]);
}
free(enumFileInfo->fileListTemp);
free(enumFileInfo->paths);
// FIXME: this here could have a bug
enumFileInfo->fileListTemp = NULL;
enumFileInfo->handle = 0;
enumFileInfo->active = 0;
}
static int dirfilter(const struct dirent *d)
{
#ifdef _DIRENT_HAVE_D_TYPE
if (d->d_type != DT_DIR)
{
return 1;
} else {
return 0;
}
#else
#error implement dirfilter!
#endif // _DIRENT_HAVE_D_TYPE
}
int platform_enumerate_directories_begin(const utf8 *directory)
{
int i;
enumerate_file_info *enumFileInfo;
wchar_t *wpattern = utf8_to_widechar(directory);
int length = min(utf8_length(directory), MAX_PATH);
char *npattern = malloc(length);
int converted;
converted = wcstombs(npattern, wpattern, length);
npattern[length - 1] = '\0';
if (converted == MAX_PATH) {
log_warning("truncated string %s", npattern);
}
log_warning("begin directory listing, path: %s", npattern);
// TODO: add some checking for stringness and directoryness
int cnt;
for (i = 0; i < countof(_enumerateFileInfoList); i++) {
enumFileInfo = &_enumerateFileInfoList[i];
if (!enumFileInfo->active) {
strncpy(enumFileInfo->pattern, npattern, length);
cnt = scandir(npattern, &enumFileInfo->fileListTemp, dirfilter, alphasort);
if (cnt < 0)
{
break;
}
log_warning("found %d files in dir '%s'", cnt, npattern);
enumFileInfo->cnt = cnt;
enumFileInfo->paths = malloc(cnt * sizeof(char *));
char **paths = enumFileInfo->paths;
// 256 is size of dirent.d_name
const int buf_len = min(MAX_PATH, 256);
const int dir_name_len = strnlen(npattern, MAX_PATH);
char separator[] = {platform_get_path_separator(), 0};
for (int idx = 0; idx < cnt; idx++)
{
struct dirent *d = enumFileInfo->fileListTemp[idx];
const int entry_len = strnlen(d->d_name, MAX_PATH);
// 1 for separator, 1 for trailing null
paths[idx] = malloc(sizeof(char) * min(MAX_PATH, entry_len + dir_name_len + 2));
paths[idx][0] = '\0';
log_verbose("dir_name: %s", npattern);
strncat(paths[idx], npattern, MAX_PATH);
strncat(paths[idx], separator, MAX_PATH);
strncat(paths[idx], d->d_name, MAX_PATH);
log_verbose("paths[%d] = %s", idx, paths[idx]);
}
enumFileInfo->handle = 0;
enumFileInfo->active = 1;
free(wpattern);
free(npattern);
return i;
}
}
free(wpattern);
free(npattern);
return -1;
}
bool platform_enumerate_directories_next(int handle, utf8 *path)
{
bool result;
enumerate_file_info *enumFileInfo;
enumFileInfo = &_enumerateFileInfoList[handle];
log_verbose("handle = %d", handle);
if (enumFileInfo->handle < enumFileInfo->cnt) {
result = true;
} else {
result = false;
}
if (result) {
int entryIdx = enumFileInfo->handle++;
struct stat fileInfo;
log_verbose("trying handle %d", entryIdx);
char *fileName = enumFileInfo->paths[entryIdx];
int statRes;
statRes = stat(fileName, &fileInfo);
if (statRes == -1) {
log_error("failed to stat file '%s'! errno = %i", fileName, errno);
return 0;
}
// so very, very wrong…
strncpy(path, basename(fileName), MAX_PATH);
strncat(path, "/", MAX_PATH);
path[MAX_PATH - 1] = '\0';
return 1;
} else {
return 0;
}
}
void platform_enumerate_directories_end(int handle)
{
int i;
enumerate_file_info *enumFileInfo;
enumFileInfo = &_enumerateFileInfoList[handle];
int cnt = enumFileInfo->cnt;
for (i = 0; i < cnt; i++) {
free(enumFileInfo->fileListTemp[i]);
free(enumFileInfo->paths[i]);
}
free(enumFileInfo->fileListTemp);
free(enumFileInfo->paths);
// FIXME: this here could have a bug
enumFileInfo->fileListTemp = NULL;
enumFileInfo->handle = 0;
enumFileInfo->active = 0;
}
int platform_get_drives(){
/*
return GetLogicalDrives();
*/
STUB();
return 0xff;
}
bool platform_file_copy(const utf8 *srcPath, const utf8 *dstPath, bool overwrite)
{
STUB();
return 0;
}
bool platform_file_move(const utf8 *srcPath, const utf8 *dstPath)
{
STUB();
return 0;
}
bool platform_file_delete(const utf8 *path)
{
STUB();
return 0;
}
void platform_hide_cursor()
{
STUB();
}
void platform_show_cursor()
{
STUB();
}
void platform_get_cursor_position(int *x, int *y)
{
STUB();
}
void platform_set_cursor_position(int x, int y)
{
STUB();
}
unsigned int platform_get_ticks()
{
STUB();
return 100;
}
wchar_t *regular_to_wchar(const char* src)
{
int len = strnlen(src, MAX_PATH);
wchar_t *w_buffer = malloc((len + 1) * sizeof(wchar_t));
mbtowc (NULL, NULL, 0); /* reset mbtowc */
int max = len;
int i = 0;
while (max > 0)
{
int length;
length = mbtowc(&w_buffer[i], &src[i], max);
if (length < 1)
{
w_buffer[i + 1] = '\0';
break;
}
i += length;
max -= length;
}
return w_buffer;
}
void platform_get_user_directory(utf8 *outPath, const utf8 *subDirectory)
{
char buffer[MAX_PATH];
buffer[0] = '\0';
log_verbose("buffer = '%s'", buffer);
const char *homedir = getenv("XDG_CONFIG_HOME");
log_verbose("homedir = '%s'", homedir);
if (homedir == NULL)
{
homedir = getpwuid(getuid())->pw_dir;
log_verbose("homedir was null, used getuid, now is = '%s'", homedir);
if (homedir == NULL)
{
log_error("Couldn't find user home directory");
return;
}
}
char separator[2] = { platform_get_path_separator(), 0 };
strncat(buffer, homedir, MAX_PATH);
strncat(buffer, separator, MAX_PATH);
strncat(buffer, "OpenRCT2", MAX_PATH);
strncat(buffer, separator, MAX_PATH);
log_verbose("outPath + OpenRCT2 = '%s'", buffer);
if (subDirectory != NULL && subDirectory[0] != 0) {
log_verbose("adding subDirectory '%s'", subDirectory);
strcat(buffer, subDirectory);
strcat(buffer, separator);
}
int len = strnlen(buffer, MAX_PATH);
wchar_t *w_buffer = regular_to_wchar(buffer);
w_buffer[len] = '\0';
utf8 *path = widechar_to_utf8(w_buffer);
free(w_buffer);
strcpy(outPath, path);
free(path);
log_verbose("outPath + subDirectory = '%s'", buffer);
}
void platform_show_messagebox(char *message)
{
STUB();
log_warning(message);
}
/**
*
* rct2: 0x004080EA
*/
int platform_open_common_file_dialog(int type, utf8 *title, utf8 *filename, utf8 *filterPattern, utf8 *filterName)
{
STUB();
return 0;
}
utf8 *platform_open_directory_browser(utf8 *title)
{
STUB();
return NULL;
}
uint16 platform_get_locale_language(){
/*
CHAR langCode[4];
if (GetLocaleInfo(LOCALE_USER_DEFAULT,
LOCALE_SABBREVLANGNAME,
(LPSTR)&langCode,
sizeof(langCode)) == 0){
return LANGUAGE_UNDEFINED;
}
if (strcmp(langCode, "ENG") == 0){
return LANGUAGE_ENGLISH_UK;
}
else if (strcmp(langCode, "ENU") == 0){
return LANGUAGE_ENGLISH_US;
}
else if (strcmp(langCode, "DEU") == 0){
return LANGUAGE_GERMAN;
}
else if (strcmp(langCode, "NLD") == 0){
return LANGUAGE_DUTCH;
}
else if (strcmp(langCode, "FRA") == 0){
return LANGUAGE_FRENCH;
}
else if (strcmp(langCode, "HUN") == 0){
return LANGUAGE_HUNGARIAN;
}
else if (strcmp(langCode, "PLK") == 0){
return LANGUAGE_POLISH;
}
else if (strcmp(langCode, "ESP") == 0){
return LANGUAGE_SPANISH;
}
else if (strcmp(langCode, "SVE") == 0){
return LANGUAGE_SWEDISH;
}
else if (strcmp(langCode, "ITA") == 0){
return LANGUAGE_ITALIAN;
}
else if (strcmp(langCode, "POR") == 0){
return LANGUAGE_PORTUGUESE_BR;
}
*/
STUB();
return LANGUAGE_ENGLISH_UK;
}
time_t platform_file_get_modified_time(const utf8* path){
/*
WIN32_FILE_ATTRIBUTE_DATA data;
if (!GetFileAttributesEx(path, GetFileExInfoStandard, &data))
return 0;
ULARGE_INTEGER ull;
ull.LowPart = data.ftLastWriteTime.dwLowDateTime;
ull.HighPart = data.ftLastWriteTime.dwHighDateTime;
return ull.QuadPart / 10000000ULL - 11644473600ULL;
*/
STUB();
return 100;
}
uint8 platform_get_locale_currency(){
/*
CHAR currCode[4];
if (GetLocaleInfo(LOCALE_USER_DEFAULT,
LOCALE_SINTLSYMBOL,
(LPSTR)&currCode,
sizeof(currCode)) == 0){
return CURRENCY_POUNDS;
}
if (strcmp(currCode, "GBP") == 0){
return CURRENCY_POUNDS;
}
else if (strcmp(currCode, "USD") == 0){
return CURRENCY_DOLLARS;
}
else if (strcmp(currCode, "EUR") == 0){
return CURRENCY_EUROS;
}
else if (strcmp(currCode, "SEK") == 0){
return CURRENCY_KRONA;
}
else if (strcmp(currCode, "DEM") == 0){
return CURRENCY_DEUTSCHMARK;
}
else if (strcmp(currCode, "ITL") == 0){
return CURRENCY_LIRA;
}
else if (strcmp(currCode, "JPY") == 0){
return CURRENCY_YEN;
}
else if (strcmp(currCode, "ESP") == 0){
return CURRENCY_PESETA;
}
else if (strcmp(currCode, "FRF") == 0){
return CURRENCY_FRANC;
}
else if (strcmp(currCode, "NLG") == 0){
return CURRENCY_GUILDERS;
}
*/
STUB();
return CURRENCY_POUNDS;
}
uint8 platform_get_locale_measurement_format(){
/*
UINT measurement_system;
if (GetLocaleInfo(LOCALE_USER_DEFAULT,
LOCALE_IMEASURE | LOCALE_RETURN_NUMBER,
(LPSTR)&measurement_system,
sizeof(measurement_system)) == 0){
return MEASUREMENT_FORMAT_IMPERIAL;
}
switch (measurement_system){
case 0:
return MEASUREMENT_FORMAT_METRIC;
case 1:
default:
return MEASUREMENT_FORMAT_IMPERIAL;
}*/
STUB();
return MEASUREMENT_FORMAT_METRIC;
}
uint8 platform_get_locale_temperature_format(){
/*
// There does not seem to be a function to obtain this, just check the countries
UINT country;
if (GetLocaleInfo(LOCALE_USER_DEFAULT,
LOCALE_IMEASURE | LOCALE_RETURN_NUMBER,
(LPSTR)&country,
sizeof(country)) == 0){
return TEMPERATURE_FORMAT_C;
}
switch (country){
case CTRY_UNITED_STATES:
case CTRY_BELIZE:
return TEMPERATURE_FORMAT_F;
default:
return TEMPERATURE_FORMAT_C;
}
*/
STUB();
return TEMPERATURE_FORMAT_C;
}
#endif // __linux

View File

@ -147,6 +147,7 @@ uint8 platform_get_locale_temperature_format();
#define _strcmpi _stricmp
#define _stricmp(x, y) strcasecmp((x), (y))
#define _strnicmp(x, y, n) strncasecmp((x), (y), (n))
#define _strdup(x) strdup((x))
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
#define RCT2_ENDIANESS __ORDER_LITTLE_ENDIAN__

View File

@ -1,9 +1,9 @@
/*****************************************************************************
* Copyright (c) 2014 Ted John
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
*
*
* This file is part of 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
@ -31,14 +31,16 @@
// {
// if (cmdline_run(argv, argc))
// openrct2_launch();
//
//
// return gExitCode;
// }
/*
char platform_get_path_separator()
{
return '/';
}
*/
#endif
#endif
#endif

View File

@ -1,9 +1,9 @@
/*****************************************************************************
* Copyright (c) 2014 Ted John
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
*
*
* This file is part of 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
@ -17,7 +17,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*****************************************************************************/
#ifndef _RCT2_H_
#define _RCT2_H_
@ -206,7 +206,11 @@ enum {
// rct2 @ 0x0097F67C
static const char * const file_paths[] =
{
"Data\\G1.DAT",
#ifdef _WIN32
"data\\g1.dat",
#else
"data/g1.dat",
#endif // _WIN32
"Data\\PLUGIN.DAT",
"Data\\CSS1.DAT",
"Data\\CSS2.DAT",