From e6377b40ef9e8dd0df2b14d882e91dd09ebdd905 Mon Sep 17 00:00:00 2001 From: Ted John Date: Sat, 12 Nov 2016 15:52:03 +0000 Subject: [PATCH] Write new code for loading title sequence ZIPs --- openrct2.vcxproj | 2 + src/config.c | 5 + src/core/Collections.hpp | 20 +++ src/core/Path.cpp | 10 ++ src/core/Path.hpp | 1 + src/interface/title_sequences.c | 1 + src/title.c | 1 + src/title.h | 15 -- src/title/TitleSequence.cpp | 267 +++++++++++++++++++++++++++++ src/title/TitleSequence.h | 75 ++++++++ src/windows/title_command_editor.c | 1 + src/windows/title_editor.c | 1 + 12 files changed, 384 insertions(+), 15 deletions(-) create mode 100644 src/title/TitleSequence.cpp create mode 100644 src/title/TitleSequence.h diff --git a/openrct2.vcxproj b/openrct2.vcxproj index 738db609f1..92ae3037be 100644 --- a/openrct2.vcxproj +++ b/openrct2.vcxproj @@ -324,6 +324,7 @@ + @@ -568,6 +569,7 @@ + diff --git a/src/config.c b/src/config.c index cebc91efc3..8ca2707d32 100644 --- a/src/config.c +++ b/src/config.c @@ -1211,6 +1211,8 @@ static void * get_zip_data(zip_t * zip, const char * name, size_t * outSize) return data; } +#include "title/TitleSequence.h" + static void title_sequence_open(const char *path, const char *customName) { utf8 titlePath[MAX_PATH]; @@ -1219,6 +1221,9 @@ static void title_sequence_open(const char *path, const char *customName) int fileEnumHandle, i, preset; char parts[3 * 128], *token, *part1, *part2; + TitleSequence * seq = LoadTitleSequence(path); + FreeTitleSequence(seq); + int error; zip_t * zip = zip_open(path, ZIP_RDONLY, &error); if (zip == NULL) { diff --git a/src/core/Collections.hpp b/src/core/Collections.hpp index 4caceb6dd1..c937bfed26 100644 --- a/src/core/Collections.hpp +++ b/src/core/Collections.hpp @@ -18,6 +18,7 @@ #include #include "../common.h" +#include "Memory.hpp" #include "String.hpp" namespace Collections @@ -100,5 +101,24 @@ namespace Collections } } + template + static typename TCollection::value_type * ToArray(const TCollection &collection) + { + size_t count = collection.size(); + if (count == 0) + { + return nullptr; + } + + auto * items = Memory::AllocateArray(count); + size_t i = 0; + for (const auto &item : collection) + { + items[i] = item; + i++; + } + return items; + } + #pragma endregion } diff --git a/src/core/Path.cpp b/src/core/Path.cpp index f0e6735381..ad729785c1 100644 --- a/src/core/Path.cpp +++ b/src/core/Path.cpp @@ -71,6 +71,16 @@ namespace Path lastPathSeperator + 1; } + utf8 * GetFileNameWithoutExtension(const utf8 * path) + { + size_t maxSize = String::SizeOf(path) + 1; + utf8 * result = Memory::Allocate(maxSize); + GetFileNameWithoutExtension(result, maxSize, path); + size_t reducedSize = String::SizeOf(path) + 1; + result = Memory::Reallocate(result, reducedSize); + return result; + } + utf8 * GetFileNameWithoutExtension(utf8 * buffer, size_t bufferSize, const utf8 * path) { path = GetFileName(path); diff --git a/src/core/Path.hpp b/src/core/Path.hpp index 14353a56f7..ef0ca98fc6 100644 --- a/src/core/Path.hpp +++ b/src/core/Path.hpp @@ -26,6 +26,7 @@ namespace Path utf8 * Append(utf8 * buffer, size_t bufferSize, const utf8 * src); utf8 * GetDirectory(utf8 * buffer, size_t bufferSize, const utf8 * path); const utf8 * GetFileName(const utf8 * path); + utf8 * GetFileNameWithoutExtension(const utf8 * path); utf8 * GetFileNameWithoutExtension(utf8 * buffer, size_t bufferSize, const utf8 * path); const utf8 * GetExtension(const utf8 * path); utf8 * GetAbsolute(utf8 * buffer, size_t bufferSize, const utf8 * relativePath); diff --git a/src/interface/title_sequences.c b/src/interface/title_sequences.c index f94fd0daf7..0ccd075188 100644 --- a/src/interface/title_sequences.c +++ b/src/interface/title_sequences.c @@ -17,6 +17,7 @@ #include "../localisation/string_ids.h" #include "../rct2.h" #include "../title.h" +#include "../title/TitleSequence.h" #include "../util/util.h" #include "title_sequences.h" #include "window.h" diff --git a/src/title.c b/src/title.c index 130b5765e3..113f0ddb34 100644 --- a/src/title.c +++ b/src/title.c @@ -35,6 +35,7 @@ #include "scenario.h" #include "ScenarioRepository.h" #include "ScenarioSources.h" +#include "title/TitleSequence.h" #include "util/util.h" #include "world/climate.h" #include "world/map.h" diff --git a/src/title.h b/src/title.h index 244f1b994b..ad93583be4 100644 --- a/src/title.h +++ b/src/title.h @@ -20,21 +20,6 @@ #include #include "drawing/drawing.h" -enum { - TITLE_SCRIPT_WAIT, - TITLE_SCRIPT_LOADMM, - TITLE_SCRIPT_LOCATION, - TITLE_SCRIPT_ROTATE, - TITLE_SCRIPT_ZOOM, - TITLE_SCRIPT_RESTART, - TITLE_SCRIPT_LOAD, - TITLE_SCRIPT_END, - TITLE_SCRIPT_SPEED, - TITLE_SCRIPT_LOOP, - TITLE_SCRIPT_ENDLOOP, - TITLE_SCRIPT_LOADRCT1, -}; - extern bool gTitleHideVersionInfo; extern sint32 gTitleScriptCommand; diff --git a/src/title/TitleSequence.cpp b/src/title/TitleSequence.cpp new file mode 100644 index 0000000000..fafe93b9e7 --- /dev/null +++ b/src/title/TitleSequence.cpp @@ -0,0 +1,267 @@ +#pragma region Copyright (c) 2014-2016 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 "../common.h" +#include +#include +#include +#include "../core/Collections.hpp" +#include "../core/Math.hpp" +#include "../core/Memory.hpp" +#include "../core/Path.hpp" +#include "../core/String.hpp" +#include "TitleSequence.h" + +static std::vector GetSaves(zip_t * zip); +static std::vector LegacyScriptRead(utf8 * script, size_t scriptLength, std::vector saves); +static void LegacyScriptGetLine(SDL_RWops * file, char * parts); +static void * GetZipFileData(zip_t * zip, const char * name, size_t * outSize); + +extern "C" +{ + TitleSequence * LoadTitleSequence(const utf8 * path) + { + int error; + zip_t * zip = zip_open(path, ZIP_RDONLY, &error); + if (zip == nullptr) + { + // Unable to open zip + return nullptr; + } + + size_t scriptLength; + char * script = (char *)GetZipFileData(zip, "script.txt", &scriptLength); + if (script == NULL) { + // Unable to open script + zip_close(zip); + return nullptr; + } + + std::vector saves = GetSaves(zip); + std::vector commands = LegacyScriptRead(script, scriptLength, saves); + + TitleSequence * seq = Memory::Allocate(); + seq->Name = Path::GetFileNameWithoutExtension(path); + seq->Path = String::Duplicate(path); + seq->NumSaves = saves.size(); + seq->Saves = Collections::ToArray(saves); + seq->NumCommands = commands.size(); + seq->Commands = Collections::ToArray(commands); + return seq; + } + + void FreeTitleSequence(TitleSequence * seq) + { + Memory::Free(seq->Name); + Memory::Free(seq->Path); + Memory::Free(seq->Commands); + for (size_t i = 0; i < seq->NumSaves; i++) + { + Memory::Free(seq->Saves[i]); + } + Memory::Free(seq->Saves); + } +} + +static std::vector GetSaves(zip_t * zip) +{ + std::vector saves; + int numFiles = zip_get_num_files(zip); + for (int i = 0; i < numFiles; i++) + { + const utf8 * name = zip_get_name(zip, i, ZIP_FL_ENC_GUESS); + const utf8 * ext = Path::GetExtension(name); + if (String::Equals(ext, ".sv6", true) || + String::Equals(ext, ".sc6", true)) + { + saves.push_back(String::Duplicate(name)); + } + } + return saves; +} + +static std::vector LegacyScriptRead(utf8 * script, size_t scriptLength, std::vector saves) +{ + std::vector commands; + SDL_RWops * file = SDL_RWFromMem(script, (int)scriptLength); + do { + char parts[3 * 128], *token, *part1, *part2; + LegacyScriptGetLine(file, parts); + + token = &parts[0 * 128]; + part1 = &parts[1 * 128]; + part2 = &parts[2 * 128]; + TitleCommand command = { 0 }; + command.Type = TITLE_SCRIPT_UNDEFINED; + + if (token[0] != 0) + { + if (_stricmp(token, "LOAD") == 0) + { + command.Type = TITLE_SCRIPT_LOAD; + command.SaveIndex = UINT8_MAX; + for (size_t i = 0; i < saves.size(); i++) + { + if (String::Equals(part1, saves[i], true)) + { + command.SaveIndex = (uint8)i; + break; + } + } + } + else if (_stricmp(token, "LOCATION") == 0) + { + command.Type = TITLE_SCRIPT_LOCATION; + command.X = atoi(part1) & 0xFF; + command.Y = atoi(part2) & 0xFF; + } + else if (_stricmp(token, "ROTATE") == 0) + { + command.Type = TITLE_SCRIPT_ROTATE; + command.Rotations = atoi(part1) & 0xFF; + } + else if (_stricmp(token, "ZOOM") == 0) + { + command.Type = TITLE_SCRIPT_ZOOM; + command.Zoom = atoi(part1) & 0xFF; + } + else if (_stricmp(token, "SPEED") == 0) + { + command.Type = TITLE_SCRIPT_SPEED; + command.Speed = Math::Max(1, Math::Min(4, atoi(part1) & 0xFF)); + } + else if (_stricmp(token, "WAIT") == 0) + { + command.Type = TITLE_SCRIPT_WAIT; + command.Seconds = atoi(part1) & 0xFF; + } + else if (_stricmp(token, "RESTART") == 0) + { + command.Type = TITLE_SCRIPT_RESTART; + } + else if (_stricmp(token, "END") == 0) + { + command.Type = TITLE_SCRIPT_END; + } + else if (_stricmp(token, "LOADMM") == 0) + { + command.Type = TITLE_SCRIPT_LOADMM; + } + else if (_stricmp(token, "LOADRCT1") == 0) + { + command.Type = TITLE_SCRIPT_LOADRCT1; + command.SaveIndex = atoi(part1) & 0xFF; + } + } + if (command.Type != TITLE_SCRIPT_UNDEFINED) + { + commands.push_back(command); + } + } while (SDL_RWtell(file) < (int)scriptLength); + SDL_RWclose(file); + + return commands; +} + +static void LegacyScriptGetLine(SDL_RWops * file, char * parts) +{ + for (int i = 0; i < 3; i++) + { + parts[i * 128] = 0; + } + int part = 0; + int cindex = 0; + int whitespace = 1; + int comment = 0; + int load = 0; + for (; part < 3;) + { + int c = 0; + if (SDL_RWread(file, &c, 1, 1) != 1) + { + c = EOF; + } + if (c == '\n' || c == '\r' || c == EOF) + { + parts[part * 128 + cindex] = 0; + return; + } + else if (c == '#') + { + parts[part * 128 + cindex] = 0; + comment = 1; + } + else if (c == ' ' && !comment && !load) + { + if (!whitespace) + { + if (part == 0 && cindex == 4 && _strnicmp(parts, "LOAD", 4) == 0) + { + load = true; + } + parts[part * 128 + cindex] = 0; + part++; + cindex = 0; + } + } + else if (!comment) + { + whitespace = 0; + if (cindex < 127) + { + parts[part * 128 + cindex] = c; + cindex++; + } + else + { + parts[part * 128 + cindex] = 0; + part++; + cindex = 0; + } + } + } +} + +static void * GetZipFileData(zip_t * zip, const char * name, size_t * outSize) +{ + void * data = nullptr; + size_t dataSize = 0; + + zip_stat_t zipFileStat; + if (zip_stat(zip, name, 0, &zipFileStat) == ZIP_ER_OK) + { + zip_file_t * zipFile = zip_fopen(zip, name, 0); + if (zipFile != nullptr) + { + if (zipFileStat.size < SIZE_MAX) + { + dataSize = zipFileStat.size; + data = malloc(dataSize); + size_t readBytes = zip_fread(zipFile, data, dataSize); + if (readBytes != dataSize) + { + free(data); + data = NULL; + dataSize = 0; + } + zip_fclose(zipFile); + } + } + } + + if (outSize != NULL) *outSize = dataSize; + return data; +} diff --git a/src/title/TitleSequence.h b/src/title/TitleSequence.h new file mode 100644 index 0000000000..4f4d061970 --- /dev/null +++ b/src/title/TitleSequence.h @@ -0,0 +1,75 @@ +#pragma region Copyright (c) 2014-2016 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 + +#pragma once + +#include "../common.h" + +typedef struct TitleCommand +{ + uint8 Type; + union { + uint8 SaveIndex; // LOAD (this index is internal only) + struct // LOCATION + { + uint8 X; + uint8 Y; + }; + uint8 Rotations; // ROTATE (counter-clockwise) + uint8 Zoom; // ZOOM + uint8 Speed; // SPEED + uint8 Seconds; // WAIT + }; +} TitleCommand; + +typedef struct TitleSequence +{ + const utf8 * Name; + const utf8 * Path; + + size_t NumCommands; + TitleCommand * Commands; + + size_t NumSaves; + utf8 * * Saves; +} TitleSequence; + +enum TITLE_SCRIPT +{ + TITLE_SCRIPT_UNDEFINED = 0xFF, + TITLE_SCRIPT_WAIT = 0, + TITLE_SCRIPT_LOADMM, + TITLE_SCRIPT_LOCATION, + TITLE_SCRIPT_ROTATE, + TITLE_SCRIPT_ZOOM, + TITLE_SCRIPT_RESTART, + TITLE_SCRIPT_LOAD, + TITLE_SCRIPT_END, + TITLE_SCRIPT_SPEED, + TITLE_SCRIPT_LOOP, + TITLE_SCRIPT_ENDLOOP, + TITLE_SCRIPT_LOADRCT1, +}; + +#ifdef __cplusplus +extern "C" +{ +#endif + TitleSequence * LoadTitleSequence(const utf8 * path); + void FreeTitleSequence(TitleSequence * seq); +#ifdef __cplusplus +} +#endif diff --git a/src/windows/title_command_editor.c b/src/windows/title_command_editor.c index 50cd0e8460..34b686eab6 100644 --- a/src/windows/title_command_editor.c +++ b/src/windows/title_command_editor.c @@ -26,6 +26,7 @@ #include "../interface/themes.h" #include "../interface/title_sequences.h" #include "../title.h" +#include "../title/TitleSequence.h" #include "../util/util.h" #include "dropdown.h" diff --git a/src/windows/title_editor.c b/src/windows/title_editor.c index 003d846652..94a6dffa6b 100644 --- a/src/windows/title_editor.c +++ b/src/windows/title_editor.c @@ -30,6 +30,7 @@ #include "../interface/themes.h" #include "../sprites.h" #include "../title.h" +#include "../title/TitleSequence.h" #include "../interface/title_sequences.h" #include "error.h" #include "../scenario.h"