1
0
mirror of https://github.com/OpenRCT2/OpenRCT2 synced 2026-01-15 19:13:07 +01:00

Refactor loading of SC6 information when scanning scenarios

This commit is contained in:
Ted John
2017-01-30 18:55:17 +00:00
parent 94d38b9f7a
commit c9d7cd8216
7 changed files with 154 additions and 81 deletions

View File

@@ -16,6 +16,7 @@
#include "core/FileStream.hpp"
#include "FileClassifier.h"
#include "rct12/SawyerEncoding.h"
extern "C"
{
@@ -26,7 +27,6 @@ extern "C"
static bool TryClassifyAsS6(IStream * stream, ClassifiedFile * result);
static bool TryClassifyAsS4(IStream * stream, ClassifiedFile * result);
static bool TryClassifyAsTD4_TD6(IStream * stream, ClassifiedFile * result);
static bool sawyercoding_try_read_chunk(void * dst, size_t expectedSize, IStream * stream);
bool TryClassifyFile(const std::string &path, ClassifiedFile * result)
{
@@ -72,7 +72,7 @@ bool TryClassifyFile(IStream * stream, ClassifiedFile * result)
static bool TryClassifyAsS6(IStream * stream, ClassifiedFile * result)
{
rct_s6_header s6Header;
if (sawyercoding_try_read_chunk(&s6Header, sizeof(rct_s6_header), stream))
if (SawyerEncoding::TryReadChunk(&s6Header, stream))
{
if (s6Header.type == S6_TYPE_SAVEDGAME)
{
@@ -145,31 +145,3 @@ static bool TryClassifyAsTD4_TD6(IStream * stream, ClassifiedFile * result)
return success;
}
static bool sawyercoding_try_read_chunk(void * dst, size_t expectedSize, IStream * stream)
{
uint64 originalPosition = stream->GetPosition();
bool success = false;
auto header = stream->ReadValue<sawyercoding_chunk_header>();
switch (header.encoding) {
case CHUNK_ENCODING_NONE:
case CHUNK_ENCODING_RLE:
case CHUNK_ENCODING_RLECOMPRESSED:
case CHUNK_ENCODING_ROTATE:
uint8 * compressedData = Memory::Allocate<uint8>(header.length);
if (stream->TryRead(compressedData, header.length) == header.length)
{
size_t uncompressedLength = sawyercoding_read_chunk_buffer((uint8 *)dst, compressedData, header, expectedSize);
if (uncompressedLength == expectedSize)
{
success = true;
}
}
Memory::Free(compressedData);
break;
}
stream->SetPosition(originalPosition);
return success;
}

View File

@@ -73,6 +73,7 @@
<ClCompile Include="audio\MemoryAudioSource.cpp" />
<ClCompile Include="audio\NullAudioSource.cpp" />
<ClCompile Include="FileClassifier.cpp" />
<ClCompile Include="rct12\SawyerEncoding.cpp" />
<ClCompile Include="rct2\addresses.c" />
<ClCompile Include="audio\audio.c" />
<ClCompile Include="cheats.c" />
@@ -414,6 +415,7 @@
<ClInclude Include="audio\AudioSource.h" />
<ClInclude Include="FileClassifier.h" />
<ClInclude Include="rct12.h" />
<ClInclude Include="rct12\SawyerEncoding.h" />
<ClInclude Include="rct2\addresses.h" />
<ClInclude Include="audio\audio.h" />
<ClInclude Include="cheats.h" />
@@ -596,4 +598,4 @@
<Image Include="..\..\resources\logo\icon.ico" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
</Project>
</Project>

View File

@@ -0,0 +1,57 @@
#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 "../core/IStream.hpp"
#include "SawyerEncoding.h"
extern "C"
{
#include "../util/sawyercoding.h"
}
namespace SawyerEncoding
{
bool TryReadChunk(void * dst, size_t expectedSize, IStream * stream)
{
uint64 originalPosition = stream->GetPosition();
bool success = false;
auto header = stream->ReadValue<sawyercoding_chunk_header>();
switch (header.encoding) {
case CHUNK_ENCODING_NONE:
case CHUNK_ENCODING_RLE:
case CHUNK_ENCODING_RLECOMPRESSED:
case CHUNK_ENCODING_ROTATE:
uint8 * compressedData = Memory::Allocate<uint8>(header.length);
if (stream->TryRead(compressedData, header.length) == header.length)
{
size_t uncompressedLength = sawyercoding_read_chunk_buffer((uint8 *)dst, compressedData, header, expectedSize);
if (uncompressedLength == expectedSize)
{
success = true;
}
}
Memory::Free(compressedData);
break;
}
if (!success)
{
stream->SetPosition(originalPosition);
}
return success;
}
}

View File

@@ -0,0 +1,32 @@
#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"
interface IStream;
namespace SawyerEncoding
{
bool TryReadChunk(void * dst, size_t expectedSize, IStream * stream);
template<typename T>
bool TryReadChunk(T * dst, IStream * stream)
{
return TryReadChunk(dst, sizeof(T), stream);
}
}

View File

@@ -25,6 +25,7 @@
#include "../core/String.hpp"
#include "../core/Util.hpp"
#include "../PlatformEnvironment.h"
#include "../rct12/SawyerEncoding.h"
#include "ScenarioRepository.h"
#include "ScenarioSources.h"
@@ -142,8 +143,10 @@ public:
_scenarios.clear();
// Scan RCT2 directory
std::string rct1dir = _env->GetDirectoryPath(DIRBASE::RCT1, DIRID::SCENARIO);
std::string rct2dir = _env->GetDirectoryPath(DIRBASE::RCT2, DIRID::SCENARIO);
std::string openrct2dir = _env->GetDirectoryPath(DIRBASE::USER, DIRID::SCENARIO);
Scan(rct1dir);
Scan(rct2dir);
Scan(openrct2dir);
@@ -253,7 +256,7 @@ private:
{
utf8 pattern[MAX_PATH];
String::Set(pattern, sizeof(pattern), directory.c_str());
Path::Append(pattern, sizeof(pattern), "*.sc6");
Path::Append(pattern, sizeof(pattern), "*.sc4;*.sc6");
IFileScanner * scanner = Path::ScanDirectory(pattern, true);
while (scanner->Next())
@@ -265,43 +268,88 @@ private:
delete scanner;
}
void AddScenario(const utf8 * path, uint64 timestamp)
void AddScenario(const std::string &path, uint64 timestamp)
{
rct_s6_header s6Header;
rct_s6_info s6Info;
if (!scenario_load_basic(path, &s6Header, &s6Info))
if (!GetScenarioInfo(path, &s6Info))
{
Console::Error::WriteLine("Unable to read scenario: '%s'", path);
return;
}
const utf8 * filename = Path::GetFileName(path);
scenario_index_entry * existingEntry = GetByFilename(filename);
const std::string filename = Path::GetFileName(path);
scenario_index_entry * existingEntry = GetByFilename(filename.c_str());
if (existingEntry != nullptr)
{
const utf8 * conflictPath;
std::string conflictPath;
if (existingEntry->timestamp > timestamp)
{
// Existing entry is more recent
conflictPath = existingEntry->path;
conflictPath = String::ToStd(existingEntry->path);
// Overwrite existing entry with this one
*existingEntry = CreateNewScenarioEntry(path, timestamp, &s6Info);
*existingEntry = CreateNewScenarioEntry(path.c_str(), timestamp, &s6Info);
}
else
{
// This entry is more recent
conflictPath = path;
}
Console::WriteLine("Scenario conflict: '%s' ignored because it is newer.", conflictPath);
Console::WriteLine("Scenario conflict: '%s' ignored because it is newer.", conflictPath.c_str());
}
else
{
scenario_index_entry entry = CreateNewScenarioEntry(path, timestamp, &s6Info);
scenario_index_entry entry = CreateNewScenarioEntry(path.c_str(), timestamp, &s6Info);
_scenarios.push_back(entry);
}
}
/**
* Reads basic information from a scenario file.
*/
bool GetScenarioInfo(const std::string &path, rct_s6_info * info)
{
log_verbose("GetScenarioInfo(%s, ...)", path);
try
{
auto fs = FileStream(path, FILE_MODE_OPEN);
std::string extension = Path::GetExtension(path);
if (String::Equals(extension, ".sc4", true))
{
// RCT1 scenario
log_verbose("%s is an RCT1 scenario, not yet supported", path);
}
else
{
// RCT2 scenario
rct_s6_header header;
if (SawyerEncoding::TryReadChunk(&header, &fs))
{
if (header.type == S6_TYPE_SCENARIO)
{
if (SawyerEncoding::TryReadChunk(info, &fs))
{
return true;
}
else
{
throw new IOException("Unable to read S6_INFO chunk.");
}
}
else
{
log_verbose("%s is not a scenario", path);
}
}
}
}
catch (Exception)
{
Console::Error::WriteLine("Unable to read scenario: '%s'", path);
}
return false;
}
scenario_index_entry CreateNewScenarioEntry(const utf8 * path, uint64 timestamp, rct_s6_info * s6Info)
{
scenario_index_entry entry = { 0 };

View File

@@ -88,43 +88,6 @@ money32 gScenarioCompanyValueRecord;
static sint32 scenario_create_ducks();
static void scenario_objective_check();
/**
* Loads only the basic information from a scenario.
* rct2: 0x006761D6
*/
bool scenario_load_basic(const char *path, rct_s6_header *header, rct_s6_info *info)
{
log_verbose("loading scenario details, %s", path);
SDL_RWops* rw = SDL_RWFromFile(path, "rb");
if (rw != NULL) {
// Read first chunk
size_t loaded_size = sawyercoding_read_chunk_with_size(rw, (uint8*)header, sizeof(rct_s6_header));
if (loaded_size != sizeof(rct_s6_header)) {
log_error("Failed to read header from scenario %s", path);
SDL_RWclose(rw);
return false;
}
if (header->type == S6_TYPE_SCENARIO) {
// Read second chunk
loaded_size = sawyercoding_read_chunk_with_size(rw, (uint8*)info, sizeof(rct_s6_info));
SDL_RWclose(rw);
if (loaded_size != sizeof(rct_s6_info)) {
log_error("Failed to read info from scenario %s", path);
return false;
}
return true;
} else {
log_error("invalid scenario, %s", path);
SDL_RWclose(rw);
return false;
}
}
log_error("unable to open scenario, %s", path);
return false;
}
sint32 scenario_load_and_play_from_path(const char *path)
{
window_close_construction_windows();

View File

@@ -376,7 +376,6 @@ extern uint32 gLastAutoSaveUpdate;
extern const char *_scenarioFileName;
bool scenario_load_basic(const char *path, rct_s6_header *header, rct_s6_info *info);
bool scenario_load_rw(SDL_RWops * rw);
sint32 scenario_load(const char *path);
sint32 scenario_load_and_play_from_path(const char *path);