psOff_public/tools/gamereport/entry.cpp
2024-06-02 12:28:54 +03:00

646 lines
26 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#define __APICALL_EXTERN
#include "gamereport.h"
#undef __APICALL_EXTERN
#include "config_emu.h"
#include "core/hotkeys/hotkeys.h"
#include "core/systemContent/systemContent.h"
#include "core/videoout/videoout.h"
#include "git_ver.h"
#include "logging.h"
#include "modules_include/system_param.h"
#include "nlohmann/json.hpp"
#include <SDL2/SDL.h>
#include <Windows.h>
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <boost/beast.hpp>
#include <boost/url.hpp>
#include <format>
#include <shellapi.h>
#include <unordered_map>
LOG_DEFINE_MODULE(GameReport);
namespace {
struct GitHubIssue {
int id;
std::string status;
std::string descr;
};
enum Langs : uint32_t {
ENGLISH,
RUSSIAN,
FRENCH,
GERMAN,
SPANISH,
POLISH,
LANGS_MAX
};
enum LangString : uint32_t {
BUTTONS_YES,
BUTTONS_NO,
BUTTONS_CANCEL,
BUTTONS_OK,
MAIN_TITLE,
MAIN_USER,
MAIN_CRASH,
ISSUE_TEXT,
ISSUE_STATUS_NOTHING,
ISSUE_STATUS_INTRO,
ISSUE_STATUS_INGAME,
ISSUE_STATUS_PLAYABLE,
ISSUE_KIND_AUDIO,
ISSUE_KIND_GRAPHICS,
ISSUE_KIND_INPUT,
ISSUE_KIND_SAVEDATA,
ISSUE_KIND_VIDEO,
ISSUE_KIND_MISSING_SYMBOL,
ISSUE_KIND_NVIDIA,
LOGGING_TITLE,
LOGGING_TEXT,
LANG_STR_MAX,
};
static const char* strings[LANGS_MAX][LANG_STR_MAX] = {
{
/* Buttons */
"Yes",
"No",
"Cancel",
"OK",
/* Main window */
"File a game report",
"Do you want to file a report about the game you're currently running?\n"
"If you click \"Yes\", your default browser will be opened.\n\n"
"You must have a GitHub account to create an issue!",
"We believe your emulator just crashed! Do you want to file a game report?\n"
"If you click \"Yes\", your default browser will be opened.\n\n"
"You must have a GitHub account to create an issue!",
/* Issues window */
"It looks like we already know about the issues in this game!\n\n"
"Current status: {}\nPossible issues:\n{}\n\n"
"Do you want to open a game's issue page on GitHub?\n"
"Report data will be copied to your clipboard.",
"nothing (the game don't initialize properly, not loading at all and/or crashing the emulator)",
"intro (the game display image but don't make it past the menus)",
"ingame (the game can't be either finished, have serious glitches or insufficient performance)",
"playable (the game can be finished with playable performance and no game breaking glitches)",
"* audio issues (sound is missing, choppy or played incorrectly);\n",
"* graphics issues (visual artifacts, low framerate, black screen);\n",
"* input issues (gamepad/keyboard won't work, input lag is present);\n",
"* savedata issues (the game does not save your progress or setting);\n",
"* video issues (ingame videos are decoded incorrectly or are not played at all);\n",
"* missing symbol (the game needs functions that we haven't implemented yet);\n",
"* NVIDIA specific (the game has known issues on NVIDIA cards);\n",
/* Logging warning window */
"Logging warning",
"You may not be able to provide log files for the issue you are about to open.\n"
"Your logger configuration seems to be incorrect, since \"sink\" does not equal \"FileBin\".\n"
"Creating issues without log file is not recommended.",
},
{
/* Buttons */
"Да",
"Нет",
"Отмена",
"OK",
/* Main window */
"Отправка репорта",
"Хотите отправить репорт по текущей игре?\n"
"Если вы нажмёте \"Да\", будет открыт ваш стандартный браузер.\n\n"
"Вы должны иметь аккаунт на GitHub чтобы создавать репорты!",
"Похоже, что эмулятор только что крашнулся! Хотите отправить репорт?\n"
"Если вы нажмёте \"Да\", будет открыт ваш стандартный браузер.\n\n"
"Вы должны иметь аккаунт на GitHub чтобы создавать репорты!\n",
/* Issues window */
"Похоже, что мы уже знаем о проблемах в этой игре!\n\n"
"Текущий статус: {}\nВозможные проблемы:\n{}\n\n"
"Хотите открыть страницу игры на GitHub?\n"
"Данные о вашем тесте будут скопированы в буфер обмена.",
"nothing (игра не инициализируется правильно, не загружается вообще и/или крашит эмулятор)",
"intro (игра что-то рисует на экране, но уйти дальше главного меню не выходит)",
"igname (попасть в игру возможно, но пройти её до конца нельзя из-за серьезных багов)",
"playable (игра может быть полностью завершена без каких-либо серьезных проблем)",
"* аудио (звук отстает, прерывается или не играет вообще);\n",
"* графика (артефакты, черный экран, низкий fps);\n",
"* ввод (геймпад/клавиатура не работают, или присутствует инпут-лаг);\n",
"* сохранения (the game does not save your progress or setting);\n",
"* видео (внутриигровые видео декодируются неверно или вообще не воспроизводятся);\n",
"* нереализовано (этой игре нужны пока что не реализванные эмулятором функции);\n",
"* NVIDIA-специфика (эта игра имеет проблемы, которые встречаются только на видеокартах NVIDIA);\n",
/* Logging warning window */
"Конфигурация логгера",
"Возможно вы не сможете предоставить лог-файл при создании репорта.\n"
"Ваша конфигурация логгера может быть неверна, так как \"sink\" не равен \"FileBin\".\n"
"Создание репортов без логов не рекомендуется.",
},
{
/* Buttons */
"Oui",
"Non",
"Annuler",
"D'ACCORD",
/* Main window */
"Déposer un rapport de jeu",
"Voulez-vous déposer un rapport sur le jeu que vous utilisez actuellement ?\n"
"Si vous cliquez sur \"Oui\", votre navigateur par défaut s'ouvrira.\n\n"
"Vous devez avoir un compte GitHub pour créer un ticket !",
"Nous pensons que votre émulateur vient de planter ! Voulez-vous déposer un rapport de jeu ?\n"
"Si vous cliquez sur \"Oui\", votre navigateur par défaut s'ouvrira.\n\n"
"Vous devez avoir un compte GitHub pour créer un ticket !",
/* Issues window */
"Il semble que nous soyons déjà au courant des problèmes de ce jeu !\n\n"
"État actuel : {}\nProblèmes possibles :\n{}\n\n"
"Voulez-vous ouvrir la page des problèmes d'un jeu sur GitHub ?\n"
"Report data will be copied to your clipboard.",
"nothing (le jeu ne s'initialise pas correctement, ne se charge pas du tout et/ou plante l'émulateur)",
"intro (l'image du jeu affiche mais ne dépasse pas les menus)",
"ingame (le jeu ne peut pas être terminé, avoir de sérieux problèmes ou des performances insuffisantes)",
"playable (le jeu peut être terminé avec des performances jouables et sans problème de jeu)",
"* problèmes audio (le son est manquant, saccadé ou mal joué);\n",
"* problèmes graphiques (artefacts visuels, faible fréquence d'images, écran noir);\n",
"* problèmes de saisie (la manette de jeu/le clavier ne fonctionneront pas, un décalage d'entrée est présent);\n",
"* problèmes de données de sauvegarde (le jeu n'enregistre pas votre progression ou vos paramètres) ;\n",
"* problèmes vidéo (les vidéos du jeu sont mal décodées ou ne sont pas lues du tout);\n",
"* symbole manquant (le jeu a besoin de fonctions que nous n'avons pas encore implémentées);\n",
"* Spécifique à NVIDIA (le jeu présente des problèmes connus sur les cartes NVIDIA);\n",
/* Logging warning window */
"Avertissement de journalisation",
"Vous ne pourrez peut-être pas fournir de fichiers journaux pour le problème que vous êtes sur le point d'ouvrir.\n"
"La configuration de votre enregistreur semble être incorrecte, car \"sink\" n'est pas égal à \"FileBin\".\n"
"La création de problèmes sans fichier journal n'est pas recommandée.",
},
{
/* Buttons */
"Ja",
"Nein",
"Abbrechen",
"OK",
/* Main window */
"Spielbericht einreichen",
"Möchten Sie einen Bericht über das aktuelle Spiel einreichen?\n"
"Falls Sie \"Ja\" drücken, öffnet sich Ihr Browser.\n\n"
"Sie müssen einen GitHub Account besitzen, um einen Bericht einzureichen!",
"Wir glauben, dass dein Emulator abgestürzt ist! Möchten Sie einen Bericht einreichen?\n"
"Falls Sie \"Ja\" drücken, öffnet sich Ihr Browser.\n\n"
"Sie müssen einen GitHub Account besitzen, um einen Bericht einzureichen!",
/* Issues window */
"Es scheint, als wären uns die Probleme in diesem Spiel bereits bekannt!\n\n"
"Aktueller Status: {}\nMögliche Probleme:\n{}\n\n"
"Möchtest du eine Fehlerseite für ein Spiel auf GitHub öffnen?\n"
"Report data will be copied to your clipboard.",
"nothing (Das Spiel initialisiert nicht richtig, es lädt nicht und/oder lässt den Emulator abstürzen)",
"intro (Das Spiel zeigt ein Bild, kommt jedoch nicht weiter als Menüs)",
"ingame (Das Spiel kann entweder nicht abgeschlossen werden, erhebliche Fehler aufweisen oder eine unzureichende Leistung haben)",
"playable (Das Spiel kann mit einer spielbaren Leistung abgeschlossen werden und weist keine spielentscheidenden Fehler auf)",
"* Audio-Probleme (Ton fehlt, ist abgehackt oder wird falsch wiedergegeben);\n",
"* Grafikprobleme (visuelle Artefakte, niedrige Bildrate, schwarzer Bildschirm);\n",
"* Eingabeprobleme (Gamepad/Tastatur funktionieren nicht, Eingabeverzögerung liegt vor);\n",
"* Probleme mit den gespeicherten Daten (Das Spiel speichert Ihren Fortschritt oder Ihre Einstellungen nicht);\n",
"* Video-Probleme (Ingame-Videos werden falsch dekodiert oder überhaupt nicht abgespielt);\n",
"* Fehlendes Symbol (Das Spiel benötigt Funktionen, die wir noch nicht implementiert haben);\n",
"* NVIDIA-spezifisch (Das Spiel hat bekannte Probleme auf NVIDIA-Karten);\n",
/* Logging warning window */
"Protokollierungswarnung",
"Sie können möglicherweise keine Protokolldateien für das Problem bereitstellen, das Sie gerade öffnen möchten.\n"
"Ihre Logger-Konfiguration scheint inkorrekt zu sein, da \"sink\" nicht gleich \"FileBin\" ist.\n"
"Das Erstellen von Problemen ohne Protokolldatei wird nicht empfohlen.",
},
{
/* Buttons */
"",
"No",
"Cancelar",
"OK",
/* Main window */
"Abrir informe de compatibilidad",
"Desea hacer un informe de compatibilidad sobre el juego actual?\n"
"Si presionas \"\", se abrirá su navegador por defecto.\n\n"
"Necesitas una cuenta en GitHub para abrir un informe.",
"Ha habido un error fatal en psOff. Desea abrir un informe de compatibilidad?\n"
"Si presionas \"\", se abrirá su navegador por defecto.\n\n"
"Necesitas una cuenta en GitHub para abrir un informe.",
/* Issues window */
"Se ha detectado un fallo en la emulación!\n\n"
"Estado actual: {}\nPosibles fallos:\n{}\n\n"
"Deseas abrir el reporte de compatibilidad del juego?\n"
"Los datos de reporte se copiarán a tu portapapeles.",
"nothing (el juego no carga y/o se cuelga el emulador)",
"intro (el juego produce imagen pero no funciona más allá del menú)",
"ingame (el juego no se puede completar, es inestable o posee un bajo rendimiento)",
"playable (el juego se puede completar con buen rendimiento y ningún fallo)",
"* Problemas de sonido (no hay sonido, está distorsionado o no se reproduce correctamente);\n",
"* Problemas gráficos (el juego posee defectos visuales, velocidad inestable o pantalla negra);\n",
"* Problemas de entrada (no funciona el mando/teclado, retraso de entrada);\n",
"* Problemas de guardado (el juego no es capaz de guardar la partida);\n",
"* Problemas de vídeo (las cinemáticas son reproducidas incorrectamente o no son reproducidas);\n",
"* Símbolo ausente (el juego necesita símbolos los cuáles no se han implementado aún);\n",
"* Problemas con tarjetas NVIDIA (el juego tiene problemas en tarjetas gráficas NVIDIA);\n",
/* Logging warning window */
"Aviso de registro",
"Puede que no sea capaz de proporcionar archivos de registro para el informe que está por abrir.\n"
"Su configuración de registro parece ser incorrecta, dado que \"sink\" no se ha configurado a \"FileBin\".\n"
"Reportar fallos sin un registro no es recomendable.",
},
{
/* Buttons */
"Tak",
"Nie",
"Anuluj",
"OK",
/* Main window */
"Zgłoś raport gry",
"Czy chcesz zgłosić raport gry, która jest teraz uruchomiona?\n"
"Jeśli klikniesz \"Tak\", zostanie otwarta domyślna przeglądarka.\n\n"
"Musisz posiadać konto GitHub, żeby stworzyć raport!",
"Wydaje się że emulator właśnie uległ awarii! Chcesz zgłosić raport gry?\n"
"Jeśli klikniesz \"Yes\", zostanie otwarta domyślna przeglądarka.\n\n"
"Musisz posiadać konto GitHub, żeby stworzyć raport!",
/* Issues window */
"Wydaje się, że już znamy problemy z tą grą!\n\n"
"Obecny status: {}\nMożliwe problemy:\n{}\n\n"
"Czy chcesz otworzyć zgłoszony raport gry na Github?\n"
"Report data will be copied to your clipboard.",
"nic (gra nie uruchamia się poprawnie, w ogóle się nie wczytuje i/lub powoduje awarię emulatora)",
"intro (gra wyświetla obraz, ale nie przechodzi dalej niż menu)",
"w grze (gra nie może zostać ukończona, ma poważne błędy lub niewystarczającą wydajność)",
"grywalne (gra może być ukończona z akceptowalną wydajnością i bez poważnych błędów uniemożliwiających rozgrywkę)",
"* problemy dźwiękowe (brak dźwięku, przerywanie dźwięku lub nieprawidłowe odtwarzanie);\n",
"* problemy graficzne (artefakty wizualne, niska wydajność, czarny ekran);\n",
"* problemy z kontrolą (kontroler/klawiatura może nie działać, występuje opóźnienie);\n",
"* problemy z zapisanymi danymi (gra nie zapisuje postępu gry czy ustawień);\n",
"* problemy wideo (wideo w grze jest nieprawidłowo dekodowane lub w ogóle nie jest odtwarzane);\n",
"* brakujący symbol (gra potrzebuje funkcji, których jeszcze nie zaimplementowaliśmy);\n",
"* specyficzne dla NVIDIA (gra ma znane problemy na kartach graficznych NVIDIA);\n",
/* Logging warning window */
"Ostrzeżenie o rejestrowaniu",
"Możesz nie być w stanie dostarczyć plików z rejestracji dla problemu, który chcesz zgłosić.\n"
"Twoja konfiguracja rejestratora jest błędna, gdyż \"sink\" nie jest równy \"FileBin\".\n"
"Tworzenie problemów bez pliku od rejestratora nie jest zalecane.",
},
};
static const char* get_lang_string(SystemParamLang lang_id, LangString str_id) {
const char* out = nullptr;
switch (lang_id) {
case SystemParamLang::Russian: out = strings[Langs::RUSSIAN][str_id]; break;
case SystemParamLang::French:
case SystemParamLang::FrenchCA: out = strings[Langs::FRENCH][str_id]; break;
case SystemParamLang::Spanish: out = strings[Langs::SPANISH][str_id]; break;
case SystemParamLang::German: out = strings[Langs::GERMAN][str_id]; break;
case SystemParamLang::Polish: out = strings[Langs::POLISH][str_id]; break;
default: out = strings[Langs::ENGLISH][str_id];
}
if (out == nullptr && (out = strings[Langs::ENGLISH][str_id]) == nullptr) out = "UNKNOWN_LANGUAGE_ID";
return out;
}
static std::unordered_map<std::string, LangString> descrs = {
{"audio", LangString::ISSUE_KIND_AUDIO},
{"graphics", LangString::ISSUE_KIND_GRAPHICS},
{"input", LangString::ISSUE_KIND_INPUT},
{"savedata", LangString::ISSUE_KIND_SAVEDATA},
{"video", LangString::ISSUE_KIND_VIDEO},
{"missing-symbol", LangString::ISSUE_KIND_MISSING_SYMBOL},
{"nvidia-specific", LangString::ISSUE_KIND_NVIDIA},
};
static std::unordered_map<std::string, LangString> statuses = {
{"status-nothing", LangString::ISSUE_STATUS_NOTHING},
{"status-intro", LangString::ISSUE_STATUS_INTRO},
{"status-ingame", LangString::ISSUE_STATUS_INGAME},
{"status-playable", LangString::ISSUE_STATUS_PLAYABLE},
};
static const char* retrieve_label_description(SystemParamLang lang_id, std::string& label) {
auto it = descrs.find(label);
if (it != descrs.end()) return get_lang_string(lang_id, it->second);
return nullptr;
}
static const char* retrieve_label_status(SystemParamLang lang_id, std::string& label) {
auto it = statuses.find(label);
if (it != statuses.end()) return get_lang_string(lang_id, it->second);
return nullptr;
};
static int find_issue(SystemParamLang lang_id, const char* title_id, GitHubIssue* issue) {
LOG_USE_MODULE(GameReport);
using namespace boost::asio;
using namespace boost::beast;
using json = nlohmann::json;
boost::urls::url link("https://api.github.com/search/issues");
auto params = link.params();
params.append({"q", std::format("repo:{} is:open is:issue {} in:title", GAMEREPORT_REPO_NAME, title_id)});
link.normalize();
io_service svc;
ssl::context ctx(ssl::context::sslv23_client);
ssl::stream<ip::tcp::socket> sock = {svc, ctx};
try {
ip::tcp::resolver resolver(svc);
ip::tcp::resolver::query query(ip::tcp::v4(), link.host(), link.scheme());
boost::asio::connect(sock.lowest_layer(), resolver.resolve(query));
sock.handshake(ssl::stream_base::handshake_type::client);
} catch (boost::system::system_error& ex) {
LOG_ERR(L"%S", ex.what());
return -1;
}
http::request<http::empty_body> req {http::verb::get, link, 11};
req.set(http::field::host, link.host());
req.set(http::field::user_agent, "psOff-http/1.0");
req.set(http::field::accept, "application/vnd.github+json");
req.set("X-GitHub-Api-Version", "2022-11-28");
// req.set(http::field::authorization, "Bearer @GIT_TOKEN@");
http::write(sock, req);
http::response<http::string_body> res;
flat_buffer buf;
http::read(sock, buf, res);
if (res[http::field::content_type].contains("application/json") && res.result_int() == 200) {
try {
json jresp = json::parse(res.body());
if (jresp["total_count"] < 1) return -1;
auto& jissue = jresp["items"][0];
auto& jlabels = jissue["labels"];
std::string tempstr;
for (auto it: jlabels) {
it["name"].get_to(tempstr);
if (auto gstat = retrieve_label_status(lang_id, tempstr)) issue->status = gstat;
if (auto descr = retrieve_label_description(lang_id, tempstr)) issue->descr += descr;
}
if (auto len = issue->descr.length()) issue->descr.erase(len - 2);
jissue["number"].get_to(issue->id);
} catch (json::exception& ex) {
LOG_ERR(L"find_issue => jsonparse error: %S", ex.what());
return -1;
}
} else {
LOG_ERR(L"GitHub request error: %S", res.body().c_str());
return -1;
}
return 0;
}
} // namespace
class GameReport: public IGameReport {
public:
GameReport();
virtual ~GameReport() = default;
virtual void ShowReportWindow(const Info& info) final;
};
GameReport::GameReport() {
// for (int i = 0; i < LANG_STR_MAX; i++) {
// printf("%s\n = \n%s\n*************************\n", strings[Langs::ENGLISH][i], strings[Langs::RUSSIAN][i]);
// }
accessHotkeys().registerCallback(HkCommand::GAMEREPORT_SEND_REPORT, [this](HkCommand) {
auto title = accessSystemContent().getString("TITLE");
auto title_id = accessSystemContent().getString("TITLE_ID");
auto app_ver = accessSystemContent().getString("APP_VER");
ShowReportWindow({
.title = title ? title.value().data() : "Your PS4 Game Name",
.title_id = title_id ? title_id.value().data() : "CUSA00000",
.app_ver = app_ver ? app_ver.value().data() : "v0.0",
.emu_ver = PSOFF_RENDER_VERSION,
.type = IGameReport::Type::USER,
.add =
{
.ex = nullptr,
},
});
});
}
void GameReport::ShowReportWindow(const Info& info) {
LOG_USE_MODULE(GameReport);
if (m_bEnabled == false) {
LOG_WARN(L"[gamereport] Reporting is disabled!");
return;
}
SystemParamLang lang_id;
{
auto [lock, jData] = accessConfig()->accessModule(ConfigModFlag::GENERAL);
if (!getJsonParam(jData, "systemlang", lang_id)) lang_id = SystemParamLang::EnglishUS;
}
const char* message;
switch (info.type) {
case USER: message = get_lang_string(lang_id, LangString::MAIN_USER); break;
case EXCEPTION:
case MISSING_SYMBOL: message = get_lang_string(lang_id, LangString::MAIN_CRASH); break;
default: break;
}
SDL_MessageBoxButtonData btns[2] {
{.flags = 0, .buttonid = 2, .text = get_lang_string(lang_id, LangString::BUTTONS_NO)},
{.flags = 0, .buttonid = 1, .text = get_lang_string(lang_id, LangString::BUTTONS_YES)},
};
SDL_MessageBoxData mbd {
.flags = SDL_MESSAGEBOX_INFORMATION,
.window = accessVideoOut().SDLWindow(),
.title = get_lang_string(lang_id, LangString::MAIN_TITLE),
.message = message,
.numbuttons = 2,
.buttons = btns,
};
int btn = -1;
if (SDL_ShowMessageBox(&mbd, &btn) != 0) {
LOG_ERR(L"[gamereport] Failed to generate SDL MessageBox: %S", SDL_GetError());
return;
}
if (btn != 1) return;
GitHubIssue issue {
.id = -1,
};
std::string issue_msg;
if (find_issue(lang_id, info.title_id, &issue) == 0) {
issue_msg = std::vformat(get_lang_string(lang_id, LangString::ISSUE_TEXT), std::make_format_args(issue.status, issue.descr));
mbd.message = issue_msg.c_str();
if (SDL_ShowMessageBox(&mbd, &btn) != 0) {
LOG_ERR(L"[gamereport] Failed to generate SDL MessageBox: %S", SDL_GetError());
return;
}
if (btn != 1) return;
}
try {
std::string str;
auto [lock, jData] = accessConfig()->accessModule(ConfigModFlag::LOGGING);
(*jData)["sink"].get_to(str);
std::transform(str.begin(), str.end(), str.begin(), [](unsigned char c) { return std::tolower(c); });
if (str != "filebin") {
mbd.title = get_lang_string(lang_id, LangString::LOGGING_TITLE);
mbd.message = get_lang_string(lang_id, LangString::LOGGING_TEXT);
mbd.flags = SDL_MESSAGEBOX_WARNING;
mbd.numbuttons = 2;
btns[1].text = get_lang_string(lang_id, LangString::BUTTONS_OK);
btns[0].text = get_lang_string(lang_id, LangString::BUTTONS_CANCEL);
if (SDL_ShowMessageBox(&mbd, &btn) != 0) {
LOG_ERR(L"[gamereport] Failed to generate SDL MessageBox: %S", SDL_GetError());
return;
}
if (btn != 1) return;
}
} catch (const json::exception& ex) {
LOG_ERR(L"Something off with the logging configuration: %S", ex.what());
}
boost::urls::url link("https://github.com/");
auto extver = std::format("{}: {}", git::Branch(), git::CommitSHA1());
const char* emuver = nullptr;
const char* descr = nullptr;
if (git::Branch() == "main") {
emuver = info.emu_ver;
} else {
emuver = extver.data();
}
switch (info.type) {
case EXCEPTION:
descr = info.add.ex->what(); // todo: add stack trace?
break;
case MISSING_SYMBOL:
descr = info.add.message; // todo: some formatting?
break;
default:
descr = ""; //
break;
}
if (issue.id == -1) {
link.set_path(std::format("/{}/issues/new", GAMEREPORT_REPO_NAME));
auto params = link.params();
params.append({"template", "game_report.yml"});
params.append({"what-happened", descr});
params.append({"title", std::format("[{}]: {}", info.title_id, info.title)});
params.append({"game-version", info.app_ver});
params.append({"emu-version", emuver});
} else {
link.set_path(std::format("/{}/issues/{}", GAMEREPORT_REPO_NAME, issue.id));
std::string output = std::format("Retested with `{}`\n\n### Please, describe what you get\n\n{}\n\n"
"### Game version you are using\n\n{}\n\n"
"### Current game status\n\nNothing\n\n"
"### Upload your log there\n\nNo logs :(\n\n"
"### Upload your screenshots there\n\nNo screenshots :(\n\n",
emuver, descr, info.app_ver);
auto strlen = output.size() + 1;
HGLOBAL hMem = ::GlobalAlloc(GMEM_MOVEABLE, strlen);
::memcpy(GlobalLock(hMem), output.c_str(), strlen);
::GlobalUnlock(hMem);
::OpenClipboard(nullptr);
::EmptyClipboard();
::SetClipboardData(CF_TEXT, hMem);
::CloseClipboard();
::GlobalFree(hMem);
}
ShellExecuteA(nullptr, nullptr, link.normalize().c_str(), nullptr, nullptr, SW_SHOW);
}
IGameReport& accessGameReport() {
static GameReport obj;
return obj;
}