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:
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user