diff --git a/src/libretro/CMakeLists.txt b/src/libretro/CMakeLists.txt index 7dff6ac8..424c3504 100644 --- a/src/libretro/CMakeLists.txt +++ b/src/libretro/CMakeLists.txt @@ -81,6 +81,10 @@ add_library(melondsds_libretro MODULE std/chrono.hpp std/semaphore.hpp std/span.hpp + strings/en_us.hpp + strings/en_us/error.hpp + strings/en_us/notice.hpp + strings/en_us/osd.hpp sram.cpp sram.hpp tracy.hpp diff --git a/src/libretro/config/config.cpp b/src/libretro/config/config.cpp index ec4adf97..f3be86f0 100644 --- a/src/libretro/config/config.cpp +++ b/src/libretro/config/config.cpp @@ -67,12 +67,14 @@ #include "std/span.hpp" #include "tracy.hpp" #include "pcap.hpp" +#include "strings/en_us.hpp" #ifdef interface #undef interface #endif using namespace melonDS; +using namespace MelonDsDs::strings::en_us; using std::array; using std::find_if; using std::from_chars; @@ -874,7 +876,7 @@ bool MelonDsDs::RegisterCoreOptions() noexcept { } } else { - retro::set_error_message("Failed to get system directory, anything that needs it won't work."); + retro::set_error_message(SysDirFailed); } if (!dsiNandPaths.empty()) { @@ -1003,7 +1005,7 @@ bool MelonDsDs::RegisterCoreOptions() noexcept { #endif if (!retro::set_core_options(optionsUs)) { - retro::set_error_message("Failed to set core option definitions, functionality will be limited."); + retro::set_error_message(OptionInitFailed); return false; } diff --git a/src/libretro/config/console.cpp b/src/libretro/config/console.cpp index 8b1e7d3e..cb9a8bd5 100644 --- a/src/libretro/config/console.cpp +++ b/src/libretro/config/console.cpp @@ -40,6 +40,7 @@ #include "format.hpp" #include "retro/http.hpp" #include "retro/info.hpp" +#include "strings/strings.hpp" #include "types.hpp" using std::make_optional; @@ -59,6 +60,7 @@ namespace MelonDsDs { const char *TMD_DIR_NAME = "tmd"; const char* SENTINEL_NAME = "melon.dat"; constexpr uint32_t RSA256_SIGNATURE_TYPE = 16777472; + using namespace strings::en_us; static melonDS::NDSArgs GetNdsArgs( const CoreConfig& config, @@ -134,9 +136,7 @@ std::unique_ptr MelonDsDs::CreateConsole( if (type == ConsoleType::DSi) { // If we're in DSi mode... if (gbaInfo || gbaSaveInfo) { - retro::set_warn_message( - "The DSi does not support GBA connectivity. Not loading the requested GBA ROM or SRAM." - ); + retro::set_warn_message(DsiDoesntHaveGbaSlot); } return std::make_unique(GetDSiArgs(config, ndsInfo)); } @@ -471,11 +471,7 @@ static std::pair, size_t> MelonDsDs::LoadGbaSram(const ret // We don't support GBA SRAM files in archives right now; // libretro-common has APIs for extracting and re-inserting them, // but I just can't be bothered. - retro::set_error_message( - "melonDS DS does not support archived GBA save data right now. " - "Please extract it and try again. " - "Continuing without using the save data." - ); + retro::set_error_message(ArchivedGbaSaveNotSupported); return { nullptr, 0 }; } @@ -492,11 +488,7 @@ static std::pair, size_t> MelonDsDs::LoadGbaSram(const ret // We don't support rzip-compressed GBA save files right now; // I can't be bothered. - retro::set_error_message( - "melonDS DS does not support compressed GBA save data right now. " - "Please disable save data compression in the frontend and try again. " - "Continuing without using the save data." - ); + retro::set_error_message(CompressedGbaSaveNotSupported); rzipstream_close(gba_save_file); return { nullptr, 0 }; @@ -955,15 +947,13 @@ static void MelonDsDs::CustomizeFirmware(const CoreConfig& config, Firmware& fir memset(&chk2[0x0C], 0, 8); if (!memcmp(chk1, chk2, sizeof(chk1))) { - constexpr const char* const WARNING_MESSAGE = - "Corrupted firmware detected!\n" - "Any game that alters Wi-fi settings will break this firmware, even on real hardware.\n"; + if (config.ShowBiosWarnings()) { - retro::set_warn_message(WARNING_MESSAGE); + retro::set_warn_message(HackedFirmwareWarning); } else { - retro::warn(WARNING_MESSAGE); + retro::warn(HackedFirmwareWarning); } } } @@ -981,7 +971,7 @@ static void MelonDsDs::CustomizeFirmware(const CoreConfig& config, Firmware& fir optional convertedUsername = ConvertUsername(*username); if (!convertedUsername) { - retro::set_warn_message("Can't use the name \"{}\" on the DS, using default name instead.", *username); + retro::set_warn_message(UsernameFailed, fmt::arg("name", *username)); convertedUsername = ConvertUsername(config::values::firmware::DEFAULT_USERNAME); } diff --git a/src/libretro/config/definitions/network.hpp b/src/libretro/config/definitions/network.hpp index 1e31e883..705c33ea 100644 --- a/src/libretro/config/definitions/network.hpp +++ b/src/libretro/config/definitions/network.hpp @@ -31,12 +31,13 @@ namespace MelonDsDs::config::definitions { "Indirect: Use libslirp to emulate the DS's network stack. Simple and needs no setup.\n" #ifdef HAVE_NETWORKING_DIRECT_MODE "Direct: Routes emulated Wi-Fi packets to the host's network interface. " - "Faster and more reliable, but requires an ethernet connection and " -#ifdef _WIN32 - "that WinPcap or Npcap is installed. " -#else - "that libpcap is installed. " -#endif +# ifdef _WIN32 + "Faster and more reliable, but requires an ethernet connection " + "and that WinPcap or Npcap is installed. " +# else + "Faster and more reliable, but requires an ethernet connection " + "and that libpcap is installed. " +# endif "If unavailable, falls back to Indirect mode.\n" #endif "\n" diff --git a/src/libretro/core/core.cpp b/src/libretro/core/core.cpp index 1e703b4a..61cf6ccf 100644 --- a/src/libretro/core/core.cpp +++ b/src/libretro/core/core.cpp @@ -28,6 +28,7 @@ #include #include +#include "../strings/strings.hpp" #include "../config/console.hpp" #include "../exceptions.hpp" #include "../format.hpp" @@ -44,16 +45,10 @@ using std::span; using namespace melonDS::DSi_NAND; +using namespace MelonDsDs::strings::en_us; constexpr size_t DS_MEMORY_SIZE = 0x400000; constexpr size_t DSI_MEMORY_SIZE = 0x1000000; -static const char* const INTERNAL_ERROR_MESSAGE = - "An internal error occurred with melonDS DS. " - "Please contact the developer with the log file."; - -static const char* const UNKNOWN_ERROR_MESSAGE = - "An unknown error has occurred with melonDS DS. " - "Please contact the developer with the log file."; date::local_seconds LocalTime() noexcept { using namespace std::chrono; @@ -189,7 +184,7 @@ void MelonDsDs::CoreState::Reset() { ZoneScopedN(TracyFunction); if (_messageScreen) { - retro::set_error_message("Please follow the advice on this screen, then unload/reload the core."); + retro::set_error_message(PleaseResetCore); return; // TODO: Allow the game to be reset from the error screen // (gotta reinitialize the DS here) @@ -290,7 +285,7 @@ bool MelonDsDs::CoreState::RunDeferredInitialization() noexcept { } catch (...) { retro::error("Deferred initialization failed; exiting core"); - retro::set_error_message(UNKNOWN_ERROR_MESSAGE); + retro::set_error_message(UnknownError); return false; } @@ -430,9 +425,6 @@ void MelonDsDs::CoreState::InitFlushFirmwareTask() noexcept _flushTaskId = flushTask.Identifier(); retro::task::push(std::move(flushTask)); } - else { - retro::set_error_message("System path not found, changes to firmware settings won't be saved."); - } } void MelonDsDs::CoreState::ResetRenderState() { @@ -450,8 +442,7 @@ bool MelonDsDs::CoreState::LoadGame(unsigned type, std::span data) noexcept if (major < SAVESTATE_MAJOR) { // If this savestate is too old... - retro::set_error_message( - "This savestate is too old, can't load it.\n" - "Save your game normally in the older version and import the save data."); + retro::set_error_message(StateTooOld); } else if (major > SAVESTATE_MAJOR) { // If this savestate is too new... - retro::set_error_message( - "This savestate is too new, can't load it.\n" - "Save your game normally in the newer version, " - "then update this core or import the save data."); + retro::set_error_message(StateTooNew); } return false; @@ -816,7 +801,7 @@ bool MelonDsDs::CoreState::Unserialize(std::span data) noexcept if (data.size() != *_savestateSize) { retro::error("Expected a {}-byte savestate, got one of {} bytes", *_savestateSize, data.size()); - retro::set_error_message("Can't load this savestate, most likely the ROM or the core is wrong."); + retro::set_error_message(StateLoadFailed); return false; } @@ -875,13 +860,13 @@ void MelonDsDs::CoreState::CheatSet(unsigned index, bool enabled, std::string_vi return; if (!enabled) { - retro::set_warn_message("Action Replay codes can't be undone, restart the game to remove their effects."); + retro::set_warn_message(CantDisableCheat); return; } if (!regex_match(code.data(), _cheatSyntax)) { // If we're trying to activate this cheat code, but it's not valid... - retro::set_warn_message("Cheat #{} ({:.8}...) isn't valid, ignoring it.", index, code); + retro::set_warn_message(InvalidCheat, fmt::arg("index", index), fmt::arg("code", code)); return; } diff --git a/src/libretro/core/tasks.cpp b/src/libretro/core/tasks.cpp index ede7c85e..347a80c7 100644 --- a/src/libretro/core/tasks.cpp +++ b/src/libretro/core/tasks.cpp @@ -34,8 +34,10 @@ #include "microphone.hpp" #include "retro/task_queue.hpp" #include "tracy.hpp" +#include "strings/en_us.hpp" using namespace melonDS; +using namespace MelonDsDs::strings::en_us; using std::nullopt; using std::optional; @@ -338,18 +340,18 @@ retro::task::TaskSpec MelonDsDs::CoreState::OnScreenDisplayTask() noexcept { if (Config.ShowCurrentLayout()) { fmt::format_to( inserter, - "{}Layout {}/{}", - buf.size() == 0 ? "" : OSD_DELIMITER, - _screenLayout.LayoutIndex() + 1, - _screenLayout.NumberOfLayouts() + CurrentLayout, + fmt::arg("sep", buf.size() == 0 ? "" : OSD_DELIMITER), + fmt::arg("layout_num", _screenLayout.LayoutIndex() + 1), + fmt::arg("num_layouts", _screenLayout.NumberOfLayouts()) ); } if (Config.ShowLidState() && nds.IsLidClosed()) { fmt::format_to( inserter, - "{}Closed", - buf.size() == 0 ? "" : OSD_DELIMITER + ScreenState, + fmt::arg("sep", buf.size() == 0 ? "" : OSD_DELIMITER) ); } diff --git a/src/libretro/exceptions.cpp b/src/libretro/exceptions.cpp index 7d3a408c..86fd23a2 100644 --- a/src/libretro/exceptions.cpp +++ b/src/libretro/exceptions.cpp @@ -22,157 +22,113 @@ #include #include +#include "strings/en_us.hpp" + using std::optional; using std::string; using std::string_view; +using namespace MelonDsDs::strings::en_us; +using fmt::arg; -MelonDsDs::nds_firmware_not_bootable_exception::nds_firmware_not_bootable_exception(string_view firmwareName) noexcept +MelonDsDs::nds_firmware_not_bootable_exception::nds_firmware_not_bootable_exception(string_view path) noexcept : bios_exception( - fmt::format( - FMT_STRING( - "The firmware file at \"{}\" can't be used to boot to the DS menu." - ), - firmwareName - ), - "Ensure you have native DS (not DSi) firmware in your frontend's system folder. " - "Pick it in the core options, then restart the core. " - "If you just want to play a DS game, try setting Boot Mode to \"Direct\" " - "or BIOS/Firmware Mode to \"Built-In\" in the core options." + fmt::format(NativeFirmwareNotBootableProblem, arg("path", path)), + FirmwareNotBootableSolution ) { } MelonDsDs::nds_firmware_not_bootable_exception::nds_firmware_not_bootable_exception() noexcept : bios_exception( - "The built-in firmware can't be used to boot to the DS menu.", - "Ensure you have native DS (not DSi) firmware in your frontend's system folder. " - "Pick it in the core options, then restart the core. " - "If you just want to play a DS game, try setting Boot Mode to \"Direct\" " - "or BIOS/Firmware Mode to \"Built-In\" in the core options." + BuiltInFirmwareNotBootableProblem, + FirmwareNotBootableSolution ) { } MelonDsDs::wrong_firmware_type_exception::wrong_firmware_type_exception( - std::string_view firmwareName, - MelonDsDs::ConsoleType consoleType, - melonDS::Firmware::FirmwareConsoleType firmwareConsoleType + std::string_view path, + MelonDsDs::ConsoleType console, + melonDS::Firmware::FirmwareConsoleType firmwareConsole ) noexcept : bios_exception( fmt::format( - FMT_STRING("The firmware file at \"{}\" is for the {}, but it can't be used in {} mode."), - firmwareName, - firmwareConsoleType, - consoleType + WrongFirmwareProblem, + arg("path", path), + arg("firmwareConsole", firmwareConsole), + arg("console", console) ), fmt::format( - FMT_STRING( - "Ensure you have a {}-compatible firmware file in your frontend's system folder (any name works). " - "Pick it in the core options, then restart the core. " - "If you just want to play a DS game, try disabling DSi mode in the core options." - ), - consoleType + WrongFirmwareSolution, + arg("console", console) ) ) { } MelonDsDs::dsi_region_mismatch_exception::dsi_region_mismatch_exception( - string_view nandName, - melonDS::DSi_NAND::ConsoleRegion nandRegion, - melonDS::RegionMask gameRegionMask + string_view path, + melonDS::DSi_NAND::ConsoleRegion region, + melonDS::RegionMask regions ) noexcept : config_exception( fmt::format( - "The NAND file at \"{}\" has the region \"{}\", " - "but the loaded DSiWare game will only run in the following regions: {}", - nandName, - nandRegion, - gameRegionMask + WrongNandRegionProblem, + arg("path", path), + arg("region", region), + arg("regions", regions) ), - "Double-check that you're using the right NAND file " - "and the right copy of your game." + WrongNandRegionSolution ) { } MelonDsDs::dsi_no_firmware_found_exception::dsi_no_firmware_found_exception() noexcept : bios_exception( - "DSi mode requires a firmware file from a DSi, but none was found.", - "Place your DSi firmware file in your frontend's system folder, " - "then restart the core. " - "If you just want to play a DS game, " - "try disabling DSi mode in the core options." + NoDsiFirmwareProblem, + NoDsiFirmwareSolution ) { } -MelonDsDs::firmware_missing_exception::firmware_missing_exception(std::string_view firmwareName) noexcept +MelonDsDs::firmware_missing_exception::firmware_missing_exception(std::string_view path) noexcept : bios_exception( - fmt::format( - "The core is set to use the firmware file at \"{}\", but it wasn't there or it couldn't be loaded.", - firmwareName - ), - fmt::format( - "Place your DSi firmware file in your frontend's system folder, name it \"{}\", then restart the core.", - firmwareName - ) + fmt::format(NoFirmwareProblem, arg("path", path)), + fmt::format(NoFirmwareSolution, arg("path", path)) ) { } MelonDsDs::nds_sysfiles_incomplete_exception::nds_sysfiles_incomplete_exception() noexcept : bios_exception( - "Booting to the native DS menu requires native DS firmware and BIOS files, " - "but some of them were missing or couldn't be loaded.", - "Place your DS system files in your frontend's system folder, then restart the core. " - "If you want to play a regular DS game, try setting Boot Mode to \"Direct\" " - "and BIOS/Firmware Mode to \"Built-In\" in the core options." + IncompleteNdsSysfilesProblem, + IncompleteNdsSysfilesSolution ) { } -MelonDsDs::dsi_missing_bios_exception::dsi_missing_bios_exception(MelonDsDs::BiosType bios, string_view biosName) noexcept +MelonDsDs::dsi_missing_bios_exception::dsi_missing_bios_exception(MelonDsDs::BiosType bios, string_view path) noexcept : bios_exception( - fmt::format(FMT_STRING("DSi mode requires the {} BIOS file, but none was found."), bios), + fmt::format(MissingDsiBiosProblem, arg("bios", bios)), fmt::format( - FMT_STRING( - "Place your {} BIOS file in your frontend's system folder, name it \"{}\", then restart the core. " - "If you want to play a regular DS game, try disabling DSi mode in the core options." - ), - bios, - biosName + MissingDsiBiosSolution, + arg("bios", bios), + arg("path", path) ) ) { } MelonDsDs::dsi_no_nand_found_exception::dsi_no_nand_found_exception() noexcept : bios_exception( - "DSi mode requires a NAND image, but none was found.", - "Place your NAND file in your frontend's system folder (any name works), then restart the core. " - "If you have multiple NAND files, you can choose one in the core options. " - "If you want to play a regular DS game, try disabling DSi mode in the core options." + NoDsiNandProblem, + NoDsiNandSolution ) { - } -MelonDsDs::dsi_nand_missing_exception::dsi_nand_missing_exception(string_view nandName) noexcept +MelonDsDs::dsi_nand_missing_exception::dsi_nand_missing_exception(string_view path) noexcept : bios_exception( - fmt::format( - "The core is set to use the NAND file at \"{}\", but it wasn't there or it couldn't be loaded.", - nandName - ), - fmt::format( - "Place your NAND file in your frontend's system folder, name it \"{}\", then restart the core. " - "If you've already done that, ensure that you're using the right NAND file.", - nandName - ) + fmt::format(MissingDsiNandProblem, arg("path", path)), + fmt::format(MissingDsiNandSolution, arg("path", path)) ) { } -MelonDsDs::dsi_nand_corrupted_exception::dsi_nand_corrupted_exception(string_view nandName) noexcept +MelonDsDs::dsi_nand_corrupted_exception::dsi_nand_corrupted_exception(string_view path) noexcept : bios_exception( - fmt::format( - "The core managed to load the configured NAND file at \"{}\", " - "but it seems to be corrupted or invalid.", - nandName - ), - "Make sure that you're using the right NAND file, " - "and restore it from a backup copy if necessary. " - "Check to see if this NAND file works in the original melonDS emulator." + fmt::format(CorruptDsiNandProblem, arg("path", path)), + CorruptDsiNandSolution ) { } diff --git a/src/libretro/exceptions.hpp b/src/libretro/exceptions.hpp index c13ac2dd..e498a97e 100644 --- a/src/libretro/exceptions.hpp +++ b/src/libretro/exceptions.hpp @@ -97,7 +97,7 @@ namespace MelonDsDs class nds_firmware_not_bootable_exception : public bios_exception { public: explicit nds_firmware_not_bootable_exception() noexcept; - explicit nds_firmware_not_bootable_exception(std::string_view firmwareName) noexcept; + explicit nds_firmware_not_bootable_exception(std::string_view path) noexcept; }; diff --git a/src/libretro/libretro.cpp b/src/libretro/libretro.cpp index 5dcfe28b..b2dbd7f8 100644 --- a/src/libretro/libretro.cpp +++ b/src/libretro/libretro.cpp @@ -44,10 +44,12 @@ #include "info.hpp" #include "retro/task_queue.hpp" #include "sram.hpp" +#include "strings/strings.hpp" #include "tracy.hpp" #include "version.hpp" using namespace melonDS; +using namespace MelonDsDs::strings::en_us; using std::make_optional; using std::optional; using std::nullopt; @@ -194,7 +196,7 @@ PUBLIC_SYMBOL void retro_reset(void) { retro::shutdown(); } catch (...) { - retro::set_error_message("An unknown error has occurred."); + retro::set_error_message(UnknownError); retro::shutdown(); } } @@ -280,7 +282,7 @@ void MelonDsDs::HardwareContextReset() noexcept { retro::shutdown(); } catch (...) { - retro::set_error_message("OpenGL context initialization failed with an unknown error. Please report this issue."); + retro::set_error_message(UnknownError); retro::shutdown(); } } diff --git a/src/libretro/message/error.cpp b/src/libretro/message/error.cpp index 066229fa..44c69df7 100644 --- a/src/libretro/message/error.cpp +++ b/src/libretro/message/error.cpp @@ -24,6 +24,7 @@ #include "embedded/melondsds_error_title_font.h" #include "embedded/melondsds_error_body_font.h" #include "screenlayout.hpp" +#include "strings/en_us.hpp" #include "tracy.hpp" constexpr int TITLE_FONT_HEIGHT = 20; // in pixels @@ -34,12 +35,9 @@ constexpr pntr_color TEXT_COLOR_TOP = {.rgba = {.b = 0x19, .g = 0x0F, .r = 0xD7, constexpr pntr_color BACKGROUND_COLOR_BOTTOM = {.rgba = {.b = 0x36, .g = 0x7D, .r = 0x63, .a = 0xFF}}; // dark green constexpr pntr_color TEXT_COLOR_BOTTOM = {.rgba = {.b = 0x98, .g = 0xE5, .r = 0xE7, .a = 0xFF}}; // light green -static constexpr const char* const ERROR_TITLE = "Oh no! melonDS DS couldn't start..."; -static constexpr const char* const SOLUTION_TITLE = "Here's what you can do:"; -static constexpr const char* const THANK_YOU = "Thank you for using melonDS DS!"; - using std::span; using MelonDsDs::NDS_SCREEN_AREA; +using namespace MelonDsDs::strings::en_us; // I intentionally fix the error message to the DS screen size to simplify the layout. MelonDsDs::error::ErrorScreen::ErrorScreen(const config_exception& e) noexcept : exception(e) { @@ -101,11 +99,11 @@ void MelonDsDs::error::ErrorScreen::DrawTopScreen(pntr_font* titleFont, pntr_fon pntr_unload_image(errorIcon); // now draw the title - pntr_vector titleTextSize = pntr_measure_text_ex(titleFont, ERROR_TITLE, 0); + pntr_vector titleTextSize = pntr_measure_text_ex(titleFont, ErrorScreenTitle, 0); pntr_draw_text( topScreen, titleFont, - ERROR_TITLE, + ErrorScreenTitle, (NDS_SCREEN_WIDTH - titleTextSize.x) / 2, MARGIN, TEXT_COLOR_TOP @@ -146,11 +144,11 @@ void MelonDsDs::error::ErrorScreen::DrawBottomScreen(pntr_font* titleFont, pntr_ pntr_unload_image(sorryIcon); // now draw the title - pntr_vector titleTextSize = pntr_measure_text_ex(titleFont, SOLUTION_TITLE, 0); + pntr_vector titleTextSize = pntr_measure_text_ex(titleFont, ErrorScreenSolution, 0); pntr_draw_text( bottomScreen, titleFont, - SOLUTION_TITLE, + ErrorScreenSolution, (NDS_SCREEN_WIDTH - titleTextSize.x) / 2, MARGIN, TEXT_COLOR_BOTTOM @@ -167,11 +165,11 @@ void MelonDsDs::error::ErrorScreen::DrawBottomScreen(pntr_font* titleFont, pntr_ TEXT_COLOR_BOTTOM ); - pntr_vector thankYouTextSize = pntr_measure_text_ex(bodyFont, THANK_YOU, 0); + pntr_vector thankYouTextSize = pntr_measure_text_ex(bodyFont, ErrorScreenThanks, 0); pntr_draw_text( bottomScreen, bodyFont, - THANK_YOU, + ErrorScreenThanks, NDS_SCREEN_WIDTH - thankYouTextSize.x - MARGIN, NDS_SCREEN_HEIGHT - thankYouTextSize.y - MARGIN, TEXT_COLOR_BOTTOM diff --git a/src/libretro/platform/lan.cpp b/src/libretro/platform/lan.cpp index e7afacab..04b593d9 100644 --- a/src/libretro/platform/lan.cpp +++ b/src/libretro/platform/lan.cpp @@ -27,9 +27,11 @@ #include "../config/constants.hpp" #include "../config/config.hpp" #include "../environment.hpp" +#include "../strings/strings.hpp" #include "tracy.hpp" #include "pcap.hpp" +using namespace MelonDsDs::strings::en_us; using namespace melonDS; using std::string; using std::string_view; @@ -164,7 +166,7 @@ bool MelonDsDs::CoreState::LanInit() noexcept { return true; } - retro::set_error_message("Failed to initialize indirect-mode Wi-fi support. Wi-fi will not be emulated."); + retro::set_error_message(IndirectWifiInitFailed); [[fallthrough]]; default: _activeNetworkMode = MelonDsDs::NetworkMode::None; diff --git a/src/libretro/platform/platform.cpp b/src/libretro/platform/platform.cpp index b75deba7..3136b8fb 100644 --- a/src/libretro/platform/platform.cpp +++ b/src/libretro/platform/platform.cpp @@ -32,9 +32,11 @@ #include "../format.hpp" #include "retro/scaler.hpp" #include "sram.hpp" +#include "strings/strings.hpp" #include "tracy.hpp" using namespace melonDS; +using namespace MelonDsDs::strings::en_us; constexpr unsigned DSI_CAMERA_WIDTH = 640; constexpr unsigned DSI_CAMERA_HEIGHT = 480; @@ -42,11 +44,11 @@ void Platform::SignalStop(Platform::StopReason reason) { retro::debug("Platform::SignalStop({})\n", reason); switch (reason) { case StopReason::BadExceptionRegion: - retro::set_error_message("An internal error occurred in the emulated console."); + retro::set_error_message(InternalConsoleError); retro::shutdown(); break; case StopReason::GBAModeNotSupported: - retro::set_error_message("GBA mode is not supported. Use a GBA core instead."); + retro::set_error_message(GbaModeNotSupported); retro::shutdown(); break; case StopReason::PowerOff: diff --git a/src/libretro/render/render.cpp b/src/libretro/render/render.cpp index d6ddf93d..841dfcca 100644 --- a/src/libretro/render/render.cpp +++ b/src/libretro/render/render.cpp @@ -26,13 +26,14 @@ #include "message/error.hpp" #include "render/software.hpp" #include "screenlayout.hpp" +#include "strings/en_us.hpp" #if defined(HAVE_OPENGL) || defined(HAVE_OPENGLES) #include #include "render/opengl.hpp" #endif - +using namespace MelonDsDs::strings::en_us; void MelonDsDs::RenderStateWrapper::Render( melonDS::NDS& nds, const InputState& input, @@ -73,7 +74,7 @@ void MelonDsDs::RenderStateWrapper::SetRenderer(const CoreConfig& config) { break; } - retro::set_warn_message("Failed to initialize OpenGL render state, falling back to software mode."); + retro::set_warn_message(OpenGlInitFailed); [[fallthrough]]; } #endif @@ -115,7 +116,7 @@ void MelonDsDs::RenderStateWrapper::UpdateRenderer(const CoreConfig& config, mel nds.GPU.SetRenderer3D(std::move(renderer)); glRender->RequestRefresh(); } else { - retro::set_warn_message("Failed to initialize OpenGL renderer, falling back to software mode."); + retro::set_warn_message(OpenGlInitFailed); _renderState = std::make_unique(config); nds.GPU.SetRenderer3D(std::make_unique(config.ThreadedSoftRenderer())); } diff --git a/src/libretro/screenlayout.cpp b/src/libretro/screenlayout.cpp index 245cc6c6..44443471 100644 --- a/src/libretro/screenlayout.cpp +++ b/src/libretro/screenlayout.cpp @@ -30,7 +30,9 @@ #include "math.hpp" #include "tracy.hpp" #include "render/render.hpp" +#include "strings/strings.hpp" +using namespace MelonDsDs::strings::en_us; using std::array; using std::max; using glm::inverse; @@ -233,7 +235,7 @@ void MelonDsDs::ScreenLayoutData::Update() noexcept { } else if (newOrientation != retro::ScreenOrientation::Normal) { // A rotation to normal orientation may "fail", even though it's the default. // So only log an error if we're trying to rotate to something besides 0 degrees. - retro::set_error_message("Failed to rotate screen."); + retro::set_error_message(ScreenRotateFailed); } _dirty = false; diff --git a/src/libretro/strings/en_us.hpp b/src/libretro/strings/en_us.hpp new file mode 100644 index 00000000..f6bd0164 --- /dev/null +++ b/src/libretro/strings/en_us.hpp @@ -0,0 +1,27 @@ +/* + Copyright 2024 Jesse Talavera + + 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 MELONDSDS_STRINGS_EN_US_HPP +#define MELONDSDS_STRINGS_EN_US_HPP + +#include "en_us/error.hpp" +#include "en_us/notice.hpp" +#include "en_us/osd.hpp" + +// These strings are intended to be shown directly to the player; +// log messages don't need to be declared here. + +#endif // MELONDSDS_STRINGS_EN_US_HPP diff --git a/src/libretro/strings/en_us/error.hpp b/src/libretro/strings/en_us/error.hpp new file mode 100644 index 00000000..68c84e0e --- /dev/null +++ b/src/libretro/strings/en_us/error.hpp @@ -0,0 +1,110 @@ +/* + Copyright 2024 Jesse Talavera + + 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 MELONDSDS_STRINGS_EN_US_ERROR_HPP +#define MELONDSDS_STRINGS_EN_US_ERROR_HPP + +// For messages that are meant to be displayed on the error screen +namespace MelonDsDs::strings::en_us { + constexpr const char* const ErrorScreenTitle = "Oh no! melonDS DS couldn't start..."; + constexpr const char* const ErrorScreenSolution = "Here's what you can do:"; + constexpr const char* const ErrorScreenThanks = "Thank you for using melonDS DS!"; + + constexpr const char* const NativeFirmwareNotBootableProblem = + "The firmware file at \"{path}\" can't be used to boot to the DS menu."; + + constexpr const char* const BuiltInFirmwareNotBootableProblem = + "The built-in firmware can't be used to boot to the DS menu."; + + constexpr const char* const FirmwareNotBootableSolution = + "Ensure you have native DS (not DSi) firmware in your frontend's system folder. " + "Pick it in the core options, then restart the core. " + "If you just want to play a DS game, try setting Boot Mode to \"Direct\" " + "or BIOS/Firmware Mode to \"Built-In\" in the core options."; + + constexpr const char* const WrongFirmwareProblem = + "The firmware file at \"{path}\" is for the {firmwareConsole}, but it can't be used in {console} mode."; + + constexpr const char* const WrongFirmwareSolution = + "Ensure you have a {console}-compatible firmware file in your frontend's system folder (any name works). " + "Pick it in the core options, then restart the core. " + "If you just want to play a DS game, try disabling DSi mode in the core options."; + + constexpr const char* const WrongNandRegionProblem = + "The NAND file at \"{path}\" has the region \"{region}\", " + "but the loaded DSiWare game will only run in the following regions: {regions}"; + + constexpr const char* const WrongNandRegionSolution = + "Double-check that you're using the right NAND file " + "and the right copy of your game."; + + constexpr const char* const NoDsiFirmwareProblem = + "DSi mode requires a firmware file from a DSi, but none was found."; + + constexpr const char* const NoDsiFirmwareSolution = + "Place your DSi firmware file in your frontend's system folder, " + "then restart the core. " + "If you just want to play a DS game, " + "try disabling DSi mode in the core options."; + + constexpr const char* const NoFirmwareProblem = + "The core is set to use the firmware file at \"{path}\", but it wasn't there or it couldn't be loaded."; + + constexpr const char* const NoFirmwareSolution = + "Place your DSi firmware file in your frontend's system folder, name it \"{path}\", then restart the core."; + + constexpr const char* const IncompleteNdsSysfilesProblem = + "Booting to the native DS menu requires native DS firmware and BIOS files, " + "but some of them were missing or couldn't be loaded."; + + constexpr const char* const IncompleteNdsSysfilesSolution = + "Place your DS system files in your frontend's system folder, then restart the core. " + "If you want to play a regular DS game, try setting Boot Mode to \"Direct\" " + "and BIOS/Firmware Mode to \"Built-In\" in the core options."; + + constexpr const char* const MissingDsiBiosProblem = + "DSi mode requires the {bios} BIOS file, but none was found."; + + constexpr const char* const MissingDsiBiosSolution = + "Place your {bios} BIOS file in your frontend's system folder, name it \"{path}\", then restart the core. " + "If you want to play a regular DS game, try disabling DSi mode in the core options."; + + constexpr const char* const NoDsiNandProblem = + "DSi mode requires a NAND image, but none was found."; + + constexpr const char* const NoDsiNandSolution = + "Place your NAND file in your frontend's system folder (any name works), then restart the core. " + "If you have multiple NAND files, you can choose one in the core options. " + "If you want to play a regular DS game, try disabling DSi mode in the core options."; + + constexpr const char* const MissingDsiNandProblem = + "The core is set to use the NAND file at \"{path}\", but it wasn't there or it couldn't be loaded."; + + constexpr const char* const MissingDsiNandSolution = + "Place your NAND file in your frontend's system folder, name it \"{path}\", then restart the core. " + "If you've already done that, ensure that you're using the right NAND file."; + + constexpr const char* const CorruptDsiNandProblem = + "The core managed to load the configured NAND file at \"{path}\", " + "but it seems to be corrupted or invalid."; + + constexpr const char* const CorruptDsiNandSolution = + "Make sure that you're using the right NAND file, " + "and restore it from a backup copy if necessary. " + "Check to see if this NAND file works in the standalone melonDS emulator."; +} + +#endif //MELONDSDS_STRINGS_EN_US_ERROR_HPP diff --git a/src/libretro/strings/en_us/notice.hpp b/src/libretro/strings/en_us/notice.hpp new file mode 100644 index 00000000..52ffbb5c --- /dev/null +++ b/src/libretro/strings/en_us/notice.hpp @@ -0,0 +1,103 @@ +/* + Copyright 2024 Jesse Talavera + + 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 MELONDSDS_STRINGS_EN_US_NOTICE_HPP +#define MELONDSDS_STRINGS_EN_US_NOTICE_HPP + +// For strings that are meant to be briefly displayed as on-screen notices +namespace MelonDsDs::strings::en_us { + constexpr const char* const InternalError = + "An internal error occurred with melonDS DS. " + "Please contact the developer with the log file."; + + constexpr const char* const UnknownError = + "An unknown error has occurred with melonDS DS. " + "Please contact the developer with the log file."; + + constexpr const char* const InvalidCheat = "Cheat #{index} ({code:.8}...) isn't valid, ignoring it."; + + constexpr const char* const ArchivedGbaSaveNotSupported = + "melonDS DS does not support archived GBA save data right now. " + "Please extract it and try again. " + "Continuing without using the save data."; + + constexpr const char* const CantDisableCheat = + "Action Replay codes can't be undone, restart the game to disable them."; + + constexpr const char* const CompressedGbaSaveNotSupported = + "melonDS DS does not support compressed GBA save data right now. " + "Please disable save data compression in the frontend and try again. " + "Continuing without using the save data."; + + constexpr const char* const DsiDoesntHaveGbaSlot = + "The DSi doesn't have a GBA slot, " + "please use DS mode instead. " + "Ignoring the loaded GBA ROM."; + + constexpr const char* const GbaModeNotSupported = + "GBA mode is not supported. Use a GBA core instead."; + + constexpr const char* const IndirectWifiInitFailed = + "Failed to initialize indirect-mode Wi-fi support. Wi-fi will be disabled."; + + constexpr const char* const InternalConsoleError = + "An internal error occurred in the emulated console."; + + constexpr const char* const HackedFirmwareWarning = + "Corrupted firmware detected! " + "Any game that alters Wi-fi settings will break this firmware, even on real hardware."; + + constexpr const char* const MicNotSupported = + "This frontend doesn't support microphones."; + + constexpr const char* const OpenGlInitFailed = + "Failed to initialize OpenGL, falling back to software mode."; + + constexpr const char* const OptionInitFailed = + "Failed to set core option definitions, functionality will be limited."; + + constexpr const char* const PixelFormatUnsupported = + "Failed to set the required XRGB8888 pixel format for rendering; it may not be supported."; + + constexpr const char* const PleaseResetCore = + "Please follow the advice on this screen, then unload/reload the core."; + + constexpr const char* const ScreenRotateFailed = + "Failed to rotate screen."; + + constexpr const char* const StateTooOld = + "This savestate is too old, can't load it.\n" + "Save your game normally in the older version and import the save data."; + + constexpr const char* const StateTooNew = + "This savestate is too new, can't load it.\n" + "Save your game normally in the newer version, " + "then update this core or import the save data."; + + constexpr const char* const StateLoadFailed = + "Can't load this savestate; did it come from the right core and game?"; + + constexpr const char* const SysDirFailed = + "Failed to get the system directory, functionality will be limited."; + + constexpr const char* const UsernameFailed = + "Can't use the name \"{name}\" on the DS, using default name instead."; + + constexpr const char* const UsernameUnavailable = + "Failed to get username, or none was provided; using default"; +} + +#endif //MELONDSDS_STRINGS_EN_US_NOTICE_HPP diff --git a/src/libretro/strings/en_us/osd.hpp b/src/libretro/strings/en_us/osd.hpp new file mode 100644 index 00000000..01a53db1 --- /dev/null +++ b/src/libretro/strings/en_us/osd.hpp @@ -0,0 +1,26 @@ +/* + Copyright 2024 Jesse Talavera + + 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 MELONDSDS_STRINGS_EN_US_OSD_HPP +#define MELONDSDS_STRINGS_EN_US_OSD_HPP + +// For strings that are meant to be displayed on the on-screen status display +namespace MelonDsDs::strings::en_us { + constexpr const char* const CurrentLayout = "{sep}Layout {layout_num}/{num_layouts}"; + constexpr const char* const ScreenState = "{sep}Closed"; +} + +#endif //MELONDSDS_STRINGS_EN_US_OSD_HPP diff --git a/src/libretro/strings/strings.hpp b/src/libretro/strings/strings.hpp new file mode 100644 index 00000000..a5a910b9 --- /dev/null +++ b/src/libretro/strings/strings.hpp @@ -0,0 +1,22 @@ +/* + Copyright 2024 Jesse Talavera + + 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 MELONDSDS_STRINGS_STRINGS_HPP +#define MELONDSDS_STRINGS_STRINGS_HPP + +#include "en_us.hpp" + +#endif // MELONDSDS_STRINGS_STRINGS_HPP