From 882c3fa9ae250b1d87a6cee53d7bb9c248abbaae Mon Sep 17 00:00:00 2001 From: IntelOrca Date: Sun, 6 Apr 2014 17:45:09 +0100 Subject: [PATCH] add scenario list loading --- projects/openrct2.vcxproj | 3 + projects/openrct2.vcxproj.filters | 9 + src/addresses.h | 56 ++++-- src/news_item.c | 2 +- src/rct2.c | 29 ++- src/rct2.h | 2 + src/scenario.c | 287 ++++++++++++++++++++++++++++++ src/scenario.h | 47 +++++ src/strings.c | 34 ++++ src/strings.h | 2 +- 10 files changed, 450 insertions(+), 21 deletions(-) create mode 100644 src/scenario.c create mode 100644 src/scenario.h create mode 100644 src/strings.c diff --git a/projects/openrct2.vcxproj b/projects/openrct2.vcxproj index a436c6a336..7472f7d0d7 100644 --- a/projects/openrct2.vcxproj +++ b/projects/openrct2.vcxproj @@ -24,6 +24,7 @@ + @@ -42,6 +43,8 @@ + + diff --git a/projects/openrct2.vcxproj.filters b/projects/openrct2.vcxproj.filters index d76e652147..4209140061 100644 --- a/projects/openrct2.vcxproj.filters +++ b/projects/openrct2.vcxproj.filters @@ -69,6 +69,9 @@ Header Files + + Header Files + @@ -122,6 +125,12 @@ Windows + + Source Files + + + Source Files + diff --git a/src/addresses.h b/src/addresses.h index f5b46b9d41..81b30ebdea 100644 --- a/src/addresses.h +++ b/src/addresses.h @@ -23,16 +23,16 @@ #pragma warning(disable : 4731) -#define RCT2_ADDRESS(address, type) ((type*)address) -#define RCT2_GLOBAL(address, type) (*((type*)address)) -#define RCT2_CALLPROC(address) (((void(*)())address)()) -#define RCT2_CALLFUNC(address, returnType) (((returnType(*)())address)()) +#define RCT2_ADDRESS(address, type) ((type*)(address)) +#define RCT2_GLOBAL(address, type) (*((type*)(address))) +#define RCT2_CALLPROC(address) (((void(*)())(address))()) +#define RCT2_CALLFUNC(address, returnType) ((((returnType)(*)())(address))()) -#define RCT2_CALLFUNC_1(address, returnType, a1, v1) (((returnType(*)(a1))address)(v1)) -#define RCT2_CALLFUNC_2(address, returnType, a1, a2, v1, v2) (((returnType(*)(a1, a2))address)(v1, v2)) -#define RCT2_CALLFUNC_3(address, returnType, a1, a2, a3, v1, v2, v3) (((returnType(*)(a1, a2, a3))address)(v1, v2, v3)) -#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_1(address, returnType, a1, v1) (((returnType(*)(a1))(address))(v1)) +#define RCT2_CALLFUNC_2(address, returnType, a1, a2, v1, v2) (((returnType(*)(a1, a2))(address))(v1, v2)) +#define RCT2_CALLFUNC_3(address, returnType, a1, a2, a3, v1, v2, v3) (((returnType(*)(a1, a2, a3))(address))(v1, v2, v3)) +#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_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) @@ -40,7 +40,8 @@ #define RCT2_CALLPROC_4(address, a1, a2, a3, a4, v1, v2, v3, v4) RCT2_CALLFUNC_4(address, void, a1, a2, a3, a4, v1, v2, v3, v4) #define RCT2_CALLPROC_5(address, a1, a2, a3, a4, a5, v1, v2, v3, v4, v5) RCT2_CALLFUNC_4(address, void, a1, a2, a3, a4, a5, v1, v2, v3, v4, v5) -#define RCT2_ADDRESS_CMDLINE 0x009E2D98 +#define RCT2_ADDRESS_SCENARIO_LIST 0x009A9FF4 +#define RCT2_ADDRESS_NUM_SCENARIOS 0x009AA008 #define RCT2_ADDRESS_APP_PATH 0x009AA214 #define RCT2_ADDRESS_APP_PATH_SLASH 0x009AB4D9 @@ -69,6 +70,8 @@ #define RCT2_ADDRESS_PLACE_OBJECT_MODIFIER 0x009DEA70 #define RCT2_ADDRESS_ON_TUTORIAL 0x009DEA71 +#define RCT2_ADDRESS_CMDLINE 0x009E2D98 + #define RCT2_ADDRESS_G1_ELEMENTS 0x009EBD28 #define RCT2_ADDRESS_SPRITE_LIST 0x010E63BC @@ -80,6 +83,9 @@ #define RCT2_ADDRESS_NEWS_ITEM_LIST 0x013CA754 +#define RCT2_ADDRESS_SCENARIO_NAME 0x0141F5B8 +#define RCT2_ADDRESS_SCENARIO_DETAILS 0x0141F5F8 + #define RCT2_ADDRESS_WINDOW_LIST 0x01420078 #define RCT2_ADDRESS_NEW_WINDOW_PTR 0x014234B8 #define RCT2_ADDRESS_VIEWPORT_LIST 0x014234BC @@ -115,10 +121,13 @@ static void RCT2_CALLPROC_X(int address, int _eax, int _ebx, int _ecx, int _edx, static void RCT2_CALLFUNC_X(int address, int *_eax, int *_ebx, int *_ecx, int *_edx, int *_esi, int *_edi, int *_ebp) { __asm { + // Store C's base pointer push ebp + + // Store address to call push address - // Set register to variable address, then set register to variable value + // Set all registers to the input values mov eax, [_eax] mov eax, [eax] mov ebx, [_ebx] @@ -134,15 +143,28 @@ static void RCT2_CALLFUNC_X(int address, int *_eax, int *_ebx, int *_ecx, int *_ mov ebp, [_ebp] mov ebp, [ebp] - // Call func address - call[esp] + // Call function + call [esp] add esp, 4 - pop ebp - // mov[_ebp], ebp + // Store output eax + push eax + + // Put original C base pointer into eax + mov eax, [esp+4] + + // Store output ebp + push ebp + + // Set ebp to the original C base pointer + mov ebp, eax + + // Put output ebp into ebp parameter + mov eax, [esp] + mov [_ebp], eax + add esp, 4 // Get resulting ebx, ecx, edx, esi, edi registers - push eax mov eax, [_edi] mov [eax], edi mov eax, [_esi] @@ -158,6 +180,8 @@ static void RCT2_CALLFUNC_X(int address, int *_eax, int *_ebx, int *_ecx, int *_ // Get resulting eax register mov ebx, [_eax] mov [ebx], eax + + add esp, 4 } } diff --git a/src/news_item.c b/src/news_item.c index e51b5ab0a8..ee9efdf9d3 100644 --- a/src/news_item.c +++ b/src/news_item.c @@ -50,7 +50,7 @@ void news_item_update_current() short ax, bx, remove_time; rct_news_item *newsItems = RCT2_ADDRESS(RCT2_ADDRESS_NEWS_ITEM_LIST, rct_news_item); - // get_system_time(); + RCT2_CALLPROC_EBPSAFE(0x00407671); // get_system_time() ax = RCT2_GLOBAL(RCT2_ADDRESS_OS_TIME_DAY, sint16); bx = RCT2_GLOBAL(RCT2_ADDRESS_OS_TIME_MONTH, sint16); diff --git a/src/rct2.c b/src/rct2.c index 2dcfe952a9..55fd78414a 100644 --- a/src/rct2.c +++ b/src/rct2.c @@ -30,6 +30,7 @@ #include "intro.h" #include "osinterface.h" #include "rct2.h" +#include "scenario.h" #include "title.h" void rct2_init_directories(); @@ -98,7 +99,7 @@ void rct2_init() RCT2_CALLPROC_EBPSAFE(0x006752D5); // config_load() // RCT2_CALLPROC_EBPSAFE(0x00674B81); // pointless expansion pack crap RCT2_CALLPROC_EBPSAFE(0x006A8B40); // object_load_list() - RCT2_CALLPROC_EBPSAFE(0x006775A8); // scenario_load_list() + scenario_load_list(); RCT2_CALLPROC_X(0x006CED50, 0, 0, 0, 253, 0, 0, 0); // track_load_list(253) gfx_load_g1(); RCT2_CALLPROC_EBPSAFE(0x006C19AC); @@ -167,9 +168,11 @@ void rct2_startup_checks() void rct2_update() { + // Set 0x009DE564 to the value of esp + // RCT2 sets the stack pointer to the value of this address when ending the current game tick from anywhere __asm { mov eax, 009DE564h - mov[eax], esp + mov [eax], esp } if (!setjmp(_end_update_jump)) @@ -221,10 +224,30 @@ char *get_file_path(int pathId) } /** - * + * RCT2 and this DLL can not free each other's allocated memory blocks. Use this to allocate memory if RCT2 is still able to + * free it. * rct2: 0x004068B2 */ void *rct2_malloc(size_t numBytes) { return RCT2_CALLFUNC_1(0x004068B2, void*, size_t, numBytes); +} + +/** + * RCT2 and this DLL can not free each other's allocated memory blocks. Use this to reallocate memory if RCT2 is still able to + * free it. + * rct2: 0x004068BD + */ +void *rct2_realloc(void *block, size_t numBytes) +{ + return RCT2_CALLFUNC_2(0x004068BD, void*, void*, size_t, block, numBytes); +} + +/** + * RCT2 and this DLL can not free each other's allocated memory blocks. Use this to free memory that was allocated by RCT2. + * rct2: 0x004068DE + */ +void rct2_free(void *block) +{ + return RCT2_CALLPROC_1(0x004068DE, void*, block); } \ No newline at end of file diff --git a/src/rct2.h b/src/rct2.h index 880d2c4906..7d4f623984 100644 --- a/src/rct2.h +++ b/src/rct2.h @@ -53,5 +53,7 @@ enum { char *get_file_path(int pathId); void *rct2_malloc(size_t numBytes); +void *rct2_realloc(void *block, size_t numBytes); +void rct2_free(void *block); #endif \ No newline at end of file diff --git a/src/scenario.c b/src/scenario.c new file mode 100644 index 0000000000..ba7edb435a --- /dev/null +++ b/src/scenario.c @@ -0,0 +1,287 @@ +/***************************************************************************** + * 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 . + *****************************************************************************/ + +#define _CRT_SECURE_NO_WARNINGS + +#include +#include "addresses.h" +#include "rct2.h" +#include "scenario.h" +#include "strings.h" + +#define UNINITIALISED_SCENARIO_LIST ((rct_scenario_basic*)-1) + +#define RCT2_SCENARIO_LIST RCT2_GLOBAL(RCT2_ADDRESS_SCENARIO_LIST, rct_scenario_basic*) +#define RCT2_NUM_SCENARIOS RCT2_GLOBAL(RCT2_ADDRESS_NUM_SCENARIOS, sint32) +int _scenarioListSize; + +static void scenario_list_sort(); +static int scenario_list_sort_compare(const void* a, const void* b); +static void scenario_scores_load(); +static void scenario_scores_save(); +static int scenario_load_basic(char *path); + +static void subsitute_path(char *dest, char *path, char *filename) +{ + while (*path != '*') { + *dest++ = *path++; + } + strcpy(dest, filename); +} + +static rct_scenario_basic *get_scenario_by_filename(char *filename) +{ + int i; + for (i = 0; i < RCT2_NUM_SCENARIOS; i++) + if (strcmp(RCT2_SCENARIO_LIST[i].path, filename) == 0) + return &(RCT2_SCENARIO_LIST[i]); + + return NULL; +} + +/** + * + * rct2: 0x006775A8 + */ +void scenario_load_list() +{ + HANDLE hFindFile; + WIN32_FIND_DATAA findFileData; + int i; + char *src, *dst; + + // Load scores + scenario_scores_load(); + + // Unset flag 1 for each scenario + for (i = 0; i < RCT2_NUM_SCENARIOS; i++) + RCT2_SCENARIO_LIST[i].var_0268 &= ~0x01; + + // Enumerate through each scenario in the directory + hFindFile = FindFirstFile(RCT2_ADDRESS(RCT2_ADDRESS_SCENARIOS_PATH, char), &findFileData); + if (hFindFile != INVALID_HANDLE_VALUE) { + do { + // Check if scenario already exists in list, likely if in scores + rct_scenario_basic *scenario = get_scenario_by_filename(findFileData.cFileName); + if (scenario != NULL) { + // Set 0141EF68 to the scenario path + subsitute_path( + RCT2_ADDRESS(0x0141EF68, char), + RCT2_ADDRESS(RCT2_ADDRESS_SCENARIOS_PATH, char), + findFileData.cFileName + ); + + // Load the basic scenario information + if (!scenario_load_basic(RCT2_ADDRESS(0x0141EF68, char))) + continue; + + // + if (RCT2_GLOBAL(0x0141F570, sint8) != -1) + continue; + + // Update the scenario information + scenario->var_0268 |= 0x01; + scenario->category = RCT2_GLOBAL(0x0141F571, uint8); + scenario->var_0120 = RCT2_GLOBAL(0x0141F572, sint8); + scenario->var_0121 = RCT2_GLOBAL(0x0141F573, sint8); + scenario->var_0122 = RCT2_GLOBAL(0x0141F574, sint32); + scenario->var_0126 = RCT2_GLOBAL(0x0141F578, sint16); + strcpy(scenario->name, RCT2_ADDRESS(RCT2_ADDRESS_SCENARIO_NAME, char)); + strcpy(scenario->details, RCT2_ADDRESS(RCT2_ADDRESS_SCENARIO_DETAILS, char)); + continue; + } + + // Check if the scenario list buffer has room for another scenario + if ((RCT2_NUM_SCENARIOS + 1) * sizeof(rct_scenario_basic) > _scenarioListSize) { + // Allocate more room + _scenarioListSize += 16 * sizeof(rct_scenario_basic); + RCT2_SCENARIO_LIST = (rct_scenario_basic*)rct2_realloc(RCT2_SCENARIO_LIST, _scenarioListSize); + } + + // Set 0141EF68 to the scenario path + subsitute_path( + RCT2_ADDRESS(0x0141EF68, char), + RCT2_ADDRESS(RCT2_ADDRESS_SCENARIOS_PATH, char), + findFileData.cFileName + ); + + // Load the scenario information + if (!scenario_load_basic(RCT2_ADDRESS(0x0141EF68, char))) + continue; + + // + if (RCT2_GLOBAL(0x0141F570, sint8) != -1) + continue; + + // Increment the number of scenarios + i = RCT2_NUM_SCENARIOS; + RCT2_NUM_SCENARIOS++; + + // Add this new scenario to the list + strcpy(RCT2_SCENARIO_LIST[i].path, findFileData.cFileName); + RCT2_SCENARIO_LIST[i].var_0268 = 0x01; + if (RCT2_GLOBAL(0x009AA00C, uint8) & 1) + RCT2_SCENARIO_LIST[i].var_0268 |= 0x04; + RCT2_SCENARIO_LIST[i].category = RCT2_GLOBAL(0x0141F571, uint8); + RCT2_SCENARIO_LIST[i].var_0120 = RCT2_GLOBAL(0x0141F572, sint8); + RCT2_SCENARIO_LIST[i].var_0121 = RCT2_GLOBAL(0x0141F573, sint8); + RCT2_SCENARIO_LIST[i].var_0122 = RCT2_GLOBAL(0x0141F574, sint32); + RCT2_SCENARIO_LIST[i].var_0126 = RCT2_GLOBAL(0x0141F578, sint16); + strcpy(RCT2_SCENARIO_LIST[i].name, RCT2_ADDRESS(RCT2_ADDRESS_SCENARIO_NAME, char)); + strcpy(RCT2_SCENARIO_LIST[i].details, RCT2_ADDRESS(RCT2_ADDRESS_SCENARIO_DETAILS, char)); + } while (FindNextFile(hFindFile, &findFileData)); + FindClose(hFindFile); + } + + scenario_list_sort(); + + // Save the scores + scenario_scores_save(); +} + +/** +* Sort the list of scenarios. This used to be an insertion sort which took +* place as each scenario loaded. It has now been changed to a quicksort which +* takes place after all the scenarios have been loaded in. +* rct2: 0x00677C3B +*/ +static void scenario_list_sort() +{ + qsort( + RCT2_GLOBAL(RCT2_ADDRESS_SCENARIO_LIST, rct_scenario_basic*), + RCT2_NUM_SCENARIOS, + sizeof(rct_scenario_basic), + scenario_list_sort_compare + ); +} + +/** +* Basic scenario information compare function for sorting. +* rct2: 0x00677C08 +*/ +static int scenario_list_sort_compare(const void* a, const void* b) +{ + return strcmp(((rct_scenario_basic*)a)->name, ((rct_scenario_basic*)b)->name); +} + +/** + * + * rct2: 0x006775A8 + */ +static void scenario_scores_load() +{ + HANDLE hFile; + DWORD bytes_read; + + // Free scenario list if already allocated + if (RCT2_SCENARIO_LIST != UNINITIALISED_SCENARIO_LIST) { + rct2_free(RCT2_SCENARIO_LIST); + RCT2_SCENARIO_LIST = UNINITIALISED_SCENARIO_LIST; + } + + // Try and load the scores + hFile = CreateFile(get_file_path(32), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, + FILE_FLAG_RANDOM_ACCESS | FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile != INVALID_HANDLE_VALUE) { + ReadFile(hFile, (void*)0x009A9FFC, 16, &bytes_read, NULL); + if (bytes_read == 16) { + _scenarioListSize = RCT2_NUM_SCENARIOS * sizeof(rct_scenario_basic); + RCT2_SCENARIO_LIST = (rct_scenario_basic*)rct2_malloc(_scenarioListSize); + ReadFile(hFile, RCT2_SCENARIO_LIST, _scenarioListSize, &bytes_read, NULL); + CloseHandle(hFile); + if (bytes_read == _scenarioListSize) + return; + } else { + CloseHandle(hFile); + } + } + + // Unable to load scores, allocate some space for a reload + RCT2_NUM_SCENARIOS = 0; + _scenarioListSize = 0x4000; + RCT2_SCENARIO_LIST = (rct_scenario_basic*)rct2_malloc(_scenarioListSize); +} + +/** + * + * rct2: 0x00677B50 + */ +static void scenario_scores_save() +{ + HANDLE hFile; + DWORD bytes_written; + + hFile = CreateFile(get_file_path(32), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile != INVALID_HANDLE_VALUE) { + WriteFile(hFile, (void*)0x009A9FFC, 16, &bytes_written, NULL); + if (RCT2_NUM_SCENARIOS > 0) + WriteFile(hFile, RCT2_SCENARIO_LIST, RCT2_NUM_SCENARIOS * sizeof(rct_scenario_basic), &bytes_written, NULL); + CloseHandle(hFile); + } +} + +/** + * Loads only the basic information from a scenario. + * rct2: 0x006761D6 + */ +static int scenario_load_basic(char *path) +{ + HANDLE hFile; + int _eax; + + hFile = CreateFile(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, + FILE_FLAG_RANDOM_ACCESS | FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile != INVALID_HANDLE_VALUE) { + RCT2_GLOBAL(0x009E382C, HANDLE*) = hFile; + RCT2_CALLPROC_X(0x0067685F, 0, 0, 0, 0, 0x009E34E4, 0, 0); + if (RCT2_GLOBAL(0x009E34E4, uint8) == 1) { + RCT2_CALLPROC_X(0x0067685F, 0, 0, 0, 0, 0x0141F570, 0, 0); + CloseHandle(hFile); + RCT2_GLOBAL(0x009AA00C, uint8) = 0; + if (RCT2_GLOBAL(0x0141F6F8, uint8) != 255) { + __asm { + push ebp + mov ebp, 0141F6F8h + mov eax, 006A9428h + call eax + pop ebp + mov _eax, eax + jb loc_67628F + } + + int ebp = RCT2_GLOBAL(0x009ADAF8, uint32); + format_string(RCT2_ADDRESS(RCT2_ADDRESS_SCENARIO_NAME, char), RCT2_GLOBAL(ebp, sint16), NULL); + format_string(RCT2_ADDRESS(RCT2_ADDRESS_SCENARIO_DETAILS, char), RCT2_GLOBAL(ebp + 4, sint16), NULL); + RCT2_GLOBAL(0x009AA00C, uint8) = RCT2_GLOBAL(ebp + 6, uint8); + RCT2_CALLPROC(0x006A982D); + __asm mov _eax, eax + loc_67628F : + return _eax; + } + } else { + CloseHandle(hFile); + } + } + + RCT2_GLOBAL(0x009AC31B, sint8) = -1; + RCT2_GLOBAL(0x009AC31C, sint16) = 3011; + return 0; +} \ No newline at end of file diff --git a/src/scenario.h b/src/scenario.h new file mode 100644 index 0000000000..185dab89f0 --- /dev/null +++ b/src/scenario.h @@ -0,0 +1,47 @@ +/***************************************************************************** + * 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 . + *****************************************************************************/ + +#ifndef _SCENARIO_H_ +#define _SCENARIO_H_ + +#include "rct2.h" + +/** + * Scenario basic structure, mainly for scenario select + * size: 0x02B0 + */ +typedef struct { + char path[256]; // 0x0000 + uint8 category; // 0x0100 + uint8 pad_0101[0x1F]; + sint8 var_0120; + sint8 var_0121; + sint32 var_0122; + sint16 var_0126; + char name[64]; // 0x0128 + char details[256]; // 0x0168 + sint32 var_0268; + uint32 pad_026C; + sint8 var_0270[64]; +} rct_scenario_basic; + +void scenario_load_list(); + +#endif \ No newline at end of file diff --git a/src/strings.c b/src/strings.c new file mode 100644 index 0000000000..8cbef39840 --- /dev/null +++ b/src/strings.c @@ -0,0 +1,34 @@ +/***************************************************************************** + * 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 . + *****************************************************************************/ + +#include "addresses.h" +#include "strings.h" + +/** + * Writes a formatted string to a buffer. + * rct2: 0x006C2555 + * dest (edi) + * format (ax) + * args (ecx) + */ +void format_string(char *dest, rct_string_id format, void *args) +{ + RCT2_CALLPROC_X(0x006C2555, format, 0, args, 0, 0, dest, 0); +} \ No newline at end of file diff --git a/src/strings.h b/src/strings.h index a76f5f3d1c..c66328bbad 100644 --- a/src/strings.h +++ b/src/strings.h @@ -23,7 +23,7 @@ typedef unsigned short rct_string_id; -void format_string(char* dest, rct_string_id format, void* args); +void format_string(char *dest, rct_string_id format, void *args); void generate_string_file(); enum {