mirror of
https://github.com/hrydgard/ppsspp.git
synced 2024-11-23 13:30:02 +00:00
Get it to recognize the hash by using the rcheevos hash implementation
This commit is contained in:
parent
a3b7e99259
commit
f6fdaa4f56
@ -258,6 +258,15 @@ std::wstring Path::ToWString() const {
|
|||||||
}
|
}
|
||||||
return w;
|
return w;
|
||||||
}
|
}
|
||||||
|
std::string Path::ToCString() const {
|
||||||
|
std::string w = path_;
|
||||||
|
for (size_t i = 0; i < w.size(); i++) {
|
||||||
|
if (w[i] == '/') {
|
||||||
|
w[i] = '\\';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return w;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
std::string Path::ToVisualString(const char *relativeRoot) const {
|
std::string Path::ToVisualString(const char *relativeRoot) const {
|
||||||
|
@ -90,6 +90,11 @@ public:
|
|||||||
|
|
||||||
#if PPSSPP_PLATFORM(WINDOWS)
|
#if PPSSPP_PLATFORM(WINDOWS)
|
||||||
std::wstring ToWString() const;
|
std::wstring ToWString() const;
|
||||||
|
std::string ToCString() const; // Flips the slashes back to Windows standard, but string still UTF-8.
|
||||||
|
#else
|
||||||
|
std::string ToCSTring() const {
|
||||||
|
return ToString();
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Pass in a relative root to turn the path into a relative path - if it is one!
|
// Pass in a relative root to turn the path into a relative path - if it is one!
|
||||||
|
@ -212,6 +212,10 @@ public:
|
|||||||
|
|
||||||
std::vector<float> GetCurrentProgress();
|
std::vector<float> GetCurrentProgress();
|
||||||
|
|
||||||
|
size_t GetActiveCount() const {
|
||||||
|
return downloads_.size();
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<std::shared_ptr<Download>> downloads_;
|
std::vector<std::shared_ptr<Download>> downloads_;
|
||||||
// These get copied to downloads_ in Update(). It's so that callbacks can add new downloads
|
// These get copied to downloads_ in Update(). It's so that callbacks can add new downloads
|
||||||
|
@ -347,6 +347,8 @@ void EmuScreen::bootGame(const Path &filename) {
|
|||||||
g_OSD.Show(OSDType::MESSAGE_WARNING, gr->T("DefaultCPUClockRequired", "Warning: This game requires the CPU clock to be set to default."), 10.0f);
|
g_OSD.Show(OSDType::MESSAGE_WARNING, gr->T("DefaultCPUClockRequired", "Warning: This game requires the CPU clock to be set to default."), 10.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Achievements::GameChanged(filename);
|
||||||
|
|
||||||
loadingViewColor_->Divert(0xFFFFFFFF, 0.75f);
|
loadingViewColor_->Divert(0xFFFFFFFF, 0.75f);
|
||||||
loadingViewVisible_->Divert(UI::V_VISIBLE, 0.75f);
|
loadingViewVisible_->Divert(UI::V_VISIBLE, 0.75f);
|
||||||
|
|
||||||
|
@ -15,6 +15,8 @@
|
|||||||
#include "ext/rcheevos/include/rc_api_runtime.h"
|
#include "ext/rcheevos/include/rc_api_runtime.h"
|
||||||
#include "ext/rcheevos/include/rc_api_user.h"
|
#include "ext/rcheevos/include/rc_api_user.h"
|
||||||
#include "ext/rcheevos/include/rc_url.h"
|
#include "ext/rcheevos/include/rc_url.h"
|
||||||
|
#include "ext/rcheevos/include/rc_hash.h"
|
||||||
|
#include "ext/rcheevos/src/rhash/md5.h"
|
||||||
|
|
||||||
#include "UI/RetroAchievements.h"
|
#include "UI/RetroAchievements.h"
|
||||||
#include "ext/rapidjson/include/rapidjson/document.h"
|
#include "ext/rapidjson/include/rapidjson/document.h"
|
||||||
@ -46,8 +48,7 @@
|
|||||||
#include "RA_Interface.h"
|
#include "RA_Interface.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Simply wrap our current HTTP backend.
|
// Simply wrap our current HTTP backend to fit the DuckStation-derived code.
|
||||||
// Which will need replacement anyway for HTTPS...
|
|
||||||
namespace Common {
|
namespace Common {
|
||||||
class HTTPDownloader {
|
class HTTPDownloader {
|
||||||
public:
|
public:
|
||||||
@ -66,6 +67,9 @@ namespace Common {
|
|||||||
void WaitForAllRequests() {
|
void WaitForAllRequests() {
|
||||||
downloader_.WaitForAll();
|
downloader_.WaitForAll();
|
||||||
}
|
}
|
||||||
|
bool HasAnyRequests() const {
|
||||||
|
return downloader_.GetActiveCount() > 0;
|
||||||
|
}
|
||||||
void CreateRequest(std::string &&url, Request::Callback &&callback) {
|
void CreateRequest(std::string &&url, Request::Callback &&callback) {
|
||||||
Request::Callback movedCallback = std::move(callback);
|
Request::Callback movedCallback = std::move(callback);
|
||||||
downloader_.StartDownloadWithCallback(url, Path(), [=](http::Download &download) {
|
downloader_.StartDownloadWithCallback(url, Path(), [=](http::Download &download) {
|
||||||
@ -110,6 +114,14 @@ void OSDCloseBackgroundProgressDialog(const char *str_id) {
|
|||||||
NOTICE_LOG(ACHIEVEMENTS, "Progress dialog closed: %s", str_id);
|
NOTICE_LOG(ACHIEVEMENTS, "Progress dialog closed: %s", str_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void OSDAddKeyedMessage(const char *str_id, std::string message, float duration) {
|
||||||
|
NOTICE_LOG(ACHIEVEMENTS, "Keyed message: %s %s (%0.1f s)", str_id, message.c_str(), duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OSDDisplayLoadingScreen(const char *text) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
namespace Host {
|
namespace Host {
|
||||||
void OnAchievementsRefreshed() {
|
void OnAchievementsRefreshed() {
|
||||||
System_PostUIMessage("achievements_refreshed", "");
|
System_PostUIMessage("achievements_refreshed", "");
|
||||||
@ -168,7 +180,7 @@ static void GetUserUnlocks();
|
|||||||
static void GetPatchesCallback(s32 status_code, std::string content_type, Common::HTTPDownloader::Request::Data data);
|
static void GetPatchesCallback(s32 status_code, std::string content_type, Common::HTTPDownloader::Request::Data data);
|
||||||
static void GetLbInfoCallback(s32 status_code, std::string content_type, Common::HTTPDownloader::Request::Data data);
|
static void GetLbInfoCallback(s32 status_code, std::string content_type, Common::HTTPDownloader::Request::Data data);
|
||||||
static void GetPatches(u32 game_id);
|
static void GetPatches(u32 game_id);
|
||||||
static std::string GetGameHash(CDImage *image);
|
static std::string GetGameHash(const Path &path);
|
||||||
static void SetChallengeMode(bool enabled);
|
static void SetChallengeMode(bool enabled);
|
||||||
static void SendGetGameId();
|
static void SendGetGameId();
|
||||||
static void GetGameIdCallback(s32 status_code, std::string content_type, Common::HTTPDownloader::Request::Data data);
|
static void GetGameIdCallback(s32 status_code, std::string content_type, Common::HTTPDownloader::Request::Data data);
|
||||||
@ -199,7 +211,7 @@ static std::unique_ptr<Common::HTTPDownloader> s_http_downloader;
|
|||||||
static std::string s_username;
|
static std::string s_username;
|
||||||
static std::string s_api_token;
|
static std::string s_api_token;
|
||||||
|
|
||||||
static std::string s_game_path;
|
static Path s_game_path;
|
||||||
static std::string s_game_hash;
|
static std::string s_game_hash;
|
||||||
static std::string s_game_title;
|
static std::string s_game_title;
|
||||||
static std::string s_game_icon;
|
static std::string s_game_icon;
|
||||||
@ -453,8 +465,8 @@ void Achievements::ClearGameInfo(bool clear_achievements, bool clear_leaderboard
|
|||||||
|
|
||||||
void Achievements::ClearGameHash()
|
void Achievements::ClearGameHash()
|
||||||
{
|
{
|
||||||
s_game_path = {};
|
s_game_path.clear();
|
||||||
std::string().swap(s_game_hash);
|
s_game_hash.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Achievements::GetUserAgent()
|
std::string Achievements::GetUserAgent()
|
||||||
@ -547,6 +559,9 @@ void Achievements::Initialize()
|
|||||||
s_api_token = g_Config.sAchievementsToken;
|
s_api_token = g_Config.sAchievementsToken;
|
||||||
s_logged_in = (!s_username.empty() && !s_api_token.empty());
|
s_logged_in = (!s_username.empty() && !s_api_token.empty());
|
||||||
|
|
||||||
|
// this is just the non-SSL path.
|
||||||
|
rc_api_set_host("http://retroachievements.org");
|
||||||
|
|
||||||
// if (System::IsValid())
|
// if (System::IsValid())
|
||||||
// GameChanged();
|
// GameChanged();
|
||||||
}
|
}
|
||||||
@ -1389,13 +1404,27 @@ void Achievements::GetPatches(u32 game_id)
|
|||||||
request.Send(GetPatchesCallback);
|
request.Send(GetPatchesCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Achievements::GetGameHash(CDImage *image)
|
std::string Achievements::GetGameHash(const Path &path)
|
||||||
{
|
{
|
||||||
// According to https://docs.retroachievements.org/Game-Identification/, we should simply
|
// According to https://docs.retroachievements.org/Game-Identification/, we should simply
|
||||||
// concatenate param.sfo and eboot.bin, and hash the result, to obtain the game hash.
|
// concatenate param.sfo and eboot.bin, and hash the result, to obtain the game hash.
|
||||||
|
|
||||||
|
// UNFORTUNATELY, it's borked. Turns out that retroarch's rc_hash_cd_file is broken and will read
|
||||||
|
// outside the last sector in every case. Doubly unfortunately, all the hashes on retroachievements
|
||||||
|
// are generated like that. Oh well.
|
||||||
|
|
||||||
|
// We will need to reimplement it properly (hash some zeroes I guess, below) to handle file types
|
||||||
|
// that the cdreader can't handle (or we make a custom cdreader) but for now we just return orig_hash_str.
|
||||||
|
|
||||||
|
rc_hash_init_default_cdreader();
|
||||||
|
|
||||||
|
char orig_hash_str[33]{};
|
||||||
|
std::string ppath = path.ToCString();
|
||||||
|
|
||||||
|
rc_hash_generate_from_file(orig_hash_str, RC_CONSOLE_PSP, ppath.c_str());
|
||||||
|
|
||||||
const char *paramSFO = "disc0:/PSP_GAME/PARAM.SFO";
|
const char *paramSFO = "disc0:/PSP_GAME/PARAM.SFO";
|
||||||
const char *ebootBIN = "disc0:/PSP_GAME/EBOOT.BIN";
|
const char *ebootBIN = "disc0:/PSP_GAME/SYSDIR/EBOOT.BIN";
|
||||||
|
|
||||||
std::vector<uint8_t> paramSFOContents;
|
std::vector<uint8_t> paramSFOContents;
|
||||||
std::vector<uint8_t> ebootContents;
|
std::vector<uint8_t> ebootContents;
|
||||||
@ -1404,21 +1433,28 @@ std::string Achievements::GetGameHash(CDImage *image)
|
|||||||
pspFileSystem.ReadEntireFile(ebootBIN, ebootContents);
|
pspFileSystem.ReadEntireFile(ebootBIN, ebootContents);
|
||||||
|
|
||||||
uint8_t hash[16]{};
|
uint8_t hash[16]{};
|
||||||
|
md5_state_t md5;
|
||||||
|
md5_init(&md5);
|
||||||
|
md5_append(&md5, paramSFOContents.data(), (int)paramSFOContents.size());
|
||||||
|
md5_append(&md5, ebootContents.data(), (int)ebootContents.size());
|
||||||
|
md5_finish(&md5, hash);
|
||||||
|
|
||||||
|
/*
|
||||||
md5_context md5ctx{};
|
md5_context md5ctx{};
|
||||||
ppsspp_md5_starts(&md5ctx);
|
ppsspp_md5_starts(&md5ctx);
|
||||||
ppsspp_md5_update(&md5ctx, paramSFOContents.data(), (int)paramSFOContents.size());
|
ppsspp_md5_update(&md5ctx, paramSFOContents.data(), (int)paramSFOContents.size());
|
||||||
ppsspp_md5_update(&md5ctx, ebootContents.data(), (int)ebootContents.size());
|
ppsspp_md5_update(&md5ctx, ebootContents.data(), (int)ebootContents.size());
|
||||||
ppsspp_md5_finish(&md5ctx, hash);
|
ppsspp_md5_finish(&md5ctx, hash);
|
||||||
|
*/
|
||||||
|
|
||||||
// digest.Final(hash);
|
// This is straight from rc_hash_finalize
|
||||||
size_t hash_size = 0;
|
size_t hash_size = 0;
|
||||||
std::string hash_str(StringFromFormat(
|
std::string hash_str(StringFromFormat(
|
||||||
"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", hash[0], hash[1], hash[2], hash[3], hash[4],
|
"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", hash[0], hash[1], hash[2], hash[3], hash[4],
|
||||||
hash[5], hash[6], hash[7], hash[8], hash[9], hash[10], hash[11], hash[12], hash[13], hash[14], hash[15]));
|
hash[5], hash[6], hash[7], hash[8], hash[9], hash[10], hash[11], hash[12], hash[13], hash[14], hash[15]));
|
||||||
|
|
||||||
INFO_LOG(ACHIEVEMENTS, "Hash for '%s' & '%s' (%zu bytes, %u bytes hashed): %s", paramSFO, ebootBIN, (int)paramSFOContents.size(), (int)ebootContents.size());
|
INFO_LOG(ACHIEVEMENTS, "Hash for '%s' & '%s' (%u bytes, %u bytes hashed): %s", paramSFO, ebootBIN, (int)paramSFOContents.size(), (int)ebootContents.size(), hash_str.c_str());
|
||||||
return hash_str;
|
return std::string(orig_hash_str);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Achievements::GetGameIdCallback(s32 status_code, std::string content_type,
|
void Achievements::GetGameIdCallback(s32 status_code, std::string content_type,
|
||||||
@ -1434,7 +1470,7 @@ void Achievements::GetGameIdCallback(s32 status_code, std::string content_type,
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
const u32 game_id = response.game_id;
|
const u32 game_id = response.game_id;
|
||||||
INFO_LOG(ACHIEVEMENTS, "Server returned GameID % u", game_id);
|
NOTICE_LOG(ACHIEVEMENTS, "Server returned GameID %u", game_id);
|
||||||
if (game_id == 0)
|
if (game_id == 0)
|
||||||
{
|
{
|
||||||
// We don't want to block saving/loading states when there's no achievements.
|
// We don't want to block saving/loading states when there's no achievements.
|
||||||
@ -1449,66 +1485,29 @@ void Achievements::LeftGame() {
|
|||||||
// Should just uninitialize
|
// Should just uninitialize
|
||||||
}
|
}
|
||||||
|
|
||||||
void Achievements::GameChanged()
|
void Achievements::GameChanged(const Path &path)
|
||||||
{
|
{
|
||||||
/*
|
|
||||||
if (!IsActive() || s_game_path == path)
|
if (!IsActive() || s_game_path == path)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
std::unique_ptr<CDImage> temp_image;
|
|
||||||
if (!path.empty() && (!image || (g_settings.achievements_use_first_disc_from_playlist && image->HasSubImages() &&
|
|
||||||
image->GetCurrentSubImage() != 0)))
|
|
||||||
{
|
|
||||||
temp_image = CDImage::Open(path.c_str(), g_settings.cdrom_load_image_patches, nullptr);
|
|
||||||
image = temp_image.get();
|
|
||||||
if (!temp_image)
|
|
||||||
{
|
|
||||||
ERROR_LOG(ACHIEVEMENTS, "Failed to open temporary CD image '%s'", path.c_str());
|
|
||||||
s_http_downloader->WaitForAllRequests();
|
|
||||||
std::unique_lock lock(s_achievements_mutex);
|
|
||||||
DisableChallengeMode();
|
|
||||||
ClearGameInfo();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string game_hash;
|
std::string game_hash;
|
||||||
if (image)
|
|
||||||
|
game_hash = GetGameHash(path);
|
||||||
|
if (s_game_hash == game_hash)
|
||||||
{
|
{
|
||||||
game_hash = GetGameHash(image);
|
// only the path has changed - different format/save state/etc.
|
||||||
if (s_game_hash == game_hash)
|
INFO_LOG(ACHIEVEMENTS, "Detected path change from '%s' to '%s'", s_game_path.c_str(), path.c_str());
|
||||||
{
|
s_game_path = path;
|
||||||
// only the path has changed - different format/save state/etc.
|
return;
|
||||||
INFO_LOG(ACHIEVEMENTS, "Detected path change from '%s' to '%s'", s_game_path.c_str(), path.c_str());
|
|
||||||
s_game_path = path;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!IsUsingRAIntegration() && s_http_downloader->HasAnyRequests())
|
if (!IsUsingRAIntegration() && s_http_downloader->HasAnyRequests())
|
||||||
{
|
{
|
||||||
if (image)
|
OSDDisplayLoadingScreen("Downloading achievements data...");
|
||||||
Host::DisplayLoadingScreen("Downloading achievements data...");
|
|
||||||
|
|
||||||
s_http_downloader->WaitForAllRequests();
|
s_http_downloader->WaitForAllRequests();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (image && image->HasSubImages() && image->GetCurrentSubImage() != 0)
|
|
||||||
{
|
|
||||||
std::unique_ptr<CDImage> image_copy(
|
|
||||||
CDImage::Open(image->GetFileName().c_str(), g_settings.cdrom_load_image_patches, nullptr));
|
|
||||||
if (!image_copy)
|
|
||||||
{
|
|
||||||
ERROR_LOG(ACHIEVEMENTS, "Failed to reopen image '%s'", image->GetFileName().c_str());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// this will go to subimage zero automatically
|
|
||||||
Assert(image_copy->GetCurrentSubImage() == 0);
|
|
||||||
GameChanged(path, image_copy.get());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_lock lock(s_achievements_mutex);
|
std::unique_lock lock(s_achievements_mutex);
|
||||||
if (!IsUsingRAIntegration())
|
if (!IsUsingRAIntegration())
|
||||||
s_http_downloader->WaitForAllRequests();
|
s_http_downloader->WaitForAllRequests();
|
||||||
@ -1531,7 +1530,7 @@ void Achievements::GameChanged()
|
|||||||
// when we're booting the bios, this will fail
|
// when we're booting the bios, this will fail
|
||||||
if (!s_game_path.empty())
|
if (!s_game_path.empty())
|
||||||
{
|
{
|
||||||
Host::AddKeyedOSDMessage("retroachievements_disc_read_failed",
|
OSDAddKeyedMessage("retroachievements_disc_read_failed",
|
||||||
"Failed to read executable from disc. Achievements disabled.", 10.0f);
|
"Failed to read executable from disc. Achievements disabled.", 10.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1541,7 +1540,6 @@ void Achievements::GameChanged()
|
|||||||
|
|
||||||
if (IsLoggedIn())
|
if (IsLoggedIn())
|
||||||
SendGetGameId();
|
SendGetGameId();
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Achievements::SendGetGameId()
|
void Achievements::SendGetGameId()
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
|
|
||||||
#define ALWAYS_INLINE __forceinline
|
#define ALWAYS_INLINE __forceinline
|
||||||
|
|
||||||
class CDImage;
|
class Path;
|
||||||
class PointerWrap;
|
class PointerWrap;
|
||||||
|
|
||||||
namespace Achievements {
|
namespace Achievements {
|
||||||
@ -127,7 +127,7 @@ bool LoginAsync(const char *username, const char *password);
|
|||||||
bool Login(const char *username, const char *password);
|
bool Login(const char *username, const char *password);
|
||||||
void Logout();
|
void Logout();
|
||||||
|
|
||||||
void GameChanged();
|
void GameChanged(const Path &path);
|
||||||
void LeftGame();
|
void LeftGame();
|
||||||
|
|
||||||
/// Re-enables hardcode mode if it is enabled in the settings.
|
/// Re-enables hardcode mode if it is enabled in the settings.
|
||||||
|
Loading…
Reference in New Issue
Block a user