Make language configurable in-game

This commit is contained in:
MysterD 2023-03-31 19:38:06 -07:00
parent a205bb8c2a
commit 7bcf44ba54
13 changed files with 190 additions and 14 deletions

View File

@ -217,6 +217,7 @@ PAUSE_IN_SINGLEPLAYER = "Pause In Singleplayer"
DISABLE_POPUPS = "Disable Popups"
MENU_OPTIONS = "Menu Options"
DEBUG = "Debug"
LANGUAGE = "Language"
[MODLIST]
MODS = "MODS"
@ -312,3 +313,6 @@ MASTER_VOLUME = "Master Volume"
MUSIC_VOLUME = "Music Volume"
SFX_VOLUME = "Sfx Volume"
ENV_VOLUME = "Env Volume"
[LANGUAGE]
LANGUAGE = "LANGUAGE"

View File

@ -217,6 +217,7 @@ PAUSE_IN_SINGLEPLAYER = "Pausar en un jugador"
DISABLE_POPUPS = "Desactivar ventanas emergentes"
MENU_OPTIONS = "Opciones de menú"
DEBUG = "Depurar"
LANGUAGE = "Idioma"
[MODLIST]
MODS = "MODS"
@ -312,3 +313,6 @@ MASTER_VOLUME = "Volumen principal"
MUSIC_VOLUME = "Volumen de la música"
SFX_VOLUME = "Volumen SFX"
ENV_VOLUME = "Volumen envolvente"
[LANGUAGE]
LANGUAGE = "IDIOMA"

View File

@ -154,6 +154,7 @@ bool configSingleplayerPause = 0;
bool configDebugPrint = 0;
bool configDebugInfo = 0;
bool configDebugError = 0;
char configLanguage[MAX_CONFIG_STRING] = "";
static const struct ConfigOption options[] = {
{.name = "fullscreen", .type = CONFIG_TYPE_BOOL, .boolValue = &configWindow.fullscreen},
@ -261,6 +262,7 @@ static const struct ConfigOption options[] = {
{.name = "debug_print", .type = CONFIG_TYPE_BOOL , .boolValue = &configDebugPrint},
{.name = "debug_info", .type = CONFIG_TYPE_BOOL , .boolValue = &configDebugInfo},
{.name = "debug_error", .type = CONFIG_TYPE_BOOL , .boolValue = &configDebugError},
{.name = "language", .type = CONFIG_TYPE_STRING, .stringValue = (char*)&configLanguage, .maxStringLength = MAX_CONFIG_STRING},
};
// FunctionConfigOption functions

View File

@ -109,6 +109,7 @@ extern bool configSingleplayerPause;
extern bool configDebugPrint;
extern bool configDebugInfo;
extern bool configDebugError;
extern char configLanguage[];
void configfile_load(const char *filename);
void configfile_save(const char *filename);

View File

@ -1,4 +1,5 @@
#include "djui.h"
#include "djui_panel_language.h"
#include "../debuglog.h"
#include "pc/cliopts.h"
#include "game/level_update.h"
@ -62,6 +63,10 @@ void djui_init(void) {
if (gCLIOpts.Network != NT_SERVER) {
djui_panel_main_create(NULL);
if (configLanguage[0] == '\0') {
gPanelLanguageOnStartup = true;
djui_panel_language_create(NULL);
}
//djui_panel_debug_create();
}
djui_cursor_create();

View File

@ -22,6 +22,7 @@ static void djui_checkbox_update_style(struct DjuiBase* base) {
djui_base_set_color(&checkbox->text->base, 200, 200, 200, 255);
djui_base_set_color(&checkbox->rectValue->base, 200, 200, 200, 255);
}
djui_base_set_visible(&checkbox->rectValue->base, *checkbox->value);
}
static void djui_checkbox_get_cursor_hover_location(struct DjuiBase* base, f32* x, f32* y) {

View File

@ -5,16 +5,28 @@
ini_t* sLang = NULL;
void djui_language_init(char* filename) {
bool djui_language_init(char* lang) {
// free old ini
if (sLang != NULL) {
ini_free(sLang);
sLang = NULL;
}
// construct path
char path[SYS_MAX_PATH] = "";
snprintf(path, SYS_MAX_PATH, "%s/lang/%s", sys_exe_path(), filename);
if (!lang || lang[0] == '\0') { lang = "english"; }
snprintf(path, SYS_MAX_PATH, "%s/lang/%s.ini", sys_exe_path(), lang);
// load
sLang = ini_load(path);
return sLang != NULL;
}
char* djui_language_get(const char *section, const char *key) {
if (!sLang) { return "???"; }
if (!sLang) { return (char*)key; }
char* value = (char*)ini_get(sLang, section, key);
if (!value) { return "???"; }
if (!value) { return (char*)key; }
return value;
}

View File

@ -1,7 +1,7 @@
#pragma once
#define DLANG(_SECTION, _KEY) djui_language_get(#_SECTION, #_KEY)
void djui_language_init(char* filename);
bool djui_language_init(char* lang);
char* djui_language_get(const char *section, const char *key);
void djui_language_replace(char* src, char* dst, int size, char key, char* value);
void djui_language_replace2(char* src, char* dst, int size, char key1, char* value1, char key2, char* value2);

View File

@ -144,12 +144,13 @@ void djui_panel_update(void) {
djui_cursor_input_controlled_center(sPanelList->defaultElementBase);
if (removingBase != NULL) {
if (sPanelRemoving->on_panel_destroy) {
sPanelRemoving->on_panel_destroy(NULL);
struct DjuiPanel* panel = sPanelRemoving;
sPanelRemoving = NULL;
if (panel->on_panel_destroy) {
panel->on_panel_destroy(NULL);
}
djui_base_destroy(removingBase);
free(sPanelRemoving);
sPanelRemoving = NULL;
free(panel);
}
}
@ -165,6 +166,9 @@ void djui_panel_update(void) {
}
void djui_panel_shutdown(void) {
static bool sShuttingDown = false;
if (sShuttingDown) { return; }
sShuttingDown = true;
struct DjuiPanel* panel = sPanelList;
while (panel != NULL) {
struct DjuiPanel* next = panel->parent;
@ -177,11 +181,13 @@ void djui_panel_shutdown(void) {
}
if (sPanelRemoving != NULL) {
if (sPanelRemoving->on_panel_destroy) {
sPanelRemoving->on_panel_destroy(NULL);
struct DjuiPanel* panel = sPanelRemoving;
sPanelRemoving = NULL;
if (panel->on_panel_destroy) {
panel->on_panel_destroy(NULL);
}
djui_base_destroy(sPanelRemoving->base);
free(sPanelRemoving);
djui_base_destroy(panel->base);
free(panel);
}
sPanelList = NULL;
@ -197,4 +203,5 @@ void djui_panel_shutdown(void) {
gDjuiInMainMenu = false;
newcam_init_settings();
}
sShuttingDown = false;
}

View File

@ -0,0 +1,130 @@
#include "djui.h"
#include "pc/debuglog.h"
#include "pc/utils/misc.h"
#include "pc/configfile.h"
extern bool directory_sanity_check(struct dirent* dir, char* dirPath, char* outPath);
static bool sTrue = true;
static bool sFalse = false;
static bool sLanguageChanged = false;
static struct DjuiBase* sLayoutBase = NULL;
bool gPanelLanguageOnStartup = false;
static void select_language(struct DjuiBase* caller) {
// god this is so hacky and terrible
struct DjuiCheckbox* checkbox = (struct DjuiCheckbox*) caller;
sTrue = true;
sFalse = false;
struct DjuiBaseChild* child = sLayoutBase->child;
while (child) {
struct DjuiCheckbox* tmp = (struct DjuiCheckbox*)child->base;
tmp->value = &sFalse;
tmp->base.interactable->update_style(&tmp->base);
child = child->next;
}
if (strcmp(configLanguage, checkbox->text->message)) {
snprintf(configLanguage, MAX_CONFIG_STRING, "%s", checkbox->text->message);
sLanguageChanged = true;
}
checkbox->value = &sTrue;
checkbox->base.interactable->update_style(caller);
}
static void djui_panel_language_destroy(UNUSED struct DjuiBase* caller) {
// god this is so hacky and terrible
if (sLanguageChanged) {
sLanguageChanged = false;
if (!djui_language_init(configLanguage)) {
snprintf(configLanguage, MAX_CONFIG_STRING, "%s", "");
}
if (gPanelLanguageOnStartup) {
djui_panel_shutdown();
gDjuiInMainMenu = true;
djui_panel_main_create(NULL);
} else if (gDjuiInMainMenu) {
djui_panel_shutdown();
gDjuiInMainMenu = true;
djui_panel_main_create(NULL);
djui_panel_options_create(NULL);
djui_panel_misc_create(NULL);
} else if (gDjuiPanelPauseCreated) {
djui_panel_shutdown();
djui_panel_pause_create(NULL);
djui_panel_options_create(NULL);
djui_panel_misc_create(NULL);
} else {
djui_panel_shutdown();
}
}
if (configLanguage[0] == '\0') {
snprintf(configLanguage, MAX_CONFIG_STRING, "%s", "english");
}
gPanelLanguageOnStartup = false;
}
void djui_panel_language_create(struct DjuiBase* caller) {
struct DjuiThreePanel* panel = djui_panel_menu_create(DLANG(LANGUAGE, LANGUAGE));
struct DjuiBase* body = djui_three_panel_get_body(panel);
sLanguageChanged = false;
{
struct DjuiPaginated* paginated = djui_paginated_create(body, 8);
sLayoutBase = &paginated->layout->base;
// construct lang path
char lpath[SYS_MAX_PATH] = "";
snprintf(lpath, SYS_MAX_PATH, "%s/lang", sys_exe_path());
// open directory
struct dirent* dir = NULL;
DIR* d = opendir(lpath);
if (!d) {
LOG_ERROR("Could not open directory '%s'", lpath);
return;
}
struct DjuiCheckbox* chkEnglish = NULL;
bool foundMatch = false;
// iterate
char path[SYS_MAX_PATH] = { 0 };
while ((dir = readdir(d)) != NULL) {
// sanity check / fill path[]
if (!directory_sanity_check(dir, lpath, path)) { continue; }
snprintf(path, SYS_MAX_PATH, "%s", dir->d_name);
// strip the name before the .
char* c = path;
while (*c != '\0') {
if (*c == '.') { *c = '\0'; break;}
c++;
}
if (strlen(path) == 0) { continue; }
bool match = !strcmp(path, configLanguage);
if (match) { foundMatch = true; }
struct DjuiCheckbox* checkbox = djui_checkbox_create(sLayoutBase, path, match ? &sTrue : &sFalse, select_language);
if (!strcmp(path, "english")) { chkEnglish = checkbox; }
}
closedir(d);
if (!foundMatch && chkEnglish) {
chkEnglish->value = &sTrue;
chkEnglish->base.interactable->update_style(&chkEnglish->base);
}
djui_paginated_calculate_height(paginated);
djui_button_create(body, DLANG(MENU, BACK), DJUI_BUTTON_STYLE_BACK, djui_panel_menu_back);
panel->bodySize.value = paginated->base.height.value + 16 + 64;
}
struct DjuiPanel* p = djui_panel_add(caller, panel, NULL);
p->on_panel_destroy = djui_panel_language_destroy;
}

View File

@ -0,0 +1,6 @@
#pragma once
#include "djui.h"
extern bool gPanelLanguageOnStartup;
void djui_panel_language_create(struct DjuiBase* caller);

View File

@ -1,4 +1,5 @@
#include "djui.h"
#include "djui_panel_language.h"
#include "src/pc/utils/misc.h"
#include "src/pc/configfile.h"
#include "game/hardcoded.h"
@ -29,6 +30,7 @@ void djui_panel_misc_create(struct DjuiBase* caller) {
{
djui_checkbox_create(body, DLANG(MISC, PAUSE_IN_SINGLEPLAYER), &configSingleplayerPause, NULL);
djui_checkbox_create(body, DLANG(MISC, DISABLE_POPUPS), &configDisablePopups, NULL);
djui_button_create(body, DLANG(MISC, LANGUAGE), DJUI_BUTTON_STYLE_NORMAL, djui_panel_language_create);
djui_button_create(body, DLANG(MISC, MENU_OPTIONS), DJUI_BUTTON_STYLE_NORMAL, djui_panel_main_menu_create);
#ifdef DEVELOPMENT
djui_button_create(body, DLANG(MISC, DEBUG), DJUI_BUTTON_STYLE_NORMAL, djui_panel_options_debug_create);

View File

@ -273,11 +273,13 @@ void main_func(void) {
const char *userpath = gCLIOpts.SavePath[0] ? gCLIOpts.SavePath : sys_user_path();
fs_init(sys_ropaths, gamedir, userpath);
djui_language_init("english.ini");
sync_objects_init_system();
djui_unicode_init();
mods_init();
configfile_load(configfile_name());
if (!djui_language_init(configLanguage)) {
snprintf(configLanguage, MAX_CONFIG_STRING, "%s", "");
}
dynos_pack_init();
// If coop_custom_palette_* values are not found in sm64config.txt, the custom palette config will use the default values (Mario's palette)