diff --git a/data/language/en-GB.txt b/data/language/en-GB.txt index 0eaa1171dd..c63e11c73f 100644 --- a/data/language/en-GB.txt +++ b/data/language/en-GB.txt @@ -3839,3 +3839,5 @@ STR_7006 :Draw border around image buttons STR_7007 :Ride Type STR_7008 :Unknown ride type ({INT32}) STR_7009 :Receiving scripts… +STR_7010 :Could not start replay, file ‘{STRING}’ doesn’t exist or isn’t valid +STR_7011 :Could not start replay diff --git a/src/openrct2/ReplayManager.cpp b/src/openrct2/ReplayManager.cpp index f20979fce2..7a7a103f47 100644 --- a/src/openrct2/ReplayManager.cpp +++ b/src/openrct2/ReplayManager.cpp @@ -27,12 +27,16 @@ #include "config/Config.h" #include "core/Compression.h" #include "core/DataSerialiser.h" +#include "core/EnumUtils.hpp" #include "core/FileStream.h" #include "core/FileSystem.hpp" #include "core/Path.hpp" +#include "core/String.hpp" #include "entity/EntityRegistry.h" #include "entity/EntityTweener.h" #include "interface/Window.h" +#include "localisation/Formatting.h" +#include "localisation/StringIds.h" #include "management/NewsItem.h" #include "object/ObjectManager.h" #include "object/ObjectRepository.h" @@ -41,7 +45,10 @@ #include "world/Park.h" #include +#include #include +#include +#include #include namespace OpenRCT2 @@ -121,6 +128,13 @@ namespace OpenRCT2 NORMALISATION, }; + static constexpr std::array modeToName = { + "NONE", + "RECORDING", + "PLAYING", + "NORMALISATION", + }; + public: virtual ~ReplayManager() { @@ -415,23 +429,25 @@ namespace OpenRCT2 } } - virtual bool StartPlayback(const std::string& file) override + void StartPlayback(const std::string& file) override { if (_mode != ReplayMode::NONE && _mode != ReplayMode::NORMALISATION) - return false; + throw std::invalid_argument(std::string("Unexpected mode ") + modeToName[EnumValue(_mode)]); auto replayData = std::make_unique(); - if (!ReadReplayData(file, *replayData)) + try { - LOG_ERROR("Unable to read replay data."); - return false; + ReadReplayData(file, *replayData); + } + catch (const std::exception&) + { + throw; } if (!LoadReplayDataMap(*replayData)) { - LOG_ERROR("Unable to load map."); - return false; + throw std::runtime_error("Unable to load map."); } getGameState().currentTicks = replayData->tickStart; @@ -447,8 +463,6 @@ namespace OpenRCT2 if (_mode != ReplayMode::NORMALISATION) _mode = ReplayMode::PLAYING; - - return true; } virtual bool IsPlaybackStateMismatching() const override @@ -485,7 +499,11 @@ namespace OpenRCT2 { _mode = ReplayMode::NORMALISATION; - if (!StartPlayback(file)) + try + { + StartPlayback(file); + } + catch (const std::invalid_argument&) { return false; } @@ -595,23 +613,31 @@ namespace OpenRCT2 return recFile.data; } - bool ReadReplayData(const std::string& file, ReplayRecordData& data) + void ReadReplayData(const std::string& file, ReplayRecordData& data) { fs::path filePath = file; - if (filePath.extension() != ".parkrep") - filePath += ".parkrep"; - if (filePath.is_relative()) + if (filePath.is_absolute()) { + if (!fs::exists(filePath)) + { + throw std::runtime_error(FormatStringID(STR_REPLAY_FILE_NOT_FOUND, filePath.u8string().c_str())); + } + } + else if (filePath.is_relative()) + { + if (filePath.extension() != ".parkrep") + filePath += ".parkrep"; fs::path replayPath = GetContext()->GetPlatformEnvironment().GetDirectoryPath( DirBase::user, DirId::replayRecordings) / filePath; - if (fs::is_regular_file(replayPath)) - filePath = replayPath; + filePath = replayPath; } if (!fs::is_regular_file(filePath)) - return false; + { + throw std::runtime_error(FormatStringID(STR_REPLAY_FILE_NOT_FOUND, filePath.u8string().c_str())); + } FileStream fileStream(filePath, FileMode::open); MemoryStream stream = DecompressFile(fileStream); @@ -620,7 +646,7 @@ namespace OpenRCT2 DataSerialiser serialiser(false, stream); if (!Serialise(serialiser, data)) { - return false; + throw std::runtime_error(LanguageGetString(STR_REPLAY_NOT_STARTED)); } // Reset position of all streams. @@ -628,8 +654,6 @@ namespace OpenRCT2 data.parkParams.SetPosition(0); data.cheatData.SetPosition(0); data.gameStateSnapshots.SetPosition(0); - - return true; } bool SerialiseCheats(DataSerialiser& serialiser) diff --git a/src/openrct2/ReplayManager.h b/src/openrct2/ReplayManager.h index d3f3a98bc0..a6a9ef7410 100644 --- a/src/openrct2/ReplayManager.h +++ b/src/openrct2/ReplayManager.h @@ -60,7 +60,7 @@ namespace OpenRCT2 virtual bool StopRecording(bool discard = false) = 0; virtual bool GetCurrentReplayInfo(ReplayRecordInfo& info) const = 0; - virtual bool StartPlayback(const std::string& file) = 0; + virtual void StartPlayback(const std::string& file) = 0; virtual bool IsPlaybackStateMismatching() const = 0; virtual bool StopPlayback() = 0; diff --git a/src/openrct2/interface/InteractiveConsole.cpp b/src/openrct2/interface/InteractiveConsole.cpp index 1b8a96615d..f76ae92885 100644 --- a/src/openrct2/interface/InteractiveConsole.cpp +++ b/src/openrct2/interface/InteractiveConsole.cpp @@ -1485,25 +1485,33 @@ static void ConsoleCommandReplayStart(InteractiveConsole& console, const argumen std::string name = argv[0]; auto* replayManager = OpenRCT2::GetContext()->GetReplayManager(); - if (replayManager->StartPlayback(name)) + + try { - OpenRCT2::ReplayRecordInfo info; - replayManager->GetCurrentReplayInfo(info); - - std::time_t ts = info.TimeRecorded; - - char recordingDate[128] = {}; - std::strftime(recordingDate, sizeof(recordingDate), "%c", std::localtime(&ts)); - - const char* logFmt = "Replay playback started: %s\n" - " Date Recorded: %s\n" - " Ticks: %u\n" - " Commands: %u\n" - " Checksums: %u"; - - console.WriteFormatLine(logFmt, info.FilePath.c_str(), recordingDate, info.Ticks, info.NumCommands, info.NumChecksums); - Console::WriteLine(logFmt, info.FilePath.c_str(), recordingDate, info.Ticks, info.NumCommands, info.NumChecksums); + replayManager->StartPlayback(name); } + catch (const std::exception& e) + { + console.WriteLine(e.what()); + return; + } + + OpenRCT2::ReplayRecordInfo info; + replayManager->GetCurrentReplayInfo(info); + + std::time_t ts = info.TimeRecorded; + + char recordingDate[128] = {}; + std::strftime(recordingDate, sizeof(recordingDate), "%c", std::localtime(&ts)); + + const char* logFmt = "Replay playback started: %s\n" + " Date Recorded: %s\n" + " Ticks: %u\n" + " Commands: %u\n" + " Checksums: %u"; + + console.WriteFormatLine(logFmt, info.FilePath.c_str(), recordingDate, info.Ticks, info.NumCommands, info.NumChecksums); + Console::WriteLine(logFmt, info.FilePath.c_str(), recordingDate, info.Ticks, info.NumCommands, info.NumChecksums); } static void ConsoleCommandReplayStop(InteractiveConsole& console, const arguments_t& argv) diff --git a/src/openrct2/localisation/StringIds.h b/src/openrct2/localisation/StringIds.h index b1fedb852c..6d4dbe7f09 100644 --- a/src/openrct2/localisation/StringIds.h +++ b/src/openrct2/localisation/StringIds.h @@ -1758,6 +1758,9 @@ enum : StringId // Window: Error STR_AUDIO_FILE_TRUNCATED = 7003, + STR_REPLAY_FILE_NOT_FOUND = 7010, + STR_REPLAY_NOT_STARTED = 7011, + // Have to include resource strings (from scenarios and objects) for the time being now that language is partially working /* MAX_STR_COUNT = 32768 */ // MAX_STR_COUNT - upper limit for number of strings, not the current count strings }; diff --git a/test/tests/ReplayTests.cpp b/test/tests/ReplayTests.cpp index 7780b158d4..e60c0f7c23 100644 --- a/test/tests/ReplayTests.cpp +++ b/test/tests/ReplayTests.cpp @@ -9,8 +9,10 @@ #include "TestData.h" +#include #include #include +#include #include #include #include @@ -22,6 +24,7 @@ #include #include #include +#include #include using namespace OpenRCT2; @@ -58,7 +61,7 @@ static std::vector GetReplayFiles() { ReplayTestData test; test.name = sanitizeTestName(scanner->GetFileInfo().Name); - test.filePath = scanner->GetPath(); + test.filePath = Path::GetAbsolute(scanner->GetPath()); res.push_back(std::move(test)); } return res; @@ -84,8 +87,15 @@ TEST_P(ReplayTests, RunReplay) IReplayManager* replayManager = context->GetReplayManager(); ASSERT_NE(replayManager, nullptr); - bool startedReplay = replayManager->StartPlayback(replayFile); - ASSERT_TRUE(startedReplay); + try + { + replayManager->StartPlayback(replayFile); + } + catch (const std::exception& e) + { + LOG_WARNING("Can't start replay!. %s", e.what()); + FAIL(); + } while (replayManager->IsReplaying()) {