mirror of
https://github.com/OpenRCT2/OpenRCT2
synced 2025-12-24 00:03:11 +01:00
redo entire scenario highscore load / saving
- load and save highscores to new file format (highscores.dat) - scan scenarios from RCT2 data and user data - load highscores from legacy file (scores.dat) - fix various issues with new scenario select window
This commit is contained in:
@@ -264,6 +264,7 @@
|
|||||||
<ClInclude Include="src\ride\track_paint.h" />
|
<ClInclude Include="src\ride\track_paint.h" />
|
||||||
<ClInclude Include="src\ride\vehicle.h" />
|
<ClInclude Include="src\ride\vehicle.h" />
|
||||||
<ClInclude Include="src\scenario.h" />
|
<ClInclude Include="src\scenario.h" />
|
||||||
|
<ClInclude Include="src\scenario_sources.h" />
|
||||||
<ClInclude Include="src\sprites.h" />
|
<ClInclude Include="src\sprites.h" />
|
||||||
<ClInclude Include="src\version.h" />
|
<ClInclude Include="src\version.h" />
|
||||||
<ClInclude Include="test\management\finance_test.h" />
|
<ClInclude Include="test\management\finance_test.h" />
|
||||||
|
|||||||
@@ -845,5 +845,6 @@
|
|||||||
<ClInclude Include="src\image_io.h">
|
<ClInclude Include="src\image_io.h">
|
||||||
<Filter>Source</Filter>
|
<Filter>Source</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="src\scenario_sources.h" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
@@ -203,7 +203,7 @@ config_property_definition _generalDefinitions[] = {
|
|||||||
{ offsetof(general_configuration, show_fps), "show_fps", CONFIG_VALUE_TYPE_BOOLEAN, false, NULL },
|
{ offsetof(general_configuration, show_fps), "show_fps", CONFIG_VALUE_TYPE_BOOLEAN, false, NULL },
|
||||||
{ offsetof(general_configuration, trap_cursor), "trap_cursor", CONFIG_VALUE_TYPE_BOOLEAN, false, NULL },
|
{ offsetof(general_configuration, trap_cursor), "trap_cursor", CONFIG_VALUE_TYPE_BOOLEAN, false, NULL },
|
||||||
{ offsetof(general_configuration, auto_open_shops), "auto_open_shops", CONFIG_VALUE_TYPE_BOOLEAN, false, NULL },
|
{ offsetof(general_configuration, auto_open_shops), "auto_open_shops", CONFIG_VALUE_TYPE_BOOLEAN, false, NULL },
|
||||||
{ offsetof(general_configuration, scenario_select_mode), "scenario_select_mode", CONFIG_VALUE_TYPE_UINT8, 1, NULL },
|
{ offsetof(general_configuration, scenario_select_mode), "scenario_select_mode", CONFIG_VALUE_TYPE_UINT8, SCENARIO_SELECT_MODE_ORIGIN, NULL },
|
||||||
{ offsetof(general_configuration, scenario_unlocking_enabled), "scenario_unlocking_enabled", CONFIG_VALUE_TYPE_BOOLEAN, false, NULL },
|
{ offsetof(general_configuration, scenario_unlocking_enabled), "scenario_unlocking_enabled", CONFIG_VALUE_TYPE_BOOLEAN, false, NULL },
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -128,6 +128,11 @@ enum {
|
|||||||
SORT_DATE_DESCENDING,
|
SORT_DATE_DESCENDING,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
SCENARIO_SELECT_MODE_DIFFICULTY,
|
||||||
|
SCENARIO_SELECT_MODE_ORIGIN,
|
||||||
|
};
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint8 play_intro;
|
uint8 play_intro;
|
||||||
uint8 confirmation_prompt;
|
uint8 confirmation_prompt;
|
||||||
|
|||||||
@@ -219,19 +219,6 @@ int scenario_load(const char *path)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* rct2: 0x00678282
|
|
||||||
* scenario (ebx)
|
|
||||||
*/
|
|
||||||
int scenario_load_and_play(const rct_scenario_basic *scenario)
|
|
||||||
{
|
|
||||||
char path[MAX_PATH];
|
|
||||||
|
|
||||||
substitute_path(path, RCT2_ADDRESS(RCT2_ADDRESS_SCENARIOS_PATH, char), scenario->path);
|
|
||||||
return scenario_load_and_play_from_path(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
int scenario_load_and_play_from_path(const char *path)
|
int scenario_load_and_play_from_path(const char *path)
|
||||||
{
|
{
|
||||||
window_close_construction_windows();
|
window_close_construction_windows();
|
||||||
@@ -437,29 +424,35 @@ void scenario_failure()
|
|||||||
*/
|
*/
|
||||||
void scenario_success()
|
void scenario_success()
|
||||||
{
|
{
|
||||||
int i;
|
const money32 companyValue = RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_COMPANY_VALUE, money32);
|
||||||
rct_scenario_basic* scenario;
|
|
||||||
uint32 current_val = RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_COMPANY_VALUE, uint32);
|
|
||||||
|
|
||||||
RCT2_GLOBAL(RCT2_ADDRESS_COMPLETED_COMPANY_VALUE, uint32) = current_val;
|
RCT2_GLOBAL(RCT2_ADDRESS_COMPLETED_COMPANY_VALUE, uint32) = companyValue;
|
||||||
peep_applause();
|
peep_applause();
|
||||||
|
|
||||||
for (i = 0; i < gScenarioListCount; i++) {
|
uint8 scenarioRoot = SCENARIO_ROOT_RCT2;
|
||||||
scenario = &gScenarioList[i];
|
scenario_index_entry *scenario = scenario_list_find_by_root_path(scenarioRoot, _scenarioFileName);
|
||||||
|
if (scenario == NULL) {
|
||||||
|
scenarioRoot = SCENARIO_ROOT_USER;
|
||||||
|
scenario = scenario_list_find_by_root_path(scenarioRoot, _scenarioFileName);
|
||||||
|
}
|
||||||
|
|
||||||
if (strequals(scenario->path, _scenarioFileName, 256, true)) {
|
if (scenario != NULL) {
|
||||||
// Check if record company value has been broken
|
// Check if record company value has been broken
|
||||||
if ((scenario->flags & SCENARIO_FLAGS_COMPLETED) && scenario->company_value >= current_val)
|
if (scenario->highscore == NULL || scenario->highscore->company_value < companyValue) {
|
||||||
break;
|
if (scenario->highscore == NULL) {
|
||||||
|
scenario->highscore = scenario_highscore_insert();
|
||||||
|
} else {
|
||||||
|
scenario_highscore_free(scenario->highscore);
|
||||||
|
}
|
||||||
|
scenario->highscore->fileNameRoot = scenarioRoot;
|
||||||
|
scenario->highscore->fileName = (utf8*)path_get_filename(scenario->path);
|
||||||
|
scenario->highscore->name = NULL;
|
||||||
|
scenario->highscore->company_value = companyValue;
|
||||||
|
|
||||||
// Allow name entry
|
// Allow name entry
|
||||||
RCT2_GLOBAL(RCT2_ADDRESS_PARK_FLAGS, uint32) |= PARK_FLAGS_SCENARIO_COMPLETE_NAME_INPUT;
|
RCT2_GLOBAL(RCT2_ADDRESS_PARK_FLAGS, uint32) |= PARK_FLAGS_SCENARIO_COMPLETE_NAME_INPUT;
|
||||||
scenario->company_value = current_val;
|
RCT2_GLOBAL(0x013587C0, money32) = companyValue;
|
||||||
scenario->flags |= SCENARIO_FLAGS_COMPLETED;
|
|
||||||
scenario->completed_by[0] = 0;
|
|
||||||
RCT2_GLOBAL(0x013587C0, uint32) = current_val;
|
|
||||||
scenario_scores_save();
|
scenario_scores_save();
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
scenario_end();
|
scenario_end();
|
||||||
@@ -471,21 +464,19 @@ void scenario_success()
|
|||||||
*/
|
*/
|
||||||
void scenario_success_submit_name(const char *name)
|
void scenario_success_submit_name(const char *name)
|
||||||
{
|
{
|
||||||
int i;
|
uint8 scenarioRoot = SCENARIO_ROOT_RCT2;
|
||||||
rct_scenario_basic* scenario;
|
scenario_index_entry *scenario = scenario_list_find_by_root_path(scenarioRoot, _scenarioFileName);
|
||||||
uint32 scenarioWinCompanyValue;
|
if (scenario == NULL) {
|
||||||
|
scenarioRoot = SCENARIO_ROOT_USER;
|
||||||
|
scenario = scenario_list_find_by_root_path(scenarioRoot, _scenarioFileName);
|
||||||
|
}
|
||||||
|
|
||||||
for (i = 0; i < gScenarioListCount; i++) {
|
if (scenario != NULL) {
|
||||||
scenario = &gScenarioList[i];
|
money32 scenarioWinCompanyValue = RCT2_GLOBAL(0x013587C0, money32);
|
||||||
|
if (scenario->highscore->company_value == scenarioWinCompanyValue) {
|
||||||
if (strequals(scenario->path, _scenarioFileName, 256, true)) {
|
scenario->highscore->name = _strdup(name);
|
||||||
scenarioWinCompanyValue = RCT2_GLOBAL(0x013587C0, uint32);
|
safe_strncpy((char*)0x013587D8, name, 32);
|
||||||
if (scenario->company_value == scenarioWinCompanyValue) {
|
scenario_scores_save();
|
||||||
safe_strncpy(scenario->completed_by, name, 64);
|
|
||||||
safe_strncpy((char*)0x013587D8, name, 32);
|
|
||||||
scenario_scores_save();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -100,8 +100,8 @@ typedef struct {
|
|||||||
sint32 flags; // 0x0268
|
sint32 flags; // 0x0268
|
||||||
uint32 company_value; // 0x026C
|
uint32 company_value; // 0x026C
|
||||||
char completed_by[64]; // 0x0270
|
char completed_by[64]; // 0x0270
|
||||||
uint8 source_game; // new in OpenRCT2
|
// uint8 source_game; // new in OpenRCT2
|
||||||
sint16 source_index; // new in OpenRCT2
|
// sint16 source_index; // new in OpenRCT2
|
||||||
} rct_scenario_basic;
|
} rct_scenario_basic;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@@ -420,20 +420,55 @@ enum {
|
|||||||
OBJECTIVE_MONTHLY_FOOD_INCOME
|
OBJECTIVE_MONTHLY_FOOD_INCOME
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8 fileNameRoot;
|
||||||
|
utf8 *fileName;
|
||||||
|
utf8 *name;
|
||||||
|
money32 company_value;
|
||||||
|
} scenario_highscore_entry;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
utf8 path[MAX_PATH];
|
||||||
|
|
||||||
|
// Category / sequence
|
||||||
|
uint8 flags;
|
||||||
|
uint8 category;
|
||||||
|
uint8 source_game;
|
||||||
|
sint16 source_index;
|
||||||
|
|
||||||
|
// Objective
|
||||||
|
uint8 objective_type;
|
||||||
|
uint8 objective_arg_1;
|
||||||
|
sint32 objective_arg_2;
|
||||||
|
sint16 objective_arg_3;
|
||||||
|
scenario_highscore_entry *highscore;
|
||||||
|
|
||||||
|
utf8 name[64];
|
||||||
|
utf8 details[256];
|
||||||
|
} scenario_index_entry;
|
||||||
|
|
||||||
|
enum {
|
||||||
|
SCENARIO_ROOT_RCT2,
|
||||||
|
SCENARIO_ROOT_USER,
|
||||||
|
};
|
||||||
|
|
||||||
// Scenario list
|
// Scenario list
|
||||||
extern int gScenarioListCount;
|
extern int gScenarioListCount;
|
||||||
extern int gScenarioListCapacity;
|
extern int gScenarioListCapacity;
|
||||||
extern rct_scenario_basic *gScenarioList;
|
extern scenario_index_entry *gScenarioList;
|
||||||
|
|
||||||
extern char gScenarioSavePath[MAX_PATH];
|
extern char gScenarioSavePath[MAX_PATH];
|
||||||
extern int gFirstTimeSave;
|
extern int gFirstTimeSave;
|
||||||
|
|
||||||
int scenario_scores_save();
|
bool scenario_scores_save();
|
||||||
void scenario_load_list();
|
void scenario_load_list();
|
||||||
rct_scenario_basic *get_scenario_by_filename(const char *filename);
|
void scenario_list_dispose();
|
||||||
|
scenario_index_entry *scenario_list_find_by_path(const utf8 *path);
|
||||||
|
scenario_index_entry *scenario_list_find_by_root_path(uint8 root, const utf8 *filename);
|
||||||
|
scenario_highscore_entry *scenario_highscore_insert();
|
||||||
|
void scenario_highscore_free(scenario_highscore_entry *highscore);
|
||||||
int scenario_load_basic(const char *path, rct_s6_header *header, rct_s6_info *info);
|
int scenario_load_basic(const char *path, rct_s6_header *header, rct_s6_info *info);
|
||||||
int scenario_load(const char *path);
|
int scenario_load(const char *path);
|
||||||
int scenario_load_and_play(const rct_scenario_basic *scenario);
|
|
||||||
int scenario_load_and_play_from_path(const char *path);
|
int scenario_load_and_play_from_path(const char *path);
|
||||||
void scenario_begin();
|
void scenario_begin();
|
||||||
void scenario_update();
|
void scenario_update();
|
||||||
|
|||||||
@@ -28,55 +28,194 @@
|
|||||||
// Scenario list
|
// Scenario list
|
||||||
int gScenarioListCount = 0;
|
int gScenarioListCount = 0;
|
||||||
int gScenarioListCapacity = 0;
|
int gScenarioListCapacity = 0;
|
||||||
rct_scenario_basic *gScenarioList = NULL;
|
scenario_index_entry *gScenarioList = NULL;
|
||||||
|
|
||||||
|
int gScenarioHighscoreListCount = 0;
|
||||||
|
int gScenarioHighscoreListCapacity = 0;
|
||||||
|
scenario_highscore_entry *gScenarioHighscoreList = NULL;
|
||||||
|
|
||||||
|
static void scenario_list_include(const utf8 *directory);
|
||||||
static void scenario_list_add(const char *path);
|
static void scenario_list_add(const char *path);
|
||||||
static void scenario_list_sort();
|
static void scenario_list_sort();
|
||||||
static int scenario_list_sort_by_name(const void *a, const void *b);
|
static int scenario_list_sort_by_name(const void *a, const void *b);
|
||||||
static int scenario_list_sort_by_index(const void *a, const void *b);
|
static int scenario_list_sort_by_index(const void *a, const void *b);
|
||||||
static int scenario_scores_load();
|
static sint32 get_scenario_index(utf8 *name);
|
||||||
|
static void normalise_scenario_name(utf8 *name);
|
||||||
|
static scenario_source source_by_index(sint32 index);
|
||||||
|
|
||||||
rct_scenario_basic *get_scenario_by_filename(const char *filename)
|
static bool scenario_scores_load();
|
||||||
|
static bool scenario_scores_legacy_load();
|
||||||
|
static void scenario_highscore_remove(scenario_highscore_entry *higscore);
|
||||||
|
static void scenario_highscore_list_dispose();
|
||||||
|
static utf8 *io_read_string(SDL_RWops *file);
|
||||||
|
static void io_write_string(SDL_RWops *file, utf8 *source);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Searches and grabs the metadata for all the scenarios.
|
||||||
|
*/
|
||||||
|
void scenario_load_list()
|
||||||
{
|
{
|
||||||
int i;
|
utf8 directory[MAX_PATH];
|
||||||
for (i = 0; i < gScenarioListCount; i++)
|
|
||||||
if (strcmp(gScenarioList[i].path, filename) == 0)
|
|
||||||
return &gScenarioList[i];
|
|
||||||
|
|
||||||
return NULL;
|
// Clear scenario list
|
||||||
|
gScenarioListCount = 0;
|
||||||
|
|
||||||
|
// Get scenario directory from RCT2
|
||||||
|
safe_strncpy(directory, gConfigGeneral.game_path, sizeof(directory));
|
||||||
|
safe_strcat_path(directory, "Scenarios", sizeof(directory));
|
||||||
|
scenario_list_include(directory);
|
||||||
|
|
||||||
|
// Get scenario directory from user directory
|
||||||
|
platform_get_user_directory(directory, "scenario");
|
||||||
|
scenario_list_include(directory);
|
||||||
|
|
||||||
|
scenario_list_sort();
|
||||||
|
scenario_scores_load();
|
||||||
|
scenario_scores_legacy_load();
|
||||||
}
|
}
|
||||||
|
|
||||||
sint16 get_scenario_index(rct_scenario_basic *scenario)
|
static void scenario_list_include(const utf8 *directory)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < NUM_ORIGINAL_SCENARIOS; i++) {
|
int handle;
|
||||||
if (strcmp(original_scenario_names[i], scenario->name) == 0)
|
file_info fileInfo;
|
||||||
return i;
|
|
||||||
|
// Scenarios in this directory
|
||||||
|
utf8 pattern[MAX_PATH];
|
||||||
|
safe_strncpy(pattern, directory, sizeof(pattern));
|
||||||
|
safe_strcat_path(pattern, "*.sc6", sizeof(pattern));
|
||||||
|
|
||||||
|
handle = platform_enumerate_files_begin(pattern);
|
||||||
|
while (platform_enumerate_files_next(handle, &fileInfo)) {
|
||||||
|
utf8 path[MAX_PATH];
|
||||||
|
safe_strncpy(path, directory, sizeof(pattern));
|
||||||
|
safe_strcat_path(path, fileInfo.path, sizeof(pattern));
|
||||||
|
scenario_list_add(path);
|
||||||
|
}
|
||||||
|
platform_enumerate_files_end(handle);
|
||||||
|
|
||||||
|
// Include sub-directories
|
||||||
|
utf8 subDirectory[MAX_PATH];
|
||||||
|
handle = platform_enumerate_directories_begin(directory);
|
||||||
|
while (platform_enumerate_directories_next(handle, subDirectory)) {
|
||||||
|
utf8 path[MAX_PATH];
|
||||||
|
safe_strncpy(path, directory, sizeof(pattern));
|
||||||
|
safe_strcat_path(path, subDirectory, sizeof(pattern));
|
||||||
|
scenario_list_include(path);
|
||||||
|
}
|
||||||
|
platform_enumerate_directories_end(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void scenario_list_add(const utf8 *path)
|
||||||
|
{
|
||||||
|
// Load the basic scenario information
|
||||||
|
rct_s6_header s6Header;
|
||||||
|
rct_s6_info s6Info;
|
||||||
|
if (!scenario_load_basic(path, &s6Header, &s6Info)) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Increase cache size
|
||||||
|
if (gScenarioListCount == gScenarioListCapacity) {
|
||||||
|
gScenarioListCapacity = max(8, gScenarioListCapacity * 2);
|
||||||
|
gScenarioList = (scenario_index_entry*)realloc(gScenarioList, gScenarioListCapacity * sizeof(scenario_index_entry));
|
||||||
|
}
|
||||||
|
scenario_index_entry *newEntry = &gScenarioList[gScenarioListCount];
|
||||||
|
gScenarioListCount++;
|
||||||
|
|
||||||
|
// Set new entry
|
||||||
|
safe_strncpy(newEntry->path, path, sizeof(newEntry->path));
|
||||||
|
newEntry->category = s6Info.category;
|
||||||
|
newEntry->flags = SCENARIO_FLAGS_VISIBLE;
|
||||||
|
newEntry->objective_type = s6Info.objective_type;
|
||||||
|
newEntry->objective_arg_1 = s6Info.objective_arg_1;
|
||||||
|
newEntry->objective_arg_2 = s6Info.objective_arg_2;
|
||||||
|
newEntry->objective_arg_3 = s6Info.objective_arg_3;
|
||||||
|
newEntry->highscore = NULL;
|
||||||
|
safe_strncpy(newEntry->name, s6Info.name, sizeof(newEntry->name));
|
||||||
|
safe_strncpy(newEntry->details, s6Info.details, sizeof(newEntry->details));
|
||||||
|
|
||||||
|
// Normalise the name to make the scenario as recognisable as possible.
|
||||||
|
normalise_scenario_name(newEntry->name);
|
||||||
|
|
||||||
|
// Look up and store information regarding the origins of this scenario.
|
||||||
|
newEntry->source_index = get_scenario_index(newEntry->name);
|
||||||
|
newEntry->source_game = source_by_index(newEntry->source_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void scenario_list_dispose()
|
||||||
|
{
|
||||||
|
gScenarioListCapacity = 0;
|
||||||
|
gScenarioListCount = 0;
|
||||||
|
SafeFree(gScenarioList);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void scenario_list_sort()
|
||||||
|
{
|
||||||
|
int(*compareFunc)(void const*, void const*);
|
||||||
|
|
||||||
|
compareFunc = gConfigGeneral.scenario_select_mode == SCENARIO_SELECT_MODE_ORIGIN ?
|
||||||
|
scenario_list_sort_by_index :
|
||||||
|
scenario_list_sort_by_name;
|
||||||
|
|
||||||
|
qsort(gScenarioList, gScenarioListCount, sizeof(scenario_index_entry), compareFunc);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int scenario_list_sort_by_name(const void *a, const void *b)
|
||||||
|
{
|
||||||
|
const scenario_index_entry *entryA = (const scenario_index_entry*)a;
|
||||||
|
const scenario_index_entry *entryB = (const scenario_index_entry*)b;
|
||||||
|
|
||||||
|
return strcmp(entryA->name, entryB->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int scenario_list_sort_by_index(const void *a, const void *b)
|
||||||
|
{
|
||||||
|
const scenario_index_entry *entryA = (const scenario_index_entry*)a;
|
||||||
|
const scenario_index_entry *entryB = (const scenario_index_entry*)b;
|
||||||
|
|
||||||
|
if (entryA->source_game == SCENARIO_SOURCE_OTHER && entryB->source_game == SCENARIO_SOURCE_OTHER) {
|
||||||
|
return scenario_list_sort_by_name(a, b);
|
||||||
|
}
|
||||||
|
return entryA->source_index - entryB->source_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
static sint32 get_scenario_index(utf8 *name)
|
||||||
|
{
|
||||||
|
for (sint32 i = 0; i < NUM_ORIGINAL_SCENARIOS; i++) {
|
||||||
|
if (_strcmpi(original_scenario_names[i], name) == 0) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void normalise_scenario_name(rct_scenario_basic *scenario)
|
static void normalise_scenario_name(utf8 *name)
|
||||||
{
|
{
|
||||||
char* name = scenario->name;
|
size_t nameLength = strlen(name);
|
||||||
|
|
||||||
// Strip "RCT2 " prefix off scenario names.
|
// Strip "RCT(1|2)? *" prefix off scenario names.
|
||||||
if (name[0] == 'R' && name[1] == 'C' && name[2] == 'T' && name[3] == '2') {
|
if (nameLength >= 3 && (name[0] == 'R' && name[1] == 'C' && name[2] == 'T')) {
|
||||||
log_verbose("Stripping RCT2 from name: %s", name);
|
if (nameLength >= 4 && (name[3] == '1' || name[3] == '2')) {
|
||||||
safe_strncpy(scenario->name, name + 5, 64);
|
log_verbose("Stripping RCT/1/2 from name: %s", name);
|
||||||
|
safe_strncpy(name, name + 4, 64);
|
||||||
|
} else {
|
||||||
|
safe_strncpy(name, name + 3, 64);
|
||||||
|
}
|
||||||
|
|
||||||
|
safe_strtrimleft(name, name, 64);
|
||||||
}
|
}
|
||||||
|
|
||||||
// American scenario titles should be handled by their British counterpart, internally.
|
// American scenario titles should be converted to British name
|
||||||
|
// Don't worry, names will be translated using language packs later
|
||||||
for (int i = 0; i < NUM_ALIASES; i++) {
|
for (int i = 0; i < NUM_ALIASES; i++) {
|
||||||
if (strcmp(scenario_aliases[i * 2], name) == 0)
|
if (strcmp(scenario_aliases[(i * 2) + 1], name) == 0) {
|
||||||
{
|
log_verbose("Found alias: %s; will treat as: %s", name, scenario_aliases[i * 2]);
|
||||||
log_verbose("Found alias: %s; will treat as: %s", scenario->name, scenario_aliases[i * 2 + 1]);
|
safe_strncpy(name, scenario_aliases[i * 2], 64);
|
||||||
safe_strncpy(scenario->name, scenario_aliases[i * 2 + 1], 64);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
scenario_source source_by_index(uint8 index)
|
static scenario_source source_by_index(sint32 index)
|
||||||
{
|
{
|
||||||
if (index >= SCENARIO_SOURCE_RCT1_INDEX && index < SCENARIO_SOURCE_RCT1_AA_INDEX) {
|
if (index >= SCENARIO_SOURCE_RCT1_INDEX && index < SCENARIO_SOURCE_RCT1_AA_INDEX) {
|
||||||
return SCENARIO_SOURCE_RCT1;
|
return SCENARIO_SOURCE_RCT1;
|
||||||
@@ -97,168 +236,65 @@ scenario_source source_by_index(uint8 index)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
scenario_index_entry *scenario_list_find_by_path(const utf8 *path)
|
||||||
*
|
|
||||||
* rct2: 0x006775A8
|
|
||||||
*/
|
|
||||||
void scenario_load_list()
|
|
||||||
{
|
{
|
||||||
int i, enumFileHandle;
|
for (int i = 0; i < gScenarioListCount; i++) {
|
||||||
file_info enumFileInfo;
|
if (_strcmpi(path, gScenarioList[i].path) == 0) {
|
||||||
|
return &gScenarioList[i];
|
||||||
// Load scores
|
|
||||||
scenario_scores_load();
|
|
||||||
|
|
||||||
// Set all scenarios to be invisible
|
|
||||||
for (i = 0; i < gScenarioListCount; i++)
|
|
||||||
gScenarioList[i].flags &= ~SCENARIO_FLAGS_VISIBLE;
|
|
||||||
|
|
||||||
// Enumerate through each scenario in the directory
|
|
||||||
enumFileHandle = platform_enumerate_files_begin(RCT2_ADDRESS(RCT2_ADDRESS_SCENARIOS_PATH, char));
|
|
||||||
if (enumFileHandle != INVALID_HANDLE) {
|
|
||||||
while (platform_enumerate_files_next(enumFileHandle, &enumFileInfo)) {
|
|
||||||
scenario_list_add(enumFileInfo.path);
|
|
||||||
}
|
}
|
||||||
platform_enumerate_files_end(enumFileHandle);
|
|
||||||
}
|
}
|
||||||
|
return NULL;
|
||||||
// Sort alphabetically
|
|
||||||
scenario_list_sort();
|
|
||||||
|
|
||||||
// Save the scores
|
|
||||||
scenario_scores_save();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void scenario_list_add(const char *path)
|
scenario_index_entry *scenario_list_find_by_root_path(uint8 root, const utf8 *filename)
|
||||||
{
|
{
|
||||||
char scenarioPath[MAX_PATH];
|
// Derive path
|
||||||
rct_scenario_basic *scenario;
|
utf8 path[MAX_PATH];
|
||||||
rct_s6_header s6Header;
|
if (root == SCENARIO_ROOT_RCT2) {
|
||||||
rct_s6_info s6Info;
|
safe_strncpy(path, gConfigGeneral.game_path, sizeof(path));
|
||||||
|
safe_strcat_path(path, "Scenarios", sizeof(path));
|
||||||
// Get absolute path
|
|
||||||
substitute_path(scenarioPath, RCT2_ADDRESS(RCT2_ADDRESS_SCENARIOS_PATH, char), path);
|
|
||||||
|
|
||||||
// Load the basic scenario information
|
|
||||||
if (!scenario_load_basic(scenarioPath, &s6Header, &s6Info))
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Ignore scenarios where first header byte is not 255
|
|
||||||
if (s6Info.editor_step != 255)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Check if scenario already exists in list, likely if in scores
|
|
||||||
scenario = get_scenario_by_filename(path);
|
|
||||||
if (scenario != NULL) {
|
|
||||||
// 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;
|
|
||||||
safe_strncpy(scenario->name, s6Info.name, 64);
|
|
||||||
safe_strncpy(scenario->details, s6Info.details, 256);
|
|
||||||
} else {
|
} else {
|
||||||
// Check if the scenario list buffer has room for another scenario
|
platform_get_user_directory(path, "scenario");
|
||||||
if (gScenarioListCount >= gScenarioListCapacity) {
|
|
||||||
// Allocate more room
|
|
||||||
gScenarioListCapacity += 16;
|
|
||||||
gScenarioList = realloc(gScenarioList, gScenarioListCapacity * sizeof(rct_scenario_basic));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Increment the number of scenarios
|
|
||||||
scenario = &gScenarioList[gScenarioListCount];
|
|
||||||
gScenarioListCount++;
|
|
||||||
|
|
||||||
// Add this new scenario to the list
|
|
||||||
safe_strncpy(scenario->path, path, 256);
|
|
||||||
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;
|
|
||||||
safe_strncpy(scenario->name, s6Info.name, 64);
|
|
||||||
safe_strncpy(scenario->details, s6Info.details, 256);
|
|
||||||
}
|
}
|
||||||
|
safe_strcat_path(path, filename, sizeof(path));
|
||||||
|
|
||||||
// Normalize the name to make the scenario as recognisable as possible.
|
// Find matching scenario entry
|
||||||
normalise_scenario_name(scenario);
|
return scenario_list_find_by_path(path);
|
||||||
|
|
||||||
// Look up and store information regarding the origins of this scenario.
|
|
||||||
scenario->source_index = get_scenario_index(scenario);
|
|
||||||
scenario->source_game = source_by_index(scenario->source_index);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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()
|
|
||||||
{
|
|
||||||
if (gConfigGeneral.scenario_select_mode == 1) // and not tabIndex > REAL, OTHER
|
|
||||||
qsort(gScenarioList, gScenarioListCount, sizeof(rct_scenario_basic), scenario_list_sort_by_index);
|
|
||||||
else
|
|
||||||
qsort(gScenarioList, gScenarioListCount, sizeof(rct_scenario_basic), scenario_list_sort_by_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int scenario_list_sort_by_index(const void *a, const void *b)
|
|
||||||
{
|
|
||||||
if (((rct_scenario_basic*)a)->source_game == SCENARIO_SOURCE_OTHER && ((rct_scenario_basic*)b)->source_game == SCENARIO_SOURCE_OTHER)
|
|
||||||
return scenario_list_sort_by_name(a, b);
|
|
||||||
|
|
||||||
return ((rct_scenario_basic*)a)->source_index - ((rct_scenario_basic*)b)->source_index;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Basic scenario information compare function for sorting.
|
|
||||||
* rct2: 0x00677C08
|
|
||||||
*/
|
|
||||||
static int scenario_list_sort_by_name(const void *a, const void *b)
|
|
||||||
{
|
|
||||||
return strcmp(((rct_scenario_basic*)a)->name, ((rct_scenario_basic*)b)->name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the path for the scenario scores path.
|
* Gets the path for the scenario scores path.
|
||||||
*/
|
*/
|
||||||
static void scenario_scores_get_path(utf8 *outPath)
|
static void scenario_scores_get_path(utf8 *outPath)
|
||||||
|
{
|
||||||
|
platform_get_user_directory(outPath, NULL);
|
||||||
|
strcat(outPath, "highscores.dat");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the path for the scenario scores path.
|
||||||
|
*/
|
||||||
|
static void scenario_scores_legacy_get_path(utf8 *outPath)
|
||||||
{
|
{
|
||||||
platform_get_user_directory(outPath, NULL);
|
platform_get_user_directory(outPath, NULL);
|
||||||
strcat(outPath, "scores.dat");
|
strcat(outPath, "scores.dat");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* Loads the original scores.dat file and replaces any highscores that
|
||||||
* rct2: 0x006775A8
|
* are better for matching scenarios.
|
||||||
*/
|
*/
|
||||||
static int scenario_scores_load()
|
static bool scenario_scores_legacy_load()
|
||||||
{
|
{
|
||||||
SDL_RWops *file;
|
utf8 scoresPath[MAX_PATH];
|
||||||
char scoresPath[MAX_PATH];
|
scenario_scores_legacy_get_path(scoresPath);
|
||||||
|
|
||||||
scenario_scores_get_path(scoresPath);
|
|
||||||
|
|
||||||
// Free scenario list if already allocated
|
|
||||||
if (gScenarioList != NULL) {
|
|
||||||
free(gScenarioList);
|
|
||||||
gScenarioList = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try and load the scores file
|
|
||||||
|
|
||||||
// First check user folder and then fallback to install directory
|
// First check user folder and then fallback to install directory
|
||||||
file = SDL_RWFromFile(scoresPath, "rb");
|
SDL_RWops *file = SDL_RWFromFile(scoresPath, "rb");
|
||||||
if (file == NULL) {
|
if (file == NULL) {
|
||||||
file = SDL_RWFromFile(get_file_path(PATH_ID_SCORES), "rb");
|
file = SDL_RWFromFile(get_file_path(PATH_ID_SCORES), "rb");
|
||||||
if (file == NULL) {
|
if (file == NULL) {
|
||||||
log_error("Unable to load scenario scores.");
|
return false;
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -266,53 +302,208 @@ static int scenario_scores_load()
|
|||||||
rct_scenario_scores_header header;
|
rct_scenario_scores_header header;
|
||||||
if (SDL_RWread(file, &header, 16, 1) != 1) {
|
if (SDL_RWread(file, &header, 16, 1) != 1) {
|
||||||
SDL_RWclose(file);
|
SDL_RWclose(file);
|
||||||
log_error("Invalid header in scenario scores file.");
|
log_error("Invalid header in legacy scenario scores file.");
|
||||||
return 0;
|
return false;
|
||||||
}
|
|
||||||
gScenarioListCount = header.scenario_count;
|
|
||||||
|
|
||||||
// Load scenario information with scores
|
|
||||||
int scenarioListBufferSize = gScenarioListCount * sizeof(rct_scenario_basic);
|
|
||||||
gScenarioListCapacity = gScenarioListCount;
|
|
||||||
gScenarioList = malloc(scenarioListBufferSize);
|
|
||||||
if (SDL_RWread(file, gScenarioList, scenarioListBufferSize, 1) == 1) {
|
|
||||||
SDL_RWclose(file);
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unable to load scores, free scenario list
|
// Read scenarios
|
||||||
|
bool highscoresDirty = false;
|
||||||
|
for (uint32 i = 0; i < header.scenario_count; i++) {
|
||||||
|
// Read legacy entry
|
||||||
|
rct_scenario_basic scBasic;
|
||||||
|
if (SDL_RWread(file, &scBasic, sizeof(rct_scenario_basic), 1) != 1) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore non-completed scenarios
|
||||||
|
if (!(scBasic.flags & SCENARIO_FLAGS_COMPLETED)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find matching scenario entry
|
||||||
|
scenario_index_entry *scenarioIndexEntry = scenario_list_find_by_root_path(SCENARIO_ROOT_RCT2, scBasic.path);
|
||||||
|
if (scenarioIndexEntry != NULL) {
|
||||||
|
// Check if legacy highscore is better
|
||||||
|
scenario_highscore_entry *highscore = scenarioIndexEntry->highscore;
|
||||||
|
if (highscore == NULL) {
|
||||||
|
highscore = scenario_highscore_insert();
|
||||||
|
scenarioIndexEntry->highscore = highscore;
|
||||||
|
} else if (highscore->company_value < (money32)scBasic.company_value) {
|
||||||
|
scenario_highscore_free(highscore);
|
||||||
|
// Re-use highscore entry
|
||||||
|
} else {
|
||||||
|
highscore = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set new highscore
|
||||||
|
if (highscore != NULL) {
|
||||||
|
highscore->fileNameRoot = SCENARIO_ROOT_RCT2;
|
||||||
|
highscore->fileName = _strdup(scBasic.path);
|
||||||
|
highscore->name = _strdup(scBasic.completed_by);
|
||||||
|
highscore->company_value = (money32)scBasic.company_value;
|
||||||
|
highscoresDirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exit loop
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
SDL_RWclose(file);
|
SDL_RWclose(file);
|
||||||
gScenarioListCount = 0;
|
|
||||||
gScenarioListCapacity = 0;
|
if (highscoresDirty) {
|
||||||
free(gScenarioList);
|
scenario_scores_save();
|
||||||
gScenarioList = NULL;
|
}
|
||||||
return 0;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool scenario_scores_load()
|
||||||
|
{
|
||||||
|
utf8 scoresPath[MAX_PATH];
|
||||||
|
scenario_scores_get_path(scoresPath);
|
||||||
|
|
||||||
|
// Load scores file
|
||||||
|
SDL_RWops *file = SDL_RWFromFile(scoresPath, "rb");
|
||||||
|
if (file == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check file version
|
||||||
|
uint32 fileVersion;
|
||||||
|
SDL_RWread(file, &fileVersion, sizeof(fileVersion), 1);
|
||||||
|
if (fileVersion != 1) {
|
||||||
|
log_error("Invalid or incompatible highscores file.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read and allocate the highscore list
|
||||||
|
scenario_highscore_list_dispose();
|
||||||
|
SDL_RWread(file, &gScenarioHighscoreListCount, sizeof(gScenarioHighscoreListCount), 1);
|
||||||
|
gScenarioHighscoreListCapacity = gScenarioHighscoreListCount;
|
||||||
|
gScenarioHighscoreList = malloc(gScenarioHighscoreListCapacity * sizeof(scenario_highscore_entry));
|
||||||
|
|
||||||
|
// Read highscores
|
||||||
|
for (int i = 0; i < gScenarioHighscoreListCount; i++) {
|
||||||
|
scenario_highscore_entry *highscore = &gScenarioHighscoreList[i];
|
||||||
|
|
||||||
|
SDL_RWread(file, &highscore->fileNameRoot, sizeof(highscore->fileNameRoot), 1);
|
||||||
|
highscore->fileName = io_read_string(file);
|
||||||
|
highscore->name = io_read_string(file);
|
||||||
|
SDL_RWread(file, &highscore->company_value, sizeof(highscore->company_value), 1);
|
||||||
|
|
||||||
|
// Attach highscore to correct scenario entry
|
||||||
|
scenario_index_entry *scenarioIndexEntry = scenario_list_find_by_root_path(highscore->fileNameRoot, highscore->fileName);
|
||||||
|
if (scenarioIndexEntry != NULL) {
|
||||||
|
scenarioIndexEntry->highscore = highscore;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_RWclose(file);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* rct2: 0x00677B50
|
* rct2: 0x00677B50
|
||||||
*/
|
*/
|
||||||
int scenario_scores_save()
|
bool scenario_scores_save()
|
||||||
{
|
{
|
||||||
SDL_RWops *file;
|
|
||||||
utf8 scoresPath[MAX_PATH];
|
utf8 scoresPath[MAX_PATH];
|
||||||
|
|
||||||
scenario_scores_get_path(scoresPath);
|
scenario_scores_get_path(scoresPath);
|
||||||
|
|
||||||
file = SDL_RWFromFile(scoresPath, "wb");
|
SDL_RWops *file = SDL_RWFromFile(scoresPath, "wb");
|
||||||
if (file == NULL) {
|
if (file == NULL) {
|
||||||
log_error("Unable to save scenario scores.");
|
log_error("Unable to save scenario scores.");
|
||||||
return 0;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
rct_scenario_scores_header header;
|
const uint32 fileVersion = 1;
|
||||||
header.scenario_count = gScenarioListCount;
|
|
||||||
|
|
||||||
SDL_RWwrite(file, &header, sizeof(header), 1);
|
|
||||||
if (gScenarioListCount > 0)
|
|
||||||
SDL_RWwrite(file, gScenarioList, gScenarioListCount * sizeof(rct_scenario_basic), 1);
|
|
||||||
|
|
||||||
|
SDL_RWwrite(file, &fileVersion, sizeof(fileVersion), 1);
|
||||||
|
SDL_RWwrite(file, &gScenarioHighscoreListCount, sizeof(gScenarioHighscoreListCount), 1);
|
||||||
|
for (int i = 0; i < gScenarioHighscoreListCount; i++) {
|
||||||
|
scenario_highscore_entry *highscore = &gScenarioHighscoreList[i];
|
||||||
|
SDL_RWwrite(file, &highscore->fileNameRoot, sizeof(highscore->fileNameRoot), 1);
|
||||||
|
io_write_string(file, highscore->fileName);
|
||||||
|
io_write_string(file, highscore->name);
|
||||||
|
SDL_RWwrite(file, &highscore->company_value, sizeof(highscore->company_value), 1);
|
||||||
|
}
|
||||||
SDL_RWclose(file);
|
SDL_RWclose(file);
|
||||||
return 1;
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
scenario_highscore_entry *scenario_highscore_insert()
|
||||||
|
{
|
||||||
|
if (gScenarioHighscoreListCount >= gScenarioHighscoreListCapacity) {
|
||||||
|
gScenarioHighscoreListCapacity = max(8, gScenarioHighscoreListCapacity * 2);
|
||||||
|
gScenarioHighscoreList = realloc(gScenarioHighscoreList, gScenarioHighscoreListCapacity * sizeof(scenario_highscore_entry));
|
||||||
|
}
|
||||||
|
return &gScenarioHighscoreList[gScenarioHighscoreListCount++];
|
||||||
|
}
|
||||||
|
|
||||||
|
static void scenario_highscore_remove(scenario_highscore_entry *highscore)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < gScenarioHighscoreListCount; i++) {
|
||||||
|
if (&gScenarioHighscoreList[i] == highscore) {
|
||||||
|
size_t moveSize = (gScenarioHighscoreListCount - i - 1) * sizeof(scenario_highscore_entry);
|
||||||
|
if (moveSize > 0) {
|
||||||
|
memmove(&gScenarioHighscoreList[i], &gScenarioHighscoreList[i + 1], moveSize);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void scenario_highscore_free(scenario_highscore_entry *highscore)
|
||||||
|
{
|
||||||
|
SafeFree(highscore->fileName);
|
||||||
|
SafeFree(highscore->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void scenario_highscore_list_dispose()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < gScenarioHighscoreListCount; i++) {
|
||||||
|
scenario_highscore_free(&gScenarioHighscoreList[i]);
|
||||||
|
}
|
||||||
|
gScenarioHighscoreListCapacity = 0;
|
||||||
|
gScenarioHighscoreListCount = 0;
|
||||||
|
SafeFree(gScenarioHighscoreList);
|
||||||
|
}
|
||||||
|
|
||||||
|
static utf8 *io_read_string(SDL_RWops *file)
|
||||||
|
{
|
||||||
|
size_t bufferCount = 0;
|
||||||
|
size_t bufferCapacity = 0;
|
||||||
|
utf8 *buffer = NULL;
|
||||||
|
|
||||||
|
utf8 ch;
|
||||||
|
do {
|
||||||
|
SDL_RWread(file, &ch, sizeof(ch), 1);
|
||||||
|
if (ch == '\0' && buffer == NULL) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bufferCount >= bufferCapacity) {
|
||||||
|
bufferCapacity = max(32, bufferCapacity * 2);
|
||||||
|
buffer = realloc(buffer, bufferCapacity * sizeof(uint8));
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer[bufferCount] = ch;
|
||||||
|
bufferCount++;
|
||||||
|
} while (ch != '\0');
|
||||||
|
|
||||||
|
if (bufferCount < bufferCapacity) {
|
||||||
|
buffer = realloc(buffer, bufferCount);
|
||||||
|
}
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void io_write_string(SDL_RWops *file, utf8 *source)
|
||||||
|
{
|
||||||
|
if (source == NULL) {
|
||||||
|
utf8 empty = 0;
|
||||||
|
SDL_RWwrite(file, &empty, sizeof(utf8), 1);
|
||||||
|
} else {
|
||||||
|
SDL_RWwrite(file, source, strlen(source) + 1, 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#define NUM_ORIGINAL_SCENARIOS 136
|
#define NUM_ORIGINAL_SCENARIOS 136
|
||||||
#define NUM_ALIASES 5
|
#define NUM_ALIASES 6
|
||||||
|
|
||||||
#define SCENARIO_SOURCE_RCT1_INDEX 0
|
#define SCENARIO_SOURCE_RCT1_INDEX 0
|
||||||
#define SCENARIO_SOURCE_RCT1_AA_INDEX 22
|
#define SCENARIO_SOURCE_RCT1_AA_INDEX 22
|
||||||
@@ -10,15 +10,16 @@
|
|||||||
#define SCENARIO_SOURCE_REAL_INDEX 128
|
#define SCENARIO_SOURCE_REAL_INDEX 128
|
||||||
|
|
||||||
const char * const scenario_aliases[NUM_ALIASES * 2] = {
|
const char * const scenario_aliases[NUM_ALIASES * 2] = {
|
||||||
"Katie's World", "Katie's Dreamland",
|
"Katie's Dreamland", "Katie's World",
|
||||||
"Dinky Park", "Pokey Park",
|
"Pokey Park", "Dinky Park",
|
||||||
"Aqua Park", "White Water Park",
|
"White Water Park", "Aqua Park",
|
||||||
"Mothball Mountain","Mystic Mountain",
|
"Mystic Mountain", "Mothball Mountain",
|
||||||
"Big Pier", "Paradise Pier"
|
"Paradise Pier", "Big Pier",
|
||||||
|
"Paradise Pier 2", "Big Pier 2",
|
||||||
};
|
};
|
||||||
|
|
||||||
const char * const original_scenario_names[NUM_ORIGINAL_SCENARIOS] = {
|
const char * const original_scenario_names[NUM_ORIGINAL_SCENARIOS] = {
|
||||||
// RCT Classic
|
// RCT
|
||||||
"Forest Frontiers",
|
"Forest Frontiers",
|
||||||
"Dynamite Dunes",
|
"Dynamite Dunes",
|
||||||
"Leafy Lake",
|
"Leafy Lake",
|
||||||
@@ -42,7 +43,7 @@ const char * const original_scenario_names[NUM_ORIGINAL_SCENARIOS] = {
|
|||||||
"Thunder Rock",
|
"Thunder Rock",
|
||||||
"Mega Park",
|
"Mega Park",
|
||||||
|
|
||||||
// RCT: Corkscrew Follies
|
// RCT: Added Attractions
|
||||||
"Whispering Cliffs",
|
"Whispering Cliffs",
|
||||||
"Three Monkeys Park",
|
"Three Monkeys Park",
|
||||||
"Canary Mines",
|
"Canary Mines",
|
||||||
@@ -81,7 +82,7 @@ const char * const original_scenario_names[NUM_ORIGINAL_SCENARIOS] = {
|
|||||||
"Razor Rocks",
|
"Razor Rocks",
|
||||||
"Crater Lake",
|
"Crater Lake",
|
||||||
"Vertigo Views",
|
"Vertigo Views",
|
||||||
"Big Pier 2",
|
"Paradise Pier 2",
|
||||||
"Dragon's Cove",
|
"Dragon's Cove",
|
||||||
"Good Knight Park",
|
"Good Knight Park",
|
||||||
"Wacky Warren",
|
"Wacky Warren",
|
||||||
@@ -106,7 +107,7 @@ const char * const original_scenario_names[NUM_ORIGINAL_SCENARIOS] = {
|
|||||||
"Venus Ponds",
|
"Venus Ponds",
|
||||||
"Micro Park",
|
"Micro Park",
|
||||||
|
|
||||||
// RCT2 Vanilla
|
// RCT2
|
||||||
"Crazy Castle",
|
"Crazy Castle",
|
||||||
"Electric Fields",
|
"Electric Fields",
|
||||||
"Factory Capers",
|
"Factory Capers",
|
||||||
@@ -123,7 +124,7 @@ const char * const original_scenario_names[NUM_ORIGINAL_SCENARIOS] = {
|
|||||||
"Lucky Lake",
|
"Lucky Lake",
|
||||||
"Rainbow Summit",
|
"Rainbow Summit",
|
||||||
|
|
||||||
// RCT2 Wacky Worlds
|
// RCT2: Wacky Worlds
|
||||||
"Africa - Victoria Falls",
|
"Africa - Victoria Falls",
|
||||||
"Asia - Great Wall of China Tourism Enhancement",
|
"Asia - Great Wall of China Tourism Enhancement",
|
||||||
"North America - Grand Canyon",
|
"North America - Grand Canyon",
|
||||||
@@ -142,7 +143,7 @@ const char * const original_scenario_names[NUM_ORIGINAL_SCENARIOS] = {
|
|||||||
"N. America - Extreme Hawaiian Island",
|
"N. America - Extreme Hawaiian Island",
|
||||||
"South America - Rain Forest Plateau",
|
"South America - Rain Forest Plateau",
|
||||||
|
|
||||||
// RCT2 Time Twister
|
// RCT2: Time Twister
|
||||||
"Dark Age - Robin Hood",
|
"Dark Age - Robin Hood",
|
||||||
"Prehistoric - After the Asteroid",
|
"Prehistoric - After the Asteroid",
|
||||||
"Roaring Twenties - Prison Island",
|
"Roaring Twenties - Prison Island",
|
||||||
@@ -158,7 +159,7 @@ const char * const original_scenario_names[NUM_ORIGINAL_SCENARIOS] = {
|
|||||||
"Roaring Twenties - Skyscrapers",
|
"Roaring Twenties - Skyscrapers",
|
||||||
"Rock 'n' Roll - Rock 'n' Roll",
|
"Rock 'n' Roll - Rock 'n' Roll",
|
||||||
|
|
||||||
// Real parks
|
// Real parks
|
||||||
"Alton Towers",
|
"Alton Towers",
|
||||||
"Heide-Park",
|
"Heide-Park",
|
||||||
"Blackpool Pleasure Beach",
|
"Blackpool Pleasure Beach",
|
||||||
|
|||||||
@@ -222,6 +222,70 @@ char *safe_strncpy(char * destination, const char * source, size_t size)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char *safe_strcat(char *destination, const char *source, size_t size)
|
||||||
|
{
|
||||||
|
assert(destination != NULL);
|
||||||
|
assert(source != NULL);
|
||||||
|
|
||||||
|
if (size == 0) {
|
||||||
|
return destination;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *result = destination;
|
||||||
|
|
||||||
|
size_t i;
|
||||||
|
for (i = 0; i < size; i++) {
|
||||||
|
if (*destination == '\0') {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
destination++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool terminated = false;
|
||||||
|
for (; i < size; i++) {
|
||||||
|
if (*source != '\0') {
|
||||||
|
*destination++ = *source++;
|
||||||
|
} else {
|
||||||
|
*destination = *source;
|
||||||
|
terminated = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!terminated) {
|
||||||
|
result[size - 1] = '\0';
|
||||||
|
log_warning("Truncating string \"%s\" to %d bytes.", result, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *safe_strcat_path(char *destination, const char *source, size_t size)
|
||||||
|
{
|
||||||
|
const char pathSeparator = platform_get_path_separator();
|
||||||
|
|
||||||
|
size_t length = strlen(destination);
|
||||||
|
if (length >= size - 1) {
|
||||||
|
return destination;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (destination[length - 1] != pathSeparator) {
|
||||||
|
destination[length] = pathSeparator;
|
||||||
|
destination[length + 1] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
return safe_strcat(destination, source, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
char *safe_strtrimleft(char *destination, const char *source, size_t size)
|
||||||
|
{
|
||||||
|
while (*source == ' ' && *source != '\0') {
|
||||||
|
source++;
|
||||||
|
}
|
||||||
|
return safe_strncpy(destination, source, size);
|
||||||
|
}
|
||||||
|
|
||||||
bool utf8_is_bom(const char *str)
|
bool utf8_is_bom(const char *str)
|
||||||
{
|
{
|
||||||
return str[0] == (char)0xEF && str[1] == (char)0xBB && str[2] == (char)0xBF;
|
return str[0] == (char)0xEF && str[1] == (char)0xBB && str[2] == (char)0xBF;
|
||||||
|
|||||||
@@ -42,6 +42,9 @@ int bitcount(int source);
|
|||||||
bool strequals(const char *a, const char *b, int length, bool caseInsensitive);
|
bool strequals(const char *a, const char *b, int length, bool caseInsensitive);
|
||||||
int strcicmp(char const *a, char const *b);
|
int strcicmp(char const *a, char const *b);
|
||||||
char *safe_strncpy(char * destination, const char * source, size_t num);
|
char *safe_strncpy(char * destination, const char * source, size_t num);
|
||||||
|
char *safe_strcat(char *destination, const char *source, size_t size);
|
||||||
|
char *safe_strcat_path(char *destination, const char *source, size_t size);
|
||||||
|
char *safe_strtrimleft(char *destination, const char *source, size_t size);
|
||||||
|
|
||||||
bool utf8_is_bom(const char *str);
|
bool utf8_is_bom(const char *str);
|
||||||
bool str_is_null_or_empty(const char *str);
|
bool str_is_null_or_empty(const char *str);
|
||||||
|
|||||||
@@ -1179,6 +1179,7 @@ static void window_options_dropdown(rct_window *w, int widgetIndex, int dropdown
|
|||||||
gConfigGeneral.scenario_select_mode = dropdownIndex;
|
gConfigGeneral.scenario_select_mode = dropdownIndex;
|
||||||
config_save_default();
|
config_save_default();
|
||||||
window_invalidate(w);
|
window_invalidate(w);
|
||||||
|
window_close_by_class(WC_SCENARIO_SELECT);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -1530,7 +1531,9 @@ static void window_options_paint(rct_window *w, rct_drawpixelinfo *dpi)
|
|||||||
gfx_draw_string_left(dpi, STR_OPTIONS_SCENARIO_GROUPING, NULL, w->colours[1], w->x + 10, w->y + window_options_controls_and_interface_widgets[WIDX_SCENARIO_GROUPING].top + 1);
|
gfx_draw_string_left(dpi, STR_OPTIONS_SCENARIO_GROUPING, NULL, w->colours[1], w->x + 10, w->y + window_options_controls_and_interface_widgets[WIDX_SCENARIO_GROUPING].top + 1);
|
||||||
gfx_draw_string_left_clipped(
|
gfx_draw_string_left_clipped(
|
||||||
dpi,
|
dpi,
|
||||||
gConfigGeneral.scenario_select_mode == 0 ? STR_OPTIONS_SCENARIO_DIFFICULTY : STR_OPTIONS_SCENARIO_ORIGIN,
|
gConfigGeneral.scenario_select_mode == SCENARIO_SELECT_MODE_DIFFICULTY ?
|
||||||
|
STR_OPTIONS_SCENARIO_DIFFICULTY :
|
||||||
|
STR_OPTIONS_SCENARIO_ORIGIN,
|
||||||
NULL,
|
NULL,
|
||||||
w->colours[1],
|
w->colours[1],
|
||||||
w->x + window_options_controls_and_interface_widgets[WIDX_SCENARIO_GROUPING].left + 1,
|
w->x + window_options_controls_and_interface_widgets[WIDX_SCENARIO_GROUPING].left + 1,
|
||||||
|
|||||||
@@ -121,8 +121,7 @@ void window_scenarioselect_open()
|
|||||||
scenario_load_list();
|
scenario_load_list();
|
||||||
|
|
||||||
// Shrink the window if we're showing scenarios by difficulty level.
|
// Shrink the window if we're showing scenarios by difficulty level.
|
||||||
if (gConfigGeneral.scenario_select_mode == 2)
|
if (gConfigGeneral.scenario_select_mode == SCENARIO_SELECT_MODE_DIFFICULTY) {
|
||||||
{
|
|
||||||
window_width = 610;
|
window_width = 610;
|
||||||
window_scenarioselect_widgets[WIDX_BACKGROUND].right = 609;
|
window_scenarioselect_widgets[WIDX_BACKGROUND].right = 609;
|
||||||
window_scenarioselect_widgets[WIDX_TITLEBAR].right = 608;
|
window_scenarioselect_widgets[WIDX_TITLEBAR].right = 608;
|
||||||
@@ -130,9 +129,9 @@ void window_scenarioselect_open()
|
|||||||
window_scenarioselect_widgets[WIDX_CLOSE].right = 607;
|
window_scenarioselect_widgets[WIDX_CLOSE].right = 607;
|
||||||
window_scenarioselect_widgets[WIDX_TABCONTENT].right = 609;
|
window_scenarioselect_widgets[WIDX_TABCONTENT].right = 609;
|
||||||
window_scenarioselect_widgets[WIDX_SCENARIOLIST].right = 433;
|
window_scenarioselect_widgets[WIDX_SCENARIOLIST].right = 433;
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
window_width = 733;
|
window_width = 733;
|
||||||
|
}
|
||||||
|
|
||||||
window = window_create_centred(
|
window = window_create_centred(
|
||||||
window_width,
|
window_width,
|
||||||
@@ -164,15 +163,15 @@ static void window_scenarioselect_init_tabs()
|
|||||||
{
|
{
|
||||||
int show_pages = 0;
|
int show_pages = 0;
|
||||||
for (int i = 0; i < gScenarioListCount; i++) {
|
for (int i = 0; i < gScenarioListCount; i++) {
|
||||||
rct_scenario_basic* scenario = &gScenarioList[i];
|
scenario_index_entry *scenario = &gScenarioList[i];
|
||||||
if (scenario->flags & SCENARIO_FLAGS_VISIBLE)
|
if (scenario->flags & SCENARIO_FLAGS_VISIBLE) {
|
||||||
{
|
if (gConfigGeneral.scenario_select_mode == SCENARIO_SELECT_MODE_ORIGIN) {
|
||||||
if (gConfigGeneral.scenario_select_mode == 1)
|
|
||||||
show_pages |= 1 << scenario->source_game;
|
show_pages |= 1 << scenario->source_game;
|
||||||
else
|
} else {
|
||||||
show_pages |= 1 << scenario->category;
|
show_pages |= 1 << scenario->category;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int x = 3;
|
int x = 3;
|
||||||
for (int i = 0; i < 8; i++) {
|
for (int i = 0; i < 8; i++) {
|
||||||
@@ -212,10 +211,10 @@ static void window_scenarioselect_scrollgetsize(rct_window *w, int scrollIndex,
|
|||||||
{
|
{
|
||||||
*height = 0;
|
*height = 0;
|
||||||
for (int i = 0; i < gScenarioListCount; i++) {
|
for (int i = 0; i < gScenarioListCount; i++) {
|
||||||
rct_scenario_basic *scenario = &gScenarioList[i];
|
scenario_index_entry *scenario = &gScenarioList[i];
|
||||||
|
|
||||||
if ((gConfigGeneral.scenario_select_mode == 1 && scenario->source_game != w->selected_tab) ||
|
if ((gConfigGeneral.scenario_select_mode == SCENARIO_SELECT_MODE_ORIGIN && scenario->source_game != w->selected_tab) ||
|
||||||
(gConfigGeneral.scenario_select_mode == 2 && scenario->category != w->selected_tab))
|
(gConfigGeneral.scenario_select_mode == SCENARIO_SELECT_MODE_DIFFICULTY && scenario->category != w->selected_tab))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (scenario->flags & SCENARIO_FLAGS_VISIBLE)
|
if (scenario->flags & SCENARIO_FLAGS_VISIBLE)
|
||||||
@@ -231,20 +230,20 @@ static void window_scenarioselect_scrollmousedown(rct_window *w, int scrollIndex
|
|||||||
{
|
{
|
||||||
int num_unlocks = 5;
|
int num_unlocks = 5;
|
||||||
for (int i = 0; i < gScenarioListCount; i++) {
|
for (int i = 0; i < gScenarioListCount; i++) {
|
||||||
rct_scenario_basic *scenario = &gScenarioList[i];
|
scenario_index_entry *scenario = &gScenarioList[i];
|
||||||
|
|
||||||
if ((gConfigGeneral.scenario_select_mode == 1 && scenario->source_game != w->selected_tab) ||
|
if ((gConfigGeneral.scenario_select_mode == SCENARIO_SELECT_MODE_ORIGIN && scenario->source_game != w->selected_tab) ||
|
||||||
(gConfigGeneral.scenario_select_mode == 2 && scenario->category != w->selected_tab))
|
(gConfigGeneral.scenario_select_mode == SCENARIO_SELECT_MODE_DIFFICULTY && scenario->category != w->selected_tab))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (!(scenario->flags & SCENARIO_FLAGS_VISIBLE))
|
if (!(scenario->flags & SCENARIO_FLAGS_VISIBLE))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (gConfigGeneral.scenario_unlocking_enabled) {
|
if (gConfigGeneral.scenario_select_mode == SCENARIO_SELECT_MODE_ORIGIN && gConfigGeneral.scenario_unlocking_enabled) {
|
||||||
if (num_unlocks <= 0)
|
if (num_unlocks <= 0)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
bool is_completed = scenario->flags & SCENARIO_FLAGS_COMPLETED;
|
bool is_completed = scenario->highscore != NULL;
|
||||||
if (is_completed) {
|
if (is_completed) {
|
||||||
num_unlocks++;
|
num_unlocks++;
|
||||||
} else {
|
} else {
|
||||||
@@ -257,7 +256,7 @@ static void window_scenarioselect_scrollmousedown(rct_window *w, int scrollIndex
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
audio_play_sound_panned(SOUND_CLICK_1, w->width / 2 + w->x, 0, 0, 0);
|
audio_play_sound_panned(SOUND_CLICK_1, w->width / 2 + w->x, 0, 0, 0);
|
||||||
scenario_load_and_play(scenario);
|
scenario_load_and_play_from_path(scenario->path);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -268,22 +267,22 @@ static void window_scenarioselect_scrollmousedown(rct_window *w, int scrollIndex
|
|||||||
*/
|
*/
|
||||||
static void window_scenarioselect_scrollmouseover(rct_window *w, int scrollIndex, int x, int y)
|
static void window_scenarioselect_scrollmouseover(rct_window *w, int scrollIndex, int x, int y)
|
||||||
{
|
{
|
||||||
rct_scenario_basic *selected = NULL;
|
scenario_index_entry *selected = NULL;
|
||||||
int num_unlocks = 5;
|
int num_unlocks = 5;
|
||||||
for (int i = 0; i < gScenarioListCount; i++) {
|
for (int i = 0; i < gScenarioListCount; i++) {
|
||||||
rct_scenario_basic *scenario = &gScenarioList[i];
|
scenario_index_entry *scenario = &gScenarioList[i];
|
||||||
if ((gConfigGeneral.scenario_select_mode == 1 && scenario->source_game != w->selected_tab) ||
|
if ((gConfigGeneral.scenario_select_mode == SCENARIO_SELECT_MODE_ORIGIN && scenario->source_game != w->selected_tab) ||
|
||||||
(gConfigGeneral.scenario_select_mode == 2 && scenario->category != w->selected_tab))
|
(gConfigGeneral.scenario_select_mode == SCENARIO_SELECT_MODE_DIFFICULTY && scenario->category != w->selected_tab))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (!(scenario->flags & SCENARIO_FLAGS_VISIBLE))
|
if (!(scenario->flags & SCENARIO_FLAGS_VISIBLE))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (gConfigGeneral.scenario_unlocking_enabled) {
|
if (gConfigGeneral.scenario_select_mode == SCENARIO_SELECT_MODE_ORIGIN && gConfigGeneral.scenario_unlocking_enabled) {
|
||||||
if (num_unlocks <= 0)
|
if (num_unlocks <= 0)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
bool is_completed = scenario->flags & SCENARIO_FLAGS_COMPLETED;
|
bool is_completed = scenario->highscore != NULL;
|
||||||
num_unlocks += is_completed ? 1 : -1;
|
num_unlocks += is_completed ? 1 : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -315,7 +314,7 @@ static void window_scenarioselect_paint(rct_window *w, rct_drawpixelinfo *dpi)
|
|||||||
{
|
{
|
||||||
int i, x, y, format;
|
int i, x, y, format;
|
||||||
rct_widget *widget;
|
rct_widget *widget;
|
||||||
rct_scenario_basic *scenario;
|
scenario_index_entry *scenario;
|
||||||
|
|
||||||
window_draw_widgets(w, dpi);
|
window_draw_widgets(w, dpi);
|
||||||
|
|
||||||
@@ -330,7 +329,7 @@ static void window_scenarioselect_paint(rct_window *w, rct_drawpixelinfo *dpi)
|
|||||||
x = (widget->left + widget->right) / 2 + w->x;
|
x = (widget->left + widget->right) / 2 + w->x;
|
||||||
y = (widget->top + widget->bottom) / 2 + w->y - 3;
|
y = (widget->top + widget->bottom) / 2 + w->y - 3;
|
||||||
|
|
||||||
if (gConfigGeneral.scenario_select_mode == 1) {
|
if (gConfigGeneral.scenario_select_mode == SCENARIO_SELECT_MODE_ORIGIN) {
|
||||||
RCT2_GLOBAL(RCT2_ADDRESS_COMMON_FORMAT_ARGS + 0, short) = STR_SCENARIO_CATEGORY_RCT1 + i;
|
RCT2_GLOBAL(RCT2_ADDRESS_COMMON_FORMAT_ARGS + 0, short) = STR_SCENARIO_CATEGORY_RCT1 + i;
|
||||||
} else { // old-style
|
} else { // old-style
|
||||||
RCT2_GLOBAL(RCT2_ADDRESS_COMMON_FORMAT_ARGS + 0, short) = STR_BEGINNER_PARKS + i;
|
RCT2_GLOBAL(RCT2_ADDRESS_COMMON_FORMAT_ARGS + 0, short) = STR_BEGINNER_PARKS + i;
|
||||||
@@ -339,7 +338,7 @@ static void window_scenarioselect_paint(rct_window *w, rct_drawpixelinfo *dpi)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Return if no scenario highlighted
|
// Return if no scenario highlighted
|
||||||
scenario = w->scenario;
|
scenario = (scenario_index_entry*)w->highlighted_item;
|
||||||
if (scenario == NULL)
|
if (scenario == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -364,10 +363,10 @@ static void window_scenarioselect_paint(rct_window *w, rct_drawpixelinfo *dpi)
|
|||||||
y += gfx_draw_string_left_wrapped(dpi, (void*)RCT2_ADDRESS_COMMON_FORMAT_ARGS, x, y, 170, STR_OBJECTIVE, 0) + 5;
|
y += gfx_draw_string_left_wrapped(dpi, (void*)RCT2_ADDRESS_COMMON_FORMAT_ARGS, x, y, 170, STR_OBJECTIVE, 0) + 5;
|
||||||
|
|
||||||
// Scenario score
|
// Scenario score
|
||||||
if (scenario->flags & SCENARIO_FLAGS_COMPLETED) {
|
if (scenario->highscore != NULL) {
|
||||||
safe_strncpy((char*)0x009BC677, scenario->completed_by, 64);
|
safe_strncpy((char*)0x009BC677, scenario->highscore->name, 64);
|
||||||
RCT2_GLOBAL(RCT2_ADDRESS_COMMON_FORMAT_ARGS + 0, short) = 3165; // empty string
|
RCT2_GLOBAL(RCT2_ADDRESS_COMMON_FORMAT_ARGS + 0, short) = 3165; // empty string
|
||||||
RCT2_GLOBAL(RCT2_ADDRESS_COMMON_FORMAT_ARGS + 2, int) = scenario->company_value;
|
RCT2_GLOBAL(RCT2_ADDRESS_COMMON_FORMAT_ARGS + 2, int) = scenario->highscore->company_value;
|
||||||
y += gfx_draw_string_left_wrapped(dpi, (void*)RCT2_ADDRESS_COMMON_FORMAT_ARGS, x, y, 170, STR_COMPLETED_BY_WITH_COMPANY_VALUE, 0);
|
y += gfx_draw_string_left_wrapped(dpi, (void*)RCT2_ADDRESS_COMMON_FORMAT_ARGS, x, y, 170, STR_COMPLETED_BY_WITH_COMPANY_VALUE, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -382,15 +381,15 @@ static void window_scenarioselect_scrollpaint(rct_window *w, rct_drawpixelinfo *
|
|||||||
int unhighlighted_format = (theme_get_preset()->features.rct1_scenario_font) ? 5139 : 1191;
|
int unhighlighted_format = (theme_get_preset()->features.rct1_scenario_font) ? 5139 : 1191;
|
||||||
int disabled_format = 5619;
|
int disabled_format = 5619;
|
||||||
|
|
||||||
bool wide = gConfigGeneral.scenario_select_mode == 1;
|
bool wide = gConfigGeneral.scenario_select_mode == SCENARIO_SELECT_MODE_ORIGIN;
|
||||||
|
|
||||||
int y = 0;
|
int y = 0;
|
||||||
int num_unlocks = 5;
|
int num_unlocks = 5;
|
||||||
for (int i = 0; i < gScenarioListCount; i++) {
|
for (int i = 0; i < gScenarioListCount; i++) {
|
||||||
rct_scenario_basic *scenario = &gScenarioList[i];
|
scenario_index_entry *scenario = &gScenarioList[i];
|
||||||
|
|
||||||
if ((gConfigGeneral.scenario_select_mode == 1 && scenario->source_game != w->selected_tab) ||
|
if ((gConfigGeneral.scenario_select_mode == SCENARIO_SELECT_MODE_ORIGIN && scenario->source_game != w->selected_tab) ||
|
||||||
(gConfigGeneral.scenario_select_mode == 2 && scenario->category != w->selected_tab))
|
(gConfigGeneral.scenario_select_mode == SCENARIO_SELECT_MODE_DIFFICULTY && scenario->category != w->selected_tab))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (!(scenario->flags & SCENARIO_FLAGS_VISIBLE))
|
if (!(scenario->flags & SCENARIO_FLAGS_VISIBLE))
|
||||||
@@ -401,24 +400,26 @@ static void window_scenarioselect_scrollpaint(rct_window *w, rct_drawpixelinfo *
|
|||||||
|
|
||||||
// Draw hover highlight
|
// Draw hover highlight
|
||||||
bool is_highlighted = w->highlighted_item == (int)scenario;
|
bool is_highlighted = w->highlighted_item == (int)scenario;
|
||||||
if (is_highlighted)
|
if (is_highlighted) {
|
||||||
gfx_fill_rect(dpi, 0, y, w->width, y + 23, 0x02000031);
|
gfx_fill_rect(dpi, 0, y, w->width, y + 23, 0x02000031);
|
||||||
|
}
|
||||||
|
|
||||||
bool is_completed = scenario->flags & SCENARIO_FLAGS_COMPLETED;
|
bool is_completed = scenario->highscore != NULL;
|
||||||
bool is_disabled = false;
|
bool is_disabled = false;
|
||||||
if (gConfigGeneral.scenario_unlocking_enabled) {
|
if (gConfigGeneral.scenario_select_mode == SCENARIO_SELECT_MODE_ORIGIN && gConfigGeneral.scenario_unlocking_enabled) {
|
||||||
if (num_unlocks <= 0)
|
if (num_unlocks <= 0) {
|
||||||
is_disabled = true;
|
is_disabled = true;
|
||||||
|
}
|
||||||
num_unlocks += is_completed ? 1 : -1;
|
num_unlocks += is_completed ? 1 : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int format = is_disabled ? 5619 : (is_highlighted ? highlighted_format : unhighlighted_format);
|
|
||||||
|
|
||||||
// Draw scenario name
|
// Draw scenario name
|
||||||
safe_strncpy((char*)0x009BC677, scenario->name, 64);
|
rct_string_id placeholderStringId = 3165;
|
||||||
RCT2_GLOBAL(RCT2_ADDRESS_COMMON_FORMAT_ARGS, short) = 3165;
|
safe_strncpy((char*)language_get_string(placeholderStringId), scenario->name, 64);
|
||||||
gfx_draw_string_centred(dpi, format, wide ? 270 : 210, y + 1, 0, (void*)RCT2_ADDRESS_COMMON_FORMAT_ARGS);
|
int format = is_disabled ? 865 : (is_highlighted ? highlighted_format : unhighlighted_format);
|
||||||
|
colour = is_disabled ? w->colours[1] | 0x40 : COLOUR_BLACK;
|
||||||
|
gfx_draw_string_centred(dpi, format, wide ? 270 : 210, y + 1, colour, &placeholderStringId);
|
||||||
|
|
||||||
// Check if scenario is completed
|
// Check if scenario is completed
|
||||||
if (is_completed) {
|
if (is_completed) {
|
||||||
@@ -426,9 +427,9 @@ static void window_scenarioselect_scrollpaint(rct_window *w, rct_drawpixelinfo *
|
|||||||
gfx_draw_sprite(dpi, 0x5A9F, wide ? 500 : 395, y + 1, 0);
|
gfx_draw_sprite(dpi, 0x5A9F, wide ? 500 : 395, y + 1, 0);
|
||||||
|
|
||||||
// Draw completion score
|
// Draw completion score
|
||||||
safe_strncpy((char*)0x009BC677, scenario->completed_by, 64);
|
safe_strncpy((char*)language_get_string(placeholderStringId), scenario->highscore->name, 64);
|
||||||
RCT2_GLOBAL(RCT2_ADDRESS_COMMON_FORMAT_ARGS, short) = 2793;
|
RCT2_GLOBAL(RCT2_ADDRESS_COMMON_FORMAT_ARGS + 0, rct_string_id) = 2793;
|
||||||
RCT2_GLOBAL(0x013CE954, short) = 3165;
|
RCT2_GLOBAL(RCT2_ADDRESS_COMMON_FORMAT_ARGS + 2, rct_string_id) = placeholderStringId;
|
||||||
gfx_draw_string_centred(dpi, format, wide ? 270 : 210, y + 11, 0, (void*)RCT2_ADDRESS_COMMON_FORMAT_ARGS);
|
gfx_draw_string_centred(dpi, format, wide ? 270 : 210, y + 11, 0, (void*)RCT2_ADDRESS_COMMON_FORMAT_ARGS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -33,11 +33,11 @@ int run_all_tests();
|
|||||||
#include "../src/scenario.h"
|
#include "../src/scenario.h"
|
||||||
|
|
||||||
static void test_load_scenario(CuTest* tc, const char* file_name) {
|
static void test_load_scenario(CuTest* tc, const char* file_name) {
|
||||||
const rct_scenario_basic* scenario = get_scenario_by_filename(file_name);
|
const scenario_index_entry* scenario = scenario_list_find_by_path(file_name);
|
||||||
if (scenario == NULL) {
|
if (scenario == NULL) {
|
||||||
CuFail(tc, "Could not load scenario");
|
CuFail(tc, "Could not load scenario");
|
||||||
}
|
}
|
||||||
scenario_load_and_play(scenario);
|
scenario_load_and_play_from_path(scenario->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
Reference in New Issue
Block a user