Skip to content

Commit

Permalink
Merge pull request #4878 from ligfx/geckodownloadcodes
Browse files Browse the repository at this point in the history
Move GeckoCodeDiag download logic to GeckoCodeConfig (and make it use Curl)
  • Loading branch information
shuffle2 committed Jun 6, 2017
2 parents 4c8f8c0 + 2fd63ff commit 3fed604
Show file tree
Hide file tree
Showing 3 changed files with 172 additions and 137 deletions.
143 changes: 142 additions & 1 deletion Source/Core/Core/GeckoCodeConfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,158 @@
// Licensed under GPLv2+
// Refer to the license.txt file included.

#include "Core/GeckoCodeConfig.h"

#include <algorithm>
#include <sstream>
#include <string>
#include <vector>

#include <curl/curl.h>

#include "Common/IniFile.h"
#include "Common/Logging/Log.h"
#include "Common/StringUtil.h"
#include "Core/GeckoCodeConfig.h"

namespace Gecko
{
static size_t DownloadCodesWriteCallback(void* contents, size_t size, size_t nmemb,
std::string* body)
{
size_t realsize = size * nmemb;
body->insert(body->end(), reinterpret_cast<char*>(contents),
reinterpret_cast<char*>(contents) + realsize);
return realsize;
}

std::vector<GeckoCode> DownloadCodes(std::string gameid, bool* succeeded)
{
switch (gameid[0])
{
case 'R':
case 'S':
case 'G':
break;
default:
// All channels (WiiWare, VirtualConsole, etc) are identified by their first four characters
gameid = gameid.substr(0, 4);
break;
}

std::unique_ptr<CURL, decltype(&curl_easy_cleanup)> curl{curl_easy_init(), curl_easy_cleanup};

std::string endpoint{"http://geckocodes.org/txt.php?txt=" + gameid};
curl_easy_setopt(curl.get(), CURLOPT_URL, endpoint.c_str());
curl_easy_setopt(curl.get(), CURLOPT_TIMEOUT, 5);
std::string response_body;
curl_easy_setopt(curl.get(), CURLOPT_WRITEFUNCTION, DownloadCodesWriteCallback);
curl_easy_setopt(curl.get(), CURLOPT_WRITEDATA, &response_body);

*succeeded = true;
CURLcode res = curl_easy_perform(curl.get());
if (res != CURLE_OK)
{
ERROR_LOG(COMMON, "DownloadCodes: Curl error: %s", curl_easy_strerror(res));
*succeeded = false;
return {};
}
long response_code{0};
curl_easy_getinfo(curl.get(), CURLINFO_RESPONSE_CODE, &response_code);
if (response_code != 200)
{
WARN_LOG(COMMON, "DownloadCodes: Curl response code: %li", response_code);
*succeeded = false;
return {};
}

// temp vector containing parsed codes
std::vector<GeckoCode> gcodes;

// parse the codes
std::istringstream ss(response_body);

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);

return gcodes;
}

std::vector<GeckoCode> LoadCodes(const IniFile& globalIni, const IniFile& localIni)
{
std::vector<GeckoCode> gcodes;
Expand Down
2 changes: 2 additions & 0 deletions Source/Core/Core/GeckoCodeConfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

#pragma once

#include <string>
#include <vector>
#include "Core/GeckoCode.h"

Expand All @@ -12,5 +13,6 @@ class IniFile;
namespace Gecko
{
std::vector<GeckoCode> LoadCodes(const IniFile& globalIni, const IniFile& localIni);
std::vector<GeckoCode> DownloadCodes(std::string gameid, bool* succeeded);
void SaveCodes(IniFile& inifile, const std::vector<GeckoCode>& gcodes);
}
164 changes: 28 additions & 136 deletions Source/Core/DolphinWX/Cheats/GeckoCodeDiag.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
// Licensed under GPLv2+
// Refer to the license.txt file included.

#include <SFML/Network/Http.hpp>
#include <sstream>
#include <string>
#include <vector>
Expand Down Expand Up @@ -160,154 +159,47 @@ void CodeConfigPanel::DownloadCodes(wxCommandEvent&)
if (m_gameid.empty())
return;

std::string gameid = m_gameid;

switch (m_gameid[0])
bool succeeded;
std::vector<GeckoCode> gcodes = Gecko::DownloadCodes(m_gameid, &succeeded);
if (!succeeded)
{
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;
WxUtils::ShowErrorDialog(_("Failed to download codes."));
return;
}

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())
if (!gcodes.size())
{
// 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);
wxMessageBox(_("File contained no codes."));
return;
}

int read_state = 0;
GeckoCode gcode;
unsigned long added_count = 0;

while ((std::getline(ss, line).good()))
// 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)
{
// 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())
if (existing_gcodes_end == existing_gcodes_iter)
{
// 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
}
m_gcodes.push_back(code);
++added_count;
break;
}
break;

// read comment lines
case 2:
// append comment line
gcode.notes.push_back(line);
// code exists
if (*existing_gcodes_iter == code)
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));
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."));
}
// refresh the list
UpdateCodeList();
}
}

0 comments on commit 3fed604

Please sign in to comment.