1
0
mirror of https://github.com/OpenRCT2/OpenRCT2 synced 2026-01-28 01:04:50 +01:00
Files
OpenRCT2/src/openrct2/command_line/ConvertCommand.cpp
2025-09-17 23:18:52 +02:00

193 lines
6.6 KiB
C++

/*****************************************************************************
* Copyright (c) 2014-2025 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 "../Context.h"
#include "../FileClassifier.h"
#include "../GameState.h"
#include "../OpenRCT2.h"
#include "../ParkImporter.h"
#include "../PlatformEnvironment.h"
#include "../config/Config.h"
#include "../core/Console.hpp"
#include "../core/Path.hpp"
#include "../core/String.hpp"
#include "../object/ObjectManager.h"
#include "../park/ParkFile.h"
#include "../scenario/Scenario.h"
#include "../ui/WindowManager.h"
#include "CommandLine.hpp"
#include <cassert>
#include <limits>
#include <memory>
namespace OpenRCT2
{
static int32_t _compressLevel = kParkFileSaveCompressionLevel;
// clang-format off
static constexpr CommandLineOptionDefinition kConvertOptions[]
{
{ CMDLINE_TYPE_INTEGER, &_compressLevel, 'l', "compress-level", "The compression level to use when writing the converted file" },
kOptionTableEnd
};
static exitcode_t HandleCommandConvert(CommandLineArgEnumerator* argEnumerator);
const CommandLineCommand CommandLine::kConvertCommands[]{
// Main commands
DefineCommand("", "<source> [destination]", kConvertOptions, HandleCommandConvert),
kCommandTableEnd
};
// clang-format on
static void WriteConvertFromAndToMessage(FileExtension sourceFileType, FileExtension destinationFileType);
static u8string GetFileTypeFriendlyName(FileExtension fileType);
static exitcode_t HandleCommandConvert(CommandLineArgEnumerator* enumerator)
{
exitcode_t result = CommandLine::HandleCommandDefault();
if (result != EXITCODE_CONTINUE)
{
return result;
}
// Get the source path
const utf8* rawSourcePath;
if (!enumerator->TryPopString(&rawSourcePath))
{
Console::Error::WriteLine("Expected a source path.");
return EXITCODE_FAIL;
}
const auto sourcePath = Path::GetAbsolute(rawSourcePath);
auto sourceFileType = GetFileExtensionType(sourcePath.c_str());
// Get the destination path
const utf8* rawDestinationPath;
if (!enumerator->TryPopString(&rawDestinationPath) || String::startsWith(rawDestinationPath, "-"))
{
// if no destination path is provided, convert the park file in-place
rawDestinationPath = rawSourcePath;
}
const auto destinationPath = Path::GetAbsolute(rawDestinationPath);
auto destinationFileType = GetFileExtensionType(destinationPath.c_str());
// Validate target type
if (destinationFileType != FileExtension::PARK)
{
Console::Error::WriteLine("Only conversion to .PARK is supported.");
return EXITCODE_FAIL;
}
// Validate the source type
switch (sourceFileType)
{
case FileExtension::SC4:
case FileExtension::SV4:
case FileExtension::SC6:
case FileExtension::SV6:
break;
case FileExtension::PARK:
if (destinationFileType == FileExtension::PARK)
{
Console::Error::WriteLine(
"File is already an OpenRCT2 saved game or scenario. Updating file version and recompressing.");
}
break;
default:
Console::Error::WriteLine("Only conversion from .SC4, .SV4, .SC6, .SV6, or .PARK is supported.");
return EXITCODE_FAIL;
}
// Perform conversion
WriteConvertFromAndToMessage(sourceFileType, destinationFileType);
gOpenRCT2Headless = true;
auto context = OpenRCT2::CreateContext();
context->Initialise();
auto& objManager = context->GetObjectManager();
auto& gameState = getGameState();
try
{
auto importer = ParkImporter::Create(sourcePath);
auto loadResult = importer->Load(sourcePath.c_str(), false);
objManager.LoadObjects(loadResult.RequiredObjects);
// TODO: Have a separate GameState and exchange once loaded.
importer->Import(gameState);
}
catch (const std::exception& ex)
{
Console::Error::WriteLine(ex.what());
return EXITCODE_FAIL;
}
if (sourceFileType == FileExtension::SC4 || sourceFileType == FileExtension::SC6)
{
// We are converting a scenario, so reset the park
ScenarioBegin(gameState);
}
try
{
auto exporter = std::make_unique<ParkFileExporter>();
// HACK remove the main window so it saves the park with the
// correct initial view
auto* windowMgr = Ui::GetWindowManager();
windowMgr->CloseByClass(WindowClass::mainWindow);
exporter->Export(gameState, destinationPath, static_cast<int16_t>(_compressLevel));
}
catch (const std::exception& ex)
{
Console::Error::WriteLine(ex.what());
return EXITCODE_FAIL;
}
Console::WriteLine("Conversion successful!");
return EXITCODE_OK;
}
static void WriteConvertFromAndToMessage(FileExtension sourceFileType, FileExtension destinationFileType)
{
const auto sourceFileTypeName = GetFileTypeFriendlyName(sourceFileType);
const auto destinationFileTypeName = GetFileTypeFriendlyName(destinationFileType);
Console::WriteFormat("Converting from a %s to a %s.", sourceFileTypeName.c_str(), destinationFileTypeName.c_str());
Console::WriteLine();
}
static u8string GetFileTypeFriendlyName(FileExtension fileType)
{
switch (fileType)
{
case FileExtension::SC4:
return "RollerCoaster Tycoon 1 scenario";
case FileExtension::SV4:
return "RollerCoaster Tycoon 1 saved game";
case FileExtension::SC6:
return "RollerCoaster Tycoon 2 scenario";
case FileExtension::SV6:
return "RollerCoaster Tycoon 2 saved game";
case FileExtension::PARK:
return "OpenRCT2 park";
default:
break;
}
assert(false);
return nullptr;
}
} // namespace OpenRCT2