mirror of
https://github.com/OpenRCT2/OpenRCT2
synced 2026-01-18 04:23:20 +01:00
Merge pull request #5179 from IntelOrca/refactor/sc-chunk-writer
Use streams for SV6 export
This commit is contained in:
@@ -290,7 +290,7 @@ endif ()
|
||||
|
||||
# set necessary flags to compile code as is
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${TARGET_M} -std=gnu99 ${COMMON_COMPILE_OPTIONS} -Wimplicit")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${TARGET_M} -std=gnu++11 ${COMMON_COMPILE_OPTIONS} -Wnon-virtual-dtor")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${TARGET_M} -std=gnu++14 ${COMMON_COMPILE_OPTIONS} -Wnon-virtual-dtor")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${TARGET_M}")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${CMAKE_SHARED_LINKER_FLAGS} ${PIE_FLAG}")
|
||||
|
||||
|
||||
@@ -242,6 +242,9 @@
|
||||
D41B73EF1C2101890080A7B9 /* libcurl.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = D41B73EE1C2101890080A7B9 /* libcurl.tbd */; };
|
||||
D41B741D1C210A7A0080A7B9 /* libiconv.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = D41B741C1C210A7A0080A7B9 /* libiconv.tbd */; };
|
||||
D41B74731C2125E50080A7B9 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D41B74721C2125E50080A7B9 /* Assets.xcassets */; };
|
||||
D433A5001E4A861F00D9A6DF /* SawyerChunk.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D433A4FA1E4A861F00D9A6DF /* SawyerChunk.cpp */; };
|
||||
D433A5011E4A861F00D9A6DF /* SawyerChunkReader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D433A4FC1E4A861F00D9A6DF /* SawyerChunkReader.cpp */; };
|
||||
D433A5021E4A861F00D9A6DF /* SawyerChunkWriter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D433A4FE1E4A861F00D9A6DF /* SawyerChunkWriter.cpp */; };
|
||||
D43407D61D0E14BE00C2B3D4 /* CopyFramebufferShader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D43407C01D0E14BE00C2B3D4 /* CopyFramebufferShader.cpp */; };
|
||||
D43407D81D0E14BE00C2B3D4 /* DrawImageShader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D43407C41D0E14BE00C2B3D4 /* DrawImageShader.cpp */; };
|
||||
D43407D91D0E14BE00C2B3D4 /* DrawLineShader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D43407C61D0E14BE00C2B3D4 /* DrawLineShader.cpp */; };
|
||||
@@ -709,6 +712,13 @@
|
||||
D41B73EE1C2101890080A7B9 /* libcurl.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libcurl.tbd; path = usr/lib/libcurl.tbd; sourceTree = SDKROOT; };
|
||||
D41B741C1C210A7A0080A7B9 /* libiconv.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libiconv.tbd; path = usr/lib/libiconv.tbd; sourceTree = SDKROOT; };
|
||||
D41B74721C2125E50080A7B9 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = distribution/macos/Assets.xcassets; sourceTree = SOURCE_ROOT; };
|
||||
D433A4FA1E4A861F00D9A6DF /* SawyerChunk.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SawyerChunk.cpp; path = rct12/SawyerChunk.cpp; sourceTree = "<group>"; };
|
||||
D433A4FB1E4A861F00D9A6DF /* SawyerChunk.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SawyerChunk.h; path = rct12/SawyerChunk.h; sourceTree = "<group>"; };
|
||||
D433A4FC1E4A861F00D9A6DF /* SawyerChunkReader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SawyerChunkReader.cpp; path = rct12/SawyerChunkReader.cpp; sourceTree = "<group>"; };
|
||||
D433A4FD1E4A861F00D9A6DF /* SawyerChunkReader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SawyerChunkReader.h; path = rct12/SawyerChunkReader.h; sourceTree = "<group>"; };
|
||||
D433A4FE1E4A861F00D9A6DF /* SawyerChunkWriter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SawyerChunkWriter.cpp; path = rct12/SawyerChunkWriter.cpp; sourceTree = "<group>"; };
|
||||
D433A4FF1E4A861F00D9A6DF /* SawyerChunkWriter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SawyerChunkWriter.h; path = rct12/SawyerChunkWriter.h; sourceTree = "<group>"; };
|
||||
D433A5031E4A862F00D9A6DF /* rct12.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = rct12.h; sourceTree = "<group>"; };
|
||||
D43407C01D0E14BE00C2B3D4 /* CopyFramebufferShader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CopyFramebufferShader.cpp; sourceTree = "<group>"; };
|
||||
D43407C11D0E14BE00C2B3D4 /* CopyFramebufferShader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CopyFramebufferShader.h; sourceTree = "<group>"; };
|
||||
D43407C41D0E14BE00C2B3D4 /* DrawImageShader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DrawImageShader.cpp; sourceTree = "<group>"; };
|
||||
@@ -1242,6 +1252,12 @@
|
||||
652747E91E41CDFE000F36FD /* rct12 */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D433A4FA1E4A861F00D9A6DF /* SawyerChunk.cpp */,
|
||||
D433A4FB1E4A861F00D9A6DF /* SawyerChunk.h */,
|
||||
D433A4FC1E4A861F00D9A6DF /* SawyerChunkReader.cpp */,
|
||||
D433A4FD1E4A861F00D9A6DF /* SawyerChunkReader.h */,
|
||||
D433A4FE1E4A861F00D9A6DF /* SawyerChunkWriter.cpp */,
|
||||
D433A4FF1E4A861F00D9A6DF /* SawyerChunkWriter.h */,
|
||||
652747EA1E41CE1B000F36FD /* SawyerEncoding.cpp */,
|
||||
652747EB1E41CE1B000F36FD /* SawyerEncoding.h */,
|
||||
);
|
||||
@@ -1524,8 +1540,8 @@
|
||||
D442715B1CC81B3200D84D28 /* peep */,
|
||||
D44271601CC81B3200D84D28 /* platform */,
|
||||
C650B2141CCABBDD00B4D91C /* rct1 */,
|
||||
652747E91E41CDFE000F36FD /* rct12 */,
|
||||
C6B5A7CF1CDFE4CB00C9C006 /* rct2 */,
|
||||
652747E91E41CDFE000F36FD /* rct12 */,
|
||||
D442716E1CC81B3200D84D28 /* ride */,
|
||||
C6E96E1C1E04070E0076A04F /* scenario */,
|
||||
C6E96E261E04072F0076A04F /* title */,
|
||||
@@ -1564,6 +1580,7 @@
|
||||
D442716A1CC81B3200D84D28 /* rct1.h */,
|
||||
D442716B1CC81B3200D84D28 /* rct2.c */,
|
||||
D442716C1CC81B3200D84D28 /* rct2.h */,
|
||||
D433A5031E4A862F00D9A6DF /* rct12.h */,
|
||||
D44271851CC81B3200D84D28 /* sprites.h */,
|
||||
D4CA88651D4E64C800060C11 /* version.c */,
|
||||
D442718D1CC81B3200D84D28 /* version.h */,
|
||||
@@ -2896,6 +2913,7 @@
|
||||
C6E96E2F1E04072F0076A04F /* TitleScreen.cpp in Sources */,
|
||||
D442728E1CC81B3200D84D28 /* title_logo.c in Sources */,
|
||||
C686F8B21CDBC37E009F9BFC /* scenery_multiple.c in Sources */,
|
||||
D433A5011E4A861F00D9A6DF /* SawyerChunkReader.cpp in Sources */,
|
||||
D442725E1CC81B3200D84D28 /* editor_objective_options.c in Sources */,
|
||||
D44272531CC81B3200D84D28 /* about.c in Sources */,
|
||||
C686F9311CDBC3B7009F9BFC /* ghost_train.c in Sources */,
|
||||
@@ -2923,6 +2941,7 @@
|
||||
D44272761CC81B3200D84D28 /* options.c in Sources */,
|
||||
C686F93D1CDBC3B7009F9BFC /* shop.c in Sources */,
|
||||
C686F8AD1CDBC37E009F9BFC /* entrance.c in Sources */,
|
||||
D433A5001E4A861F00D9A6DF /* SawyerChunk.cpp in Sources */,
|
||||
D44272111CC81B3200D84D28 /* string.c in Sources */,
|
||||
C686F93E1CDBC3B7009F9BFC /* 3d_cinema.c in Sources */,
|
||||
D44272771CC81B3200D84D28 /* park.c in Sources */,
|
||||
@@ -2968,6 +2987,7 @@
|
||||
D442726C1CC81B3200D84D28 /* map_tooltip.c in Sources */,
|
||||
C686F9281CDBC3B7009F9BFC /* wild_mouse.c in Sources */,
|
||||
D44272701CC81B3200D84D28 /* music_credits.c in Sources */,
|
||||
D433A5021E4A861F00D9A6DF /* SawyerChunkWriter.cpp in Sources */,
|
||||
C686F9511CDBC3B7009F9BFC /* river_rafts.c in Sources */,
|
||||
C686F90E1CDBC3B7009F9BFC /* corkscrew_roller_coaster.c in Sources */,
|
||||
D44272A61CC81B3200D84D28 /* scenery.c in Sources */,
|
||||
@@ -3084,7 +3104,7 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = NO;
|
||||
@@ -3132,7 +3152,7 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = NO;
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
#include "core/FileStream.hpp"
|
||||
#include "FileClassifier.h"
|
||||
#include "rct12/SawyerEncoding.h"
|
||||
#include "rct12/SawyerChunkReader.h"
|
||||
|
||||
extern "C"
|
||||
{
|
||||
@@ -71,9 +71,10 @@ bool TryClassifyFile(IStream * stream, ClassifiedFile * result)
|
||||
|
||||
static bool TryClassifyAsS6(IStream * stream, ClassifiedFile * result)
|
||||
{
|
||||
rct_s6_header s6Header;
|
||||
if (SawyerEncoding::TryReadChunk(&s6Header, stream))
|
||||
try
|
||||
{
|
||||
auto chunkReader = SawyerChunkReader(stream);
|
||||
auto s6Header = chunkReader.ReadChunkAs<rct_s6_header>();
|
||||
if (s6Header.type == S6_TYPE_SAVEDGAME)
|
||||
{
|
||||
result->Type = FILE_TYPE::SAVED_GAME;
|
||||
@@ -85,7 +86,9 @@ static bool TryClassifyAsS6(IStream * stream, ClassifiedFile * result)
|
||||
result->Version = s6Header.version;
|
||||
return true;
|
||||
}
|
||||
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
#include <SDL.h>
|
||||
#include "IStream.hpp"
|
||||
#include "Math.hpp"
|
||||
|
||||
enum
|
||||
{
|
||||
@@ -56,8 +57,8 @@ public:
|
||||
_canWrite = false;
|
||||
break;
|
||||
case FILE_MODE_WRITE:
|
||||
mode = "wb";
|
||||
_canRead = false;
|
||||
mode = "w+b";
|
||||
_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
|
||||
|
||||
@@ -74,6 +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" />
|
||||
@@ -417,6 +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" />
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -929,7 +930,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);
|
||||
@@ -1465,7 +1467,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -31,7 +31,8 @@
|
||||
#include "../core/Stopwatch.hpp"
|
||||
#include "../core/String.hpp"
|
||||
#include "../PlatformEnvironment.h"
|
||||
#include "../rct12/SawyerEncoding.h"
|
||||
#include "../rct12/SawyerChunkReader.h"
|
||||
#include "../rct12/SawyerChunkWriter.h"
|
||||
#include "../scenario/ScenarioRepository.h"
|
||||
#include "Object.h"
|
||||
#include "ObjectFactory.h"
|
||||
@@ -224,29 +225,41 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
bool TryExportPackedObject(IStream * stream) override
|
||||
void ExportPackedObject(IStream * stream) override
|
||||
{
|
||||
auto chunkReader = SawyerChunkReader(stream);
|
||||
|
||||
// Check if we already have this object
|
||||
rct_object_entry entry = stream->ReadValue<rct_object_entry>();
|
||||
if (FindObject(&entry) != nullptr)
|
||||
{
|
||||
SawyerEncoding::SkipChunk(stream);
|
||||
chunkReader.SkipChunk();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Read object and save to new file
|
||||
size_t chunkSize;
|
||||
void * chunk = SawyerEncoding::ReadChunk(stream, &chunkSize);
|
||||
if (chunk == nullptr)
|
||||
{
|
||||
log_error("Failed to reallocate buffer for packed object.");
|
||||
return false;
|
||||
}
|
||||
|
||||
AddObject(&entry, chunk, chunkSize);
|
||||
Memory::Free(chunk);
|
||||
std::shared_ptr<SawyerChunk> chunk = chunkReader.ReadChunk();
|
||||
AddObject(&entry, chunk->GetData(), chunk->GetLength());
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -654,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;
|
||||
@@ -669,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)
|
||||
@@ -761,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();
|
||||
|
||||
@@ -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
|
||||
|
||||
30
src/openrct2/rct12/SawyerChunk.cpp
Normal file
30
src/openrct2/rct12/SawyerChunk.cpp
Normal 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);
|
||||
}
|
||||
49
src/openrct2/rct12/SawyerChunk.h
Normal file
49
src/openrct2/rct12/SawyerChunk.h
Normal 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();
|
||||
};
|
||||
124
src/openrct2/rct12/SawyerChunkReader.cpp
Normal file
124
src/openrct2/rct12/SawyerChunkReader.cpp
Normal file
@@ -0,0 +1,124 @@
|
||||
#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 "SawyerChunkReader.h"
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include "../util/sawyercoding.h"
|
||||
}
|
||||
|
||||
// Allow chunks to be uncompressed to a maximum of 16 MiB
|
||||
constexpr size_t MAX_UNCOMPRESSED_CHUNK_SIZE = 16 * 1024 * 1024;
|
||||
|
||||
class SawyerChunkException : public IOException
|
||||
{
|
||||
public:
|
||||
explicit SawyerChunkException(const char * message) : IOException(message) { }
|
||||
explicit SawyerChunkException(const std::string &message) : IOException(message) { }
|
||||
};
|
||||
|
||||
SawyerChunkReader::SawyerChunkReader(IStream * stream)
|
||||
: _stream(stream)
|
||||
{
|
||||
}
|
||||
|
||||
void SawyerChunkReader::SkipChunk()
|
||||
{
|
||||
uint64 originalPosition = _stream->GetPosition();
|
||||
try
|
||||
{
|
||||
auto header = _stream->ReadValue<sawyercoding_chunk_header>();
|
||||
_stream->Seek(header.length, STREAM_SEEK_CURRENT);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// Rewind stream back to original position
|
||||
_stream->SetPosition(originalPosition);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<SawyerChunk> SawyerChunkReader::ReadChunk()
|
||||
{
|
||||
uint64 originalPosition = _stream->GetPosition();
|
||||
try
|
||||
{
|
||||
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:
|
||||
{
|
||||
std::unique_ptr<uint8> compressedData = std::unique_ptr<uint8>(Memory::Allocate<uint8>(header.length));
|
||||
if (_stream->TryRead(compressedData.get(), header.length) != header.length)
|
||||
{
|
||||
throw SawyerChunkException("Corrupt chunk size.");
|
||||
}
|
||||
|
||||
// Allow 16MiB for chunk data
|
||||
size_t bufferSize = MAX_UNCOMPRESSED_CHUNK_SIZE;
|
||||
uint8 * buffer = Memory::Allocate<uint8>(bufferSize);
|
||||
if (buffer == nullptr)
|
||||
{
|
||||
throw Exception("Unable to allocate buffer.");
|
||||
}
|
||||
|
||||
size_t uncompressedLength = sawyercoding_read_chunk_buffer(buffer, compressedData.get(), header, bufferSize);
|
||||
buffer = Memory::Reallocate(buffer, uncompressedLength);
|
||||
if (buffer == nullptr)
|
||||
{
|
||||
throw Exception("Unable to reallocate buffer.");
|
||||
}
|
||||
|
||||
return std::make_shared<SawyerChunk>((SAWYER_ENCODING)header.encoding, buffer, uncompressedLength);
|
||||
}
|
||||
default:
|
||||
throw SawyerChunkException("Invalid chunk encoding.");
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// Rewind stream back to original position
|
||||
_stream->SetPosition(originalPosition);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
void SawyerChunkReader::ReadChunk(void * dst, size_t length)
|
||||
{
|
||||
auto chunk = ReadChunk();
|
||||
const void * chunkData = chunk->GetData();
|
||||
size_t chunkLength = chunk->GetLength();
|
||||
if (chunkLength > length)
|
||||
{
|
||||
Memory::Copy(dst, chunkData, length);
|
||||
}
|
||||
else
|
||||
{
|
||||
Memory::Copy(dst, chunkData, chunkLength);
|
||||
size_t remainingLength = length - chunkLength;
|
||||
if (remainingLength > 0)
|
||||
{
|
||||
void * offset = (void *)((uintptr_t)dst + chunkLength);
|
||||
Memory::Set(offset, 0, remainingLength);
|
||||
}
|
||||
}
|
||||
}
|
||||
70
src/openrct2/rct12/SawyerChunkReader.h
Normal file
70
src/openrct2/rct12/SawyerChunkReader.h
Normal file
@@ -0,0 +1,70 @@
|
||||
#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;
|
||||
|
||||
/**
|
||||
* Reads sawyer encoding chunks from a data stream. This can be used to read
|
||||
* SC6, SV6 and RCT2 objects.
|
||||
*/
|
||||
class SawyerChunkReader final
|
||||
{
|
||||
private:
|
||||
IStream * const _stream = nullptr;
|
||||
|
||||
public:
|
||||
SawyerChunkReader(IStream * stream);
|
||||
|
||||
/**
|
||||
* Skips the next chunk in the stream without decoding or reading its data
|
||||
* into RAM.
|
||||
*/
|
||||
void SkipChunk();
|
||||
|
||||
/**
|
||||
* Reads the next chunk from the stream.
|
||||
*/
|
||||
std::shared_ptr<SawyerChunk> ReadChunk();
|
||||
|
||||
/**
|
||||
* Reads the next chunk from the stream and copies it directly to the
|
||||
* destination buffer. If the chunk is larger than length, only length
|
||||
* is copied. If the chunk is smaller than length, the remaining space
|
||||
* is padded with zero.
|
||||
* @param dst The destination buffer.
|
||||
* @param length The size of the destination buffer.
|
||||
*/
|
||||
void ReadChunk(void * dst, size_t length);
|
||||
|
||||
/**
|
||||
* Reads the next chunk from the stream into a buffer returned as the
|
||||
* specified type. If the chunk is smaller than the size of the type
|
||||
* then the remaining space is padded with zero.
|
||||
*/
|
||||
template<typename T>
|
||||
T ReadChunkAs()
|
||||
{
|
||||
T result;
|
||||
ReadChunk(&result, sizeof(result));
|
||||
return result;
|
||||
}
|
||||
};
|
||||
50
src/openrct2/rct12/SawyerChunkWriter.cpp
Normal file
50
src/openrct2/rct12/SawyerChunkWriter.cpp
Normal 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);
|
||||
}
|
||||
57
src/openrct2/rct12/SawyerChunkWriter.h
Normal file
57
src/openrct2/rct12/SawyerChunkWriter.h
Normal 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);
|
||||
}
|
||||
};
|
||||
@@ -14,140 +14,14 @@
|
||||
*****************************************************************************/
|
||||
#pragma endregion
|
||||
|
||||
#include <memory>
|
||||
#include "../core/IStream.hpp"
|
||||
#include "../core/Math.hpp"
|
||||
#include "SawyerEncoding.h"
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include "../util/sawyercoding.h"
|
||||
}
|
||||
|
||||
namespace SawyerEncoding
|
||||
{
|
||||
void SkipChunk(IStream * stream)
|
||||
{
|
||||
auto header = stream->ReadValue<sawyercoding_chunk_header>();
|
||||
stream->Seek(header.length, STREAM_SEEK_CURRENT);
|
||||
}
|
||||
|
||||
void * ReadChunk(IStream * stream, size_t * outSize)
|
||||
{
|
||||
uint64 originalPosition = stream->GetPosition();
|
||||
|
||||
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:
|
||||
{
|
||||
std::unique_ptr<uint8> compressedData = std::unique_ptr<uint8>(Memory::Allocate<uint8>(header.length));
|
||||
if (stream->TryRead(compressedData.get(), header.length) != header.length)
|
||||
{
|
||||
throw IOException("Corrupt chunk size.");
|
||||
}
|
||||
|
||||
// Allow 16MiB for chunk data
|
||||
size_t bufferSize = 16 * 1024 * 1024;
|
||||
uint8 * buffer = Memory::Allocate<uint8>(bufferSize);
|
||||
if (buffer == nullptr)
|
||||
{
|
||||
throw Exception("Unable to allocate buffer.");
|
||||
}
|
||||
|
||||
size_t uncompressedLength = sawyercoding_read_chunk_buffer(buffer, compressedData.get(), header, bufferSize);
|
||||
buffer = Memory::Reallocate(buffer, uncompressedLength);
|
||||
if (buffer == nullptr)
|
||||
{
|
||||
throw Exception("Unable to reallocate buffer.");
|
||||
}
|
||||
|
||||
if (outSize != nullptr) *outSize = uncompressedLength;
|
||||
return buffer;
|
||||
}
|
||||
default:
|
||||
stream->SetPosition(originalPosition);
|
||||
throw IOException("Invalid chunk encoding.");
|
||||
}
|
||||
}
|
||||
|
||||
void ReadChunk(void * dst, size_t expectedSize, IStream * stream)
|
||||
{
|
||||
if (!TryReadChunk(dst, expectedSize, stream))
|
||||
{
|
||||
throw IOException("Invalid or incorrect chunk size.");
|
||||
}
|
||||
}
|
||||
|
||||
void ReadChunkTolerant(void * dst, size_t expectedSize, IStream * stream)
|
||||
{
|
||||
uint64 originalPosition = stream->GetPosition();
|
||||
|
||||
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:
|
||||
{
|
||||
std::unique_ptr<uint8> compressedData = std::unique_ptr<uint8>(Memory::Allocate<uint8>(header.length));
|
||||
if (stream->TryRead(compressedData.get(), header.length) != header.length)
|
||||
{
|
||||
throw IOException("Corrupt chunk size.");
|
||||
}
|
||||
|
||||
// Allow 16MiB for chunk data
|
||||
size_t bufferSize = 16 * 1024 * 1024;
|
||||
std::unique_ptr<uint8> buffer = std::unique_ptr<uint8>(Memory::Allocate<uint8>(bufferSize));
|
||||
size_t uncompressedLength = sawyercoding_read_chunk_buffer(buffer.get(), compressedData.get(), header, bufferSize);
|
||||
size_t copyLength = Math::Min(uncompressedLength, expectedSize);
|
||||
|
||||
Memory::Set(dst, 0, expectedSize);
|
||||
Memory::Copy<void>(dst, buffer.get(), copyLength);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
stream->SetPosition(originalPosition);
|
||||
throw IOException("Invalid chunk encoding.");
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
bool ValidateChecksum(IStream * stream)
|
||||
{
|
||||
// Get data size
|
||||
uint64 initialPosition = stream->GetPosition();
|
||||
uint64 dataSize = stream->GetLength() - initialPosition;
|
||||
if (dataSize < 8)
|
||||
@@ -156,29 +30,37 @@ namespace SawyerEncoding
|
||||
}
|
||||
dataSize -= 4;
|
||||
|
||||
// Calculate checksum
|
||||
uint32 checksum = 0;
|
||||
do
|
||||
try
|
||||
{
|
||||
uint8 buffer[4096];
|
||||
uint64 bufferSize = Math::Min<uint64>(dataSize, sizeof(buffer));
|
||||
stream->Read(buffer, bufferSize);
|
||||
|
||||
for (uint64 i = 0; i < bufferSize; i++)
|
||||
// Calculate checksum
|
||||
uint32 checksum = 0;
|
||||
do
|
||||
{
|
||||
checksum += buffer[i];
|
||||
uint8 buffer[4096];
|
||||
uint64 bufferSize = Math::Min<uint64>(dataSize, sizeof(buffer));
|
||||
stream->Read(buffer, bufferSize);
|
||||
|
||||
for (uint64 i = 0; i < bufferSize; i++)
|
||||
{
|
||||
checksum += buffer[i];
|
||||
}
|
||||
|
||||
dataSize -= bufferSize;
|
||||
}
|
||||
while (dataSize != 0);
|
||||
|
||||
dataSize -= bufferSize;
|
||||
// Read file checksum
|
||||
uint32 fileChecksum = stream->ReadValue<uint32>();
|
||||
|
||||
// Rewind back to original position
|
||||
stream->SetPosition(initialPosition);
|
||||
return checksum == fileChecksum;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// Rewind back to original position
|
||||
stream->SetPosition(initialPosition);
|
||||
return false;
|
||||
}
|
||||
while (dataSize != 0);
|
||||
|
||||
// Read file checksum
|
||||
uint32 fileChecksum = stream->ReadValue<uint32>();
|
||||
|
||||
// Rewind
|
||||
stream->SetPosition(initialPosition);
|
||||
|
||||
return checksum == fileChecksum;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,17 +22,5 @@ interface IStream;
|
||||
|
||||
namespace SawyerEncoding
|
||||
{
|
||||
void SkipChunk(IStream * stream);
|
||||
void * ReadChunk(IStream * stream, size_t * outSize);
|
||||
void ReadChunk(void * dst, size_t expectedSize, IStream * stream);
|
||||
void ReadChunkTolerant(void * dst, size_t expectedSize, IStream * stream);
|
||||
bool TryReadChunk(void * dst, size_t expectedSize, IStream * stream);
|
||||
|
||||
template<typename T>
|
||||
bool TryReadChunk(T * dst, IStream * stream)
|
||||
{
|
||||
return TryReadChunk(dst, sizeof(T), stream);
|
||||
}
|
||||
|
||||
bool ValidateChecksum(IStream * stream);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "../object/ObjectRepository.h"
|
||||
#include "../ParkImporter.h"
|
||||
#include "../rct12/SawyerEncoding.h"
|
||||
#include "../rct12/SawyerChunkReader.h"
|
||||
|
||||
extern "C"
|
||||
{
|
||||
@@ -110,7 +111,8 @@ public:
|
||||
throw IOException("Invalid checksum.");
|
||||
}
|
||||
|
||||
SawyerEncoding::ReadChunkTolerant(&_s6.header, sizeof(_s6.header), stream);
|
||||
auto chunkReader = SawyerChunkReader(stream);
|
||||
chunkReader.ReadChunk(&_s6.header, sizeof(_s6.header));
|
||||
|
||||
log_verbose("saved game classic_flag = 0x%02x\n", _s6.header.classic_flag);
|
||||
if (isScenario)
|
||||
@@ -119,7 +121,7 @@ public:
|
||||
{
|
||||
throw Exception("Park is not a scenario.");
|
||||
}
|
||||
SawyerEncoding::ReadChunkTolerant(&_s6.info, sizeof(_s6.info), stream);
|
||||
chunkReader.ReadChunk(&_s6.info, sizeof(_s6.info));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -134,29 +136,29 @@ public:
|
||||
IObjectRepository * objectRepo = GetObjectRepository();
|
||||
for (uint16 i = 0; i < _s6.header.num_packed_objects; i++)
|
||||
{
|
||||
objectRepo->TryExportPackedObject(stream);
|
||||
objectRepo->ExportPackedObject(stream);
|
||||
}
|
||||
|
||||
if (isScenario)
|
||||
{
|
||||
SawyerEncoding::ReadChunkTolerant(&_s6.objects, sizeof(_s6.objects), stream);
|
||||
SawyerEncoding::ReadChunkTolerant(&_s6.elapsed_months, 16, stream);
|
||||
SawyerEncoding::ReadChunkTolerant(&_s6.map_elements, sizeof(_s6.map_elements), stream);
|
||||
SawyerEncoding::ReadChunkTolerant(&_s6.next_free_map_element_pointer_index, 2560076, stream);
|
||||
SawyerEncoding::ReadChunkTolerant(&_s6.guests_in_park, 4, stream);
|
||||
SawyerEncoding::ReadChunkTolerant(&_s6.last_guests_in_park, 8, stream);
|
||||
SawyerEncoding::ReadChunkTolerant(&_s6.park_rating, 2, stream);
|
||||
SawyerEncoding::ReadChunkTolerant(&_s6.active_research_types, 1082, stream);
|
||||
SawyerEncoding::ReadChunkTolerant(&_s6.current_expenditure, 16, stream);
|
||||
SawyerEncoding::ReadChunkTolerant(&_s6.park_value, 4, stream);
|
||||
SawyerEncoding::ReadChunkTolerant(&_s6.completed_company_value, 483816, stream);
|
||||
chunkReader.ReadChunk(&_s6.objects, sizeof(_s6.objects));
|
||||
chunkReader.ReadChunk(&_s6.elapsed_months, 16);
|
||||
chunkReader.ReadChunk(&_s6.map_elements, sizeof(_s6.map_elements));
|
||||
chunkReader.ReadChunk(&_s6.next_free_map_element_pointer_index, 2560076);
|
||||
chunkReader.ReadChunk(&_s6.guests_in_park, 4);
|
||||
chunkReader.ReadChunk(&_s6.last_guests_in_park, 8);
|
||||
chunkReader.ReadChunk(&_s6.park_rating, 2);
|
||||
chunkReader.ReadChunk(&_s6.active_research_types, 1082);
|
||||
chunkReader.ReadChunk(&_s6.current_expenditure, 16);
|
||||
chunkReader.ReadChunk(&_s6.park_value, 4);
|
||||
chunkReader.ReadChunk(&_s6.completed_company_value, 483816);
|
||||
}
|
||||
else
|
||||
{
|
||||
SawyerEncoding::ReadChunkTolerant(&_s6.objects, sizeof(_s6.objects), stream);
|
||||
SawyerEncoding::ReadChunkTolerant(&_s6.elapsed_months, 16, stream);
|
||||
SawyerEncoding::ReadChunkTolerant(&_s6.map_elements, sizeof(_s6.map_elements), stream);
|
||||
SawyerEncoding::ReadChunkTolerant(&_s6.next_free_map_element_pointer_index, 3048816, stream);
|
||||
chunkReader.ReadChunk(&_s6.objects, sizeof(_s6.objects));
|
||||
chunkReader.ReadChunk(&_s6.elapsed_months, 16);
|
||||
chunkReader.ReadChunk(&_s6.map_elements, sizeof(_s6.map_elements));
|
||||
chunkReader.ReadChunk(&_s6.next_free_map_element_pointer_index, 3048816);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
#include "../core/Util.hpp"
|
||||
#include "../ParkImporter.h"
|
||||
#include "../PlatformEnvironment.h"
|
||||
#include "../rct12/SawyerEncoding.h"
|
||||
#include "../rct12/SawyerChunkReader.h"
|
||||
#include "ScenarioRepository.h"
|
||||
#include "ScenarioSources.h"
|
||||
|
||||
@@ -336,26 +336,18 @@ private:
|
||||
{
|
||||
// RCT2 scenario
|
||||
auto fs = FileStream(path, FILE_MODE_OPEN);
|
||||
rct_s6_header header;
|
||||
if (SawyerEncoding::TryReadChunk(&header, &fs))
|
||||
auto chunkReader = SawyerChunkReader(&fs);
|
||||
|
||||
rct_s6_header header = chunkReader.ReadChunkAs<rct_s6_header>();
|
||||
if (header.type == S6_TYPE_SCENARIO)
|
||||
{
|
||||
if (header.type == S6_TYPE_SCENARIO)
|
||||
{
|
||||
rct_s6_info info;
|
||||
if (SawyerEncoding::TryReadChunk(&info, &fs))
|
||||
{
|
||||
*entry = CreateNewScenarioEntry(path, timestamp, &info);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new IOException("Unable to read S6_INFO chunk.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
log_verbose("%s is not a scenario", path.c_str());
|
||||
}
|
||||
rct_s6_info info = chunkReader.ReadChunkAs<rct_s6_info>();
|
||||
*entry = CreateNewScenarioEntry(path, timestamp, &info);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
log_verbose("%s is not a scenario", path.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user