Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 108 additions & 11 deletions launcher/recomp-ui/launcher_main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ namespace {

struct ModState {
bool adaptive_widescreen = true;
std::string widescreen_mode = "adaptive";
// HD rendering. Off by default: it costs GPU time and VRAM, and the
// faithful native output stays the reference. internal_resolution
// multiplies 3D sample density; texture_upscale filters each decoded DS
Expand Down Expand Up @@ -157,6 +158,20 @@ constexpr std::array<RendererChoice, 3> kRendererChoices{{

constexpr const char* kDefaultRendererType = "compute";

struct WidescreenChoice {
const char* value;
const char* label;
int width;
};

constexpr std::array<WidescreenChoice, 5> kWidescreenChoices{{
{"adaptive", "Dynamic", 0},
{"fixed-4-3", "4:3", 256},
{"fixed-5-3", "5:3", 320},
{"fixed-2-1", "2:1", 384},
{"fixed-21-9", "21:9", 448},
}};

struct SensitivityChoice {
int percent;
const char* label;
Expand Down Expand Up @@ -443,6 +458,37 @@ const char* runner_renderer_policy_arg(const ModState& state) {
: "auto";
}

bool is_widescreen_choice(const char* value) {
if (!value) return false;
for (const WidescreenChoice& choice : kWidescreenChoices) {
if (std::strcmp(choice.value, value) == 0) return true;
}
return false;
}

bool is_widescreen_feature(const char* package_id, const char* feature_id) {
return ((std::strcmp(package_id, "mph-widescreen") == 0 ||
std::strcmp(package_id, "mph-adaptive-widescreen") == 0) &&
(std::strcmp(feature_id, "widescreen") == 0 ||
std::strcmp(feature_id, "adaptive-widescreen") == 0));
}

const WidescreenChoice& widescreen_choice(const ModState& state) {
for (const WidescreenChoice& choice : kWidescreenChoices) {
if (state.widescreen_mode == choice.value) return choice;
}
return kWidescreenChoices[0];
}

const char* widescreen_status(const ModState& state) {
return state.adaptive_widescreen ? widescreen_choice(state).label
: "Disabled";
}

int runner_widescreen_width_arg(const ModState& state) {
return state.adaptive_widescreen ? widescreen_choice(state).width : 0;
}

std::wstring widen(const char* source);

struct ScopedRendererEnv {
Expand Down Expand Up @@ -521,6 +567,9 @@ void load_mod_state(ModState& state) {
settings_version = static_cast<int>(parsed);
} else if (key == "adaptive_widescreen") {
state.adaptive_widescreen = value != "false";
} else if (key == "widescreen_mode") {
if (is_widescreen_choice(value.c_str()))
state.widescreen_mode = value;
} else if (key == "hd_rendering") {
state.hd_rendering = value == "true";
} else if (key == "internal_resolution") {
Expand Down Expand Up @@ -665,9 +714,10 @@ bool save_mod_state(ModState& state) {
state.last_error = "Could not write launcher mod settings.";
return false;
}
file << "settings_version=8\n"
file << "settings_version=9\n"
<< "adaptive_widescreen="
<< (state.adaptive_widescreen ? "true" : "false") << '\n'
<< "widescreen_mode=" << widescreen_choice(state).value << '\n'
<< "hd_rendering="
<< (state.hd_rendering ? "true" : "false") << '\n'
<< "internal_resolution=" << state.internal_resolution << '\n'
Expand Down Expand Up @@ -742,20 +792,20 @@ int mod_feature_get(void* context, int index,
const auto* state = static_cast<const ModState*>(context);
std::memset(output, 0, sizeof(*output));
if (index == 0) {
copy_text(output->id, "adaptive-widescreen");
copy_text(output->package_id, "mph-adaptive-widescreen");
copy_text(output->id, "widescreen");
copy_text(output->package_id, "mph-widescreen");
copy_text(output->package_version, "0.1.0");
copy_text(output->package_name, "MPH Adaptive Widescreen");
copy_text(output->name, "Adaptive Widescreen");
copy_text(output->package_name, "MPH Widescreen");
copy_text(output->name, "Widescreen");
copy_text(output->author, "ndsrecomp");
copy_text(
output->description,
"Expands the upper gameplay screen to 21:9 and anchors its HUD "
"while keeping the lower touchscreen native and clickable.");
"Expands the upper gameplay screen and anchors its HUD while "
"keeping the lower touchscreen native and clickable.");
copy_text(output->group, "Display enhancements");
copy_text(output->status,
state->adaptive_widescreen ? "Enabled" : "Disabled");
copy_text(output->status, widescreen_status(*state));
output->enabled = state->adaptive_widescreen ? 1 : 0;
output->option_count = 1;
} else if (index == 2) {
copy_text(output->id, "hd-rendering");
copy_text(output->package_id, "mph-hd-rendering");
Expand Down Expand Up @@ -824,9 +874,12 @@ int mod_feature_enable(void* context, const char* package_id,
const char* feature_id, int enabled) {
if (!context || !package_id || !feature_id) return 0;
auto* state = static_cast<ModState*>(context);
if (std::strcmp(package_id, "mph-adaptive-widescreen") == 0 &&
std::strcmp(feature_id, "adaptive-widescreen") == 0) {
if (is_widescreen_feature(package_id, feature_id)) {
state->adaptive_widescreen = enabled != 0;
if (state->adaptive_widescreen &&
!is_widescreen_choice(state->widescreen_mode.c_str())) {
state->widescreen_mode = "adaptive";
}
return 1;
}
if (std::strcmp(package_id, "mph-prime-controls") == 0 &&
Expand Down Expand Up @@ -861,6 +914,22 @@ int mod_feature_option_get(void* context, const char* package_id,
RecompLauncherCModOption* output) {
if (!context || !package_id || !feature_id || !output || index < 0)
return 0;
if (is_widescreen_feature(package_id, feature_id)) {
if (index != 0) return 0;
const auto* state = static_cast<const ModState*>(context);
std::memset(output, 0, sizeof(*output));
copy_text(output->id, "widescreen-mode");
copy_text(output->label, "Mode");
copy_text(output->description,
"Dynamic follows the window. Fixed ratios keep the upper "
"screen at a specific aspect ratio.");
copy_text(output->group, "Aspect ratio");
copy_text(output->value, widescreen_choice(*state).value);
copy_text(output->default_value, "adaptive");
output->type = RECOMP_MOD_OPTION_CHOICE;
output->choice_count = static_cast<int>(kWidescreenChoices.size());
return 1;
}
if (std::strcmp(package_id, "mph-hd-rendering") == 0 &&
std::strcmp(feature_id, "hd-rendering") == 0) {
if (index > 1) return 0;
Expand Down Expand Up @@ -996,6 +1065,17 @@ int mod_feature_choice_get(void*, const char* package_id,
int index, RecompLauncherCModChoice* output) {
if (!package_id || !feature_id || !option_id || !output || index < 0)
return 0;
if (is_widescreen_feature(package_id, feature_id)) {
if (std::strcmp(option_id, "widescreen-mode") != 0 ||
index >= static_cast<int>(kWidescreenChoices.size())) {
return 0;
}
std::memset(output, 0, sizeof(*output));
const WidescreenChoice& choice = kWidescreenChoices[index];
copy_text(output->value, choice.value);
copy_text(output->label, choice.label);
return 1;
}
if (std::strcmp(package_id, "mph-hd-rendering") == 0 &&
std::strcmp(feature_id, "hd-rendering") == 0) {
if (std::strcmp(option_id, "internal-resolution") == 0) {
Expand Down Expand Up @@ -1071,6 +1151,15 @@ int mod_feature_set_option(void* context, const char* package_id,
const char* value) {
if (!context || !package_id || !feature_id || !option_id || !value)
return 0;
if (is_widescreen_feature(package_id, feature_id)) {
if (std::strcmp(option_id, "widescreen-mode") != 0 ||
!is_widescreen_choice(value)) {
return 0;
}
auto* state = static_cast<ModState*>(context);
state->widescreen_mode = value;
return 1;
}
auto* hd_state = static_cast<ModState*>(context);
if (std::strcmp(package_id, "mph-hd-rendering") == 0 &&
std::strcmp(feature_id, "hd-rendering") == 0) {
Expand Down Expand Up @@ -1559,6 +1648,11 @@ bool launch_runner(const std::filesystem::path& game_dir, const char* rom,
#ifdef _WIN32
const std::wstring rom_wide = widen(rom);
if (rom_wide.empty()) return false;
std::wstring widescreen_width_arg;
if (const int width = runner_widescreen_width_arg(mods); width > 0) {
widescreen_width_arg =
L" --widescreen-width " + std::to_wstring(width);
}

std::wstring command =
quote(runner.wstring()) + L" " + quote(bios.wstring()) +
Expand All @@ -1568,6 +1662,7 @@ bool launch_runner(const std::filesystem::path& game_dir, const char* rom,
L" --fullscreen " + widen(runner_fullscreen_arg(fullscreen)) +
L" --adaptive-widescreen " +
(adaptive ? L"top" : L"none") +
widescreen_width_arg +
// Inert unless the HD mod is on, so the faithful native output stays
// the default for anyone who never opens the Mods page.
L" --internal-resolution " +
Expand Down Expand Up @@ -1650,6 +1745,8 @@ bool launch_runner(const std::filesystem::path& game_dir, const char* rom,
runner_screen_layout_arg(display_layout));
append_arg(args, "--fullscreen", runner_fullscreen_arg(fullscreen));
append_arg(args, "--adaptive-widescreen", adaptive ? "top" : "none");
if (const int width = runner_widescreen_width_arg(mods); width > 0)
append_arg(args, "--widescreen-width", std::to_string(width));
append_arg(args, "--internal-resolution",
std::to_string(mods.hd_rendering
? mods.internal_resolution : 1));
Expand Down
66 changes: 59 additions & 7 deletions launcher/recomp-ui/tests/launcher_mod_provider_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,23 +107,72 @@ int main() {

RecompLauncherCModFeature feature{};
if (!require(provider.feature_get(provider.ctx, 0, &feature),
"feature_get adaptive widescreen")) {
"feature_get widescreen")) {
return 3;
}
if (!require(std::strcmp(feature.id, "adaptive-widescreen") == 0,
"adaptive feature id")) {
if (!require(std::strcmp(feature.id, "widescreen") == 0,
"widescreen feature id")) {
return 4;
}
if (!require(feature.option_count == 0,
"adaptive has no renderer option")) {
if (!require(std::strcmp(feature.package_id, "mph-widescreen") == 0,
"widescreen package id")) {
return 4;
}
if (!require(std::strcmp(feature.name, "Widescreen") == 0,
"widescreen feature name")) {
return 4;
}
if (!require(feature.option_count == 1,
"widescreen has mode option")) {
return 4;
}
if (!require(!provider.feature_set_option(
provider.ctx, "mph-adaptive-widescreen", "adaptive-widescreen",
provider.ctx, "mph-widescreen", "widescreen",
"renderer-type", "soft"),
"adaptive rejects renderer type option")) {
"widescreen rejects renderer type option")) {
return 4;
}
RecompLauncherCModOption widescreen_option{};
if (!require(provider.feature_option_get(
provider.ctx, "mph-widescreen", "widescreen", 0,
&widescreen_option), "widescreen mode option get")) {
return 4;
}
if (!require(std::strcmp(widescreen_option.id, "widescreen-mode") == 0,
"widescreen mode option id")) return 4;
if (!require(std::strcmp(widescreen_option.value, "adaptive") == 0,
"widescreen mode default value")) return 4;
if (!require(widescreen_option.choice_count == 5,
"widescreen mode choice count")) return 4;
RecompLauncherCModChoice widescreen_choice{};
if (!require(provider.feature_choice_get(
provider.ctx, "mph-widescreen", "widescreen",
"widescreen-mode", 0, &widescreen_choice),
"widescreen dynamic choice get")) return 4;
if (!require(std::strcmp(widescreen_choice.label, "Dynamic") == 0,
"widescreen dynamic choice label")) return 4;
if (!require(provider.feature_choice_get(
provider.ctx, "mph-widescreen", "widescreen",
"widescreen-mode", 2, &widescreen_choice),
"widescreen 5:3 choice get")) return 4;
if (!require(std::strcmp(widescreen_choice.value, "fixed-5-3") == 0,
"widescreen 5:3 choice value")) return 4;
if (!require(provider.feature_set_option(
provider.ctx, "mph-widescreen", "widescreen",
"widescreen-mode", "fixed-5-3"),
"widescreen mode set")) return 4;
if (!require(state.widescreen_mode == "fixed-5-3",
"widescreen mode state set")) return 4;
if (!require(runner_widescreen_width_arg(state) == 320,
"widescreen fixed 5:3 launch width")) return 4;
if (!require(provider.feature_enable(
provider.ctx, "mph-widescreen", "widescreen", 0),
"disable widescreen")) return 4;
if (!require(runner_widescreen_width_arg(state) == 0,
"disabled widescreen has no width")) return 4;
if (!require(provider.feature_enable(
provider.ctx, "mph-adaptive-widescreen", "adaptive-widescreen", 1),
"legacy widescreen package enables")) return 4;

feature = {};
if (!require(provider.feature_get(provider.ctx, 1, &feature),
Expand Down Expand Up @@ -444,6 +493,7 @@ int main() {
std::filesystem::temp_directory_path() /
"mph_mod_provider_all_mod_settings.ini";
saved.adaptive_widescreen = false;
saved.widescreen_mode = "fixed-5-3";
saved.hd_rendering = true;
saved.internal_resolution = 4;
saved.texture_upscale = 4;
Expand Down Expand Up @@ -474,6 +524,8 @@ int main() {
load_mod_state(loaded);
if (!require(!loaded.adaptive_widescreen,
"adaptive widescreen round trip")) return 129;
if (!require(loaded.widescreen_mode == "fixed-5-3",
"widescreen mode round trip")) return 129;
if (!require(loaded.hd_rendering,
"hd rendering feature round trip")) return 130;
if (!require(loaded.internal_resolution == 4,
Expand Down