mirror of https://github.com/OpenRCT2/OpenRCT2.git
Start work on loading
This commit is contained in:
parent
c264bc7b72
commit
fbc120feba
|
@ -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";
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -37,6 +37,7 @@ enum class FILE_TYPE
|
||||||
SAVED_GAME,
|
SAVED_GAME,
|
||||||
SCENARIO,
|
SCENARIO,
|
||||||
TRACK_DESIGN,
|
TRACK_DESIGN,
|
||||||
|
PARK,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ClassifiedFileInfo
|
struct ClassifiedFileInfo
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in New Issue