mirror of
https://github.com/OpenRCT2/OpenRCT2.git
synced 2025-01-23 02:41:58 -05:00
Merge pull request #12184 from IntelOrca/sea
Add support for reading .sea (RCT classic) files
This commit is contained in:
commit
1cf89362f0
12 changed files with 142 additions and 7 deletions
|
@ -70,6 +70,7 @@
|
|||
932A211E22D73CFA00C57EDB /* GameActionCompat.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 932A20CF22D73CEE00C57EDB /* GameActionCompat.cpp */; };
|
||||
932A211F22D73CFA00C57EDB /* GameActionRegistration.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 932A20D322D73CEF00C57EDB /* GameActionRegistration.cpp */; };
|
||||
932A212022D73CFA00C57EDB /* GameAction.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 932A211C22D73CFA00C57EDB /* GameAction.cpp */; };
|
||||
933C55B524B858490057E64B /* SeaDecrypt.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 933C55B424B858490057E64B /* SeaDecrypt.cpp */; };
|
||||
933CBDB520CB1ACD00134678 /* Widget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 933CBDB120CB1ACC00134678 /* Widget.cpp */; };
|
||||
933CBDB620CB1ACD00134678 /* Theme.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 933CBDB220CB1ACD00134678 /* Theme.cpp */; };
|
||||
933CBDBB20CB1B3F00134678 /* TitleSequencePlayer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 933CBDB920CB1B3F00134678 /* TitleSequencePlayer.cpp */; };
|
||||
|
@ -1061,6 +1062,7 @@
|
|||
932A211B22D73CFA00C57EDB /* TrackSetBrakeSpeedAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = TrackSetBrakeSpeedAction.hpp; sourceTree = "<group>"; };
|
||||
932A211C22D73CFA00C57EDB /* GameAction.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GameAction.cpp; sourceTree = "<group>"; };
|
||||
932A211D22D73CFA00C57EDB /* LargeSceneryRemoveAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = LargeSceneryRemoveAction.hpp; sourceTree = "<group>"; };
|
||||
933C55B424B858490057E64B /* SeaDecrypt.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SeaDecrypt.cpp; sourceTree = "<group>"; };
|
||||
933CBDB120CB1ACC00134678 /* Widget.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Widget.cpp; sourceTree = "<group>"; };
|
||||
933CBDB220CB1ACD00134678 /* Theme.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Theme.cpp; sourceTree = "<group>"; };
|
||||
933CBDB320CB1ACD00134678 /* Theme.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Theme.h; sourceTree = "<group>"; };
|
||||
|
@ -3158,6 +3160,7 @@
|
|||
F76C847D1EC4E7CC00FA49E2 /* S6Exporter.cpp */,
|
||||
F76C847E1EC4E7CC00FA49E2 /* S6Exporter.h */,
|
||||
F76C847F1EC4E7CC00FA49E2 /* S6Importer.cpp */,
|
||||
933C55B424B858490057E64B /* SeaDecrypt.cpp */,
|
||||
01C6F0C522FD51FC0057E2F7 /* T6Exporter.cpp */,
|
||||
01C6F0C722FD51FC0057E2F7 /* T6Exporter.h */,
|
||||
01C6F0C622FD51FC0057E2F7 /* T6Importer.cpp */,
|
||||
|
@ -4202,6 +4205,7 @@
|
|||
C68878E220289B9B0084B384 /* Staff.cpp in Sources */,
|
||||
F76C85CF1EC4E88300FA49E2 /* Console.cpp in Sources */,
|
||||
C68878DC20289B9B0084B384 /* Painter.cpp in Sources */,
|
||||
933C55B524B858490057E64B /* SeaDecrypt.cpp in Sources */,
|
||||
C688790120289B9B0084B384 /* ReverserRollerCoaster.cpp in Sources */,
|
||||
C688786120289A0A0084B384 /* MapAnimation.cpp in Sources */,
|
||||
F76C85D11EC4E88300FA49E2 /* Diagnostics.cpp in Sources */,
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
- Feature: [#11788] Command to extract images from a .DAT file.
|
||||
- Feature: [#11959] Hacked go-kart tracks can now use 2x2 bends, 3x3 bends and S-bends.
|
||||
- Feature: [#12090] Boosters for the Wooden Roller Coaster (if the "Show all track pieces" cheat is enabled).
|
||||
- Feature: [#12184] .sea (RCT Classic) scenario files can now be imported.
|
||||
- Change: [#11209] Warn when user is running OpenRCT2 through Wine.
|
||||
- Change: [#11358] Switch copy and paste button positions in tile inspector.
|
||||
- Change: [#11449] Remove complete circuit requirement from Air Powered Vertical Coaster (for RCT1 parity).
|
||||
|
|
|
@ -1205,7 +1205,7 @@ namespace OpenRCT2::Ui::Windows
|
|||
{
|
||||
if (customWidgetInfo->Name == name)
|
||||
{
|
||||
return i;
|
||||
return static_cast<rct_widgetindex>(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -150,7 +150,7 @@ static std::vector<LoadSaveListItem> _listItems;
|
|||
static char _directory[MAX_PATH];
|
||||
static char _shortenedDirectory[MAX_PATH];
|
||||
static char _parentDirectory[MAX_PATH];
|
||||
static char _extension[32];
|
||||
static char _extension[256];
|
||||
static char _defaultName[MAX_PATH];
|
||||
static int32_t _type;
|
||||
|
||||
|
@ -218,10 +218,10 @@ static const char* getFilterPatternByType(const int32_t type, const bool isSave)
|
|||
switch (type & 0x0E)
|
||||
{
|
||||
case LOADSAVETYPE_GAME:
|
||||
return isSave ? "*.sv6" : "*.sv6;*.sc6;*.sc4;*.sv4;*.sv7";
|
||||
return isSave ? "*.sv6" : "*.sv6;*.sc6;*.sc4;*.sv4;*.sv7;*.sea;";
|
||||
|
||||
case LOADSAVETYPE_LANDSCAPE:
|
||||
return isSave ? "*.sc6" : "*.sc6;*.sv6;*.sc4;*.sv4;*.sv7";
|
||||
return isSave ? "*.sc6" : "*.sc6;*.sv6;*.sc4;*.sv4;*.sv7;*.sea;";
|
||||
|
||||
case LOADSAVETYPE_SCENARIO:
|
||||
return "*.sc6";
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
// Then the rest
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <openrct2-ui/Ui.h>
|
||||
#include <openrct2/core/String.hpp>
|
||||
#include <stdio.h>
|
||||
|
|
|
@ -541,8 +541,22 @@ namespace OpenRCT2
|
|||
log_verbose("Context::LoadParkFromFile(%s)", path.c_str());
|
||||
try
|
||||
{
|
||||
auto fs = FileStream(path, FILE_MODE_OPEN);
|
||||
return LoadParkFromStream(&fs, path, loadTitleScreenOnFail);
|
||||
if (String::Equals(Path::GetExtension(path), ".sea", true))
|
||||
{
|
||||
auto data = DecryptSea(fs::u8path(path));
|
||||
auto ms = MemoryStream(data.data(), data.size(), MEMORY_ACCESS::READ);
|
||||
if (!LoadParkFromStream(&ms, path, loadTitleScreenOnFail))
|
||||
{
|
||||
Console::Error::WriteLine(".sea file may have been renamed.");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto fs = FileStream(path, FILE_MODE_OPEN);
|
||||
return LoadParkFromStream(&fs, path, loadTitleScreenOnFail);
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
|
|
|
@ -43,6 +43,7 @@ namespace fs = std::filesystem;
|
|||
# define WIN32_LEAN_AND_MEAN
|
||||
# endif
|
||||
# define BITMAP WIN32_BITMAP
|
||||
# define PATTERN WIN32_PATTERN
|
||||
# endif
|
||||
# include <filesystem.hpp>
|
||||
# ifdef _WIN32
|
||||
|
@ -50,6 +51,7 @@ namespace fs = std::filesystem;
|
|||
# undef CreateWindow
|
||||
# undef GetMessage
|
||||
# undef BITMAP
|
||||
# undef PATTERN
|
||||
# endif
|
||||
namespace fs = ghc::filesystem;
|
||||
#endif
|
||||
|
|
|
@ -629,6 +629,7 @@
|
|||
<ClCompile Include="rct1\Tables.cpp" />
|
||||
<ClCompile Include="rct2\S6Exporter.cpp" />
|
||||
<ClCompile Include="rct2\S6Importer.cpp" />
|
||||
<ClCompile Include="rct2\SeaDecrypt.cpp" />
|
||||
<ClCompile Include="rct2\T6Exporter.cpp" />
|
||||
<ClCompile Include="rct2\T6Importer.cpp" />
|
||||
<ClCompile Include="ReplayManager.cpp" />
|
||||
|
|
|
@ -10,11 +10,14 @@
|
|||
#pragma once
|
||||
|
||||
#include "../common.h"
|
||||
#include "../core/FileSystem.hpp"
|
||||
#include "../object/Object.h"
|
||||
#include "../rct12/RCT12.h"
|
||||
#include "../ride/RideRatings.h"
|
||||
#include "../ride/Vehicle.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
constexpr const uint8_t RCT2_MAX_STAFF = 200;
|
||||
constexpr const uint8_t RCT2_MAX_BANNERS_IN_PARK = 250;
|
||||
constexpr const uint8_t RCT2_MAX_VEHICLES_PER_RIDE = 31;
|
||||
|
@ -760,3 +763,5 @@ struct RCT2RideRatingCalculationData
|
|||
assert_struct_size(RCT2RideRatingCalculationData, 76);
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
std::vector<uint8_t> DecryptSea(const fs::path& path);
|
||||
|
|
96
src/openrct2/rct2/SeaDecrypt.cpp
Normal file
96
src/openrct2/rct2/SeaDecrypt.cpp
Normal file
|
@ -0,0 +1,96 @@
|
|||
/*****************************************************************************
|
||||
* Copyright (c) 2014-2020 OpenRCT2 developers
|
||||
*
|
||||
* For a complete list of all authors, please refer to contributors.md
|
||||
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
|
||||
*
|
||||
* OpenRCT2 is licensed under the GNU General Public License version 3.
|
||||
*****************************************************************************/
|
||||
|
||||
#include "../common.h"
|
||||
#include "../core/File.h"
|
||||
#include "../core/Path.hpp"
|
||||
#include "RCT2.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <string_view>
|
||||
|
||||
constexpr int32_t MASK_SIZE = 0x1000;
|
||||
|
||||
struct EncryptionKey
|
||||
{
|
||||
uint32_t Seed0{};
|
||||
uint32_t Seed1{};
|
||||
};
|
||||
|
||||
static EncryptionKey GetEncryptionKey(const std::string_view& fileName)
|
||||
{
|
||||
auto fileNameLen = static_cast<int32_t>(fileName.size());
|
||||
uint32_t s0 = 0;
|
||||
for (int i = fileNameLen - 1; i >= 0; i--)
|
||||
{
|
||||
s0 = (s0 + (s0 << 5)) ^ fileName[i];
|
||||
}
|
||||
|
||||
uint32_t s1 = 0;
|
||||
for (int i = 0; i < fileNameLen; i++)
|
||||
{
|
||||
s1 = (s1 + (s1 << 5)) ^ fileName[i];
|
||||
}
|
||||
|
||||
return EncryptionKey{ s0, s1 };
|
||||
}
|
||||
|
||||
static std::vector<uint8_t> CreateMask(const EncryptionKey& key)
|
||||
{
|
||||
std::vector<uint8_t> result;
|
||||
result.resize(MASK_SIZE);
|
||||
uint32_t seed0 = key.Seed0;
|
||||
uint32_t seed1 = key.Seed1;
|
||||
for (size_t i = 0; i < MASK_SIZE; i += 4)
|
||||
{
|
||||
uint32_t s0 = seed0;
|
||||
uint32_t s1 = seed1 ^ 0xF7654321;
|
||||
seed0 = rol32(s1, 25) + s0;
|
||||
seed1 = rol32(s0, 29);
|
||||
result[i + 0] = (s0 >> 3) & 0xFF;
|
||||
result[i + 1] = (s0 >> 11) & 0xFF;
|
||||
result[i + 2] = (s0 >> 19) & 0xFF;
|
||||
result[i + 3] = (seed1 >> 24) & 0xFF;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static void Decrypt(std::vector<uint8_t>& data, const EncryptionKey& key)
|
||||
{
|
||||
auto mask = CreateMask(key);
|
||||
uint32_t b = 0;
|
||||
uint32_t c = 0;
|
||||
for (size_t i = 0; i < data.size(); i++)
|
||||
{
|
||||
auto a = b % MASK_SIZE;
|
||||
c = c % MASK_SIZE;
|
||||
b = (a + 1) % MASK_SIZE;
|
||||
|
||||
data[i] = (((data[i] - mask[b]) ^ mask[c]) + mask[a]) & 0xFF;
|
||||
|
||||
c += 3;
|
||||
b = a + 7;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<uint8_t> DecryptSea(const fs::path& path)
|
||||
{
|
||||
auto key = GetEncryptionKey(path.filename().u8string());
|
||||
auto data = File::ReadAllBytes(path.u8string());
|
||||
|
||||
// Last 4 bytes is the checksum
|
||||
size_t inputSize = data.size() - 4;
|
||||
uint32_t checksum;
|
||||
std::memcpy(&checksum, data.data() + inputSize, sizeof(checksum));
|
||||
data.resize(inputSize);
|
||||
|
||||
Decrypt(data, key);
|
||||
return data;
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*****************************************************************************
|
||||
* Copyright (c) 2014-2019 OpenRCT2 developers
|
||||
* Copyright (c) 2014-2020 OpenRCT2 developers
|
||||
*
|
||||
* For a complete list of all authors, please refer to contributors.md
|
||||
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
|
||||
|
@ -18,6 +18,7 @@
|
|||
#include <openrct2/ParkImporter.h>
|
||||
#include <openrct2/audio/AudioContext.h>
|
||||
#include <openrct2/config/Config.h>
|
||||
#include <openrct2/core/Crypt.h>
|
||||
#include <openrct2/core/File.h>
|
||||
#include <openrct2/core/MemoryStream.h>
|
||||
#include <openrct2/core/Path.hpp>
|
||||
|
@ -566,3 +567,13 @@ TEST(S6ImportExportAdvanceTicks, all)
|
|||
|
||||
SUCCEED();
|
||||
}
|
||||
|
||||
TEST(SeaDecrypt, DecryptSea)
|
||||
{
|
||||
auto path = TestData::GetParkPath("volcania.sea");
|
||||
auto decrypted = DecryptSea(path);
|
||||
auto sha1 = Crypt::SHA1(decrypted.data(), decrypted.size());
|
||||
std::array<uint8_t, 20> expected = { 0x1B, 0x85, 0xFC, 0xC0, 0xE8, 0x9B, 0xBE, 0x72, 0xD9, 0x1F,
|
||||
0x6E, 0xC8, 0xB1, 0xFF, 0xEC, 0x70, 0x2A, 0x72, 0x05, 0xBB };
|
||||
ASSERT_EQ(sha1, expected);
|
||||
}
|
||||
|
|
BIN
test/tests/testdata/parks/volcania.sea
vendored
Normal file
BIN
test/tests/testdata/parks/volcania.sea
vendored
Normal file
Binary file not shown.
Loading…
Reference in a new issue