dolphin/Source/Core/DolphinWX/Cheats/GeckoCodeDiag.cpp
Lioncash f91292eff2 GeckoCodeConfig: Return vector by value for LoadCodes()
Using an out-param is a leftover from C++03. Action Replay codes already
return the vector of codes by value as well.
2017-03-21 16:58:13 -04:00

314 lines
8.9 KiB
C++

// Copyright 2010 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include <SFML/Network/Http.hpp>
#include <sstream>
#include <string>
#include <vector>
#include <wx/button.h>
#include <wx/checklst.h>
#include <wx/listbox.h>
#include <wx/msgdlg.h>
#include <wx/panel.h>
#include <wx/sizer.h>
#include <wx/stattext.h>
#include <wx/textctrl.h>
#include "Common/CommonTypes.h"
#include "Common/StringUtil.h"
#include "Core/Core.h"
#include "Core/GeckoCode.h"
#include "Core/GeckoCodeConfig.h"
#include "DolphinWX/Cheats/GeckoCodeDiag.h"
#include "DolphinWX/WxUtils.h"
wxDEFINE_EVENT(DOLPHIN_EVT_GECKOCODE_TOGGLED, wxCommandEvent);
namespace Gecko
{
static const char str_name[] = wxTRANSLATE("Name: ");
static const char str_notes[] = wxTRANSLATE("Notes: ");
static const char str_creator[] = wxTRANSLATE("Creator: ");
CodeConfigPanel::CodeConfigPanel(wxWindow* const parent) : wxPanel(parent)
{
m_listbox_gcodes = new wxCheckListBox(this, wxID_ANY);
m_listbox_gcodes->Bind(wxEVT_LISTBOX, &CodeConfigPanel::UpdateInfoBox, this);
m_listbox_gcodes->Bind(wxEVT_CHECKLISTBOX, &CodeConfigPanel::ToggleCode, this);
m_infobox.label_name = new wxStaticText(this, wxID_ANY, wxGetTranslation(str_name));
m_infobox.label_creator = new wxStaticText(this, wxID_ANY, wxGetTranslation(str_creator));
m_infobox.label_notes = new wxStaticText(this, wxID_ANY, wxGetTranslation(str_notes));
m_infobox.textctrl_notes = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition,
wxDefaultSize, wxTE_MULTILINE | wxTE_READONLY);
m_infobox.listbox_codes =
new wxListBox(this, wxID_ANY, wxDefaultPosition, wxDLG_UNIT(this, wxSize(-1, 48)));
// TODO: buttons to add/edit codes
// sizers
const int space5 = FromDIP(5);
wxBoxSizer* const sizer_infobox = new wxBoxSizer(wxVERTICAL);
sizer_infobox->Add(m_infobox.label_name);
sizer_infobox->Add(m_infobox.label_creator, 0, wxTOP, space5);
sizer_infobox->Add(m_infobox.label_notes, 0, wxTOP, space5);
sizer_infobox->Add(m_infobox.textctrl_notes, 0, wxEXPAND | wxTOP, space5);
sizer_infobox->Add(m_infobox.listbox_codes, 1, wxEXPAND | wxTOP, space5);
// button sizer
wxBoxSizer* const sizer_buttons = new wxBoxSizer(wxHORIZONTAL);
btn_download = new wxButton(this, wxID_ANY, _("Download Codes (WiiRD Database)"));
btn_download->Disable();
btn_download->Bind(wxEVT_BUTTON, &CodeConfigPanel::DownloadCodes, this);
sizer_buttons->AddStretchSpacer(1);
sizer_buttons->Add(WxUtils::GiveMinSizeDIP(btn_download, wxSize(128, -1)), 1, wxEXPAND);
wxBoxSizer* const sizer_main = new wxBoxSizer(wxVERTICAL);
sizer_main->AddSpacer(space5);
sizer_main->Add(m_listbox_gcodes, 1, wxEXPAND | wxLEFT | wxRIGHT, space5);
sizer_main->AddSpacer(space5);
sizer_main->Add(sizer_infobox, 0, wxEXPAND | wxLEFT | wxRIGHT, space5);
sizer_main->AddSpacer(space5);
sizer_main->Add(sizer_buttons, 0, wxEXPAND | wxLEFT | wxRIGHT, space5);
sizer_main->AddSpacer(space5);
SetSizerAndFit(sizer_main);
}
void CodeConfigPanel::UpdateCodeList(bool checkRunning)
{
// disable the button if it doesn't have an effect
btn_download->Enable((!checkRunning || Core::IsRunning()) && !m_gameid.empty());
m_listbox_gcodes->Clear();
// add the codes to the listbox
for (const GeckoCode& code : m_gcodes)
{
m_listbox_gcodes->Append(m_listbox_gcodes->EscapeMnemonics(StrToWxStr(code.name)));
if (code.enabled)
{
m_listbox_gcodes->Check(m_listbox_gcodes->GetCount() - 1, true);
}
}
wxCommandEvent evt;
UpdateInfoBox(evt);
}
void CodeConfigPanel::LoadCodes(const IniFile& globalIni, const IniFile& localIni,
const std::string& gameid, bool checkRunning)
{
m_gameid = gameid;
m_gcodes.clear();
if (!checkRunning || Core::IsRunning())
m_gcodes = Gecko::LoadCodes(globalIni, localIni);
UpdateCodeList(checkRunning);
}
void CodeConfigPanel::ToggleCode(wxCommandEvent& evt)
{
const int sel = evt.GetInt(); // this right?
if (sel > -1)
{
m_gcodes[sel].enabled = m_listbox_gcodes->IsChecked(sel);
wxCommandEvent toggle_event(DOLPHIN_EVT_GECKOCODE_TOGGLED, GetId());
toggle_event.SetClientData(&m_gcodes[sel]);
GetEventHandler()->ProcessEvent(toggle_event);
}
}
void CodeConfigPanel::UpdateInfoBox(wxCommandEvent&)
{
m_infobox.listbox_codes->Clear();
const int sel = m_listbox_gcodes->GetSelection();
if (sel > -1)
{
m_infobox.label_name->SetLabel(wxGetTranslation(str_name) + StrToWxStr(m_gcodes[sel].name));
// notes textctrl
m_infobox.textctrl_notes->Clear();
for (const std::string& note : m_gcodes[sel].notes)
{
m_infobox.textctrl_notes->AppendText(StrToWxStr(note));
}
m_infobox.textctrl_notes->ScrollLines(-99); // silly
m_infobox.label_creator->SetLabel(wxGetTranslation(str_creator) +
StrToWxStr(m_gcodes[sel].creator));
// add codes to info listbox
for (const GeckoCode::Code& code : m_gcodes[sel].codes)
{
m_infobox.listbox_codes->Append(wxString::Format("%08X %08X", code.address, code.data));
}
}
else
{
m_infobox.label_name->SetLabel(wxGetTranslation(str_name));
m_infobox.textctrl_notes->Clear();
m_infobox.label_creator->SetLabel(wxGetTranslation(str_creator));
}
}
void CodeConfigPanel::DownloadCodes(wxCommandEvent&)
{
if (m_gameid.empty())
return;
std::string gameid = m_gameid;
switch (m_gameid[0])
{
case 'R':
case 'S':
case 'G':
break;
default:
// All channels (WiiWare, VirtualConsole, etc) are identified by their first four characters
gameid = m_gameid.substr(0, 4);
break;
}
sf::Http::Request req;
req.setUri("/txt.php?txt=" + gameid);
sf::Http http;
http.setHost("geckocodes.org");
const sf::Http::Response resp = http.sendRequest(req, sf::seconds(5));
if (sf::Http::Response::Ok == resp.getStatus())
{
// temp vector containing parsed codes
std::vector<GeckoCode> gcodes;
// parse the codes
std::istringstream ss(resp.getBody());
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 = StripSpaces(line);
if (line.empty())
{
// add the code
if (gcode.codes.size())
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 = StripSpaces(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;
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.size())
gcodes.push_back(gcode);
if (gcodes.size())
{
unsigned long added_count = 0;
// append the codes to the code list
for (const GeckoCode& code : gcodes)
{
// only add codes which do not already exist
auto existing_gcodes_iter = m_gcodes.begin();
auto existing_gcodes_end = m_gcodes.end();
for (;; ++existing_gcodes_iter)
{
if (existing_gcodes_end == existing_gcodes_iter)
{
m_gcodes.push_back(code);
++added_count;
break;
}
// code exists
if (*existing_gcodes_iter == code)
break;
}
}
wxMessageBox(wxString::Format(_("Downloaded %lu codes. (added %lu)"),
(unsigned long)gcodes.size(), added_count));
// refresh the list
UpdateCodeList();
}
else
{
wxMessageBox(_("File contained no codes."));
}
}
else
{
WxUtils::ShowErrorDialog(_("Failed to download codes."));
}
}
}