[qt] clean up some orphaned_profiles bugs; add help (#2894)

Some weird edge cases of "phantom" profiles that are actually needed
for... reasons I guess

Also, fixed some of the logic w.r.t empty checking, plus added a help
page

Signed-off-by: crueter <crueter@eden-emu.dev>

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2894
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Reviewed-by: Maufeat <sahyno1996@gmail.com>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
This commit is contained in:
crueter 2025-10-30 11:03:08 +01:00
parent d989166044
commit e93159b047
No known key found for this signature in database
GPG key ID: 425ACD2D4830EBC6
6 changed files with 100 additions and 38 deletions

View file

@ -10,16 +10,19 @@
#include <iostream>
#include <random>
#include <boost/algorithm/string/case_conv.hpp>
#include <boost/algorithm/string/find.hpp>
#include <fmt/ranges.h>
#include "common/fs/file.h"
#include "common/fs/fs.h"
#include "common/fs/fs_types.h"
#include "common/fs/path_util.h"
#include <ranges>
#include "common/settings.h"
#include "common/string_util.h"
#include "core/file_sys/savedata_factory.h"
#include "core/hle/service/acc/profile_manager.h"
#include <ranges>
namespace Service::Account {
@ -39,6 +42,7 @@ struct ProfileDataRaw {
INSERT_PADDING_BYTES(0x10);
std::array<UserRaw, MAX_USERS> users{};
};
static_assert(sizeof(ProfileDataRaw) == 0x650, "ProfileDataRaw has incorrect size.");
// TODO(ogniK): Get actual error codes
@ -490,8 +494,22 @@ void ProfileManager::ResetUserSaveFile()
std::vector<std::string> ProfileManager::FindGoodProfiles()
{
namespace fs = std::filesystem;
std::vector<std::string> good_uuids;
const auto path = Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir)
/ "user/save/0000000000000000";
// some exceptions because certain games just LOVE TO CAUSE ISSUES
static constexpr const std::array<const char* const, 2> EXCEPTION_UUIDS
= {"5755CC2A545A87128500000000000000", "00000000000000000000000000000000"};
for (const char *const uuid : EXCEPTION_UUIDS) {
if (fs::exists(path / uuid))
good_uuids.emplace_back(uuid);
}
for (const ProfileInfo& p : profiles) {
std::string uuid_string = [p]() -> std::string {
auto uuid = p.user_uuid;
@ -506,12 +524,9 @@ std::vector<std::string> ProfileManager::FindGoodProfiles()
return fmt::format("{:016X}{:016X}", user_id[1], user_id[0]);
}();
good_uuids.emplace_back(uuid_string);
if (uuid_string != "0") good_uuids.emplace_back(uuid_string);
}
// used for acnh, etc
good_uuids.emplace_back("00000000000000000000000000000000");
return good_uuids;
}
@ -519,6 +534,8 @@ std::vector<std::string> ProfileManager::FindOrphanedProfiles()
{
std::vector<std::string> good_uuids = FindGoodProfiles();
namespace fs = std::filesystem;
// TODO: fetch save_id programmatically
const auto path = Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir)
/ "user/save/0000000000000000";
@ -530,32 +547,47 @@ std::vector<std::string> ProfileManager::FindOrphanedProfiles()
[&good_uuids, &orphaned_profiles](const std::filesystem::directory_entry& entry) -> bool {
const std::string uuid = entry.path().stem().string();
bool override = false;
// first off, we should always clear empty profiles
// 99% of the time these are useless. If not, they are recreated anyways...
namespace fs = std::filesystem;
const auto is_empty = [&entry]() -> bool {
const auto is_empty = [&entry, &override]() -> bool {
try {
for (const auto& file : fs::recursive_directory_iterator(entry.path())) {
if (file.is_regular_file()) {
return true;
}
// TODO: .yuzu_save_size is a weird file that gets created by certain games
// I have no idea what its purpose is, but TEMPORARY SOLUTION: just mark the profile as valid if
// this file exists (???) e.g. for SSBU
// In short: if .yuzu_save_size is the ONLY file in a profile it's probably fine to keep
if (file.path().filename().string() == FileSys::GetSaveDataSizeFileName())
override = true;
// if there are any regular files (NOT directories) there, do NOT delete it :p
if (file.is_regular_file())
return false;
}
} catch (const fs::filesystem_error& e) {
// if we get an error--no worries, just pretend it's not empty
return false;
return true;
}
return false;
return true;
}();
if (!is_empty) {
if (is_empty) {
fs::remove_all(entry);
return true;
}
// edge-case: some filesystems forcefully change filenames to lowercase
// so we can just ignore any differences
// looking at you microsoft... ;)
std::string upper_uuid = uuid;
boost::to_upper(upper_uuid);
// if profiles.dat contains the UUID--all good
// if not--it's an orphaned profile and should be resolved by the user
if (std::find(good_uuids.begin(), good_uuids.end(), uuid) == good_uuids.end()) {
if (!override
&& std::find(good_uuids.begin(), good_uuids.end(), upper_uuid) == good_uuids.end()) {
orphaned_profiles.emplace_back(uuid);
}
return true;

View file

@ -69,7 +69,7 @@ enum class FatalType : u32 {
static void GenerateErrorReport(Core::System& system, Result error_code, const FatalInfo& info) {
const auto title_id = system.GetApplicationProcessProgramID();
std::string crash_report = fmt::format(
"Yuzu {}-{} crash report\n"
"Eden {}-{} crash report\n"
"Title ID: {:016x}\n"
"Result: {:#X} ({:04}-{:04d})\n"
"Set flags: 0x{:16X}\n"

View file

@ -13,7 +13,6 @@
#include <discord_rpc.h>
#include <fmt/format.h>
#include <qdebug.h>
#include "common/common_types.h"
#include "common/string_util.h"
@ -88,8 +87,6 @@ void DiscordImpl::UpdateGameStatus(bool use_default) {
presence.details = "Currently in game";
presence.startTimestamp = start_time;
Discord_UpdatePresence(&presence);
qDebug() << "game status updated";
}
void DiscordImpl::Update() {

View file

@ -340,33 +340,34 @@ void FixProfiles()
QString qorphaned;
// max. of 8 orphaned profiles is fair, I think
// 33 = 32 (UUID) + 1 (\n)
qorphaned.reserve(8 * 33);
// 36 = 32 (UUID) + 4 (<br>)
qorphaned.reserve(8 * 36);
for (const std::string& s : orphaned) {
qorphaned = qorphaned % QStringLiteral("\n") % QString::fromStdString(s);
qorphaned = qorphaned % QStringLiteral("<br>") % QString::fromStdString(s);
}
QString qgood;
// max. of 8 good profiles is fair, I think
// 33 = 32 (UUID) + 1 (\n)
qgood.reserve(8 * 33);
// 36 = 32 (UUID) + 4 (<br>)
qgood.reserve(8 * 36);
for (const std::string& s : good) {
qgood = qgood % QStringLiteral("\n") % QString::fromStdString(s);
qgood = qgood % QStringLiteral("<br>") % QString::fromStdString(s);
}
QtCommon::Frontend::Critical(
tr("Orphaned Profiles Detected!"),
tr("UNEXPECTED BAD THINGS MAY HAPPEN IF YOU DON'T READ THIS!\n"
"Eden has detected the following save directories with no attached profile:\n"
"%1\n\n"
"The following profiles are valid:\n"
"%2\n\n"
"Click \"OK\" to open your save folder and fix up your profiles.\n"
tr("UNEXPECTED BAD THINGS MAY HAPPEN IF YOU DON'T READ THIS!<br>"
"Eden has detected the following save directories with no attached profile:<br>"
"%1<br><br>"
"The following profiles are valid:<br>"
"%2<br><br>"
"Click \"OK\" to open your save folder and fix up your profiles.<br>"
"Hint: copy the contents of the largest or last-modified folder elsewhere, "
"delete all orphaned profiles, and move your copied contents to the good profile.")
"delete all orphaned profiles, and move your copied contents to the good profile.<br><br>"
"Still confused? See the <a href='https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/Orphaned.md'>help page</a>.<br>")
.arg(qorphaned, qgood));
QtCommon::Game::OpenSaveFolder();