Merge pull request #12047 from AdmiralCurtiss/nand-stats-user

Add file size stats to NAND Check.
This commit is contained in:
JosJuice
2023-07-30 21:51:07 +02:00
committed by GitHub
6 changed files with 125 additions and 21 deletions

View File

@ -107,6 +107,33 @@ struct Metadata
u16 fst_index;
};
// size of a single cluster in the NAND in bytes
constexpr u16 CLUSTER_SIZE = 16384;
// total number of clusters available in the NAND
constexpr u16 TOTAL_CLUSTERS = 0x7ec0;
// number of clusters reserved for bad blocks and similar, not accessible to normal writes
constexpr u16 RESERVED_CLUSTERS = 0x0300;
// number of clusters actually usable by the file system
constexpr u16 USABLE_CLUSTERS = TOTAL_CLUSTERS - RESERVED_CLUSTERS;
// size of a single 'block' as defined by the Wii System Menu in clusters
constexpr u16 CLUSTERS_PER_BLOCK = 8;
// total number of user-accessible blocks in the NAND
constexpr u16 USER_BLOCKS = 2176;
// total number of user-accessible clusters in the NAND
constexpr u16 USER_CLUSTERS = USER_BLOCKS * CLUSTERS_PER_BLOCK;
// the inverse of that, the amount of usable clusters reserved for system files
constexpr u16 SYSTEM_CLUSTERS = USABLE_CLUSTERS - USER_CLUSTERS;
// total number of inodes available in the NAND
constexpr u16 TOTAL_INODES = 0x17ff;
struct NandStats
{
u32 cluster_size;
@ -124,6 +151,14 @@ struct DirectoryStats
u32 used_inodes;
};
// Not a real Wii data struct, but useful for calculating how full the user's NAND is even if it's
// way larger than it should be.
struct ExtendedDirectoryStats
{
u64 used_clusters;
u64 used_inodes;
};
struct FileStatus
{
u32 offset;
@ -252,6 +287,9 @@ public:
/// Get usage information about a directory (used cluster and inode counts).
virtual Result<DirectoryStats> GetDirectoryStats(const std::string& path) = 0;
/// Like GetDirectoryStats() but not limited to the actual 512 MB NAND limit.
virtual Result<ExtendedDirectoryStats> GetExtendedDirectoryStats(const std::string& path) = 0;
virtual void SetNandRedirects(std::vector<NandRedirect> nand_redirects) = 0;
};

View File

@ -29,21 +29,6 @@ namespace IOS::HLE::FS
{
constexpr u32 BUFFER_CHUNK_SIZE = 65536;
// size of a single cluster in the NAND
constexpr u16 CLUSTER_SIZE = 16384;
// total number of clusters available in the NAND
constexpr u16 TOTAL_CLUSTERS = 0x7ec0;
// number of clusters reserved for bad blocks and similar, not accessible to normal writes
constexpr u16 RESERVED_CLUSTERS = 0x0300;
// number of clusters actually usable by the file system
constexpr u16 USABLE_CLUSTERS = TOTAL_CLUSTERS - RESERVED_CLUSTERS;
// total number of inodes available in the NAND
constexpr u16 TOTAL_INODES = 0x17ff;
HostFileSystem::HostFilename HostFileSystem::BuildFilename(const std::string& wii_path) const
{
for (const auto& redirect : m_nand_redirects)
@ -818,11 +803,24 @@ Result<NandStats> HostFileSystem::GetNandStats()
}
Result<DirectoryStats> HostFileSystem::GetDirectoryStats(const std::string& wii_path)
{
const auto result = GetExtendedDirectoryStats(wii_path);
if (!result)
return result.Error();
DirectoryStats stats{};
stats.used_inodes = static_cast<u32>(std::min<u64>(result->used_inodes, TOTAL_INODES));
stats.used_clusters = static_cast<u32>(std::min<u64>(result->used_clusters, USABLE_CLUSTERS));
return stats;
}
Result<ExtendedDirectoryStats>
HostFileSystem::GetExtendedDirectoryStats(const std::string& wii_path)
{
if (!IsValidPath(wii_path))
return ResultCode::Invalid;
DirectoryStats stats{};
ExtendedDirectoryStats stats{};
std::string path(BuildFilename(wii_path).host_path);
File::FileInfo info(path);
if (!info.Exists())
@ -835,10 +833,8 @@ Result<DirectoryStats> HostFileSystem::GetDirectoryStats(const std::string& wii_
FixupDirectoryEntries(&parent_dir, wii_path == "/");
// add one for the folder itself
stats.used_inodes = static_cast<u32>(std::min<u64>(1 + parent_dir.size, TOTAL_INODES));
const u64 clusters = ComputeUsedClusters(parent_dir);
stats.used_clusters = static_cast<u32>(std::min<u64>(clusters, USABLE_CLUSTERS));
stats.used_inodes = 1 + parent_dir.size;
stats.used_clusters = ComputeUsedClusters(parent_dir);
}
else
{

View File

@ -55,6 +55,7 @@ public:
Result<NandStats> GetNandStats() override;
Result<DirectoryStats> GetDirectoryStats(const std::string& path) override;
Result<ExtendedDirectoryStats> GetExtendedDirectoryStats(const std::string& path) override;
void SetNandRedirects(std::vector<NandRedirect> nand_redirects) override;

View File

@ -961,6 +961,34 @@ static NANDCheckResult CheckNAND(IOS::HLE::Kernel& ios, bool repair)
}
}
// Get some storage stats.
const auto fs = ios.GetFS();
const auto root_stats = fs->GetExtendedDirectoryStats("/");
// The Wii System Menu's save/channel management only considers a specific subset of the Wii NAND
// user-accessible and will only use those folders when calculating the amount of free blocks it
// displays. This can have weird side-effects where the other parts of the NAND contain more data
// than reserved and it will display free blocks even though there isn't any space left. To avoid
// confusion, report the 'user' and 'system' data separately to the user.
u64 used_clusters_user = 0;
u64 used_inodes_user = 0;
for (std::string user_path : {"/meta", "/ticket", "/title/00010000", "/title/00010001",
"/title/00010003", "/title/00010004", "/title/00010005",
"/title/00010006", "/title/00010007", "/shared2/title"})
{
const auto dir_stats = fs->GetExtendedDirectoryStats(user_path);
if (dir_stats)
{
used_clusters_user += dir_stats->used_clusters;
used_inodes_user += dir_stats->used_inodes;
}
}
result.used_clusters_user = used_clusters_user;
result.used_clusters_system = root_stats ? (root_stats->used_clusters - used_clusters_user) : 0;
result.used_inodes_user = used_inodes_user;
result.used_inodes_system = root_stats ? (root_stats->used_inodes - used_inodes_user) : 0;
return result;
}

View File

@ -101,6 +101,10 @@ struct NANDCheckResult
{
bool bad = false;
std::unordered_set<u64> titles_to_remove;
u64 used_clusters_user = 0;
u64 used_clusters_system = 0;
u64 used_inodes_user = 0;
u64 used_inodes_system = 0;
};
NANDCheckResult CheckNAND(IOS::HLE::Kernel& ios);
bool RepairNAND(IOS::HLE::Kernel& ios);