MemStickScreen: Don't blame the folder after the data has moved

If switching failed after the data was moved, the screen said the folder
doesn't work as a memory stick, though the data was already in it.

The switch's checks (the new folder and the one holding memstick_dir.txt
are writable) now run before anything is moved, where that message is
accurate. If the switch still fails after a move, a new message says the
data is in the new folder, and OK only retries the switch.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Henrik RydgårdandClaude Opus 5.5 committed 2026-11-06 14:53:36 +01:00
1 parent 86a12a7df2
commit ed5ffe3d1d
4 files changed
+52 -10

No files matched your search

+24 -4
View File
@@ -172,23 +172,43 @@ MemstickFolderCheck CheckNewMemstickFolder(const Path &newFolder, const Path &cu
return MemstickFolderCheck::OK;
}
bool SwitchMemstickFolderTo(Path newMemstickFolder) {
static bool CanWriteTo(const Path &folder) {
const Path testWriteFile = folder / ".write_verify_file";
if (!File::WriteDataToFile(true, "1", 1, testWriteFile)) {
return false;
}
File::Delete(testWriteFile);
return true;
}
bool CanSwitchMemstickFolderTo(const Path &newMemstickFolder) {
// Doesn't already exist, create.
// Should this ever happen?
if (newMemstickFolder.Type() == PathType::NATIVE) {
if (!File::Exists(newMemstickFolder)) {
File::CreateFullPath(newMemstickFolder);
}
Path testWriteFile = newMemstickFolder / ".write_verify_file";
if (!File::WriteDataToFile(true, "1", 1, testWriteFile)) {
if (!CanWriteTo(newMemstickFolder)) {
ERROR_LOG(Log::System, "Can't write to '%s'", newMemstickFolder.c_str());
return false;
}
File::Delete(testWriteFile);
} else {
// TODO: Do the same but with scoped storage? Not really necessary, right? If it came from a browse
// for folder, we can assume it exists and is writable, barring wacky race conditions like the user
// being connected by USB and deleting it.
}
// Where memstick_dir.txt goes. Not set on desktops, which only get here through a debug start screen.
if (!g_Config.internalDataDirectory.empty() && !CanWriteTo(g_Config.internalDataDirectory)) {
ERROR_LOG(Log::System, "Can't write to '%s'", g_Config.internalDataDirectory.c_str());
return false;
}
return true;
}
bool SwitchMemstickFolderTo(Path newMemstickFolder) {
if (!CanSwitchMemstickFolderTo(newMemstickFolder)) {
return false;
}
Path memStickDirFile = g_Config.internalDataDirectory / "memstick_dir.txt";
#if PPSSPP_PLATFORM(UWP)
+2
View File
@@ -48,5 +48,7 @@ enum class MemstickFolderCheck {
MemstickFolderCheck CheckNewMemstickFolder(const Path &newFolder, const Path &currentFolder);
bool FolderSeemsToBeUsed(const Path &newMemstickFolder);
// The checks SwitchMemstickFolderTo() starts with, to run before moving any data.
bool CanSwitchMemstickFolderTo(const Path &newMemstickFolder);
bool SwitchMemstickFolderTo(Path newMemstickFolder);
MoveResult *MoveDirectoryContentsSafe(Path moveSrc, Path moveDest, MoveProgressReporter &progressReporter);
+24 -6
View File
@@ -551,7 +551,7 @@ void ConfirmMemstickMoveScreen::CreateViews() {
}
if (!error_.empty()) {
leftColumn->Add(new TextView(error_, ALIGN_LEFT, false));
leftColumn->Add(new TextView(error_, ALIGN_LEFT | FLAG_WRAP_TEXT, false));
}
if (!oldMemstickFolder.empty()) {
@@ -566,7 +566,7 @@ void ConfirmMemstickMoveScreen::CreateViews() {
}
if (!moveDataTask_) {
if (!initialSetup_ && CanMoveData()) {
if (!initialSetup_ && CanMoveData() && !dataMoved_) {
leftColumn->Add(new CheckBox(&moveData_, ms->T("Move Data")))->OnClick.Handle(this, &ConfirmMemstickMoveScreen::OnMoveDataClick);
}
@@ -611,7 +611,9 @@ void ConfirmMemstickMoveScreen::update() {
if (result->success) {
progressReporter_.SetProgress(T(I18NCat::DIALOG, "Done!"));
INFO_LOG(Log::System, "Move data task finished successfully!");
// Succeeded!
// Succeeded! If the switch below fails, OK only retries the switch.
dataMoved_ = true;
moveData_ = false;
FinishFolderMove();
} else {
progressReporter_.SetProgress(ms->T("Failed to move some files!"));
@@ -639,7 +641,20 @@ void ConfirmMemstickMoveScreen::OnConfirm(UI::EventParams &params) {
// Should probably be done on a background thread so we can show some UI.
// So we probably need another screen for this with a progress bar..
// If the directory itself is called PSP, don't go below.
if (IsBlocked()) {
if (IsBlocked() || moveDataTask_) {
return;
}
// Only the switch is left to retry, and its own failure message says the data moved.
if (dataMoved_) {
FinishFolderMove();
return;
}
// Find out now if the switch would fail, rather than after moving the data away.
if (!CanSwitchMemstickFolderTo(newMemstickFolder_)) {
error_ = T(I18NCat::MEMSTICK, "That folder doesn't work as a memstick folder.");
RecreateViews();
return;
}
@@ -667,8 +682,11 @@ void ConfirmMemstickMoveScreen::FinishFolderMove() {
// Successful so far, switch the memstick folder.
if (!SwitchMemstickFolderTo(newMemstickFolder_)) {
// TODO: More precise errors.
error_ = ms->T("That folder doesn't work as a memstick folder.");
if (dataMoved_) {
error_ = ms->T("DataMovedButNotSwitched", "Your data was moved to the new folder, but PPSSPP couldn't switch to it. Please try again.");
} else {
error_ = ms->T("That folder doesn't work as a memstick folder.");
}
RecreateViews();
return;
}
+2
View File
@@ -134,6 +134,8 @@ private:
bool moveData_ = true;
#endif
bool initialSetup_;
// The data is already in the new folder.
bool dataMoved_ = false;
MoveProgressReporter progressReporter_;
UI::TextView *progressView_ = nullptr;