From 214bf3988bc40711d610b77d4d90c71a90ba280f Mon Sep 17 00:00:00 2001 From: rwjuk Date: Tue, 6 Jun 2017 14:34:30 +0100 Subject: [PATCH 1/5] Implement 'missing objects' window Implement 'missing objects' window Basic implementation of 'bad objects' window Add new object_load_error.c Add object_load_error.c Faffing about String stuff Stuff Get window basically displaying Proper col header for object Display object types Display file name and explanatory message Probably about time I added myself to the dev list Cleanup and comments Make bad object window work with SC6 Fix whitespace, string IDs, flip core function sense Fix spacing in string_ids.h Fix string ID snafu Fix HasNoInvalidObjects() sense Attempt to refactor this to pass data properly Move typedefs to separate header Fix up signatures Add park_load_result_types.h Clean up includes and remnants of prev implementation Split duplication into function, free invalid entries list on close Use pointer for object_validity_result param Fixup string IDs Use LoadObject() directly Use dependency injection, fix string termination Xcode fix, make helper function static Fix buffer overrun and memory leak Use SDL for clipboard functionality Fix function & variable declarations Rework editor_read_s6() to use new park load result type Update changelog for #5624 [ci skip] Fix mem leak, function signature and whitespace --- OpenRCT2.xcodeproj/project.pbxproj | 8 + contributors.md | 1 + data/language/en-GB.txt | 8 + distribution/changelog.txt | 1 + src/openrct2-ui/UiContext.cpp | 5 + src/openrct2/Context.cpp | 5 + src/openrct2/ParkImporter.h | 17 +- src/openrct2/editor.c | 4 +- src/openrct2/game.c | 25 +- src/openrct2/game.h | 9 +- src/openrct2/interface/window.h | 2 + src/openrct2/localisation/string_ids.h | 9 + src/openrct2/object/ObjectManager.cpp | 53 +++- src/openrct2/object/ObjectManager.h | 20 +- src/openrct2/park_load_result_types.h | 33 +++ src/openrct2/platform/platform.h | 1 + src/openrct2/platform/windows.c | 3 +- src/openrct2/rct1/S4Importer.cpp | 23 +- src/openrct2/rct2/S6Importer.cpp | 89 ++++-- src/openrct2/scenario/scenario.c | 12 +- src/openrct2/scenario/scenario.h | 3 +- src/openrct2/ui/DummyUiContext.cpp | 3 + src/openrct2/ui/UiContext.h | 3 + src/openrct2/windows/object_load_error.c | 328 +++++++++++++++++++++++ 24 files changed, 598 insertions(+), 67 deletions(-) create mode 100644 src/openrct2/park_load_result_types.h create mode 100644 src/openrct2/windows/object_load_error.c diff --git a/OpenRCT2.xcodeproj/project.pbxproj b/OpenRCT2.xcodeproj/project.pbxproj index 2b5311724e..dfc7179f70 100644 --- a/OpenRCT2.xcodeproj/project.pbxproj +++ b/OpenRCT2.xcodeproj/project.pbxproj @@ -471,6 +471,8 @@ F7D774AC1EC6741D00BE6EBC /* language in CopyFiles */ = {isa = PBXBuildFile; fileRef = D4EC48E41C2637710024B507 /* language */; }; F7D774AD1EC6741D00BE6EBC /* shaders in CopyFiles */ = {isa = PBXBuildFile; fileRef = D43407E11D0E14CE00C2B3D4 /* shaders */; }; F7D774AE1EC6741D00BE6EBC /* title in CopyFiles */ = {isa = PBXBuildFile; fileRef = D4EC48E51C2637710024B507 /* title */; }; + F7FFCDAA1F02FD8E0078BFFB /* object_load_error.c in Sources */ = {isa = PBXBuildFile; fileRef = F7FFCDA91F02FD8E0078BFFB /* object_load_error.c */; }; + F7FFCDAC1F02FF570078BFFB /* object_load_error.c in Sources */ = {isa = PBXBuildFile; fileRef = F7FFCDA91F02FD8E0078BFFB /* object_load_error.c */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -1354,6 +1356,8 @@ F7CB864C1EEDA1A80030C877 /* WindowManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WindowManager.h; sourceTree = ""; }; F7D7747E1EC61E5100BE6EBC /* UiContext.macOS.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = UiContext.macOS.mm; sourceTree = ""; usesTabs = 0; }; F7D774841EC66CD700BE6EBC /* OpenRCT2-cli */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "OpenRCT2-cli"; sourceTree = BUILT_PRODUCTS_DIR; }; + F7FFCDA91F02FD8E0078BFFB /* object_load_error.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = object_load_error.c; sourceTree = ""; }; + F7FFCDAB1F02FEC00078BFFB /* park_load_result_types.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = park_load_result_types.h; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -1776,6 +1780,7 @@ F76C84371EC4E7CC00FA49E2 /* object_list.h */, F76C84381EC4E7CC00FA49E2 /* OpenRCT2.cpp */, F76C84391EC4E7CC00FA49E2 /* OpenRCT2.h */, + F7FFCDAB1F02FEC00078BFFB /* park_load_result_types.h */, F76C84511EC4E7CC00FA49E2 /* ParkImporter.cpp */, F76C84521EC4E7CC00FA49E2 /* ParkImporter.h */, F76C84641EC4E7CC00FA49E2 /* PlatformEnvironment.cpp */, @@ -2425,6 +2430,7 @@ F76C85321EC4E7CD00FA49E2 /* new_ride.c */, F76C85331EC4E7CD00FA49E2 /* news.c */, F76C85341EC4E7CD00FA49E2 /* news_options.c */, + F7FFCDA91F02FD8E0078BFFB /* object_load_error.c */, F76C85351EC4E7CD00FA49E2 /* options.c */, F76C85361EC4E7CD00FA49E2 /* park.c */, F76C85371EC4E7CD00FA49E2 /* player.c */, @@ -3014,6 +3020,7 @@ F76C888C1EC5324E00FA49E2 /* UiContext.cpp in Sources */, F76C888D1EC5324E00FA49E2 /* UiContext.Linux.cpp in Sources */, F76C888E1EC5324E00FA49E2 /* UiContext.Win32.cpp in Sources */, + F7FFCDAA1F02FD8E0078BFFB /* object_load_error.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -3021,6 +3028,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + F7FFCDAC1F02FF570078BFFB /* object_load_error.c in Sources */, F7CB864E1EEDA2050030C877 /* DummyWindowManager.cpp in Sources */, F775F5381EE3725C001F00E7 /* DummyAudioContext.cpp in Sources */, F775F5351EE35A89001F00E7 /* DummyUiContext.cpp in Sources */, diff --git a/contributors.md b/contributors.md index 5c906fa9d1..04dcad5afc 100644 --- a/contributors.md +++ b/contributors.md @@ -9,6 +9,7 @@ Includes all git commit authors. Aliases are GitHub user names. * Lewis Fox (LRFLEW) - Developer, macOS management * Marijn van der Werf (marijnvdwerf) - Developer * (zsilencer) - Developer +* Richard Jenkins (rwjuk) - Developer, issue management ## Implementation (RCT2) * Ted John (IntelOrca) diff --git a/data/language/en-GB.txt b/data/language/en-GB.txt index 5fefe5deaa..2126236283 100644 --- a/data/language/en-GB.txt +++ b/data/language/en-GB.txt @@ -4431,6 +4431,14 @@ STR_6119 :A cheap and easy to build roller coaster, but with a limited height STR_6120 :{BABYBLUE}New vehicle now available for {STRINGID}:{NEWLINE}{STRINGID} STR_6121 :{SMALLFONT}{BLACK}Extends the park's land rights all the way to the edges of the map STR_6122 :There are not enough roller coasters in this scenario! +STR_6123 :Error loading objects for park +STR_6124 :Object name +STR_6125 :Object type +STR_6126 :Unknown type +STR_6127 :File: {STRING} +STR_6128 :The file could not be loaded as some of the objects referenced in it are missing or corrupt. A list of these objects is given below. +STR_6129 :Copy selected item to clipboard +STR_6130 :Copy entire list to clipboard ############# # Scenarios # diff --git a/distribution/changelog.txt b/distribution/changelog.txt index edc815b9a4..1f9e5ae6ea 100644 --- a/distribution/changelog.txt +++ b/distribution/changelog.txt @@ -1,5 +1,6 @@ 0.0.8 (in development) ------------------------------------------------------------------------ +- Feature: [#1399 (partial), #5177] Add window that displays any missing/corrupt objects when loading a park - Feature: [#5056] Add cheat to own all land. - Feature: [#5133] Add option to display guest expenditure (as seen in RCTC). - Feature: [#5196] Add cheat to disable ride ageing. diff --git a/src/openrct2-ui/UiContext.cpp b/src/openrct2-ui/UiContext.cpp index 988feb218d..b12aea7dac 100644 --- a/src/openrct2-ui/UiContext.cpp +++ b/src/openrct2-ui/UiContext.cpp @@ -639,6 +639,11 @@ public: } } + bool SetClipboardText(const utf8* target) override + { + return (SDL_SetClipboardText(target) == 0); + } + private: void OnResize(sint32 width, sint32 height) diff --git a/src/openrct2/Context.cpp b/src/openrct2/Context.cpp index ea7da2c897..d067bd9791 100644 --- a/src/openrct2/Context.cpp +++ b/src/openrct2/Context.cpp @@ -721,4 +721,9 @@ extern "C" return nullptr; } } + + bool platform_place_string_on_clipboard(utf8* target) + { + return GetContext()->GetUiContext()->SetClipboardText(target); + } } diff --git a/src/openrct2/ParkImporter.h b/src/openrct2/ParkImporter.h index bbce6a92b9..f66a062c1a 100644 --- a/src/openrct2/ParkImporter.h +++ b/src/openrct2/ParkImporter.h @@ -18,6 +18,15 @@ #include "common.h" +#ifdef __cplusplus +extern "C" +{ +#endif +#include "park_load_result_types.h" +#ifdef __cplusplus +} +#endif + #ifdef __cplusplus #include @@ -34,10 +43,10 @@ interface IParkImporter { public: virtual ~IParkImporter() = default; - virtual void Load(const utf8 * path) abstract; - virtual void LoadSavedGame(const utf8 * path) abstract; - virtual void LoadScenario(const utf8 * path) abstract; - virtual void LoadFromStream(IStream * stream, bool isScenario) abstract; + virtual park_load_result * Load(const utf8 * path) abstract; + virtual park_load_result * LoadSavedGame(const utf8 * path) abstract; + virtual park_load_result * LoadScenario(const utf8 * path) abstract; + virtual park_load_result * LoadFromStream(IStream * stream, bool isScenario) abstract; virtual void Import() abstract; virtual bool GetDetails(scenario_index_entry * dst) abstract; }; diff --git a/src/openrct2/editor.c b/src/openrct2/editor.c index 140f30aa4f..b9ab7e7f02 100644 --- a/src/openrct2/editor.c +++ b/src/openrct2/editor.c @@ -261,14 +261,14 @@ static sint32 editor_load_landscape_from_sc4(const char *path) */ static sint32 editor_read_s6(const char *path) { - bool loadResult = false; + park_load_result* loadResult = { 0 }; const char *extension = path_get_extension(path); if (_stricmp(extension, ".sc6") == 0) { loadResult = scenario_load(path); } else if (_stricmp(extension, ".sv6") == 0) { loadResult = game_load_sv6_path(path); } - if (!loadResult) { + if (loadResult->error != PARK_LOAD_ERROR_NONE) { return 0; } diff --git a/src/openrct2/game.c b/src/openrct2/game.c index 5e1ff8993a..4c712f40c2 100644 --- a/src/openrct2/game.c +++ b/src/openrct2/game.c @@ -1094,18 +1094,20 @@ bool game_load_save(const utf8 *path) safe_strcpy(gScenarioSavePath, path, MAX_PATH); uint32 extension_type = get_file_extension_type(path); - bool result = false; + park_load_result* result = {0}; + bool load_success = false; if (extension_type == FILE_EXTENSION_SV6) { result = game_load_sv6_path(path); - if (result) + load_success = (result->error == PARK_LOAD_ERROR_NONE); + if (load_success) gFirstTimeSaving = false; } else if (extension_type == FILE_EXTENSION_SV4) { - result = rct1_load_saved_game(path); - if (result) + load_success = rct1_load_saved_game(path); + if (load_success) gFirstTimeSaving = true; } - if (result) { + if (load_success) { if (network_get_mode() == NETWORK_MODE_CLIENT) { network_close(); } @@ -1120,9 +1122,16 @@ bool game_load_save(const utf8 *path) return true; } else { - // If loading the SV6 or SV4 failed, the current park state will be corrupted - // so just go back to the title screen. - title_load(); + if (result->error == PARK_LOAD_ERROR_BAD_OBJECTS) + { + // The path needs to be duplicated as it's a const here + // which the window function doesn't like + window_object_load_error_open(strndup(path, strnlen(path, MAX_PATH)), result->object_validity); + } else { + // If loading the SV6 or SV4 failed for a reason other than invalid objects + // the current park state will be corrupted so just go back to the title screen. + title_load(); + } return false; } } diff --git a/src/openrct2/game.h b/src/openrct2/game.h index 1f205694b7..74986880e3 100644 --- a/src/openrct2/game.h +++ b/src/openrct2/game.h @@ -20,6 +20,7 @@ #include "rct2/addresses.h" #include "common.h" #include "scenario/scenario.h" +#include "park_load_result_types.h" enum GAME_COMMAND { GAME_COMMAND_SET_RIDE_APPEARANCE, @@ -120,6 +121,12 @@ enum { ERROR_TYPE_FILE_LOAD = 255 }; +enum PARK_LOAD_ERROR { + PARK_LOAD_ERROR_NONE, + PARK_LOAD_ERROR_BAD_OBJECTS, + PARK_LOAD_ERROR_UNKNOWN = 255 +}; + typedef void (GAME_COMMAND_POINTER)(sint32* eax, sint32* ebx, sint32* ecx, sint32* edx, sint32* esi, sint32* edi, sint32* ebp); typedef void (GAME_COMMAND_CALLBACK_POINTER)(sint32 eax, sint32 ebx, sint32 ecx, sint32 edx, sint32 esi, sint32 edi, sint32 ebp); @@ -170,7 +177,7 @@ sint32 game_do_command_p(sint32 command, sint32 *eax, sint32 *ebx, sint32 *ecx, void game_log_multiplayer_command(int command, int *eax, int* ebx, int* ecx, int* edx, int* edi, int* ebp); void game_load_or_quit_no_save_prompt(); -bool game_load_sv6_path(const char * path); +park_load_result * game_load_sv6_path(const char * path); bool game_load_save(const utf8 *path); void game_load_init(); void game_pause_toggle(sint32 *eax, sint32 *ebx, sint32 *ecx, sint32 *edx, sint32 *esi, sint32 *edi, sint32 *ebp); diff --git a/src/openrct2/interface/window.h b/src/openrct2/interface/window.h index f72def0a68..72525e131b 100644 --- a/src/openrct2/interface/window.h +++ b/src/openrct2/interface/window.h @@ -477,6 +477,7 @@ enum { WC_CUSTOM_CURRENCY_CONFIG = 129, WC_DEBUG_PAINT = 130, WC_VIEW_CLIPPING = 131, + WC_OBJECT_LOAD_ERROR = 132, // Only used for colour schemes WC_STAFF = 220, @@ -766,6 +767,7 @@ rct_window *window_mapgen_open(); rct_window *window_loadsave_open(sint32 type, char *defaultName); rct_window *window_changelog_open(); void window_debug_paint_open(); +rct_window *window_object_load_error_open(utf8* path, object_validity_result* result); rct_window * window_editor_main_open(); void window_editor_bottom_toolbar_open(); diff --git a/src/openrct2/localisation/string_ids.h b/src/openrct2/localisation/string_ids.h index 54984fa6da..21048584fe 100644 --- a/src/openrct2/localisation/string_ids.h +++ b/src/openrct2/localisation/string_ids.h @@ -3791,6 +3791,15 @@ enum { STR_NOT_ENOUGH_ROLLER_COASTERS = 6122, + STR_OBJECT_LOAD_ERROR_TITLE = 6123, + STR_OBJECT_NAME = 6124, + STR_OBJECT_TYPE = 6125, + STR_UNKNOWN_OBJECT_TYPE = 6126, + STR_OBJECT_ERROR_WINDOW_FILE = 6127, + STR_OBJECT_ERROR_WINDOW_EXPLANATION = 6128, + STR_COPY_SELECTED = 6129, + STR_COPY_ALL = 6130, + // Have to include resource strings (from scenarios and objects) for the time being now that language is partially working STR_COUNT = 32768 }; diff --git a/src/openrct2/object/ObjectManager.cpp b/src/openrct2/object/ObjectManager.cpp index 9672c7b6fb..b4e77135e4 100644 --- a/src/openrct2/object/ObjectManager.cpp +++ b/src/openrct2/object/ObjectManager.cpp @@ -36,8 +36,8 @@ extern "C" class ObjectManager final : public IObjectManager { private: - IObjectRepository * _objectRepository; - Object * * _loadedObjects = nullptr; + IObjectRepository * _objectRepository; + Object * * _loadedObjects = nullptr; public: explicit ObjectManager(IObjectRepository * objectRepository) @@ -438,6 +438,51 @@ private: return entryIndex; } + rct_object_entry* DuplicateObjectEntry(const rct_object_entry* original) + { + rct_object_entry * duplicate = Memory::Allocate(sizeof(rct_object_entry)); + duplicate->checksum = original->checksum; + strncpy(duplicate->name, original->name, 8); + duplicate->flags = original->flags; + return duplicate; + } + + object_validity_result* GetInvalidObjects(const rct_object_entry * entries) override + { + uint16 invalidObjectCount = 0; + rct_object_entry * * invalidEntries = Memory::AllocateArray(OBJECT_ENTRY_COUNT); + for (sint32 i = 0; i < OBJECT_ENTRY_COUNT; i++) + { + const rct_object_entry * entry = &entries[i]; + const ObjectRepositoryItem * ori = nullptr; + if (!object_entry_is_empty(entry)) + { + ori = _objectRepository->FindObject(entry); + if (ori == nullptr) + { + invalidEntries[invalidObjectCount++] = DuplicateObjectEntry(entry); + } + else + { + Object * loadedObject = nullptr; + loadedObject = ori->LoadedObject; + if (loadedObject == nullptr) + { + loadedObject = _objectRepository->LoadObject(ori); + if (loadedObject == nullptr) + { + invalidEntries[invalidObjectCount++] = DuplicateObjectEntry(entry); + } + } + } + } + } + object_validity_result* result = Memory::Allocate(sizeof(object_validity_result)); + result->invalid_object_count = invalidObjectCount; + result->invalid_objects = invalidEntries; + return result; + } + bool GetRequiredObjects(const rct_object_entry * entries, const ObjectRepositoryItem * * requiredObjects, size_t * outNumRequiredObjects) @@ -519,14 +564,14 @@ private: return loadedObject; } - static void ReportMissingObject(const rct_object_entry * entry) + void ReportMissingObject(const rct_object_entry * entry) { utf8 objName[9] = { 0 }; Memory::Copy(objName, entry->name, 8); Console::Error::WriteLine("[%s] Object not found.", objName); } - static void ReportObjectLoadProblem(const rct_object_entry * entry) + void ReportObjectLoadProblem(const rct_object_entry * entry) { utf8 objName[9] = { 0 }; Memory::Copy(objName, entry->name, 8); diff --git a/src/openrct2/object/ObjectManager.h b/src/openrct2/object/ObjectManager.h index 2afb049203..d93f5bcda3 100644 --- a/src/openrct2/object/ObjectManager.h +++ b/src/openrct2/object/ObjectManager.h @@ -27,6 +27,7 @@ extern "C" { #endif #include "../object.h" + #include "../park_load_result_types.h" #ifdef __cplusplus } #endif @@ -41,9 +42,10 @@ interface IObjectManager { virtual ~IObjectManager() { } - virtual Object * GetLoadedObject(size_t index) abstract; - virtual Object * GetLoadedObject(const rct_object_entry * entry) abstract; - virtual uint8 GetLoadedObjectEntryIndex(const Object * object) abstract; + virtual Object * GetLoadedObject(size_t index) abstract; + virtual Object * GetLoadedObject(const rct_object_entry * entry) abstract; + virtual uint8 GetLoadedObjectEntryIndex(const Object * object) abstract; + virtual object_validity_result* GetInvalidObjects(const rct_object_entry * entries) abstract; virtual Object * LoadObject(const rct_object_entry * entry) abstract; virtual bool LoadObjects(const rct_object_entry * entries, size_t count) abstract; @@ -65,12 +67,12 @@ extern "C" { #endif -void * object_manager_get_loaded_object_by_index(size_t index); -void * object_manager_get_loaded_object(const rct_object_entry * entry); -uint8 object_manager_get_loaded_object_entry_index(const void * loadedObject); -void * object_manager_load_object(const rct_object_entry * entry); -void object_manager_unload_objects(const rct_object_entry * entries, size_t count); -void object_manager_unload_all_objects(); +void * object_manager_get_loaded_object_by_index(size_t index); +void * object_manager_get_loaded_object(const rct_object_entry * entry); +uint8 object_manager_get_loaded_object_entry_index(const void * loadedObject); +void * object_manager_load_object(const rct_object_entry * entry); +void object_manager_unload_objects(const rct_object_entry * entries, size_t count); +void object_manager_unload_all_objects(); #ifdef __cplusplus } diff --git a/src/openrct2/park_load_result_types.h b/src/openrct2/park_load_result_types.h new file mode 100644 index 0000000000..29e3f7c67f --- /dev/null +++ b/src/openrct2/park_load_result_types.h @@ -0,0 +1,33 @@ +#pragma region Copyright (c) 2014-2017 OpenRCT2 Developers +/***************************************************************************** +* OpenRCT2, an open source clone of Roller Coaster Tycoon 2. +* +* OpenRCT2 is the work of many authors, a full list can be found in contributors.md +* For more information, visit https://github.com/OpenRCT2/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. +* +* A full copy of the GNU General Public License can be found in licence.txt +*****************************************************************************/ +#pragma endregion + +#ifndef _PARK_LOAD_RESULT_H_ +#define _PARK_LOAD_RESULT_H_ + +#include "object.h" + +typedef struct object_validity_result +{ + uint16 invalid_object_count; + rct_object_entry * * invalid_objects; + +} object_validity_result; + +typedef struct park_load_result { + uint8 error; + object_validity_result* object_validity; +} park_load_result; +#endif diff --git a/src/openrct2/platform/platform.h b/src/openrct2/platform/platform.h index dd174d9d6c..e0c82ff805 100644 --- a/src/openrct2/platform/platform.h +++ b/src/openrct2/platform/platform.h @@ -109,6 +109,7 @@ sint32 platform_enumerate_directories_begin(const utf8 *directory); bool platform_enumerate_directories_next(sint32 handle, utf8 *path); void platform_enumerate_directories_end(sint32 handle); void platform_init_window_icon(); +bool platform_place_string_on_clipboard(utf8* target); // Returns the bitmask of the GetLogicalDrives function for windows, 0 for other systems sint32 platform_get_drives(); diff --git a/src/openrct2/platform/windows.c b/src/openrct2/platform/windows.c index c3a7c88470..da62d39a70 100644 --- a/src/openrct2/platform/windows.c +++ b/src/openrct2/platform/windows.c @@ -804,7 +804,8 @@ datetime64 platform_get_datetime_now_utc() return utcNow; } -utf8* platform_get_username() { +utf8* platform_get_username() +{ static char username[UNLEN + 1]; DWORD usernameLength = UNLEN + 1; diff --git a/src/openrct2/rct1/S4Importer.cpp b/src/openrct2/rct1/S4Importer.cpp index 1d06277299..d4c77d03b5 100644 --- a/src/openrct2/rct1/S4Importer.cpp +++ b/src/openrct2/rct1/S4Importer.cpp @@ -125,16 +125,16 @@ private: uint8 _researchRideTypeUsed[128]; public: - void Load(const utf8 * path) override + park_load_result* Load(const utf8 * path) override { const utf8 * extension = Path::GetExtension(path); if (String::Equals(extension, ".sc4", true)) { - LoadScenario(path); + return LoadScenario(path); } else if (String::Equals(extension, ".sv4", true)) { - LoadSavedGame(path); + return LoadSavedGame(path); } else { @@ -142,22 +142,27 @@ public: } } - void LoadSavedGame(const utf8 * path) override + park_load_result* LoadSavedGame(const utf8 * path) override { auto fs = FileStream(path, FILE_MODE_OPEN); - LoadFromStream(&fs, false); + park_load_result* result = LoadFromStream(&fs, false); _s4Path = path; + return result; } - void LoadScenario(const utf8 * path) override + park_load_result* LoadScenario(const utf8 * path) override { auto fs = FileStream(path, FILE_MODE_OPEN); - LoadFromStream(&fs, true); + park_load_result* result = LoadFromStream(&fs, true); _s4Path = path; + return result; } - void LoadFromStream(IStream * stream, bool isScenario) override + park_load_result* LoadFromStream(IStream * stream, bool isScenario) override { + park_load_result* result = Memory::Allocate(sizeof(park_load_result)); + result->error = PARK_LOAD_ERROR_UNKNOWN; + size_t dataSize = stream->GetLength() - stream->GetPosition(); std::unique_ptr data = std::unique_ptr(stream->ReadArray(dataSize)); std::unique_ptr decodedData = std::unique_ptr(Memory::Allocate(sizeof(rct1_s4))); @@ -182,6 +187,8 @@ public: { throw Exception("Unable to decode park."); } + result->error = PARK_LOAD_ERROR_NONE; + return result; } void Import() override diff --git a/src/openrct2/rct2/S6Importer.cpp b/src/openrct2/rct2/S6Importer.cpp index e98599ad32..9ce3c36827 100644 --- a/src/openrct2/rct2/S6Importer.cpp +++ b/src/openrct2/rct2/S6Importer.cpp @@ -24,6 +24,7 @@ #include "../network/network.h" #include "../object/ObjectManager.h" #include "../object/ObjectRepository.h" +#include "../object/ObjectManager.h" #include "../ParkImporter.h" #include "../rct12/SawyerChunkReader.h" #include "../rct12/SawyerEncoding.h" @@ -80,16 +81,16 @@ public: Memory::Set(&_s6, 0, sizeof(_s6)); } - void Load(const utf8 * path) override + park_load_result* Load(const utf8 * path) override { const utf8 * extension = Path::GetExtension(path); if (String::Equals(extension, ".sc6", true)) { - LoadScenario(path); + return LoadScenario(path); } else if (String::Equals(extension, ".sv6", true)) { - LoadSavedGame(path); + return LoadSavedGame(path); } else { @@ -97,22 +98,29 @@ public: } } - void LoadSavedGame(const utf8 * path) override + park_load_result* LoadSavedGame(const utf8 * path) override { auto fs = FileStream(path, FILE_MODE_OPEN); - LoadFromStream(&fs, false); + park_load_result* result = LoadFromStream(&fs, false); _s6Path = path; + + return result; } - void LoadScenario(const utf8 * path) override + park_load_result* LoadScenario(const utf8 * path) override { auto fs = FileStream(path, FILE_MODE_OPEN); - LoadFromStream(&fs, true); + park_load_result* result = LoadFromStream(&fs, true); _s6Path = path; + + return result; } - void LoadFromStream(IStream * stream, bool isScenario) override + park_load_result* LoadFromStream(IStream * stream, bool isScenario) override { + park_load_result* result = Memory::Allocate(sizeof(park_load_result)); + result->error = PARK_LOAD_ERROR_UNKNOWN; + if (isScenario && !gConfigGeneral.allow_loading_with_incorrect_checksum && !SawyerEncoding::ValidateChecksum(stream)) { throw IOException("Invalid checksum."); @@ -166,6 +174,19 @@ public: chunkReader.ReadChunk(&_s6.map_elements, sizeof(_s6.map_elements)); chunkReader.ReadChunk(&_s6.next_free_map_element_pointer_index, 3048816); } + + object_validity_result* object_result = _objectManager->GetInvalidObjects(_s6.objects); + + result->object_validity = object_result; + if (object_result->invalid_object_count > 0) + { + result->error = PARK_LOAD_ERROR_BAD_OBJECTS; + } + else + { + result->error = PARK_LOAD_ERROR_NONE; + } + return result; } bool GetDetails(scenario_index_entry * dst) override @@ -418,23 +439,28 @@ IParkImporter * ParkImporter::CreateS6(IObjectRepository * objectRepository, IOb extern "C" { - bool game_load_sv6_path(const char * path) + park_load_result* game_load_sv6_path(const char * path) { - bool result = false; + park_load_result* result = {}; auto s6Importer = new S6Importer(GetObjectRepository(), GetObjectManager()); try { - s6Importer->LoadSavedGame(path); - s6Importer->Import(); + result = s6Importer->LoadSavedGame(path); - game_fix_save_vars(); - sprite_position_tween_reset(); - result = true; + // We mustn't import if there's something + // wrong with the park data + if (result->error == PARK_LOAD_ERROR_NONE) + { + s6Importer->Import(); + + game_fix_save_vars(); + sprite_position_tween_reset(); + } } catch (const ObjectLoadException &) { gErrorType = ERROR_TYPE_FILE_LOAD; - gErrorStringId = STR_GAME_SAVE_FAILED; + gErrorStringId = STR_FILE_CONTAINS_INVALID_DATA; } catch (const IOException &) { @@ -448,8 +474,11 @@ extern "C" } delete s6Importer; - gScreenAge = 0; - gLastAutoSaveUpdate = AUTOSAVE_PAUSE; + if (result->error == PARK_LOAD_ERROR_NONE) + { + gScreenAge = 0; + gLastAutoSaveUpdate = AUTOSAVE_PAUSE; + } return result; } @@ -458,18 +487,20 @@ extern "C" * rct2: 0x00676053 * scenario (ebx) */ - sint32 scenario_load(const char * path) + park_load_result *scenario_load(const char * path) { - bool result = false; + park_load_result* result = {}; auto s6Importer = new S6Importer(GetObjectRepository(), GetObjectManager()); try { - s6Importer->LoadScenario(path); - s6Importer->Import(); + result = s6Importer->LoadScenario(path); + if (result->error != PARK_LOAD_ERROR_NONE) + { + s6Importer->Import(); - game_fix_save_vars(); - sprite_position_tween_reset(); - result = true; + game_fix_save_vars(); + sprite_position_tween_reset(); + } } catch (const ObjectLoadException &) { @@ -487,9 +518,11 @@ extern "C" gErrorStringId = STR_FILE_CONTAINS_INVALID_DATA; } delete s6Importer; - - gScreenAge = 0; - gLastAutoSaveUpdate = AUTOSAVE_PAUSE; + if (result->error != PARK_LOAD_ERROR_NONE) + { + gScreenAge = 0; + gLastAutoSaveUpdate = AUTOSAVE_PAUSE; + } return result; } } diff --git a/src/openrct2/scenario/scenario.c b/src/openrct2/scenario/scenario.c index 939fec7261..20a4b8abc3 100644 --- a/src/openrct2/scenario/scenario.c +++ b/src/openrct2/scenario/scenario.c @@ -93,9 +93,19 @@ sint32 scenario_load_and_play_from_path(const char *path) window_close_construction_windows(); uint32 extension = get_file_extension_type(path); + park_load_result* result; if (extension == FILE_EXTENSION_SC6) { - if (!scenario_load(path)) + result = scenario_load(path); + if (result->error != PARK_LOAD_ERROR_NONE) + { + if (result->error == PARK_LOAD_ERROR_BAD_OBJECTS) + { + // The path needs to be duplicated as it's a const here + // which the window function doesn't like + window_object_load_error_open(strndup(path, strnlen(path, MAX_PATH)), result->object_validity); + } return 0; + } } else if (extension == FILE_EXTENSION_SC4) { if (!rct1_load_scenario(path)) diff --git a/src/openrct2/scenario/scenario.h b/src/openrct2/scenario/scenario.h index fa76197933..a2000c9bf9 100644 --- a/src/openrct2/scenario/scenario.h +++ b/src/openrct2/scenario/scenario.h @@ -21,6 +21,7 @@ #include "../management/finance.h" #include "../management/research.h" #include "../object.h" +#include "../park_load_result_types.h" #include "../rct12.h" #include "../rct2.h" #include "../rct2/addresses.h" @@ -389,7 +390,7 @@ extern uint32 gLastAutoSaveUpdate; extern const char *_scenarioFileName; -sint32 scenario_load(const char *path); +park_load_result *scenario_load(const char *path); sint32 scenario_load_and_play_from_path(const char *path); void scenario_begin(); void scenario_update(); diff --git a/src/openrct2/ui/DummyUiContext.cpp b/src/openrct2/ui/DummyUiContext.cpp index 906bf0f6aa..3b3dab1d67 100644 --- a/src/openrct2/ui/DummyUiContext.cpp +++ b/src/openrct2/ui/DummyUiContext.cpp @@ -79,6 +79,9 @@ namespace OpenRCT2 { namespace Ui // Misc bool ReadBMP(void * * outPixels, uint32 * outWidth, uint32 * outHeight, const std::string &path) override { return false; } + + // Clipboard + bool SetClipboardText(const utf8* target) override { return false; } }; IUiContext * CreateDummyUiContext() diff --git a/src/openrct2/ui/UiContext.h b/src/openrct2/ui/UiContext.h index 7ebf92ec35..49fe1ec225 100644 --- a/src/openrct2/ui/UiContext.h +++ b/src/openrct2/ui/UiContext.h @@ -136,6 +136,9 @@ namespace OpenRCT2 // HACK: This should either be implemented ourselves in libopenrct2 // or the mapgen height map code is moved to libopenrct2ui. virtual bool ReadBMP(void * * outPixels, uint32 * outWidth, uint32 * outHeight, const std::string &path) abstract; + + // Clipboard + virtual bool SetClipboardText(const utf8* target) abstract; }; IUiContext * CreateDummyUiContext(); diff --git a/src/openrct2/windows/object_load_error.c b/src/openrct2/windows/object_load_error.c new file mode 100644 index 0000000000..659c368cc7 --- /dev/null +++ b/src/openrct2/windows/object_load_error.c @@ -0,0 +1,328 @@ +#pragma region Copyright (c) 2014-2017 OpenRCT2 Developers +/***************************************************************************** + * OpenRCT2, an open source clone of Roller Coaster Tycoon 2. + * + * OpenRCT2 is the work of many authors, a full list can be found in contributors.md + * For more information, visit https://github.com/OpenRCT2/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. + * + * A full copy of the GNU General Public License can be found in licence.txt + *****************************************************************************/ +#pragma endregion + +#include "../interface/widget.h" +#include "../interface/window.h" +#include "../localisation/localisation.h" +#include "../object/ObjectManager.h" +#include "../object.h" +#include "../platform/platform.h" +#include "../sprites.h" +#include "../util/util.h" + +enum WINDOW_OBJECT_LOAD_ERROR_WIDGET_IDX { + WIDX_BACKGROUND, + WIDX_TITLE, + WIDX_CLOSE, + WIDX_COLUMN_OBJECT_NAME, + WIDX_COLUMN_OBJECT_TYPE, + WIDX_SCROLL, + WIDX_COPY_CURRENT, + WIDX_COPY_ALL +}; + +#define WW 400 +#define WH 400 +#define LIST_ITEM_HEIGHT 10 + +rct_widget window_object_load_error_widgets[] = { + { WWT_FRAME, 0, 0, WW - 1, 0, WH - 1, STR_NONE, STR_NONE }, // Background + { WWT_CAPTION, 0, 1, WW - 2, 1, 14, STR_OBJECT_LOAD_ERROR_TITLE, STR_WINDOW_TITLE_TIP }, // Title bar + { WWT_CLOSEBOX, 0, WW - 13, WW - 3, 2, 13, STR_CLOSE_X, STR_CLOSE_WINDOW_TIP }, // Close button + { WWT_13, 0, 4, (WW - 5) / 3, 57, 68, STR_OBJECT_NAME, STR_NONE }, // 'Object name' header + { WWT_13, 0, (WW - 5) / 3 + 1, WW - 5 - 1, 57, 68, STR_OBJECT_TYPE, STR_NONE }, // 'Object type' header + { WWT_SCROLL, 0, 4, WW - 5, 68, WH - 40, SCROLL_VERTICAL, STR_NONE }, // Scrollable list area + { WWT_CLOSEBOX, 0, 20, 200, WW - 32, WW - 12, STR_COPY_SELECTED, STR_NONE }, // Copy selected btn + { WWT_CLOSEBOX, 0, 200, 370, WW - 32, WW - 12, STR_COPY_ALL, STR_NONE }, // Copy all btn + { WIDGETS_END }, +}; + +static rct_string_id get_object_type_string(const rct_object_entry *entry); +static void window_object_load_error_close(rct_window *w); +static void window_object_load_error_update(rct_window *w); +static void window_object_load_error_mouseup(rct_window *w, rct_widgetindex widgetIndex); +static void window_object_load_error_scrollgetsize(rct_window *w, sint32 scrollIndex, sint32 *width, sint32 *height); +static void window_object_load_error_scrollmouseover(rct_window *w, sint32 scrollIndex, sint32 x, sint32 y); +static void window_object_load_error_scrollmousedown(rct_window *w, sint32 scrollIndex, sint32 x, sint32 y); +static void window_object_load_error_paint(rct_window *w, rct_drawpixelinfo *dpi); +static void window_object_load_error_scrollpaint(rct_window *w, rct_drawpixelinfo *dpi, sint32 scrollIndex); + +static rct_window_event_list window_object_load_error_events = { + window_object_load_error_close, + window_object_load_error_mouseup, + NULL, + NULL, + NULL, + NULL, + window_object_load_error_update, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + window_object_load_error_scrollgetsize, + window_object_load_error_scrollmousedown, + NULL, + window_object_load_error_scrollmouseover, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + window_object_load_error_paint, + window_object_load_error_scrollpaint +}; + +rct_object_entry * * invalid_entries = NULL; +sint32 highlighted_index = -1; +utf8* file_path = NULL; + +/** +* Returns an rct_string_id that represents an rct_object_entry's type. +* +* Could possibly be moved out of the window file if other +* uses exist and a suitable location is found. +*/ +static rct_string_id get_object_type_string(const rct_object_entry *entry) +{ + rct_string_id result; + uint8 objectType = entry->flags & 0x0F; + switch (objectType) { + case OBJECT_TYPE_RIDE: + result = STR_OBJECT_SELECTION_RIDE_VEHICLES_ATTRACTIONS; + break; + case OBJECT_TYPE_SMALL_SCENERY: + result = STR_OBJECT_SELECTION_SMALL_SCENERY; + break; + case OBJECT_TYPE_LARGE_SCENERY: + result = STR_OBJECT_SELECTION_LARGE_SCENERY; + break; + case OBJECT_TYPE_WALLS: + result = STR_OBJECT_SELECTION_WALLS_FENCES; + break; + case OBJECT_TYPE_BANNERS: + result = STR_OBJECT_SELECTION_PATH_SIGNS; + break; + case OBJECT_TYPE_PATHS: + result = STR_OBJECT_SELECTION_FOOTPATHS; + break; + case OBJECT_TYPE_PATH_BITS: + result = STR_OBJECT_SELECTION_PATH_EXTRAS; + break; + case OBJECT_TYPE_SCENERY_SETS: + result = STR_OBJECT_SELECTION_SCENERY_GROUPS; + break; + case OBJECT_TYPE_PARK_ENTRANCE: + result = STR_OBJECT_SELECTION_PARK_ENTRANCE; + break; + case OBJECT_TYPE_WATER: + result = STR_OBJECT_SELECTION_WATER; + break; + case OBJECT_TYPE_SCENARIO_TEXT: + result = STR_OBJECT_SELECTION_SCENARIO_DESCRIPTION; + break; + default: + result = STR_UNKNOWN_OBJECT_TYPE; + } + return result; +} + +/** +* Returns a newline-separated string listing all object names. +* Used for placing all names on the clipboard. +*/ +static utf8* combine_object_names(rct_window *w) +{ + if (w->no_list_items > OBJECT_ENTRY_COUNT || w->no_list_items == 0) { + // Something's gone wrong, this shouldn't happen + // We don't want to allocate stupidly large amounts of memory + // for no reason, so bail + return NULL; + } + utf8* buffer; + + // No system has a newline over 2 characters + size_t line_sep_len = strnlen(PLATFORM_NEWLINE, 2); + size_t buffer_len = (w->no_list_items * (8 + line_sep_len)) + 1; + buffer = (utf8*)malloc(buffer_len); + buffer[0] = '\0'; + size_t cur_len = 0; + for (uint16 i = 0; i < w->no_list_items; i++) { + cur_len += (8 + line_sep_len); + assert(cur_len < buffer_len); + strncat(buffer, invalid_entries[i]->name, 8); + strncat(buffer, PLATFORM_NEWLINE, line_sep_len); + } + return buffer; +} + +rct_window *window_object_load_error_open(utf8* path, object_validity_result* result) +{ + rct_window* window; + + invalid_entries = result->invalid_objects; + + // Check if window is already open + window = window_bring_to_front_by_class(WC_OBJECT_LOAD_ERROR); + if (window == NULL) { + window = window_create_centred( + WW, + WH, + &window_object_load_error_events, + WC_OBJECT_LOAD_ERROR, + WF_STICK_TO_FRONT + ); + + window->widgets = window_object_load_error_widgets; + window->enabled_widgets = (1 << WIDX_CLOSE) | (1 << WIDX_COPY_CURRENT) | (1 << WIDX_COPY_ALL); + + window_init_scroll_widgets(window); + window->colours[0] = COLOUR_LIGHT_BLUE; + window->colours[1] = COLOUR_LIGHT_BLUE; + window->colours[2] = COLOUR_LIGHT_BLUE; + } + + // Refresh list items and path + window->no_list_items = result->invalid_object_count; + file_path = path; + + window_invalidate(window); + return window; +} + +static void window_object_load_error_close(rct_window *w) +{ + SafeFree(invalid_entries); +} + +static void window_object_load_error_update(rct_window *w) +{ + // Check if the mouse is hovering over the list + if (!widget_is_highlighted(w, WIDX_SCROLL)) { + highlighted_index = -1; + widget_invalidate(w, WIDX_SCROLL); + } +} + +static void window_object_load_error_mouseup(rct_window *w, rct_widgetindex widgetIndex) +{ + utf8* selected_name; + utf8* combined_list; + switch (widgetIndex) { + case WIDX_CLOSE: + window_close(w); + break; + case WIDX_COPY_CURRENT: + if (w->selected_list_item > -1) { + selected_name = strndup(invalid_entries[w->selected_list_item]->name, 8); + platform_place_string_on_clipboard(selected_name); + SafeFree(selected_name); + } + break; + case WIDX_COPY_ALL: + combined_list = combine_object_names(w); + platform_place_string_on_clipboard(combined_list); + SafeFree(combined_list); + break; + } +} + +static void window_object_load_error_scrollmouseover(rct_window *w, sint32 scrollIndex, sint32 x, sint32 y) +{ + // Highlight item that the cursor is over, or remove highlighting if none + sint32 selected_item; + selected_item = y / 10; + if (selected_item < 0 || selected_item >= w->no_list_items) + highlighted_index = -1; + else + highlighted_index = selected_item; + + widget_invalidate(w, WIDX_SCROLL); +} + +static void window_object_load_error_select_element_from_list(rct_window *w, sint32 index) +{ + if (index < 0 || index > w->no_list_items) { + w->selected_list_item = -1; + } + else { + w->selected_list_item = index; + } +} + +static void window_object_load_error_scrollmousedown(rct_window *w, sint32 scrollIndex, sint32 x, sint32 y) +{ + sint32 selected_item; + selected_item = y / 10; + window_object_load_error_select_element_from_list(w, selected_item); +} + +static void window_object_load_error_scrollgetsize(rct_window *w, sint32 scrollIndex, sint32 *width, sint32 *height) +{ + *height = w->no_list_items * 10; +} + +static void window_object_load_error_paint(rct_window *w, rct_drawpixelinfo *dpi) +{ + window_draw_widgets(w, dpi); + + // Draw explanatory message + set_format_arg(0, rct_string_id, STR_OBJECT_ERROR_WINDOW_EXPLANATION); + gfx_draw_string_left_wrapped(dpi, gCommonFormatArgs, w->x + 5, w->y + 18, WW-10, STR_BLACK_STRING, COLOUR_BLACK); + + // Draw file name + set_format_arg(0, rct_string_id, STR_OBJECT_ERROR_WINDOW_FILE); + set_format_arg(2, utf8*, file_path); + gfx_draw_string_left_clipped(dpi, STR_BLACK_STRING, gCommonFormatArgs, COLOUR_BLACK, w->x + 5, w->y + 43, WW-5); +} + +static void window_object_load_error_scrollpaint(rct_window *w, rct_drawpixelinfo *dpi, sint32 scrollIndex) +{ + + gfx_fill_rect(dpi, dpi->x, dpi->y, dpi->x + dpi->width - 1, dpi->y + dpi->height - 1, ColourMapA[w->colours[1]].mid_light); + const sint32 list_width = w->widgets[WIDX_SCROLL].right - w->widgets[WIDX_SCROLL].left; + + for (sint32 i = 0; i < w->no_list_items; i++) { + sint32 y = i * 10; + if (y > dpi->y + dpi->height) + break; + + if (y + 10 < dpi->y) + continue; + + // If hovering over item, change the color and fill the backdrop. + if (i == w->selected_list_item) // Currently selected element + gfx_fill_rect(dpi, 0, y, list_width, y + LIST_ITEM_HEIGHT - 1, ColourMapA[w->colours[1]].darker | 0x1000000); + else if (i == highlighted_index) // Hovering + gfx_fill_rect(dpi, 0, y, list_width, y + LIST_ITEM_HEIGHT - 1, ColourMapA[w->colours[1]].mid_dark | 0x1000000); + else if ((i & 1) != 0) // odd / even check + gfx_fill_rect(dpi, 0, y, list_width, y + LIST_ITEM_HEIGHT - 1, ColourMapA[w->colours[1]].lighter | 0x1000000); + + // Draw the actual object entry's name... + gfx_draw_string(dpi, strndup(invalid_entries[i]->name, 8), COLOUR_DARK_GREEN, 5, y); + + // ... and type + rct_string_id type = get_object_type_string(invalid_entries[i]); + gfx_draw_string_left(dpi, type, NULL, COLOUR_DARK_GREEN, (WW - 5) / 3 + 1, y); + } + + +} From ab38c07fb909c3c7b5946fab9b8775716fa443bb Mon Sep 17 00:00:00 2001 From: rwjuk Date: Fri, 30 Jun 2017 00:37:41 +0100 Subject: [PATCH 2/5] Make object window work with S4s and scenarios --- src/openrct2/ParkImporter.h | 6 +- src/openrct2/game.c | 16 +-- src/openrct2/game.h | 6 - src/openrct2/park_load_result_types.h | 7 + src/openrct2/rct1.h | 5 +- src/openrct2/rct1/S4Importer.cpp | 140 ++++++++++++++++--- src/openrct2/rct2.c | 7 +- src/openrct2/rct2/S6Importer.cpp | 14 +- src/openrct2/scenario/ScenarioRepository.cpp | 2 +- src/openrct2/scenario/scenario.c | 25 ++-- src/openrct2/scenario/scenario.h | 2 +- src/openrct2/util/util.c | 18 +++ src/openrct2/util/util.h | 2 + src/openrct2/windows/server_start.c | 7 +- src/openrct2/windows/title_menu.c | 6 +- src/openrct2/windows/top_toolbar.c | 5 +- 16 files changed, 195 insertions(+), 73 deletions(-) diff --git a/src/openrct2/ParkImporter.h b/src/openrct2/ParkImporter.h index f66a062c1a..7a0bee4e89 100644 --- a/src/openrct2/ParkImporter.h +++ b/src/openrct2/ParkImporter.h @@ -44,9 +44,9 @@ interface IParkImporter public: virtual ~IParkImporter() = default; virtual park_load_result * Load(const utf8 * path) abstract; - virtual park_load_result * LoadSavedGame(const utf8 * path) abstract; - virtual park_load_result * LoadScenario(const utf8 * path) abstract; - virtual park_load_result * LoadFromStream(IStream * stream, bool isScenario) abstract; + virtual park_load_result * LoadSavedGame(const utf8 * path, bool skipObjectCheck = false) abstract; + virtual park_load_result * LoadScenario(const utf8 * path, bool skipObjectCheck = false) abstract; + virtual park_load_result * LoadFromStream(IStream * stream, bool isScenario, bool skipObjectCheck = false) abstract; virtual void Import() abstract; virtual bool GetDetails(scenario_index_entry * dst) abstract; }; diff --git a/src/openrct2/game.c b/src/openrct2/game.c index 4c712f40c2..c0d347b520 100644 --- a/src/openrct2/game.c +++ b/src/openrct2/game.c @@ -1119,19 +1119,9 @@ bool game_load_save(const utf8 *path) // This ensures that the newly loaded save reflects the user's // 'show real names of guests' option, now that it's a global setting peep_update_names(gConfigGeneral.show_real_names_of_guests); - return true; } else { - if (result->error == PARK_LOAD_ERROR_BAD_OBJECTS) - { - // The path needs to be duplicated as it's a const here - // which the window function doesn't like - window_object_load_error_open(strndup(path, strnlen(path, MAX_PATH)), result->object_validity); - } else { - // If loading the SV6 or SV4 failed for a reason other than invalid objects - // the current park state will be corrupted so just go back to the title screen. - title_load(); - } + handle_park_load_failure(result, path); return false; } } @@ -1372,6 +1362,7 @@ void rct2_exit() bool game_load_save_or_scenario(const utf8 * path) { + park_load_result* result; uint32 extension = get_file_extension_type(path); switch (extension) { case FILE_EXTENSION_SV4: @@ -1379,7 +1370,8 @@ bool game_load_save_or_scenario(const utf8 * path) return game_load_save(path); case FILE_EXTENSION_SC4: case FILE_EXTENSION_SC6: - return scenario_load_and_play_from_path(path); + result = scenario_load_and_play_from_path(path); + return (result->error == PARK_LOAD_ERROR_NONE); } return false; } diff --git a/src/openrct2/game.h b/src/openrct2/game.h index 74986880e3..2e273160fb 100644 --- a/src/openrct2/game.h +++ b/src/openrct2/game.h @@ -121,12 +121,6 @@ enum { ERROR_TYPE_FILE_LOAD = 255 }; -enum PARK_LOAD_ERROR { - PARK_LOAD_ERROR_NONE, - PARK_LOAD_ERROR_BAD_OBJECTS, - PARK_LOAD_ERROR_UNKNOWN = 255 -}; - typedef void (GAME_COMMAND_POINTER)(sint32* eax, sint32* ebx, sint32* ecx, sint32* edx, sint32* esi, sint32* edi, sint32* ebp); typedef void (GAME_COMMAND_CALLBACK_POINTER)(sint32 eax, sint32 ebx, sint32 ecx, sint32 edx, sint32 esi, sint32 edi, sint32 ebp); diff --git a/src/openrct2/park_load_result_types.h b/src/openrct2/park_load_result_types.h index 29e3f7c67f..6e43388c6b 100644 --- a/src/openrct2/park_load_result_types.h +++ b/src/openrct2/park_load_result_types.h @@ -19,6 +19,13 @@ #include "object.h" +enum PARK_LOAD_ERROR { + PARK_LOAD_ERROR_NONE, + PARK_LOAD_ERROR_BAD_OBJECTS, + PARK_LOAD_ERROR_INVALID_EXTENSION, + PARK_LOAD_ERROR_UNKNOWN = 255 +}; + typedef struct object_validity_result { uint16 invalid_object_count; diff --git a/src/openrct2/rct1.h b/src/openrct2/rct1.h index 5d2505f283..0126015adc 100644 --- a/src/openrct2/rct1.h +++ b/src/openrct2/rct1.h @@ -20,6 +20,7 @@ #include "management/award.h" #include "management/news_item.h" #include "management/research.h" +#include "park_load_result_types.h" #include "rct12.h" #include "rct2.h" #include "ride/ride.h" @@ -1209,8 +1210,8 @@ extern const uint8 gRideCategories[RIDE_TYPE_COUNT]; sint32 vehicle_preference_compare(uint8 rideType, const char * a, const char * b); bool rideTypeShouldLoseSeparateFlag(const rct_ride_entry *rideEntry); -bool rct1_load_saved_game(const char *path); -bool rct1_load_scenario(const char *path); +park_load_result * rct1_load_saved_game(const char *path); +park_load_result * rct1_load_scenario(const char *path); colour_t rct1_get_colour(colour_t colour); diff --git a/src/openrct2/rct1/S4Importer.cpp b/src/openrct2/rct1/S4Importer.cpp index d4c77d03b5..93ac2f85a1 100644 --- a/src/openrct2/rct1/S4Importer.cpp +++ b/src/openrct2/rct1/S4Importer.cpp @@ -27,6 +27,7 @@ #include "../core/String.hpp" #include "../core/Util.hpp" #include "../object/ObjectManager.h" +#include "../object/ObjectRepository.h" #include "../ParkImporter.h" #include "../scenario/ScenarioSources.h" #include "Tables.h" @@ -142,23 +143,23 @@ public: } } - park_load_result* LoadSavedGame(const utf8 * path) override + park_load_result* LoadSavedGame(const utf8 * path, bool skipObjectCheck = false) override { auto fs = FileStream(path, FILE_MODE_OPEN); - park_load_result* result = LoadFromStream(&fs, false); + park_load_result* result = LoadFromStream(&fs, false, skipObjectCheck); _s4Path = path; return result; } - park_load_result* LoadScenario(const utf8 * path) override + park_load_result* LoadScenario(const utf8 * path, bool skipObjectCheck = false) override { auto fs = FileStream(path, FILE_MODE_OPEN); - park_load_result* result = LoadFromStream(&fs, true); + park_load_result* result = LoadFromStream(&fs, true, skipObjectCheck); _s4Path = path; return result; } - park_load_result* LoadFromStream(IStream * stream, bool isScenario) override + park_load_result* LoadFromStream(IStream * stream, bool isScenario, bool skipObjectCheck = false) override { park_load_result* result = Memory::Allocate(sizeof(park_load_result)); result->error = PARK_LOAD_ERROR_UNKNOWN; @@ -182,12 +183,28 @@ public: { Memory::Copy(&_s4, decodedData.get(), sizeof(rct1_s4)); _s4Path = ""; + + if (!skipObjectCheck) + { + InitialiseEntryMaps(); + CreateAvailableObjectMappings(); + object_validity_result* object_result = GetInvalidObjects(); + + result->object_validity = object_result; + if (object_result->invalid_object_count > 0) + { + result->error = PARK_LOAD_ERROR_BAD_OBJECTS; + } + } + else + { + result->error = PARK_LOAD_ERROR_NONE; + } } else { throw Exception("Unable to decode park."); } - result->error = PARK_LOAD_ERROR_NONE; return result; } @@ -309,6 +326,7 @@ private: Memory::Set(_pathAdditionTypeToEntryMap, 255, sizeof(_pathAdditionTypeToEntryMap)); Memory::Set(_sceneryThemeTypeToEntryMap, 255, sizeof(_sceneryThemeTypeToEntryMap)); + InitialiseEntryMaps(); uint16 mapSize = _s4.map_size == 0 ? 128 : _s4.map_size; // Do map initialisation, same kind of stuff done when loading scenario editor @@ -319,6 +337,18 @@ private: gS6Info.category = SCENARIO_CATEGORY_OTHER; } + void InitialiseEntryMaps() + { + Memory::Set(_rideTypeToRideEntryMap, 255, sizeof(_rideTypeToRideEntryMap)); + Memory::Set(_vehicleTypeToRideEntryMap, 255, sizeof(_vehicleTypeToRideEntryMap)); + Memory::Set(_smallSceneryTypeToEntryMap, 255, sizeof(_smallSceneryTypeToEntryMap)); + Memory::Set(_largeSceneryTypeToEntryMap, 255, sizeof(_largeSceneryTypeToEntryMap)); + Memory::Set(_wallTypeToEntryMap, 255, sizeof(_wallTypeToEntryMap)); + Memory::Set(_pathTypeToEntryMap, 255, sizeof(_pathTypeToEntryMap)); + Memory::Set(_pathAdditionTypeToEntryMap, 255, sizeof(_pathAdditionTypeToEntryMap)); + Memory::Set(_sceneryThemeTypeToEntryMap, 255, sizeof(_sceneryThemeTypeToEntryMap)); + } + /** * Scans the map and research list for all the object types used and builds lists and * lookup tables for converting from hard coded RCT1 object types to dynamic object entries. @@ -1725,6 +1755,73 @@ private: } } + object_validity_result* GetInvalidObjects() + { + object_validity_result* result = Memory::Allocate(sizeof(object_validity_result)); + uint16 invalidObjectCount = 0; + rct_object_entry * * invalidEntries = Memory::AllocateArray(OBJECT_ENTRY_COUNT); + + result->invalid_object_count = invalidObjectCount; + result->invalid_objects = invalidEntries; + + GetInvalidObjects(OBJECT_TYPE_RIDE, _rideEntries.GetEntries(), *result); + GetInvalidObjects(OBJECT_TYPE_SMALL_SCENERY, _smallSceneryEntries.GetEntries(), *result); + GetInvalidObjects(OBJECT_TYPE_LARGE_SCENERY, _largeSceneryEntries.GetEntries(), *result); + GetInvalidObjects(OBJECT_TYPE_WALLS, _wallEntries.GetEntries(), *result); + GetInvalidObjects(OBJECT_TYPE_PATHS, _pathEntries.GetEntries(), *result); + GetInvalidObjects(OBJECT_TYPE_PATH_BITS, _pathAdditionEntries.GetEntries(), *result); + GetInvalidObjects(OBJECT_TYPE_SCENERY_SETS, _sceneryGroupEntries.GetEntries(), *result); + GetInvalidObjects(OBJECT_TYPE_BANNERS, std::vector({ + "BN1 ", + "BN2 ", + "BN3 ", + "BN4 ", + "BN5 ", + "BN6 ", + "BN7 ", + "BN8 ", + "BN9 " + }), *result); + GetInvalidObjects(OBJECT_TYPE_PARK_ENTRANCE, std::vector({ "PKENT1 " }), *result); + GetInvalidObjects(OBJECT_TYPE_WATER, _waterEntry.GetEntries(), *result); + + return result; + } + + void GetInvalidObjects(uint8 objectType, const std::vector &entries, object_validity_result &result) + { + IObjectRepository * objectRepository = GetObjectRepository(); + for (const char * objectName : entries) + { + rct_object_entry entry; + entry.flags = 0x00008000 + objectType; + Memory::Copy(entry.name, objectName, 8); + entry.checksum = 0; + + const ObjectRepositoryItem * ori = nullptr; + ori = objectRepository->FindObject(&entry); + if (ori == nullptr) + { + rct_object_entry * invalid_entry = Memory::Allocate(sizeof(rct_object_entry)); + invalid_entry->flags = entry.flags; + Memory::Copy(invalid_entry->name, objectName, 8); + result.invalid_objects[result.invalid_object_count++] = invalid_entry; + } + else + { + Object * object = objectRepository->LoadObject(ori); + if (object == nullptr && objectType != OBJECT_TYPE_SCENERY_SETS) + { + rct_object_entry * invalid_entry = Memory::Allocate(sizeof(rct_object_entry)); + invalid_entry->flags = entry.flags; + Memory::Copy(invalid_entry->name, objectName, 8); + result.invalid_objects[result.invalid_object_count++] = invalid_entry; + } + SafeFree(object); + } + } + } + void ImportMapElements() { Memory::Copy(gMapElements, _s4.map_elements, RCT1_MAX_MAP_ELEMENTS * sizeof(rct_map_element)); @@ -2568,37 +2665,44 @@ IParkImporter * ParkImporter::CreateS4() ///////////////////////////////////////// extern "C" { - bool rct1_load_saved_game(const utf8 * path) + park_load_result* rct1_load_saved_game(const utf8 * path) { - bool result; + park_load_result* result = {}; auto s4Importer = new S4Importer(); try { - s4Importer->LoadSavedGame(path); - s4Importer->Import(); - result = true; + result = s4Importer->LoadSavedGame(path); + if (result->error == PARK_LOAD_ERROR_NONE) + { + s4Importer->Import(); + } + } catch (const Exception &) { - result = false; + result = {}; + result->error = PARK_LOAD_ERROR_UNKNOWN; } delete s4Importer; return result; } - bool rct1_load_scenario(const utf8 * path) + park_load_result* rct1_load_scenario(const utf8 * path) { - bool result; + park_load_result* result = {}; auto s4Importer = new S4Importer(); try { - s4Importer->LoadScenario(path); - s4Importer->Import(); - result = true; + result = s4Importer->LoadSavedGame(path); + if (result->error == PARK_LOAD_ERROR_NONE) + { + s4Importer->Import(); + } } catch (const Exception &) { - result = false; + result = {}; + result->error = PARK_LOAD_ERROR_UNKNOWN; } delete s4Importer; return result; diff --git a/src/openrct2/rct2.c b/src/openrct2/rct2.c index 5c48cb7962..89fe7de289 100644 --- a/src/openrct2/rct2.c +++ b/src/openrct2/rct2.c @@ -339,9 +339,14 @@ bool rct2_open_file(const char *path) } } else if (_stricmp(extension, "sc6") == 0) { // TODO scenario install - if (scenario_load_and_play_from_path(path)) { + park_load_result *result = scenario_load_and_play_from_path(path); + if (result->error == PARK_LOAD_ERROR_NONE) { return true; } + else { + handle_park_load_failure(result, path); + return false; + } } else if (_stricmp(extension, "td6") == 0 || _stricmp(extension, "td4") == 0) { // TODO track design install return true; diff --git a/src/openrct2/rct2/S6Importer.cpp b/src/openrct2/rct2/S6Importer.cpp index 9ce3c36827..9ca6083054 100644 --- a/src/openrct2/rct2/S6Importer.cpp +++ b/src/openrct2/rct2/S6Importer.cpp @@ -98,25 +98,25 @@ public: } } - park_load_result* LoadSavedGame(const utf8 * path) override + park_load_result* LoadSavedGame(const utf8 * path, bool skipObjectCheck = false) override { auto fs = FileStream(path, FILE_MODE_OPEN); - park_load_result* result = LoadFromStream(&fs, false); + park_load_result* result = LoadFromStream(&fs, false, skipObjectCheck); _s6Path = path; return result; } - park_load_result* LoadScenario(const utf8 * path) override + park_load_result* LoadScenario(const utf8 * path, bool skipObjectCheck = false) override { auto fs = FileStream(path, FILE_MODE_OPEN); - park_load_result* result = LoadFromStream(&fs, true); + park_load_result* result = LoadFromStream(&fs, true, skipObjectCheck); _s6Path = path; return result; } - park_load_result* LoadFromStream(IStream * stream, bool isScenario) override + park_load_result* LoadFromStream(IStream * stream, bool isScenario, bool skipObjectCheck = false) override { park_load_result* result = Memory::Allocate(sizeof(park_load_result)); result->error = PARK_LOAD_ERROR_UNKNOWN; @@ -487,14 +487,14 @@ extern "C" * rct2: 0x00676053 * scenario (ebx) */ - park_load_result *scenario_load(const char * path) + park_load_result* scenario_load(const char * path) { park_load_result* result = {}; auto s6Importer = new S6Importer(GetObjectRepository(), GetObjectManager()); try { result = s6Importer->LoadScenario(path); - if (result->error != PARK_LOAD_ERROR_NONE) + if (result->error == PARK_LOAD_ERROR_NONE) { s6Importer->Import(); diff --git a/src/openrct2/scenario/ScenarioRepository.cpp b/src/openrct2/scenario/ScenarioRepository.cpp index b0d89666e6..d732fbb413 100644 --- a/src/openrct2/scenario/ScenarioRepository.cpp +++ b/src/openrct2/scenario/ScenarioRepository.cpp @@ -322,7 +322,7 @@ private: try { auto s4Importer = std::unique_ptr(ParkImporter::CreateS4()); - s4Importer->LoadScenario(path.c_str()); + s4Importer->LoadScenario(path.c_str(), true); if (s4Importer->GetDetails(entry)) { String::Set(entry->path, sizeof(entry->path), path.c_str()); diff --git a/src/openrct2/scenario/scenario.c b/src/openrct2/scenario/scenario.c index 20a4b8abc3..17ca3d4677 100644 --- a/src/openrct2/scenario/scenario.c +++ b/src/openrct2/scenario/scenario.c @@ -88,31 +88,29 @@ money32 gScenarioCompanyValueRecord; static sint32 scenario_create_ducks(); static void scenario_objective_check(); -sint32 scenario_load_and_play_from_path(const char *path) +park_load_result* scenario_load_and_play_from_path(const char *path) { window_close_construction_windows(); uint32 extension = get_file_extension_type(path); - park_load_result* result; + park_load_result* result = malloc(sizeof(park_load_result)); if (extension == FILE_EXTENSION_SC6) { result = scenario_load(path); if (result->error != PARK_LOAD_ERROR_NONE) { - if (result->error == PARK_LOAD_ERROR_BAD_OBJECTS) - { - // The path needs to be duplicated as it's a const here - // which the window function doesn't like - window_object_load_error_open(strndup(path, strnlen(path, MAX_PATH)), result->object_validity); - } - return 0; + return result; } } else if (extension == FILE_EXTENSION_SC4) { - if (!rct1_load_scenario(path)) - return 0; + result = rct1_load_scenario(path); + if (result->error != PARK_LOAD_ERROR_NONE) + { + return result; + } } else { - return 0; + result->error = PARK_LOAD_ERROR_INVALID_EXTENSION; + return result; } reset_sprite_spatial_index(); @@ -141,8 +139,7 @@ sint32 scenario_load_and_play_from_path(const char *path) // This ensures that the newly loaded scenario reflects the user's // 'show real names of guests' option, now that it's a global setting peep_update_names(gConfigGeneral.show_real_names_of_guests); - - return 1; + return result; } void scenario_begin() diff --git a/src/openrct2/scenario/scenario.h b/src/openrct2/scenario/scenario.h index a2000c9bf9..13db64199b 100644 --- a/src/openrct2/scenario/scenario.h +++ b/src/openrct2/scenario/scenario.h @@ -391,7 +391,7 @@ extern uint32 gLastAutoSaveUpdate; extern const char *_scenarioFileName; park_load_result *scenario_load(const char *path); -sint32 scenario_load_and_play_from_path(const char *path); +park_load_result *scenario_load_and_play_from_path(const char *path); void scenario_begin(); void scenario_update(); diff --git a/src/openrct2/util/util.c b/src/openrct2/util/util.c index 9955cc9181..26844f5e2d 100644 --- a/src/openrct2/util/util.c +++ b/src/openrct2/util/util.c @@ -18,8 +18,10 @@ #include #include "../common.h" #include "../core/Guard.hpp" +#include "../interface/window.h" #include "../localisation/localisation.h" #include "../platform/platform.h" +#include "../title/TitleScreen.h" #include "util.h" #include "zlib.h" @@ -547,3 +549,19 @@ size_t strcatftime(char * buffer, size_t bufferSize, const char * format, const } return 0; } + +void handle_park_load_failure(park_load_result* result, const utf8* path) +{ + if (result->error == PARK_LOAD_ERROR_BAD_OBJECTS) + { + // The path needs to be duplicated as it's a const here + // which the window function doesn't like + window_object_load_error_open(strndup(path, strnlen(path, MAX_PATH)), result->object_validity); + } + else if (result->error != PARK_LOAD_ERROR_NONE) { + // If loading the SV6 or SV4 failed for a reason other than invalid objects + // the current park state will be corrupted so just go back to the title screen. + title_load(); + } + SafeFree(result); +} \ No newline at end of file diff --git a/src/openrct2/util/util.h b/src/openrct2/util/util.h index 16a1eb64a0..43e0da7261 100644 --- a/src/openrct2/util/util.h +++ b/src/openrct2/util/util.h @@ -19,6 +19,7 @@ #include #include "../common.h" +#include "../park_load_result_types.h" sint32 squaredmetres_to_squaredfeet(sint32 squaredMetres); sint32 metres_to_feet(sint32 metres); @@ -65,4 +66,5 @@ money32 add_clamp_money32(money32 value, money32 value_to_add); size_t strcatftime(char * buffer, size_t bufferSize, const char * format, const struct tm * tp); +void handle_park_load_failure(park_load_result* result, const utf8* path); #endif diff --git a/src/openrct2/windows/server_start.c b/src/openrct2/windows/server_start.c index 5f17dcc2eb..54bac250b5 100644 --- a/src/openrct2/windows/server_start.c +++ b/src/openrct2/windows/server_start.c @@ -21,6 +21,7 @@ #include "../interface/window.h" #include "../localisation/localisation.h" #include "../network/network.h" +#include "../platform/platform.h" #include "../sprites.h" #include "../title/TitleScreen.h" #include "../util/util.h" @@ -164,11 +165,13 @@ static void window_server_start_close(rct_window *w) static void window_server_start_scenarioselect_callback(const utf8 *path) { + park_load_result* result; network_set_password(_password); - if (scenario_load_and_play_from_path(path)) { + result = scenario_load_and_play_from_path(path); + if (result->error == PARK_LOAD_ERROR_NONE) { network_begin_server(gConfigNetwork.default_port, gConfigNetwork.listen_address); } else { - title_load(); + handle_park_load_failure(result, path); } } diff --git a/src/openrct2/windows/title_menu.c b/src/openrct2/windows/title_menu.c index 2b74ec9dac..a722cc8781 100644 --- a/src/openrct2/windows/title_menu.c +++ b/src/openrct2/windows/title_menu.c @@ -24,6 +24,7 @@ #include "../localisation/localisation.h" #include "../sprites.h" #include "../title/TitleScreen.h" +#include "../util/util.h" #include "dropdown.h" enum { @@ -128,9 +129,8 @@ void window_title_menu_open() static void window_title_menu_scenarioselect_callback(const utf8 *path) { - if (!scenario_load_and_play_from_path(path)) { - title_load(); - } + park_load_result *result = scenario_load_and_play_from_path(path); + handle_park_load_failure(result, path); } static void window_title_menu_mouseup(rct_window *w, rct_widgetindex widgetIndex) diff --git a/src/openrct2/windows/top_toolbar.c b/src/openrct2/windows/top_toolbar.c index bc4ee90583..c05751e896 100644 --- a/src/openrct2/windows/top_toolbar.c +++ b/src/openrct2/windows/top_toolbar.c @@ -519,9 +519,8 @@ static void window_top_toolbar_mousedown(rct_widgetindex widgetIndex, rct_window static void window_top_toolbar_scenarioselect_callback(const utf8 *path) { - if (!scenario_load_and_play_from_path(path)) { - title_load(); - } + park_load_result *result = scenario_load_and_play_from_path(path); + handle_park_load_failure(result, path); } /** From c3356d457ec271cb3ed26f333405edf7c4cade5a Mon Sep 17 00:00:00 2001 From: Ted John Date: Fri, 30 Jun 2017 18:30:37 +0100 Subject: [PATCH 3/5] Fix S4s never loading if no object errors --- src/openrct2/rct1/S4Importer.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/openrct2/rct1/S4Importer.cpp b/src/openrct2/rct1/S4Importer.cpp index 93ac2f85a1..0666430295 100644 --- a/src/openrct2/rct1/S4Importer.cpp +++ b/src/openrct2/rct1/S4Importer.cpp @@ -162,7 +162,7 @@ public: park_load_result* LoadFromStream(IStream * stream, bool isScenario, bool skipObjectCheck = false) override { park_load_result* result = Memory::Allocate(sizeof(park_load_result)); - result->error = PARK_LOAD_ERROR_UNKNOWN; + result->error = PARK_LOAD_ERROR_NONE; size_t dataSize = stream->GetLength() - stream->GetPosition(); std::unique_ptr data = std::unique_ptr(stream->ReadArray(dataSize)); @@ -196,10 +196,6 @@ public: result->error = PARK_LOAD_ERROR_BAD_OBJECTS; } } - else - { - result->error = PARK_LOAD_ERROR_NONE; - } } else { From d75295b0272d3ef2fdc91d18aca178d6a4571069 Mon Sep 17 00:00:00 2001 From: Ted John Date: Fri, 30 Jun 2017 19:03:40 +0100 Subject: [PATCH 4/5] Move handle_park_load_failure into game.c --- src/openrct2/game.c | 16 ++++++++++++++++ src/openrct2/game.h | 1 + src/openrct2/util/util.c | 16 ---------------- src/openrct2/util/util.h | 1 - 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/openrct2/game.c b/src/openrct2/game.c index c0d347b520..e180737a03 100644 --- a/src/openrct2/game.c +++ b/src/openrct2/game.c @@ -1126,6 +1126,22 @@ bool game_load_save(const utf8 *path) } } +void handle_park_load_failure(park_load_result* result, const utf8* path) +{ + if (result->error == PARK_LOAD_ERROR_BAD_OBJECTS) + { + // The path needs to be duplicated as it's a const here + // which the window function doesn't like + window_object_load_error_open(strndup(path, strnlen(path, MAX_PATH)), result->object_validity); + } + else if (result->error != PARK_LOAD_ERROR_NONE) { + // If loading the SV6 or SV4 failed for a reason other than invalid objects + // the current park state will be corrupted so just go back to the title screen. + title_load(); + } + SafeFree(result); +} + void game_load_init() { rct_window *mainWindow; diff --git a/src/openrct2/game.h b/src/openrct2/game.h index 2e273160fb..9457e972fc 100644 --- a/src/openrct2/game.h +++ b/src/openrct2/game.h @@ -180,6 +180,7 @@ bool game_is_paused(); bool game_is_not_paused(); void save_game(); void save_game_as(); +void handle_park_load_failure(park_load_result* result, const utf8* path); void rct2_exit(); void rct2_exit_reason(rct_string_id title, rct_string_id body); void game_autosave(); diff --git a/src/openrct2/util/util.c b/src/openrct2/util/util.c index 26844f5e2d..90a7088c8a 100644 --- a/src/openrct2/util/util.c +++ b/src/openrct2/util/util.c @@ -549,19 +549,3 @@ size_t strcatftime(char * buffer, size_t bufferSize, const char * format, const } return 0; } - -void handle_park_load_failure(park_load_result* result, const utf8* path) -{ - if (result->error == PARK_LOAD_ERROR_BAD_OBJECTS) - { - // The path needs to be duplicated as it's a const here - // which the window function doesn't like - window_object_load_error_open(strndup(path, strnlen(path, MAX_PATH)), result->object_validity); - } - else if (result->error != PARK_LOAD_ERROR_NONE) { - // If loading the SV6 or SV4 failed for a reason other than invalid objects - // the current park state will be corrupted so just go back to the title screen. - title_load(); - } - SafeFree(result); -} \ No newline at end of file diff --git a/src/openrct2/util/util.h b/src/openrct2/util/util.h index 43e0da7261..1ada3c6a5d 100644 --- a/src/openrct2/util/util.h +++ b/src/openrct2/util/util.h @@ -66,5 +66,4 @@ money32 add_clamp_money32(money32 value, money32 value_to_add); size_t strcatftime(char * buffer, size_t bufferSize, const char * format, const struct tm * tp); -void handle_park_load_failure(park_load_result* result, const utf8* path); #endif From ddb96ec2675260afd690d7ac78950de9cd59fb5d Mon Sep 17 00:00:00 2001 From: Ted John Date: Sat, 1 Jul 2017 01:26:23 +0100 Subject: [PATCH 5/5] Refactor load park result Use a C++ struct and pass that to C. --- src/openrct2/ParkImporter.cpp | 59 ++++++++++++ src/openrct2/ParkImporter.h | 46 ++++++++- src/openrct2/editor.c | 7 +- src/openrct2/game.c | 29 +++--- src/openrct2/game.h | 5 +- src/openrct2/interface/window.h | 2 +- src/openrct2/object/ObjectManager.cpp | 16 ++- src/openrct2/object/ObjectManager.h | 9 +- src/openrct2/park_load_result_types.h | 40 -------- src/openrct2/rct1.h | 7 +- src/openrct2/rct1/S4Importer.cpp | 118 ++++++++++------------- src/openrct2/rct2.c | 10 +- src/openrct2/rct2/S6Importer.cpp | 60 ++++++------ src/openrct2/scenario/scenario.c | 19 ++-- src/openrct2/scenario/scenario.h | 7 +- src/openrct2/util/util.h | 1 - src/openrct2/windows/object_load_error.c | 26 +++-- src/openrct2/windows/server_start.c | 7 +- src/openrct2/windows/title_menu.c | 4 +- src/openrct2/windows/top_toolbar.c | 4 +- 20 files changed, 257 insertions(+), 219 deletions(-) delete mode 100644 src/openrct2/park_load_result_types.h diff --git a/src/openrct2/ParkImporter.cpp b/src/openrct2/ParkImporter.cpp index 6b20575930..3fe174a906 100644 --- a/src/openrct2/ParkImporter.cpp +++ b/src/openrct2/ParkImporter.cpp @@ -21,6 +21,65 @@ #include "object/ObjectRepository.h" #include "ParkImporter.h" +ParkLoadResult::ParkLoadResult(PARK_LOAD_ERROR error) + : Error(error) +{ +} + +ParkLoadResult::ParkLoadResult(PARK_LOAD_ERROR error, const std::vector &missingObjects) + : Error(error), + MissingObjects(missingObjects) +{ +} + +ParkLoadResult ParkLoadResult::CreateOK() +{ + return ParkLoadResult(PARK_LOAD_ERROR::PARK_LOAD_ERROR_OK); +} + +ParkLoadResult ParkLoadResult::CreateInvalidExtension() +{ + return ParkLoadResult(PARK_LOAD_ERROR::PARK_LOAD_ERROR_INVALID_EXTENSION); +} + +ParkLoadResult ParkLoadResult::CreateMissingObjects(const std::vector &missingObjects) +{ + return ParkLoadResult(PARK_LOAD_ERROR::PARK_LOAD_ERROR_MISSING_OBJECTS, missingObjects); +} + +ParkLoadResult ParkLoadResult::CreateUnknown() +{ + return ParkLoadResult(PARK_LOAD_ERROR::PARK_LOAD_ERROR_UNKNOWN); +} + +extern "C" +{ + PARK_LOAD_ERROR ParkLoadResult_GetError(const ParkLoadResult * t) + { + return t->Error; + } + + size_t ParkLoadResult_GetMissingObjectsCount(const ParkLoadResult * t) + { + return t->MissingObjects.size(); + } + + const rct_object_entry * ParkLoadResult_GetMissingObjects(const ParkLoadResult * t) + { + return t->MissingObjects.data(); + } + + void ParkLoadResult_Delete(ParkLoadResult * t) + { + delete t; + } + + ParkLoadResult * ParkLoadResult_CreateInvalidExtension() + { + return new ParkLoadResult(ParkLoadResult::CreateInvalidExtension()); + } +} + namespace ParkImporter { IParkImporter * Create(const std::string &hintPath) diff --git a/src/openrct2/ParkImporter.h b/src/openrct2/ParkImporter.h index 7a0bee4e89..814cd9f45a 100644 --- a/src/openrct2/ParkImporter.h +++ b/src/openrct2/ParkImporter.h @@ -22,20 +22,45 @@ extern "C" { #endif -#include "park_load_result_types.h" + #include "object.h" #ifdef __cplusplus } #endif +typedef enum PARK_LOAD_ERROR +{ + PARK_LOAD_ERROR_OK, + PARK_LOAD_ERROR_MISSING_OBJECTS, + PARK_LOAD_ERROR_INVALID_EXTENSION, + PARK_LOAD_ERROR_UNKNOWN = 255 +} PARK_LOAD_ERROR; + #ifdef __cplusplus #include +#include #include "scenario/ScenarioRepository.h" interface IObjectManager; interface IObjectRepository; interface IStream; +struct ParkLoadResult final +{ +public: + const PARK_LOAD_ERROR Error; + const std::vector MissingObjects; + + static ParkLoadResult CreateOK(); + static ParkLoadResult CreateInvalidExtension(); + static ParkLoadResult CreateMissingObjects(const std::vector &missingObjects); + static ParkLoadResult CreateUnknown(); + +private: + ParkLoadResult(PARK_LOAD_ERROR error); + ParkLoadResult(PARK_LOAD_ERROR error, const std::vector &missingObjects); +}; + /** * Interface to import scenarios and saved games. */ @@ -43,10 +68,12 @@ interface IParkImporter { public: virtual ~IParkImporter() = default; - virtual park_load_result * Load(const utf8 * path) abstract; - virtual park_load_result * LoadSavedGame(const utf8 * path, bool skipObjectCheck = false) abstract; - virtual park_load_result * LoadScenario(const utf8 * path, bool skipObjectCheck = false) abstract; - virtual park_load_result * LoadFromStream(IStream * stream, bool isScenario, bool skipObjectCheck = false) abstract; + + virtual ParkLoadResult Load(const utf8 * path) abstract; + virtual ParkLoadResult LoadSavedGame(const utf8 * path, bool skipObjectCheck = false) abstract; + virtual ParkLoadResult LoadScenario(const utf8 * path, bool skipObjectCheck = false) abstract; + virtual ParkLoadResult LoadFromStream(IStream * stream, bool isScenario, bool skipObjectCheck = false) abstract; + virtual void Import() abstract; virtual bool GetDetails(scenario_index_entry * dst) abstract; }; @@ -61,6 +88,10 @@ namespace ParkImporter bool ExtensionIsScenario(const std::string &extension); } +#else + +typedef struct ParkLoadResult ParkLoadResult; + #endif #ifdef __cplusplus @@ -70,6 +101,11 @@ extern "C" void park_importer_load_from_stream(void * stream, const utf8 * hintPath); bool park_importer_extension_is_scenario(const utf8 * extension); + PARK_LOAD_ERROR ParkLoadResult_GetError(const ParkLoadResult * t); + size_t ParkLoadResult_GetMissingObjectsCount(const ParkLoadResult * t); + const rct_object_entry * ParkLoadResult_GetMissingObjects(const ParkLoadResult * t); + void ParkLoadResult_Delete(ParkLoadResult * t); + ParkLoadResult * ParkLoadResult_CreateInvalidExtension(); #ifdef __cplusplus } #endif diff --git a/src/openrct2/editor.c b/src/openrct2/editor.c index b9ab7e7f02..07c8c95d20 100644 --- a/src/openrct2/editor.c +++ b/src/openrct2/editor.c @@ -31,6 +31,7 @@ #include "platform/platform.h" #include "rct1.h" #include "ride/ride.h" +#include "ParkImporter.h" #include "scenario/scenario.h" #include "util/sawyercoding.h" #include "util/util.h" @@ -261,16 +262,18 @@ static sint32 editor_load_landscape_from_sc4(const char *path) */ static sint32 editor_read_s6(const char *path) { - park_load_result* loadResult = { 0 }; + ParkLoadResult * loadResult = NULL; const char *extension = path_get_extension(path); if (_stricmp(extension, ".sc6") == 0) { loadResult = scenario_load(path); } else if (_stricmp(extension, ".sv6") == 0) { loadResult = game_load_sv6_path(path); } - if (loadResult->error != PARK_LOAD_ERROR_NONE) { + if (ParkLoadResult_GetError(loadResult) != PARK_LOAD_ERROR_OK) { + ParkLoadResult_Delete(loadResult); return 0; } + ParkLoadResult_Delete(loadResult); editor_clear_map_for_editing(true); diff --git a/src/openrct2/game.c b/src/openrct2/game.c index e180737a03..9f7ecab429 100644 --- a/src/openrct2/game.c +++ b/src/openrct2/game.c @@ -33,6 +33,7 @@ #include "network/network.h" #include "object.h" #include "OpenRCT2.h" +#include "ParkImporter.h" #include "peep/peep.h" #include "peep/staff.h" #include "platform/platform.h" @@ -1094,15 +1095,16 @@ bool game_load_save(const utf8 *path) safe_strcpy(gScenarioSavePath, path, MAX_PATH); uint32 extension_type = get_file_extension_type(path); - park_load_result* result = {0}; + ParkLoadResult * result = NULL; bool load_success = false; if (extension_type == FILE_EXTENSION_SV6) { result = game_load_sv6_path(path); - load_success = (result->error == PARK_LOAD_ERROR_NONE); + load_success = (ParkLoadResult_GetError(result) == PARK_LOAD_ERROR_OK); if (load_success) gFirstTimeSaving = false; } else if (extension_type == FILE_EXTENSION_SV4) { - load_success = rct1_load_saved_game(path); + result = rct1_load_saved_game(path); + load_success = (ParkLoadResult_GetError(result) == PARK_LOAD_ERROR_OK); if (load_success) gFirstTimeSaving = true; } @@ -1126,20 +1128,20 @@ bool game_load_save(const utf8 *path) } } -void handle_park_load_failure(park_load_result* result, const utf8* path) +void handle_park_load_failure(const ParkLoadResult * result, const utf8 * path) { - if (result->error == PARK_LOAD_ERROR_BAD_OBJECTS) + if (ParkLoadResult_GetError(result) == PARK_LOAD_ERROR_MISSING_OBJECTS) { // The path needs to be duplicated as it's a const here // which the window function doesn't like - window_object_load_error_open(strndup(path, strnlen(path, MAX_PATH)), result->object_validity); - } - else if (result->error != PARK_LOAD_ERROR_NONE) { + window_object_load_error_open(strndup(path, strnlen(path, MAX_PATH)), + ParkLoadResult_GetMissingObjectsCount(result), + ParkLoadResult_GetMissingObjects(result)); + } else if (ParkLoadResult_GetError(result) != PARK_LOAD_ERROR_OK) { // If loading the SV6 or SV4 failed for a reason other than invalid objects // the current park state will be corrupted so just go back to the title screen. title_load(); } - SafeFree(result); } void game_load_init() @@ -1378,7 +1380,6 @@ void rct2_exit() bool game_load_save_or_scenario(const utf8 * path) { - park_load_result* result; uint32 extension = get_file_extension_type(path); switch (extension) { case FILE_EXTENSION_SV4: @@ -1386,8 +1387,12 @@ bool game_load_save_or_scenario(const utf8 * path) return game_load_save(path); case FILE_EXTENSION_SC4: case FILE_EXTENSION_SC6: - result = scenario_load_and_play_from_path(path); - return (result->error == PARK_LOAD_ERROR_NONE); + { + ParkLoadResult * result = scenario_load_and_play_from_path(path); + bool success = (ParkLoadResult_GetError(result) == PARK_LOAD_ERROR_OK); + ParkLoadResult_Delete(result); + return success; + } } return false; } diff --git a/src/openrct2/game.h b/src/openrct2/game.h index 9457e972fc..60614715f3 100644 --- a/src/openrct2/game.h +++ b/src/openrct2/game.h @@ -20,7 +20,6 @@ #include "rct2/addresses.h" #include "common.h" #include "scenario/scenario.h" -#include "park_load_result_types.h" enum GAME_COMMAND { GAME_COMMAND_SET_RIDE_APPEARANCE, @@ -171,7 +170,7 @@ sint32 game_do_command_p(sint32 command, sint32 *eax, sint32 *ebx, sint32 *ecx, void game_log_multiplayer_command(int command, int *eax, int* ebx, int* ecx, int* edx, int* edi, int* ebp); void game_load_or_quit_no_save_prompt(); -park_load_result * game_load_sv6_path(const char * path); +ParkLoadResult * game_load_sv6_path(const char * path); bool game_load_save(const utf8 *path); void game_load_init(); void game_pause_toggle(sint32 *eax, sint32 *ebx, sint32 *ecx, sint32 *edx, sint32 *esi, sint32 *edi, sint32 *ebp); @@ -180,7 +179,7 @@ bool game_is_paused(); bool game_is_not_paused(); void save_game(); void save_game_as(); -void handle_park_load_failure(park_load_result* result, const utf8* path); +void handle_park_load_failure(const ParkLoadResult * result, const utf8 * path); void rct2_exit(); void rct2_exit_reason(rct_string_id title, rct_string_id body); void game_autosave(); diff --git a/src/openrct2/interface/window.h b/src/openrct2/interface/window.h index 72525e131b..f38e7bcfe3 100644 --- a/src/openrct2/interface/window.h +++ b/src/openrct2/interface/window.h @@ -767,7 +767,7 @@ rct_window *window_mapgen_open(); rct_window *window_loadsave_open(sint32 type, char *defaultName); rct_window *window_changelog_open(); void window_debug_paint_open(); -rct_window *window_object_load_error_open(utf8* path, object_validity_result* result); +rct_window * window_object_load_error_open(utf8 * path, size_t numMissingObjects, const rct_object_entry * missingObjects); rct_window * window_editor_main_open(); void window_editor_bottom_toolbar_open(); diff --git a/src/openrct2/object/ObjectManager.cpp b/src/openrct2/object/ObjectManager.cpp index b4e77135e4..b1e0921cba 100644 --- a/src/openrct2/object/ObjectManager.cpp +++ b/src/openrct2/object/ObjectManager.cpp @@ -447,10 +447,10 @@ private: return duplicate; } - object_validity_result* GetInvalidObjects(const rct_object_entry * entries) override + std::vector GetInvalidObjects(const rct_object_entry * entries) override { - uint16 invalidObjectCount = 0; - rct_object_entry * * invalidEntries = Memory::AllocateArray(OBJECT_ENTRY_COUNT); + std::vector invalidEntries; + invalidEntries.reserve(OBJECT_ENTRY_COUNT); for (sint32 i = 0; i < OBJECT_ENTRY_COUNT; i++) { const rct_object_entry * entry = &entries[i]; @@ -460,7 +460,7 @@ private: ori = _objectRepository->FindObject(entry); if (ori == nullptr) { - invalidEntries[invalidObjectCount++] = DuplicateObjectEntry(entry); + invalidEntries.push_back(*entry); } else { @@ -471,16 +471,14 @@ private: loadedObject = _objectRepository->LoadObject(ori); if (loadedObject == nullptr) { - invalidEntries[invalidObjectCount++] = DuplicateObjectEntry(entry); + invalidEntries.push_back(*entry); } + delete loadedObject; } } } } - object_validity_result* result = Memory::Allocate(sizeof(object_validity_result)); - result->invalid_object_count = invalidObjectCount; - result->invalid_objects = invalidEntries; - return result; + return invalidEntries; } bool GetRequiredObjects(const rct_object_entry * entries, diff --git a/src/openrct2/object/ObjectManager.h b/src/openrct2/object/ObjectManager.h index d93f5bcda3..7aa837cea6 100644 --- a/src/openrct2/object/ObjectManager.h +++ b/src/openrct2/object/ObjectManager.h @@ -27,7 +27,6 @@ extern "C" { #endif #include "../object.h" - #include "../park_load_result_types.h" #ifdef __cplusplus } #endif @@ -42,10 +41,10 @@ interface IObjectManager { virtual ~IObjectManager() { } - virtual Object * GetLoadedObject(size_t index) abstract; - virtual Object * GetLoadedObject(const rct_object_entry * entry) abstract; - virtual uint8 GetLoadedObjectEntryIndex(const Object * object) abstract; - virtual object_validity_result* GetInvalidObjects(const rct_object_entry * entries) abstract; + virtual Object * GetLoadedObject(size_t index) abstract; + virtual Object * GetLoadedObject(const rct_object_entry * entry) abstract; + virtual uint8 GetLoadedObjectEntryIndex(const Object * object) abstract; + virtual std::vector GetInvalidObjects(const rct_object_entry * entries) abstract; virtual Object * LoadObject(const rct_object_entry * entry) abstract; virtual bool LoadObjects(const rct_object_entry * entries, size_t count) abstract; diff --git a/src/openrct2/park_load_result_types.h b/src/openrct2/park_load_result_types.h deleted file mode 100644 index 6e43388c6b..0000000000 --- a/src/openrct2/park_load_result_types.h +++ /dev/null @@ -1,40 +0,0 @@ -#pragma region Copyright (c) 2014-2017 OpenRCT2 Developers -/***************************************************************************** -* OpenRCT2, an open source clone of Roller Coaster Tycoon 2. -* -* OpenRCT2 is the work of many authors, a full list can be found in contributors.md -* For more information, visit https://github.com/OpenRCT2/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. -* -* A full copy of the GNU General Public License can be found in licence.txt -*****************************************************************************/ -#pragma endregion - -#ifndef _PARK_LOAD_RESULT_H_ -#define _PARK_LOAD_RESULT_H_ - -#include "object.h" - -enum PARK_LOAD_ERROR { - PARK_LOAD_ERROR_NONE, - PARK_LOAD_ERROR_BAD_OBJECTS, - PARK_LOAD_ERROR_INVALID_EXTENSION, - PARK_LOAD_ERROR_UNKNOWN = 255 -}; - -typedef struct object_validity_result -{ - uint16 invalid_object_count; - rct_object_entry * * invalid_objects; - -} object_validity_result; - -typedef struct park_load_result { - uint8 error; - object_validity_result* object_validity; -} park_load_result; -#endif diff --git a/src/openrct2/rct1.h b/src/openrct2/rct1.h index 0126015adc..3135da565b 100644 --- a/src/openrct2/rct1.h +++ b/src/openrct2/rct1.h @@ -20,7 +20,6 @@ #include "management/award.h" #include "management/news_item.h" #include "management/research.h" -#include "park_load_result_types.h" #include "rct12.h" #include "rct2.h" #include "ride/ride.h" @@ -35,6 +34,8 @@ #define RCT1_MAX_RIDES_IN_PARK 128 #define RCT1_RESEARCH_FLAGS_SEPARATOR 0xFF +typedef struct ParkLoadResult ParkLoadResult; + #pragma pack(push, 1) typedef struct rct1_entrance { uint16 x; @@ -1210,8 +1211,8 @@ extern const uint8 gRideCategories[RIDE_TYPE_COUNT]; sint32 vehicle_preference_compare(uint8 rideType, const char * a, const char * b); bool rideTypeShouldLoseSeparateFlag(const rct_ride_entry *rideEntry); -park_load_result * rct1_load_saved_game(const char *path); -park_load_result * rct1_load_scenario(const char *path); +ParkLoadResult * rct1_load_saved_game(const char *path); +ParkLoadResult * rct1_load_scenario(const char *path); colour_t rct1_get_colour(colour_t colour); diff --git a/src/openrct2/rct1/S4Importer.cpp b/src/openrct2/rct1/S4Importer.cpp index 0666430295..b7a99d4c01 100644 --- a/src/openrct2/rct1/S4Importer.cpp +++ b/src/openrct2/rct1/S4Importer.cpp @@ -26,6 +26,7 @@ #include "../core/Path.hpp" #include "../core/String.hpp" #include "../core/Util.hpp" +#include "../object/Object.h" #include "../object/ObjectManager.h" #include "../object/ObjectRepository.h" #include "../ParkImporter.h" @@ -126,7 +127,7 @@ private: uint8 _researchRideTypeUsed[128]; public: - park_load_result* Load(const utf8 * path) override + ParkLoadResult Load(const utf8 * path) override { const utf8 * extension = Path::GetExtension(path); if (String::Equals(extension, ".sc4", true)) @@ -143,27 +144,24 @@ public: } } - park_load_result* LoadSavedGame(const utf8 * path, bool skipObjectCheck = false) override + ParkLoadResult LoadSavedGame(const utf8 * path, bool skipObjectCheck = false) override { auto fs = FileStream(path, FILE_MODE_OPEN); - park_load_result* result = LoadFromStream(&fs, false, skipObjectCheck); + auto result = LoadFromStream(&fs, false, skipObjectCheck); _s4Path = path; return result; } - park_load_result* LoadScenario(const utf8 * path, bool skipObjectCheck = false) override + ParkLoadResult LoadScenario(const utf8 * path, bool skipObjectCheck = false) override { auto fs = FileStream(path, FILE_MODE_OPEN); - park_load_result* result = LoadFromStream(&fs, true, skipObjectCheck); + auto result = LoadFromStream(&fs, true, skipObjectCheck); _s4Path = path; return result; } - park_load_result* LoadFromStream(IStream * stream, bool isScenario, bool skipObjectCheck = false) override + ParkLoadResult LoadFromStream(IStream * stream, bool isScenario, bool skipObjectCheck = false) override { - park_load_result* result = Memory::Allocate(sizeof(park_load_result)); - result->error = PARK_LOAD_ERROR_NONE; - size_t dataSize = stream->GetLength() - stream->GetPosition(); std::unique_ptr data = std::unique_ptr(stream->ReadArray(dataSize)); std::unique_ptr decodedData = std::unique_ptr(Memory::Allocate(sizeof(rct1_s4))); @@ -188,12 +186,11 @@ public: { InitialiseEntryMaps(); CreateAvailableObjectMappings(); - object_validity_result* object_result = GetInvalidObjects(); - result->object_validity = object_result; - if (object_result->invalid_object_count > 0) + auto missingObjects = GetInvalidObjects(); + if (missingObjects.size() > 0) { - result->error = PARK_LOAD_ERROR_BAD_OBJECTS; + return ParkLoadResult::CreateMissingObjects(missingObjects); } } } @@ -201,7 +198,7 @@ public: { throw Exception("Unable to decode park."); } - return result; + return ParkLoadResult::CreateOK(); } void Import() override @@ -1751,22 +1748,16 @@ private: } } - object_validity_result* GetInvalidObjects() + std::vector GetInvalidObjects() { - object_validity_result* result = Memory::Allocate(sizeof(object_validity_result)); - uint16 invalidObjectCount = 0; - rct_object_entry * * invalidEntries = Memory::AllocateArray(OBJECT_ENTRY_COUNT); - - result->invalid_object_count = invalidObjectCount; - result->invalid_objects = invalidEntries; - - GetInvalidObjects(OBJECT_TYPE_RIDE, _rideEntries.GetEntries(), *result); - GetInvalidObjects(OBJECT_TYPE_SMALL_SCENERY, _smallSceneryEntries.GetEntries(), *result); - GetInvalidObjects(OBJECT_TYPE_LARGE_SCENERY, _largeSceneryEntries.GetEntries(), *result); - GetInvalidObjects(OBJECT_TYPE_WALLS, _wallEntries.GetEntries(), *result); - GetInvalidObjects(OBJECT_TYPE_PATHS, _pathEntries.GetEntries(), *result); - GetInvalidObjects(OBJECT_TYPE_PATH_BITS, _pathAdditionEntries.GetEntries(), *result); - GetInvalidObjects(OBJECT_TYPE_SCENERY_SETS, _sceneryGroupEntries.GetEntries(), *result); + std::vector missingObjects; + GetInvalidObjects(OBJECT_TYPE_RIDE, _rideEntries.GetEntries(), missingObjects); + GetInvalidObjects(OBJECT_TYPE_SMALL_SCENERY, _smallSceneryEntries.GetEntries(), missingObjects); + GetInvalidObjects(OBJECT_TYPE_LARGE_SCENERY, _largeSceneryEntries.GetEntries(), missingObjects); + GetInvalidObjects(OBJECT_TYPE_WALLS, _wallEntries.GetEntries(), missingObjects); + GetInvalidObjects(OBJECT_TYPE_PATHS, _pathEntries.GetEntries(), missingObjects); + GetInvalidObjects(OBJECT_TYPE_PATH_BITS, _pathAdditionEntries.GetEntries(), missingObjects); + GetInvalidObjects(OBJECT_TYPE_SCENERY_SETS, _sceneryGroupEntries.GetEntries(), missingObjects); GetInvalidObjects(OBJECT_TYPE_BANNERS, std::vector({ "BN1 ", "BN2 ", @@ -1777,14 +1768,13 @@ private: "BN7 ", "BN8 ", "BN9 " - }), *result); - GetInvalidObjects(OBJECT_TYPE_PARK_ENTRANCE, std::vector({ "PKENT1 " }), *result); - GetInvalidObjects(OBJECT_TYPE_WATER, _waterEntry.GetEntries(), *result); - - return result; + }), missingObjects); + GetInvalidObjects(OBJECT_TYPE_PARK_ENTRANCE, std::vector({ "PKENT1 " }), missingObjects); + GetInvalidObjects(OBJECT_TYPE_WATER, _waterEntry.GetEntries(), missingObjects); + return missingObjects; } - void GetInvalidObjects(uint8 objectType, const std::vector &entries, object_validity_result &result) + void GetInvalidObjects(uint8 objectType, const std::vector &entries, std::vector &missingObjects) { IObjectRepository * objectRepository = GetObjectRepository(); for (const char * objectName : entries) @@ -1794,26 +1784,19 @@ private: Memory::Copy(entry.name, objectName, 8); entry.checksum = 0; - const ObjectRepositoryItem * ori = nullptr; - ori = objectRepository->FindObject(&entry); + const ObjectRepositoryItem * ori = objectRepository->FindObject(&entry); if (ori == nullptr) { - rct_object_entry * invalid_entry = Memory::Allocate(sizeof(rct_object_entry)); - invalid_entry->flags = entry.flags; - Memory::Copy(invalid_entry->name, objectName, 8); - result.invalid_objects[result.invalid_object_count++] = invalid_entry; + missingObjects.push_back(entry); } else { Object * object = objectRepository->LoadObject(ori); if (object == nullptr && objectType != OBJECT_TYPE_SCENERY_SETS) { - rct_object_entry * invalid_entry = Memory::Allocate(sizeof(rct_object_entry)); - invalid_entry->flags = entry.flags; - Memory::Copy(invalid_entry->name, objectName, 8); - result.invalid_objects[result.invalid_object_count++] = invalid_entry; + missingObjects.push_back(entry); } - SafeFree(object); + delete object; } } } @@ -2661,46 +2644,43 @@ IParkImporter * ParkImporter::CreateS4() ///////////////////////////////////////// extern "C" { - park_load_result* rct1_load_saved_game(const utf8 * path) + ParkLoadResult * rct1_load_saved_game(const utf8 * path) { - park_load_result* result = {}; - - auto s4Importer = new S4Importer(); + ParkLoadResult * result = nullptr; + auto s4Importer = std::make_unique(); try { - result = s4Importer->LoadSavedGame(path); - if (result->error == PARK_LOAD_ERROR_NONE) + result = new ParkLoadResult(s4Importer->LoadSavedGame(path)); + if (result->Error == PARK_LOAD_ERROR_OK) { s4Importer->Import(); } - - } catch (const Exception &) - { - result = {}; - result->error = PARK_LOAD_ERROR_UNKNOWN; } - delete s4Importer; + catch (const Exception &) + { + delete result; + result = new ParkLoadResult(ParkLoadResult::CreateUnknown()); + } return result; } - park_load_result* rct1_load_scenario(const utf8 * path) + ParkLoadResult * rct1_load_scenario(const utf8 * path) { - park_load_result* result = {}; - - auto s4Importer = new S4Importer(); + ParkLoadResult * result = nullptr; + auto s4Importer = std::make_unique(); try { - result = s4Importer->LoadSavedGame(path); - if (result->error == PARK_LOAD_ERROR_NONE) + result = new ParkLoadResult(s4Importer->LoadSavedGame(path)); + if (result->Error == PARK_LOAD_ERROR_OK) { s4Importer->Import(); } - } catch (const Exception &) - { - result = {}; - result->error = PARK_LOAD_ERROR_UNKNOWN; } - delete s4Importer; + catch (const Exception &) + { + delete result; + result = new ParkLoadResult(ParkLoadResult::CreateUnknown()); + } return result; } diff --git a/src/openrct2/rct2.c b/src/openrct2/rct2.c index 89fe7de289..7179ef7dfb 100644 --- a/src/openrct2/rct2.c +++ b/src/openrct2/rct2.c @@ -38,6 +38,7 @@ #include "object.h" #include "object/ObjectManager.h" #include "OpenRCT2.h" +#include "ParkImporter.h" #include "peep/staff.h" #include "platform/platform.h" #include "rct1.h" @@ -339,12 +340,13 @@ bool rct2_open_file(const char *path) } } else if (_stricmp(extension, "sc6") == 0) { // TODO scenario install - park_load_result *result = scenario_load_and_play_from_path(path); - if (result->error == PARK_LOAD_ERROR_NONE) { + ParkLoadResult * result = scenario_load_and_play_from_path(path); + if (ParkLoadResult_GetError(result) == PARK_LOAD_ERROR_OK) { + ParkLoadResult_Delete(result); return true; - } - else { + } else { handle_park_load_failure(result, path); + ParkLoadResult_Delete(result); return false; } } else if (_stricmp(extension, "td6") == 0 || _stricmp(extension, "td4") == 0) { diff --git a/src/openrct2/rct2/S6Importer.cpp b/src/openrct2/rct2/S6Importer.cpp index 9ca6083054..53ac9a2971 100644 --- a/src/openrct2/rct2/S6Importer.cpp +++ b/src/openrct2/rct2/S6Importer.cpp @@ -81,7 +81,7 @@ public: Memory::Set(&_s6, 0, sizeof(_s6)); } - park_load_result* Load(const utf8 * path) override + ParkLoadResult Load(const utf8 * path) override { const utf8 * extension = Path::GetExtension(path); if (String::Equals(extension, ".sc6", true)) @@ -98,29 +98,24 @@ public: } } - park_load_result* LoadSavedGame(const utf8 * path, bool skipObjectCheck = false) override + ParkLoadResult LoadSavedGame(const utf8 * path, bool skipObjectCheck = false) override { auto fs = FileStream(path, FILE_MODE_OPEN); - park_load_result* result = LoadFromStream(&fs, false, skipObjectCheck); + auto result = LoadFromStream(&fs, false, skipObjectCheck); _s6Path = path; - return result; } - park_load_result* LoadScenario(const utf8 * path, bool skipObjectCheck = false) override + ParkLoadResult LoadScenario(const utf8 * path, bool skipObjectCheck = false) override { auto fs = FileStream(path, FILE_MODE_OPEN); - park_load_result* result = LoadFromStream(&fs, true, skipObjectCheck); + auto result = LoadFromStream(&fs, true, skipObjectCheck); _s6Path = path; - return result; } - park_load_result* LoadFromStream(IStream * stream, bool isScenario, bool skipObjectCheck = false) override + ParkLoadResult LoadFromStream(IStream * stream, bool isScenario, bool skipObjectCheck = false) override { - park_load_result* result = Memory::Allocate(sizeof(park_load_result)); - result->error = PARK_LOAD_ERROR_UNKNOWN; - if (isScenario && !gConfigGeneral.allow_loading_with_incorrect_checksum && !SawyerEncoding::ValidateChecksum(stream)) { throw IOException("Invalid checksum."); @@ -175,18 +170,12 @@ public: chunkReader.ReadChunk(&_s6.next_free_map_element_pointer_index, 3048816); } - object_validity_result* object_result = _objectManager->GetInvalidObjects(_s6.objects); - - result->object_validity = object_result; - if (object_result->invalid_object_count > 0) + auto missingObjects = _objectManager->GetInvalidObjects(_s6.objects); + if (missingObjects.size() > 0) { - result->error = PARK_LOAD_ERROR_BAD_OBJECTS; + return ParkLoadResult::CreateMissingObjects(missingObjects); } - else - { - result->error = PARK_LOAD_ERROR_NONE; - } - return result; + return ParkLoadResult::CreateOK(); } bool GetDetails(scenario_index_entry * dst) override @@ -439,17 +428,17 @@ IParkImporter * ParkImporter::CreateS6(IObjectRepository * objectRepository, IOb extern "C" { - park_load_result* game_load_sv6_path(const char * path) + ParkLoadResult * game_load_sv6_path(const char * path) { - park_load_result* result = {}; + ParkLoadResult * result = nullptr; auto s6Importer = new S6Importer(GetObjectRepository(), GetObjectManager()); try { - result = s6Importer->LoadSavedGame(path); + result = new ParkLoadResult(s6Importer->LoadSavedGame(path)); // We mustn't import if there's something // wrong with the park data - if (result->error == PARK_LOAD_ERROR_NONE) + if (result->Error == PARK_LOAD_ERROR_OK) { s6Importer->Import(); @@ -474,7 +463,11 @@ extern "C" } delete s6Importer; - if (result->error == PARK_LOAD_ERROR_NONE) + if (result == nullptr) + { + result = new ParkLoadResult(ParkLoadResult::CreateUnknown()); + } + if (result->Error == PARK_LOAD_ERROR_OK) { gScreenAge = 0; gLastAutoSaveUpdate = AUTOSAVE_PAUSE; @@ -487,14 +480,14 @@ extern "C" * rct2: 0x00676053 * scenario (ebx) */ - park_load_result* scenario_load(const char * path) + ParkLoadResult * scenario_load(const char * path) { - park_load_result* result = {}; + ParkLoadResult * result = nullptr; auto s6Importer = new S6Importer(GetObjectRepository(), GetObjectManager()); try { - result = s6Importer->LoadScenario(path); - if (result->error == PARK_LOAD_ERROR_NONE) + result = new ParkLoadResult(s6Importer->LoadScenario(path)); + if (result->Error == PARK_LOAD_ERROR_OK) { s6Importer->Import(); @@ -518,7 +511,12 @@ extern "C" gErrorStringId = STR_FILE_CONTAINS_INVALID_DATA; } delete s6Importer; - if (result->error != PARK_LOAD_ERROR_NONE) + + if (result == nullptr) + { + result = new ParkLoadResult(ParkLoadResult::CreateUnknown()); + } + if (result->Error != PARK_LOAD_ERROR_OK) { gScreenAge = 0; gLastAutoSaveUpdate = AUTOSAVE_PAUSE; diff --git a/src/openrct2/scenario/scenario.c b/src/openrct2/scenario/scenario.c index 17ca3d4677..2e1a7d6277 100644 --- a/src/openrct2/scenario/scenario.c +++ b/src/openrct2/scenario/scenario.c @@ -30,6 +30,7 @@ #include "../object.h" #include "../object_list.h" #include "../OpenRCT2.h" +#include "../ParkImporter.h" #include "../peep/staff.h" #include "../platform/platform.h" #include "../rct1.h" @@ -88,28 +89,24 @@ money32 gScenarioCompanyValueRecord; static sint32 scenario_create_ducks(); static void scenario_objective_check(); -park_load_result* scenario_load_and_play_from_path(const char *path) +ParkLoadResult * scenario_load_and_play_from_path(const char * path) { window_close_construction_windows(); uint32 extension = get_file_extension_type(path); - park_load_result* result = malloc(sizeof(park_load_result)); + ParkLoadResult * result = NULL; if (extension == FILE_EXTENSION_SC6) { result = scenario_load(path); - if (result->error != PARK_LOAD_ERROR_NONE) - { + if (ParkLoadResult_GetError(result) != PARK_LOAD_ERROR_OK) { return result; } - } - else if (extension == FILE_EXTENSION_SC4) { + } else if (extension == FILE_EXTENSION_SC4) { result = rct1_load_scenario(path); - if (result->error != PARK_LOAD_ERROR_NONE) - { + if (ParkLoadResult_GetError(result) != PARK_LOAD_ERROR_OK) { return result; } - } - else { - result->error = PARK_LOAD_ERROR_INVALID_EXTENSION; + } else { + result = ParkLoadResult_CreateInvalidExtension(); return result; } diff --git a/src/openrct2/scenario/scenario.h b/src/openrct2/scenario/scenario.h index 13db64199b..f592b001e6 100644 --- a/src/openrct2/scenario/scenario.h +++ b/src/openrct2/scenario/scenario.h @@ -21,7 +21,6 @@ #include "../management/finance.h" #include "../management/research.h" #include "../object.h" -#include "../park_load_result_types.h" #include "../rct12.h" #include "../rct2.h" #include "../rct2/addresses.h" @@ -32,6 +31,8 @@ #include "../world/map_animation.h" #include "../world/sprite.h" +typedef struct ParkLoadResult ParkLoadResult; + #pragma pack(push, 1) /** * SV6/SC6 header chunk @@ -390,8 +391,8 @@ extern uint32 gLastAutoSaveUpdate; extern const char *_scenarioFileName; -park_load_result *scenario_load(const char *path); -park_load_result *scenario_load_and_play_from_path(const char *path); +ParkLoadResult * scenario_load(const char *path); +ParkLoadResult * scenario_load_and_play_from_path(const char *path); void scenario_begin(); void scenario_update(); diff --git a/src/openrct2/util/util.h b/src/openrct2/util/util.h index 1ada3c6a5d..16a1eb64a0 100644 --- a/src/openrct2/util/util.h +++ b/src/openrct2/util/util.h @@ -19,7 +19,6 @@ #include #include "../common.h" -#include "../park_load_result_types.h" sint32 squaredmetres_to_squaredfeet(sint32 squaredMetres); sint32 metres_to_feet(sint32 metres); diff --git a/src/openrct2/windows/object_load_error.c b/src/openrct2/windows/object_load_error.c index 659c368cc7..64de98ac6e 100644 --- a/src/openrct2/windows/object_load_error.c +++ b/src/openrct2/windows/object_load_error.c @@ -21,7 +21,6 @@ #include "../object.h" #include "../platform/platform.h" #include "../sprites.h" -#include "../util/util.h" enum WINDOW_OBJECT_LOAD_ERROR_WIDGET_IDX { WIDX_BACKGROUND, @@ -91,7 +90,7 @@ static rct_window_event_list window_object_load_error_events = { window_object_load_error_scrollpaint }; -rct_object_entry * * invalid_entries = NULL; +rct_object_entry * invalid_entries = NULL; sint32 highlighted_index = -1; utf8* file_path = NULL; @@ -168,20 +167,20 @@ static utf8* combine_object_names(rct_window *w) for (uint16 i = 0; i < w->no_list_items; i++) { cur_len += (8 + line_sep_len); assert(cur_len < buffer_len); - strncat(buffer, invalid_entries[i]->name, 8); + strncat(buffer, invalid_entries[i].name, 8); strncat(buffer, PLATFORM_NEWLINE, line_sep_len); } return buffer; } -rct_window *window_object_load_error_open(utf8* path, object_validity_result* result) +rct_window * window_object_load_error_open(utf8 * path, size_t numMissingObjects, const rct_object_entry * missingObjects) { - rct_window* window; - - invalid_entries = result->invalid_objects; + size_t missingObjectsSize = numMissingObjects * sizeof(rct_object_entry); + invalid_entries = malloc(missingObjectsSize); + memcpy(invalid_entries, missingObjects, missingObjectsSize); // Check if window is already open - window = window_bring_to_front_by_class(WC_OBJECT_LOAD_ERROR); + rct_window * window = window_bring_to_front_by_class(WC_OBJECT_LOAD_ERROR); if (window == NULL) { window = window_create_centred( WW, @@ -201,7 +200,7 @@ rct_window *window_object_load_error_open(utf8* path, object_validity_result* re } // Refresh list items and path - window->no_list_items = result->invalid_object_count; + window->no_list_items = (uint16)numMissingObjects; file_path = path; window_invalidate(window); @@ -232,7 +231,7 @@ static void window_object_load_error_mouseup(rct_window *w, rct_widgetindex widg break; case WIDX_COPY_CURRENT: if (w->selected_list_item > -1) { - selected_name = strndup(invalid_entries[w->selected_list_item]->name, 8); + selected_name = strndup(invalid_entries[w->selected_list_item].name, 8); platform_place_string_on_clipboard(selected_name); SafeFree(selected_name); } @@ -296,7 +295,6 @@ static void window_object_load_error_paint(rct_window *w, rct_drawpixelinfo *dpi static void window_object_load_error_scrollpaint(rct_window *w, rct_drawpixelinfo *dpi, sint32 scrollIndex) { - gfx_fill_rect(dpi, dpi->x, dpi->y, dpi->x + dpi->width - 1, dpi->y + dpi->height - 1, ColourMapA[w->colours[1]].mid_light); const sint32 list_width = w->widgets[WIDX_SCROLL].right - w->widgets[WIDX_SCROLL].left; @@ -317,12 +315,10 @@ static void window_object_load_error_scrollpaint(rct_window *w, rct_drawpixelinf gfx_fill_rect(dpi, 0, y, list_width, y + LIST_ITEM_HEIGHT - 1, ColourMapA[w->colours[1]].lighter | 0x1000000); // Draw the actual object entry's name... - gfx_draw_string(dpi, strndup(invalid_entries[i]->name, 8), COLOUR_DARK_GREEN, 5, y); + gfx_draw_string(dpi, strndup(invalid_entries[i].name, 8), COLOUR_DARK_GREEN, 5, y); // ... and type - rct_string_id type = get_object_type_string(invalid_entries[i]); + rct_string_id type = get_object_type_string(&invalid_entries[i]); gfx_draw_string_left(dpi, type, NULL, COLOUR_DARK_GREEN, (WW - 5) / 3 + 1, y); } - - } diff --git a/src/openrct2/windows/server_start.c b/src/openrct2/windows/server_start.c index 54bac250b5..d2f4bcc4c8 100644 --- a/src/openrct2/windows/server_start.c +++ b/src/openrct2/windows/server_start.c @@ -21,6 +21,7 @@ #include "../interface/window.h" #include "../localisation/localisation.h" #include "../network/network.h" +#include "../ParkImporter.h" #include "../platform/platform.h" #include "../sprites.h" #include "../title/TitleScreen.h" @@ -165,14 +166,14 @@ static void window_server_start_close(rct_window *w) static void window_server_start_scenarioselect_callback(const utf8 *path) { - park_load_result* result; network_set_password(_password); - result = scenario_load_and_play_from_path(path); - if (result->error == PARK_LOAD_ERROR_NONE) { + ParkLoadResult * result = scenario_load_and_play_from_path(path); + if (ParkLoadResult_GetError(result) == PARK_LOAD_ERROR_OK) { network_begin_server(gConfigNetwork.default_port, gConfigNetwork.listen_address); } else { handle_park_load_failure(result, path); } + ParkLoadResult_Delete(result); } static void window_server_start_loadsave_callback(sint32 result, const utf8 * path) diff --git a/src/openrct2/windows/title_menu.c b/src/openrct2/windows/title_menu.c index a722cc8781..b5b0c52308 100644 --- a/src/openrct2/windows/title_menu.c +++ b/src/openrct2/windows/title_menu.c @@ -22,6 +22,7 @@ #include "../interface/widget.h" #include "../interface/window.h" #include "../localisation/localisation.h" +#include "../ParkImporter.h" #include "../sprites.h" #include "../title/TitleScreen.h" #include "../util/util.h" @@ -129,8 +130,9 @@ void window_title_menu_open() static void window_title_menu_scenarioselect_callback(const utf8 *path) { - park_load_result *result = scenario_load_and_play_from_path(path); + ParkLoadResult * result = scenario_load_and_play_from_path(path); handle_park_load_failure(result, path); + ParkLoadResult_Delete(result); } static void window_title_menu_mouseup(rct_window *w, rct_widgetindex widgetIndex) diff --git a/src/openrct2/windows/top_toolbar.c b/src/openrct2/windows/top_toolbar.c index c05751e896..abea8d1be7 100644 --- a/src/openrct2/windows/top_toolbar.c +++ b/src/openrct2/windows/top_toolbar.c @@ -30,6 +30,7 @@ #include "../localisation/localisation.h" #include "../network/network.h" #include "../network/twitch.h" +#include "../ParkImporter.h" #include "../peep/staff.h" #include "../scenario/scenario.h" #include "../sprites.h" @@ -519,8 +520,9 @@ static void window_top_toolbar_mousedown(rct_widgetindex widgetIndex, rct_window static void window_top_toolbar_scenarioselect_callback(const utf8 *path) { - park_load_result *result = scenario_load_and_play_from_path(path); + ParkLoadResult * result = scenario_load_and_play_from_path(path); handle_park_load_failure(result, path); + ParkLoadResult_Delete(result); } /**