Start work on loading

This commit is contained in:
Ted John 2018-12-15 12:46:32 +00:00
parent c264bc7b72
commit fbc120feba
7 changed files with 236 additions and 7 deletions

View File

@ -201,10 +201,10 @@ static const char* getFilterPatternByType(const int32_t type, const bool isSave)
switch (type & 0x0E)
{
case LOADSAVETYPE_GAME:
return isSave ? "*.park" : "*.sv6;*.sc6;*.sc4;*.sv4;*.sv7;*.sea;";
return isSave ? "*.park" : "*.park;*.sv6;*.sc6;*.sc4;*.sv4;*.sv7;*.sea";
case LOADSAVETYPE_LANDSCAPE:
return isSave ? "*.park" : "*.sc6;*.sv6;*.sc4;*.sv4;*.sv7;*.sea;";
return isSave ? "*.park" : "*.park;*.sc6;*.sv6;*.sc4;*.sv4;*.sv7;*.sea";
case LOADSAVETYPE_SCENARIO:
return "*.park";

View File

@ -603,13 +603,17 @@ namespace OpenRCT2
throw std::runtime_error("Unable to detect file type");
}
if (info.Type != FILE_TYPE::SAVED_GAME && info.Type != FILE_TYPE::SCENARIO)
if (info.Type != FILE_TYPE::PARK && info.Type != FILE_TYPE::SAVED_GAME && info.Type != FILE_TYPE::SCENARIO)
{
throw std::runtime_error("Invalid file type.");
}
std::unique_ptr<IParkImporter> parkImporter;
if (info.Version <= FILE_TYPE_S4_CUTOFF)
if (info.Type == FILE_TYPE::PARK)
{
parkImporter = ParkImporter::CreateParkFile(*_objectRepository);
}
else if (info.Version <= FILE_TYPE_S4_CUTOFF)
{
// Save is an S4 (RCT1 format)
parkImporter = ParkImporter::CreateS4();

View File

@ -17,6 +17,7 @@
#include "scenario/Scenario.h"
#include "util/SawyerCoding.h"
static bool TryClassifyAsPark(OpenRCT2::IStream* stream, ClassifiedFileInfo* result);
static bool TryClassifyAsS6(OpenRCT2::IStream* stream, ClassifiedFileInfo* result);
static bool TryClassifyAsS4(OpenRCT2::IStream* stream, ClassifiedFileInfo* result);
static bool TryClassifyAsTD4_TD6(OpenRCT2::IStream* stream, ClassifiedFileInfo* result);
@ -41,6 +42,12 @@ bool TryClassifyFile(OpenRCT2::IStream* stream, ClassifiedFileInfo* result)
// between them is to decode it. Decoding however is currently not protected
// against invalid compression data for that decoding algorithm and will crash.
// Park detection
if (TryClassifyAsPark(stream, result))
{
return true;
}
// S6 detection
if (TryClassifyAsS6(stream, result))
{
@ -62,6 +69,29 @@ bool TryClassifyFile(OpenRCT2::IStream* stream, ClassifiedFileInfo* result)
return false;
}
static bool TryClassifyAsPark(OpenRCT2::IStream* stream, ClassifiedFileInfo* result)
{
bool success = false;
uint64_t originalPosition = stream->GetPosition();
try
{
auto magic = stream->ReadValue<uint32_t>();
if (magic == 0x4B524150)
{
result->Type = FILE_TYPE::PARK;
result->Version = 0;
success = true;
}
}
catch (const std::exception& e)
{
success = false;
log_verbose(e.what());
}
stream->SetPosition(originalPosition);
return success;
}
static bool TryClassifyAsS6(OpenRCT2::IStream* stream, ClassifiedFileInfo* result)
{
bool success = false;

View File

@ -37,6 +37,7 @@ enum class FILE_TYPE
SAVED_GAME,
SCENARIO,
TRACK_DESIGN,
PARK,
};
struct ClassifiedFileInfo

View File

@ -1,15 +1,16 @@
#include "ParkFile.h"
#include "Context.h"
#include "object/ObjectManager.h"
#include "object/ObjectRepository.h"
#include "OpenRCT2.h"
#include "ParkImporter.h"
#include "Version.h"
#include "core/Crypt.h"
#include "drawing/Drawing.h"
#include "interface/Viewport.h"
#include "interface/Window.h"
#include "localisation/Date.h"
#include "object/ObjectManager.h"
#include "object/ObjectRepository.h"
#include "scenario/Scenario.h"
#include "world/Map.h"
#include "world/Park.h"
@ -60,6 +61,8 @@ void ParkFile::Save(const std::string_view& path)
_header.MinVersion = PARK_FILE_MIN_VERSION;
_header.Compression = COMPRESSION_NONE;
_buffer = std::stringstream(std::ios::out | std::ios::binary);
WriteAuthoringChunk();
WriteObjectsChunk();
WriteGeneralChunk();
@ -131,6 +134,10 @@ void ParkFile::NextArrayElement()
void ParkFile::EndArray()
{
auto backupPos = _buffer.tellp();
if ((size_t)backupPos != (size_t)_currentArrayStartPos + 8 && _currentArrayCount == 0)
{
throw std::runtime_error("Array data was written but no elements were added.");
}
_buffer.seekp(_currentArrayStartPos);
WriteValue((uint32_t)_currentArrayCount);
WriteValue((uint32_t)_currentArrayElementSize);
@ -145,7 +152,12 @@ void ParkFile::WriteBuffer(const void* buffer, size_t len)
void ParkFile::WriteString(const std::string_view& s)
{
char nullt = '\0';
_buffer.write(s.data(), s.size());
auto len = s.find('\0');
if (len == std::string_view::npos)
{
len = s.size();
}
_buffer.write(s.data(), len);
_buffer.write(&nullt, sizeof(nullt));
}
@ -181,6 +193,7 @@ void ParkFile::WriteObjectsChunk()
WriteString("");
WriteString("");
}
NextArrayElement();
}
EndArray();
EndChunk();
@ -234,6 +247,115 @@ void ParkFile::WriteTilesChunk()
EndChunk();
}
void ParkFile::Load(const std::string_view& path)
{
std::ifstream fs(std::string(path).c_str(), std::ios::binary);
_header = ReadHeader(fs);
_chunks.clear();
for (uint32_t i = 0; i < _header.NumChunks; i++)
{
ChunkEntry entry;
fs.read((char*)&entry, sizeof(entry));
_chunks.push_back(entry);
}
_buffer = std::stringstream(std::ios::in | std::ios::out | std::ios::binary);
_buffer.clear();
char temp[2048];
size_t read = 0;
do
{
fs.read(temp, sizeof(temp));
read = fs.gcount();
_buffer.write(temp, read);
} while (read != 0);
RequiredObjects.clear();
if (SeekChunk(ParkFileChunkType::OBJECTS))
{
auto len = ReadArray();
for (size_t i = 0; i < len; i++)
{
auto type = ReadValue<uint16_t>();
auto id = ReadString();
auto version = ReadString();
rct_object_entry entry{};
entry.flags = type & 0x7FFF;
strncpy(entry.name, id.c_str(), 8);
RequiredObjects.push_back(entry);
ReadNextArrayElement();
}
}
}
void ParkFile::Import()
{
}
ParkFile::Header ParkFile::ReadHeader(std::istream& fs)
{
Header header;
fs.read((char*)&header, sizeof(header));
return header;
}
bool ParkFile::SeekChunk(uint32_t id)
{
auto result = std::find_if(_chunks.begin(), _chunks.end(), [id](const ChunkEntry& e) { return e.Id == id; });
if (result != _chunks.end())
{
auto offset = result->Offset;
_buffer.seekg(offset);
return true;
}
return false;
}
size_t ParkFile::ReadArray()
{
_currentArrayCount = ReadValue<uint32_t>();
_currentArrayElementSize = ReadValue<uint32_t>();
_currentArrayLastPos = _buffer.tellg();
return _currentArrayCount;
}
bool ParkFile::ReadNextArrayElement()
{
if (_currentArrayCount == 0)
{
return false;
}
if (_currentArrayElementSize != 0)
{
_buffer.seekg((size_t)_currentArrayLastPos + _currentArrayElementSize);
}
_currentArrayCount--;
return _currentArrayCount == 0;
}
void ParkFile::ReadBuffer(void* dst, size_t len)
{
_buffer.read((char*)dst, len);
}
std::string ParkFile::ReadString()
{
std::string buffer;
buffer.reserve(64);
char c;
while ((c = ReadValue<char>()) != 0)
{
buffer.push_back(c);
}
buffer.shrink_to_fit();
return buffer;
}
enum : uint32_t
{
S6_SAVE_FLAG_EXPORT = 1 << 0,
@ -294,3 +416,52 @@ int32_t scenario_save(const utf8* path, int32_t flags)
}
return result;
}
class ParkFileImporter : public IParkImporter
{
private:
const IObjectRepository& _objectRepository;
public:
ParkFileImporter(IObjectRepository& objectRepository)
: _objectRepository(objectRepository)
{
}
ParkLoadResult Load(const utf8* path) override
{
auto parkFile = std::make_unique<ParkFile>();
parkFile->Load(path);
return ParkLoadResult(std::move(parkFile->RequiredObjects));
}
ParkLoadResult LoadSavedGame(const utf8* path, bool skipObjectCheck = false) override
{
return Load(path);
}
ParkLoadResult LoadScenario(const utf8* path, bool skipObjectCheck = false) override
{
return Load(path);
}
ParkLoadResult LoadFromStream(
IStream* stream, bool isScenario, bool skipObjectCheck = false, const utf8* path = String::Empty) override
{
return Load(path);
}
void Import() override
{
}
bool GetDetails(scenario_index_entry* dst) override
{
return false;
}
};
std::unique_ptr<IParkImporter> ParkImporter::CreateParkFile(IObjectRepository& objectRepository)
{
return std::make_unique<ParkFileImporter>(objectRepository);
}

View File

@ -1,16 +1,23 @@
#pragma once
#include <array>
#include <cstdint>
#include <fstream>
#include <sstream>
#include <string_view>
#include <vector>
#include "object/Object.h"
namespace OpenRCT2
{
class ParkFile
{
public:
std::vector<rct_object_entry> RequiredObjects;
void Load(const std::string_view& path);
void Save(const std::string_view& path);
void Import();
private:
#pragma pack(push, 1)
@ -62,5 +69,20 @@ namespace OpenRCT2
void WriteObjectsChunk();
void WriteGeneralChunk();
void WriteTilesChunk();
Header ReadHeader(std::istream& fs);
bool SeekChunk(uint32_t id);
size_t ReadArray();
bool ReadNextArrayElement();
void ReadBuffer(void* dst, size_t len);
std::string ReadString();
template<typename T>
T ReadValue()
{
T v{};
ReadBuffer(&v, sizeof(v));
return v;
}
};
} // namespace OpenRCT2

View File

@ -60,6 +60,7 @@ namespace ParkImporter
std::unique_ptr<IParkImporter> Create(const std::string& hintPath);
std::unique_ptr<IParkImporter> CreateS4();
std::unique_ptr<IParkImporter> CreateS6(IObjectRepository& objectRepository);
std::unique_ptr<IParkImporter> CreateParkFile(IObjectRepository& objectRepository);
bool ExtensionIsRCT1(const std::string& extension);
bool ExtensionIsScenario(const std::string& extension);