Add sea decryption

This commit is contained in:
Ted John 2020-07-09 22:33:45 +01:00
parent 0e788918e2
commit 8c81cacc6f
6 changed files with 130 additions and 5 deletions

View File

@ -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).

View File

@ -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";

View File

@ -541,8 +541,17 @@ 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);
return LoadParkFromStream(&ms, path, loadTitleScreenOnFail);
}
else
{
auto fs = FileStream(path, FILE_MODE_OPEN);
return LoadParkFromStream(&fs, path, loadTitleScreenOnFail);
}
}
catch (const std::exception& e)
{

View File

@ -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" />

View File

@ -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);

View File

@ -0,0 +1,109 @@
/*****************************************************************************
* 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::unique_ptr<uint8_t[]> CreateMask(const EncryptionKey& key)
{
auto result = std::make_unique<uint8_t[]>(MASK_SIZE);
auto dst8 = result.get();
uint32_t seed0 = key.Seed0;
uint32_t seed1 = key.Seed1;
int32_t i = MASK_SIZE;
while (i > 0)
{
uint32_t s0 = seed0;
uint32_t s1 = seed1 ^ 0xF7654321;
seed0 = rol32(s1, 25) + s0;
seed1 = rol32(s0, 29);
*dst8++ = (s0 >> 3) & 0xFF;
if (i >= 2)
{
*dst8++ = (s0 >> 11) & 0xFF;
if (i >= 3)
{
*dst8++ = (s0 >> 19) & 0xFF;
if (i >= 4)
{
*dst8++ = (seed1 >> 24) & 0xFF;
i -= 4;
}
}
}
}
return result;
}
static void Decrypt(std::vector<uint8_t>& data, const EncryptionKey& key)
{
auto mask = CreateMask(key);
const uint8_t* mask8 = (const uint8_t*)mask.get();
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] - mask8[b]) ^ mask8[c]) + mask8[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;
}