OpenRCT2/src/openrct2/rct2/SeaDecrypt.cpp

99 lines
2.5 KiB
C++

/*****************************************************************************
* 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/Numerics.hpp"
#include "../core/Path.hpp"
#include "RCT2.h"
#include <cstdint>
#include <cstring>
#include <memory>
#include <string_view>
constexpr int32_t MASK_SIZE = 0x1000;
struct EncryptionKey
{
uint32_t Seed0{};
uint32_t Seed1{};
};
static EncryptionKey GetEncryptionKey(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 = Numerics::rol32(s1, 25) + s0;
seed1 = Numerics::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;
}