v1.1.0 — SD card recovery, 25 new filesystem types, UI theme overhaul

- SD card recovery tool: detects and repairs cards Windows can't see,
  including mid-format failures, corrupted partition tables, RAW media.
  Uses IOCTL_DISK_CREATE_DISK + IOCTL_DISK_SET_DRIVE_LAYOUT_EX directly.
- 25 new filesystem types: F2FS, JFFS2, NILFS2, FATX, STFS, GDFX, PS2MC,
  VHD, VHDX, VMDK, QCOW2, VDI, RVZ, WUA, WBFS, NRG, CDFS, HDFS + more
- Detection routines for all new types with correct magic number signatures
- UI theme: bright green replaced with pale grayish peach, all text white
- hwdiag rebuilt in Release mode; added build_hwdiag_release.bat
- garbage.xtx auto-copied to build output directory post-build
This commit is contained in:
DigiJ
2026-03-11 23:56:12 -07:00
parent ff643d23f6
commit 6e40691548
17 changed files with 1592 additions and 35 deletions

View File

@@ -233,8 +233,8 @@ void DiagnosticsTab::displaySmartData(const SmartData& data)
{
case SmartStatus::OK:
healthText = tr("PASSED - Healthy");
healthColor = QColor(0, 180, 0);
m_healthIcon->setStyleSheet("background-color: #00b400; border-radius: 24px;");
healthColor = QColor(212, 165, 116);
m_healthIcon->setStyleSheet("background-color: #d4a574; border-radius: 24px;");
break;
case SmartStatus::Warning:
healthText = tr("WARNING - Issues Detected");
@@ -523,7 +523,7 @@ QColor DiagnosticsTab::smartStatusColor(SmartStatus status)
{
switch (status)
{
case SmartStatus::OK: return QColor(0, 180, 0);
case SmartStatus::OK: return QColor(212, 165, 116);
case SmartStatus::Warning: return QColor(255, 180, 0);
case SmartStatus::Critical: return QColor(255, 0, 0);
default: return QColor(128, 128, 128);

View File

@@ -3,6 +3,7 @@
#include "core/disk/DiskEnumerator.h"
#include "core/disk/RawDiskHandle.h"
#include "core/maintenance/SecureErase.h"
#include "core/maintenance/SdCardRecovery.h"
#include "core/recovery/BootRepair.h"
#include <QCheckBox>
@@ -137,6 +138,60 @@ void MaintenanceTab::setupUi()
bootLayout->addWidget(m_bootStatusLabel);
layout->addWidget(bootGroup);
// ===== SD Card Recovery Section =====
auto* sdGroup = new QGroupBox(tr("SD Card Recovery"));
auto* sdLayout = new QGridLayout(sdGroup);
auto* sdInfo = new QLabel(
tr("Detect and fix SD/microSD cards that Windows cannot see.\n"
"Repairs corrupted partition tables from interrupted formats, "
"RAW cards, and uninitialized media."));
sdInfo->setWordWrap(true);
sdLayout->addWidget(sdInfo, 0, 0, 1, 3);
m_sdScanBtn = new QPushButton(tr("Scan for SD Cards"));
m_sdScanBtn->setToolTip(tr("Scan all disk interfaces for SD/MMC cards, including invisible ones"));
connect(m_sdScanBtn, &QPushButton::clicked, this, &MaintenanceTab::onSdScan);
sdLayout->addWidget(m_sdScanBtn, 1, 0);
m_sdCardCombo = new QComboBox();
sdLayout->addWidget(m_sdCardCombo, 1, 1, 1, 2);
sdLayout->addWidget(new QLabel(tr("Format As:")), 2, 0);
m_sdFsCombo = new QComboBox();
m_sdFsCombo->addItems({
tr("FAT32 (recommended for <= 32 GB)"),
tr("exFAT (recommended for > 32 GB)"),
tr("NTFS")
});
sdLayout->addWidget(m_sdFsCombo, 2, 1, 1, 2);
sdLayout->addWidget(new QLabel(tr("Volume Label:")), 3, 0);
m_sdLabelEdit = new QLineEdit(QStringLiteral("SD_CARD"));
m_sdLabelEdit->setMaxLength(11);
sdLayout->addWidget(m_sdLabelEdit, 3, 1, 1, 2);
m_sdFixBtn = new QPushButton(tr("Fix SD Card"));
m_sdFixBtn->setMinimumHeight(40);
m_sdFixBtn->setStyleSheet(
"QPushButton { background-color: #d4a574; color: #1e1e2e; font-size: 14px; "
"font-weight: bold; border: 2px solid #b08050; border-radius: 6px; }"
"QPushButton:hover { background-color: #e0b584; }"
"QPushButton:pressed { background-color: #c49060; }");
m_sdFixBtn->setEnabled(false);
connect(m_sdFixBtn, &QPushButton::clicked, this, &MaintenanceTab::onSdFix);
sdLayout->addWidget(m_sdFixBtn, 4, 0, 1, 3);
m_sdProgress = new QProgressBar();
m_sdProgress->setVisible(false);
sdLayout->addWidget(m_sdProgress, 5, 0, 1, 3);
m_sdStatusLabel = new QLabel();
m_sdStatusLabel->setWordWrap(true);
sdLayout->addWidget(m_sdStatusLabel, 6, 0, 1, 3);
layout->addWidget(sdGroup);
layout->addStretch();
}
@@ -465,6 +520,149 @@ void MaintenanceTab::onReinstallBootloader()
thread->start();
}
void MaintenanceTab::onSdScan()
{
m_sdScanBtn->setEnabled(false);
m_sdStatusLabel->setText(tr("Scanning for SD cards..."));
m_sdCardCombo->clear();
m_detectedCards.clear();
m_sdFixBtn->setEnabled(false);
auto* thread = QThread::create([this]() {
auto result = SdCardRecovery::detectSdCards();
if (result.isError())
{
QMetaObject::invokeMethod(m_sdStatusLabel, "setText",
Qt::QueuedConnection,
Q_ARG(QString, tr("Scan failed: %1")
.arg(QString::fromStdString(result.error().message))));
return;
}
m_detectedCards = std::move(result.value());
});
connect(thread, &QThread::finished, thread, &QThread::deleteLater);
connect(thread, &QThread::finished, this, [this]() {
m_sdScanBtn->setEnabled(true);
if (m_detectedCards.empty())
{
m_sdStatusLabel->setText(
tr("No SD/MMC cards detected.\n"
"Make sure the card is inserted in a reader and the reader is connected."));
return;
}
for (const auto& card : m_detectedCards)
{
QString statusStr;
switch (card.status)
{
case SdCardStatus::Healthy: statusStr = tr("Healthy"); break;
case SdCardStatus::NoPartitionTable: statusStr = tr("NO PARTITION TABLE"); break;
case SdCardStatus::CorruptPartition: statusStr = tr("CORRUPT"); break;
case SdCardStatus::RawFilesystem: statusStr = tr("RAW"); break;
case SdCardStatus::NoMedia: statusStr = tr("No Media"); break;
default: statusStr = tr("Unknown"); break;
}
QString label = QString("Disk %1: %2 (%3) [%4]")
.arg(card.diskId)
.arg(QString::fromStdWString(card.model))
.arg(formatSize(card.sizeBytes))
.arg(statusStr);
m_sdCardCombo->addItem(label, card.diskId);
}
m_sdFixBtn->setEnabled(true);
m_sdStatusLabel->setText(
tr("Found %1 SD card(s). Select one and click Fix to repair.")
.arg(m_detectedCards.size()));
emit statusMessage(tr("SD card scan complete — %1 card(s) found")
.arg(m_detectedCards.size()));
});
thread->start();
}
void MaintenanceTab::onSdFix()
{
int diskId = m_sdCardCombo->currentData().toInt();
// Find the card info
const SdCardInfo* cardInfo = nullptr;
for (const auto& card : m_detectedCards)
{
if (card.diskId == diskId)
{
cardInfo = &card;
break;
}
}
if (!cardInfo)
return;
// Confirmation
auto reply = QMessageBox::warning(this, tr("Fix SD Card"),
tr("This will ERASE ALL DATA on:\n\n"
"Disk %1: %2 (%3)\n\n"
"The card will be cleaned, repartitioned, and formatted.\n\n"
"Continue?")
.arg(diskId)
.arg(QString::fromStdWString(cardInfo->model))
.arg(formatSize(cardInfo->sizeBytes)),
QMessageBox::Yes | QMessageBox::No);
if (reply != QMessageBox::Yes)
return;
// Build config
SdFixConfig config;
config.action = SdFixAction::CleanAndFormat;
switch (m_sdFsCombo->currentIndex())
{
case 0: config.targetFs = FilesystemType::FAT32; break;
case 1: config.targetFs = FilesystemType::ExFAT; break;
case 2: config.targetFs = FilesystemType::NTFS; break;
}
config.volumeLabel = m_sdLabelEdit->text().toStdWString();
m_sdFixBtn->setEnabled(false);
m_sdScanBtn->setEnabled(false);
m_sdProgress->setVisible(true);
m_sdProgress->setValue(0);
m_sdStatusLabel->setText(tr("Fixing SD card..."));
auto* thread = QThread::create([this, diskId, config]() {
auto result = SdCardRecovery::fixCard(diskId, config,
[this](const std::string& stage, int pct) {
QMetaObject::invokeMethod(m_sdProgress, "setValue",
Qt::QueuedConnection, Q_ARG(int, pct));
QMetaObject::invokeMethod(m_sdStatusLabel, "setText",
Qt::QueuedConnection,
Q_ARG(QString, QString::fromStdString(stage)));
});
if (result.isError())
{
QMetaObject::invokeMethod(m_sdStatusLabel, "setText",
Qt::QueuedConnection,
Q_ARG(QString, tr("Fix failed: %1")
.arg(QString::fromStdString(result.error().message))));
}
});
connect(thread, &QThread::finished, thread, &QThread::deleteLater);
connect(thread, &QThread::finished, this, [this]() {
m_sdProgress->setVisible(false);
m_sdFixBtn->setEnabled(true);
m_sdScanBtn->setEnabled(true);
emit statusMessage(tr("SD card fix completed"));
});
thread->start();
}
QString MaintenanceTab::formatSize(uint64_t bytes)
{
if (bytes >= 1099511627776ULL)

View File

@@ -3,6 +3,7 @@
#include "core/common/Types.h"
#include "core/disk/DiskEnumerator.h"
#include "core/maintenance/SecureErase.h"
#include "core/maintenance/SdCardRecovery.h"
#include <QWidget>
#include <atomic>
@@ -40,6 +41,8 @@ private slots:
void onRepairGpt();
void onRepairBcd();
void onReinstallBootloader();
void onSdScan();
void onSdFix();
private:
void setupUi();
@@ -65,6 +68,16 @@ private:
QProgressBar* m_bootProgress = nullptr;
QLabel* m_bootStatusLabel = nullptr;
// SD Card Recovery
QComboBox* m_sdCardCombo = nullptr;
QPushButton* m_sdScanBtn = nullptr;
QPushButton* m_sdFixBtn = nullptr;
QComboBox* m_sdFsCombo = nullptr;
QLineEdit* m_sdLabelEdit = nullptr;
QLabel* m_sdStatusLabel = nullptr;
QProgressBar* m_sdProgress = nullptr;
std::vector<SdCardInfo> m_detectedCards;
// Data
SystemDiskSnapshot m_snapshot;
std::atomic<bool> m_cancelFlag{false};

View File

@@ -291,10 +291,10 @@ QColor DiskMapWidget::colorForFilesystem(FilesystemType fs)
switch (fs)
{
case FilesystemType::NTFS: return QColor(52, 101, 164);
case FilesystemType::FAT32: return QColor(78, 154, 6);
case FilesystemType::FAT16: return QColor(78, 154, 6);
case FilesystemType::FAT12: return QColor(78, 154, 6);
case FilesystemType::ExFAT: return QColor(115, 210, 22);
case FilesystemType::FAT32: return QColor(180, 145, 120);
case FilesystemType::FAT16: return QColor(180, 145, 120);
case FilesystemType::FAT12: return QColor(180, 145, 120);
case FilesystemType::ExFAT: return QColor(212, 165, 116);
case FilesystemType::ReFS: return QColor(32, 74, 135);
case FilesystemType::Ext2: return QColor(204, 0, 0);
case FilesystemType::Ext3: return QColor(204, 0, 0);
@@ -307,6 +307,29 @@ QColor DiskMapWidget::colorForFilesystem(FilesystemType fs)
case FilesystemType::ISO9660: return QColor(85, 87, 83);
case FilesystemType::UDF: return QColor(85, 87, 83);
case FilesystemType::Unallocated: return QColor(80, 80, 80);
// Flash-optimized
case FilesystemType::F2FS: return QColor(0, 150, 136); // Teal
case FilesystemType::JFFS2: return QColor(121, 85, 72); // Brown
case FilesystemType::NILFS2: return QColor(158, 157, 36); // Lime
// Console / gaming
case FilesystemType::FATX: return QColor(76, 175, 80); // Xbox green
case FilesystemType::STFS: return QColor(56, 142, 60); // Xbox dark green
case FilesystemType::GDFX: return QColor(46, 125, 50); // Xbox disc green
case FilesystemType::PS2MC: return QColor(33, 150, 243); // PS blue
// Virtual disk images
case FilesystemType::VHD: return QColor(0, 120, 215); // Windows blue
case FilesystemType::VHDX: return QColor(0, 99, 177);
case FilesystemType::VMDK: return QColor(120, 144, 156); // Slate
case FilesystemType::QCOW2: return QColor(255, 87, 34); // Deep orange
case FilesystemType::VDI: return QColor(63, 81, 181); // Indigo
// Disc images
case FilesystemType::RVZ: return QColor(156, 39, 176); // Purple
case FilesystemType::WUA: return QColor(103, 58, 183); // Deep purple
case FilesystemType::WBFs: return QColor(0, 188, 212); // Cyan
case FilesystemType::NRG: return QColor(96, 125, 139); // Blue grey
case FilesystemType::MDF: return QColor(96, 125, 139);
case FilesystemType::CDI: return QColor(96, 125, 139);
case FilesystemType::CDFS: return QColor(85, 87, 83);
default: return QColor(136, 138, 133);
}
}
@@ -332,6 +355,25 @@ QString DiskMapWidget::filesystemShortName(FilesystemType fs)
case FilesystemType::SWAP_LINUX: return QStringLiteral("Swap");
case FilesystemType::ISO9660: return QStringLiteral("ISO9660");
case FilesystemType::UDF: return QStringLiteral("UDF");
case FilesystemType::F2FS: return QStringLiteral("F2FS");
case FilesystemType::JFFS2: return QStringLiteral("JFFS2");
case FilesystemType::NILFS2: return QStringLiteral("NILFS2");
case FilesystemType::FATX: return QStringLiteral("FATX");
case FilesystemType::STFS: return QStringLiteral("STFS");
case FilesystemType::GDFX: return QStringLiteral("GDFX");
case FilesystemType::PS2MC: return QStringLiteral("PS2MC");
case FilesystemType::VHD: return QStringLiteral("VHD");
case FilesystemType::VHDX: return QStringLiteral("VHDX");
case FilesystemType::VMDK: return QStringLiteral("VMDK");
case FilesystemType::QCOW2: return QStringLiteral("QCOW2");
case FilesystemType::VDI: return QStringLiteral("VDI");
case FilesystemType::RVZ: return QStringLiteral("RVZ");
case FilesystemType::WUA: return QStringLiteral("WUA");
case FilesystemType::WBFs: return QStringLiteral("WBFS");
case FilesystemType::NRG: return QStringLiteral("NRG");
case FilesystemType::MDF: return QStringLiteral("MDF");
case FilesystemType::CDI: return QStringLiteral("CDI");
case FilesystemType::CDFS: return QStringLiteral("CDFS");
case FilesystemType::Unallocated: return QStringLiteral("Free");
case FilesystemType::Unknown: return QStringLiteral("Unknown");
case FilesystemType::Raw: return QStringLiteral("RAW");