mirror of
https://github.com/JesseTG/melonds-ds.git
synced 2024-11-24 06:59:40 +00:00
Simplify dynamic options handling
This commit is contained in:
parent
cd12b5d4cc
commit
f5d24fc5bd
@ -19,11 +19,13 @@
|
||||
// This must come before <GPU.h>!
|
||||
#include "PlatformOGLPrivate.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <charconv>
|
||||
#include <cstring>
|
||||
#include <initializer_list>
|
||||
#include <memory>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include <file/file_path.h>
|
||||
#include <libretro.h>
|
||||
@ -45,11 +47,12 @@
|
||||
#include "microphone.hpp"
|
||||
#include "opengl.hpp"
|
||||
#include "render.hpp"
|
||||
#include "retro/dirent.hpp"
|
||||
#include "screenlayout.hpp"
|
||||
#include "tracy.hpp"
|
||||
#include "dynamic.hpp"
|
||||
|
||||
using std::array;
|
||||
using std::find_if;
|
||||
using std::from_chars;
|
||||
using std::from_chars_result;
|
||||
using std::initializer_list;
|
||||
@ -59,6 +62,7 @@ using std::optional;
|
||||
using std::string;
|
||||
using std::string_view;
|
||||
using std::unique_ptr;
|
||||
using std::vector;
|
||||
|
||||
constexpr unsigned AUTO_SDCARD_SIZE = 0;
|
||||
constexpr unsigned DEFAULT_SDCARD_SIZE = 4096;
|
||||
@ -81,8 +85,7 @@ namespace melonds::config {
|
||||
static void set_core_options(
|
||||
const optional<retro_game_info> &nds_info,
|
||||
const optional<NDSHeader> &nds_header
|
||||
) noexcept;
|
||||
static bool _config_categories_supported;
|
||||
);
|
||||
namespace visibility {
|
||||
static bool ShowDsiOptions = true;
|
||||
static bool ShowDsiSdCardOptions = true;
|
||||
@ -93,6 +96,7 @@ namespace melonds::config {
|
||||
static bool ShowHybridOptions = true;
|
||||
static bool ShowVerticalLayoutOptions = true;
|
||||
static bool ShowCursorTimeout = true;
|
||||
static bool ShowAlarm = true;
|
||||
static unsigned NumberOfShownScreenLayouts = screen::MAX_SCREEN_LAYOUTS;
|
||||
|
||||
#ifdef JIT_ENABLED
|
||||
@ -489,6 +493,9 @@ bool melonds::update_option_visibility() {
|
||||
updated = true;
|
||||
}
|
||||
|
||||
bool oldShowAlarm = ShowAlarm;
|
||||
|
||||
|
||||
#ifdef JIT_ENABLED
|
||||
// Show/hide JIT core options
|
||||
bool oldShowJitOptions = ShowJitOptions;
|
||||
@ -1421,23 +1428,101 @@ static void melonds::config::apply_screen_options(ScreenLayoutData& screenLayout
|
||||
static void melonds::config::set_core_options(
|
||||
const optional<retro_game_info> &nds_info,
|
||||
const optional<NDSHeader> &nds_header
|
||||
) noexcept {
|
||||
) {
|
||||
ZoneScopedN("retro::set_core_options");
|
||||
|
||||
_config_categories_supported = false;
|
||||
|
||||
array categories = definitions::OptionCategories<RETRO_LANGUAGE_ENGLISH>;
|
||||
array definitions = definitions::CoreOptionDefinitions<RETRO_LANGUAGE_ENGLISH>;
|
||||
DynamicCoreOptions options(
|
||||
definitions.data(),
|
||||
definitions.size(),
|
||||
categories.data(),
|
||||
categories.size()
|
||||
);
|
||||
|
||||
retro_core_options_v2* optionsUs = options.GetOptions();
|
||||
vector<string> bases;
|
||||
|
||||
retro::set_core_options(*optionsUs);
|
||||
if (optional<string> subdir = retro::get_system_subdirectory(); subdir && path_is_directory(subdir->c_str())) {
|
||||
bases.push_back(*subdir);
|
||||
}
|
||||
|
||||
if (optional<string> fallback = retro::get_system_fallback_subdirectory(); fallback && path_is_directory(fallback->c_str())) {
|
||||
bases.push_back(*fallback);
|
||||
}
|
||||
vector<string> dsiNandPaths;
|
||||
vector<string> firmwarePaths;
|
||||
optional<string> sysdir = retro::get_system_directory();
|
||||
|
||||
if (sysdir) {
|
||||
for (const string& base : bases) {
|
||||
for (const retro::dirent& d : retro::readdir(base, true)) {
|
||||
if (IsDsiNandImage(d)) {
|
||||
dsiNandPaths.emplace_back(d.path);
|
||||
}
|
||||
if (IsFirmwareImage(d)) {
|
||||
firmwarePaths.emplace_back(d.path);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
retro::set_error_message("Failed to get system directory, anything that needs it won't work.");
|
||||
}
|
||||
|
||||
if (!dsiNandPaths.empty()) {
|
||||
// If we found at least one DSi NAND image...
|
||||
retro_core_option_v2_definition* dsiNandPathOption = find_if(definitions.begin(), definitions.end(), [](const auto& def) {
|
||||
return string_is_equal(def.key, melonds::config::storage::DSI_NAND_PATH);
|
||||
});
|
||||
|
||||
retro_assert(dsiNandPathOption != definitions.end());
|
||||
|
||||
memset(dsiNandPathOption->values, 0, sizeof(dsiNandPathOption->values));
|
||||
int length = std::min((int)dsiNandPaths.size(), (int)RETRO_NUM_CORE_OPTION_VALUES_MAX - 1);
|
||||
for (int i = 0; i < length; ++i) {
|
||||
retro::debug("Found a DSi NAND image at \"%s\"", dsiNandPaths[i].c_str());
|
||||
string_view path = dsiNandPaths[i];
|
||||
path.remove_prefix(sysdir->size() + 1);
|
||||
dsiNandPathOption->values[i].value = path.data();
|
||||
dsiNandPathOption->values[i].label = nullptr;
|
||||
}
|
||||
|
||||
dsiNandPathOption->default_value = dsiNandPaths[0].c_str();
|
||||
}
|
||||
|
||||
if (!firmwarePaths.empty()) {
|
||||
// If we found at least one firmware image...
|
||||
retro_core_option_v2_definition* firmwarePathOption = find_if(definitions.begin(), definitions.end(), [](const auto& def) {
|
||||
return string_is_equal(def.key, melonds::config::firmware::FIRMWARE_PATH);
|
||||
});
|
||||
retro_core_option_v2_definition* firmwarePathDsiOption = find_if(definitions.begin(), definitions.end(), [](const auto& def) {
|
||||
return string_is_equal(def.key, melonds::config::firmware::FIRMWARE_DSI_PATH);
|
||||
});
|
||||
|
||||
retro_assert(firmwarePathOption != definitions.end());
|
||||
retro_assert(firmwarePathDsiOption != definitions.end());
|
||||
|
||||
// Keep the first element, it's for built-in firmware
|
||||
// We subtract 2 because we need room for the terminating element and the built-in firmware
|
||||
int length = std::min((int)firmwarePaths.size(), (int)RETRO_NUM_CORE_OPTION_VALUES_MAX - 2);
|
||||
for (int i = 0; i < length; ++i) {
|
||||
retro::debug("Found a firmware image at \"%s\"", firmwarePaths[i].c_str());
|
||||
string_view path = firmwarePaths[i];
|
||||
path.remove_prefix(sysdir->size() + 1);
|
||||
firmwarePathOption->values[i + 1] = { path.data(), nullptr };
|
||||
firmwarePathDsiOption->values[i + 1] = { path.data(), nullptr };
|
||||
}
|
||||
firmwarePathOption->values[length + 2] = { nullptr, nullptr };
|
||||
firmwarePathDsiOption->values[length + 2] = { nullptr, nullptr };
|
||||
|
||||
firmwarePathOption->default_value = firmwarePathOption->values[0].value;
|
||||
firmwarePathDsiOption->default_value = firmwarePathDsiOption->values[0].value;
|
||||
|
||||
retro_assert(firmwarePathOption->values[0].value != nullptr);
|
||||
retro_assert(firmwarePathDsiOption->values[0].value != nullptr);
|
||||
}
|
||||
|
||||
retro_core_options_v2 optionsUs = {
|
||||
.categories = categories.data(),
|
||||
.definitions = definitions.data(),
|
||||
};
|
||||
|
||||
if (!retro::set_core_options(optionsUs)) {
|
||||
throw emulator_exception("Failed to set core options");
|
||||
}
|
||||
}
|
||||
|
||||
using namespace melonds::config;
|
||||
|
@ -16,12 +16,15 @@
|
||||
|
||||
#include "constants.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <net/net_compat.h>
|
||||
#include <string/stdstring.h>
|
||||
|
||||
#include "environment.hpp"
|
||||
#include "retro/dirent.hpp"
|
||||
|
||||
using std::find;
|
||||
using std::optional;
|
||||
using std::nullopt;
|
||||
using std::string;
|
||||
@ -148,4 +151,16 @@ optional<SPI_Firmware::IpAddress> melonds::config::ParseIpAddress(const char* va
|
||||
}
|
||||
|
||||
return nullopt;
|
||||
}
|
||||
|
||||
bool melonds::config::IsDsiNandImage(const retro::dirent &file) noexcept {
|
||||
// TODO: Validate the NoCash footer
|
||||
return file.is_regular_file() && file.size == DSI_NAND_SIZE;
|
||||
}
|
||||
|
||||
bool melonds::config::IsFirmwareImage(const retro::dirent& file) noexcept {
|
||||
return
|
||||
file.is_regular_file() &&
|
||||
find(FIRMWARE_SIZES.begin(), FIRMWARE_SIZES.end(), file.size) != FIRMWARE_SIZES.end() &&
|
||||
!string_ends_with(file.path, ".bak");
|
||||
}
|
@ -29,6 +29,10 @@
|
||||
|
||||
#include "../config.hpp"
|
||||
|
||||
namespace retro {
|
||||
struct dirent;
|
||||
}
|
||||
|
||||
namespace melonds::config {
|
||||
constexpr unsigned DS_NAME_LIMIT = 10;
|
||||
|
||||
@ -57,6 +61,8 @@ namespace melonds::config {
|
||||
static constexpr const char *const BIRTH_DAY = "melonds_firmware_birth_day";
|
||||
static constexpr const char *const ENABLE_ALARM = "melonds_firmware_enable_alarm";
|
||||
static constexpr const char *const FAVORITE_COLOR = "melonds_firmware_favorite_color";
|
||||
static constexpr const char *const FIRMWARE_PATH = "melonds_firmware_nds_path";
|
||||
static constexpr const char *const FIRMWARE_DSI_PATH = "melonds_firmware_dsi_path";
|
||||
static constexpr const char *const LANGUAGE = "melonds_firmware_language";
|
||||
static constexpr const char *const USERNAME = "melonds_firmware_username";
|
||||
static constexpr const char *const WFC_DNS = "melonds_firmware_wfc_dns";
|
||||
@ -160,6 +166,7 @@ namespace melonds::config {
|
||||
static constexpr const char *const BOTTOM_TOP = "bottom-top";
|
||||
static constexpr const char *const BOTH = "both";
|
||||
static constexpr const char *const BOTTOM = "bottom";
|
||||
static constexpr const char *const BUILT_IN = "builtin";
|
||||
static constexpr const char *const COSINE = "cosine";
|
||||
static constexpr const char *const CUBIC = "cubic";
|
||||
static constexpr const char *const DEDICATED = "dedicated";
|
||||
@ -255,7 +262,11 @@ namespace melonds::config {
|
||||
|
||||
std::optional<SPI_Firmware::IpAddress> ParseIpAddress(const char* value) noexcept;
|
||||
|
||||
constexpr size_t DSI_NAND_SIZE = 251658304;
|
||||
constexpr std::array<size_t, 3> FIRMWARE_SIZES = { 131072, 262144, 524288 };
|
||||
|
||||
bool IsDsiNandImage(const retro::dirent &file) noexcept;
|
||||
bool IsFirmwareImage(const retro::dirent &file) noexcept;
|
||||
}
|
||||
|
||||
#endif //MELONDS_DS_CONSTANTS_HPP
|
||||
|
@ -1,127 +0,0 @@
|
||||
/*
|
||||
Copyright 2023 Jesse Talavera-Greenberg
|
||||
|
||||
melonDS DS is free software: you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation, either version 3 of the License, or (at your option)
|
||||
any later version.
|
||||
|
||||
melonDS DS is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with melonDS DS. If not, see http://www.gnu.org/licenses/.
|
||||
*/
|
||||
|
||||
#include "dynamic.hpp"
|
||||
#include "constants.hpp"
|
||||
#include "environment.hpp"
|
||||
#include "retro/dirent.hpp"
|
||||
#include "tracy.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
|
||||
#include <retro_dirent.h>
|
||||
#include <retro_assert.h>
|
||||
#include <string/stdstring.h>
|
||||
#include <vfs/vfs_implementation.h>
|
||||
|
||||
using std::find_if;
|
||||
using std::min;
|
||||
using std::optional;
|
||||
using std::pair;
|
||||
using std::string;
|
||||
using std::string_view;
|
||||
using std::vector;
|
||||
|
||||
constexpr size_t DSI_NAND_SIZE = 251658304;
|
||||
|
||||
static vector<string> GetNandPaths() noexcept;
|
||||
|
||||
melonds::config::DynamicCoreOptions::DynamicCoreOptions(
|
||||
const retro_core_option_v2_definition *definitions,
|
||||
size_t definitions_length,
|
||||
const retro_core_option_v2_category *categories,
|
||||
size_t categories_length
|
||||
) {
|
||||
ZoneScopedN("melonds::config::DynamicCoreOptions::DynamicCoreOptions");
|
||||
// TODO: Generate an option for selecting the wi-fi interface
|
||||
// TODO: Generate an option for selecting the DS BIOS7 file
|
||||
// TODO: Generate an option for selecting the DS BIOS9 file
|
||||
// TODO: Generate an option for selecting the DS firmware file
|
||||
// TODO: Generate an option for selecting the DSi BIOS7 file
|
||||
// TODO: Generate an option for selecting the DSi BIOS9 file
|
||||
// TODO: Generate an option for selecting the DSi firmware file
|
||||
// TODO: Generate an option for selecting the homebrew SD card
|
||||
// TODO: Generate an option for selecting the DSi SD card
|
||||
_optionDefs = new retro_core_option_v2_definition[definitions_length];
|
||||
_optionDefsEnd = &_optionDefs[definitions_length - 1];
|
||||
_optionDefsLength = definitions_length;
|
||||
memcpy(_optionDefs, definitions, definitions_length * sizeof(retro_core_option_v2_definition));
|
||||
|
||||
_optionCategories = new retro_core_option_v2_category[categories_length];
|
||||
memcpy(_optionCategories, categories, categories_length * sizeof(retro_core_option_v2_category));
|
||||
|
||||
|
||||
|
||||
{
|
||||
retro_core_option_v2_definition* dsiNandPathOption = find_if(_optionDefs, _optionDefsEnd, [](const auto& def) {
|
||||
return string_is_equal(def.key, melonds::config::storage::DSI_NAND_PATH);
|
||||
});
|
||||
|
||||
retro_assert(dsiNandPathOption != _optionDefsEnd);
|
||||
|
||||
for (const string& path : GetNandPaths()) {
|
||||
_dsiNandPaths.push_back(std::move(path));
|
||||
}
|
||||
|
||||
if (!_dsiNandPaths.empty()) {
|
||||
memset(dsiNandPathOption->values, 0, sizeof(dsiNandPathOption->values));
|
||||
int length = min((int)_dsiNandPaths.size(), (int)RETRO_NUM_CORE_OPTION_VALUES_MAX - 1);
|
||||
for (int i = 0; i < length; ++i) {
|
||||
dsiNandPathOption->values[i].value = _dsiNandPaths[i].c_str();
|
||||
dsiNandPathOption->values[i].label = nullptr;
|
||||
}
|
||||
|
||||
dsiNandPathOption->default_value = _dsiNandPaths[0].c_str();
|
||||
}
|
||||
}
|
||||
|
||||
_options.categories = _optionCategories;
|
||||
_options.definitions = _optionDefs;
|
||||
}
|
||||
|
||||
melonds::config::DynamicCoreOptions::~DynamicCoreOptions() noexcept {
|
||||
delete[] _optionCategories;
|
||||
delete[] _optionDefs;
|
||||
}
|
||||
|
||||
static vector<string> GetNandPaths() noexcept {
|
||||
vector<string> paths;
|
||||
optional<string> sysdir = retro::get_system_directory();
|
||||
|
||||
auto appendPaths = [&paths, &sysdir](optional<string> base) {
|
||||
if (base) {
|
||||
for (const retro::dirent& d : retro::readdir(*base, true)) {
|
||||
retro_assert(retro::is_regular_file(d.flags));
|
||||
if (d.size == DSI_NAND_SIZE) {
|
||||
// If this is a regular file...
|
||||
string_view path = d.path;
|
||||
retro_assert(path.size() > sysdir->size());
|
||||
|
||||
path.remove_prefix(sysdir->size() + 1);
|
||||
paths.emplace_back(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
appendPaths(sysdir);
|
||||
appendPaths(retro::get_system_subdirectory());
|
||||
appendPaths(retro::get_system_fallback_subdirectory());
|
||||
|
||||
return paths;
|
||||
}
|
@ -1,67 +0,0 @@
|
||||
/*
|
||||
Copyright 2023 Jesse Talavera-Greenberg
|
||||
|
||||
melonDS DS is free software: you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation, either version 3 of the License, or (at your option)
|
||||
any later version.
|
||||
|
||||
melonDS DS is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with melonDS DS. If not, see http://www.gnu.org/licenses/.
|
||||
*/
|
||||
|
||||
#ifndef MELONDS_DS_DYNAMIC_HPP
|
||||
#define MELONDS_DS_DYNAMIC_HPP
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <libretro.h>
|
||||
|
||||
struct NDSHeader;
|
||||
|
||||
namespace melonds::config {
|
||||
class DynamicCoreOptions {
|
||||
public:
|
||||
DynamicCoreOptions(
|
||||
const retro_core_option_v2_definition *definitions,
|
||||
size_t definitions_length,
|
||||
const retro_core_option_v2_category *categories,
|
||||
size_t categories_length
|
||||
);
|
||||
|
||||
~DynamicCoreOptions() noexcept;
|
||||
|
||||
const retro_core_option_v2_definition* GetDefinitions() const noexcept {
|
||||
return _optionDefs;
|
||||
}
|
||||
|
||||
retro_core_option_v2_definition* GetDefinitions() noexcept {
|
||||
return _optionDefs;
|
||||
}
|
||||
|
||||
const retro_core_options_v2* GetOptions() const noexcept {
|
||||
return &_options;
|
||||
}
|
||||
|
||||
retro_core_options_v2* GetOptions() noexcept {
|
||||
return &_options;
|
||||
}
|
||||
|
||||
private:
|
||||
retro_core_option_v2_definition *_optionDefs;
|
||||
retro_core_option_v2_definition *_optionDefsEnd;
|
||||
retro_core_option_v2_category *_optionCategories;
|
||||
size_t _optionDefsLength; // Excluding the null option at the end
|
||||
|
||||
retro_core_options_v2 _options;
|
||||
std::vector<std::string> _dsiNandPaths;
|
||||
};
|
||||
|
||||
}
|
||||
#endif //MELONDS_DS_DYNAMIC_HPP
|
Loading…
Reference in New Issue
Block a user