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;
|
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
|
struct NandStats
|
||||||
{
|
{
|
||||||
u32 cluster_size;
|
u32 cluster_size;
|
||||||
|
@ -124,6 +151,14 @@ struct DirectoryStats
|
||||||
u32 used_inodes;
|
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
|
struct FileStatus
|
||||||
{
|
{
|
||||||
u32 offset;
|
u32 offset;
|
||||||
|
@ -252,6 +287,9 @@ public:
|
||||||
/// Get usage information about a directory (used cluster and inode counts).
|
/// Get usage information about a directory (used cluster and inode counts).
|
||||||
virtual Result<DirectoryStats> GetDirectoryStats(const std::string& path) = 0;
|
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;
|
virtual void SetNandRedirects(std::vector<NandRedirect> nand_redirects) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -29,21 +29,6 @@ namespace IOS::HLE::FS
|
||||||
{
|
{
|
||||||
constexpr u32 BUFFER_CHUNK_SIZE = 65536;
|
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
|
HostFileSystem::HostFilename HostFileSystem::BuildFilename(const std::string& wii_path) const
|
||||||
{
|
{
|
||||||
for (const auto& redirect : m_nand_redirects)
|
for (const auto& redirect : m_nand_redirects)
|
||||||
|
@ -818,11 +803,24 @@ Result<NandStats> HostFileSystem::GetNandStats()
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<DirectoryStats> HostFileSystem::GetDirectoryStats(const std::string& wii_path)
|
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))
|
if (!IsValidPath(wii_path))
|
||||||
return ResultCode::Invalid;
|
return ResultCode::Invalid;
|
||||||
|
|
||||||
DirectoryStats stats{};
|
ExtendedDirectoryStats stats{};
|
||||||
std::string path(BuildFilename(wii_path).host_path);
|
std::string path(BuildFilename(wii_path).host_path);
|
||||||
File::FileInfo info(path);
|
File::FileInfo info(path);
|
||||||
if (!info.Exists())
|
if (!info.Exists())
|
||||||
|
@ -835,10 +833,8 @@ Result<DirectoryStats> HostFileSystem::GetDirectoryStats(const std::string& wii_
|
||||||
FixupDirectoryEntries(&parent_dir, wii_path == "/");
|
FixupDirectoryEntries(&parent_dir, wii_path == "/");
|
||||||
|
|
||||||
// add one for the folder itself
|
// add one for the folder itself
|
||||||
stats.used_inodes = static_cast<u32>(std::min<u64>(1 + parent_dir.size, TOTAL_INODES));
|
stats.used_inodes = 1 + parent_dir.size;
|
||||||
|
stats.used_clusters = ComputeUsedClusters(parent_dir);
|
||||||
const u64 clusters = ComputeUsedClusters(parent_dir);
|
|
||||||
stats.used_clusters = static_cast<u32>(std::min<u64>(clusters, USABLE_CLUSTERS));
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -55,6 +55,7 @@ public:
|
||||||
|
|
||||||
Result<NandStats> GetNandStats() override;
|
Result<NandStats> GetNandStats() override;
|
||||||
Result<DirectoryStats> GetDirectoryStats(const std::string& path) 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;
|
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;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -101,6 +101,10 @@ struct NANDCheckResult
|
||||||
{
|
{
|
||||||
bool bad = false;
|
bool bad = false;
|
||||||
std::unordered_set<u64> titles_to_remove;
|
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);
|
NANDCheckResult CheckNAND(IOS::HLE::Kernel& ios);
|
||||||
bool RepairNAND(IOS::HLE::Kernel& ios);
|
bool RepairNAND(IOS::HLE::Kernel& ios);
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#include <QMap>
|
#include <QMap>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
|
|
||||||
|
#include "Common/Align.h"
|
||||||
#include "Common/CommonPaths.h"
|
#include "Common/CommonPaths.h"
|
||||||
#include "Common/FileUtil.h"
|
#include "Common/FileUtil.h"
|
||||||
#include "Common/StringUtil.h"
|
#include "Common/StringUtil.h"
|
||||||
|
@ -32,6 +33,7 @@
|
||||||
#include "Core/HW/WiiSave.h"
|
#include "Core/HW/WiiSave.h"
|
||||||
#include "Core/HW/Wiimote.h"
|
#include "Core/HW/Wiimote.h"
|
||||||
#include "Core/IOS/ES/ES.h"
|
#include "Core/IOS/ES/ES.h"
|
||||||
|
#include "Core/IOS/FS/FileSystem.h"
|
||||||
#include "Core/IOS/IOS.h"
|
#include "Core/IOS/IOS.h"
|
||||||
#include "Core/IOS/USB/Bluetooth/BTEmu.h"
|
#include "Core/IOS/USB/Bluetooth/BTEmu.h"
|
||||||
#include "Core/Movie.h"
|
#include "Core/Movie.h"
|
||||||
|
@ -1137,7 +1139,42 @@ void MenuBar::CheckNAND()
|
||||||
WiiUtils::NANDCheckResult result = WiiUtils::CheckNAND(ios);
|
WiiUtils::NANDCheckResult result = WiiUtils::CheckNAND(ios);
|
||||||
if (!result.bad)
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue