Use streams for SV6 export

This commit is contained in:
Ted John 2017-02-05 15:45:23 +00:00
parent 0dcab94411
commit 2518362112
17 changed files with 344 additions and 275 deletions

View File

@ -20,6 +20,7 @@
#include <SDL.h>
#include "IStream.hpp"
#include "Math.hpp"
enum
{
@ -57,7 +58,7 @@ public:
break;
case FILE_MODE_WRITE:
mode = "wb";
_canRead = false;
_canRead = true;
_canWrite = true;
break;
default:
@ -82,7 +83,7 @@ public:
_canWrite = false;
break;
case FILE_MODE_WRITE:
_canRead = false;
_canRead = true;
_canWrite = true;
break;
default:
@ -148,6 +149,9 @@ public:
{
throw IOException("Unable to write to file.");
}
uint64 position = GetPosition();
_fileSize = Math::Max(_fileSize, position);
}
uint64 TryRead(void * buffer, uint64 length) override

View File

@ -74,7 +74,9 @@
<ClCompile Include="audio\NullAudioSource.cpp" />
<ClCompile Include="FileClassifier.cpp" />
<ClCompile Include="ParkImporter.cpp" />
<ClCompile Include="rct12\SawyerChunk.cpp" />
<ClCompile Include="rct12\SawyerChunkReader.cpp" />
<ClCompile Include="rct12\SawyerChunkWriter.cpp" />
<ClCompile Include="rct12\SawyerEncoding.cpp" />
<ClCompile Include="rct2\addresses.c" />
<ClCompile Include="audio\audio.c" />
@ -418,7 +420,9 @@
<ClInclude Include="FileClassifier.h" />
<ClInclude Include="rct12.h" />
<ClInclude Include="ParkImporter.h" />
<ClInclude Include="rct12\SawyerChunk.h" />
<ClInclude Include="rct12\SawyerChunkReader.h" />
<ClInclude Include="rct12\SawyerChunkWriter.h" />
<ClInclude Include="rct12\SawyerEncoding.h" />
<ClInclude Include="rct2\addresses.h" />
<ClInclude Include="audio\audio.h" />

View File

@ -50,6 +50,7 @@ sint32 _pickup_peep_old_x = SPRITE_LOCATION_NULL;
#include "../core/Path.hpp"
#include "../core/String.hpp"
#include "../core/Util.hpp"
#include "../object/ObjectManager.h"
#include "../object/ObjectRepository.h"
#include "../rct2/S6Exporter.h"
@ -923,7 +924,8 @@ void Network::Server_Send_MAP(NetworkConnection* connection)
} else {
// This will send all custom objects to connected clients
// TODO: fix it so custom objects negotiation is performed even in this case.
objects = scenario_get_packable_objects();
IObjectManager * objManager = GetObjectManager();
objects = objManager->GetPackableObjects();
}
header = save_for_network(rw, out_size, objects);
SDL_RWclose(rw);
@ -1458,7 +1460,9 @@ void Network::Server_Client_Joined(const char* name, const std::string &keyhash,
const char * player_name = (const char *) player->Name.c_str();
format_string(text, 256, STR_MULTIPLAYER_PLAYER_HAS_JOINED_THE_GAME, &player_name);
chat_history_add(text);
std::vector<const ObjectRepositoryItem *> objects = scenario_get_packable_objects();
IObjectManager * objManager = GetObjectManager();
auto objects = objManager->GetPackableObjects();
Server_Send_OBJECTS(connection, objects);
}
}

View File

@ -215,6 +215,23 @@ public:
}
}
std::vector<const ObjectRepositoryItem *> GetPackableObjects() override
{
std::vector<const ObjectRepositoryItem *> objects;
size_t numObjects = _objectRepository->GetNumObjects();
for (size_t i = 0; i < numObjects; i++)
{
const ObjectRepositoryItem * item = &_objectRepository->GetObjects()[i];
if (item->LoadedObject != nullptr &&
item->LoadedObject->GetLegacyData() != nullptr &&
IsObjectCustom(item))
{
objects.push_back(item);
}
}
return objects;
}
private:
sint32 FindSpareSlot(uint8 objectType)
{

View File

@ -16,6 +16,10 @@
#pragma once
#ifdef __cplusplus
#include <vector>
#endif
#include "../common.h"
#ifdef __cplusplus
@ -29,7 +33,8 @@ extern "C"
#ifdef __cplusplus
class Object;
class Object;
struct ObjectRepositoryItem;
interface IObjectManager
{
@ -45,6 +50,8 @@ interface IObjectManager
virtual void UnloadAll() abstract;
virtual void ResetObjects() abstract;
virtual std::vector<const ObjectRepositoryItem *> GetPackableObjects() abstract;
};
IObjectManager * GetObjectManager();

View File

@ -32,6 +32,7 @@
#include "../core/String.hpp"
#include "../PlatformEnvironment.h"
#include "../rct12/SawyerChunkReader.h"
#include "../rct12/SawyerChunkWriter.h"
#include "../scenario/ScenarioRepository.h"
#include "Object.h"
#include "ObjectFactory.h"
@ -224,7 +225,7 @@ public:
}
}
bool TryExportPackedObject(IStream * stream) override
void ExportPackedObject(IStream * stream) override
{
auto chunkReader = SawyerChunkReader(stream);
@ -240,7 +241,25 @@ public:
std::shared_ptr<SawyerChunk> chunk = chunkReader.ReadChunk();
AddObject(&entry, chunk->GetData(), chunk->GetLength());
}
return true;
}
void WritePackedObjects(IStream * stream, std::vector<const ObjectRepositoryItem *> &objects) override
{
log_verbose("packing %u objects", objects.size());
for (const auto &object : objects)
{
Guard::ArgumentNotNull(object);
log_verbose("exporting object %.8s", object->ObjectEntry.name);
if (IsObjectCustom(object))
{
WritePackedObject(stream, &object->ObjectEntry);
}
else
{
log_warning("Refusing to pack vanilla/expansion object \"%s\"", object->ObjectEntry.name);
}
}
}
private:
@ -648,6 +667,30 @@ private:
String::Append(buffer, bufferSize, ".DAT");
}
}
void WritePackedObject(IStream * stream, const rct_object_entry * entry)
{
const ObjectRepositoryItem * item = FindObject(entry);
if (item == nullptr)
{
throw Exception(String::StdFormat("Unable to find object '%.8s'", entry->name));
}
// Read object data from file
auto fs = FileStream(item->Path, FILE_MODE_OPEN);
auto fileEntry = fs.ReadValue<rct_object_entry>();
if (!object_entry_compare(entry, &fileEntry))
{
throw Exception("Header found in object file does not match object to pack.");
}
auto chunkReader = SawyerChunkReader(&fs);
auto chunk = chunkReader.ReadChunk();
// Write object data to stream
auto chunkWriter = SawyerChunkWriter(stream);
stream->WriteValue(*entry);
chunkWriter.WriteChunk(chunk.get());
}
};
static std::unique_ptr<ObjectRepository> _objectRepository;
@ -663,6 +706,14 @@ IObjectRepository * GetObjectRepository()
return _objectRepository.get();
}
bool IsObjectCustom(const ObjectRepositoryItem * object)
{
Guard::ArgumentNotNull(object);
// Validate the object is not one from base game or expansion pack
return !(object->ObjectEntry.flags & 0xF0);
}
extern "C"
{
rct_object_entry * object_list_find(rct_object_entry * entry)
@ -755,45 +806,6 @@ extern "C"
}
}
bool object_saved_packed(SDL_RWops * rw, const rct_object_entry * entry)
{
IObjectRepository * objectRepository = GetObjectRepository();
const ObjectRepositoryItem * item = objectRepository->FindObject(entry);
if (item == nullptr)
{
return false;
}
auto fs = FileStream(item->Path, FILE_MODE_OPEN);
rct_object_entry fileEntry = fs.ReadValue<rct_object_entry>();
if (!object_entry_compare(entry, &fileEntry))
{
return false;
}
sawyercoding_chunk_header chunkHeader = fs.ReadValue<sawyercoding_chunk_header>();
uint8 * chunkData = fs.ReadArray<uint8>(chunkHeader.length);
if (SDL_RWwrite(rw, entry, sizeof(rct_object_entry), 1) != 1)
{
Memory::Free(chunkData);
return false;
}
if (SDL_RWwrite(rw, &chunkHeader, sizeof(sawyercoding_chunk_header), 1) != 1)
{
Memory::Free(chunkData);
return false;
}
if (SDL_RWwrite(rw, chunkData, chunkHeader.length, 1) != 1)
{
Memory::Free(chunkData);
return false;
}
Memory::Free(chunkData);
return true;
}
size_t object_repository_get_items_count()
{
IObjectRepository * objectRepository = GetObjectRepository();

View File

@ -16,6 +16,10 @@
#pragma once
#ifdef __cplusplus
#include <vector>
#endif
#include "../common.h"
#ifdef __cplusplus
@ -79,12 +83,15 @@ interface IObjectRepository
const void * data,
size_t dataSize) abstract;
virtual bool TryExportPackedObject(IStream * stream) abstract;
virtual void ExportPackedObject(IStream * stream) abstract;
virtual void WritePackedObjects(IStream * stream, std::vector<const ObjectRepositoryItem *> &objects) abstract;
};
IObjectRepository * CreateObjectRepository(IPlatformEnvironment * env);
IObjectRepository * GetObjectRepository();
bool IsObjectCustom(const ObjectRepositoryItem * object);
#endif
#ifdef __cplusplus

View File

@ -0,0 +1,30 @@
#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/Memory.hpp"
#include "SawyerChunk.h"
SawyerChunk::SawyerChunk(SAWYER_ENCODING encoding, void * data, size_t length)
{
_encoding = encoding;
_data = data;
_length = length;
}
SawyerChunk::~SawyerChunk()
{
Memory::Free(_data);
}

View File

@ -0,0 +1,49 @@
#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"
/**
* The type of encoding / compression for a sawyer encoded chunk.
*/
enum class SAWYER_ENCODING : uint8
{
NONE,
RLE,
RLECOMPRESSED,
ROTATE,
};
/**
* Represents a sawyer encoded chunk.
*/
class SawyerChunk final
{
private:
void * _data = nullptr;
size_t _length = 0;
SAWYER_ENCODING _encoding = SAWYER_ENCODING::NONE;
public:
const void * GetData() const { return _data; }
size_t GetLength() const { return _length; }
SAWYER_ENCODING GetEncoding() const { return _encoding; }
SawyerChunk(SAWYER_ENCODING encoding, void * data, size_t length);
~SawyerChunk();
};

View File

@ -34,18 +34,6 @@ public:
explicit SawyerChunkException(const std::string &message) : IOException(message) { }
};
SawyerChunk::SawyerChunk(SAWYER_ENCODING encoding, void * data, size_t length)
{
_encoding = encoding;
_data = data;
_length = length;
}
SawyerChunk::~SawyerChunk()
{
Memory::Free(_data);
}
SawyerChunkReader::SawyerChunkReader(IStream * stream)
: _stream(stream)
{

View File

@ -18,39 +18,10 @@
#include <memory>
#include "../common.h"
#include "SawyerChunk.h"
interface IStream;
/**
* The type of encoding / compression for a sawyer encoded chunk.
*/
enum class SAWYER_ENCODING : uint8
{
NONE,
RLE,
RLECOMPRESSED,
ROTATE,
};
/**
* Represents a sawyer encoded chunk.
*/
class SawyerChunk final
{
private:
void * _data = nullptr;
size_t _length = 0;
SAWYER_ENCODING _encoding = SAWYER_ENCODING::NONE;
public:
const void * GetData() { return _data; }
size_t GetLength() { return _length; }
SAWYER_ENCODING GetEncoding() { return _encoding; }
SawyerChunk(SAWYER_ENCODING encoding, void * data, size_t length);
~SawyerChunk();
};
/**
* Reads sawyer encoding chunks from a data stream. This can be used to read
* SC6, SV6 and RCT2 objects.

View File

@ -0,0 +1,50 @@
#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/Exception.hpp"
#include "../core/IStream.hpp"
#include "../core/Math.hpp"
#include "SawyerChunkWriter.h"
extern "C"
{
#include "../util/sawyercoding.h"
}
// Maximum buffer size to store compressed data, maximum of 16 MiB
constexpr size_t MAX_COMPRESSED_CHUNK_SIZE = 16 * 1024 * 1024;
SawyerChunkWriter::SawyerChunkWriter(IStream * stream)
: _stream(stream)
{
}
void SawyerChunkWriter::WriteChunk(const SawyerChunk * chunk)
{
WriteChunk(chunk->GetData(), chunk->GetLength(), chunk->GetEncoding());
}
void SawyerChunkWriter::WriteChunk(const void * src, size_t length, SAWYER_ENCODING encoding)
{
sawyercoding_chunk_header header;
header.encoding = (uint8)encoding;
header.length = (uint32)length;
auto data = std::make_unique<uint8[]>(MAX_COMPRESSED_CHUNK_SIZE);
size_t dataLength = sawyercoding_write_chunk_buffer(data.get(), (const uint8 *)src, header);
_stream->Write(data.get(), dataLength);
}

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
#pragma once
#include <memory>
#include "../common.h"
#include "SawyerChunk.h"
interface IStream;
/**
* Writes sawyer encoding chunks to a data stream. This can be used to write
* SC6 and SV6 files.
*/
class SawyerChunkWriter final
{
private:
IStream * const _stream = nullptr;
public:
SawyerChunkWriter(IStream * stream);
/**
* Writes a chunk to the stream.
*/
void WriteChunk(const SawyerChunk * chunk);
/**
* Writes a chunk to the stream containing the given buffer.
* @param dst The source buffer.
* @param length The size of the source buffer.
*/
void WriteChunk(const void * src, size_t length, SAWYER_ENCODING encoding);
/**
* Writes a chunk to the stream containing the given type.
*/
template<typename T>
void WriteChunk(const T * src, SAWYER_ENCODING encoding)
{
WriteChunk(src, sizeof(T), encoding);
}
};

View File

@ -28,6 +28,7 @@ namespace SawyerEncoding
{
return false;
}
dataSize -= 4;
try
{

View File

@ -15,11 +15,14 @@
#pragma endregion
#include "../core/Exception.hpp"
#include "../core/FileStream.hpp"
#include "../core/IStream.hpp"
#include "../core/String.hpp"
#include "../management/award.h"
#include "../object/Object.h"
#include "../object/ObjectManager.h"
#include "../object/ObjectRepository.h"
#include "../rct12/SawyerChunkWriter.h"
#include "S6Exporter.h"
extern "C"
@ -56,178 +59,91 @@ S6Exporter::S6Exporter()
void S6Exporter::SaveGame(const utf8 * path)
{
SDL_RWops * rw = SDL_RWFromFile(path, "rb");
if (rw == nullptr)
{
throw IOException("Unable to write to destination file.");
}
SaveGame(rw);
SDL_RWclose(rw);
auto fs = FileStream(path, FILE_MODE_WRITE);
SaveGame(&fs);
}
void S6Exporter::SaveGame(SDL_RWops *rw)
void S6Exporter::SaveGame(IStream * stream)
{
Save(rw, false);
Save(stream, false);
}
void S6Exporter::SaveScenario(const utf8 * path)
{
SDL_RWops * rw = SDL_RWFromFile(path, "rb");
if (rw == nullptr)
{
throw IOException("Unable to write to destination file.");
}
SaveGame(rw);
SDL_RWclose(rw);
auto fs = FileStream(path, FILE_MODE_WRITE);
SaveScenario(&fs);
}
void S6Exporter::SaveScenario(SDL_RWops *rw)
void S6Exporter::SaveScenario(IStream * stream)
{
Save(rw, true);
Save(stream, true);
}
void S6Exporter::Save(SDL_RWops * rw, bool isScenario)
void S6Exporter::Save(IStream * stream, bool isScenario)
{
_s6.header.type = isScenario ? S6_TYPE_SCENARIO : S6_TYPE_SAVEDGAME;
_s6.header.classic_flag = 0;
_s6.header.num_packed_objects = uint16(ExportObjectsList.size());
_s6.header.version = S6_RCT2_VERSION;
_s6.header.magic_number = S6_MAGIC_NUMBER;
_s6.game_version_number = 201028;
uint8 * buffer = (uint8 *)malloc(0x600000);
if (buffer == NULL)
{
log_error("Unable to allocate enough space for a write buffer.");
throw Exception("Unable to allocate memory.");
}
sawyercoding_chunk_header chunkHeader;
size_t encodedLength;
auto chunkWriter = SawyerChunkWriter(stream);
// 0: Write header chunk
chunkHeader.encoding = CHUNK_ENCODING_ROTATE;
chunkHeader.length = sizeof(rct_s6_header);
encodedLength = sawyercoding_write_chunk_buffer(buffer, (uint8*)&_s6.header, chunkHeader);
SDL_RWwrite(rw, buffer, encodedLength, 1);
chunkWriter.WriteChunk(&_s6.header, SAWYER_ENCODING::ROTATE);
// 1: Write scenario info chunk
if (_s6.header.type == S6_TYPE_SCENARIO)
{
chunkHeader.encoding = CHUNK_ENCODING_ROTATE;
chunkHeader.length = sizeof(rct_s6_info);
encodedLength = sawyercoding_write_chunk_buffer(buffer, (uint8*)&_s6.info, chunkHeader);
SDL_RWwrite(rw, buffer, encodedLength, 1);
chunkWriter.WriteChunk(&_s6.info, SAWYER_ENCODING::ROTATE);
}
log_verbose("exporting %u objects", _s6.header.num_packed_objects);
// 2: Write packed objects
if (_s6.header.num_packed_objects > 0)
{
if (!scenario_write_packed_objects(rw, ExportObjectsList))
{
free(buffer);
throw Exception("Unable to pack objects.");
}
IObjectRepository * objRepo = GetObjectRepository();
objRepo->WritePackedObjects(stream, ExportObjectsList);
}
// 3: Write available objects chunk
chunkHeader.encoding = CHUNK_ENCODING_ROTATE;
chunkHeader.length = OBJECT_ENTRY_COUNT * sizeof(rct_object_entry);
encodedLength = sawyercoding_write_chunk_buffer(buffer, (uint8*)_s6.objects, chunkHeader);
SDL_RWwrite(rw, buffer, encodedLength, 1);
chunkWriter.WriteChunk(_s6.objects, sizeof(_s6.objects), SAWYER_ENCODING::ROTATE);
// 4: Misc fields (data, rand...) chunk
chunkHeader.encoding = CHUNK_ENCODING_RLECOMPRESSED;
chunkHeader.length = 16;
encodedLength = sawyercoding_write_chunk_buffer(buffer, (uint8*)&_s6.elapsed_months, chunkHeader);
SDL_RWwrite(rw, buffer, encodedLength, 1);
chunkWriter.WriteChunk(&_s6.elapsed_months, 16, SAWYER_ENCODING::RLECOMPRESSED);
// 5: Map elements + sprites and other fields chunk
chunkHeader.encoding = CHUNK_ENCODING_RLECOMPRESSED;
chunkHeader.length = 0x180000;
encodedLength = sawyercoding_write_chunk_buffer(buffer, (uint8*)_s6.map_elements, chunkHeader);
SDL_RWwrite(rw, buffer, encodedLength, 1);
chunkWriter.WriteChunk(&_s6.map_elements, 0x180000, SAWYER_ENCODING::RLECOMPRESSED);
if (_s6.header.type == S6_TYPE_SCENARIO)
{
// 6:
chunkHeader.encoding = CHUNK_ENCODING_RLECOMPRESSED;
chunkHeader.length = 0x27104C;
encodedLength = sawyercoding_write_chunk_buffer(buffer, (uint8*)&_s6.next_free_map_element_pointer_index, chunkHeader);
SDL_RWwrite(rw, buffer, encodedLength, 1);
// 7:
chunkHeader.encoding = CHUNK_ENCODING_RLECOMPRESSED;
chunkHeader.length = 4;
encodedLength = sawyercoding_write_chunk_buffer(buffer, (uint8*)&_s6.guests_in_park, chunkHeader);
SDL_RWwrite(rw, buffer, encodedLength, 1);
// 8:
chunkHeader.encoding = CHUNK_ENCODING_RLECOMPRESSED;
chunkHeader.length = 8;
encodedLength = sawyercoding_write_chunk_buffer(buffer, (uint8*)&_s6.last_guests_in_park, chunkHeader);
SDL_RWwrite(rw, buffer, encodedLength, 1);
// 9:
chunkHeader.encoding = CHUNK_ENCODING_RLECOMPRESSED;
chunkHeader.length = 2;
encodedLength = sawyercoding_write_chunk_buffer(buffer, (uint8*)&_s6.park_rating, chunkHeader);
SDL_RWwrite(rw, buffer, encodedLength, 1);
// 10:
chunkHeader.encoding = CHUNK_ENCODING_RLECOMPRESSED;
chunkHeader.length = 1082;
encodedLength = sawyercoding_write_chunk_buffer(buffer, (uint8*)&_s6.active_research_types, chunkHeader);
SDL_RWwrite(rw, buffer, encodedLength, 1);
// 11:
chunkHeader.encoding = CHUNK_ENCODING_RLECOMPRESSED;
chunkHeader.length = 16;
encodedLength = sawyercoding_write_chunk_buffer(buffer, (uint8*)&_s6.current_expenditure, chunkHeader);
SDL_RWwrite(rw, buffer, encodedLength, 1);
// 12:
chunkHeader.encoding = CHUNK_ENCODING_RLECOMPRESSED;
chunkHeader.length = 4;
encodedLength = sawyercoding_write_chunk_buffer(buffer, (uint8*)&_s6.park_value, chunkHeader);
SDL_RWwrite(rw, buffer, encodedLength, 1);
// 13:
chunkHeader.encoding = CHUNK_ENCODING_RLECOMPRESSED;
chunkHeader.length = 0x761E8;
encodedLength = sawyercoding_write_chunk_buffer(buffer, (uint8*)&_s6.completed_company_value, chunkHeader);
SDL_RWwrite(rw, buffer, encodedLength, 1);
// 6 to 13:
chunkWriter.WriteChunk(&_s6.next_free_map_element_pointer_index, 0x27104C, SAWYER_ENCODING::RLECOMPRESSED);
chunkWriter.WriteChunk(&_s6.guests_in_park, 4, SAWYER_ENCODING::RLECOMPRESSED);
chunkWriter.WriteChunk(&_s6.last_guests_in_park, 8, SAWYER_ENCODING::RLECOMPRESSED);
chunkWriter.WriteChunk(&_s6.park_rating, 2, SAWYER_ENCODING::RLECOMPRESSED);
chunkWriter.WriteChunk(&_s6.active_research_types, 1082, SAWYER_ENCODING::RLECOMPRESSED);
chunkWriter.WriteChunk(&_s6.current_expenditure, 16, SAWYER_ENCODING::RLECOMPRESSED);
chunkWriter.WriteChunk(&_s6.park_value, 4, SAWYER_ENCODING::RLECOMPRESSED);
chunkWriter.WriteChunk(&_s6.completed_company_value, 0x761E8, SAWYER_ENCODING::RLECOMPRESSED);
}
else
{
// 6: Everything else...
chunkHeader.encoding = CHUNK_ENCODING_RLECOMPRESSED;
chunkHeader.length = 0x2E8570;
encodedLength = sawyercoding_write_chunk_buffer(buffer, (uint8*)&_s6.next_free_map_element_pointer_index, chunkHeader);
SDL_RWwrite(rw, buffer, encodedLength, 1);
chunkWriter.WriteChunk(&_s6.next_free_map_element_pointer_index, 0x2E8570, SAWYER_ENCODING::RLECOMPRESSED);
}
free(buffer);
// Determine number of bytes written
size_t fileSize = (size_t)SDL_RWtell(rw);
SDL_RWseek(rw, 0, RW_SEEK_SET);
size_t fileSize = stream->GetLength();
// Read all written bytes back into a single buffer
buffer = (uint8 *)malloc(fileSize);
SDL_RWread(rw, buffer, fileSize, 1);
uint32 checksum = sawyercoding_calculate_checksum(buffer, fileSize);
free(buffer);
stream->SetPosition(0);
auto data = std::unique_ptr<uint8>(stream->ReadArray<uint8>(fileSize));
uint32 checksum = sawyercoding_calculate_checksum(data.get(), fileSize);
// Append the checksum
SDL_RWseek(rw, fileSize, RW_SEEK_SET);
SDL_RWwrite(rw, &checksum, sizeof(uint32), 1);
// Write the checksum on the end
stream->SetPosition(fileSize);
stream->WriteValue(checksum);
}
void S6Exporter::Export()
@ -480,7 +396,6 @@ uint32 S6Exporter::GetLoanHash(money32 initialCash, money32 bankLoan, uint32 max
return value;
}
// Save game state without modifying any of the state for multiplayer
sint32 scenario_save_network(SDL_RWops * rw, const std::vector<const ObjectRepositoryItem *> &objects)
{
@ -490,9 +405,11 @@ sint32 scenario_save_network(SDL_RWops * rw, const std::vector<const ObjectRepos
auto s6exporter = new S6Exporter();
try
{
auto rwStream = FileStream(rw, FILE_MODE_WRITE);
s6exporter->ExportObjectsList = objects;
s6exporter->Export();
s6exporter->SaveGame(rw);
s6exporter->SaveGame(&rwStream);
result = true;
}
catch (const Exception &)
@ -534,58 +451,6 @@ sint32 scenario_save_network(SDL_RWops * rw, const std::vector<const ObjectRepos
return 1;
}
static bool object_is_custom(const ObjectRepositoryItem * object)
{
Guard::ArgumentNotNull(object);
// Validate the object is not one from base game or expansion pack
return (object->LoadedObject != nullptr &&
object->LoadedObject->GetLegacyData() != nullptr
&& !(object->ObjectEntry.flags & 0xF0));
}
sint32 scenario_write_packed_objects(SDL_RWops* rw, std::vector<const ObjectRepositoryItem *> &objects)
{
log_verbose("exporting packed objects");
for (const auto &object : objects)
{
Guard::ArgumentNotNull(object);
log_verbose("exporting object %.8s", object->ObjectEntry.name);
if (object_is_custom(object))
{
if (!object_saved_packed(rw, &object->ObjectEntry))
{
return 0;
}
}
else
{
log_warning("Refusing to pack vanilla/expansion object \"%s\"", object->ObjectEntry.name);
}
}
return 1;
}
/**
*
* rct2: 0x006AA244
*/
std::vector<const ObjectRepositoryItem *> scenario_get_packable_objects()
{
std::vector<const ObjectRepositoryItem *> objects;
IObjectRepository * repo = GetObjectRepository();
for (size_t i = 0; i < repo->GetNumObjects(); i++)
{
const ObjectRepositoryItem *item = &repo->GetObjects()[i];
// Validate the object is not one from base game or expansion pack
if (object_is_custom(item))
{
objects.push_back(item);
}
}
return objects;
}
extern "C"
{
enum {
@ -624,18 +489,22 @@ extern "C"
auto s6exporter = new S6Exporter();
try
{
if (flags & S6_SAVE_FLAG_EXPORT) {
s6exporter->ExportObjectsList = scenario_get_packable_objects();
auto rwStream = FileStream(rw, FILE_MODE_WRITE);
if (flags & S6_SAVE_FLAG_EXPORT)
{
IObjectManager * objManager = GetObjectManager();
s6exporter->ExportObjectsList = objManager->GetPackableObjects();
}
s6exporter->RemoveTracklessRides = true;
s6exporter->Export();
if (flags & S6_SAVE_FLAG_SCENARIO)
{
s6exporter->SaveScenario(rw);
s6exporter->SaveScenario(&rwStream);
}
else
{
s6exporter->SaveGame(rw);
s6exporter->SaveGame(&rwStream);
}
result = true;
}

View File

@ -27,11 +27,10 @@ extern "C"
#include "../object_list.h"
}
struct ObjectRepositoryItem;
interface IStream;
struct ObjectRepositoryItem;
sint32 scenario_save_network(SDL_RWops* rw, const std::vector<const ObjectRepositoryItem *> &objects);
sint32 scenario_write_packed_objects(SDL_RWops* rw, std::vector<const ObjectRepositoryItem *> &objects);
std::vector<const ObjectRepositoryItem *> scenario_get_packable_objects();
/**
* Class to export RollerCoaster Tycoon 2 scenarios (*.SC6) and saved games (*.SV6).
@ -45,14 +44,14 @@ public:
S6Exporter();
void SaveGame(const utf8 * path);
void SaveGame(SDL_RWops *rw);
void SaveGame(IStream * stream);
void SaveScenario(const utf8 * path);
void SaveScenario(SDL_RWops *rw);
void SaveScenario(IStream * stream);
void Export();
private:
rct_s6_data _s6;
void Save(SDL_RWops *rw, bool isScenario);
void Save(IStream * stream, bool isScenario);
static uint32 GetLoanHash(money32 initialCash, money32 bankLoan, uint32 maxBankLoan);
};

View File

@ -136,7 +136,7 @@ public:
IObjectRepository * objectRepo = GetObjectRepository();
for (uint16 i = 0; i < _s6.header.num_packed_objects; i++)
{
objectRepo->TryExportPackedObject(stream);
objectRepo->ExportPackedObject(stream);
}
if (isScenario)