mirror of
https://github.com/hrydgard/ppsspp.git
synced 2025-04-02 11:01:50 -04:00
Merge pull request #18143 from hrydgard/retroachievement-disc-swap
Fix UMD disc swap with Retroachievements enabled
This commit is contained in:
commit
9625eaf29a
5 changed files with 102 additions and 70 deletions
|
@ -487,12 +487,13 @@ static u32 sceUmdGetErrorStat()
|
|||
|
||||
void __UmdReplace(const Path &filepath) {
|
||||
std::string error = "";
|
||||
if (!UmdReplace(filepath, error)) {
|
||||
FileLoader *fileLoader;
|
||||
if (!UmdReplace(filepath, &fileLoader, error)) {
|
||||
ERROR_LOG(SCEIO, "UMD Replace failed: %s", error.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
Achievements::ChangeUMD(filepath);
|
||||
Achievements::ChangeUMD(filepath, fileLoader);
|
||||
|
||||
UMDInserted = false;
|
||||
// Wake any threads waiting for the disc to be removed.
|
||||
|
|
|
@ -385,7 +385,7 @@ bool LoadFile(FileLoader **fileLoaderPtr, std::string *error_string) {
|
|||
return false;
|
||||
}
|
||||
|
||||
bool UmdReplace(const Path &filepath, std::string &error) {
|
||||
bool UmdReplace(const Path &filepath, FileLoader **fileLoader, std::string &error) {
|
||||
IFileSystem *currentUMD = pspFileSystem.GetSystem("disc0:");
|
||||
|
||||
if (!currentUMD) {
|
||||
|
@ -404,6 +404,8 @@ bool UmdReplace(const Path &filepath, std::string &error) {
|
|||
|
||||
loadedFile = ResolveFileLoaderTarget(loadedFile);
|
||||
|
||||
*fileLoader = loadedFile;
|
||||
|
||||
std::string errorString;
|
||||
IdentifiedFileType type = Identify_File(loadedFile, &errorString);
|
||||
|
||||
|
@ -415,7 +417,6 @@ bool UmdReplace(const Path &filepath, std::string &error) {
|
|||
error = "reinit memory failed";
|
||||
return false;
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
error = "Unsupported file type: " + std::to_string((int)type) + " " + errorString;
|
||||
|
|
|
@ -157,4 +157,4 @@ void RegisterFileLoaderFactory(std::string prefix, std::unique_ptr<FileLoaderFac
|
|||
// Can modify the string filename, as it calls IdentifyFile above.
|
||||
bool LoadFile(FileLoader **fileLoaderPtr, std::string *error_string);
|
||||
|
||||
bool UmdReplace(const Path &filepath, std::string &error);
|
||||
bool UmdReplace(const Path &filepath, FileLoader **fileLoader, std::string &error);
|
||||
|
|
|
@ -86,6 +86,12 @@ static rc_client_t *g_rcClient;
|
|||
static const std::string g_RAImageID = "I_RETROACHIEVEMENTS_LOGO";
|
||||
constexpr double LOGIN_ATTEMPT_INTERVAL_S = 10.0;
|
||||
|
||||
struct FileContext {
|
||||
BlockDevice *bd;
|
||||
int64_t seekPos;
|
||||
};
|
||||
static BlockDevice *g_blockDevice;
|
||||
|
||||
#define PSP_MEMORY_OFFSET 0x08000000
|
||||
|
||||
static void TryLoginByToken(bool isInitialAttempt);
|
||||
|
@ -411,6 +417,60 @@ void Initialize() {
|
|||
|
||||
rc_client_set_event_handler(g_rcClient, event_handler_callback);
|
||||
|
||||
rc_hash_filereader rc_filereader;
|
||||
rc_filereader.open = [](const char *utf8Path) -> void *{
|
||||
if (!g_blockDevice) {
|
||||
ERROR_LOG(ACHIEVEMENTS, "No block device");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
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_hash_init_custom_filereader(&rc_filereader);
|
||||
rc_hash_init_default_cdreader();
|
||||
|
||||
TryLoginByToken(true);
|
||||
}
|
||||
|
||||
|
@ -723,13 +783,6 @@ void identify_and_load_callback(int result, const char *error_message, rc_client
|
|||
g_isIdentifying = false;
|
||||
}
|
||||
|
||||
struct FileContext {
|
||||
BlockDevice *bd;
|
||||
int64_t seekPos;
|
||||
};
|
||||
|
||||
static BlockDevice *g_blockDevice;
|
||||
|
||||
bool IsReadyToStart() {
|
||||
return !g_isLoggingIn;
|
||||
}
|
||||
|
@ -770,58 +823,6 @@ void SetGame(const Path &path, IdentifiedFileType fileType, FileLoader *fileLoad
|
|||
return;
|
||||
}
|
||||
|
||||
rc_hash_filereader rc_filereader;
|
||||
rc_filereader.open = [](const char *utf8Path) -> void * {
|
||||
if (!g_blockDevice) {
|
||||
ERROR_LOG(ACHIEVEMENTS, "No block device");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
// The caller should hold off on executing game code until this turns false, checking with IsBlockingExecution()
|
||||
g_isIdentifying = true;
|
||||
|
||||
|
@ -830,8 +831,6 @@ void SetGame(const Path &path, IdentifiedFileType fileType, FileLoader *fileLoad
|
|||
rc_client_set_encore_mode_enabled(g_rcClient, g_Config.bAchievementsEncoreMode ? 1 : 0);
|
||||
rc_client_set_unofficial_enabled(g_rcClient, g_Config.bAchievementsUnofficial ? 1 : 0);
|
||||
|
||||
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.
|
||||
|
@ -845,17 +844,47 @@ void UnloadGame() {
|
|||
}
|
||||
|
||||
void change_media_callback(int result, const char *error_message, rc_client_t *client, void *userdata) {
|
||||
auto ac = GetI18NCategory(I18NCat::ACHIEVEMENTS);
|
||||
NOTICE_LOG(ACHIEVEMENTS, "Change media callback: %d (%s)", result, error_message);
|
||||
g_isIdentifying = false;
|
||||
|
||||
switch (result) {
|
||||
case RC_OK:
|
||||
{
|
||||
// Successful! Later, show a message that we succeeded.
|
||||
break;
|
||||
}
|
||||
case RC_NO_GAME_LOADED:
|
||||
// The current game does not support achievements.
|
||||
g_OSD.Show(OSDType::MESSAGE_INFO, ac->T("RetroAchievements are not available for this game"), "", g_RAImageID, 3.0f);
|
||||
break;
|
||||
case RC_NO_RESPONSE:
|
||||
// We lost the internet connection at some point and can't report achievements.
|
||||
ShowNotLoggedInMessage();
|
||||
break;
|
||||
default:
|
||||
// Other various errors.
|
||||
ERROR_LOG(ACHIEVEMENTS, "Failed to identify/load game: %d (%s)", result, error_message);
|
||||
g_OSD.Show(OSDType::MESSAGE_ERROR, ac->T("Failed to identify game. Achievements will not unlock."), "", g_RAImageID, 6.0f);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ChangeUMD(const Path &path) {
|
||||
void ChangeUMD(const Path &path, FileLoader *fileLoader) {
|
||||
if (!IsActive()) {
|
||||
// Nothing to do.
|
||||
return;
|
||||
}
|
||||
|
||||
rc_client_begin_change_media(g_rcClient,
|
||||
g_blockDevice = constructBlockDevice(fileLoader);
|
||||
if (!g_blockDevice) {
|
||||
ERROR_LOG(ACHIEVEMENTS, "Failed to construct block device for '%s' - can't identify", path.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
g_isIdentifying = true;
|
||||
|
||||
rc_client_begin_change_media(g_rcClient,
|
||||
path.c_str(),
|
||||
nullptr,
|
||||
0,
|
||||
|
@ -863,7 +892,8 @@ void ChangeUMD(const Path &path) {
|
|||
nullptr
|
||||
);
|
||||
|
||||
g_isIdentifying = true;
|
||||
// fclose above will have deleted it.
|
||||
g_blockDevice = nullptr;
|
||||
}
|
||||
|
||||
std::set<uint32_t> GetActiveChallengeIDs() {
|
||||
|
|
|
@ -108,7 +108,7 @@ void Logout();
|
|||
|
||||
bool IsReadyToStart();
|
||||
void SetGame(const Path &path, IdentifiedFileType fileType, FileLoader *fileLoader);
|
||||
void ChangeUMD(const Path &path); // for in-game UMD change
|
||||
void ChangeUMD(const Path &path, FileLoader *fileLoader); // for in-game UMD change
|
||||
void UnloadGame(); // Call when leaving a game.
|
||||
|
||||
Statistics GetStatistics();
|
||||
|
|
Loading…
Add table
Reference in a new issue