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) switch (type & 0x0E)
{ {
case LOADSAVETYPE_GAME: 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: 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: case LOADSAVETYPE_SCENARIO:
return "*.park"; return "*.park";

View File

@ -603,13 +603,17 @@ namespace OpenRCT2
throw std::runtime_error("Unable to detect file type"); 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."); throw std::runtime_error("Invalid file type.");
} }
std::unique_ptr<IParkImporter> parkImporter; 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) // Save is an S4 (RCT1 format)
parkImporter = ParkImporter::CreateS4(); parkImporter = ParkImporter::CreateS4();

View File

@ -17,6 +17,7 @@
#include "scenario/Scenario.h" #include "scenario/Scenario.h"
#include "util/SawyerCoding.h" #include "util/SawyerCoding.h"
static bool TryClassifyAsPark(OpenRCT2::IStream* stream, ClassifiedFileInfo* result);
static bool TryClassifyAsS6(OpenRCT2::IStream* stream, ClassifiedFileInfo* result); static bool TryClassifyAsS6(OpenRCT2::IStream* stream, ClassifiedFileInfo* result);
static bool TryClassifyAsS4(OpenRCT2::IStream* stream, ClassifiedFileInfo* result); static bool TryClassifyAsS4(OpenRCT2::IStream* stream, ClassifiedFileInfo* result);
static bool TryClassifyAsTD4_TD6(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 // between them is to decode it. Decoding however is currently not protected
// against invalid compression data for that decoding algorithm and will crash. // against invalid compression data for that decoding algorithm and will crash.
// Park detection
if (TryClassifyAsPark(stream, result))
{
return true;
}
// S6 detection // S6 detection
if (TryClassifyAsS6(stream, result)) if (TryClassifyAsS6(stream, result))
{ {
@ -62,6 +69,29 @@ bool TryClassifyFile(OpenRCT2::IStream* stream, ClassifiedFileInfo* result)
return false; 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) static bool TryClassifyAsS6(OpenRCT2::IStream* stream, ClassifiedFileInfo* result)
{ {
bool success = false; bool success = false;

View File

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

View File

@ -1,15 +1,16 @@
#include "ParkFile.h" #include "ParkFile.h"
#include "Context.h" #include "Context.h"
#include "object/ObjectManager.h"
#include "object/ObjectRepository.h"
#include "OpenRCT2.h" #include "OpenRCT2.h"
#include "ParkImporter.h"
#include "Version.h" #include "Version.h"
#include "core/Crypt.h" #include "core/Crypt.h"
#include "drawing/Drawing.h" #include "drawing/Drawing.h"
#include "interface/Viewport.h" #include "interface/Viewport.h"
#include "interface/Window.h" #include "interface/Window.h"
#include "localisation/Date.h" #include "localisation/Date.h"
#include "object/ObjectManager.h"
#include "object/ObjectRepository.h"
#include "scenario/Scenario.h" #include "scenario/Scenario.h"
#include "world/Map.h" #include "world/Map.h"
#include "world/Park.h" #include "world/Park.h"
@ -60,6 +61,8 @@ void ParkFile::Save(const std::string_view& path)
_header.MinVersion = PARK_FILE_MIN_VERSION; _header.MinVersion = PARK_FILE_MIN_VERSION;
_header.Compression = COMPRESSION_NONE; _header.Compression = COMPRESSION_NONE;
_buffer = std::stringstream(std::ios::out | std::ios::binary);
WriteAuthoringChunk(); WriteAuthoringChunk();
WriteObjectsChunk(); WriteObjectsChunk();
WriteGeneralChunk(); WriteGeneralChunk();
@ -131,6 +134,10 @@ void ParkFile::NextArrayElement()
void ParkFile::EndArray() void ParkFile::EndArray()
{ {
auto backupPos = _buffer.tellp(); 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); _buffer.seekp(_currentArrayStartPos);
WriteValue((uint32_t)_currentArrayCount); WriteValue((uint32_t)_currentArrayCount);
WriteValue((uint32_t)_currentArrayElementSize); 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) void ParkFile::WriteString(const std::string_view& s)
{ {
char nullt = '\0'; 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)); _buffer.write(&nullt, sizeof(nullt));
} }
@ -181,6 +193,7 @@ void ParkFile::WriteObjectsChunk()
WriteString(""); WriteString("");
WriteString(""); WriteString("");
} }
NextArrayElement();
} }
EndArray(); EndArray();
EndChunk(); EndChunk();
@ -234,6 +247,115 @@ void ParkFile::WriteTilesChunk()
EndChunk(); 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 enum : uint32_t
{ {
S6_SAVE_FLAG_EXPORT = 1 << 0, S6_SAVE_FLAG_EXPORT = 1 << 0,
@ -294,3 +416,52 @@ int32_t scenario_save(const utf8* path, int32_t flags)
} }
return result; 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 <array>
#include <cstdint> #include <cstdint>
#include <fstream> #include <fstream>
#include <sstream> #include <sstream>
#include <string_view> #include <string_view>
#include <vector> #include <vector>
#include "object/Object.h"
namespace OpenRCT2 namespace OpenRCT2
{ {
class ParkFile class ParkFile
{ {
public: public:
std::vector<rct_object_entry> RequiredObjects;
void Load(const std::string_view& path);
void Save(const std::string_view& path); void Save(const std::string_view& path);
void Import();
private: private:
#pragma pack(push, 1) #pragma pack(push, 1)
@ -62,5 +69,20 @@ namespace OpenRCT2
void WriteObjectsChunk(); void WriteObjectsChunk();
void WriteGeneralChunk(); void WriteGeneralChunk();
void WriteTilesChunk(); 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 } // namespace OpenRCT2

View File

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