2014-04-06 18:45:09 +02:00
|
|
|
/*****************************************************************************
|
|
|
|
* 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/>.
|
|
|
|
*****************************************************************************/
|
|
|
|
|
|
|
|
#define _CRT_SECURE_NO_WARNINGS
|
|
|
|
|
|
|
|
#include <windows.h>
|
|
|
|
#include "addresses.h"
|
|
|
|
#include "rct2.h"
|
2014-04-10 01:22:57 +02:00
|
|
|
#include "sawyercoding.h"
|
2014-04-06 18:45:09 +02:00
|
|
|
#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;
|
|
|
|
|
2014-04-10 04:23:12 +02:00
|
|
|
static void scenario_list_add(char *path);
|
2014-04-06 18:45:09 +02:00
|
|
|
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()
|
|
|
|
{
|
2014-04-10 04:23:12 +02:00
|
|
|
int i;
|
2014-04-06 18:45:09 +02:00
|
|
|
HANDLE hFindFile;
|
|
|
|
WIN32_FIND_DATAA findFileData;
|
|
|
|
|
|
|
|
// Load scores
|
|
|
|
scenario_scores_load();
|
|
|
|
|
2014-04-10 04:23:12 +02:00
|
|
|
// Set all scenarios to be invisible
|
2014-04-06 18:45:09 +02:00
|
|
|
for (i = 0; i < RCT2_NUM_SCENARIOS; i++)
|
2014-04-10 04:23:12 +02:00
|
|
|
RCT2_SCENARIO_LIST[i].flags &= ~SCENARIO_FLAGS_VISIBLE;
|
2014-04-06 18:45:09 +02:00
|
|
|
|
|
|
|
// Enumerate through each scenario in the directory
|
|
|
|
hFindFile = FindFirstFile(RCT2_ADDRESS(RCT2_ADDRESS_SCENARIOS_PATH, char), &findFileData);
|
|
|
|
if (hFindFile != INVALID_HANDLE_VALUE) {
|
|
|
|
do {
|
2014-04-10 04:23:12 +02:00
|
|
|
scenario_list_add(findFileData.cFileName);
|
2014-04-06 18:45:09 +02:00
|
|
|
} while (FindNextFile(hFindFile, &findFileData));
|
|
|
|
FindClose(hFindFile);
|
|
|
|
}
|
|
|
|
|
2014-04-10 04:23:12 +02:00
|
|
|
// Sort alphabetically
|
2014-04-06 18:45:09 +02:00
|
|
|
scenario_list_sort();
|
|
|
|
|
|
|
|
// Save the scores
|
|
|
|
scenario_scores_save();
|
|
|
|
}
|
|
|
|
|
2014-04-10 04:23:12 +02:00
|
|
|
static void scenario_list_add(char *path)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
rct_scenario_basic *scenario;
|
|
|
|
rct_s6_info *s6Info = 0x0141F570;
|
|
|
|
|
|
|
|
// Check if scenario already exists in list, likely if in scores
|
|
|
|
scenario = get_scenario_by_filename(path);
|
|
|
|
if (scenario != NULL) {
|
|
|
|
// Set 0141EF68 to the scenario path
|
|
|
|
subsitute_path(
|
|
|
|
RCT2_ADDRESS(0x0141EF68, char),
|
|
|
|
RCT2_ADDRESS(RCT2_ADDRESS_SCENARIOS_PATH, char),
|
|
|
|
path
|
|
|
|
);
|
|
|
|
|
|
|
|
// Load the basic scenario information
|
|
|
|
if (!scenario_load_basic(RCT2_ADDRESS(0x0141EF68, char)))
|
|
|
|
return;
|
|
|
|
|
|
|
|
//
|
|
|
|
if (s6Info->var_000 != 255)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Update the scenario information
|
|
|
|
scenario->flags |= SCENARIO_FLAGS_VISIBLE;
|
|
|
|
scenario->category = s6Info->category;
|
|
|
|
scenario->objective_type = s6Info->objective_type;
|
|
|
|
scenario->objective_arg_1 = s6Info->objective_arg_1;
|
|
|
|
scenario->objective_arg_2 = s6Info->objective_arg_2;
|
|
|
|
scenario->objective_arg_3 = s6Info->objective_arg_3;
|
|
|
|
strcpy(scenario->name, s6Info->name);
|
|
|
|
strcpy(scenario->details, s6Info->details);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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),
|
|
|
|
path
|
|
|
|
);
|
|
|
|
|
|
|
|
// Load the scenario information
|
|
|
|
if (!scenario_load_basic(RCT2_ADDRESS(0x0141EF68, char)))
|
|
|
|
return;
|
|
|
|
|
|
|
|
//
|
|
|
|
if (s6Info->var_000 != 255)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Increment the number of scenarios
|
|
|
|
scenario = &RCT2_SCENARIO_LIST[RCT2_NUM_SCENARIOS];
|
|
|
|
RCT2_NUM_SCENARIOS++;
|
|
|
|
|
|
|
|
// Add this new scenario to the list
|
|
|
|
strcpy(scenario->path, path);
|
|
|
|
scenario->flags = SCENARIO_FLAGS_VISIBLE;
|
|
|
|
if (RCT2_GLOBAL(0x009AA00C, uint8) & 1)
|
|
|
|
scenario->flags |= SCENARIO_FLAGS_SIXFLAGS;
|
|
|
|
scenario->category = s6Info->category;
|
|
|
|
scenario->objective_type = s6Info->objective_type;
|
|
|
|
scenario->objective_arg_1 = s6Info->objective_arg_1;
|
|
|
|
scenario->objective_arg_2 = s6Info->objective_arg_2;
|
|
|
|
scenario->objective_arg_3 = s6Info->objective_arg_3;
|
|
|
|
strcpy(scenario->name, s6Info->name);
|
|
|
|
strcpy(scenario->details, s6Info->details);
|
|
|
|
}
|
|
|
|
|
2014-04-06 18:45:09 +02:00
|
|
|
/**
|
|
|
|
* 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
|
2014-04-08 01:05:05 +02:00
|
|
|
hFile = CreateFile(get_file_path(PATH_ID_SCORES), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
|
2014-04-06 18:45:09 +02:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-04-10 01:22:57 +02:00
|
|
|
void sub_67685F(HFILE hFile, uint32 address);
|
|
|
|
|
2014-04-06 18:45:09 +02:00
|
|
|
/**
|
|
|
|
* Loads only the basic information from a scenario.
|
|
|
|
* rct2: 0x006761D6
|
|
|
|
*/
|
|
|
|
static int scenario_load_basic(char *path)
|
|
|
|
{
|
|
|
|
HANDLE hFile;
|
|
|
|
int _eax;
|
2014-04-10 04:23:12 +02:00
|
|
|
rct_s6_header *s6Header = 0x009E34E4;
|
|
|
|
rct_s6_info *s6Info = 0x0141F570;
|
2014-04-06 18:45:09 +02:00
|
|
|
|
|
|
|
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;
|
2014-04-10 04:23:12 +02:00
|
|
|
|
|
|
|
// Read first chunk
|
|
|
|
sawyercoding_read_chunk(hFile, s6Header);
|
|
|
|
if (s6Header->type == S6_TYPE_SCENARIO) {
|
|
|
|
// Read second chunk
|
|
|
|
sawyercoding_read_chunk(hFile, s6Info);
|
2014-04-06 18:45:09 +02:00
|
|
|
CloseHandle(hFile);
|
|
|
|
RCT2_GLOBAL(0x009AA00C, uint8) = 0;
|
2014-04-10 04:23:12 +02:00
|
|
|
if (s6Info->flags != 255) {
|
2014-04-06 18:45:09 +02:00
|
|
|
__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);
|
2014-04-10 04:23:12 +02:00
|
|
|
format_string(s6Info->name, RCT2_GLOBAL(ebp, sint16), NULL);
|
|
|
|
format_string(s6Info->details, RCT2_GLOBAL(ebp + 4, sint16), NULL);
|
2014-04-06 18:45:09 +02:00
|
|
|
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;
|
2014-04-09 19:38:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
*
|
|
|
|
* rct2: 0x00678282
|
|
|
|
* scenario (ebx)
|
|
|
|
*/
|
|
|
|
void scenario_load(rct_scenario_basic *scenario)
|
|
|
|
{
|
|
|
|
RCT2_CALLPROC_X(0x00678282, 0, scenario, 0, 0, 0, 0, 0);
|
2014-04-06 18:45:09 +02:00
|
|
|
}
|