mirror of
https://github.com/OpenRCT2/OpenRCT2.git
synced 2025-01-22 10:21:57 -05:00
290 lines
8.8 KiB
C++
290 lines
8.8 KiB
C++
/*****************************************************************************
|
|
* Copyright (c) 2014-2024 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 "TestData.h"
|
|
|
|
#include <gtest/gtest.h>
|
|
#include <openrct2/Cheats.h>
|
|
#include <openrct2/Context.h>
|
|
#include <openrct2/Game.h>
|
|
#include <openrct2/GameState.h>
|
|
#include <openrct2/GameStateSnapshots.h>
|
|
#include <openrct2/OpenRCT2.h>
|
|
#include <openrct2/ParkImporter.h>
|
|
#include <openrct2/audio/AudioContext.h>
|
|
#include <openrct2/core/Crypt.h>
|
|
#include <openrct2/core/File.h>
|
|
#include <openrct2/core/MemoryStream.h>
|
|
#include <openrct2/core/Path.hpp>
|
|
#include <openrct2/core/String.hpp>
|
|
#include <openrct2/entity/EntityRegistry.h>
|
|
#include <openrct2/entity/EntityTweener.h>
|
|
#include <openrct2/network/network.h>
|
|
#include <openrct2/object/ObjectManager.h>
|
|
#include <openrct2/park/ParkFile.h>
|
|
#include <openrct2/platform/Platform.h>
|
|
#include <openrct2/rct2/RCT2.h>
|
|
#include <openrct2/ride/Ride.h>
|
|
#include <openrct2/scenario/Scenario.h>
|
|
#include <openrct2/world/MapAnimation.h>
|
|
#include <openrct2/world/Scenery.h>
|
|
#include <stdio.h>
|
|
#include <string>
|
|
|
|
using namespace OpenRCT2;
|
|
|
|
static bool LoadFileToBuffer(MemoryStream& stream, const std::string& filePath)
|
|
{
|
|
FILE* fp = fopen(filePath.c_str(), "rb");
|
|
EXPECT_NE(fp, nullptr);
|
|
if (fp == nullptr)
|
|
return false;
|
|
|
|
uint8_t buf[1024];
|
|
size_t bytesRead = fread(buf, 1, sizeof(buf), fp);
|
|
while (bytesRead > 0)
|
|
{
|
|
stream.Write(buf, bytesRead);
|
|
bytesRead = fread(buf, 1, sizeof(buf), fp);
|
|
}
|
|
fclose(fp);
|
|
|
|
return true;
|
|
}
|
|
|
|
static void GameInit(bool retainSpatialIndices)
|
|
{
|
|
if (!retainSpatialIndices)
|
|
ResetEntitySpatialIndices();
|
|
|
|
ResetAllSpriteQuadrantPlacements();
|
|
ScenerySetDefaultPlacementConfiguration();
|
|
LoadPalette();
|
|
EntityTweener::Get().Reset();
|
|
MapAnimationAutoCreate();
|
|
FixInvalidVehicleSpriteSizes();
|
|
|
|
gGameSpeed = 1;
|
|
}
|
|
|
|
static bool ImportS6(MemoryStream& stream, std::unique_ptr<IContext>& context, bool retainSpatialIndices)
|
|
{
|
|
stream.SetPosition(0);
|
|
|
|
auto& objManager = context->GetObjectManager();
|
|
|
|
auto importer = ParkImporter::CreateS6(context->GetObjectRepository());
|
|
auto loadResult = importer->LoadFromStream(&stream, false);
|
|
objManager.LoadObjects(loadResult.RequiredObjects);
|
|
|
|
// TODO: Have a separate GameState and exchange once loaded.
|
|
auto& gameState = GetGameState();
|
|
importer->Import(gameState);
|
|
|
|
GameInit(retainSpatialIndices);
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool ImportPark(MemoryStream& stream, std::unique_ptr<IContext>& context, bool retainSpatialIndices)
|
|
{
|
|
stream.SetPosition(0);
|
|
|
|
auto& objManager = context->GetObjectManager();
|
|
|
|
auto importer = ParkImporter::CreateParkFile(context->GetObjectRepository());
|
|
auto loadResult = importer->LoadFromStream(&stream, false);
|
|
objManager.LoadObjects(loadResult.RequiredObjects);
|
|
|
|
// TODO: Have a separate GameState and exchange once loaded.
|
|
auto& gameState = GetGameState();
|
|
importer->Import(gameState);
|
|
|
|
GameInit(retainSpatialIndices);
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool ExportSave(MemoryStream& stream, std::unique_ptr<IContext>& context)
|
|
{
|
|
auto& objManager = context->GetObjectManager();
|
|
|
|
auto exporter = std::make_unique<ParkFileExporter>();
|
|
exporter->ExportObjectsList = objManager.GetPackableObjects();
|
|
|
|
auto& gameState = GetGameState();
|
|
exporter->Export(gameState, stream);
|
|
|
|
return true;
|
|
}
|
|
|
|
static void RecordGameStateSnapshot(std::unique_ptr<IContext>& context, MemoryStream& snapshotStream)
|
|
{
|
|
auto* snapshots = context->GetGameStateSnapshots();
|
|
|
|
auto& snapshot = snapshots->CreateSnapshot();
|
|
snapshots->Capture(snapshot);
|
|
snapshots->LinkSnapshot(snapshot, GetGameState().CurrentTicks, ScenarioRandState().s0);
|
|
DataSerialiser snapShotDs(true, snapshotStream);
|
|
snapshots->SerialiseSnapshot(snapshot, snapShotDs);
|
|
}
|
|
|
|
static void AdvanceGameTicks(uint32_t ticks, std::unique_ptr<IContext>& context)
|
|
{
|
|
for (uint32_t i = 0; i < ticks; i++)
|
|
{
|
|
gameStateUpdateLogic();
|
|
}
|
|
}
|
|
|
|
static void CompareStates(MemoryStream& importBuffer, MemoryStream& exportBuffer, MemoryStream& snapshotStream)
|
|
{
|
|
if (importBuffer.GetLength() != exportBuffer.GetLength())
|
|
{
|
|
LOG_WARNING(
|
|
"Inconsistent export size! Import Size: %llu bytes, Export Size: %llu bytes",
|
|
static_cast<unsigned long long>(importBuffer.GetLength()),
|
|
static_cast<unsigned long long>(exportBuffer.GetLength()));
|
|
}
|
|
|
|
std::unique_ptr<IContext> context = CreateContext();
|
|
EXPECT_NE(context, nullptr);
|
|
bool initialised = context->Initialise();
|
|
ASSERT_TRUE(initialised);
|
|
|
|
DataSerialiser ds(false, snapshotStream);
|
|
IGameStateSnapshots* snapshots = GetContext()->GetGameStateSnapshots();
|
|
|
|
GameStateSnapshot_t& importSnapshot = snapshots->CreateSnapshot();
|
|
snapshots->SerialiseSnapshot(importSnapshot, ds);
|
|
|
|
GameStateSnapshot_t& exportSnapshot = snapshots->CreateSnapshot();
|
|
snapshots->SerialiseSnapshot(exportSnapshot, ds);
|
|
|
|
try
|
|
{
|
|
GameStateCompareData cmpData = snapshots->Compare(importSnapshot, exportSnapshot);
|
|
|
|
// Find out if there are any differences between the two states
|
|
auto res = std::find_if(
|
|
cmpData.spriteChanges.begin(), cmpData.spriteChanges.end(),
|
|
[](const GameStateSpriteChange& diff) { return diff.changeType != GameStateSpriteChange::EQUAL; });
|
|
|
|
if (res != cmpData.spriteChanges.end())
|
|
{
|
|
LOG_WARNING("Snapshot data differences. %s", snapshots->GetCompareDataText(cmpData).c_str());
|
|
FAIL();
|
|
}
|
|
}
|
|
catch (const std::runtime_error& err)
|
|
{
|
|
LOG_WARNING("Snapshot data failed to be read. Snapshot not compared. %s", err.what());
|
|
FAIL();
|
|
}
|
|
}
|
|
|
|
TEST(S6ImportExportBasic, all)
|
|
{
|
|
gOpenRCT2Headless = true;
|
|
gOpenRCT2NoGraphics = true;
|
|
|
|
MemoryStream importBuffer;
|
|
MemoryStream exportBuffer;
|
|
MemoryStream snapshotStream;
|
|
|
|
// Load initial park data.
|
|
{
|
|
std::unique_ptr<IContext> context = CreateContext();
|
|
EXPECT_NE(context, nullptr);
|
|
|
|
bool initialised = context->Initialise();
|
|
ASSERT_TRUE(initialised);
|
|
|
|
std::string testParkPath = TestData::GetParkPath("BigMapTest.sv6");
|
|
ASSERT_TRUE(LoadFileToBuffer(importBuffer, testParkPath));
|
|
ASSERT_TRUE(ImportS6(importBuffer, context, false));
|
|
RecordGameStateSnapshot(context, snapshotStream);
|
|
|
|
ASSERT_TRUE(ExportSave(exportBuffer, context));
|
|
}
|
|
|
|
// Import the exported version.
|
|
{
|
|
std::unique_ptr<IContext> context = CreateContext();
|
|
EXPECT_NE(context, nullptr);
|
|
|
|
bool initialised = context->Initialise();
|
|
ASSERT_TRUE(initialised);
|
|
|
|
ASSERT_TRUE(ImportPark(exportBuffer, context, true));
|
|
|
|
RecordGameStateSnapshot(context, snapshotStream);
|
|
}
|
|
|
|
snapshotStream.SetPosition(0);
|
|
CompareStates(importBuffer, exportBuffer, snapshotStream);
|
|
|
|
SUCCEED();
|
|
}
|
|
|
|
TEST(S6ImportExportAdvanceTicks, all)
|
|
{
|
|
gOpenRCT2Headless = true;
|
|
gOpenRCT2NoGraphics = true;
|
|
|
|
MemoryStream importBuffer;
|
|
MemoryStream exportBuffer;
|
|
MemoryStream snapshotStream;
|
|
|
|
// Load initial park data.
|
|
{
|
|
std::unique_ptr<IContext> context = CreateContext();
|
|
EXPECT_NE(context, nullptr);
|
|
|
|
bool initialised = context->Initialise();
|
|
ASSERT_TRUE(initialised);
|
|
|
|
std::string testParkPath = TestData::GetParkPath("BigMapTest.sv6");
|
|
ASSERT_TRUE(LoadFileToBuffer(importBuffer, testParkPath));
|
|
ASSERT_TRUE(ImportS6(importBuffer, context, false));
|
|
AdvanceGameTicks(1000, context);
|
|
ASSERT_TRUE(ExportSave(exportBuffer, context));
|
|
|
|
RecordGameStateSnapshot(context, snapshotStream);
|
|
}
|
|
|
|
// Import the exported version.
|
|
{
|
|
std::unique_ptr<IContext> context = CreateContext();
|
|
EXPECT_NE(context, nullptr);
|
|
|
|
bool initialised = context->Initialise();
|
|
ASSERT_TRUE(initialised);
|
|
|
|
ASSERT_TRUE(ImportPark(exportBuffer, context, true));
|
|
|
|
RecordGameStateSnapshot(context, snapshotStream);
|
|
}
|
|
|
|
snapshotStream.SetPosition(0);
|
|
CompareStates(importBuffer, exportBuffer, snapshotStream);
|
|
|
|
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);
|
|
}
|