diff --git a/src/openrct2/CommandLineSprite.cpp b/src/openrct2/CommandLineSprite.cpp index d6e8965663..9b40b83229 100644 --- a/src/openrct2/CommandLineSprite.cpp +++ b/src/openrct2/CommandLineSprite.cpp @@ -31,249 +31,264 @@ #include #include -using namespace OpenRCT2; -using namespace OpenRCT2::Drawing; - -static int32_t CommandLineForSpriteCombine(const char** argv, int32_t argc); - -class SpriteFile +namespace OpenRCT2 { -public: - RCTG1Header Header{}; - std::vector Entries; - std::vector Data; - void AddImage(ImageImporter::ImportResult& image); - bool Save(const utf8* path); - static std::optional Open(const utf8* path); + using namespace OpenRCT2::Drawing; -private: - class ScopedRelativeSpriteFile + static int32_t CommandLineForSpriteCombine(const char** argv, int32_t argc); + + class SpriteFile { - private: - SpriteFile& _SpriteFile; - bool _WasAbsolute; - public: - ScopedRelativeSpriteFile(SpriteFile& sFile) - : _SpriteFile(sFile) - , _WasAbsolute(sFile.isAbsolute) + RCTG1Header Header{}; + std::vector Entries; + std::vector Data; + void AddImage(ImageImporter::ImportResult& image); + bool Save(const utf8* path); + static std::optional Open(const utf8* path); + + private: + class ScopedRelativeSpriteFile { - if (_WasAbsolute) - _SpriteFile.MakeEntriesRelative(); - } + private: + SpriteFile& _SpriteFile; + bool _WasAbsolute; - ~ScopedRelativeSpriteFile() - { - if (_WasAbsolute) - _SpriteFile.MakeEntriesAbsolute(); - } - }; - bool isAbsolute = false; - void MakeEntriesAbsolute(); - void MakeEntriesRelative(); -}; - -void SpriteFile::MakeEntriesAbsolute() -{ - if (!isAbsolute) - { - for (auto& entry : Entries) - entry.offset += reinterpret_cast(Data.data()); - } - isAbsolute = true; -} - -void SpriteFile::MakeEntriesRelative() -{ - if (isAbsolute) - { - for (auto& entry : Entries) - entry.offset -= reinterpret_cast(Data.data()); - } - isAbsolute = false; -} - -void SpriteFile::AddImage(ImageImporter::ImportResult& image) -{ - Header.num_entries++; - // New image will have its data inserted after previous image - uint8_t* newElementOffset = reinterpret_cast(static_cast(Header.total_size)); - Header.total_size += static_cast(image.Buffer.size()); - Entries.reserve(Header.num_entries); - - { - ScopedRelativeSpriteFile scopedRelative(*this); - Data.reserve(Header.total_size); - Entries.push_back(image.Element); - Entries.back().offset = newElementOffset; - const auto& buffer = image.Buffer; - std::copy(buffer.begin(), buffer.end(), std::back_inserter(Data)); - } -} - -std::optional SpriteFile::Open(const utf8* path) -{ - try - { - OpenRCT2::FileStream stream(path, OpenRCT2::FileMode::open); - - SpriteFile spriteFile; - stream.Read(&spriteFile.Header, sizeof(RCTG1Header)); - - if (spriteFile.Header.num_entries > 0) - { - spriteFile.Entries.reserve(spriteFile.Header.num_entries); - - for (uint32_t i = 0; i < spriteFile.Header.num_entries; ++i) + public: + ScopedRelativeSpriteFile(SpriteFile& sFile) + : _SpriteFile(sFile) + , _WasAbsolute(sFile.isAbsolute) { - RCTG1Element entry32bit{}; - stream.Read(&entry32bit, sizeof(entry32bit)); - G1Element entry{}; - - entry.offset = reinterpret_cast(static_cast(entry32bit.offset)); - entry.width = entry32bit.width; - entry.height = entry32bit.height; - entry.x_offset = entry32bit.x_offset; - entry.y_offset = entry32bit.y_offset; - entry.flags = entry32bit.flags; - entry.zoomed_offset = entry32bit.zoomed_offset; - spriteFile.Entries.push_back(std::move(entry)); + if (_WasAbsolute) + _SpriteFile.MakeEntriesRelative(); } - spriteFile.Data.resize(spriteFile.Header.total_size); - stream.Read(spriteFile.Data.data(), spriteFile.Header.total_size); + + ~ScopedRelativeSpriteFile() + { + if (_WasAbsolute) + _SpriteFile.MakeEntriesAbsolute(); + } + }; + bool isAbsolute = false; + void MakeEntriesAbsolute(); + void MakeEntriesRelative(); + }; + + void SpriteFile::MakeEntriesAbsolute() + { + if (!isAbsolute) + { + for (auto& entry : Entries) + entry.offset += reinterpret_cast(Data.data()); } - spriteFile.MakeEntriesAbsolute(); - return spriteFile; + isAbsolute = true; } - catch (IOException&) + + void SpriteFile::MakeEntriesRelative() { - return std::nullopt; + if (isAbsolute) + { + for (auto& entry : Entries) + entry.offset -= reinterpret_cast(Data.data()); + } + isAbsolute = false; } -} -bool SpriteFile::Save(const utf8* path) -{ - try + void SpriteFile::AddImage(ImageImporter::ImportResult& image) { - OpenRCT2::FileStream stream(path, OpenRCT2::FileMode::write); - stream.Write(&Header, sizeof(RCTG1Header)); + Header.num_entries++; + // New image will have its data inserted after previous image + uint8_t* newElementOffset = reinterpret_cast(static_cast(Header.total_size)); + Header.total_size += static_cast(image.Buffer.size()); + Entries.reserve(Header.num_entries); - if (Header.num_entries > 0) { ScopedRelativeSpriteFile scopedRelative(*this); + Data.reserve(Header.total_size); + Entries.push_back(image.Element); + Entries.back().offset = newElementOffset; + const auto& buffer = image.Buffer; + std::copy(buffer.begin(), buffer.end(), std::back_inserter(Data)); + } + } - for (const auto& entry : Entries) + std::optional SpriteFile::Open(const utf8* path) + { + try + { + FileStream stream(path, FileMode::open); + + SpriteFile spriteFile; + stream.Read(&spriteFile.Header, sizeof(RCTG1Header)); + + if (spriteFile.Header.num_entries > 0) { - RCTG1Element entry32bit{}; + spriteFile.Entries.reserve(spriteFile.Header.num_entries); - entry32bit.offset = static_cast(reinterpret_cast(const_cast(entry.offset))); - entry32bit.width = entry.width; - entry32bit.height = entry.height; - entry32bit.x_offset = entry.x_offset; - entry32bit.y_offset = entry.y_offset; - entry32bit.flags = entry.flags; - entry32bit.zoomed_offset = entry.zoomed_offset; + for (uint32_t i = 0; i < spriteFile.Header.num_entries; ++i) + { + RCTG1Element entry32bit{}; + stream.Read(&entry32bit, sizeof(entry32bit)); + G1Element entry{}; - stream.Write(&entry32bit, sizeof(entry32bit)); + entry.offset = reinterpret_cast(static_cast(entry32bit.offset)); + entry.width = entry32bit.width; + entry.height = entry32bit.height; + entry.x_offset = entry32bit.x_offset; + entry.y_offset = entry32bit.y_offset; + entry.flags = entry32bit.flags; + entry.zoomed_offset = entry32bit.zoomed_offset; + spriteFile.Entries.push_back(std::move(entry)); + } + spriteFile.Data.resize(spriteFile.Header.total_size); + stream.Read(spriteFile.Data.data(), spriteFile.Header.total_size); } - stream.Write(Data.data(), Header.total_size); + spriteFile.MakeEntriesAbsolute(); + return spriteFile; } - return true; - } - catch (IOException&) - { - return false; - } -} - -static bool SpriteImageExport(const G1Element& spriteElement, u8string_view outPath) -{ - const size_t pixelBufferSize = static_cast(spriteElement.width) * spriteElement.height; - auto pixelBuffer = std::make_unique(pixelBufferSize); - auto pixels = pixelBuffer.get(); - - RenderTarget rt; - rt.bits = pixels; - rt.x = 0; - rt.y = 0; - rt.width = spriteElement.width; - rt.height = spriteElement.height; - rt.pitch = 0; - rt.zoom_level = ZoomLevel{ 0 }; - - DrawSpriteArgs args( - ImageId(), PaletteMap::GetDefault(), spriteElement, 0, 0, spriteElement.width, spriteElement.height, pixels); - GfxSpriteToBuffer(rt, args); - - auto const pixels8 = rt.bits; - auto const pixelsLen = rt.LineStride() * rt.WorldHeight(); - try - { - Image image; - image.Width = rt.width; - image.Height = rt.height; - image.Depth = 8; - image.Stride = rt.LineStride(); - image.Palette = StandardPalette; - image.Pixels = std::vector(pixels8, pixels8 + pixelsLen); - Imaging::WriteToFile(outPath, image, ImageFormat::png); - return true; - } - catch (const std::exception& e) - { - fprintf(stderr, "Unable to write png: %s", e.what()); - return false; - } -} - -static std::optional SpriteImageImport(u8string_view path, ImageImportMeta meta) -{ - try - { - auto format = ImageFormat::png32; - if (meta.palette == Palette::KeepIndices) + catch (IOException&) { - format = ImageFormat::png; + return std::nullopt; } - - ImageImporter importer; - auto image = Imaging::ReadFromFile(path, format); - - return importer.Import(image, meta); } - catch (const std::exception& e) + + bool SpriteFile::Save(const utf8* path) { - fprintf(stderr, "%s\n", e.what()); - return std::nullopt; - } -} - -// TODO: Remove when C++20 is enabled and std::format can be used -static std::string PopStr(std::ostringstream& oss) -{ - auto str = oss.str(); - oss.str(""); - oss.clear(); - return str; -} - -int32_t CommandLineForSprite(const char** argv, int32_t argc) -{ - gOpenRCT2Headless = true; - if (argc == 0) - return -1; - - if (String::iequals(argv[0], "details")) - { - if (argc < 2) + try { - fprintf(stdout, "usage: sprite details [idx]\n"); + FileStream stream(path, FileMode::write); + stream.Write(&Header, sizeof(RCTG1Header)); + + if (Header.num_entries > 0) + { + ScopedRelativeSpriteFile scopedRelative(*this); + + for (const auto& entry : Entries) + { + RCTG1Element entry32bit{}; + + entry32bit.offset = static_cast(reinterpret_cast(const_cast(entry.offset))); + entry32bit.width = entry.width; + entry32bit.height = entry.height; + entry32bit.x_offset = entry.x_offset; + entry32bit.y_offset = entry.y_offset; + entry32bit.flags = entry.flags; + entry32bit.zoomed_offset = entry.zoomed_offset; + + stream.Write(&entry32bit, sizeof(entry32bit)); + } + stream.Write(Data.data(), Header.total_size); + } + return true; + } + catch (IOException&) + { + return false; + } + } + + static bool SpriteImageExport(const G1Element& spriteElement, u8string_view outPath) + { + const size_t pixelBufferSize = static_cast(spriteElement.width) * spriteElement.height; + auto pixelBuffer = std::make_unique(pixelBufferSize); + auto pixels = pixelBuffer.get(); + + RenderTarget rt; + rt.bits = pixels; + rt.x = 0; + rt.y = 0; + rt.width = spriteElement.width; + rt.height = spriteElement.height; + rt.pitch = 0; + rt.zoom_level = ZoomLevel{ 0 }; + + DrawSpriteArgs args( + ImageId(), PaletteMap::GetDefault(), spriteElement, 0, 0, spriteElement.width, spriteElement.height, pixels); + GfxSpriteToBuffer(rt, args); + + auto const pixels8 = rt.bits; + auto const pixelsLen = rt.LineStride() * rt.WorldHeight(); + try + { + Image image; + image.Width = rt.width; + image.Height = rt.height; + image.Depth = 8; + image.Stride = rt.LineStride(); + image.Palette = StandardPalette; + image.Pixels = std::vector(pixels8, pixels8 + pixelsLen); + Imaging::WriteToFile(outPath, image, ImageFormat::png); + return true; + } + catch (const std::exception& e) + { + fprintf(stderr, "Unable to write png: %s", e.what()); + return false; + } + } + + static std::optional SpriteImageImport(u8string_view path, ImageImportMeta meta) + { + try + { + auto format = ImageFormat::png32; + if (meta.palette == Palette::KeepIndices) + { + format = ImageFormat::png; + } + + ImageImporter importer; + auto image = Imaging::ReadFromFile(path, format); + + return importer.Import(image, meta); + } + catch (const std::exception& e) + { + fprintf(stderr, "%s\n", e.what()); + return std::nullopt; + } + } + + // TODO: Remove when C++20 is enabled and std::format can be used + static std::string PopStr(std::ostringstream& oss) + { + auto str = oss.str(); + oss.str(""); + oss.clear(); + return str; + } + + int32_t CommandLineForSprite(const char** argv, int32_t argc) + { + gOpenRCT2Headless = true; + if (argc == 0) return -1; - } - if (argc == 2) + if (String::iequals(argv[0], "details")) { + if (argc < 2) + { + fprintf(stdout, "usage: sprite details [idx]\n"); + return -1; + } + + if (argc == 2) + { + const utf8* spriteFilePath = argv[1]; + auto spriteFile = SpriteFile::Open(spriteFilePath); + if (!spriteFile.has_value()) + { + fprintf(stderr, "Unable to open input sprite file.\n"); + return -1; + } + + printf("sprites: %u\n", spriteFile->Header.num_entries); + printf("data size: %u\n", spriteFile->Header.total_size); + return 1; + } + const utf8* spriteFilePath = argv[1]; + int32_t spriteIndex = atoi(argv[2]); auto spriteFile = SpriteFile::Open(spriteFilePath); if (!spriteFile.has_value()) { @@ -281,391 +296,378 @@ int32_t CommandLineForSprite(const char** argv, int32_t argc) return -1; } - printf("sprites: %u\n", spriteFile->Header.num_entries); - printf("data size: %u\n", spriteFile->Header.total_size); + if (spriteIndex < 0 || spriteIndex >= static_cast(spriteFile->Header.num_entries)) + { + fprintf(stderr, "Sprite #%d does not exist in sprite file.\n", spriteIndex); + return -1; + } + + G1Element* g1 = &spriteFile->Entries[spriteIndex]; + printf("width: %d\n", g1->width); + printf("height: %d\n", g1->height); + printf("x offset: %d\n", g1->x_offset); + printf("y offset: %d\n", g1->y_offset); + printf("data offset: %p\n", g1->offset); return 1; } - const utf8* spriteFilePath = argv[1]; - int32_t spriteIndex = atoi(argv[2]); - auto spriteFile = SpriteFile::Open(spriteFilePath); - if (!spriteFile.has_value()) + if (String::iequals(argv[0], "export")) { - fprintf(stderr, "Unable to open input sprite file.\n"); - return -1; + if (argc < 4) + { + fprintf(stdout, "usage: sprite export \n"); + return -1; + } + + const utf8* spriteFilePath = argv[1]; + int32_t spriteIndex = atoi(argv[2]); + const utf8* outputPath = argv[3]; + auto spriteFile = SpriteFile::Open(spriteFilePath); + if (!spriteFile.has_value()) + { + fprintf(stderr, "Unable to open input sprite file.\n"); + return -1; + } + + if (spriteIndex < 0 || spriteIndex >= static_cast(spriteFile->Header.num_entries)) + { + fprintf(stderr, "Sprite #%d does not exist in sprite file.\n", spriteIndex); + return -1; + } + + const auto& spriteHeader = spriteFile->Entries[spriteIndex]; + if (!SpriteImageExport(spriteHeader, outputPath)) + { + fprintf(stderr, "Could not export\n"); + return -1; + } + fprintf(stdout, "{ \"x\": %d, \"y\": %d }\n", spriteHeader.x_offset, spriteHeader.y_offset); + return 1; } - if (spriteIndex < 0 || spriteIndex >= static_cast(spriteFile->Header.num_entries)) + if (String::iequals(argv[0], "exportall")) { - fprintf(stderr, "Sprite #%d does not exist in sprite file.\n", spriteIndex); - return -1; + if (argc < 3) + { + fprintf(stdout, "usage: sprite exportall \n"); + return -1; + } + + const utf8* spriteFilePath = argv[1]; + const utf8* outputPath = argv[2]; + + auto spriteFile = SpriteFile::Open(spriteFilePath); + if (!spriteFile.has_value()) + { + fprintf(stderr, "Unable to open input sprite file.\n"); + return -1; + } + + if (!Path::CreateDirectory(outputPath)) + { + fprintf(stderr, "Unable to create directory.\n"); + return -1; + } + + const uint32_t maxIndex = spriteFile->Header.num_entries; + const int32_t numbers = static_cast(std::floor(std::log10(maxIndex) + 1)); + + std::ostringstream oss; // TODO: Remove when C++20 is enabled and std::format can be used + for (uint32_t spriteIndex = 0; spriteIndex < maxIndex; spriteIndex++) + { + // Status indicator + printf("\r%u / %u, %u%%", spriteIndex + 1, maxIndex, ((spriteIndex + 1) * 100) / maxIndex); + + oss << std::setw(numbers) << std::setfill('0') << spriteIndex << ".png"; + + const auto& spriteHeader = spriteFile->Entries[spriteIndex]; + if (!SpriteImageExport(spriteHeader, Path::Combine(outputPath, PopStr(oss)))) + { + fprintf(stderr, "Could not export\n"); + return -1; + } + } + return 1; } - G1Element* g1 = &spriteFile->Entries[spriteIndex]; - printf("width: %d\n", g1->width); - printf("height: %d\n", g1->height); - printf("x offset: %d\n", g1->x_offset); - printf("y offset: %d\n", g1->y_offset); - printf("data offset: %p\n", g1->offset); + if (String::iequals(argv[0], "exportalldat")) + { + if (argc < 3) + { + fprintf(stdout, "usage: sprite exportalldat \n"); + return -1; + } + + const char* datName = argv[1]; + const utf8* outputPath = argv[2]; + auto context = CreateContext(); + context->Initialise(); + + const ObjectRepositoryItem* ori = ObjectRepositoryFindObjectByName(datName); + if (ori == nullptr) + { + fprintf(stderr, "Could not find the object.\n"); + return -1; + } + + const RCTObjectEntry* entry = &ori->ObjectEntry; + const auto* loadedObject = ObjectManagerLoadObject(entry); + if (loadedObject == nullptr) + { + fprintf(stderr, "Unable to load object.\n"); + return -1; + } + auto entryIndex = ObjectManagerGetLoadedObjectEntryIndex(loadedObject); + ObjectType objectType = entry->GetType(); + + auto& objManager = context->GetObjectManager(); + const auto* const metaObject = objManager.GetLoadedObject(objectType, entryIndex); + + if (!Path::CreateDirectory(outputPath)) + { + fprintf(stderr, "Unable to create directory.\n"); + return -1; + } + + const uint32_t maxIndex = metaObject->GetNumImages(); + const int32_t numbers = static_cast(std::floor(std::log10(maxIndex) + 1)); + + std::ostringstream oss; // TODO: Remove when C++20 is enabled and std::format can be used + for (uint32_t spriteIndex = 0; spriteIndex < maxIndex; spriteIndex++) + { + oss << std::setw(numbers) << std::setfill('0') << spriteIndex << ".png"; + auto path = Path::Combine(outputPath, PopStr(oss)); + + const auto& g1 = metaObject->GetImageTable().GetImages()[spriteIndex]; + if (!SpriteImageExport(g1, path)) + { + fprintf(stderr, "Could not export\n"); + return -1; + } + + path = fs::u8path(path).generic_u8string(); + fprintf(stdout, "{ \"path\": \"%s\", \"x\": %d, \"y\": %d },\n", path.c_str(), g1.x_offset, g1.y_offset); + } + return 1; + } + + if (String::iequals(argv[0], "create")) + { + if (argc < 2) + { + fprintf(stderr, "usage: sprite create \n"); + return -1; + } + + const utf8* spriteFilePath = argv[1]; + + SpriteFile spriteFile; + spriteFile.Save(spriteFilePath); + return 1; + } + + if (String::iequals(argv[0], "append")) + { + if (argc != 3 && argc != 5) + { + fprintf(stderr, "usage: sprite append [ ]\n"); + return -1; + } + + const utf8* spriteFilePath = argv[1]; + const utf8* imagePath = argv[2]; + int16_t xOffset = 0; + int16_t yOffset = 0; + + if (argc == 5) + { + char* endptr; + + xOffset = strtol(argv[3], &endptr, 0); + if (*endptr != 0) + { + fprintf(stderr, "X offset must be an integer\n"); + return -1; + } + + yOffset = strtol(argv[4], &endptr, 0); + if (*endptr != 0) + { + fprintf(stderr, "Y offset must be an integer\n"); + return -1; + } + } + + constexpr uint8_t importFlags = EnumToFlag(ImportFlags::RLE); + ImageImportMeta meta = { { xOffset, yOffset }, Palette::OpenRCT2, importFlags, gSpriteMode }; + auto importResult = SpriteImageImport(imagePath, meta); + if (!importResult.has_value()) + return -1; + + auto spriteFile = SpriteFile::Open(spriteFilePath); + if (!spriteFile.has_value()) + { + fprintf(stderr, "Unable to open input sprite file.\n"); + return -1; + } + + spriteFile->AddImage(importResult.value()); + + if (!spriteFile->Save(spriteFilePath)) + return -1; + + return 1; + } + + if (String::iequals(argv[0], "build")) + { + if (argc < 3) + { + fprintf(stdout, "usage: sprite build [silent]\n"); + return -1; + } + + const utf8* spriteFilePath = argv[1]; + const utf8* spriteDescriptionPath = argv[2]; + const auto directoryPath = Path::GetDirectory(spriteDescriptionPath); + + json_t jsonSprites = Json::ReadFromFile(spriteDescriptionPath); + if (jsonSprites.is_null()) + { + fprintf(stderr, "Unable to read sprite description file: %s\n", spriteDescriptionPath); + return -1; + } + + if (jsonSprites.is_object() && !jsonSprites["images"].is_null()) + jsonSprites = jsonSprites["images"]; + + if (!jsonSprites.is_array()) + { + fprintf(stderr, "Error: expected array\n"); + return -1; + } + + bool silent = (argc >= 4 && strcmp(argv[3], "silent") == 0); + + // keep sprite file entirely in memory until ready to write out a complete, + // correct file + SpriteFile spriteFile; + spriteFile.Header.num_entries = 0; + spriteFile.Header.total_size = 0; + + fprintf(stdout, "Building: %s\n", spriteFilePath); + + json_t sprite_description; + + uint32_t numSuccessful = 0; + + // Note: jsonSprite is deliberately left non-const: json_t behaviour changes when const + for (auto& [jsonKey, jsonSprite] : jsonSprites.items()) + { + if (!jsonSprite.is_object()) + { + fprintf(stderr, "Error: expected object for sprite %s\n", jsonKey.c_str()); + return -1; + } + + json_t path = jsonSprite["path"]; + if (!path.is_string()) + { + fprintf(stderr, "Error: no path provided for sprite %s\n", jsonKey.c_str()); + return -1; + } + std::string strPath = Json::GetString(path); + + auto meta = createImageImportMetaFromJson(jsonSprite); + meta.importMode = gSpriteMode; + + auto imagePath = Path::GetAbsolute(Path::Combine(directoryPath, strPath)); + + auto importResult = SpriteImageImport(imagePath, meta); + if (importResult == std::nullopt) + { + fprintf(stderr, "Could not import image file: %s\nCanceling\n", imagePath.c_str()); + return -1; + } + + spriteFile.AddImage(importResult.value()); + + if (!silent) + fprintf(stdout, "Added: %s\n", imagePath.c_str()); + + numSuccessful++; + } + + if (!spriteFile.Save(spriteFilePath)) + { + LOG_ERROR("Could not save sprite file, cancelling."); + return -1; + } + + fprintf(stdout, "Finished building graphics repository with %u images\n", numSuccessful); + + if (numSuccessful > 0) + { + printf("Replace the object's images table entries with this:\n"); + const auto spriteFileName = Path::GetFileName(spriteFilePath); + fprintf(stdout, "\"$LGX:%s[0..%u]\"\n", spriteFileName.c_str(), numSuccessful - 1); + } + + return 1; + } + + if (String::iequals(argv[0], "combine")) + { + return CommandLineForSpriteCombine(argv, argc); + } + + fprintf(stderr, "Unknown sprite command.\n"); return 1; } - if (String::iequals(argv[0], "export")) + static int32_t CommandLineForSpriteCombine(const char** argv, int32_t argc) { if (argc < 4) { - fprintf(stdout, "usage: sprite export \n"); + fprintf(stdout, "usage: sprite combine \n"); return -1; } - const utf8* spriteFilePath = argv[1]; - int32_t spriteIndex = atoi(argv[2]); + const utf8* indexFile = argv[1]; + const utf8* dataFile = argv[2]; const utf8* outputPath = argv[3]; - auto spriteFile = SpriteFile::Open(spriteFilePath); - if (!spriteFile.has_value()) + + auto fileHeader = FileStream(indexFile, FileMode::open); + auto fileData = FileStream(dataFile, FileMode::open); + auto fileHeaderSize = fileHeader.GetLength(); + auto fileDataSize = fileData.GetLength(); + + uint32_t numEntries = fileHeaderSize / sizeof(RCTG1Element); + + RCTG1Header header = {}; + header.num_entries = numEntries; + header.total_size = fileDataSize; + FileStream outputStream(outputPath, FileMode::write); + + outputStream.Write(&header, sizeof(RCTG1Header)); + auto g1Elements32 = std::make_unique(numEntries); + fileHeader.Read(g1Elements32.get(), numEntries * sizeof(RCTG1Element)); + for (uint32_t i = 0; i < numEntries; i++) { - fprintf(stderr, "Unable to open input sprite file.\n"); - return -1; - } - - if (spriteIndex < 0 || spriteIndex >= static_cast(spriteFile->Header.num_entries)) - { - fprintf(stderr, "Sprite #%d does not exist in sprite file.\n", spriteIndex); - return -1; - } - - const auto& spriteHeader = spriteFile->Entries[spriteIndex]; - if (!SpriteImageExport(spriteHeader, outputPath)) - { - fprintf(stderr, "Could not export\n"); - return -1; - } - fprintf(stdout, "{ \"x\": %d, \"y\": %d }\n", spriteHeader.x_offset, spriteHeader.y_offset); - return 1; - } - - if (String::iequals(argv[0], "exportall")) - { - if (argc < 3) - { - fprintf(stdout, "usage: sprite exportall \n"); - return -1; - } - - const utf8* spriteFilePath = argv[1]; - const utf8* outputPath = argv[2]; - - auto spriteFile = SpriteFile::Open(spriteFilePath); - if (!spriteFile.has_value()) - { - fprintf(stderr, "Unable to open input sprite file.\n"); - return -1; - } - - if (!Path::CreateDirectory(outputPath)) - { - fprintf(stderr, "Unable to create directory.\n"); - return -1; - } - - const uint32_t maxIndex = spriteFile->Header.num_entries; - const int32_t numbers = static_cast(std::floor(std::log10(maxIndex) + 1)); - - std::ostringstream oss; // TODO: Remove when C++20 is enabled and std::format can be used - for (uint32_t spriteIndex = 0; spriteIndex < maxIndex; spriteIndex++) - { - // Status indicator - printf("\r%u / %u, %u%%", spriteIndex + 1, maxIndex, ((spriteIndex + 1) * 100) / maxIndex); - - oss << std::setw(numbers) << std::setfill('0') << spriteIndex << ".png"; - - const auto& spriteHeader = spriteFile->Entries[spriteIndex]; - if (!SpriteImageExport(spriteHeader, Path::Combine(outputPath, PopStr(oss)))) + // RCT1 used zoomed offsets that counted from the beginning of the file, rather than from the current sprite. + if (g1Elements32[i].flags & G1_FLAG_HAS_ZOOM_SPRITE) { - fprintf(stderr, "Could not export\n"); - return -1; - } - } - return 1; - } - - if (String::iequals(argv[0], "exportalldat")) - { - if (argc < 3) - { - fprintf(stdout, "usage: sprite exportalldat \n"); - return -1; - } - - const char* datName = argv[1]; - const utf8* outputPath = argv[2]; - auto context = OpenRCT2::CreateContext(); - context->Initialise(); - - const ObjectRepositoryItem* ori = ObjectRepositoryFindObjectByName(datName); - if (ori == nullptr) - { - fprintf(stderr, "Could not find the object.\n"); - return -1; - } - - const RCTObjectEntry* entry = &ori->ObjectEntry; - const auto* loadedObject = ObjectManagerLoadObject(entry); - if (loadedObject == nullptr) - { - fprintf(stderr, "Unable to load object.\n"); - return -1; - } - auto entryIndex = ObjectManagerGetLoadedObjectEntryIndex(loadedObject); - ObjectType objectType = entry->GetType(); - - auto& objManager = context->GetObjectManager(); - const auto* const metaObject = objManager.GetLoadedObject(objectType, entryIndex); - - if (!Path::CreateDirectory(outputPath)) - { - fprintf(stderr, "Unable to create directory.\n"); - return -1; - } - - const uint32_t maxIndex = metaObject->GetNumImages(); - const int32_t numbers = static_cast(std::floor(std::log10(maxIndex) + 1)); - - std::ostringstream oss; // TODO: Remove when C++20 is enabled and std::format can be used - for (uint32_t spriteIndex = 0; spriteIndex < maxIndex; spriteIndex++) - { - oss << std::setw(numbers) << std::setfill('0') << spriteIndex << ".png"; - auto path = Path::Combine(outputPath, PopStr(oss)); - - const auto& g1 = metaObject->GetImageTable().GetImages()[spriteIndex]; - if (!SpriteImageExport(g1, path)) - { - fprintf(stderr, "Could not export\n"); - return -1; + g1Elements32[i].zoomed_offset = i - g1Elements32[i].zoomed_offset; } - path = fs::u8path(path).generic_u8string(); - fprintf(stdout, "{ \"path\": \"%s\", \"x\": %d, \"y\": %d },\n", path.c_str(), g1.x_offset, g1.y_offset); - } - return 1; - } - - if (String::iequals(argv[0], "create")) - { - if (argc < 2) - { - fprintf(stderr, "usage: sprite create \n"); - return -1; + outputStream.Write(&g1Elements32[i], sizeof(RCTG1Element)); } - const utf8* spriteFilePath = argv[1]; - - SpriteFile spriteFile; - spriteFile.Save(spriteFilePath); - return 1; - } - - if (String::iequals(argv[0], "append")) - { - if (argc != 3 && argc != 5) - { - fprintf(stderr, "usage: sprite append [ ]\n"); - return -1; - } - - const utf8* spriteFilePath = argv[1]; - const utf8* imagePath = argv[2]; - int16_t xOffset = 0; - int16_t yOffset = 0; - - if (argc == 5) - { - char* endptr; - - xOffset = strtol(argv[3], &endptr, 0); - if (*endptr != 0) - { - fprintf(stderr, "X offset must be an integer\n"); - return -1; - } - - yOffset = strtol(argv[4], &endptr, 0); - if (*endptr != 0) - { - fprintf(stderr, "Y offset must be an integer\n"); - return -1; - } - } - - constexpr uint8_t importFlags = EnumToFlag(ImportFlags::RLE); - ImageImportMeta meta = { { xOffset, yOffset }, Palette::OpenRCT2, importFlags, gSpriteMode }; - auto importResult = SpriteImageImport(imagePath, meta); - if (!importResult.has_value()) - return -1; - - auto spriteFile = SpriteFile::Open(spriteFilePath); - if (!spriteFile.has_value()) - { - fprintf(stderr, "Unable to open input sprite file.\n"); - return -1; - } - - spriteFile->AddImage(importResult.value()); - - if (!spriteFile->Save(spriteFilePath)) - return -1; + std::vector data; + data.resize(fileDataSize); + fileData.Read(data.data(), fileDataSize); + outputStream.Write(data.data(), fileDataSize); return 1; } - - if (String::iequals(argv[0], "build")) - { - if (argc < 3) - { - fprintf(stdout, "usage: sprite build [silent]\n"); - return -1; - } - - const utf8* spriteFilePath = argv[1]; - const utf8* spriteDescriptionPath = argv[2]; - const auto directoryPath = Path::GetDirectory(spriteDescriptionPath); - - json_t jsonSprites = Json::ReadFromFile(spriteDescriptionPath); - if (jsonSprites.is_null()) - { - fprintf(stderr, "Unable to read sprite description file: %s\n", spriteDescriptionPath); - return -1; - } - - if (jsonSprites.is_object() && !jsonSprites["images"].is_null()) - jsonSprites = jsonSprites["images"]; - - if (!jsonSprites.is_array()) - { - fprintf(stderr, "Error: expected array\n"); - return -1; - } - - bool silent = (argc >= 4 && strcmp(argv[3], "silent") == 0); - - // keep sprite file entirely in memory until ready to write out a complete, - // correct file - SpriteFile spriteFile; - spriteFile.Header.num_entries = 0; - spriteFile.Header.total_size = 0; - - fprintf(stdout, "Building: %s\n", spriteFilePath); - - json_t sprite_description; - - uint32_t numSuccessful = 0; - - // Note: jsonSprite is deliberately left non-const: json_t behaviour changes when const - for (auto& [jsonKey, jsonSprite] : jsonSprites.items()) - { - if (!jsonSprite.is_object()) - { - fprintf(stderr, "Error: expected object for sprite %s\n", jsonKey.c_str()); - return -1; - } - - json_t path = jsonSprite["path"]; - if (!path.is_string()) - { - fprintf(stderr, "Error: no path provided for sprite %s\n", jsonKey.c_str()); - return -1; - } - std::string strPath = Json::GetString(path); - - auto meta = createImageImportMetaFromJson(jsonSprite); - meta.importMode = gSpriteMode; - - auto imagePath = Path::GetAbsolute(Path::Combine(directoryPath, strPath)); - - auto importResult = SpriteImageImport(imagePath, meta); - if (importResult == std::nullopt) - { - fprintf(stderr, "Could not import image file: %s\nCanceling\n", imagePath.c_str()); - return -1; - } - - spriteFile.AddImage(importResult.value()); - - if (!silent) - fprintf(stdout, "Added: %s\n", imagePath.c_str()); - - numSuccessful++; - } - - if (!spriteFile.Save(spriteFilePath)) - { - LOG_ERROR("Could not save sprite file, cancelling."); - return -1; - } - - fprintf(stdout, "Finished building graphics repository with %u images\n", numSuccessful); - - if (numSuccessful > 0) - { - printf("Replace the object's images table entries with this:\n"); - const auto spriteFileName = OpenRCT2::Path::GetFileName(spriteFilePath); - fprintf(stdout, "\"$LGX:%s[0..%u]\"\n", spriteFileName.c_str(), numSuccessful - 1); - } - - return 1; - } - - if (String::iequals(argv[0], "combine")) - { - return CommandLineForSpriteCombine(argv, argc); - } - - fprintf(stderr, "Unknown sprite command.\n"); - return 1; -} - -static int32_t CommandLineForSpriteCombine(const char** argv, int32_t argc) -{ - if (argc < 4) - { - fprintf(stdout, "usage: sprite combine \n"); - return -1; - } - - const utf8* indexFile = argv[1]; - const utf8* dataFile = argv[2]; - const utf8* outputPath = argv[3]; - - auto fileHeader = OpenRCT2::FileStream(indexFile, OpenRCT2::FileMode::open); - auto fileData = OpenRCT2::FileStream(dataFile, OpenRCT2::FileMode::open); - auto fileHeaderSize = fileHeader.GetLength(); - auto fileDataSize = fileData.GetLength(); - - uint32_t numEntries = fileHeaderSize / sizeof(RCTG1Element); - - RCTG1Header header = {}; - header.num_entries = numEntries; - header.total_size = fileDataSize; - OpenRCT2::FileStream outputStream(outputPath, OpenRCT2::FileMode::write); - - outputStream.Write(&header, sizeof(RCTG1Header)); - auto g1Elements32 = std::make_unique(numEntries); - fileHeader.Read(g1Elements32.get(), numEntries * sizeof(RCTG1Element)); - for (uint32_t i = 0; i < numEntries; i++) - { - // RCT1 used zoomed offsets that counted from the beginning of the file, rather than from the current sprite. - if (g1Elements32[i].flags & G1_FLAG_HAS_ZOOM_SPRITE) - { - g1Elements32[i].zoomed_offset = i - g1Elements32[i].zoomed_offset; - } - - outputStream.Write(&g1Elements32[i], sizeof(RCTG1Element)); - } - - std::vector data; - data.resize(fileDataSize); - fileData.Read(data.data(), fileDataSize); - outputStream.Write(data.data(), fileDataSize); - - return 1; -} +} // namespace OpenRCT2 diff --git a/src/openrct2/CommandLineSprite.h b/src/openrct2/CommandLineSprite.h index 73aff68bcf..76976abd73 100644 --- a/src/openrct2/CommandLineSprite.h +++ b/src/openrct2/CommandLineSprite.h @@ -11,5 +11,8 @@ #include "drawing/ImageImporter.h" -int32_t CommandLineForSprite(const char** argv, int32_t argc); -extern OpenRCT2::Drawing::ImportMode gSpriteMode; +namespace OpenRCT2 +{ + int32_t CommandLineForSprite(const char** argv, int32_t argc); + extern Drawing::ImportMode gSpriteMode; +} // namespace OpenRCT2 diff --git a/src/openrct2/OpenRCT2.h b/src/openrct2/OpenRCT2.h index 1ff3a8df52..b77281eb60 100644 --- a/src/openrct2/OpenRCT2.h +++ b/src/openrct2/OpenRCT2.h @@ -56,15 +56,21 @@ namespace OpenRCT2::Network enum class Mode : int32_t; } -extern OpenRCT2::Network::Mode gNetworkStart; -extern std::string gNetworkStartHost; -extern int32_t gNetworkStartPort; -extern std::string gNetworkStartAddress; +namespace OpenRCT2 +{ + extern OpenRCT2::Network::Mode gNetworkStart; + extern std::string gNetworkStartHost; + extern int32_t gNetworkStartPort; + extern std::string gNetworkStartAddress; +} // namespace OpenRCT2 #endif +namespace OpenRCT2 +{ + int32_t CommandLineRun(const char** argv, int32_t argc); +} // namespace OpenRCT2 + extern uint32_t gCurrentDrawCount; extern LegacyScene gLegacyScene; extern uint32_t gScreenAge; extern PromptMode gSavePromptMode; - -int32_t CommandLineRun(const char** argv, int32_t argc); diff --git a/src/openrct2/command_line/CommandLine.cpp b/src/openrct2/command_line/CommandLine.cpp index 74d2e5f920..c6aea16821 100644 --- a/src/openrct2/command_line/CommandLine.cpp +++ b/src/openrct2/command_line/CommandLine.cpp @@ -17,79 +17,80 @@ #include -using namespace OpenRCT2; - #pragma region CommandLineArgEnumerator -CommandLineArgEnumerator::CommandLineArgEnumerator(const char* const* arguments, int32_t count) +namespace OpenRCT2 { - _arguments = arguments; - _count = count; - _index = 0; -} - -void CommandLineArgEnumerator::Reset() -{ - _index = 0; -} - -bool CommandLineArgEnumerator::Backtrack() -{ - if (_index > 0) + CommandLineArgEnumerator::CommandLineArgEnumerator(const char* const* arguments, int32_t count) { - _index--; - return true; + _arguments = arguments; + _count = count; + _index = 0; } - return false; -} - -bool CommandLineArgEnumerator::TryPop() -{ - if (_index < _count) + void CommandLineArgEnumerator::Reset() { - _index++; - return true; + _index = 0; } - return false; -} - -bool CommandLineArgEnumerator::TryPopInteger(int32_t* result) -{ - char const* arg; - if (TryPopString(&arg)) + bool CommandLineArgEnumerator::Backtrack() { - *result = static_cast(atol(arg)); - return true; + if (_index > 0) + { + _index--; + return true; + } + + return false; } - return false; -} - -bool CommandLineArgEnumerator::TryPopReal(float* result) -{ - char const* arg; - if (TryPopString(&arg)) + bool CommandLineArgEnumerator::TryPop() { - *result = static_cast(atof(arg)); - return true; + if (_index < _count) + { + _index++; + return true; + } + + return false; } - return false; -} - -bool CommandLineArgEnumerator::TryPopString(const char** result) -{ - if (_index < _count) + bool CommandLineArgEnumerator::TryPopInteger(int32_t* result) { - *result = _arguments[_index]; - _index++; - return true; + char const* arg; + if (TryPopString(&arg)) + { + *result = static_cast(atol(arg)); + return true; + } + + return false; } - return false; -} + bool CommandLineArgEnumerator::TryPopReal(float* result) + { + char const* arg; + if (TryPopString(&arg)) + { + *result = static_cast(atof(arg)); + return true; + } + + return false; + } + + bool CommandLineArgEnumerator::TryPopString(const char** result) + { + if (_index < _count) + { + *result = _arguments[_index]; + _index++; + return true; + } + + return false; + } +} // namespace OpenRCT2 #pragma endregion @@ -526,33 +527,36 @@ namespace OpenRCT2::CommandLine } } // namespace OpenRCT2::CommandLine -int32_t CommandLineRun(const char** argv, int32_t argc) +namespace OpenRCT2 { - auto argEnumerator = CommandLineArgEnumerator(argv, argc); - - // Pop process path - argEnumerator.TryPop(); - - const CommandLineCommand* command = CommandLine::FindCommandFor(CommandLine::kRootCommands, &argEnumerator); - - if (command == nullptr) + int32_t CommandLineRun(const char** argv, int32_t argc) { - return EXITCODE_FAIL; - } + auto argEnumerator = CommandLineArgEnumerator(argv, argc); - if (command->Options != nullptr) - { - auto argEnumeratorForOptions = CommandLineArgEnumerator(argEnumerator); - if (!CommandLine::ParseOptions(command->Options, &argEnumeratorForOptions)) + // Pop process path + argEnumerator.TryPop(); + + const CommandLineCommand* command = CommandLine::FindCommandFor(CommandLine::kRootCommands, &argEnumerator); + + if (command == nullptr) { return EXITCODE_FAIL; } - } - if (command == CommandLine::kRootCommands && command->Func == nullptr) - { - return CommandLine::HandleCommandDefault(); - } + if (command->Options != nullptr) + { + auto argEnumeratorForOptions = CommandLineArgEnumerator(argEnumerator); + if (!CommandLine::ParseOptions(command->Options, &argEnumeratorForOptions)) + { + return EXITCODE_FAIL; + } + } - return command->Func(&argEnumerator); -} + if (command == CommandLine::kRootCommands && command->Func == nullptr) + { + return CommandLine::HandleCommandDefault(); + } + + return command->Func(&argEnumerator); + } +} // namespace OpenRCT2 diff --git a/src/openrct2/command_line/CommandLine.hpp b/src/openrct2/command_line/CommandLine.hpp index 03400271e0..4557bcd0a0 100644 --- a/src/openrct2/command_line/CommandLine.hpp +++ b/src/openrct2/command_line/CommandLine.hpp @@ -11,113 +11,116 @@ #include -/** - * Class for enumerating and retrieving values for a set of command line arguments. - */ -class CommandLineArgEnumerator final +namespace OpenRCT2 { -private: - const char* const* _arguments; - uint16_t _count; - uint16_t _index; + /** + * Class for enumerating and retrieving values for a set of command line arguments. + */ + class CommandLineArgEnumerator final + { + private: + const char* const* _arguments; + uint16_t _count; + uint16_t _index; -public: - const char* const* GetArguments() const + public: + const char* const* GetArguments() const + { + return _arguments; + } + uint16_t GetCount() const + { + return _count; + } + uint16_t GetIndex() const + { + return _index; + } + + CommandLineArgEnumerator(const char* const* arguments, int32_t count); + + void Reset(); + bool Backtrack(); + bool TryPop(); + bool TryPopInteger(int32_t* result); + bool TryPopReal(float* result); + bool TryPopString(const char** result); + }; + + using exitcode_t = int32_t; + using CommandLineFunc = exitcode_t (*)(CommandLineArgEnumerator*); + + enum { - return _arguments; - } - uint16_t GetCount() const + EXITCODE_FAIL = -1, + EXITCODE_OK = 0, + EXITCODE_CONTINUE = 1, + }; + + struct CommandLineExample { - return _count; - } - uint16_t GetIndex() const + const char* Arguments; + const char* Description; + }; + + struct CommandLineOptionDefinition { - return _index; + uint8_t Type; + void* OutAddress; + char ShortName; + const char* LongName; + const char* Description; + }; + + struct CommandLineCommand + { + const char* Name; + const char* Parameters; + const CommandLineOptionDefinition* Options; + const CommandLineCommand* SubCommands; + CommandLineFunc Func; + }; + + enum + { + CMDLINE_TYPE_SWITCH, + CMDLINE_TYPE_INTEGER, + CMDLINE_TYPE_REAL, + CMDLINE_TYPE_STRING, + }; + + constexpr char kNAC = '\0'; + + constexpr CommandLineExample kExampleTableEnd = CommandLineExample{ nullptr, nullptr }; + constexpr CommandLineOptionDefinition kOptionTableEnd = CommandLineOptionDefinition{ UINT8_MAX, nullptr, kNAC, nullptr, + nullptr }; + constexpr CommandLineCommand kCommandTableEnd = CommandLineCommand{ nullptr, nullptr, nullptr, nullptr, nullptr }; + + consteval CommandLineCommand DefineCommand( + const char* name, const char* params, const CommandLineOptionDefinition* options, const CommandLineFunc func) + { + return CommandLineCommand{ name, params, options, nullptr, func }; } - CommandLineArgEnumerator(const char* const* arguments, int32_t count); + consteval CommandLineCommand DefineSubCommand(const char* name, const CommandLineCommand* subcommandtable) + { + return CommandLineCommand{ name, "", nullptr, subcommandtable, nullptr }; + } - void Reset(); - bool Backtrack(); - bool TryPop(); - bool TryPopInteger(int32_t* result); - bool TryPopReal(float* result); - bool TryPopString(const char** result); -}; + namespace CommandLine + { + extern const CommandLineCommand kRootCommands[]; + extern const CommandLineCommand kConvertCommands[]; + extern const CommandLineCommand kScreenshotCommands[]; + extern const CommandLineCommand kSpriteCommands[]; + extern const CommandLineCommand kSimulateCommands[]; + extern const CommandLineCommand kParkInfoCommands[]; -using exitcode_t = int32_t; -using CommandLineFunc = exitcode_t (*)(CommandLineArgEnumerator*); + extern const CommandLineExample kRootExamples[]; -enum -{ - EXITCODE_FAIL = -1, - EXITCODE_OK = 0, - EXITCODE_CONTINUE = 1, -}; + void PrintHelp(bool allCommands = false); + exitcode_t HandleCommandDefault(); -struct CommandLineExample -{ - const char* Arguments; - const char* Description; -}; - -struct CommandLineOptionDefinition -{ - uint8_t Type; - void* OutAddress; - char ShortName; - const char* LongName; - const char* Description; -}; - -struct CommandLineCommand -{ - const char* Name; - const char* Parameters; - const CommandLineOptionDefinition* Options; - const CommandLineCommand* SubCommands; - CommandLineFunc Func; -}; - -enum -{ - CMDLINE_TYPE_SWITCH, - CMDLINE_TYPE_INTEGER, - CMDLINE_TYPE_REAL, - CMDLINE_TYPE_STRING, -}; - -constexpr char kNAC = '\0'; - -constexpr CommandLineExample kExampleTableEnd = CommandLineExample{ nullptr, nullptr }; -constexpr CommandLineOptionDefinition kOptionTableEnd = CommandLineOptionDefinition{ UINT8_MAX, nullptr, kNAC, nullptr, - nullptr }; -constexpr CommandLineCommand kCommandTableEnd = CommandLineCommand{ nullptr, nullptr, nullptr, nullptr, nullptr }; - -consteval CommandLineCommand DefineCommand( - const char* name, const char* params, const CommandLineOptionDefinition* options, const CommandLineFunc func) -{ - return CommandLineCommand{ name, params, options, nullptr, func }; -} - -consteval CommandLineCommand DefineSubCommand(const char* name, const CommandLineCommand* subcommandtable) -{ - return CommandLineCommand{ name, "", nullptr, subcommandtable, nullptr }; -} - -namespace OpenRCT2::CommandLine -{ - extern const CommandLineCommand kRootCommands[]; - extern const CommandLineCommand kConvertCommands[]; - extern const CommandLineCommand kScreenshotCommands[]; - extern const CommandLineCommand kSpriteCommands[]; - extern const CommandLineCommand kSimulateCommands[]; - extern const CommandLineCommand kParkInfoCommands[]; - - extern const CommandLineExample kRootExamples[]; - - void PrintHelp(bool allCommands = false); - exitcode_t HandleCommandDefault(); - - exitcode_t HandleCommandUri(CommandLineArgEnumerator* enumerator); -} // namespace OpenRCT2::CommandLine + exitcode_t HandleCommandUri(CommandLineArgEnumerator* enumerator); + } // namespace CommandLine +} // namespace OpenRCT2 diff --git a/src/openrct2/command_line/ConvertCommand.cpp b/src/openrct2/command_line/ConvertCommand.cpp index 3decdf7860..9232610d26 100644 --- a/src/openrct2/command_line/ConvertCommand.cpp +++ b/src/openrct2/command_line/ConvertCommand.cpp @@ -27,165 +27,166 @@ #include #include -using namespace OpenRCT2; - -static int32_t _compressLevel = kParkFileSaveCompressionLevel; - -// clang-format off -static constexpr CommandLineOptionDefinition kConvertOptions[] +namespace OpenRCT2 { - { CMDLINE_TYPE_INTEGER, &_compressLevel, 'l', "compress-level", "The compression level to use when writing the converted file" }, - kOptionTableEnd -}; + static int32_t _compressLevel = kParkFileSaveCompressionLevel; -static exitcode_t HandleCommandConvert(CommandLineArgEnumerator* argEnumerator); - -const CommandLineCommand CommandLine::kConvertCommands[]{ - // Main commands - DefineCommand("", " [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) + // clang-format off + static constexpr CommandLineOptionDefinition kConvertOptions[] { - return result; - } + { CMDLINE_TYPE_INTEGER, &_compressLevel, 'l', "compress-level", "The compression level to use when writing the converted file" }, + kOptionTableEnd + }; - // Get the source path - const utf8* rawSourcePath; - if (!enumerator->TryPopString(&rawSourcePath)) + static exitcode_t HandleCommandConvert(CommandLineArgEnumerator* argEnumerator); + + const CommandLineCommand CommandLine::kConvertCommands[]{ + // Main commands + DefineCommand("", " [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) { - Console::Error::WriteLine("Expected a source path."); - return EXITCODE_FAIL; - } + exitcode_t result = CommandLine::HandleCommandDefault(); + if (result != EXITCODE_CONTINUE) + { + return result; + } - 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."); + // 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(); + + // 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(_compressLevel)); + } + catch (const std::exception& ex) + { + Console::Error::WriteLine(ex.what()); + return EXITCODE_FAIL; + } + + Console::WriteLine("Conversion successful!"); + return EXITCODE_OK; } - // Perform conversion - WriteConvertFromAndToMessage(sourceFileType, destinationFileType); - - gOpenRCT2Headless = true; - auto context = OpenRCT2::CreateContext(); - context->Initialise(); - - auto& objManager = context->GetObjectManager(); - auto& gameState = getGameState(); - - try + static void WriteConvertFromAndToMessage(FileExtension sourceFileType, FileExtension destinationFileType) { - 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); + 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(); } - catch (const std::exception& ex) + + static u8string GetFileTypeFriendlyName(FileExtension fileType) { - Console::Error::WriteLine(ex.what()); - return EXITCODE_FAIL; + 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; } - - if (sourceFileType == FileExtension::SC4 || sourceFileType == FileExtension::SC6) - { - // We are converting a scenario, so reset the park - ScenarioBegin(gameState); - } - - try - { - auto exporter = std::make_unique(); - - // 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(_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 diff --git a/src/openrct2/command_line/ParkInfoCommands.cpp b/src/openrct2/command_line/ParkInfoCommands.cpp index 2b50a75d59..ecb96124ed 100644 --- a/src/openrct2/command_line/ParkInfoCommands.cpp +++ b/src/openrct2/command_line/ParkInfoCommands.cpp @@ -16,132 +16,134 @@ #include "../object/ObjectRepository.h" #include "CommandLine.hpp" -using namespace OpenRCT2; - -// clang-format off -static constexpr CommandLineOptionDefinition kNoOptions[] +namespace OpenRCT2 { - kOptionTableEnd -}; - -static exitcode_t HandleObjectsInfo(CommandLineArgEnumerator *argEnumerator); - -const CommandLineCommand CommandLine::kParkInfoCommands[]{ - // Main commands - DefineCommand("objects", "", kNoOptions, HandleObjectsInfo), - - kCommandTableEnd -}; -// clang-format on - -static exitcode_t HandleObjectsInfo(CommandLineArgEnumerator* argEnumerator) -{ - exitcode_t result = CommandLine::HandleCommandDefault(); - if (result != EXITCODE_CONTINUE) + // clang-format off + static constexpr CommandLineOptionDefinition kNoOptions[] { - return result; - } - - // Get the source path - const utf8* rawSourcePath; - if (!argEnumerator->TryPopString(&rawSourcePath)) - { - Console::Error::WriteLine("Expected a source save file path."); - return EXITCODE_FAIL; - } - - auto sourcePath = Path::GetAbsolute(rawSourcePath); - - auto context = OpenRCT2::CreateContext(); - context->Initialise(); - - auto stream = OpenRCT2::FileStream(sourcePath, OpenRCT2::FileMode::open); - ClassifiedFileInfo info; - if (!TryClassifyFile(&stream, &info)) - { - Console::Error::WriteLine("Unable to detect file type"); - return EXITCODE_FAIL; - } - - if (info.Type != FileType::park && info.Type != FileType::savedGame && info.Type != FileType::scenario) - { - Console::Error::WriteLine("Invalid file type."); - return EXITCODE_FAIL; - } - - auto& objectRepository = context->GetObjectRepository(); - std::unique_ptr parkImporter; - if (info.Type == FileType::park) - { - parkImporter = ParkImporter::CreateParkFile(objectRepository); - } - else if (info.Version <= kFileTypeS4Cutoff) - { - // Save is an S4 (RCT1 format) - parkImporter = ParkImporter::CreateS4(); - } - else - { - // Save is an S6 (RCT2 format) - parkImporter = ParkImporter::CreateS6(objectRepository); - } - auto loadResult = parkImporter->LoadSavedGame(sourcePath.c_str()); - - Console::WriteLine("File contains the following objects: "); - Console::WriteLine(); - - constexpr std::array typeToName = { - "Ride", "SmallScenery", "LargeScenery", "Walls", "Banners", "Paths", - "PathAdditions", "SceneryGroup", "ParkEntrance", "Water", "ScenarioMeta", "TerrainSurface", - "TerrainEdge", "Station", "Music", "FootpathSurface", "FootpathRailings", - }; - constexpr std::array sourceGameToName = { - "Custom", "WackyWorlds", "TimeTwister", "OpenRCT2Official", "RCT1", "AddedAttractions", "LoopyLandscapes", "", "RCT2", + kOptionTableEnd }; - for (auto& objType : { - ObjectType::ride, - ObjectType::smallScenery, - ObjectType::largeScenery, - ObjectType::walls, - ObjectType::banners, - ObjectType::paths, - ObjectType::pathAdditions, - ObjectType::sceneryGroup, - ObjectType::parkEntrance, - ObjectType::water, - ObjectType::scenarioMeta, - ObjectType::terrainSurface, - ObjectType::terrainEdge, - ObjectType::station, - ObjectType::music, - ObjectType::footpathSurface, - ObjectType::footpathRailings, - }) + static exitcode_t HandleObjectsInfo(CommandLineArgEnumerator *argEnumerator); + + const CommandLineCommand CommandLine::kParkInfoCommands[]{ + // Main commands + DefineCommand("objects", "", kNoOptions, HandleObjectsInfo), + + kCommandTableEnd + }; + // clang-format on + + static exitcode_t HandleObjectsInfo(CommandLineArgEnumerator* argEnumerator) { - auto& list = loadResult.RequiredObjects.GetList(objType); - Console::WriteLine("ObjectType: %s, Number of Objects: %d", typeToName[EnumValue(objType)], list.size()); - for (auto& obj : list) + exitcode_t result = CommandLine::HandleCommandDefault(); + if (result != EXITCODE_CONTINUE) { - if (obj.Generation == ObjectGeneration::JSON && obj.Identifier.size() == 0) - { - // Empty object slot don't output anything - continue; - } - auto* ori = OpenRCT2::GetContext()->GetObjectRepository().FindObject(obj); - Console::WriteFormat("%s Object: ", sourceGameToName[EnumValue(ori->GetFirstSourceGame())]); - - std::string name{ obj.GetName() }; - if (obj.Generation == ObjectGeneration::DAT) - { - Console::WriteLine("%08X|%s|%08X", obj.Entry.flags, name.c_str(), obj.Entry.checksum); - } - else - { - Console::WriteLine("%s", name.c_str()); - } + return result; } + + // Get the source path + const utf8* rawSourcePath; + if (!argEnumerator->TryPopString(&rawSourcePath)) + { + Console::Error::WriteLine("Expected a source save file path."); + return EXITCODE_FAIL; + } + + auto sourcePath = Path::GetAbsolute(rawSourcePath); + + auto context = OpenRCT2::CreateContext(); + context->Initialise(); + + auto stream = OpenRCT2::FileStream(sourcePath, OpenRCT2::FileMode::open); + ClassifiedFileInfo info; + if (!TryClassifyFile(&stream, &info)) + { + Console::Error::WriteLine("Unable to detect file type"); + return EXITCODE_FAIL; + } + + if (info.Type != FileType::park && info.Type != FileType::savedGame && info.Type != FileType::scenario) + { + Console::Error::WriteLine("Invalid file type."); + return EXITCODE_FAIL; + } + + auto& objectRepository = context->GetObjectRepository(); + std::unique_ptr parkImporter; + if (info.Type == FileType::park) + { + parkImporter = ParkImporter::CreateParkFile(objectRepository); + } + else if (info.Version <= kFileTypeS4Cutoff) + { + // Save is an S4 (RCT1 format) + parkImporter = ParkImporter::CreateS4(); + } + else + { + // Save is an S6 (RCT2 format) + parkImporter = ParkImporter::CreateS6(objectRepository); + } + auto loadResult = parkImporter->LoadSavedGame(sourcePath.c_str()); + + Console::WriteLine("File contains the following objects: "); Console::WriteLine(); + + constexpr std::array typeToName = { + "Ride", "SmallScenery", "LargeScenery", "Walls", "Banners", "Paths", + "PathAdditions", "SceneryGroup", "ParkEntrance", "Water", "ScenarioMeta", "TerrainSurface", + "TerrainEdge", "Station", "Music", "FootpathSurface", "FootpathRailings", + }; + constexpr std::array sourceGameToName = { + "Custom", "WackyWorlds", "TimeTwister", "OpenRCT2Official", "RCT1", "AddedAttractions", "LoopyLandscapes", + "", "RCT2", + }; + + for (auto& objType : { + ObjectType::ride, + ObjectType::smallScenery, + ObjectType::largeScenery, + ObjectType::walls, + ObjectType::banners, + ObjectType::paths, + ObjectType::pathAdditions, + ObjectType::sceneryGroup, + ObjectType::parkEntrance, + ObjectType::water, + ObjectType::scenarioMeta, + ObjectType::terrainSurface, + ObjectType::terrainEdge, + ObjectType::station, + ObjectType::music, + ObjectType::footpathSurface, + ObjectType::footpathRailings, + }) + { + auto& list = loadResult.RequiredObjects.GetList(objType); + Console::WriteLine("ObjectType: %s, Number of Objects: %d", typeToName[EnumValue(objType)], list.size()); + for (auto& obj : list) + { + if (obj.Generation == ObjectGeneration::JSON && obj.Identifier.size() == 0) + { + // Empty object slot don't output anything + continue; + } + auto* ori = OpenRCT2::GetContext()->GetObjectRepository().FindObject(obj); + Console::WriteFormat("%s Object: ", sourceGameToName[EnumValue(ori->GetFirstSourceGame())]); + + std::string name{ obj.GetName() }; + if (obj.Generation == ObjectGeneration::DAT) + { + Console::WriteLine("%08X|%s|%08X", obj.Entry.flags, name.c_str(), obj.Entry.checksum); + } + else + { + Console::WriteLine("%s", name.c_str()); + } + } + Console::WriteLine(); + } + return EXITCODE_OK; } - return EXITCODE_OK; -} +} // namespace OpenRCT2 diff --git a/src/openrct2/command_line/RootCommands.cpp b/src/openrct2/command_line/RootCommands.cpp index 6ad3630ceb..abd9a4b844 100644 --- a/src/openrct2/command_line/RootCommands.cpp +++ b/src/openrct2/command_line/RootCommands.cpp @@ -31,468 +31,469 @@ #include #include -using namespace OpenRCT2; - #ifdef USE_BREAKPAD #define IMPLIES_SILENT_BREAKPAD ", implies --silent-breakpad" #else #define IMPLIES_SILENT_BREAKPAD #endif // USE_BREAKPAD -#ifndef DISABLE_NETWORK -Network::Mode gNetworkStart = Network::Mode::none; -std::string gNetworkStartHost; -int32_t gNetworkStartPort = Network::kDefaultPort; -std::string gNetworkStartAddress; - -static uint32_t _port = 0; -static char* _address = nullptr; -#endif - -static bool _help = false; -static bool _version = false; -static bool _noInstall = false; -static bool _all = false; -static bool _about = false; -static bool _verbose = false; -static bool _headless = false; -static bool _silentReplays = false; -static u8string _password = {}; -static u8string _userDataPath = {}; -static u8string _openrct2DataPath = {}; -static u8string _rct1DataPath = {}; -static u8string _rct2DataPath = {}; -static bool _silentBreakpad = false; - -// clang-format off -static constexpr CommandLineOptionDefinition kStandardOptions[] +namespace OpenRCT2 { - { CMDLINE_TYPE_SWITCH, &_help, 'h', "help", "show this help message and exit" }, - { CMDLINE_TYPE_SWITCH, &_version, 'v', "version", "show version information and exit" }, - { CMDLINE_TYPE_SWITCH, &_noInstall, 'n', "no-install", "do not install scenario if passed" }, - { CMDLINE_TYPE_SWITCH, &_all, 'a', "all", "show help for all commands" }, - { CMDLINE_TYPE_SWITCH, &_about, kNAC, "about", "show information about " OPENRCT2_NAME }, - { CMDLINE_TYPE_SWITCH, &_verbose, kNAC, "verbose", "log verbose messages" }, - { CMDLINE_TYPE_SWITCH, &_headless, kNAC, "headless", "run " OPENRCT2_NAME " headless" IMPLIES_SILENT_BREAKPAD }, - { CMDLINE_TYPE_SWITCH, &_silentReplays, kNAC, "silent-replays", "use unobtrusive replays" }, #ifndef DISABLE_NETWORK - { CMDLINE_TYPE_INTEGER, &_port, kNAC, "port", "port to use for hosting or joining a server" }, - { CMDLINE_TYPE_STRING, &_address, kNAC, "address", "address to listen on when hosting a server" }, -#endif - { CMDLINE_TYPE_STRING, &_password, kNAC, "password", "password needed to join the server" }, - { CMDLINE_TYPE_STRING, &_userDataPath, kNAC, "user-data-path", "path to the user data directory (containing config.ini)" }, - { CMDLINE_TYPE_STRING, &_openrct2DataPath, kNAC, "openrct2-data-path", "path to the OpenRCT2 data directory (containing languages)" }, - { CMDLINE_TYPE_STRING, &_rct1DataPath, kNAC, "rct1-data-path", "path to the RollerCoaster Tycoon 1 data directory (containing data/csg1.dat)" }, - { CMDLINE_TYPE_STRING, &_rct2DataPath, kNAC, "rct2-data-path", "path to the RollerCoaster Tycoon 2 data directory (containing data/g1.dat)" }, -#ifdef USE_BREAKPAD - { CMDLINE_TYPE_SWITCH, &_silentBreakpad, kNAC, "silent-breakpad", "make breakpad crash reporting silent" }, -#endif // USE_BREAKPAD - kOptionTableEnd -}; - -static exitcode_t HandleNoCommand(CommandLineArgEnumerator * enumerator); -static exitcode_t HandleCommandEdit(CommandLineArgEnumerator * enumerator); -static exitcode_t HandleCommandIntro(CommandLineArgEnumerator * enumerator); -#ifndef DISABLE_NETWORK -static exitcode_t HandleCommandHost(CommandLineArgEnumerator * enumerator); -static exitcode_t HandleCommandJoin(CommandLineArgEnumerator * enumerator); -#endif -static exitcode_t HandleCommandSetRCT2(CommandLineArgEnumerator * enumerator); -static exitcode_t HandleCommandScanObjects(CommandLineArgEnumerator * enumerator); - -#if defined(_WIN32) - -static bool _removeShell = false; - -static constexpr CommandLineOptionDefinition RegisterShellOptions[] -{ - { CMDLINE_TYPE_SWITCH, &_removeShell, 'd', "remove", "remove shell integration" }, -}; - -static exitcode_t HandleCommandRegisterShell(CommandLineArgEnumerator * enumerator); + Network::Mode gNetworkStart = Network::Mode::none; + std::string gNetworkStartHost; + int32_t gNetworkStartPort = Network::kDefaultPort; + std::string gNetworkStartAddress; + static uint32_t _port = 0; + static char* _address = nullptr; #endif -static void PrintAbout(); -static void PrintVersion(); -static void PrintLaunchInformation(); + static bool _help = false; + static bool _version = false; + static bool _noInstall = false; + static bool _all = false; + static bool _about = false; + static bool _verbose = false; + static bool _headless = false; + static bool _silentReplays = false; + static u8string _password = {}; + static u8string _userDataPath = {}; + static u8string _openrct2DataPath = {}; + static u8string _rct1DataPath = {}; + static u8string _rct2DataPath = {}; + static bool _silentBreakpad = false; -const CommandLineCommand CommandLine::kRootCommands[] -{ - // Main commands -#ifndef DISABLE_HTTP - DefineCommand("", "", kStandardOptions, HandleNoCommand ), - DefineCommand("edit", "", kStandardOptions, HandleCommandEdit ), -#else - DefineCommand("", "", kStandardOptions, HandleNoCommand ), - DefineCommand("edit", "", kStandardOptions, HandleCommandEdit ), -#endif - DefineCommand("intro", "", kStandardOptions, HandleCommandIntro ), -#ifndef DISABLE_NETWORK - DefineCommand("host", "", kStandardOptions, HandleCommandHost ), - DefineCommand("join", "", kStandardOptions, HandleCommandJoin ), -#endif - DefineCommand("set-rct2", "", kStandardOptions, HandleCommandSetRCT2), - DefineCommand("scan-objects", "", kStandardOptions, HandleCommandScanObjects), - DefineCommand("handle-uri", "openrct2://.../", kStandardOptions, CommandLine::HandleCommandUri), - -#if defined(_WIN32) - DefineCommand("register-shell", "", RegisterShellOptions, HandleCommandRegisterShell), -#endif - - // Sub-commands - DefineSubCommand("convert", CommandLine::kConvertCommands ), - DefineSubCommand("screenshot", CommandLine::kScreenshotCommands ), - DefineSubCommand("sprite", CommandLine::kSpriteCommands ), - DefineSubCommand("simulate", CommandLine::kSimulateCommands ), - DefineSubCommand("parkinfo", CommandLine::kParkInfoCommands ), - kCommandTableEnd -}; - -const CommandLineExample CommandLine::kRootExamples[] -{ - { "./my_park.sv6", "open a saved park" }, - { "./SnowyPark.sc6", "install and open a scenario" }, - { "./ShuttleLoop.td6", "install a track" }, -#ifndef DISABLE_HTTP - { "https://example.org/files/ExamplePark.sv6", "download and open a saved park" }, -#endif -#ifndef DISABLE_NETWORK - { "host ./my_park.sv6 --port 11753 --headless", "run a headless server for a saved park" }, -#endif - kExampleTableEnd -}; -// clang-format on - -exitcode_t CommandLine::HandleCommandDefault() -{ - exitcode_t result = EXITCODE_CONTINUE; - - if (_about) + // clang-format off + static constexpr CommandLineOptionDefinition kStandardOptions[] { - PrintAbout(); - result = EXITCODE_OK; - } - else - { - if (_verbose) - { - _log_levels[EnumValue(DiagnosticLevel::Verbose)] = true; - PrintLaunchInformation(); - } + { CMDLINE_TYPE_SWITCH, &_help, 'h', "help", "show this help message and exit" }, + { CMDLINE_TYPE_SWITCH, &_version, 'v', "version", "show version information and exit" }, + { CMDLINE_TYPE_SWITCH, &_noInstall, 'n', "no-install", "do not install scenario if passed" }, + { CMDLINE_TYPE_SWITCH, &_all, 'a', "all", "show help for all commands" }, + { CMDLINE_TYPE_SWITCH, &_about, kNAC, "about", "show information about " OPENRCT2_NAME }, + { CMDLINE_TYPE_SWITCH, &_verbose, kNAC, "verbose", "log verbose messages" }, + { CMDLINE_TYPE_SWITCH, &_headless, kNAC, "headless", "run " OPENRCT2_NAME " headless" IMPLIES_SILENT_BREAKPAD }, + { CMDLINE_TYPE_SWITCH, &_silentReplays, kNAC, "silent-replays", "use unobtrusive replays" }, + #ifndef DISABLE_NETWORK + { CMDLINE_TYPE_INTEGER, &_port, kNAC, "port", "port to use for hosting or joining a server" }, + { CMDLINE_TYPE_STRING, &_address, kNAC, "address", "address to listen on when hosting a server" }, + #endif + { CMDLINE_TYPE_STRING, &_password, kNAC, "password", "password needed to join the server" }, + { CMDLINE_TYPE_STRING, &_userDataPath, kNAC, "user-data-path", "path to the user data directory (containing config.ini)" }, + { CMDLINE_TYPE_STRING, &_openrct2DataPath, kNAC, "openrct2-data-path", "path to the OpenRCT2 data directory (containing languages)" }, + { CMDLINE_TYPE_STRING, &_rct1DataPath, kNAC, "rct1-data-path", "path to the RollerCoaster Tycoon 1 data directory (containing data/csg1.dat)" }, + { CMDLINE_TYPE_STRING, &_rct2DataPath, kNAC, "rct2-data-path", "path to the RollerCoaster Tycoon 2 data directory (containing data/g1.dat)" }, + #ifdef USE_BREAKPAD + { CMDLINE_TYPE_SWITCH, &_silentBreakpad, kNAC, "silent-breakpad", "make breakpad crash reporting silent" }, + #endif // USE_BREAKPAD + kOptionTableEnd + }; - if (_version) + static exitcode_t HandleNoCommand(CommandLineArgEnumerator * enumerator); + static exitcode_t HandleCommandEdit(CommandLineArgEnumerator * enumerator); + static exitcode_t HandleCommandIntro(CommandLineArgEnumerator * enumerator); + #ifndef DISABLE_NETWORK + static exitcode_t HandleCommandHost(CommandLineArgEnumerator * enumerator); + static exitcode_t HandleCommandJoin(CommandLineArgEnumerator * enumerator); + #endif + static exitcode_t HandleCommandSetRCT2(CommandLineArgEnumerator * enumerator); + static exitcode_t HandleCommandScanObjects(CommandLineArgEnumerator * enumerator); + + #if defined(_WIN32) + + static bool _removeShell = false; + + static constexpr CommandLineOptionDefinition RegisterShellOptions[] + { + { CMDLINE_TYPE_SWITCH, &_removeShell, 'd', "remove", "remove shell integration" }, + }; + + static exitcode_t HandleCommandRegisterShell(CommandLineArgEnumerator * enumerator); + + #endif + + static void PrintAbout(); + static void PrintVersion(); + static void PrintLaunchInformation(); + + const CommandLineCommand CommandLine::kRootCommands[] + { + // Main commands + #ifndef DISABLE_HTTP + DefineCommand("", "", kStandardOptions, HandleNoCommand ), + DefineCommand("edit", "", kStandardOptions, HandleCommandEdit ), + #else + DefineCommand("", "", kStandardOptions, HandleNoCommand ), + DefineCommand("edit", "", kStandardOptions, HandleCommandEdit ), + #endif + DefineCommand("intro", "", kStandardOptions, HandleCommandIntro ), + #ifndef DISABLE_NETWORK + DefineCommand("host", "", kStandardOptions, HandleCommandHost ), + DefineCommand("join", "", kStandardOptions, HandleCommandJoin ), + #endif + DefineCommand("set-rct2", "", kStandardOptions, HandleCommandSetRCT2), + DefineCommand("scan-objects", "", kStandardOptions, HandleCommandScanObjects), + DefineCommand("handle-uri", "openrct2://.../", kStandardOptions, CommandLine::HandleCommandUri), + + #if defined(_WIN32) + DefineCommand("register-shell", "", RegisterShellOptions, HandleCommandRegisterShell), + #endif + + // Sub-commands + DefineSubCommand("convert", CommandLine::kConvertCommands ), + DefineSubCommand("screenshot", CommandLine::kScreenshotCommands ), + DefineSubCommand("sprite", CommandLine::kSpriteCommands ), + DefineSubCommand("simulate", CommandLine::kSimulateCommands ), + DefineSubCommand("parkinfo", CommandLine::kParkInfoCommands ), + kCommandTableEnd + }; + + const CommandLineExample CommandLine::kRootExamples[] + { + { "./my_park.sv6", "open a saved park" }, + { "./SnowyPark.sc6", "install and open a scenario" }, + { "./ShuttleLoop.td6", "install a track" }, + #ifndef DISABLE_HTTP + { "https://example.org/files/ExamplePark.sv6", "download and open a saved park" }, + #endif + #ifndef DISABLE_NETWORK + { "host ./my_park.sv6 --port 11753 --headless", "run a headless server for a saved park" }, + #endif + kExampleTableEnd + }; + // clang-format on + + exitcode_t CommandLine::HandleCommandDefault() + { + exitcode_t result = EXITCODE_CONTINUE; + + if (_about) { - if (!_verbose) - { - PrintVersion(); - } + PrintAbout(); result = EXITCODE_OK; } - } + else + { + if (_verbose) + { + _log_levels[EnumValue(DiagnosticLevel::Verbose)] = true; + PrintLaunchInformation(); + } - if (_help || _all) - { - CommandLine::PrintHelp(_all); - result = EXITCODE_OK; - } + if (_version) + { + if (!_verbose) + { + PrintVersion(); + } + result = EXITCODE_OK; + } + } - gOpenRCT2Headless = _headless; - gOpenRCT2NoGraphics = _headless; - gOpenRCT2SilentBreakpad = _silentBreakpad || _headless; + if (_help || _all) + { + CommandLine::PrintHelp(_all); + result = EXITCODE_OK; + } - if (!_userDataPath.empty()) - { - gCustomUserDataPath = Path::GetAbsolute(_userDataPath); - } + gOpenRCT2Headless = _headless; + gOpenRCT2NoGraphics = _headless; + gOpenRCT2SilentBreakpad = _silentBreakpad || _headless; - if (!_openrct2DataPath.empty()) - { - gCustomOpenRCT2DataPath = Path::GetAbsolute(_openrct2DataPath); - } + if (!_userDataPath.empty()) + { + gCustomUserDataPath = Path::GetAbsolute(_userDataPath); + } - if (!_rct1DataPath.empty()) - { - gCustomRCT1DataPath = Path::GetAbsolute(_rct1DataPath); - } + if (!_openrct2DataPath.empty()) + { + gCustomOpenRCT2DataPath = Path::GetAbsolute(_openrct2DataPath); + } - if (!_rct2DataPath.empty()) - { - gCustomRCT2DataPath = Path::GetAbsolute(_rct2DataPath); - } + if (!_rct1DataPath.empty()) + { + gCustomRCT1DataPath = Path::GetAbsolute(_rct1DataPath); + } - if (!_password.empty()) - { - gCustomPassword = _password; - } + if (!_rct2DataPath.empty()) + { + gCustomRCT2DataPath = Path::GetAbsolute(_rct2DataPath); + } - if (_silentReplays) - { - gSilentReplays = _silentReplays; - } + if (!_password.empty()) + { + gCustomPassword = _password; + } - return result; -} + if (_silentReplays) + { + gSilentReplays = _silentReplays; + } -exitcode_t HandleNoCommand(CommandLineArgEnumerator* enumerator) -{ - exitcode_t result = CommandLine::HandleCommandDefault(); - if (result != EXITCODE_CONTINUE) - { return result; } - const char* parkUri; - if (enumerator->TryPopString(&parkUri) && parkUri[0] != '-') + exitcode_t HandleNoCommand(CommandLineArgEnumerator* enumerator) { + exitcode_t result = CommandLine::HandleCommandDefault(); + if (result != EXITCODE_CONTINUE) + { + return result; + } + + const char* parkUri; + if (enumerator->TryPopString(&parkUri) && parkUri[0] != '-') + { + String::set(gOpenRCT2StartupActionPath, sizeof(gOpenRCT2StartupActionPath), parkUri); + gOpenRCT2StartupAction = StartupAction::Open; + } + + return EXITCODE_CONTINUE; + } + + exitcode_t HandleCommandEdit(CommandLineArgEnumerator* enumerator) + { + exitcode_t result = CommandLine::HandleCommandDefault(); + if (result != EXITCODE_CONTINUE) + { + return result; + } + + const char* parkUri; + if (!enumerator->TryPopString(&parkUri)) + { + Console::Error::WriteLine("Expected path or URL to a saved park."); + return EXITCODE_FAIL; + } String::set(gOpenRCT2StartupActionPath, sizeof(gOpenRCT2StartupActionPath), parkUri); - gOpenRCT2StartupAction = StartupAction::Open; + + gOpenRCT2StartupAction = StartupAction::Edit; + return EXITCODE_CONTINUE; } - return EXITCODE_CONTINUE; -} - -exitcode_t HandleCommandEdit(CommandLineArgEnumerator* enumerator) -{ - exitcode_t result = CommandLine::HandleCommandDefault(); - if (result != EXITCODE_CONTINUE) + exitcode_t HandleCommandIntro([[maybe_unused]] CommandLineArgEnumerator* enumerator) { - return result; + exitcode_t result = CommandLine::HandleCommandDefault(); + if (result != EXITCODE_CONTINUE) + { + return result; + } + + gOpenRCT2StartupAction = StartupAction::Intro; + return EXITCODE_CONTINUE; } - const char* parkUri; - if (!enumerator->TryPopString(&parkUri)) - { - Console::Error::WriteLine("Expected path or URL to a saved park."); - return EXITCODE_FAIL; - } - String::set(gOpenRCT2StartupActionPath, sizeof(gOpenRCT2StartupActionPath), parkUri); - - gOpenRCT2StartupAction = StartupAction::Edit; - return EXITCODE_CONTINUE; -} - -exitcode_t HandleCommandIntro([[maybe_unused]] CommandLineArgEnumerator* enumerator) -{ - exitcode_t result = CommandLine::HandleCommandDefault(); - if (result != EXITCODE_CONTINUE) - { - return result; - } - - gOpenRCT2StartupAction = StartupAction::Intro; - return EXITCODE_CONTINUE; -} - #ifndef DISABLE_NETWORK -exitcode_t HandleCommandHost(CommandLineArgEnumerator* enumerator) -{ - exitcode_t result = CommandLine::HandleCommandDefault(); - if (result != EXITCODE_CONTINUE) + exitcode_t HandleCommandHost(CommandLineArgEnumerator* enumerator) { - return result; + exitcode_t result = CommandLine::HandleCommandDefault(); + if (result != EXITCODE_CONTINUE) + { + return result; + } + + const char* parkUri; + if (!enumerator->TryPopString(&parkUri)) + { + Console::Error::WriteLine("Expected path or URL to a scenario or saved park."); + return EXITCODE_FAIL; + } + + gOpenRCT2StartupAction = StartupAction::Open; + String::set(gOpenRCT2StartupActionPath, sizeof(gOpenRCT2StartupActionPath), parkUri); + + gNetworkStart = Network::Mode::server; + gNetworkStartPort = _port; + gNetworkStartAddress = String::toStd(_address); + + return EXITCODE_CONTINUE; } - const char* parkUri; - if (!enumerator->TryPopString(&parkUri)) + exitcode_t HandleCommandJoin(CommandLineArgEnumerator* enumerator) { - Console::Error::WriteLine("Expected path or URL to a scenario or saved park."); - return EXITCODE_FAIL; + exitcode_t result = CommandLine::HandleCommandDefault(); + if (result != EXITCODE_CONTINUE) + { + return result; + } + + const char* hostname; + if (!enumerator->TryPopString(&hostname)) + { + Console::Error::WriteLine("Expected a hostname or IP address to the server to connect to."); + return EXITCODE_FAIL; + } + + gNetworkStart = Network::Mode::client; + gNetworkStartPort = _port; + gNetworkStartHost = hostname; + return EXITCODE_CONTINUE; } - gOpenRCT2StartupAction = StartupAction::Open; - String::set(gOpenRCT2StartupActionPath, sizeof(gOpenRCT2StartupActionPath), parkUri); - - gNetworkStart = Network::Mode::server; - gNetworkStartPort = _port; - gNetworkStartAddress = String::toStd(_address); - - return EXITCODE_CONTINUE; -} - -exitcode_t HandleCommandJoin(CommandLineArgEnumerator* enumerator) -{ - exitcode_t result = CommandLine::HandleCommandDefault(); - if (result != EXITCODE_CONTINUE) - { - return result; - } - - const char* hostname; - if (!enumerator->TryPopString(&hostname)) - { - Console::Error::WriteLine("Expected a hostname or IP address to the server to connect to."); - return EXITCODE_FAIL; - } - - gNetworkStart = Network::Mode::client; - gNetworkStartPort = _port; - gNetworkStartHost = hostname; - return EXITCODE_CONTINUE; -} - #endif // DISABLE_NETWORK -static exitcode_t HandleCommandSetRCT2(CommandLineArgEnumerator* enumerator) -{ - exitcode_t result = CommandLine::HandleCommandDefault(); - if (result != EXITCODE_CONTINUE) + static exitcode_t HandleCommandSetRCT2(CommandLineArgEnumerator* enumerator) { - return result; - } + exitcode_t result = CommandLine::HandleCommandDefault(); + if (result != EXITCODE_CONTINUE) + { + return result; + } - // Get the path that was passed - const utf8* rawPath; - if (!enumerator->TryPopString(&rawPath)) - { - Console::Error::WriteLine("Expected a path."); + // Get the path that was passed + const utf8* rawPath; + if (!enumerator->TryPopString(&rawPath)) + { + Console::Error::WriteLine("Expected a path."); + return EXITCODE_FAIL; + } + + const auto path = Path::GetAbsolute(rawPath); + + // Check if path exists + Console::WriteLine("Checking path..."); + if (!Path::DirectoryExists(path)) + { + Console::Error::WriteLine("The path '%s' does not exist", path.c_str()); + return EXITCODE_FAIL; + } + + // Check if g1.dat exists (naive but good check) + Console::WriteLine("Checking g1.dat..."); + + auto pathG1Check = Path::Combine(path, u8"Data", u8"g1.dat"); + if (!File::Exists(pathG1Check)) + { + Console::Error::WriteLine("RCT2 path not valid."); + Console::Error::WriteLine("Unable to find %s.", pathG1Check.c_str()); + return EXITCODE_FAIL; + } + + // Update RCT2 path in config + auto env = OpenRCT2::CreatePlatformEnvironment(); + auto configPath = env->GetFilePath(OpenRCT2::PathId::config); + Config::SetDefaults(); + Config::OpenFromPath(configPath); + Config::Get().general.RCT2Path = path; + if (Config::SaveToPath(configPath)) + { + Console::WriteFormat("Updating RCT2 path to '%s'.", path.c_str()); + Console::WriteLine(); + Console::WriteLine("Updated config.ini"); + return EXITCODE_OK; + } + + Console::Error::WriteLine("Unable to update config.ini"); return EXITCODE_FAIL; } - const auto path = Path::GetAbsolute(rawPath); - - // Check if path exists - Console::WriteLine("Checking path..."); - if (!Path::DirectoryExists(path)) + static exitcode_t HandleCommandScanObjects([[maybe_unused]] CommandLineArgEnumerator* enumerator) { - Console::Error::WriteLine("The path '%s' does not exist", path.c_str()); - return EXITCODE_FAIL; - } + exitcode_t result = CommandLine::HandleCommandDefault(); + if (result != EXITCODE_CONTINUE) + { + return result; + } - // Check if g1.dat exists (naive but good check) - Console::WriteLine("Checking g1.dat..."); + gOpenRCT2Headless = true; + gOpenRCT2NoGraphics = true; - auto pathG1Check = Path::Combine(path, u8"Data", u8"g1.dat"); - if (!File::Exists(pathG1Check)) - { - Console::Error::WriteLine("RCT2 path not valid."); - Console::Error::WriteLine("Unable to find %s.", pathG1Check.c_str()); - return EXITCODE_FAIL; - } - - // Update RCT2 path in config - auto env = OpenRCT2::CreatePlatformEnvironment(); - auto configPath = env->GetFilePath(OpenRCT2::PathId::config); - Config::SetDefaults(); - Config::OpenFromPath(configPath); - Config::Get().general.RCT2Path = path; - if (Config::SaveToPath(configPath)) - { - Console::WriteFormat("Updating RCT2 path to '%s'.", path.c_str()); - Console::WriteLine(); - Console::WriteLine("Updated config.ini"); + auto context = OpenRCT2::CreateContext(); + auto& env = context->GetPlatformEnvironment(); + auto objectRepository = CreateObjectRepository(env); + objectRepository->Construct(Config::Get().general.Language); return EXITCODE_OK; } - Console::Error::WriteLine("Unable to update config.ini"); - return EXITCODE_FAIL; -} - -static exitcode_t HandleCommandScanObjects([[maybe_unused]] CommandLineArgEnumerator* enumerator) -{ - exitcode_t result = CommandLine::HandleCommandDefault(); - if (result != EXITCODE_CONTINUE) - { - return result; - } - - gOpenRCT2Headless = true; - gOpenRCT2NoGraphics = true; - - auto context = OpenRCT2::CreateContext(); - auto& env = context->GetPlatformEnvironment(); - auto objectRepository = CreateObjectRepository(env); - objectRepository->Construct(Config::Get().general.Language); - return EXITCODE_OK; -} - #if defined(_WIN32) -static exitcode_t HandleCommandRegisterShell([[maybe_unused]] CommandLineArgEnumerator* enumerator) -{ - exitcode_t result = CommandLine::HandleCommandDefault(); - if (result != EXITCODE_CONTINUE) + static exitcode_t HandleCommandRegisterShell([[maybe_unused]] CommandLineArgEnumerator* enumerator) { - return result; - } + exitcode_t result = CommandLine::HandleCommandDefault(); + if (result != EXITCODE_CONTINUE) + { + return result; + } - if (!_removeShell) - { - Platform::SetUpFileAssociations(); + if (!_removeShell) + { + Platform::SetUpFileAssociations(); + } + else + { + Platform::RemoveFileAssociations(); + } + return EXITCODE_OK; } - else - { - Platform::RemoveFileAssociations(); - } - return EXITCODE_OK; -} #endif // defined(_WIN32) -static void PrintAbout() -{ - PrintVersion(); - Console::WriteLine(); - Console::WriteLine("OpenRCT2 is an amusement park simulation game based upon the popular game"); - Console::WriteLine("RollerCoaster Tycoon 2, written by Chris Sawyer. It attempts to mimic the "); - Console::WriteLine("original game as closely as possible while extending it with new features."); - Console::WriteLine("OpenRCT2 is licensed under the GNU General Public License version 3.0, but"); - Console::WriteLine("includes some 3rd party software under different licenses. See the file"); - Console::WriteLine("\"licence.txt\" shipped with the game for details."); - Console::WriteLine(); - Console::WriteLine("Website: https://openrct2.io"); - Console::WriteLine("GitHub: https://github.com/OpenRCT2/OpenRCT2"); - Console::WriteLine("Contributors: https://github.com/OpenRCT2/OpenRCT2/blob/develop/contributors.md"); - Console::WriteLine("Privacy Policy: https://github.com/OpenRCT2/OpenRCT2/blob/develop/PRIVACY.md"); - Console::WriteLine(); -} + static void PrintAbout() + { + PrintVersion(); + Console::WriteLine(); + Console::WriteLine("OpenRCT2 is an amusement park simulation game based upon the popular game"); + Console::WriteLine("RollerCoaster Tycoon 2, written by Chris Sawyer. It attempts to mimic the "); + Console::WriteLine("original game as closely as possible while extending it with new features."); + Console::WriteLine("OpenRCT2 is licensed under the GNU General Public License version 3.0, but"); + Console::WriteLine("includes some 3rd party software under different licenses. See the file"); + Console::WriteLine("\"licence.txt\" shipped with the game for details."); + Console::WriteLine(); + Console::WriteLine("Website: https://openrct2.io"); + Console::WriteLine("GitHub: https://github.com/OpenRCT2/OpenRCT2"); + Console::WriteLine("Contributors: https://github.com/OpenRCT2/OpenRCT2/blob/develop/contributors.md"); + Console::WriteLine("Privacy Policy: https://github.com/OpenRCT2/OpenRCT2/blob/develop/PRIVACY.md"); + Console::WriteLine(); + } -static void PrintVersion() -{ - u8string versionInfo = gVersionInfoFull; - Console::WriteLine(versionInfo.c_str()); - Console::WriteFormat("%s (%s)", OPENRCT2_PLATFORM, OPENRCT2_ARCHITECTURE); - Console::WriteLine(); - Console::WriteFormat("Network version: %s", Network::GetVersion().c_str()); - Console::WriteLine(); + static void PrintVersion() + { + u8string versionInfo = gVersionInfoFull; + Console::WriteLine(versionInfo.c_str()); + Console::WriteFormat("%s (%s)", OPENRCT2_PLATFORM, OPENRCT2_ARCHITECTURE); + Console::WriteLine(); + Console::WriteFormat("Network version: %s", Network::GetVersion().c_str()); + Console::WriteLine(); #ifdef ENABLE_SCRIPTING - Console::WriteFormat("Plugin API version: %d", OpenRCT2::Scripting::kPluginApiVersion); - Console::WriteLine(); + Console::WriteFormat("Plugin API version: %d", OpenRCT2::Scripting::kPluginApiVersion); + Console::WriteLine(); #else - Console::WriteFormat("Plugin API not enabled in this build"); - Console::WriteLine(); + Console::WriteFormat("Plugin API not enabled in this build"); + Console::WriteLine(); #endif - Console::WriteFormat("Current park file version: %d", OpenRCT2::kParkFileCurrentVersion); - Console::WriteLine(); - Console::WriteFormat("Minimum park file version: %d", OpenRCT2::kParkFileMinVersion); - Console::WriteLine(); + Console::WriteFormat("Current park file version: %d", OpenRCT2::kParkFileCurrentVersion); + Console::WriteLine(); + Console::WriteFormat("Minimum park file version: %d", OpenRCT2::kParkFileMinVersion); + Console::WriteLine(); #ifdef USE_BREAKPAD - Console::WriteFormat("With breakpad support enabled"); - Console::WriteLine(); + Console::WriteFormat("With breakpad support enabled"); + Console::WriteLine(); #else - Console::WriteFormat("Breakpad support disabled"); - Console::WriteLine(); + Console::WriteFormat("Breakpad support disabled"); + Console::WriteLine(); #endif -} + } -static void PrintLaunchInformation() -{ - char buffer[256]; - time_t timer; - struct tm* tmInfo; + static void PrintLaunchInformation() + { + char buffer[256]; + time_t timer; + struct tm* tmInfo; - // Print name and version information - u8string versionInfo = gVersionInfoFull; - Console::WriteLine(versionInfo.c_str()); - Console::WriteFormat("%s (%s)", OPENRCT2_PLATFORM, OPENRCT2_ARCHITECTURE); - Console::WriteLine(); - Console::WriteLine(); + // Print name and version information + u8string versionInfo = gVersionInfoFull; + Console::WriteLine(versionInfo.c_str()); + Console::WriteFormat("%s (%s)", OPENRCT2_PLATFORM, OPENRCT2_ARCHITECTURE); + Console::WriteLine(); + Console::WriteLine(); - // Print current time - time(&timer); - tmInfo = localtime(&timer); - strftime(buffer, sizeof(buffer), "%Y/%m/%d %H:%M:%S", tmInfo); - Console::WriteFormat("VERBOSE: time is %s", buffer); - Console::WriteLine(); + // Print current time + time(&timer); + tmInfo = localtime(&timer); + strftime(buffer, sizeof(buffer), "%Y/%m/%d %H:%M:%S", tmInfo); + Console::WriteFormat("VERBOSE: time is %s", buffer); + Console::WriteLine(); - // TODO Print other potential information (e.g. user, hardware) -} + // TODO Print other potential information (e.g. user, hardware) + } +} // namespace OpenRCT2 diff --git a/src/openrct2/command_line/ScreenshotCommands.cpp b/src/openrct2/command_line/ScreenshotCommands.cpp index b39fb2f2e1..f4f07b6131 100644 --- a/src/openrct2/command_line/ScreenshotCommands.cpp +++ b/src/openrct2/command_line/ScreenshotCommands.cpp @@ -10,46 +10,47 @@ #include "../interface/Screenshot.h" #include "CommandLine.hpp" -using namespace OpenRCT2; - -static ScreenshotOptions _options; - -// clang-format off -static constexpr CommandLineOptionDefinition kScreenshotOptionsDef[] +namespace OpenRCT2 { - { CMDLINE_TYPE_INTEGER, &_options.weather, kNAC, "weather", "weather to be used (0 = default, 1 = sunny, ..., 6 = thunder)." }, - { CMDLINE_TYPE_SWITCH, &_options.hide_guests, kNAC, "no-peeps", "hide peeps" }, - { CMDLINE_TYPE_SWITCH, &_options.hide_sprites, kNAC, "no-sprites", "hide all sprites (e.g. balloons, vehicles, guests)" }, - { CMDLINE_TYPE_SWITCH, &_options.clear_grass, kNAC, "clear-grass", "set all grass to be clear of weeds" }, - { CMDLINE_TYPE_SWITCH, &_options.mowed_grass, kNAC, "mowed-grass", "set all grass to be mowed" }, - { CMDLINE_TYPE_SWITCH, &_options.water_plants, kNAC, "water-plants", "water plants for the screenshot" }, - { CMDLINE_TYPE_SWITCH, &_options.fix_vandalism, kNAC, "fix-vandalism", "fix vandalism for the screenshot" }, - { CMDLINE_TYPE_SWITCH, &_options.remove_litter, kNAC, "remove-litter", "remove litter for the screenshot" }, - { CMDLINE_TYPE_SWITCH, &_options.tidy_up_park, kNAC, "tidy-up-park", "clear grass, water plants, fix vandalism and remove litter" }, - { CMDLINE_TYPE_SWITCH, &_options.transparent, kNAC, "transparent", "make the background transparent" }, - { CMDLINE_TYPE_SWITCH, &_options.draw_bounding_boxes, kNAC, "draw-bounding-boxes", "draw bounding boxes" }, - kOptionTableEnd -}; + static ScreenshotOptions _options; -static exitcode_t HandleScreenshot(CommandLineArgEnumerator *argEnumerator); - -const CommandLineCommand CommandLine::kScreenshotCommands[] -{ - // Main commands - DefineCommand("", " [ [] ]", kScreenshotOptionsDef, HandleScreenshot), - DefineCommand("", " giant ", kScreenshotOptionsDef, HandleScreenshot), - kCommandTableEnd -}; -// clang-format on - -static exitcode_t HandleScreenshot(CommandLineArgEnumerator* argEnumerator) -{ - const char** argv = const_cast(argEnumerator->GetArguments()) + argEnumerator->GetIndex(); - int32_t argc = argEnumerator->GetCount() - argEnumerator->GetIndex(); - int32_t result = CommandLineForScreenshot(argv, argc, &_options); - if (result < 0) + // clang-format off + static constexpr CommandLineOptionDefinition kScreenshotOptionsDef[] { - return EXITCODE_FAIL; + { CMDLINE_TYPE_INTEGER, &_options.weather, kNAC, "weather", "weather to be used (0 = default, 1 = sunny, ..., 6 = thunder)." }, + { CMDLINE_TYPE_SWITCH, &_options.hide_guests, kNAC, "no-peeps", "hide peeps" }, + { CMDLINE_TYPE_SWITCH, &_options.hide_sprites, kNAC, "no-sprites", "hide all sprites (e.g. balloons, vehicles, guests)" }, + { CMDLINE_TYPE_SWITCH, &_options.clear_grass, kNAC, "clear-grass", "set all grass to be clear of weeds" }, + { CMDLINE_TYPE_SWITCH, &_options.mowed_grass, kNAC, "mowed-grass", "set all grass to be mowed" }, + { CMDLINE_TYPE_SWITCH, &_options.water_plants, kNAC, "water-plants", "water plants for the screenshot" }, + { CMDLINE_TYPE_SWITCH, &_options.fix_vandalism, kNAC, "fix-vandalism", "fix vandalism for the screenshot" }, + { CMDLINE_TYPE_SWITCH, &_options.remove_litter, kNAC, "remove-litter", "remove litter for the screenshot" }, + { CMDLINE_TYPE_SWITCH, &_options.tidy_up_park, kNAC, "tidy-up-park", "clear grass, water plants, fix vandalism and remove litter" }, + { CMDLINE_TYPE_SWITCH, &_options.transparent, kNAC, "transparent", "make the background transparent" }, + { CMDLINE_TYPE_SWITCH, &_options.draw_bounding_boxes, kNAC, "draw-bounding-boxes", "draw bounding boxes" }, + kOptionTableEnd + }; + + static exitcode_t HandleScreenshot(CommandLineArgEnumerator *argEnumerator); + + const CommandLineCommand CommandLine::kScreenshotCommands[] + { + // Main commands + DefineCommand("", " [ [] ]", kScreenshotOptionsDef, HandleScreenshot), + DefineCommand("", " giant ", kScreenshotOptionsDef, HandleScreenshot), + kCommandTableEnd + }; + // clang-format on + + static exitcode_t HandleScreenshot(CommandLineArgEnumerator* argEnumerator) + { + const char** argv = const_cast(argEnumerator->GetArguments()) + argEnumerator->GetIndex(); + int32_t argc = argEnumerator->GetCount() - argEnumerator->GetIndex(); + int32_t result = CommandLineForScreenshot(argv, argc, &_options); + if (result < 0) + { + return EXITCODE_FAIL; + } + return EXITCODE_OK; } - return EXITCODE_OK; -} +} // namespace OpenRCT2 diff --git a/src/openrct2/command_line/SimulateCommands.cpp b/src/openrct2/command_line/SimulateCommands.cpp index fcfbd82275..e2fcaf276b 100644 --- a/src/openrct2/command_line/SimulateCommands.cpp +++ b/src/openrct2/command_line/SimulateCommands.cpp @@ -21,65 +21,66 @@ #include #include -using namespace OpenRCT2; - -// clang-format off -static constexpr CommandLineOptionDefinition kNoOptions[] +namespace OpenRCT2 { - kOptionTableEnd -}; - -static exitcode_t HandleSimulate(CommandLineArgEnumerator* argEnumerator); - -const CommandLineCommand CommandLine::kSimulateCommands[]{ - // Main commands - DefineCommand("", " ", kNoOptions, HandleSimulate), - kCommandTableEnd -}; -// clang-format on - -static exitcode_t HandleSimulate(CommandLineArgEnumerator* argEnumerator) -{ - const utf8* inputPath; - if (!argEnumerator->TryPopString(&inputPath)) + // clang-format off + static constexpr CommandLineOptionDefinition kNoOptions[] { - Console::Error::WriteLine("Expected a save file path"); - return EXITCODE_FAIL; - } + kOptionTableEnd + }; - int32_t ticks; - if (!argEnumerator->TryPopInteger(&ticks)) + static exitcode_t HandleSimulate(CommandLineArgEnumerator* argEnumerator); + + const CommandLineCommand CommandLine::kSimulateCommands[]{ + // Main commands + DefineCommand("", " ", kNoOptions, HandleSimulate), + kCommandTableEnd + }; + // clang-format on + + static exitcode_t HandleSimulate(CommandLineArgEnumerator* argEnumerator) { - Console::Error::WriteLine("Expected a number of ticks to simulate"); - return EXITCODE_FAIL; - } - - gOpenRCT2Headless = true; - -#ifndef DISABLE_NETWORK - gNetworkStart = Network::Mode::server; -#endif - - std::unique_ptr context(CreateContext()); - if (context->Initialise()) - { - if (!context->LoadParkFromFile(inputPath)) + const utf8* inputPath; + if (!argEnumerator->TryPopString(&inputPath)) { + Console::Error::WriteLine("Expected a save file path"); return EXITCODE_FAIL; } - Console::WriteLine("Running %d ticks...", ticks); - for (int32_t i = 0; i < ticks; i++) + int32_t ticks; + if (!argEnumerator->TryPopInteger(&ticks)) { - gameStateUpdateLogic(); + Console::Error::WriteLine("Expected a number of ticks to simulate"); + return EXITCODE_FAIL; } - Console::WriteLine("Completed: %s", getGameState().entities.GetAllEntitiesChecksum().ToString().c_str()); - } - else - { - Console::Error::WriteLine("Context initialization failed."); - return EXITCODE_FAIL; - } - return EXITCODE_OK; -} + gOpenRCT2Headless = true; + +#ifndef DISABLE_NETWORK + gNetworkStart = Network::Mode::server; +#endif + + std::unique_ptr context(CreateContext()); + if (context->Initialise()) + { + if (!context->LoadParkFromFile(inputPath)) + { + return EXITCODE_FAIL; + } + + Console::WriteLine("Running %d ticks...", ticks); + for (int32_t i = 0; i < ticks; i++) + { + gameStateUpdateLogic(); + } + Console::WriteLine("Completed: %s", getGameState().entities.GetAllEntitiesChecksum().ToString().c_str()); + } + else + { + Console::Error::WriteLine("Context initialization failed."); + return EXITCODE_FAIL; + } + + return EXITCODE_OK; + } +} // namespace OpenRCT2 diff --git a/src/openrct2/command_line/SpriteCommands.cpp b/src/openrct2/command_line/SpriteCommands.cpp index dae0d6779c..ce7d4a7818 100644 --- a/src/openrct2/command_line/SpriteCommands.cpp +++ b/src/openrct2/command_line/SpriteCommands.cpp @@ -12,57 +12,58 @@ #include "../core/String.hpp" #include "CommandLine.hpp" -using namespace OpenRCT2; - #define SZ_DEFAULT "default" #define SZ_CLOSEST "closest" #define SZ_DITHERING "dithering" -using ImportMode = OpenRCT2::Drawing::ImportMode; - -ImportMode gSpriteMode = ImportMode::Default; - -static const char* _mode; - -// clang-format off -static constexpr CommandLineOptionDefinition kSpriteOptions[] +namespace OpenRCT2 { - { CMDLINE_TYPE_STRING, &_mode, 'm', "mode", "the type of sprite conversion <" SZ_DEFAULT "|" SZ_CLOSEST "|" SZ_DITHERING ">" }, - kOptionTableEnd -}; + using ImportMode = Drawing::ImportMode; -static exitcode_t HandleSprite(CommandLineArgEnumerator *argEnumerator); + ImportMode gSpriteMode = ImportMode::Default; -const CommandLineCommand CommandLine::kSpriteCommands[] -{ - // Main commands - DefineCommand("append", " [x_offset y_offset]", kSpriteOptions, HandleSprite), - DefineCommand("build", " [silent]", kSpriteOptions, HandleSprite), - DefineCommand("combine", " ", kSpriteOptions, HandleSprite), - DefineCommand("create", "", kSpriteOptions, HandleSprite), - DefineCommand("details", " [idx]", kSpriteOptions, HandleSprite), - DefineCommand("export", " ", kSpriteOptions, HandleSprite), - DefineCommand("exportall", " ", kSpriteOptions, HandleSprite), - DefineCommand("exportalldat", " ", kSpriteOptions, HandleSprite), + static const char* _mode; - kCommandTableEnd -}; -// clang-format on - -static exitcode_t HandleSprite(CommandLineArgEnumerator* argEnumerator) -{ - if (String::iequals(_mode, SZ_CLOSEST)) - gSpriteMode = ImportMode::Closest; - else if (String::iequals(_mode, SZ_DITHERING)) - gSpriteMode = ImportMode::Dithering; - Memory::Free(_mode); - - const char** argv = const_cast(argEnumerator->GetArguments()) + argEnumerator->GetIndex() - 1; - int32_t argc = argEnumerator->GetCount() - argEnumerator->GetIndex() + 1; - int32_t result = CommandLineForSprite(argv, argc); - if (result < 0) + // clang-format off + static constexpr CommandLineOptionDefinition kSpriteOptions[] { - return EXITCODE_FAIL; + { CMDLINE_TYPE_STRING, &_mode, 'm', "mode", "the type of sprite conversion <" SZ_DEFAULT "|" SZ_CLOSEST "|" SZ_DITHERING ">" }, + kOptionTableEnd + }; + + static exitcode_t HandleSprite(CommandLineArgEnumerator *argEnumerator); + + const CommandLineCommand CommandLine::kSpriteCommands[] + { + // Main commands + DefineCommand("append", " [x_offset y_offset]", kSpriteOptions, HandleSprite), + DefineCommand("build", " [silent]", kSpriteOptions, HandleSprite), + DefineCommand("combine", " ", kSpriteOptions, HandleSprite), + DefineCommand("create", "", kSpriteOptions, HandleSprite), + DefineCommand("details", " [idx]", kSpriteOptions, HandleSprite), + DefineCommand("export", " ", kSpriteOptions, HandleSprite), + DefineCommand("exportall", " ", kSpriteOptions, HandleSprite), + DefineCommand("exportalldat", " ", kSpriteOptions, HandleSprite), + + kCommandTableEnd + }; + // clang-format on + + static exitcode_t HandleSprite(CommandLineArgEnumerator* argEnumerator) + { + if (String::iequals(_mode, SZ_CLOSEST)) + gSpriteMode = ImportMode::Closest; + else if (String::iequals(_mode, SZ_DITHERING)) + gSpriteMode = ImportMode::Dithering; + Memory::Free(_mode); + + const char** argv = const_cast(argEnumerator->GetArguments()) + argEnumerator->GetIndex() - 1; + int32_t argc = argEnumerator->GetCount() - argEnumerator->GetIndex() + 1; + int32_t result = CommandLineForSprite(argv, argc); + if (result < 0) + { + return EXITCODE_FAIL; + } + return EXITCODE_OK; } - return EXITCODE_OK; -} +} // namespace OpenRCT2 diff --git a/src/openrct2/command_line/UriHandler.cpp b/src/openrct2/command_line/UriHandler.cpp index f32b5cf6f9..76809d5fab 100644 --- a/src/openrct2/command_line/UriHandler.cpp +++ b/src/openrct2/command_line/UriHandler.cpp @@ -13,90 +13,91 @@ #include "../network/Network.h" #include "CommandLine.hpp" -using namespace OpenRCT2; - -static exitcode_t HandleUri(const std::string& uri); +namespace OpenRCT2 +{ + static exitcode_t HandleUri(const std::string& uri); #ifndef DISABLE_NETWORK -static exitcode_t HandleUriJoin(const std::vector& args); -static bool TryParseHostnamePort( - const std::string& hostnamePort, std::string* outHostname, int32_t* outPort, int32_t defaultPort); + static exitcode_t HandleUriJoin(const std::vector& args); + static bool TryParseHostnamePort( + const std::string& hostnamePort, std::string* outHostname, int32_t* outPort, int32_t defaultPort); #endif -exitcode_t CommandLine::HandleCommandUri(CommandLineArgEnumerator* enumerator) -{ - const utf8* uri; - if (enumerator->TryPopString(&uri)) + exitcode_t CommandLine::HandleCommandUri(CommandLineArgEnumerator* enumerator) { - if (String::startsWith(uri, "openrct2://")) + const utf8* uri; + if (enumerator->TryPopString(&uri)) { - const utf8* uriCommand = uri + 11; - return HandleUri(uriCommand); + if (String::startsWith(uri, "openrct2://")) + { + const utf8* uriCommand = uri + 11; + return HandleUri(uriCommand); + } } + + Console::Error::WriteLine("Invalid URI"); + return EXITCODE_FAIL; } - Console::Error::WriteLine("Invalid URI"); - return EXITCODE_FAIL; -} - -static exitcode_t HandleUri(const std::string& uri) -{ - exitcode_t result = EXITCODE_CONTINUE; - auto args = String::split(uri, "/"); - if (!args.empty()) + static exitcode_t HandleUri(const std::string& uri) { -#ifndef DISABLE_NETWORK - std::string arg = args[0]; - if (arg == "join") + exitcode_t result = EXITCODE_CONTINUE; + auto args = String::split(uri, "/"); + if (!args.empty()) { - result = HandleUriJoin(args); - } +#ifndef DISABLE_NETWORK + std::string arg = args[0]; + if (arg == "join") + { + result = HandleUriJoin(args); + } #endif + } + return result; } - return result; -} #ifndef DISABLE_NETWORK -static exitcode_t HandleUriJoin(const std::vector& args) -{ - std::string hostname; - int32_t port; - if (args.size() > 1 && TryParseHostnamePort(args[1], &hostname, &port, Network::kDefaultPort)) + static exitcode_t HandleUriJoin(const std::vector& args) { - // Set the network start configuration - gNetworkStart = Network::Mode::client; - gNetworkStartHost = std::move(hostname); - gNetworkStartPort = port; - return EXITCODE_CONTINUE; - } - - Console::Error::WriteLine("Expected hostname:port after join"); - return EXITCODE_FAIL; -} - -static bool TryParseHostnamePort( - const std::string& hostnamePort, std::string* outHostname, int32_t* outPort, int32_t defaultPort) -{ - try - { - // Argument is in hostname:port format, so we need to split - std::string hostname = hostnamePort; - int32_t port = defaultPort; - size_t colonIndex = hostnamePort.find_first_of(':'); - if (colonIndex != std::string::npos) + std::string hostname; + int32_t port; + if (args.size() > 1 && TryParseHostnamePort(args[1], &hostname, &port, Network::kDefaultPort)) { - hostname = hostnamePort.substr(0, colonIndex); - port = std::stoi(hostnamePort.substr(colonIndex + 1)); + // Set the network start configuration + gNetworkStart = Network::Mode::client; + gNetworkStartHost = std::move(hostname); + gNetworkStartPort = port; + return EXITCODE_CONTINUE; } - *outPort = port; - *outHostname = std::move(hostname); - return true; + + Console::Error::WriteLine("Expected hostname:port after join"); + return EXITCODE_FAIL; } - catch (const std::exception&) + + static bool TryParseHostnamePort( + const std::string& hostnamePort, std::string* outHostname, int32_t* outPort, int32_t defaultPort) { - return false; + try + { + // Argument is in hostname:port format, so we need to split + std::string hostname = hostnamePort; + int32_t port = defaultPort; + size_t colonIndex = hostnamePort.find_first_of(':'); + if (colonIndex != std::string::npos) + { + hostname = hostnamePort.substr(0, colonIndex); + port = std::stoi(hostnamePort.substr(colonIndex + 1)); + } + *outPort = port; + *outHostname = std::move(hostname); + return true; + } + catch (const std::exception&) + { + return false; + } } -} #endif // DISABLE_NETWORK +} // namespace OpenRCT2