mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2026-03-22 10:03:35 -04:00
Yellow squiggly lines begone! Done automatically on .cpp files through `run-clang-tidy`, with manual corrections to the mistakes. If an import is directly used, but is technically unnecessary since it's recursively imported by something else, it is *not* removed. The tool doesn't touch .h files, so I did some of them by hand while fixing errors due to old recursive imports. Not everything is removed, but the cleanup should be substantial enough. Because this done on Linux, code that isn't used on it is mostly untouched. (Hopefully no open PR is depending on these imports...)
273 lines
6.6 KiB
C++
273 lines
6.6 KiB
C++
// Copyright 2010 Dolphin Emulator Project
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
#include "Core/GeckoCodeConfig.h"
|
|
|
|
#include <optional>
|
|
#include <sstream>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "Common/HttpRequest.h"
|
|
#include "Common/IniFile.h"
|
|
#include "Common/StringUtil.h"
|
|
#include "Core/CheatCodes.h"
|
|
|
|
namespace Gecko
|
|
{
|
|
std::vector<GeckoCode> DownloadCodes(std::string gametdb_id, bool* succeeded)
|
|
{
|
|
// codes.rc24.xyz is a mirror of the now defunct geckocodes.org.
|
|
std::string endpoint{"https://codes.rc24.xyz/txt.php?txt=" + gametdb_id};
|
|
Common::HttpRequest http;
|
|
|
|
// The server always redirects once to the same location.
|
|
http.FollowRedirects(1);
|
|
|
|
const Common::HttpRequest::Response response = http.Get(endpoint);
|
|
*succeeded = response.has_value();
|
|
if (!response)
|
|
return {};
|
|
|
|
// temp vector containing parsed codes
|
|
std::vector<GeckoCode> gcodes;
|
|
|
|
// parse the codes
|
|
std::istringstream ss(std::string(response->begin(), response->end()));
|
|
|
|
std::string line;
|
|
|
|
// seek past the header, get to the first code
|
|
std::getline(ss, line);
|
|
std::getline(ss, line);
|
|
std::getline(ss, line);
|
|
|
|
int read_state = 0;
|
|
GeckoCode gcode;
|
|
|
|
while ((std::getline(ss, line).good()))
|
|
{
|
|
// Remove \r at the end of the line for files using windows line endings, std::getline only
|
|
// removes \n
|
|
line = StripWhitespace(line);
|
|
|
|
if (line.empty())
|
|
{
|
|
// add the code
|
|
if (!gcode.codes.empty())
|
|
gcodes.push_back(gcode);
|
|
gcode = GeckoCode();
|
|
read_state = 0;
|
|
continue;
|
|
}
|
|
|
|
switch (read_state)
|
|
{
|
|
// read new code
|
|
case 0:
|
|
{
|
|
std::istringstream ssline(line);
|
|
// stop at [ character (beginning of contributor name)
|
|
std::getline(ssline, gcode.name, '[');
|
|
gcode.name = StripWhitespace(gcode.name);
|
|
gcode.user_defined = true;
|
|
// read the code creator name
|
|
std::getline(ssline, gcode.creator, ']');
|
|
read_state = 1;
|
|
}
|
|
break;
|
|
|
|
// read code lines
|
|
case 1:
|
|
{
|
|
std::istringstream ssline(line);
|
|
std::string addr, data;
|
|
|
|
// Some locales (e.g. fr_FR.UTF-8) don't split the string stream on space
|
|
// Use the C locale to workaround this behavior
|
|
ssline.imbue(std::locale::classic());
|
|
|
|
ssline >> addr >> data;
|
|
ssline.seekg(0);
|
|
|
|
// check if this line a code, silly, but the dumb txt file comment lines can start with
|
|
// valid hex chars :/
|
|
if (8 == addr.length() && 8 == data.length())
|
|
{
|
|
GeckoCode::Code new_code;
|
|
new_code.original_line = line;
|
|
ssline >> std::hex >> new_code.address >> new_code.data;
|
|
gcode.codes.push_back(new_code);
|
|
}
|
|
else
|
|
{
|
|
gcode.notes.push_back(line);
|
|
read_state = 2; // start reading comments
|
|
}
|
|
}
|
|
break;
|
|
|
|
// read comment lines
|
|
case 2:
|
|
// append comment line
|
|
gcode.notes.push_back(line);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// add the last code
|
|
if (!gcode.codes.empty())
|
|
gcodes.push_back(gcode);
|
|
|
|
return gcodes;
|
|
}
|
|
|
|
std::vector<GeckoCode> LoadCodes(const Common::IniFile& globalIni, const Common::IniFile& localIni)
|
|
{
|
|
std::vector<GeckoCode> gcodes;
|
|
|
|
for (const auto* ini : {&globalIni, &localIni})
|
|
{
|
|
std::vector<std::string> lines;
|
|
ini->GetLines("Gecko", &lines, false);
|
|
|
|
GeckoCode gcode;
|
|
|
|
std::erase_if(lines, [](const auto& line) { return line.empty() || line[0] == '#'; });
|
|
|
|
for (auto& line : lines)
|
|
{
|
|
std::istringstream ss(line);
|
|
|
|
// Some locales (e.g. fr_FR.UTF-8) don't split the string stream on space
|
|
// Use the C locale to workaround this behavior
|
|
ss.imbue(std::locale::classic());
|
|
|
|
switch ((line)[0])
|
|
{
|
|
// enabled or disabled code
|
|
case '+':
|
|
ss.seekg(1);
|
|
[[fallthrough]];
|
|
case '$':
|
|
if (!gcode.name.empty())
|
|
gcodes.push_back(gcode);
|
|
gcode = GeckoCode();
|
|
gcode.enabled = (1 == ss.tellg()); // silly
|
|
gcode.user_defined = (ini == &localIni);
|
|
ss.seekg(1, std::ios_base::cur);
|
|
// read the code name
|
|
std::getline(ss, gcode.name, '['); // stop at [ character (beginning of contributor name)
|
|
gcode.name = StripWhitespace(gcode.name);
|
|
// read the code creator name
|
|
std::getline(ss, gcode.creator, ']');
|
|
break;
|
|
|
|
// notes
|
|
case '*':
|
|
gcode.notes.push_back(std::string(++line.begin(), line.end()));
|
|
break;
|
|
|
|
// either part of the code, or an option choice
|
|
default:
|
|
{
|
|
GeckoCode::Code new_code;
|
|
// TODO: support options
|
|
if (std::optional<GeckoCode::Code> code = DeserializeLine(line))
|
|
new_code = *code;
|
|
else
|
|
new_code.original_line = line;
|
|
gcode.codes.push_back(new_code);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// add the last code
|
|
if (!gcode.name.empty())
|
|
{
|
|
gcodes.push_back(gcode);
|
|
}
|
|
|
|
ReadEnabledAndDisabled(*ini, "Gecko", &gcodes);
|
|
|
|
if (ini == &globalIni)
|
|
{
|
|
for (GeckoCode& code : gcodes)
|
|
code.default_enabled = code.enabled;
|
|
}
|
|
}
|
|
|
|
return gcodes;
|
|
}
|
|
|
|
static std::string MakeGeckoCodeTitle(const GeckoCode& code)
|
|
{
|
|
std::string title = '$' + code.name;
|
|
|
|
if (!code.creator.empty())
|
|
{
|
|
title += " [" + code.creator + ']';
|
|
}
|
|
|
|
return title;
|
|
}
|
|
|
|
// used by the SaveGeckoCodes function
|
|
static void SaveGeckoCode(std::vector<std::string>& lines, const GeckoCode& gcode)
|
|
{
|
|
if (!gcode.user_defined)
|
|
return;
|
|
|
|
lines.push_back(MakeGeckoCodeTitle(gcode));
|
|
|
|
// save all the code lines
|
|
for (const GeckoCode::Code& code : gcode.codes)
|
|
{
|
|
lines.push_back(code.original_line);
|
|
}
|
|
|
|
// save the notes
|
|
for (const std::string& note : gcode.notes)
|
|
lines.push_back('*' + note);
|
|
}
|
|
|
|
void SaveCodes(Common::IniFile& inifile, const std::vector<GeckoCode>& gcodes)
|
|
{
|
|
std::vector<std::string> lines;
|
|
std::vector<std::string> enabled_lines;
|
|
std::vector<std::string> disabled_lines;
|
|
|
|
for (const GeckoCode& geckoCode : gcodes)
|
|
{
|
|
if (geckoCode.enabled != geckoCode.default_enabled)
|
|
(geckoCode.enabled ? enabled_lines : disabled_lines).emplace_back('$' + geckoCode.name);
|
|
|
|
SaveGeckoCode(lines, geckoCode);
|
|
}
|
|
|
|
inifile.SetLines("Gecko", lines);
|
|
inifile.SetLines("Gecko_Enabled", enabled_lines);
|
|
inifile.SetLines("Gecko_Disabled", disabled_lines);
|
|
}
|
|
|
|
std::optional<GeckoCode::Code> DeserializeLine(const std::string& line)
|
|
{
|
|
std::vector<std::string> items = SplitString(line, ' ');
|
|
|
|
GeckoCode::Code code;
|
|
code.original_line = line;
|
|
|
|
if (items.size() < 2)
|
|
return std::nullopt;
|
|
|
|
if (!TryParse(items[0], &code.address, 16))
|
|
return std::nullopt;
|
|
if (!TryParse(items[1], &code.data, 16))
|
|
return std::nullopt;
|
|
|
|
return code;
|
|
}
|
|
|
|
} // namespace Gecko
|