Merge pull request #12047 from AdmiralCurtiss/nand-stats-user
Add file size stats to NAND Check.
This commit is contained in:
commit
a912a2c3f5
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <QMap>
|
||||
#include <QUrl>
|
||||
|
||||
#include "Common/Align.h"
|
||||
#include "Common/CommonPaths.h"
|
||||
#include "Common/FileUtil.h"
|
||||
#include "Common/StringUtil.h"
|
||||
|
@ -32,6 +33,7 @@
|
|||
#include "Core/HW/WiiSave.h"
|
||||
#include "Core/HW/Wiimote.h"
|
||||
#include "Core/IOS/ES/ES.h"
|
||||
#include "Core/IOS/FS/FileSystem.h"
|
||||
#include "Core/IOS/IOS.h"
|
||||
#include "Core/IOS/USB/Bluetooth/BTEmu.h"
|
||||
#include "Core/Movie.h"
|
||||
|
@ -1137,7 +1139,42 @@ void MenuBar::CheckNAND()
|
|||
WiiUtils::NANDCheckResult result = WiiUtils::CheckNAND(ios);
|
||||
if (!result.bad)
|
||||
{
|
||||
ModalMessageBox::information(this, tr("NAND Check"), tr("No issues have been detected."));
|
||||
const bool overfull = result.used_clusters_user > IOS::HLE::FS::USER_CLUSTERS ||
|
||||
result.used_clusters_system > IOS::HLE::FS::SYSTEM_CLUSTERS;
|
||||
const QString user_cluster_message =
|
||||
tr("The user-accessible part of your NAND contains %1 blocks (%2 KiB) "
|
||||
"of data, out of an allowed maximum of %3 blocks (%4 KiB).")
|
||||
.arg(Common::AlignUp(result.used_clusters_user, IOS::HLE::FS::CLUSTERS_PER_BLOCK) /
|
||||
IOS::HLE::FS::CLUSTERS_PER_BLOCK)
|
||||
.arg((result.used_clusters_user * IOS::HLE::FS::CLUSTER_SIZE) / 1024)
|
||||
.arg(IOS::HLE::FS::USER_CLUSTERS / IOS::HLE::FS::CLUSTERS_PER_BLOCK)
|
||||
.arg((IOS::HLE::FS::USER_CLUSTERS * IOS::HLE::FS::CLUSTER_SIZE) / 1024);
|
||||
const QString system_cluster_message =
|
||||
tr("The system-reserved part of your NAND contains %1 blocks (%2 KiB) "
|
||||
"of data, out of an allowed maximum of %3 blocks (%4 KiB).")
|
||||
.arg(Common::AlignUp(result.used_clusters_system, IOS::HLE::FS::CLUSTERS_PER_BLOCK) /
|
||||
IOS::HLE::FS::CLUSTERS_PER_BLOCK)
|
||||
.arg((result.used_clusters_system * IOS::HLE::FS::CLUSTER_SIZE) / 1024)
|
||||
.arg(IOS::HLE::FS::SYSTEM_CLUSTERS / IOS::HLE::FS::CLUSTERS_PER_BLOCK)
|
||||
.arg((IOS::HLE::FS::SYSTEM_CLUSTERS * IOS::HLE::FS::CLUSTER_SIZE) / 1024);
|
||||
|
||||
if (overfull)
|
||||
{
|
||||
ModalMessageBox::warning(this, tr("NAND Check"),
|
||||
QStringLiteral("<b>%1</b><br/><br/>%2<br/><br/>%3")
|
||||
.arg(tr("Your NAND contains more data than allowed. Wii "
|
||||
"software may behave incorrectly or not allow saving."))
|
||||
.arg(user_cluster_message)
|
||||
.arg(system_cluster_message));
|
||||
}
|
||||
else
|
||||
{
|
||||
ModalMessageBox::information(this, tr("NAND Check"),
|
||||
QStringLiteral("<b>%1</b><br/><br/>%2<br/><br/>%3")
|
||||
.arg(tr("No issues have been detected."))
|
||||
.arg(user_cluster_message)
|
||||
.arg(system_cluster_message));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue