Localize remaining hardcoded UI strings and persist language preference
Replaces the last hardcoded English strings (initial-scene modals, game menu save status messages) with locale-loaded text, adds a shared LOCALE_LIST so the settings dropdown and locale manager stay in sync, and persists the chosen language into savemeta_t across all platforms. Also fixes two message buffers that were too small for their longest translations. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -60,6 +60,58 @@ msgstr "Settings"
|
||||
msgid "ui.game_menu.save"
|
||||
msgstr "Save"
|
||||
|
||||
#: src/dusk/ui/frame/game/uigamemenu.c
|
||||
msgid "ui.game_menu.save_success"
|
||||
msgstr "Game saved."
|
||||
|
||||
#: src/dusk/ui/frame/game/uigamemenu.c
|
||||
msgid "ui.game_menu.save_cancelled"
|
||||
msgstr "Save cancelled."
|
||||
|
||||
#: src/dusk/ui/frame/game/uigamemenu.c
|
||||
msgid "ui.game_menu.save_unavailable"
|
||||
msgstr "Can't save - no save device found."
|
||||
|
||||
#: src/dusk/ui/frame/game/uigamemenu.c
|
||||
msgid "ui.game_menu.save_temporary"
|
||||
msgstr "This session is temporary - no save device was found, so saving is disabled."
|
||||
|
||||
#: src/dusk/ui/frame/game/uigamemenu.c
|
||||
msgid "ui.game_menu.save_create_confirm"
|
||||
msgstr "No save data found. Create a new save?"
|
||||
|
||||
#: src/dusk/ui/frame/game/uigamemenu.c
|
||||
msgid "ui.game_menu.save_failed_format"
|
||||
msgstr "Save failed: %s"
|
||||
|
||||
#: src/dusk/ui/frame/game/uigamemenu.c
|
||||
msgid "ui.game_menu.save_check_failed_format"
|
||||
msgstr "Can't save: %s"
|
||||
|
||||
#: src/dusk/ui/frame/initial/uiinitialnocard.c
|
||||
msgid "ui.initial.no_card.message"
|
||||
msgstr "No save device found. You can continue, but\nprogress will not be saved."
|
||||
|
||||
#: src/dusk/ui/frame/initial/uiinitialnocard.c
|
||||
msgid "ui.initial.no_card.retry"
|
||||
msgstr "Retry"
|
||||
|
||||
#: src/dusk/ui/frame/initial/uiinitialnocard.c
|
||||
msgid "ui.initial.no_card.continue"
|
||||
msgstr "Continue Anyway"
|
||||
|
||||
#: src/dusk/ui/frame/initial/uiinitialcreatesave.c
|
||||
msgid "ui.initial.create_save.message"
|
||||
msgstr "No save data found. Create a new save?"
|
||||
|
||||
#: src/dusk/ui/frame/initial/uiinitialcreatesave.c
|
||||
msgid "ui.initial.create_save.yes"
|
||||
msgstr "Yes"
|
||||
|
||||
#: src/dusk/ui/frame/initial/uiinitialcreatesave.c
|
||||
msgid "ui.initial.create_save.no"
|
||||
msgstr "No"
|
||||
|
||||
msgid "item.potion.name"
|
||||
msgstr "Potion"
|
||||
|
||||
|
||||
@@ -61,6 +61,58 @@ msgstr "Configuración"
|
||||
msgid "ui.game_menu.save"
|
||||
msgstr "Guardar"
|
||||
|
||||
#: src/dusk/ui/frame/game/uigamemenu.c
|
||||
msgid "ui.game_menu.save_success"
|
||||
msgstr "Partida guardada."
|
||||
|
||||
#: src/dusk/ui/frame/game/uigamemenu.c
|
||||
msgid "ui.game_menu.save_cancelled"
|
||||
msgstr "Guardado cancelado."
|
||||
|
||||
#: src/dusk/ui/frame/game/uigamemenu.c
|
||||
msgid "ui.game_menu.save_unavailable"
|
||||
msgstr "No se puede guardar: no se encontró ningún dispositivo de guardado."
|
||||
|
||||
#: src/dusk/ui/frame/game/uigamemenu.c
|
||||
msgid "ui.game_menu.save_temporary"
|
||||
msgstr "Esta sesión es temporal - no se encontró ningún dispositivo de guardado, por lo que guardar está deshabilitado."
|
||||
|
||||
#: src/dusk/ui/frame/game/uigamemenu.c
|
||||
msgid "ui.game_menu.save_create_confirm"
|
||||
msgstr "No se encontraron datos guardados. ¿Crear una partida nueva?"
|
||||
|
||||
#: src/dusk/ui/frame/game/uigamemenu.c
|
||||
msgid "ui.game_menu.save_failed_format"
|
||||
msgstr "Error al guardar: %s"
|
||||
|
||||
#: src/dusk/ui/frame/game/uigamemenu.c
|
||||
msgid "ui.game_menu.save_check_failed_format"
|
||||
msgstr "No se puede guardar: %s"
|
||||
|
||||
#: src/dusk/ui/frame/initial/uiinitialnocard.c
|
||||
msgid "ui.initial.no_card.message"
|
||||
msgstr "No se encontró ningún dispositivo de guardado. Puedes continuar, pero\nel progreso no se guardará."
|
||||
|
||||
#: src/dusk/ui/frame/initial/uiinitialnocard.c
|
||||
msgid "ui.initial.no_card.retry"
|
||||
msgstr "Reintentar"
|
||||
|
||||
#: src/dusk/ui/frame/initial/uiinitialnocard.c
|
||||
msgid "ui.initial.no_card.continue"
|
||||
msgstr "Continuar de todos modos"
|
||||
|
||||
#: src/dusk/ui/frame/initial/uiinitialcreatesave.c
|
||||
msgid "ui.initial.create_save.message"
|
||||
msgstr "No se encontraron datos guardados. ¿Crear una partida nueva?"
|
||||
|
||||
#: src/dusk/ui/frame/initial/uiinitialcreatesave.c
|
||||
msgid "ui.initial.create_save.yes"
|
||||
msgstr "Sí"
|
||||
|
||||
#: src/dusk/ui/frame/initial/uiinitialcreatesave.c
|
||||
msgid "ui.initial.create_save.no"
|
||||
msgstr "No"
|
||||
|
||||
#: src/dusk/rpg/item/item.json
|
||||
msgid "item.potion.name"
|
||||
msgstr "Poción"
|
||||
|
||||
@@ -61,6 +61,58 @@ msgstr "設定"
|
||||
msgid "ui.game_menu.save"
|
||||
msgstr "セーブ"
|
||||
|
||||
#: src/dusk/ui/frame/game/uigamemenu.c
|
||||
msgid "ui.game_menu.save_success"
|
||||
msgstr "セーブしました。"
|
||||
|
||||
#: src/dusk/ui/frame/game/uigamemenu.c
|
||||
msgid "ui.game_menu.save_cancelled"
|
||||
msgstr "セーブをキャンセルしました。"
|
||||
|
||||
#: src/dusk/ui/frame/game/uigamemenu.c
|
||||
msgid "ui.game_menu.save_unavailable"
|
||||
msgstr "セーブできません - セーブデバイスが見つかりません。"
|
||||
|
||||
#: src/dusk/ui/frame/game/uigamemenu.c
|
||||
msgid "ui.game_menu.save_temporary"
|
||||
msgstr "このセッションは一時的です - セーブデバイスが見つからなかったため、セーブは無効になっています。"
|
||||
|
||||
#: src/dusk/ui/frame/game/uigamemenu.c
|
||||
msgid "ui.game_menu.save_create_confirm"
|
||||
msgstr "セーブデータが見つかりません。新しいセーブを作成しますか?"
|
||||
|
||||
#: src/dusk/ui/frame/game/uigamemenu.c
|
||||
msgid "ui.game_menu.save_failed_format"
|
||||
msgstr "セーブに失敗しました: %s"
|
||||
|
||||
#: src/dusk/ui/frame/game/uigamemenu.c
|
||||
msgid "ui.game_menu.save_check_failed_format"
|
||||
msgstr "セーブできません: %s"
|
||||
|
||||
#: src/dusk/ui/frame/initial/uiinitialnocard.c
|
||||
msgid "ui.initial.no_card.message"
|
||||
msgstr "セーブデバイスが見つかりません。続行できますが、\n進行状況は保存されません。"
|
||||
|
||||
#: src/dusk/ui/frame/initial/uiinitialnocard.c
|
||||
msgid "ui.initial.no_card.retry"
|
||||
msgstr "再試行"
|
||||
|
||||
#: src/dusk/ui/frame/initial/uiinitialnocard.c
|
||||
msgid "ui.initial.no_card.continue"
|
||||
msgstr "続行する"
|
||||
|
||||
#: src/dusk/ui/frame/initial/uiinitialcreatesave.c
|
||||
msgid "ui.initial.create_save.message"
|
||||
msgstr "セーブデータが見つかりません。新しいセーブを作成しますか?"
|
||||
|
||||
#: src/dusk/ui/frame/initial/uiinitialcreatesave.c
|
||||
msgid "ui.initial.create_save.yes"
|
||||
msgstr "はい"
|
||||
|
||||
#: src/dusk/ui/frame/initial/uiinitialcreatesave.c
|
||||
msgid "ui.initial.create_save.no"
|
||||
msgstr "いいえ"
|
||||
|
||||
#: src/dusk/rpg/item/item.json
|
||||
msgid "item.potion.name"
|
||||
msgstr "ポーション"
|
||||
|
||||
@@ -7,16 +7,42 @@
|
||||
|
||||
#include "localemanager.h"
|
||||
#include "util/memory.h"
|
||||
#include "util/string.h"
|
||||
#include "assert/assert.h"
|
||||
#include "save/save.h"
|
||||
|
||||
localemanager_t LOCALE;
|
||||
|
||||
const localeinfo_t * const LOCALE_LIST[LOCALE_LIST_COUNT] = {
|
||||
&LOCALE_EN_US,
|
||||
&LOCALE_JP_JP,
|
||||
&LOCALE_ES_MX
|
||||
};
|
||||
|
||||
errorret_t localeManagerInit() {
|
||||
memoryZero(&LOCALE, sizeof(localemanager_t));
|
||||
errorChain(localeManagerSetLocale(&LOCALE_EN_US));
|
||||
// saveInit() runs before this (see engine.c) and, on most platforms,
|
||||
// has already loaded a persisted language choice into savemeta_t by
|
||||
// this point - see localeManagerGetByIndex().
|
||||
errorChain(localeManagerSetLocale(
|
||||
localeManagerGetByIndex(saveGetMeta()->language)
|
||||
));
|
||||
errorOk();
|
||||
}
|
||||
|
||||
uint8_t localeManagerGetIndex(const localeinfo_t *locale) {
|
||||
assertNotNull(locale, "Locale cannot be NULL");
|
||||
for(uint8_t i = 0; i < LOCALE_LIST_COUNT; i++) {
|
||||
if(stringCompare(LOCALE_LIST[i]->file, locale->file) == 0) return i;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
const localeinfo_t * localeManagerGetByIndex(const uint8_t index) {
|
||||
if(index >= LOCALE_LIST_COUNT) return LOCALE_LIST[0];
|
||||
return LOCALE_LIST[index];
|
||||
}
|
||||
|
||||
errorret_t localeManagerSetLocale(const localeinfo_t *locale) {
|
||||
assertNotNull(locale, "Locale cannot be NULL");
|
||||
|
||||
|
||||
@@ -18,6 +18,15 @@ typedef struct {
|
||||
|
||||
extern localemanager_t LOCALE;
|
||||
|
||||
/**
|
||||
* Every locale the game supports, in a fixed, stable order - index into
|
||||
* this array is what gets persisted as savemeta_t.language (see
|
||||
* save/savemeta.h), so the order here must never change once shipped
|
||||
* (only append new locales at the end).
|
||||
*/
|
||||
#define LOCALE_LIST_COUNT 3
|
||||
extern const localeinfo_t * const LOCALE_LIST[LOCALE_LIST_COUNT];
|
||||
|
||||
/**
|
||||
* Initialize the locale system.
|
||||
*
|
||||
@@ -33,6 +42,27 @@ errorret_t localeManagerInit();
|
||||
*/
|
||||
errorret_t localeManagerSetLocale(const localeinfo_t *locale);
|
||||
|
||||
/**
|
||||
* Finds the LOCALE_LIST index matching the given locale's file, by
|
||||
* content rather than pointer identity (localeinfo_t instances are
|
||||
* declared `static const` in a header, so the same locale gets a
|
||||
* distinct pointer in every translation unit that references it).
|
||||
*
|
||||
* @param locale The locale to find.
|
||||
* @return The matching index in LOCALE_LIST, or 0 if not found.
|
||||
*/
|
||||
uint8_t localeManagerGetIndex(const localeinfo_t *locale);
|
||||
|
||||
/**
|
||||
* Gets the locale at the given LOCALE_LIST index, clamping to index 0
|
||||
* (LOCALE_EN_US) if out of range - e.g. a save file's persisted language
|
||||
* index from a future build with more locales than this one supports.
|
||||
*
|
||||
* @param index The index to look up.
|
||||
* @return The locale at that index, or LOCALE_LIST[0] if out of range.
|
||||
*/
|
||||
const localeinfo_t * localeManagerGetByIndex(const uint8_t index);
|
||||
|
||||
/**
|
||||
* Get a localized string for the given message ID.
|
||||
*
|
||||
|
||||
@@ -20,6 +20,7 @@ static void _saveEagerLoadComplete(errorret_t result, void *user) {
|
||||
errorret_t saveInit(void) {
|
||||
memoryZero(&SAVE, sizeof(save_t));
|
||||
SAVE.meta.deadzone = SAVE_META_DEADZONE_DEFAULT;
|
||||
SAVE.meta.language = SAVE_META_LANGUAGE_DEFAULT;
|
||||
|
||||
#ifdef saveInitPlatform
|
||||
// A missing/unreachable save medium is expected, recoverable state,
|
||||
|
||||
@@ -25,6 +25,13 @@
|
||||
*/
|
||||
#define SAVE_META_DEADZONE_DEFAULT 0.1f
|
||||
|
||||
/**
|
||||
* Default language index for meta that's never actually been loaded from
|
||||
* disk yet - index 0 into LOCALE_LIST (see locale/localemanager.h), i.e.
|
||||
* LOCALE_EN_US.
|
||||
*/
|
||||
#define SAVE_META_LANGUAGE_DEFAULT 0
|
||||
|
||||
/**
|
||||
* Device/user-wide preferences, independent of any individual game save
|
||||
* slot (see saveslot.h) - there's exactly one of these, not one per slot,
|
||||
@@ -44,4 +51,13 @@ typedef struct {
|
||||
* rather than caching it anywhere else.
|
||||
*/
|
||||
float_t deadzone;
|
||||
/**
|
||||
* Index into LOCALE_LIST (see locale/localemanager.h) for the player's
|
||||
* preferred UI language - the save meta is the only place this lives;
|
||||
* read it directly via saveGetMeta()->language rather than caching it
|
||||
* anywhere else. Never reorder/remove entries from LOCALE_LIST, only
|
||||
* append, since this index must keep meaning the same locale across
|
||||
* versions.
|
||||
*/
|
||||
uint8_t language;
|
||||
} savemeta_t;
|
||||
|
||||
@@ -340,6 +340,7 @@ errorret_t saveMetaSerializeRead(savestream_t *stream, savemeta_t *meta) {
|
||||
);
|
||||
saveFileReadVersion(stream, &meta->version);
|
||||
saveFileReadFloat(stream, &meta->deadzone);
|
||||
saveFileReadUInt8(stream, &meta->language);
|
||||
errorChain(saveStreamVerifyChecksumImpl(stream, "Save meta"));
|
||||
meta->exists = true;
|
||||
errorOk();
|
||||
@@ -354,6 +355,7 @@ errorret_t saveMetaSerializeWrite(savestream_t *stream, savemeta_t *meta) {
|
||||
saveFileWriteHeader(stream, meta->header, SAVE_META_HEADER_SIZE);
|
||||
saveFileWriteVersion(stream, &meta->version);
|
||||
saveFileWriteFloat(stream, &meta->deadzone);
|
||||
saveFileWriteUInt8(stream, &meta->language);
|
||||
errorChain(saveStreamFinalizeWriteImpl(
|
||||
stream, headerPosition, SAVE_META_HEADER_SIZE
|
||||
));
|
||||
|
||||
@@ -33,19 +33,19 @@ static void uiGameMenuSaveWriteComplete(errorret_t result, void *user) {
|
||||
// the longest platform save-error message plus this prefix.
|
||||
char_t msg[256];
|
||||
stringFormat(
|
||||
msg, sizeof(msg), "Save failed: %s", result.state->message
|
||||
msg, sizeof(msg), UI_GAME_MENU.saveFailedFormat, result.state->message
|
||||
);
|
||||
errorCatch(result);
|
||||
uiTextboxMainSetText(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
uiTextboxMainSetText("Game saved.");
|
||||
uiTextboxMainSetText(UI_GAME_MENU.saveSuccessText);
|
||||
}
|
||||
|
||||
static void uiGameMenuSaveCreateConfirmed(const bool_t confirmed, void *user) {
|
||||
if(!confirmed) {
|
||||
uiTextboxMainSetText("Save cancelled.");
|
||||
uiTextboxMainSetText(UI_GAME_MENU.saveCancelledText);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -62,7 +62,10 @@ static void uiGameMenuSaveCreateConfirmed(const bool_t confirmed, void *user) {
|
||||
static void uiGameMenuSaveCheckComplete(errorret_t result, void *user) {
|
||||
if(errorIsNotOk(result)) {
|
||||
char_t msg[256];
|
||||
stringFormat(msg, sizeof(msg), "Can't save: %s", result.state->message);
|
||||
stringFormat(
|
||||
msg, sizeof(msg), UI_GAME_MENU.saveCheckFailedFormat,
|
||||
result.state->message
|
||||
);
|
||||
errorCatch(result);
|
||||
uiTextboxMainSetText(msg);
|
||||
return;
|
||||
@@ -74,7 +77,7 @@ static void uiGameMenuSaveCheckComplete(errorret_t result, void *user) {
|
||||
}
|
||||
|
||||
uiConfirmOpen(
|
||||
"No save data found. Create a new save?",
|
||||
UI_GAME_MENU.saveCreateConfirmText,
|
||||
uiGameMenuSaveCreateConfirmed,
|
||||
NULL
|
||||
);
|
||||
@@ -82,14 +85,11 @@ static void uiGameMenuSaveCheckComplete(errorret_t result, void *user) {
|
||||
|
||||
static void uiGameMenuSave(void) {
|
||||
if(saveIsTemporary()) {
|
||||
uiTextboxMainSetText(
|
||||
"This session is temporary - no save device was found, so saving "
|
||||
"is disabled."
|
||||
);
|
||||
uiTextboxMainSetText(UI_GAME_MENU.saveTemporaryText);
|
||||
return;
|
||||
}
|
||||
if(!saveIsAvailable()) {
|
||||
uiTextboxMainSetText("Can't save - no save device found.");
|
||||
uiTextboxMainSetText(UI_GAME_MENU.saveUnavailableText);
|
||||
return;
|
||||
}
|
||||
if(saveIsBusy()) return;// A save/load dialog (e.g. on PSP) is already up.
|
||||
@@ -140,6 +140,55 @@ errorret_t uiGameMenuInit(void) {
|
||||
UI_GAME_MENU.saveLabel,
|
||||
UI_GAME_MENU_LABEL_MAX
|
||||
));
|
||||
errorChain(assetLocaleGetString(
|
||||
&LOCALE.entry->data.locale,
|
||||
"ui.game_menu.save_success",
|
||||
0,
|
||||
UI_GAME_MENU.saveSuccessText,
|
||||
UI_GAME_MENU_MESSAGE_MAX
|
||||
));
|
||||
errorChain(assetLocaleGetString(
|
||||
&LOCALE.entry->data.locale,
|
||||
"ui.game_menu.save_cancelled",
|
||||
0,
|
||||
UI_GAME_MENU.saveCancelledText,
|
||||
UI_GAME_MENU_MESSAGE_MAX
|
||||
));
|
||||
errorChain(assetLocaleGetString(
|
||||
&LOCALE.entry->data.locale,
|
||||
"ui.game_menu.save_unavailable",
|
||||
0,
|
||||
UI_GAME_MENU.saveUnavailableText,
|
||||
UI_GAME_MENU_MESSAGE_MAX
|
||||
));
|
||||
errorChain(assetLocaleGetString(
|
||||
&LOCALE.entry->data.locale,
|
||||
"ui.game_menu.save_temporary",
|
||||
0,
|
||||
UI_GAME_MENU.saveTemporaryText,
|
||||
UI_GAME_MENU_MESSAGE_MAX
|
||||
));
|
||||
errorChain(assetLocaleGetString(
|
||||
&LOCALE.entry->data.locale,
|
||||
"ui.game_menu.save_create_confirm",
|
||||
0,
|
||||
UI_GAME_MENU.saveCreateConfirmText,
|
||||
UI_GAME_MENU_MESSAGE_MAX
|
||||
));
|
||||
errorChain(assetLocaleGetString(
|
||||
&LOCALE.entry->data.locale,
|
||||
"ui.game_menu.save_failed_format",
|
||||
0,
|
||||
UI_GAME_MENU.saveFailedFormat,
|
||||
UI_GAME_MENU_MESSAGE_MAX
|
||||
));
|
||||
errorChain(assetLocaleGetString(
|
||||
&LOCALE.entry->data.locale,
|
||||
"ui.game_menu.save_check_failed_format",
|
||||
0,
|
||||
UI_GAME_MENU.saveCheckFailedFormat,
|
||||
UI_GAME_MENU_MESSAGE_MAX
|
||||
));
|
||||
|
||||
MENU_BEGIN(
|
||||
&UI_GAME_MENU.menu, UI_GAME_MENU.items, uiGameMenuSelected, NULL, NULL
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#define UI_GAME_MENU_ITEM_COUNT 4
|
||||
#define UI_GAME_MENU_WIDTH 150.0f
|
||||
#define UI_GAME_MENU_LABEL_MAX 32
|
||||
#define UI_GAME_MENU_MESSAGE_MAX 192
|
||||
|
||||
typedef struct {
|
||||
uimenu_t menu;
|
||||
@@ -20,6 +21,13 @@ typedef struct {
|
||||
char_t itemsLabel[UI_GAME_MENU_LABEL_MAX];
|
||||
char_t settingsLabel[UI_GAME_MENU_LABEL_MAX];
|
||||
char_t saveLabel[UI_GAME_MENU_LABEL_MAX];
|
||||
char_t saveSuccessText[UI_GAME_MENU_MESSAGE_MAX];
|
||||
char_t saveCancelledText[UI_GAME_MENU_MESSAGE_MAX];
|
||||
char_t saveUnavailableText[UI_GAME_MENU_MESSAGE_MAX];
|
||||
char_t saveTemporaryText[UI_GAME_MENU_MESSAGE_MAX];
|
||||
char_t saveCreateConfirmText[UI_GAME_MENU_MESSAGE_MAX];
|
||||
char_t saveFailedFormat[UI_GAME_MENU_MESSAGE_MAX];
|
||||
char_t saveCheckFailedFormat[UI_GAME_MENU_MESSAGE_MAX];
|
||||
} uigamemenu_t;
|
||||
|
||||
extern uigamemenu_t UI_GAME_MENU;
|
||||
|
||||
@@ -16,9 +16,10 @@
|
||||
#include "display/spritebatch/spritebatch.h"
|
||||
#include "display/texture/texture.h"
|
||||
#include "display/shader/shaderunlit.h"
|
||||
#include "locale/localemanager.h"
|
||||
#include "asset/loader/locale/assetlocaleloader.h"
|
||||
|
||||
#define UI_INITIAL_CREATE_SAVE_BACKDROP_COLOR color4b(0, 0, 0, 160)
|
||||
#define UI_INITIAL_CREATE_SAVE_TEXT "No save data found. Create a new save?"
|
||||
|
||||
uiinitialcreatesave_t UI_INITIAL_CREATE_SAVE;
|
||||
|
||||
@@ -42,12 +43,34 @@ static void uiInitialCreateSaveClosed(const uimenu_t *menu) {
|
||||
errorret_t uiInitialCreateSaveInit(void) {
|
||||
memoryZero(&UI_INITIAL_CREATE_SAVE, sizeof(uiinitialcreatesave_t));
|
||||
|
||||
errorChain(assetLocaleGetString(
|
||||
&LOCALE.entry->data.locale,
|
||||
"ui.initial.create_save.message",
|
||||
0,
|
||||
UI_INITIAL_CREATE_SAVE.text,
|
||||
UI_INITIAL_CREATE_SAVE_TEXT_MAX
|
||||
));
|
||||
errorChain(assetLocaleGetString(
|
||||
&LOCALE.entry->data.locale,
|
||||
"ui.initial.create_save.yes",
|
||||
0,
|
||||
UI_INITIAL_CREATE_SAVE.yesLabel,
|
||||
UI_INITIAL_CREATE_SAVE_LABEL_MAX
|
||||
));
|
||||
errorChain(assetLocaleGetString(
|
||||
&LOCALE.entry->data.locale,
|
||||
"ui.initial.create_save.no",
|
||||
0,
|
||||
UI_INITIAL_CREATE_SAVE.noLabel,
|
||||
UI_INITIAL_CREATE_SAVE_LABEL_MAX
|
||||
));
|
||||
|
||||
MENU_BEGIN(
|
||||
&UI_INITIAL_CREATE_SAVE.menu, UI_INITIAL_CREATE_SAVE.items,
|
||||
uiInitialCreateSaveSelected, uiInitialCreateSaveClosed, NULL
|
||||
);
|
||||
MENU_BUTTON("Yes");
|
||||
MENU_BUTTON("No");
|
||||
MENU_BUTTON(UI_INITIAL_CREATE_SAVE.yesLabel);
|
||||
MENU_BUTTON(UI_INITIAL_CREATE_SAVE.noLabel);
|
||||
|
||||
MENU_END(UI_INITIAL_CREATE_SAVE.items, menuIndex);
|
||||
|
||||
@@ -75,7 +98,7 @@ errorret_t uiInitialCreateSaveDraw(void) {
|
||||
errorChain(spriteBatchFlush());
|
||||
|
||||
int32_t textW, textH;
|
||||
textMeasure(UI_INITIAL_CREATE_SAVE_TEXT, &FONT_DEFAULT, &textW, &textH);
|
||||
textMeasure(UI_INITIAL_CREATE_SAVE.text, &FONT_DEFAULT, &textW, &textH);
|
||||
|
||||
float_t rowHeight = (float_t)FONT_DEFAULT.tileset->tileHeight;
|
||||
float_t width = mathMax(
|
||||
@@ -93,7 +116,7 @@ errorret_t uiInitialCreateSaveDraw(void) {
|
||||
float_t contentWidth = width - (UI_FRAME_START_X * 2);
|
||||
|
||||
errorChain(textDraw(
|
||||
contentX, contentY, UI_INITIAL_CREATE_SAVE_TEXT, COLOR_WHITE, &FONT_DEFAULT
|
||||
contentX, contentY, UI_INITIAL_CREATE_SAVE.text, COLOR_WHITE, &FONT_DEFAULT
|
||||
));
|
||||
|
||||
float_t buttonsY = contentY + rowHeight + UI_FRAME_PADDING_Y;
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
#define UI_INITIAL_CREATE_SAVE_INDEX_NO 1
|
||||
#define UI_INITIAL_CREATE_SAVE_ITEM_COUNT 2
|
||||
#define UI_INITIAL_CREATE_SAVE_MIN_WIDTH 160.0f
|
||||
#define UI_INITIAL_CREATE_SAVE_TEXT_MAX 256
|
||||
#define UI_INITIAL_CREATE_SAVE_LABEL_MAX 32
|
||||
|
||||
/**
|
||||
* Callback invoked once the create-save modal is dismissed.
|
||||
@@ -29,6 +31,9 @@ typedef struct {
|
||||
uiinitialcreatesavecallback_t callback;
|
||||
void *user;
|
||||
bool_t create;
|
||||
char_t text[UI_INITIAL_CREATE_SAVE_TEXT_MAX];
|
||||
char_t yesLabel[UI_INITIAL_CREATE_SAVE_LABEL_MAX];
|
||||
char_t noLabel[UI_INITIAL_CREATE_SAVE_LABEL_MAX];
|
||||
} uiinitialcreatesave_t;
|
||||
|
||||
extern uiinitialcreatesave_t UI_INITIAL_CREATE_SAVE;
|
||||
|
||||
@@ -16,10 +16,10 @@
|
||||
#include "display/spritebatch/spritebatch.h"
|
||||
#include "display/texture/texture.h"
|
||||
#include "display/shader/shaderunlit.h"
|
||||
#include "locale/localemanager.h"
|
||||
#include "asset/loader/locale/assetlocaleloader.h"
|
||||
|
||||
#define UI_INITIAL_NO_CARD_BACKDROP_COLOR color4b(0, 0, 0, 160)
|
||||
#define UI_INITIAL_NO_CARD_TEXT \
|
||||
"No save device found. You can continue, but\nprogress will not be saved."
|
||||
|
||||
uiinitialnocard_t UI_INITIAL_NO_CARD;
|
||||
|
||||
@@ -41,12 +41,34 @@ static void uiInitialNoCardClosed(const uimenu_t *menu) {
|
||||
errorret_t uiInitialNoCardInit(void) {
|
||||
memoryZero(&UI_INITIAL_NO_CARD, sizeof(uiinitialnocard_t));
|
||||
|
||||
errorChain(assetLocaleGetString(
|
||||
&LOCALE.entry->data.locale,
|
||||
"ui.initial.no_card.message",
|
||||
0,
|
||||
UI_INITIAL_NO_CARD.text,
|
||||
UI_INITIAL_NO_CARD_TEXT_MAX
|
||||
));
|
||||
errorChain(assetLocaleGetString(
|
||||
&LOCALE.entry->data.locale,
|
||||
"ui.initial.no_card.retry",
|
||||
0,
|
||||
UI_INITIAL_NO_CARD.retryLabel,
|
||||
UI_INITIAL_NO_CARD_LABEL_MAX
|
||||
));
|
||||
errorChain(assetLocaleGetString(
|
||||
&LOCALE.entry->data.locale,
|
||||
"ui.initial.no_card.continue",
|
||||
0,
|
||||
UI_INITIAL_NO_CARD.continueLabel,
|
||||
UI_INITIAL_NO_CARD_LABEL_MAX
|
||||
));
|
||||
|
||||
MENU_BEGIN(
|
||||
&UI_INITIAL_NO_CARD.menu, UI_INITIAL_NO_CARD.items,
|
||||
uiInitialNoCardSelected, uiInitialNoCardClosed, NULL
|
||||
);
|
||||
MENU_BUTTON("Retry");
|
||||
MENU_BUTTON("Continue Anyway");
|
||||
MENU_BUTTON(UI_INITIAL_NO_CARD.retryLabel);
|
||||
MENU_BUTTON(UI_INITIAL_NO_CARD.continueLabel);
|
||||
|
||||
MENU_END(UI_INITIAL_NO_CARD.items, menuIndex);
|
||||
|
||||
@@ -74,7 +96,7 @@ errorret_t uiInitialNoCardDraw(void) {
|
||||
errorChain(spriteBatchFlush());
|
||||
|
||||
int32_t textW, textH;
|
||||
textMeasure(UI_INITIAL_NO_CARD_TEXT, &FONT_DEFAULT, &textW, &textH);
|
||||
textMeasure(UI_INITIAL_NO_CARD.text, &FONT_DEFAULT, &textW, &textH);
|
||||
|
||||
float_t rowHeight = (float_t)FONT_DEFAULT.tileset->tileHeight;
|
||||
float_t width = mathMax(
|
||||
@@ -92,7 +114,7 @@ errorret_t uiInitialNoCardDraw(void) {
|
||||
float_t contentWidth = width - (UI_FRAME_START_X * 2);
|
||||
|
||||
errorChain(textDraw(
|
||||
contentX, contentY, UI_INITIAL_NO_CARD_TEXT, COLOR_WHITE, &FONT_DEFAULT
|
||||
contentX, contentY, UI_INITIAL_NO_CARD.text, COLOR_WHITE, &FONT_DEFAULT
|
||||
));
|
||||
|
||||
float_t buttonsY = contentY + (float_t)textH + UI_FRAME_PADDING_Y;
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
#define UI_INITIAL_NO_CARD_INDEX_CONTINUE 1
|
||||
#define UI_INITIAL_NO_CARD_ITEM_COUNT 2
|
||||
#define UI_INITIAL_NO_CARD_MIN_WIDTH 220.0f
|
||||
#define UI_INITIAL_NO_CARD_TEXT_MAX 256
|
||||
#define UI_INITIAL_NO_CARD_LABEL_MAX 32
|
||||
|
||||
/**
|
||||
* Callback invoked once the no-save-device modal is dismissed.
|
||||
@@ -28,6 +30,9 @@ typedef struct {
|
||||
uiinitialnocardcallback_t callback;
|
||||
void *user;
|
||||
bool_t retry;
|
||||
char_t text[UI_INITIAL_NO_CARD_TEXT_MAX];
|
||||
char_t retryLabel[UI_INITIAL_NO_CARD_LABEL_MAX];
|
||||
char_t continueLabel[UI_INITIAL_NO_CARD_LABEL_MAX];
|
||||
} uiinitialnocard_t;
|
||||
|
||||
extern uiinitialnocard_t UI_INITIAL_NO_CARD;
|
||||
|
||||
@@ -8,19 +8,19 @@
|
||||
#pragma once
|
||||
#include "error/error.h"
|
||||
#include "ui/widget/uimenu.h"
|
||||
#include "locale/localemanager.h"
|
||||
|
||||
#define UI_SETTINGS_GENERAL_ITEM_COUNT 3
|
||||
#define UI_SETTINGS_GENERAL_INDEX_LANGUAGE 0
|
||||
#define UI_SETTINGS_GENERAL_INDEX_LANGUAGE_DETAIL 1
|
||||
#define UI_SETTINGS_GENERAL_INDEX_APPLY 2
|
||||
#define UI_SETTINGS_GENERAL_LOCALE_COUNT 3
|
||||
#define UI_SETTINGS_GENERAL_LABEL_MAX 32
|
||||
#define UI_SETTINGS_GENERAL_INFO_MAX 64
|
||||
#define UI_SETTINGS_GENERAL_INFO_MAX 96
|
||||
|
||||
typedef struct {
|
||||
uimenu_t menu;
|
||||
uimenuitem_t items[UI_SETTINGS_GENERAL_ITEM_COUNT];
|
||||
const char_t *localeNames[UI_SETTINGS_GENERAL_LOCALE_COUNT];
|
||||
const char_t *localeNames[LOCALE_LIST_COUNT];
|
||||
char_t languageLabel[UI_SETTINGS_GENERAL_LABEL_MAX];
|
||||
char_t labelInfo[UI_SETTINGS_GENERAL_INFO_MAX];
|
||||
} uisettingsgeneral_t;
|
||||
|
||||
@@ -9,18 +9,15 @@
|
||||
#include "uisettings.h"
|
||||
#include "assert/assert.h"
|
||||
#include "util/memory.h"
|
||||
#include "util/string.h"
|
||||
#include "locale/localemanager.h"
|
||||
#include "locale/localeinfo.h"
|
||||
#include "asset/loader/locale/assetlocaleloader.h"
|
||||
#include "save/save.h"
|
||||
#include "error/error.h"
|
||||
|
||||
static const localeinfo_t *const UI_SETTINGS_GENERAL_LOCALES[
|
||||
UI_SETTINGS_GENERAL_LOCALE_COUNT
|
||||
] = {
|
||||
&LOCALE_EN_US,
|
||||
&LOCALE_JP_JP,
|
||||
&LOCALE_ES_MX
|
||||
};
|
||||
static void uiSettingsGeneralSaveComplete(errorret_t result, void *user) {
|
||||
if(errorIsNotOk(result)) errorCatch(errorPrint(result));
|
||||
}
|
||||
|
||||
void uiSettingsGeneralSelected(
|
||||
const uimenu_t *menu,
|
||||
@@ -41,8 +38,8 @@ errorret_t uiSettingsGeneralInit(uisettingsdata_t *data) {
|
||||
uisettingsgeneral_t *general = &data->general;
|
||||
memoryZero(general, sizeof(uisettingsgeneral_t));
|
||||
|
||||
for(uint8_t i = 0; i < UI_SETTINGS_GENERAL_LOCALE_COUNT; i++) {
|
||||
general->localeNames[i] = UI_SETTINGS_GENERAL_LOCALES[i]->name;
|
||||
for(uint8_t i = 0; i < LOCALE_LIST_COUNT; i++) {
|
||||
general->localeNames[i] = LOCALE_LIST[i]->name;
|
||||
}
|
||||
|
||||
errorChain(assetLocaleGetString(
|
||||
@@ -67,7 +64,7 @@ errorret_t uiSettingsGeneralInit(uisettingsdata_t *data) {
|
||||
|
||||
MENU_DROPDOWN(
|
||||
general->languageLabel, general->localeNames,
|
||||
UI_SETTINGS_GENERAL_LOCALE_COUNT, 0
|
||||
LOCALE_LIST_COUNT, 0
|
||||
);
|
||||
MENU_LABEL(general->labelInfo);
|
||||
|
||||
@@ -80,14 +77,7 @@ errorret_t uiSettingsGeneralInit(uisettingsdata_t *data) {
|
||||
void uiSettingsGeneralLoad(void) {
|
||||
uisettingsgeneral_t *general = &UI_SETTINGS.data.general;
|
||||
|
||||
uint8_t index = 0;
|
||||
for(uint8_t i = 0; i < UI_SETTINGS_GENERAL_LOCALE_COUNT; i++) {
|
||||
if(stringCompare(LOCALE.locale->file, UI_SETTINGS_GENERAL_LOCALES[i]->file) != 0) {
|
||||
continue;
|
||||
}
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
uint8_t index = localeManagerGetIndex(LOCALE.locale);
|
||||
|
||||
uiDropdownSetSelectedIndex(
|
||||
&general->items[UI_SETTINGS_GENERAL_INDEX_LANGUAGE].dropdown, index
|
||||
@@ -100,7 +90,10 @@ void uiSettingsGeneralApply(void) {
|
||||
uint8_t index = uiDropdownGetSelectedIndex(
|
||||
&general->items[UI_SETTINGS_GENERAL_INDEX_LANGUAGE].dropdown
|
||||
);
|
||||
errorCatch(localeManagerSetLocale(UI_SETTINGS_GENERAL_LOCALES[index]));
|
||||
errorCatch(localeManagerSetLocale(LOCALE_LIST[index]));
|
||||
|
||||
saveGetMeta()->language = index;
|
||||
saveWriteMeta(uiSettingsGeneralSaveComplete, NULL);
|
||||
|
||||
uiMenuClose(&general->menu);
|
||||
}
|
||||
@@ -111,7 +104,5 @@ bool_t uiSettingsGeneralHasChanges(void) {
|
||||
uint8_t index = uiDropdownGetSelectedIndex(
|
||||
&general->items[UI_SETTINGS_GENERAL_INDEX_LANGUAGE].dropdown
|
||||
);
|
||||
return stringCompare(
|
||||
LOCALE.locale->file, UI_SETTINGS_GENERAL_LOCALES[index]->file
|
||||
) != 0;
|
||||
return localeManagerGetIndex(LOCALE.locale) != index;
|
||||
}
|
||||
|
||||
@@ -31,6 +31,12 @@ void saveJsonWriterAddFloatLinux(
|
||||
yyjson_mut_obj_add_real(writer->doc, writer->root, key, (double)value);
|
||||
}
|
||||
|
||||
void saveJsonWriterAddUInt8Linux(
|
||||
savejsonwriterlinux_t *writer, const char_t *key, const uint8_t value
|
||||
) {
|
||||
yyjson_mut_obj_add_uint(writer->doc, writer->root, key, (uint64_t)value);
|
||||
}
|
||||
|
||||
void saveJsonWriterAddStringLinux(
|
||||
savejsonwriterlinux_t *writer, const char_t *key, const char_t *value
|
||||
) {
|
||||
@@ -122,6 +128,14 @@ float_t saveJsonReadFloatLinux(
|
||||
return (float_t)yyjson_get_num(val);
|
||||
}
|
||||
|
||||
uint8_t saveJsonReadUInt8Linux(
|
||||
yyjson_val *root, const char_t *key, const uint8_t defaultValue
|
||||
) {
|
||||
yyjson_val *val = yyjson_obj_get(root, key);
|
||||
if(!val || !yyjson_is_num(val)) return defaultValue;
|
||||
return (uint8_t)yyjson_get_uint(val);
|
||||
}
|
||||
|
||||
void saveJsonReadStringLinux(
|
||||
yyjson_val *root, const char_t *key, char_t *out, const size_t maxLen,
|
||||
const char_t *defaultValue
|
||||
|
||||
@@ -42,6 +42,13 @@ void saveJsonWriterAddFloatLinux(
|
||||
savejsonwriterlinux_t *writer, const char_t *key, const float_t value
|
||||
);
|
||||
|
||||
/**
|
||||
* Adds a "key": <uint> field to the root object.
|
||||
*/
|
||||
void saveJsonWriterAddUInt8Linux(
|
||||
savejsonwriterlinux_t *writer, const char_t *key, const uint8_t value
|
||||
);
|
||||
|
||||
/**
|
||||
* Adds a string field to the root object. The value is copied into the
|
||||
* document, so the caller's buffer doesn't need to outlive the call.
|
||||
@@ -120,6 +127,14 @@ float_t saveJsonReadFloatLinux(
|
||||
yyjson_val *root, const char_t *key, const float_t defaultValue
|
||||
);
|
||||
|
||||
/**
|
||||
* Reads a uint8 field, falling back to defaultValue if the key is
|
||||
* missing or not a number.
|
||||
*/
|
||||
uint8_t saveJsonReadUInt8Linux(
|
||||
yyjson_val *root, const char_t *key, const uint8_t defaultValue
|
||||
);
|
||||
|
||||
/**
|
||||
* Reads a string field into out, falling back to defaultValue if the key
|
||||
* is missing or not a string. Always null-terminates.
|
||||
|
||||
@@ -120,6 +120,9 @@ errorret_t saveMetaLoadLinux(savemeta_t *out) {
|
||||
out->deadzone = saveJsonReadFloatLinux(
|
||||
root, "deadzone", SAVE_META_DEADZONE_DEFAULT
|
||||
);
|
||||
out->language = saveJsonReadUInt8Linux(
|
||||
root, "language", SAVE_META_LANGUAGE_DEFAULT
|
||||
);
|
||||
out->exists = true;
|
||||
|
||||
yyjson_doc_free(doc);
|
||||
@@ -136,6 +139,7 @@ errorret_t saveMetaWriteLinux(savemeta_t *meta) {
|
||||
errorChain(saveJsonWriterInitLinux(&writer));
|
||||
saveJsonWriterAddUInt32Linux(&writer, "version", meta->version);
|
||||
saveJsonWriterAddFloatLinux(&writer, "deadzone", meta->deadzone);
|
||||
saveJsonWriterAddUInt8Linux(&writer, "language", meta->language);
|
||||
|
||||
errorret_t ret = saveJsonWriterSaveLinux(&writer, path);
|
||||
saveJsonWriterDisposeLinux(&writer);
|
||||
|
||||
Reference in New Issue
Block a user