RetroAchievements CSO support, delay until identified (#17699)

* Support CSO (and any future block based formats) for RetroAchievements identification

* Avoid auto-loading savestates in challenge mode
This commit is contained in:
Henrik Rydgård authored and GitHub committed 2023-07-12 01:11:09 +02:00
1 parent 6c49af73c7
commit 431a0551b2
4 files changed
+92 -20

No files matched your search

+67 -8
View File
@@ -31,6 +31,8 @@
#include "Common/Log.h"
#include "Common/File/Path.h"
#include "Common/File/FileUtil.h"
#include "Core/FileLoaders/LocalFileLoader.h"
#include "Core/FileSystems/BlockDevices.h"
#include "Common/Net/HTTPClient.h"
#include "Common/System/OSD.h"
#include "Common/System/System.h"
@@ -502,6 +504,8 @@ void DoState(PointerWrap &p) {
}
break;
}
default:
break;
}
DoArray(p, buffer, data_size);
@@ -516,6 +520,8 @@ void DoState(PointerWrap &p) {
}
break;
}
default:
break;
}
delete[] buffer;
} else {
@@ -605,22 +611,72 @@ void identify_and_load_callback(int result, const char *error_message, rc_client
g_isIdentifying = false;
}
void SetGame(const Path &path) {
struct FileContext {
BlockDevice *bd;
int64_t seekPos;
};
static BlockDevice *g_blockDevice;
void SetGame(const Path &path, FileLoader *fileLoader) {
if (!g_rcClient || !IsLoggedIn()) {
// Nothing to do.
return;
}
// TODO: Fish the block device out of the loading process somewhere else. Though, probably easier to just do it here.
g_blockDevice = constructBlockDevice(fileLoader);
if (!g_blockDevice) {
ERROR_LOG(ACHIEVEMENTS, "Failed to construct block device for '%s' - can't identify", path.c_str());
return;
}
rc_hash_filereader rc_filereader;
rc_filereader.open = [](const char *utf8Path) {
Path path(utf8Path);
FILE *f = File::OpenCFile(path, "rb");
return (void *)f;
return (void *) new FileContext{ g_blockDevice, 0 };
};
rc_filereader.seek = [](void *file_handle, int64_t offset, int origin) {
FileContext *ctx = (FileContext *)file_handle;
switch (origin) {
case SEEK_SET: ctx->seekPos = offset; break;
case SEEK_END: ctx->seekPos = ctx->bd->GetBlockSize() * ctx->bd->GetNumBlocks() + offset; break;
case SEEK_CUR: ctx->seekPos += offset; break;
default: break;
}
};
rc_filereader.tell = [](void *file_handle) -> int64_t {
return ((FileContext *)file_handle)->seekPos;
};
rc_filereader.read = [](void *file_handle, void *buffer, size_t requested_bytes) -> size_t {
FileContext *ctx = (FileContext *)file_handle;
int blockSize = ctx->bd->GetBlockSize();
int64_t offset = ctx->seekPos;
int64_t endOffset = ctx->seekPos + requested_bytes;
int firstBlock = offset / blockSize;
int afterLastBlock = (endOffset + blockSize - 1) / blockSize;
int numBlocks = afterLastBlock - firstBlock;
// This is suboptimal, but good enough since we're not doing a lot of accesses.
uint8_t *buf = new uint8_t[numBlocks * blockSize];
bool success = ctx->bd->ReadBlocks(firstBlock, numBlocks, (u8 *)buf);
if (success) {
int64_t firstOffset = firstBlock * blockSize;
memcpy(buffer, buf + (offset - firstOffset), requested_bytes);
ctx->seekPos += requested_bytes;
delete[] buf;
return requested_bytes;
} else {
delete[] buf;
ERROR_LOG(ACHIEVEMENTS, "Block device load fail");
return 0;
}
};
rc_filereader.close = [](void *file_handle) {
FileContext *ctx = (FileContext *)file_handle;
delete ctx->bd;
delete ctx;
};
rc_filereader.seek = [](void *file_handle, int64_t offset, int origin) { fseek((FILE *)file_handle, offset, origin); };
rc_filereader.tell = [](void *file_handle) -> int64_t { return (int64_t)ftell((FILE *)file_handle); };
rc_filereader.read = [](void *file_handle, void *buffer, size_t requested_bytes) -> size_t { return fread(buffer, 1, requested_bytes, (FILE *)file_handle); };
rc_filereader.close = [](void *file_handle) { fclose((FILE *)file_handle); };
// The caller should hold off on executing game code until this turns false, checking with IsBlockingExecution()
g_isIdentifying = true;
@@ -633,6 +689,9 @@ void SetGame(const Path &path) {
rc_hash_init_custom_filereader(&rc_filereader);
rc_hash_init_default_cdreader();
rc_client_begin_identify_and_load_game(g_rcClient, RC_CONSOLE_PSP, path.c_str(), nullptr, 0, &identify_and_load_callback, nullptr);
// fclose above will have deleted it.
g_blockDevice = nullptr;
}
void UnloadGame() {
+2 -1
View File
@@ -24,6 +24,7 @@
class Path;
class PointerWrap;
class FileLoader;
namespace Achievements {
@@ -98,7 +99,7 @@ bool HasAchievementsOrLeaderboards();
bool LoginAsync(const char *username, const char *password);
void Logout();
void SetGame(const Path &path);
void SetGame(const Path &path, FileLoader *fileLoader);
void ChangeUMD(const Path &path); // for in-game UMD change
void UnloadGame(); // Call when leaving a game.
+19 -8
View File
@@ -43,6 +43,7 @@
#include "Common/TimeUtil.h"
#include "Common/GraphicsContext.h"
#include "Core/RetroAchievements.h"
#include "Core/MemFault.h"
#include "Core/HDRemaster.h"
#include "Core/MIPS/MIPS.h"
@@ -72,6 +73,7 @@
#include "GPU/GPUState.h"
#include "GPU/GPUInterface.h"
#include "GPU/Debugger/RecordFormat.h"
#include "Core/RetroAchievements.h"
enum CPUThreadState {
CPU_THREAD_NOT_RUNNING,
@@ -234,7 +236,7 @@ bool DiscIDFromGEDumpPath(const Path &path, FileLoader *fileLoader, std::string
}
}
bool CPU_Init(std::string *errorString) {
bool CPU_Init(std::string *errorString, FileLoader *loadedFile) {
coreState = CORE_POWERUP;
currentMIPS = &mipsr4k;
@@ -249,12 +251,6 @@ bool CPU_Init(std::string *errorString) {
Memory::g_PSPModel = g_Config.iPSPModel;
Path filename = g_CoreParameter.fileToStart;
loadedFile = ResolveFileLoaderTarget(ConstructFileLoader(filename));
#if PPSSPP_ARCH(AMD64)
if (g_Config.bCacheFullIsoInRam) {
loadedFile = new RamCachingFileLoader(loadedFile);
}
#endif
IdentifiedFileType type = Identify_File(loadedFile, errorString);
@@ -440,7 +436,17 @@ bool PSP_InitStart(const CoreParameter &coreParam, std::string *error_string) {
pspIsIniting = true;
PSP_SetLoading("Loading game...");
if (!CPU_Init(&g_CoreParameter.errorString)) {
Path filename = g_CoreParameter.fileToStart;
FileLoader *loadedFile = ResolveFileLoaderTarget(ConstructFileLoader(filename));
#if PPSSPP_ARCH(AMD64)
if (g_Config.bCacheFullIsoInRam) {
loadedFile = new RamCachingFileLoader(loadedFile);
}
#endif
Achievements::SetGame(filename, loadedFile);
if (!CPU_Init(&g_CoreParameter.errorString, loadedFile)) {
*error_string = g_CoreParameter.errorString;
if (error_string->empty()) {
*error_string = "Failed initializing CPU/Memory";
@@ -467,6 +473,11 @@ bool PSP_InitStart(const CoreParameter &coreParam, std::string *error_string) {
}
bool PSP_InitUpdate(std::string *error_string) {
if (Achievements::IsBlockingExecution()) {
// Keep waiting.
return false;
}
if (pspIsInited || !pspIsIniting) {
return true;
}
+4 -3
View File
@@ -246,6 +246,7 @@ void EmuScreen::bootGame(const Path &filename) {
if (PSP_IsIniting()) {
std::string error_string;
bootPending_ = !PSP_InitUpdate(&error_string);
if (!bootPending_) {
invalid_ = !PSP_IsInited();
if (invalid_) {
@@ -349,8 +350,6 @@ 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);
}
Achievements::SetGame(filename);
loadingViewColor_->Divert(0xFFFFFFFF, 0.75f);
loadingViewVisible_->Divert(UI::V_VISIBLE, 0.75f);
@@ -363,7 +362,9 @@ void EmuScreen::bootComplete() {
System_Notify(SystemNotification::DISASSEMBLY);
NOTICE_LOG(BOOT, "Loading %s...", PSP_CoreParameter().fileToStart.c_str());
autoLoad();
if (!Achievements::ChallengeModeActive()) {
autoLoad();
}
auto sc = GetI18NCategory(I18NCat::SCREEN);