From 5443ff61ccad4c3c0db92f6442f6404dbdc57d65 Mon Sep 17 00:00:00 2001 From: Ted John Date: Mon, 5 Dec 2016 03:30:25 +0000 Subject: [PATCH] Fix add park to sequence --- openrct2.vcxproj | 2 ++ src/core/File.cpp | 44 +++++++++++++++++++++++ src/core/File.h | 24 +++++++++++++ src/core/IStream.hpp | 1 + src/core/String.cpp | 71 ++++++++++++++++++++++++++++++++++++ src/core/String.hpp | 3 ++ src/title/TitleSequence.cpp | 72 +++++++++++++++++++++++++++++++++++++ src/title/TitleSequence.h | 1 + src/windows/title_editor.c | 2 +- 9 files changed, 219 insertions(+), 1 deletion(-) create mode 100644 src/core/File.cpp create mode 100644 src/core/File.h diff --git a/openrct2.vcxproj b/openrct2.vcxproj index a3867bc4d0..674ef44906 100644 --- a/openrct2.vcxproj +++ b/openrct2.vcxproj @@ -101,6 +101,7 @@ + @@ -434,6 +435,7 @@ + diff --git a/src/core/File.cpp b/src/core/File.cpp new file mode 100644 index 0000000000..195136cf6d --- /dev/null +++ b/src/core/File.cpp @@ -0,0 +1,44 @@ +#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 "Console.hpp" +#include "File.h" +#include "FileStream.hpp" +#include "String.hpp" + +namespace File +{ + void * ReadAllBytes(const utf8 * path, size_t * length) + { + void * result = nullptr; + + FileStream fs = FileStream(path, FILE_MODE_OPEN); + uint64 fsize = fs.GetLength(); + if (fsize > SIZE_MAX) + { + std::string message = String::StdFormat("'%s' exceeds maximum length of %lld bytes.", SIZE_MAX); + throw IOException(message); + } + else + { + result = fs.ReadArray(fsize); + } + *length = fsize; + return result; + } +} diff --git a/src/core/File.h b/src/core/File.h new file mode 100644 index 0000000000..a911c3fedc --- /dev/null +++ b/src/core/File.h @@ -0,0 +1,24 @@ +#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" + +namespace File +{ + void * ReadAllBytes(const utf8 * path, size_t * length); +} diff --git a/src/core/IStream.hpp b/src/core/IStream.hpp index ac49112b46..73a92bf73d 100644 --- a/src/core/IStream.hpp +++ b/src/core/IStream.hpp @@ -116,4 +116,5 @@ class IOException : public Exception { public: IOException(const char * message) : Exception(message) { } + IOException(const std::string &message) : Exception(message) { } }; diff --git a/src/core/String.cpp b/src/core/String.cpp index 2240ffd32f..2a0223da3c 100644 --- a/src/core/String.cpp +++ b/src/core/String.cpp @@ -34,6 +34,15 @@ namespace String else return std::string(str); } + std::string StdFormat(const utf8 * format, ...) + { + va_list args; + va_start(args, format); + const utf8 * buffer = Format_VA(format, args); + va_end(args); + return ToStd(buffer); + } + bool IsNullOrEmpty(const utf8 * str) { return str == nullptr || str[0] == '\0'; @@ -150,6 +159,68 @@ namespace String return buffer; } + utf8 * Format(const utf8 * format, ...) + { + va_list args; + va_start(args, format); + utf8 * result = Format_VA(format, args); + va_end(args); + return result; + } + + utf8 * Format_VA(const utf8 * format, va_list args) + { + va_list args1, args2; + va_copy(args1, args); + va_copy(args2, args); + + // Try to format to a initial buffer, enlarge if not big enough + size_t bufferSize = 4096; + utf8 * buffer = Memory::Allocate(bufferSize); + + // Start with initial buffer + int len = vsnprintf(buffer, bufferSize, format, args); + if (len < 0) + { + Memory::Free(buffer); + va_end(args1); + va_end(args2); + + // An error occured... + return nullptr; + } + + size_t requiredSize = (size_t)len + 1; + if (requiredSize > bufferSize) + { + // Try again with bigger buffer + buffer = Memory::Reallocate(buffer, bufferSize); + int len = vsnprintf(buffer, bufferSize, format, args); + if (len < 0) + { + Memory::Free(buffer); + va_end(args1); + va_end(args2); + + // An error occured... + return nullptr; + } + } + else + { + // Reduce buffer size to only what was required + bufferSize = requiredSize; + buffer = Memory::Reallocate(buffer, bufferSize); + } + + // Ensure buffer is terminated + buffer[bufferSize - 1] = '\0'; + + va_end(args1); + va_end(args2); + return buffer; + } + utf8 * AppendFormat(utf8 * buffer, size_t bufferSize, const utf8 * format, ...) { va_list args; diff --git a/src/core/String.hpp b/src/core/String.hpp index db0eaeb058..65d94e9c6b 100644 --- a/src/core/String.hpp +++ b/src/core/String.hpp @@ -24,6 +24,7 @@ namespace String constexpr const utf8 * Empty = ""; std::string ToStd(const utf8 * str); + std::string StdFormat(const utf8 * format, ...); bool IsNullOrEmpty(const utf8 * str); bool Equals(const utf8 * a, const utf8 * b, bool ignoreCase = false); @@ -44,6 +45,8 @@ namespace String utf8 * Set(utf8 * buffer, size_t bufferSize, const utf8 * src, size_t srcSize); utf8 * Append(utf8 * buffer, size_t bufferSize, const utf8 * src); utf8 * Format(utf8 * buffer, size_t bufferSize, const utf8 * format, ...); + utf8 * Format(const utf8 * format, ...); + utf8 * Format_VA(const utf8 * format, va_list args); utf8 * AppendFormat(utf8 * buffer, size_t bufferSize, const utf8 * format, ...); utf8 * Duplicate(const utf8 * src); diff --git a/src/title/TitleSequence.cpp b/src/title/TitleSequence.cpp index 6e966eea59..a49e5179dc 100644 --- a/src/title/TitleSequence.cpp +++ b/src/title/TitleSequence.cpp @@ -19,6 +19,7 @@ #include "../common.h" #include "../core/Collections.hpp" #include "../core/Console.hpp" +#include "../core/File.h" #include "../core/FileScanner.h" #include "../core/FileStream.hpp" #include "../core/Guard.hpp" @@ -30,6 +31,11 @@ #include "../core/Zip.h" #include "TitleSequence.h" +extern "C" +{ + #include "../platform/platform.h" +} + static std::vector GetSaves(const utf8 * path); static std::vector GetSaves(IZipArchive * zip); static std::vector LegacyScriptRead(utf8 * script, size_t scriptLength, std::vector saves); @@ -194,6 +200,72 @@ extern "C" Memory::Free(script); return success; } + + bool TileSequenceAddPark(TitleSequence * seq, const utf8 * path, const utf8 * name) + { + // Determine new path (relative for zip) + utf8 dstPath[260]; + if (seq->IsZip) + { + String::Set(dstPath, sizeof(dstPath), name); + } + else + { + String::Set(dstPath, sizeof(dstPath), seq->Path); + Path::Append(dstPath, sizeof(dstPath), name); + } + + // Get new save index + size_t index = SIZE_MAX; + for (size_t i = 0; i < seq->NumSaves; i++) + { + if (String::Equals(seq->Saves[i], path, true)) + { + index = i; + break; + } + } + if (index == SIZE_MAX) + { + seq->Saves = Memory::ReallocateArray(seq->Saves, seq->NumSaves + 1); + Guard::Assert(seq->Saves != nullptr, GUARD_LINE); + index = seq->NumSaves; + seq->NumSaves++; + } + seq->Saves[index] = String::Duplicate(dstPath); + + if (seq->IsZip) + { + try + { + size_t fsize; + void * fdata = File::ReadAllBytes(path, &fsize); + + IZipArchive * zip = Zip::TryOpen(seq->Path, ZIP_ACCESS_WRITE); + if (zip == nullptr) + { + Console::Error::WriteLine("Unable to open '%s'", seq->Path); + return false; + } + zip->SetFileData(dstPath, fdata, fsize); + delete zip; + Memory::Free(fdata); + } + catch (Exception ex) + { + Console::Error::WriteLine(ex.GetMessage()); + } + } + else + { + if (!platform_file_copy(path, dstPath, true)) + { + Console::Error::WriteLine("Unable to copy '%s' to '%s'", path, dstPath); + return false; + } + } + return true; + } } static std::vector GetSaves(const utf8 * directory) diff --git a/src/title/TitleSequence.h b/src/title/TitleSequence.h index 77be35e7e3..a4466e4309 100644 --- a/src/title/TitleSequence.h +++ b/src/title/TitleSequence.h @@ -91,6 +91,7 @@ extern "C" TitleSequenceParkHandle * TitleSequenceGetParkHandle(TitleSequence * seq, size_t index); void TitleSequenceCloseParkHandle(TitleSequenceParkHandle * handle); bool TileSequenceSave(TitleSequence * seq); + bool TileSequenceAddPark(TitleSequence * seq, const utf8 * path, const utf8 * name); bool TitleSequenceIsLoadCommand(const TitleCommand * command); #ifdef __cplusplus diff --git a/src/windows/title_editor.c b/src/windows/title_editor.c index b459813ba9..06d3a516dc 100644 --- a/src/windows/title_editor.c +++ b/src/windows/title_editor.c @@ -1022,7 +1022,7 @@ static void window_title_editor_add_park_callback(int result, const utf8 * path) return; } - // title_sequence_add_save(gCurrentTitleSequence, path, newName); + TileSequenceAddPark(_editingTitleSequence, path, filename); } static void window_title_editor_save_sequence()