1
0
mirror of https://github.com/OpenRCT2/OpenRCT2 synced 2025-12-23 15:52:55 +01:00

new-argparse: add basic definitions and help display

This commit is contained in:
IntelOrca
2016-01-10 14:54:33 +00:00
parent 8997f2aa6a
commit 14a266c177
8 changed files with 585 additions and 2 deletions

274
src/cmdline/CommandLine.cpp Normal file
View File

@@ -0,0 +1,274 @@
extern "C"
{
#include "../platform/platform.h"
}
#include "../core/Console.hpp"
#include "../core/Math.hpp"
#include "../core/String.hpp"
#include "CommandLine.hpp"
CommandLineArgEnumerator::CommandLineArgEnumerator(const char * const * arguments, int count)
{
_arguments = arguments;
_count = count;
_index = 0;
}
void CommandLineArgEnumerator::Reset()
{
_index = 0;
}
bool CommandLineArgEnumerator::TryPop()
{
if (_index < _count)
{
_index++;
return true;
}
else
{
return false;
}
}
bool CommandLineArgEnumerator::TryPopInteger(sint32 * result)
{
char const * arg;
if (TryPopString(&arg))
{
*result = (sint32)atol(arg);
return true;
}
return false;
}
bool CommandLineArgEnumerator::TryPopReal(float * result)
{
char const * arg;
if (TryPopString(&arg))
{
*result = (float)atof(arg);
return true;
}
return false;
}
bool CommandLineArgEnumerator::TryPopString(const char * * result)
{
if (_index < _count)
{
*result = _arguments[_index];
_index++;
return true;
}
else
{
return false;
}
}
namespace CommandLine
{
constexpr const char * HelpText = "openrct2 -ha shows help for all commands. "
"openrct2 <command> -h will show help and details for a given command.";
static void PrintOptions(const CommandLineOptionDefinition *options);
static void PrintExamples(const CommandLineExample *examples);
static utf8 * GetOptionCaption(utf8 * buffer, size_t bufferSize, const CommandLineOptionDefinition *option);
void PrintHelp()
{
const CommandLineCommand * command;
size_t maxNameLength = 0;
size_t maxParamsLength = 0;
// Get the largest command name length and parameter length
for (command = RootCommands; command->Name != nullptr; command++)
{
maxNameLength = Math::Max(maxNameLength, String::LengthOf(command->Name));
maxParamsLength = Math::Max(maxParamsLength, String::LengthOf(command->Parameters));
}
// Print usage / main commands
const char * usageString = "usage: openrct2 ";
const size_t usageStringLength = String::LengthOf(usageString);
Console::Write(usageString);
for (command = RootCommands; command->Name != nullptr; command++)
{
if (command != RootCommands)
{
Console::WriteSpace(usageStringLength);
}
Console::Write(command->Name);
Console::WriteSpace(maxNameLength - String::LengthOf(command->Name) + 1);
if (command->SubCommands == nullptr)
{
Console::Write(command->Parameters);
Console::WriteSpace(maxParamsLength - String::LengthOf(command->Parameters));
if (command->Options != nullptr)
{
Console::Write(" [options]");
}
}
else
{
Console::Write("...");
}
Console::WriteLine();
}
Console::WriteLine();
PrintOptions(RootCommands->Options);
PrintExamples(RootExamples);
Console::WriteLine(HelpText);
}
static void PrintOptions(const CommandLineOptionDefinition *options)
{
// Print options for main commands
size_t maxOptionLength = 0;
const CommandLineOptionDefinition * option = options;
for (; option->Type != 255; option++)
{
char buffer[128];
GetOptionCaption(buffer, sizeof(buffer), option);
size_t optionCaptionLength = String::LengthOf(buffer);
maxOptionLength = Math::Max(maxOptionLength, optionCaptionLength);
}
option = RootCommands->Options;
for (; option->Type != 255; option++)
{
Console::WriteSpace(4);
char buffer[128];
GetOptionCaption(buffer, sizeof(buffer), option);
size_t optionCaptionLength = String::LengthOf(buffer);
Console::Write(buffer);
Console::WriteSpace(maxOptionLength - optionCaptionLength + 4);
Console::Write(option->Description);
Console::WriteLine();
}
Console::WriteLine();
}
static void PrintExamples(const CommandLineExample *examples)
{
size_t maxArgumentsLength = 0;
const CommandLineExample * example;
for (example = examples; example->Arguments != nullptr; example++)
{
size_t argumentsLength = String::LengthOf(example->Arguments);
maxArgumentsLength = Math::Max(maxArgumentsLength, argumentsLength);
}
Console::WriteLine("examples:");
for (example = examples; example->Arguments != nullptr; example++)
{
Console::Write(" openrct2 ");
Console::Write(example->Arguments);
size_t argumentsLength = String::LengthOf(example->Arguments);
Console::WriteSpace(maxArgumentsLength - argumentsLength + 4);
Console::Write(example->Description);
Console::WriteLine();
}
Console::WriteLine();
}
static utf8 * GetOptionCaption(utf8 * buffer, size_t bufferSize, const CommandLineOptionDefinition *option)
{
buffer[0] = 0;
if (option->ShortName != '\0')
{
String::AppendFormat(buffer, bufferSize, "-%c, ", option->ShortName);
}
String::Append(buffer, bufferSize, "--");
String::Append(buffer, bufferSize, option->LongName);
switch (option->Type) {
case CMDLINE_TYPE_INTEGER:
String::Append(buffer, bufferSize, "=<int>");
break;
case CMDLINE_TYPE_REAL:
String::Append(buffer, bufferSize, "=<real>");
break;
case CMDLINE_TYPE_STRING:
String::Append(buffer, bufferSize, "=<str>");
break;
}
return buffer;
}
const CommandLineCommand * FindCommandFor(const CommandLineCommand * commands, const char * const * arguments, int count)
{
// Check if there are any arguments
if (count == 0)
{
return nullptr;
}
// Check if options have started
const char * firstArgument = arguments[0];
if (firstArgument[0] == '-')
{
return nullptr;
}
// Search through defined commands for one that matches
const CommandLineCommand * fallback = nullptr;
const CommandLineCommand * command = commands;
for (; command->Name != nullptr; command++)
{
if (command->Name[0] == '\0')
{
// If we don't find a command, this should be used
fallback = command;
}
else if (String::Equals(command->Name, firstArgument))
{
if (command->SubCommands == nullptr)
{
// Found matching command
return command;
}
else
{
// Recurse for the sub command table
return FindCommandFor(command->SubCommands, &arguments[1], count - 1);
}
}
}
return fallback;
}
}
void CommandLineDisplayUsageFor(const char * command)
{
}
extern "C"
{
int cmdline_run(const char * * argv, int argc)
{
CommandLine::PrintHelp();
return 0;
}
}

View File

@@ -0,0 +1,83 @@
#pragma once
extern "C"
{
#include "../common.h"
}
/**
* Class for enumerating and retrieving values for a set of command line arguments.
*/
class CommandLineArgEnumerator
{
private:
const char * const * _arguments;
uint16 _count;
uint16 _index;
public:
uint16 GetCount() const { return _count; }
uint16 GetIndex() const { return _index; }
CommandLineArgEnumerator(const char * const * arguments, int count);
void Reset();
bool TryPop();
bool TryPopInteger(sint32 * result);
bool TryPopReal(float * result);
bool TryPopString(const char * * result);
};
typedef int exitcode_t;
typedef exitcode_t (*CommandLineFunc)(CommandLineArgEnumerator *);
struct CommandLineExample
{
const char * Arguments;
const char * Description;
};
struct CommandLineOptionDefinition
{
uint8 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 NAC = '\0';
#define ExampleTableEnd { NULL, NULL }
#define OptionTableEnd { UINT8_MAX, NULL, NAC, NULL, NULL }
#define CommandTableEnd { NULL, NULL, NULL, NULL }
#define DefineCommand(name, params, options, func) { name, params, options, NULL, func }
#define DefineSubCommand(name, subcommandtable) { name, "", NULL, subcommandtable, NULL }
void CommandLineDisplayUsageFor(const char * command);
extern const CommandLineCommand RootCommands[];
extern const CommandLineExample RootExamples[];
namespace CommandLine
{
void PrintHelp();
}

View File

@@ -0,0 +1,104 @@
#include "CommandLine.hpp"
const CommandLineCommand * ScreenshotCommandTable;
const CommandLineCommand * SpriteCommandTable;
static bool _help;
static bool _version;
static bool _noInstall;
const CommandLineOptionDefinition StandardOptions[]
{
{ 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, nullptr, NAC, "no-install", "show information about " OPENRCT2_NAME },
{ CMDLINE_TYPE_SWITCH, nullptr, NAC, "verbose", "log verbose messages" },
{ CMDLINE_TYPE_SWITCH, nullptr, NAC, "headless", "run " OPENRCT2_NAME " headless" },
{ CMDLINE_TYPE_INTEGER, nullptr, NAC, "port", "port to use for hosting or joining a server" },
{ CMDLINE_TYPE_STRING, nullptr, NAC, "user-data-path", "path to the user data directory (containing config.ini)" },
{ CMDLINE_TYPE_STRING, nullptr, NAC, "openrct-data-path", "path to the OpenRCT2 data directory (containing languages)" },
OptionTableEnd
};
const CommandLineCommand RootCommands[]
{
// Main commands
DefineCommand("", "<uri>", StandardOptions, nullptr),
DefineCommand("intro", "", StandardOptions, nullptr),
DefineCommand("host", "<uri>", StandardOptions, nullptr),
DefineCommand("join", "<hostname>", StandardOptions, nullptr),
// Sub-commands
DefineSubCommand("screenshot", ScreenshotCommandTable),
DefineSubCommand("sprite", SpriteCommandTable ),
CommandTableEnd
};
const CommandLineExample RootExamples[]
{
{ "./my_park.sv6", "open a saved park" },
{ "./SnowyPark.sc6", "install and open a scenario" },
{ "./ShuttleLoop.td6", "install a track" },
{ "http:/openrct2.website/files/SnowyPark.sv6", "download and open a saved park" },
{ "host ./my_park.sv6 --port 11753 --headless", "run a headless server for a saved park" },
ExampleTableEnd
};
void HandleNoCommand(CommandLineArgEnumerator * enumerator)
{
}
void HandleCommandIntro(CommandLineArgEnumerator * enumerator)
{
}
void HandleCommandHost(CommandLineArgEnumerator * enumerator)
{
const char * parkUri;
if (!enumerator->TryPopString(&parkUri))
{
fprintf(stderr, "Expected path or URL to a saved park.");
CommandLineDisplayUsageFor("host");
}
}
void HandleCommandJoin(CommandLineArgEnumerator * enumerator)
{
const char * hostname;
if (!enumerator->TryPopString(&hostname))
{
fprintf(stderr, "Expected a hostname or IP address to the server to connect to.");
CommandLineDisplayUsageFor("join");
}
}
const CommandLineCommand ScreenshotCommands[]
{
// Main commands
DefineCommand("", "<file> <output_image> <width> <height> [<x> <y> <zoom> <rotation>]", nullptr, nullptr),
DefineCommand("", "<file> <output_image> giant <zoom> <rotation>", nullptr, nullptr),
CommandTableEnd
};
const CommandLineOptionDefinition SpriteOptions[]
{
{ CMDLINE_TYPE_STRING, nullptr, 'm', "mode", "the type of sprite conversion <default|simple|dithering>" },
OptionTableEnd
};
const CommandLineCommand SpriteCommands[]
{
// Main commands
DefineCommand("details", "<spritefile> [idx]", SpriteOptions, nullptr),
DefineCommand("export", "<spritefile> <idx> <output>", SpriteOptions, nullptr),
DefineCommand("build", "<spritefile> <resourcedir> [silent]", SpriteOptions, nullptr),
CommandTableEnd
};