mirror of
https://github.com/TwilitRealm/dusklight
synced 2026-07-11 13:08:33 -04:00
Allow hash-mismatched discs to be loaded with user confirmation
This commit is contained in:
+114
-36
@@ -80,16 +80,22 @@ void file_dialog_callback(void*, const char* path, const char* error) {
|
||||
|
||||
auto& state = prelaunch_state();
|
||||
const auto validation = iso::validate(path);
|
||||
if (validation != iso::ValidationError::Success) {
|
||||
state.errorString = escape(get_error_msg(validation));
|
||||
if (validation == iso::ValidationError::Success) {
|
||||
state.selectedDiscPath = path;
|
||||
state.errorString.clear();
|
||||
state.pendingDiscPath.clear();
|
||||
state.userAcceptedDiscPath.clear();
|
||||
getSettings().backend.isoPath.setValue(state.selectedDiscPath);
|
||||
config::Save();
|
||||
refresh_state();
|
||||
return;
|
||||
}
|
||||
|
||||
state.selectedDiscPath = path;
|
||||
state.errorString.clear();
|
||||
getSettings().backend.isoPath.setValue(state.selectedDiscPath);
|
||||
config::Save();
|
||||
refresh_state();
|
||||
if (validation == iso::ValidationError::HashMismatch) {
|
||||
state.pendingDiscPath = path;
|
||||
} else {
|
||||
state.pendingDiscPath.clear();
|
||||
}
|
||||
state.errorString = escape(get_error_msg(validation));
|
||||
}
|
||||
|
||||
PrelaunchState sPrelaunchState;
|
||||
@@ -100,13 +106,86 @@ PrelaunchState& prelaunch_state() noexcept {
|
||||
|
||||
void refresh_state() noexcept {
|
||||
auto& state = prelaunch_state();
|
||||
const auto validation = iso::validate(state.selectedDiscPath.c_str());
|
||||
if (state.selectedDiscPath.empty() || validation != iso::ValidationError::Success) {
|
||||
if (state.selectedDiscPath.empty()) {
|
||||
state.selectedDiscIsValid = false;
|
||||
return;
|
||||
}
|
||||
state.selectedDiscIsValid = true;
|
||||
state.selectedDiscIsPal = iso::isPal(state.selectedDiscPath.c_str());
|
||||
if (!state.userAcceptedDiscPath.empty() &&
|
||||
state.selectedDiscPath == state.userAcceptedDiscPath) {
|
||||
state.selectedDiscIsValid = true;
|
||||
state.selectedDiscIsPal = iso::isPal(state.selectedDiscPath.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
const auto validation = iso::validate(state.selectedDiscPath.c_str());
|
||||
// Allow HashMismatch, so that the user is not prompted to accept the warning on every boot.
|
||||
if (validation >= iso::ValidationError::HashMismatch) {
|
||||
state.selectedDiscIsValid = true;
|
||||
state.selectedDiscIsPal = iso::isPal(state.selectedDiscPath.c_str());
|
||||
state.userAcceptedDiscPath.clear();
|
||||
return;
|
||||
}
|
||||
state.selectedDiscIsValid = false;
|
||||
}
|
||||
|
||||
void try_push_verification_modal(Document& host) {
|
||||
auto& state = prelaunch_state();
|
||||
if (state.errorString.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto dismiss = [](Modal& modal) {
|
||||
auto& state = prelaunch_state();
|
||||
state.errorString.clear();
|
||||
state.pendingDiscPath.clear();
|
||||
modal.pop();
|
||||
};
|
||||
|
||||
if (!state.pendingDiscPath.empty()) {
|
||||
const Rml::String bodyRml = state.errorString + "<br/><br/>You may proceed at your own risk.";
|
||||
auto acceptHashMismatch = [](Modal& modal) {
|
||||
auto& st = prelaunch_state();
|
||||
std::string path = std::move(st.pendingDiscPath);
|
||||
st.pendingDiscPath.clear();
|
||||
st.errorString.clear();
|
||||
st.selectedDiscPath = path;
|
||||
st.userAcceptedDiscPath = path;
|
||||
getSettings().backend.isoPath.setValue(path);
|
||||
config::Save();
|
||||
refresh_state();
|
||||
modal.pop();
|
||||
};
|
||||
host.push(std::make_unique<Modal>(Modal::Props{
|
||||
.title = "Disc verification",
|
||||
.bodyRml = bodyRml,
|
||||
.actions =
|
||||
{
|
||||
ModalAction{
|
||||
.label = "Cancel",
|
||||
.onPressed = dismiss,
|
||||
},
|
||||
ModalAction{
|
||||
.label = "Continue anyway",
|
||||
.onPressed = acceptHashMismatch,
|
||||
},
|
||||
},
|
||||
.onDismiss = dismiss,
|
||||
}));
|
||||
return;
|
||||
}
|
||||
|
||||
host.push(std::make_unique<Modal>(Modal::Props{
|
||||
.title = "Disc verification",
|
||||
.bodyRml = state.errorString,
|
||||
.actions =
|
||||
{
|
||||
ModalAction{
|
||||
.label = "OK",
|
||||
.onPressed = dismiss,
|
||||
},
|
||||
},
|
||||
.onDismiss = dismiss,
|
||||
}));
|
||||
}
|
||||
|
||||
void ensure_initialized() noexcept {
|
||||
@@ -117,7 +196,9 @@ void ensure_initialized() noexcept {
|
||||
|
||||
state.selectedDiscPath = getSettings().backend.isoPath;
|
||||
state.initialDiscPath = state.selectedDiscPath;
|
||||
if (iso::validate(state.initialDiscPath.c_str()) == iso::ValidationError::Success) {
|
||||
const auto& res = iso::validate(state.initialDiscPath.c_str());
|
||||
if (res >= iso::ValidationError::HashMismatch) {
|
||||
state.initialDiscValidationRes = res;
|
||||
state.initialDiscIsPal = iso::isPal(state.initialDiscPath.c_str());
|
||||
}
|
||||
state.initialLanguage = getSettings().game.language;
|
||||
@@ -287,28 +368,14 @@ void Prelaunch::update() {
|
||||
ensure_initialized();
|
||||
try_apply_mirrored_layout(mDocument);
|
||||
|
||||
auto& state = prelaunch_state();
|
||||
if (!state.errorString.empty() && top_document() == this) {
|
||||
auto dismiss = [](Modal& modal) {
|
||||
prelaunch_state().errorString.clear();
|
||||
modal.pop();
|
||||
};
|
||||
push(std::make_unique<Modal>(Modal::Props{
|
||||
.title = "Invalid disc image",
|
||||
.bodyRml = state.errorString,
|
||||
.actions =
|
||||
{
|
||||
ModalAction{
|
||||
.label = "OK",
|
||||
.onPressed = dismiss,
|
||||
},
|
||||
},
|
||||
.onDismiss = dismiss,
|
||||
}));
|
||||
if (top_document() == this) {
|
||||
try_push_verification_modal(*this);
|
||||
}
|
||||
|
||||
const bool hasValidPath = prelaunch_state().selectedDiscIsValid;
|
||||
mDocument->SetClass("disc-ready", hasValidPath);
|
||||
const auto& state = prelaunch_state();
|
||||
|
||||
const bool hasValidPath = state.selectedDiscIsValid;
|
||||
mDocument->SetClass("disc-ready", IsGameLaunched);
|
||||
if (hasValidPath) {
|
||||
if (getSettings().backend.skipPreLaunchUI) {
|
||||
hide(true);
|
||||
@@ -327,17 +394,28 @@ void Prelaunch::update() {
|
||||
|
||||
const auto discStatusLabel = mDiscStatus->GetElementById("disc-status-label");
|
||||
|
||||
const bool discHashMismatchAccepted =
|
||||
hasValidPath && !state.userAcceptedDiscPath.empty() &&
|
||||
state.selectedDiscPath == state.userAcceptedDiscPath;
|
||||
|
||||
if (mDiscStatus != nullptr && discStatusLabel != nullptr) {
|
||||
if (hasValidPath) {
|
||||
if (state.initialDiscValidationRes == iso::ValidationError::Success) {
|
||||
mDiscStatus->SetAttribute("status", "good");
|
||||
discStatusLabel->SetInnerRML("Disc ready.");
|
||||
} else if (state.initialDiscValidationRes == iso::ValidationError::HashMismatch) {
|
||||
mDiscStatus->SetAttribute("status", "mismatch");
|
||||
discStatusLabel->SetInnerRML("Disc hash mismatch.");
|
||||
} else {
|
||||
mDiscStatus->RemoveAttribute("status");
|
||||
discStatusLabel->SetInnerRML("No disc image found.");
|
||||
}
|
||||
}
|
||||
if (mDiscDetail != nullptr) {
|
||||
if (hasValidPath) {
|
||||
mDiscDetail->SetProperty(Rml::PropertyId::Display, Rml::Style::Display::Block);
|
||||
mDiscDetail->SetInnerRML(
|
||||
prelaunch_state().initialDiscIsPal ? "GameCube • EUR" : "GameCube • USA");
|
||||
Rml::String innerRML = "GameCube • ";
|
||||
innerRML += state.initialDiscIsPal ? "EUR" : "USA";
|
||||
mDiscDetail->SetInnerRML(innerRML);
|
||||
} else {
|
||||
mDiscDetail->SetProperty(Rml::PropertyId::Display, Rml::Style::Display::None);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "dusk/iso_validate.hpp"
|
||||
#include "button.hpp"
|
||||
#include "document.hpp"
|
||||
|
||||
@@ -39,12 +40,15 @@ struct PrelaunchState {
|
||||
std::string selectedDiscPath;
|
||||
bool selectedDiscIsValid = false;
|
||||
bool selectedDiscIsPal = false;
|
||||
std::string errorString;
|
||||
bool initialDiscIsPal = false;
|
||||
std::string initialDiscPath;
|
||||
iso::ValidationError initialDiscValidationRes = iso::ValidationError::Unknown;
|
||||
bool initialDiscIsPal = false;
|
||||
GameLanguage initialLanguage = GameLanguage::English;
|
||||
std::string initialGraphicsBackend;
|
||||
int initialCardFileType = 0;
|
||||
std::string errorString;
|
||||
std::string pendingDiscPath;
|
||||
std::string userAcceptedDiscPath;
|
||||
};
|
||||
|
||||
PrelaunchState& prelaunch_state() noexcept;
|
||||
@@ -52,5 +56,6 @@ void ensure_initialized() noexcept;
|
||||
void refresh_state() noexcept;
|
||||
void open_iso_picker() noexcept;
|
||||
bool is_restart_pending() noexcept;
|
||||
void try_push_verification_modal(Document& host);
|
||||
|
||||
} // namespace dusk::ui
|
||||
|
||||
@@ -17,8 +17,6 @@
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "modal.hpp"
|
||||
|
||||
namespace dusk::ui {
|
||||
namespace {
|
||||
|
||||
@@ -869,27 +867,8 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) {
|
||||
}
|
||||
|
||||
void SettingsWindow::update() {
|
||||
// Show disc validation error message if present
|
||||
if (mPrelaunch && top_document() == this) {
|
||||
auto& state = prelaunch_state();
|
||||
if (!state.errorString.empty()) {
|
||||
auto dismissInvalidDisc = [](Modal& modal) {
|
||||
prelaunch_state().errorString.clear();
|
||||
modal.pop();
|
||||
};
|
||||
push_document(std::make_unique<Modal>(Modal::Props{
|
||||
.title = "Invalid disc image",
|
||||
.bodyRml = state.errorString,
|
||||
.actions =
|
||||
{
|
||||
ModalAction{
|
||||
.label = "OK",
|
||||
.onPressed = dismissInvalidDisc,
|
||||
},
|
||||
},
|
||||
.onDismiss = dismissInvalidDisc,
|
||||
}));
|
||||
}
|
||||
try_push_verification_modal(*this);
|
||||
}
|
||||
|
||||
Window::update();
|
||||
|
||||
@@ -621,7 +621,7 @@ int game_main(int argc, char* argv[]) {
|
||||
|
||||
// Invalidate a bad saved isoPath so that Dusk can't get blocked from starting up
|
||||
const std::string p = dusk::getSettings().backend.isoPath;
|
||||
if (!p.empty() && dusk::iso::validate(p.c_str()) != dusk::iso::ValidationError::Success) {
|
||||
if (!p.empty() && dusk::iso::validate(p.c_str()) < dusk::iso::ValidationError::HashMismatch) {
|
||||
dusk::getSettings().backend.isoPath.setValue("");
|
||||
}
|
||||
|
||||
@@ -629,7 +629,7 @@ int game_main(int argc, char* argv[]) {
|
||||
bool dvd_opened = false;
|
||||
if (parsed_arg_options.count("dvd")) {
|
||||
dvd_path = parsed_arg_options["dvd"].as<std::string>();
|
||||
if (dusk::iso::validate(dvd_path.c_str()) == dusk::iso::ValidationError::Success) {
|
||||
if (dusk::iso::validate(dvd_path.c_str()) >= dusk::iso::ValidationError::HashMismatch) {
|
||||
DuskLog.info("Loading DVD image from command line: {}", dvd_path);
|
||||
dvd_opened = aurora_dvd_open(dvd_path.c_str());
|
||||
if (!dvd_opened) {
|
||||
@@ -668,7 +668,8 @@ int game_main(int argc, char* argv[]) {
|
||||
if (dvd_path.empty()) {
|
||||
DuskLog.fatal("No DVD image specified, unable to boot!");
|
||||
}
|
||||
if (dusk::iso::validate(dvd_path.c_str()) != dusk::iso::ValidationError::Success) {
|
||||
if (!dusk::IsGameLaunched &&
|
||||
dusk::iso::validate(dvd_path.c_str()) < dusk::iso::ValidationError::HashMismatch) {
|
||||
DuskLog.fatal("DVD image failed verification: {}", dvd_path);
|
||||
}
|
||||
DuskLog.info("Loading DVD image: {}", dvd_path);
|
||||
|
||||
Reference in New Issue
Block a user