mirror of
https://github.com/flyinghead/flycast.git
synced 2026-11-10 10:01:28 -10:00
Game ids can have spaces, like "INITIAL D Ver.2". The parser removed all spaces in the section name so it became "INITIALDVer.2". Now only the spaces before and after the name are removed. Co-authored-by: nixxou <[email protected]> [ci skip]
227 lines
5.0 KiB
C++
227 lines
5.0 KiB
C++
/*
|
|
Copyright 2025 flyinghead
|
|
|
|
This file is part of Flycast.
|
|
|
|
Flycast is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
Flycast is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
#include "cfg/cfg.h"
|
|
#include "stdclass.h"
|
|
#include <cstdio>
|
|
#include <cstring>
|
|
|
|
namespace config
|
|
{
|
|
|
|
static void usage(const char *exe)
|
|
{
|
|
fprintf(stderr, "Usage: %s [option]... [<rom path>]\n", exe);
|
|
fprintf(stderr, "Options:\n");
|
|
fprintf(stderr, "-config section:key=value,... set a transient config value.\n");
|
|
fprintf(stderr, " Transient config values won't be saved to emu.cfg.\n");
|
|
fprintf(stderr, "-help display this help\n");
|
|
}
|
|
|
|
static void parseConfigOption(const std::string& str)
|
|
{
|
|
char inQuote = '\0';
|
|
std::string section, key, value;
|
|
int step = 0; // section, key, value
|
|
for (char c : str)
|
|
{
|
|
if (inQuote != '\0' && c == inQuote)
|
|
{
|
|
inQuote = false;
|
|
step = 0;
|
|
setTransient(section, key, value);
|
|
DEBUG_LOG(COMMON, "-config [%s] %s = %s", section.c_str(), key.c_str(), value.c_str());
|
|
section.clear();
|
|
key.clear();
|
|
value.clear();
|
|
continue;
|
|
}
|
|
switch (c)
|
|
{
|
|
case ':':
|
|
switch (step)
|
|
{
|
|
case 0:
|
|
section = trim_trailing_ws(section);
|
|
if (section.empty()) {
|
|
WARN_LOG(COMMON, "Invalid -config option '%s'. Format is: -config section:key=value,...", str.c_str());
|
|
return;
|
|
}
|
|
step = 1;
|
|
break;
|
|
case 1:
|
|
key += c;
|
|
break;
|
|
case 2:
|
|
value += c;
|
|
break;
|
|
}
|
|
break;
|
|
case '=':
|
|
switch (step)
|
|
{
|
|
case 0:
|
|
WARN_LOG(COMMON, "Invalid -config option '%s'. Format is: -config section:key=value,...", str.c_str());
|
|
return;
|
|
case 1:
|
|
key = trim_trailing_ws(key);
|
|
if (key.empty()) {
|
|
WARN_LOG(COMMON, "Invalid -config option '%s'. Format is: -config section:key=value,...", str.c_str());
|
|
return;
|
|
}
|
|
step = 2;
|
|
break;
|
|
case 2:
|
|
value += c;
|
|
break;
|
|
}
|
|
break;
|
|
case '\'':
|
|
case '"':
|
|
switch (step)
|
|
{
|
|
case 0:
|
|
section += c;
|
|
break;
|
|
case 1:
|
|
key += c;
|
|
break;
|
|
case 2:
|
|
if (inQuote == '\0') {
|
|
inQuote = c;
|
|
value.clear();
|
|
}
|
|
else
|
|
value += c;
|
|
break;
|
|
}
|
|
break;
|
|
case ',':
|
|
switch (step)
|
|
{
|
|
case 0:
|
|
// ignore consecutive commas
|
|
break;
|
|
case 1:
|
|
key += c;
|
|
break;
|
|
case 2:
|
|
if (inQuote != '\0') {
|
|
value += c;
|
|
}
|
|
else
|
|
{
|
|
step = 0;
|
|
setTransient(section, key, value);
|
|
DEBUG_LOG(COMMON, "-config [%s] %s = %s", section.c_str(), key.c_str(), value.c_str());
|
|
section.clear();
|
|
key.clear();
|
|
value.clear();
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
case ' ':
|
|
switch (step)
|
|
{
|
|
case 0:
|
|
// Ignore leading spaces. Inner spaces are kept (game ids such as "INITIAL D Ver.2")
|
|
// and trailing ones are trimmed at the ':' and '=' separators
|
|
if (!section.empty())
|
|
section += c;
|
|
break;
|
|
case 1:
|
|
if (!key.empty())
|
|
key += c;
|
|
break;
|
|
case 2:
|
|
value += c;
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
switch (step)
|
|
{
|
|
case 0:
|
|
section += c;
|
|
break;
|
|
case 1:
|
|
key += c;
|
|
break;
|
|
case 2:
|
|
value += c;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (step == 2) {
|
|
setTransient(section, key, value);
|
|
DEBUG_LOG(COMMON, "-config [%s] %s = %s", section.c_str(), key.c_str(), value.c_str());
|
|
}
|
|
}
|
|
|
|
void parseCommandLine(int argc, const char * const argv[])
|
|
{
|
|
settings.content.path.clear();
|
|
const char *exe = argv[0];
|
|
for (int i = 1; i < argc; i++)
|
|
{
|
|
if (!strcmp(argv[i], "-help") || !strcmp(argv[i], "--help")) {
|
|
usage(exe);
|
|
exit(0);
|
|
}
|
|
if (!strcmp(argv[i], "-config") || !strcmp(argv[i], "--config"))
|
|
{
|
|
if (i < argc - 1)
|
|
parseConfigOption(argv[++i]);
|
|
continue;
|
|
}
|
|
// macOS
|
|
if (!strncmp(argv[i], "-NSDocumentRevisions", 20)) {
|
|
i++;
|
|
continue;
|
|
}
|
|
if (argv[i][0] == '-') {
|
|
WARN_LOG(COMMON, "Ignoring unknown command line option '%s'", argv[i]);
|
|
continue;
|
|
}
|
|
std::string extension = get_file_extension(argv[i]);
|
|
if (extension == "cdi" || extension == "chd"
|
|
|| extension == "gdi"|| extension == "cue")
|
|
{
|
|
INFO_LOG(COMMON, "Using '%s' as CD image", argv[i]);
|
|
settings.content.path = argv[i];
|
|
}
|
|
else if (extension == "elf")
|
|
{
|
|
INFO_LOG(COMMON, "Using '%s' as reios elf file", argv[i]);
|
|
setTransient("config", "bios.UseReios", "yes");
|
|
settings.content.path = argv[i];
|
|
}
|
|
else {
|
|
INFO_LOG(COMMON, "Using '%s' as rom", argv[i]);
|
|
settings.content.path = argv[i];
|
|
}
|
|
if (i < argc - 1)
|
|
WARN_LOG(COMMON, "Rest of command line ignored: '%s'...", argv[i + 1]);
|
|
break;
|
|
}
|
|
}
|
|
|
|
} // namespace config
|