Disc verification cleanup

This commit is contained in:
Luke Street
2026-05-07 00:35:00 -06:00
parent 119830c979
commit 3968b67c5d
6 changed files with 111 additions and 82 deletions
+8
View File
@@ -228,6 +228,10 @@ body.mirrored version-info {
color: rgba(224, 219, 200, 65%);
}
#disc-status[status=pending] {
color: #FEE685;
}
#disc-status icon {
display: none;
width: 24dp;
@@ -261,6 +265,10 @@ body.mirrored version-info {
decorator: text("" center center);
}
#disc-status[status=pending] icon {
decorator: text("" center center);
}
#disc-version {
font-size: 20dp;
}
+2 -1
View File
@@ -171,7 +171,7 @@ ValidationError verify_disc(NodHandle* disc, VerificationStatus& status) {
return ValidationError::Success;
}
ValidationError validate(const char* path, VerificationStatus& status) {
ValidationError validate(const char* path, VerificationStatus& status, DiscInfo& info) {
const auto sdlStream = SDL_IOFromFile(path, "rb");
if (sdlStream == nullptr) {
return ValidationError::IOError;
@@ -202,6 +202,7 @@ ValidationError validate(const char* path, VerificationStatus& status) {
return ValidationError::WrongGame;
}
status.knownDisc = knownDisc;
info.isPal = knownDisc->region == Region::Europe;
if (!knownDisc->supported) {
return ValidationError::WrongVersion;
}
+1 -1
View File
@@ -29,7 +29,7 @@ struct DiscInfo {
};
ValidationError inspect(const char* path, DiscInfo& info);
ValidationError validate(const char* path, VerificationStatus& status);
ValidationError validate(const char* path, VerificationStatus& status, DiscInfo& info);
bool isPal(const char* path);
} // namespace dusk::iso
+82 -66
View File
@@ -68,6 +68,7 @@ constexpr std::array<SDL_DialogFileFilter, 2> kDiscFileFilters{{
struct DiscVerificationResult {
std::string path;
iso::DiscInfo info;
iso::ValidationError validation = iso::ValidationError::Unknown;
};
@@ -75,7 +76,7 @@ struct DiscVerificationTask {
explicit DiscVerificationTask(std::string discPath) : path(std::move(discPath)) {
worker = std::thread([this] {
try {
validation = iso::validate(path.c_str(), status);
validation = iso::validate(path.c_str(), status, info);
} catch (const std::exception& e) {
PrelaunchLog.error(
"Disc verification failed with exception for '{}': {}", path, e.what());
@@ -103,6 +104,7 @@ struct DiscVerificationTask {
[[nodiscard]] bool finished() const { return done.load(std::memory_order_acquire); }
std::string path;
iso::DiscInfo info;
iso::VerificationStatus status;
iso::ValidationError validation = iso::ValidationError::Unknown;
std::atomic_bool done = false;
@@ -112,17 +114,10 @@ struct DiscVerificationTask {
std::unique_ptr<DiscVerificationTask> sDiscVerificationTask;
bool sDiscVerificationModalPushed = false;
bool validation_allows_launch(iso::ValidationError validation) noexcept {
return validation == iso::ValidationError::Success ||
validation == iso::ValidationError::HashMismatch ||
validation == iso::ValidationError::Canceled;
}
bool verification_state_allows_launch(iso::ValidationError validation) noexcept {
return validation == iso::ValidationError::Unknown ||
validation == iso::ValidationError::Success ||
validation == iso::ValidationError::HashMismatch ||
validation == iso::ValidationError::Canceled;
validation == iso::ValidationError::HashMismatch;
}
iso::ValidationError verification_from_config(DiscVerificationState value) noexcept {
@@ -181,6 +176,7 @@ std::optional<DiscVerificationResult> take_finished_disc_verification() {
}
DiscVerificationResult result{
.path = sDiscVerificationTask->path,
.info = sDiscVerificationTask->info,
.validation = sDiscVerificationTask->validation,
};
sDiscVerificationTask->join();
@@ -218,17 +214,17 @@ void persist_disc_choice(const std::string& path, iso::ValidationError validatio
config::Save();
}
void apply_valid_disc_result(const std::string& path, iso::ValidationError validation) {
void apply_valid_disc_result(
const std::string& path, const iso::DiscInfo& info, iso::ValidationError validation) {
auto& state = prelaunch_state();
iso::DiscInfo info{};
const bool pal =
iso::inspect(path.c_str(), info) == iso::ValidationError::Success && info.isPal;
state.selectedDiscPath = path;
state.selectedDiscIsValid = true;
state.selectedDiscIsPal = pal;
state.initialDiscValidationRes = validation;
state.initialDiscIsPal = pal;
state.configuredDiscPath = path;
state.configuredDiscCanLaunch = true;
state.configuredDiscInfo = info;
state.configuredDiscValidation = validation;
if (state.activeDiscPath.empty() || path == state.activeDiscPath) {
state.activeDiscPath = path;
state.activeDiscInfo = info;
}
persist_disc_choice(path, validation);
}
@@ -239,23 +235,23 @@ void apply_disc_verification_result(const DiscVerificationResult& result) {
result.validation == iso::ValidationError::Canceled)
{
state.pendingDiscPath = result.path;
state.pendingDiscInfo = result.info;
state.pendingDiscValidation = result.validation;
state.errorString = escape(get_error_msg(result.validation));
return;
}
if (validation_allows_launch(result.validation)) {
apply_valid_disc_result(result.path, result.validation);
if (result.validation == iso::ValidationError::Success) {
state.errorString.clear();
state.pendingDiscPath.clear();
state.pendingDiscValidation = iso::ValidationError::Unknown;
}
if (result.validation == iso::ValidationError::Success) {
apply_valid_disc_result(result.path, result.info, result.validation);
state.errorString.clear();
state.pendingDiscPath.clear();
state.pendingDiscInfo = {};
state.pendingDiscValidation = iso::ValidationError::Unknown;
return;
}
state.pendingDiscPath.clear();
state.pendingDiscInfo = {};
state.pendingDiscValidation = iso::ValidationError::Unknown;
state.errorString = escape(get_error_msg(result.validation));
}
@@ -414,41 +410,48 @@ PrelaunchState& prelaunch_state() noexcept {
return sPrelaunchState;
}
void refresh_state() noexcept {
void refresh_configured_disc_state() noexcept {
auto& state = prelaunch_state();
if (state.selectedDiscPath.empty()) {
state.selectedDiscIsValid = false;
state.selectedDiscIsPal = false;
if (state.configuredDiscPath.empty()) {
state.configuredDiscCanLaunch = false;
state.configuredDiscInfo = {};
state.configuredDiscValidation = iso::ValidationError::Unknown;
return;
}
iso::DiscInfo info{};
const auto metadataValidation = iso::inspect(state.selectedDiscPath.c_str(), info);
const auto metadataValidation = iso::inspect(state.configuredDiscPath.c_str(), info);
if (metadataValidation != iso::ValidationError::Success) {
state.selectedDiscIsValid = false;
state.selectedDiscIsPal = false;
state.initialDiscValidationRes = metadataValidation;
state.initialDiscIsPal = false;
state.configuredDiscCanLaunch = false;
state.configuredDiscInfo = {};
state.configuredDiscValidation = metadataValidation;
if (state.configuredDiscPath == state.activeDiscPath) {
state.activeDiscInfo = {};
}
return;
}
auto verification = state.initialDiscValidationRes;
if (state.selectedDiscPath == getSettings().backend.isoPath.getValue()) {
auto verification = iso::ValidationError::Unknown;
if (state.configuredDiscPath == getSettings().backend.isoPath.getValue()) {
verification = verification_from_config(getSettings().backend.isoVerification.getValue());
}
if (verification_state_allows_launch(verification)) {
state.selectedDiscIsValid = true;
state.selectedDiscIsPal = info.isPal;
state.initialDiscValidationRes = verification;
state.initialDiscIsPal = info.isPal;
state.configuredDiscCanLaunch = true;
state.configuredDiscInfo = info;
state.configuredDiscValidation = verification;
if (state.configuredDiscPath == state.activeDiscPath) {
state.activeDiscInfo = info;
}
return;
}
state.selectedDiscIsValid = false;
state.selectedDiscIsPal = false;
state.initialDiscValidationRes = iso::ValidationError::Unknown;
state.initialDiscIsPal = false;
state.configuredDiscCanLaunch = false;
state.configuredDiscInfo = {};
state.configuredDiscValidation = iso::ValidationError::Unknown;
if (state.configuredDiscPath == state.activeDiscPath) {
state.activeDiscInfo = {};
}
}
void try_push_verification_modal(Document& host) {
@@ -467,6 +470,7 @@ void try_push_verification_modal(Document& host) {
auto& state = prelaunch_state();
state.errorString.clear();
state.pendingDiscPath.clear();
state.pendingDiscInfo = {};
state.pendingDiscValidation = iso::ValidationError::Unknown;
modal.pop();
};
@@ -477,12 +481,14 @@ void try_push_verification_modal(Document& host) {
auto acceptHashMismatch = [](Modal& modal) {
auto& st = prelaunch_state();
std::string path = std::move(st.pendingDiscPath);
const auto info = st.pendingDiscInfo;
const auto validation = st.pendingDiscValidation;
st.pendingDiscPath.clear();
st.pendingDiscInfo = {};
st.pendingDiscValidation = iso::ValidationError::Unknown;
st.errorString.clear();
apply_valid_disc_result(path, validation);
refresh_state();
apply_valid_disc_result(path, info, validation);
refresh_configured_disc_state();
modal.pop();
};
host.push(std::make_unique<Modal>(Modal::Props{
@@ -527,16 +533,16 @@ void ensure_initialized() noexcept {
return;
}
state.selectedDiscPath = getSettings().backend.isoPath;
state.initialDiscPath = state.selectedDiscPath;
state.initialDiscValidationRes =
state.configuredDiscPath = getSettings().backend.isoPath;
state.activeDiscPath = state.configuredDiscPath;
state.configuredDiscValidation =
verification_from_config(getSettings().backend.isoVerification.getValue());
state.initialLanguage = getSettings().game.language;
state.initialGraphicsBackend = getSettings().backend.graphicsBackend;
state.initialCardFileType = getSettings().backend.cardFileType;
state.errorString.clear();
state.initialized = true;
refresh_state();
refresh_configured_disc_state();
}
void open_iso_picker() noexcept {
@@ -547,7 +553,7 @@ void open_iso_picker() noexcept {
bool is_restart_pending() noexcept {
const auto& state = prelaunch_state();
if (!state.initialDiscPath.empty() && state.selectedDiscPath != state.initialDiscPath) {
if (!state.activeDiscPath.empty() && state.configuredDiscPath != state.activeDiscPath) {
return true;
}
if (getSettings().backend.graphicsBackend.getValue() != state.initialGraphicsBackend) {
@@ -579,10 +585,11 @@ Prelaunch::Prelaunch() : Document(kDocumentSource), mRoot(mDocument->GetElementB
if (auto* menuList = mDocument->GetElementById("menu-list")) {
auto& state = prelaunch_state();
mMenuButtons.push_back(std::make_unique<Button>(
menuList, state.selectedDiscIsValid ? "Play" : "Select Disc Image"));
const bool activeDiscLoaded = !state.activeDiscPath.empty();
mMenuButtons.push_back(
std::make_unique<Button>(menuList, activeDiscLoaded ? "Play" : "Select Disc Image"));
mMenuButtons.back()->on_pressed([this] {
if (!prelaunch_state().selectedDiscIsValid) {
if (prelaunch_state().activeDiscPath.empty()) {
open_iso_picker();
return;
}
@@ -705,9 +712,12 @@ void Prelaunch::update() {
const auto& state = prelaunch_state();
const bool hasValidPath = state.selectedDiscIsValid;
const bool canLaunchConfiguredDisc = state.configuredDiscCanLaunch;
const bool activeDiscLoaded = !state.activeDiscPath.empty();
const bool discRestartPending =
activeDiscLoaded && state.configuredDiscPath != state.activeDiscPath;
mDocument->SetClass("disc-ready", IsGameLaunched);
if (hasValidPath) {
if (canLaunchConfiguredDisc) {
if (getSettings().backend.skipPreLaunchUI) {
hide(true);
}
@@ -720,31 +730,37 @@ void Prelaunch::update() {
}
if (!mMenuButtons.empty()) {
mMenuButtons[0]->set_text(hasValidPath ? "Play" : "Select Disc Image");
mMenuButtons[0]->set_text(activeDiscLoaded ? "Play" : "Select Disc Image");
}
const auto discStatusLabel = mDiscStatus->GetElementById("disc-status-label");
if (mDiscStatus != nullptr && discStatusLabel != nullptr) {
if (state.initialDiscValidationRes == iso::ValidationError::Success) {
if (!activeDiscLoaded) {
mDiscStatus->RemoveAttribute("status");
discStatusLabel->SetInnerRML("No disc image found.");
} else if (discRestartPending) {
mDiscStatus->SetAttribute("status", "pending");
discStatusLabel->SetInnerRML("Pending restart.");
} else if (state.configuredDiscValidation == iso::ValidationError::Success) {
mDiscStatus->SetAttribute("status", "good");
discStatusLabel->SetInnerRML("Disc ready.");
} else if (state.initialDiscValidationRes == iso::ValidationError::HashMismatch) {
} else if (state.configuredDiscValidation == iso::ValidationError::HashMismatch) {
mDiscStatus->SetAttribute("status", "mismatch");
discStatusLabel->SetInnerRML("Disc hash mismatch.");
} else if (hasValidPath) {
} else if (canLaunchConfiguredDisc) {
mDiscStatus->SetAttribute("status", "unknown");
discStatusLabel->SetInnerRML("Disc not verified.");
} else {
mDiscStatus->RemoveAttribute("status");
discStatusLabel->SetInnerRML("No disc image found.");
mDiscStatus->SetAttribute("status", "bad");
discStatusLabel->SetInnerRML("Disc unavailable.");
}
}
if (mDiscDetail != nullptr) {
if (hasValidPath) {
if (activeDiscLoaded) {
mDiscDetail->SetProperty(Rml::PropertyId::Display, Rml::Style::Display::Block);
Rml::String innerRML = "GameCube • ";
innerRML += state.initialDiscIsPal ? "EUR" : "USA";
innerRML += state.activeDiscInfo.isPal ? "EUR" : "USA";
mDiscDetail->SetInnerRML(innerRML);
} else {
mDiscDetail->SetProperty(Rml::PropertyId::Display, Rml::Style::Display::None);
+10 -9
View File
@@ -1,8 +1,8 @@
#pragma once
#include "dusk/iso_validate.hpp"
#include "button.hpp"
#include "document.hpp"
#include "dusk/iso_validate.hpp"
#include <memory>
#include <string>
@@ -26,7 +26,7 @@ protected:
private:
bool mEntranceAnimationStarted = false;
bool mRestartSuppressed = false;
std::vector<std::unique_ptr<Button>> mMenuButtons;
std::vector<std::unique_ptr<Button> > mMenuButtons;
Rml::Element* mRoot = nullptr;
Rml::Element* mDiscStatus = nullptr;
Rml::Element* mDiscDetail = nullptr;
@@ -37,23 +37,24 @@ class PrelaunchOptions;
struct PrelaunchState {
bool initialized = false;
std::string selectedDiscPath;
bool selectedDiscIsValid = false;
bool selectedDiscIsPal = false;
std::string initialDiscPath;
iso::ValidationError initialDiscValidationRes = iso::ValidationError::Unknown;
bool initialDiscIsPal = false;
std::string configuredDiscPath;
bool configuredDiscCanLaunch = false;
iso::DiscInfo configuredDiscInfo{};
iso::ValidationError configuredDiscValidation = iso::ValidationError::Unknown;
std::string activeDiscPath;
iso::DiscInfo activeDiscInfo{};
GameLanguage initialLanguage = GameLanguage::English;
std::string initialGraphicsBackend;
int initialCardFileType = 0;
std::string errorString;
std::string pendingDiscPath;
iso::DiscInfo pendingDiscInfo{};
iso::ValidationError pendingDiscValidation = iso::ValidationError::Unknown;
};
PrelaunchState& prelaunch_state() noexcept;
void ensure_initialized() noexcept;
void refresh_state() noexcept;
void refresh_configured_disc_state() noexcept;
void open_iso_picker() noexcept;
bool is_restart_pending() noexcept;
void try_push_verification_modal(Document& host);
+8 -5
View File
@@ -302,7 +302,7 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) {
.key = "Disc Image",
.getValue =
[] {
const auto& path = prelaunch_state().selectedDiscPath;
const auto& path = prelaunch_state().configuredDiscPath;
std::string display;
if (path.empty()) {
display = "(none)";
@@ -317,8 +317,8 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) {
.isModified =
[] {
const auto& state = prelaunch_state();
const auto& initial = state.initialDiscPath;
return !initial.empty() && state.selectedDiscPath != initial;
const auto& active = state.activeDiscPath;
return !active.empty() && state.configuredDiscPath != active;
},
})
.on_pressed([] { open_iso_picker(); }),
@@ -332,7 +332,9 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) {
.getValue =
[] {
const auto& state = prelaunch_state();
if (!state.selectedDiscIsValid || !state.selectedDiscIsPal) {
if (!state.configuredDiscCanLaunch ||
!state.configuredDiscInfo.isPal)
{
return kLanguageNames[0];
}
const u8 idx = static_cast<u8>(getSettings().game.language.getValue());
@@ -341,7 +343,8 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) {
.isDisabled =
[] {
const auto& state = prelaunch_state();
return !state.selectedDiscIsValid || !state.selectedDiscIsPal;
return !state.configuredDiscCanLaunch ||
!state.configuredDiscInfo.isPal;
},
.isModified =
[] {