mirror of
https://github.com/libretro/RetroArch.git
synced 2026-11-07 08:36:02 -10:00
Open supported external media in the fullscreen menu viewer and keep image confirmation there across all four menu drivers. Disable the legacy imageviewer core in normal build configurations while preserving its sources and Griffin guard.
821 lines
28 KiB
C
821 lines
28 KiB
C
/* RetroArch - A frontend for libretro.
|
|
* Copyright (C) 2011-2017 - Daniel De Matteis
|
|
* Copyright (C) 2016-2019 - Brad Parker
|
|
*
|
|
* RetroArch 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 Found-
|
|
* ation, either version 3 of the License, or (at your option) any later version.
|
|
*
|
|
* RetroArch 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 RetroArch.
|
|
* If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <compat/strl.h>
|
|
#include <string/stdstring.h> /* for string_replace_substring */
|
|
#include <libretro.h>
|
|
#include <retro_miscellaneous.h> /* ARRAY_SIZE */
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include "msg_hash.h"
|
|
|
|
#if defined(_MSC_VER) && !defined(_XBOX) && (_MSC_VER >= 1500 && _MSC_VER < 1900)
|
|
#if (_MSC_VER >= 1700)
|
|
/* https://support.microsoft.com/en-us/kb/980263 */
|
|
#pragma execution_character_set("utf-8")
|
|
#endif
|
|
#pragma warning(disable:4566)
|
|
#endif
|
|
|
|
/* TODO/FIXME - static public global variable */
|
|
static unsigned uint_user_language;
|
|
|
|
int msg_hash_get_help_enum(enum msg_hash_enums msg, char *s, size_t len)
|
|
{
|
|
int ret = msg_hash_get_help_us_enum(msg, s, len);
|
|
/* Replace line-breaks with "empty line-breaks" for readability */
|
|
const char *temp = string_replace_substring(s, strlen(s),
|
|
"\n", (sizeof("\n")-1),
|
|
"\n \n", (sizeof("\n \n")-1));
|
|
|
|
if (temp)
|
|
{
|
|
strlcpy(s, temp, len);
|
|
free((void*)temp);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
#if defined(MSG_HASH_HAVE_STRTAB)
|
|
void msg_hash_strtab_index_build(msg_hash_strtab_index_t *idx,
|
|
const msg_hash_strtab_t *tab)
|
|
{
|
|
uint32_t i;
|
|
uint32_t *id_slots;
|
|
|
|
if (idx->tab == tab && idx->id_slots)
|
|
return;
|
|
|
|
if (!tab || tab->count == 0)
|
|
return;
|
|
|
|
id_slots = (uint32_t*)calloc(MSG_LAST, sizeof(uint32_t));
|
|
if (!id_slots)
|
|
return;
|
|
|
|
{
|
|
/* Rows are contiguous NUL-terminated strings; recover each
|
|
* row's offset with one walk. */
|
|
const char *p = tab->blob;
|
|
for (i = 0; i < tab->count; i++)
|
|
{
|
|
if (tab->ids[i] < (uint32_t)MSG_LAST)
|
|
id_slots[tab->ids[i]] = (uint32_t)(p - tab->blob) + 1;
|
|
while (*p)
|
|
p++;
|
|
p++;
|
|
}
|
|
}
|
|
|
|
/* Publish the fully built index before marking it valid, so a
|
|
* concurrent reader either sees the complete index or takes the
|
|
* fallback path below. */
|
|
if (idx->id_slots)
|
|
free(idx->id_slots);
|
|
idx->id_slots = id_slots;
|
|
idx->tab = tab;
|
|
}
|
|
|
|
const char *msg_hash_strtab_lookup(const msg_hash_strtab_t *tab,
|
|
msg_hash_strtab_index_t *idx, uint32_t id)
|
|
{
|
|
if (!tab || tab->count == 0)
|
|
return NULL;
|
|
|
|
if (idx->tab != tab || !idx->id_slots)
|
|
msg_hash_strtab_index_build(idx, tab);
|
|
|
|
if (idx->tab == tab && idx->id_slots)
|
|
{
|
|
if (id < (uint32_t)MSG_LAST)
|
|
{
|
|
uint32_t slot = idx->id_slots[id];
|
|
if (slot)
|
|
return tab->blob + (slot - 1);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/* Allocation-failure path only. */
|
|
{
|
|
uint32_t i;
|
|
const char *p = tab->blob;
|
|
for (i = 0; i < tab->count; i++)
|
|
{
|
|
if (tab->ids[i] == id)
|
|
return p;
|
|
while (*p)
|
|
p++;
|
|
p++;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
#endif /* MSG_HASH_HAVE_STRTAB */
|
|
|
|
const char *get_user_language_iso639_1(bool limit)
|
|
{
|
|
switch (uint_user_language)
|
|
{
|
|
case RETRO_LANGUAGE_FRENCH:
|
|
return "fr";
|
|
case RETRO_LANGUAGE_GERMAN:
|
|
return "de";
|
|
case RETRO_LANGUAGE_SPANISH:
|
|
return "es";
|
|
case RETRO_LANGUAGE_ITALIAN:
|
|
return "it";
|
|
case RETRO_LANGUAGE_PORTUGUESE_BRAZIL:
|
|
if (limit)
|
|
return "pt";
|
|
return "pt_br";
|
|
case RETRO_LANGUAGE_PORTUGUESE_PORTUGAL:
|
|
if (limit)
|
|
return "pt";
|
|
return "pt_pt";
|
|
case RETRO_LANGUAGE_DUTCH:
|
|
return "nl";
|
|
case RETRO_LANGUAGE_ESPERANTO:
|
|
return "eo";
|
|
case RETRO_LANGUAGE_POLISH:
|
|
return "pl";
|
|
case RETRO_LANGUAGE_JAPANESE:
|
|
return "ja";
|
|
case RETRO_LANGUAGE_KOREAN:
|
|
return "ko";
|
|
case RETRO_LANGUAGE_VIETNAMESE:
|
|
return "vi";
|
|
case RETRO_LANGUAGE_CHINESE_SIMPLIFIED:
|
|
if (limit)
|
|
return "zh";
|
|
return "zh_cn";
|
|
case RETRO_LANGUAGE_CHINESE_TRADITIONAL:
|
|
if (limit)
|
|
return "zh";
|
|
return "zh_tw";
|
|
case RETRO_LANGUAGE_ARABIC:
|
|
return "ar";
|
|
case RETRO_LANGUAGE_GREEK:
|
|
return "el";
|
|
case RETRO_LANGUAGE_TURKISH:
|
|
return "tr";
|
|
case RETRO_LANGUAGE_SLOVAK:
|
|
return "sk";
|
|
case RETRO_LANGUAGE_RUSSIAN:
|
|
return "ru";
|
|
case RETRO_LANGUAGE_PERSIAN:
|
|
return "fa";
|
|
case RETRO_LANGUAGE_HEBREW:
|
|
return "he";
|
|
case RETRO_LANGUAGE_ASTURIAN:
|
|
return "ast";
|
|
case RETRO_LANGUAGE_FINNISH:
|
|
return "fi";
|
|
case RETRO_LANGUAGE_INDONESIAN:
|
|
return "id";
|
|
case RETRO_LANGUAGE_SWEDISH:
|
|
return "sv";
|
|
case RETRO_LANGUAGE_UKRAINIAN:
|
|
return "uk";
|
|
case RETRO_LANGUAGE_CZECH:
|
|
return "cs";
|
|
case RETRO_LANGUAGE_CATALAN_VALENCIA:
|
|
if (limit)
|
|
return "ca";
|
|
return "ca_ES@valencia";
|
|
case RETRO_LANGUAGE_CATALAN:
|
|
return "ca";
|
|
case RETRO_LANGUAGE_BRITISH_ENGLISH:
|
|
if (limit)
|
|
return "en";
|
|
return "en_gb";
|
|
case RETRO_LANGUAGE_HUNGARIAN:
|
|
return "hu";
|
|
case RETRO_LANGUAGE_BELARUSIAN:
|
|
return "be";
|
|
case RETRO_LANGUAGE_GALICIAN:
|
|
return "gl";
|
|
case RETRO_LANGUAGE_NORWEGIAN:
|
|
return "no";
|
|
case RETRO_LANGUAGE_IRISH:
|
|
return "ga";
|
|
case RETRO_LANGUAGE_THAI:
|
|
return "th";
|
|
}
|
|
return "en";
|
|
}
|
|
|
|
#ifdef HAVE_LANGEXTRA
|
|
/* Per-language packed string tables, generated by intl/json2h.py.
|
|
* Each header defines msg_hash_<lang>_blob / _ids; see
|
|
* msg_hash.h for the encoding notes. */
|
|
#include "intl/msg_hash_he.h"
|
|
#include "intl/msg_hash_sk.h"
|
|
#include "intl/msg_hash_uk.h"
|
|
#include "intl/msg_hash_eo.h"
|
|
#include "intl/msg_hash_pl.h"
|
|
#include "intl/msg_hash_fi.h"
|
|
#include "intl/msg_hash_hu.h"
|
|
#include "intl/msg_hash_be.h"
|
|
#include "intl/msg_hash_en.h"
|
|
#include "intl/msg_hash_it.h"
|
|
#include "intl/msg_hash_fa.h"
|
|
#include "intl/msg_hash_ast.h"
|
|
#include "intl/msg_hash_nl.h"
|
|
#include "intl/msg_hash_sv.h"
|
|
#include "intl/msg_hash_id.h"
|
|
#include "intl/msg_hash_cs.h"
|
|
#include "intl/msg_hash_ar.h"
|
|
#include "intl/msg_hash_fr.h"
|
|
#include "intl/msg_hash_cht.h"
|
|
#include "intl/msg_hash_de.h"
|
|
#include "intl/msg_hash_es.h"
|
|
#include "intl/msg_hash_ca.h"
|
|
#include "intl/msg_hash_el.h"
|
|
#include "intl/msg_hash_ja.h"
|
|
#include "intl/msg_hash_ko.h"
|
|
#include "intl/msg_hash_pt_pt.h"
|
|
#include "intl/msg_hash_ru.h"
|
|
#include "intl/msg_hash_tr.h"
|
|
#include "intl/msg_hash_val.h"
|
|
#include "intl/msg_hash_vn.h"
|
|
#include "intl/msg_hash_chs.h"
|
|
#include "intl/msg_hash_pt_br.h"
|
|
#include "intl/msg_hash_gl.h"
|
|
#include "intl/msg_hash_no.h"
|
|
#include "intl/msg_hash_ga.h"
|
|
#include "intl/msg_hash_th.h"
|
|
|
|
static const msg_hash_strtab_t msg_hash_strtab_he =
|
|
{ msg_hash_he_ids,
|
|
(uint32_t)(sizeof(msg_hash_he_ids) / sizeof(msg_hash_he_ids[0])),
|
|
(const char*)&msg_hash_he_blob };
|
|
static const msg_hash_strtab_t msg_hash_strtab_sk =
|
|
{ msg_hash_sk_ids,
|
|
(uint32_t)(sizeof(msg_hash_sk_ids) / sizeof(msg_hash_sk_ids[0])),
|
|
(const char*)&msg_hash_sk_blob };
|
|
static const msg_hash_strtab_t msg_hash_strtab_uk =
|
|
{ msg_hash_uk_ids,
|
|
(uint32_t)(sizeof(msg_hash_uk_ids) / sizeof(msg_hash_uk_ids[0])),
|
|
(const char*)&msg_hash_uk_blob };
|
|
static const msg_hash_strtab_t msg_hash_strtab_eo =
|
|
{ msg_hash_eo_ids,
|
|
(uint32_t)(sizeof(msg_hash_eo_ids) / sizeof(msg_hash_eo_ids[0])),
|
|
(const char*)&msg_hash_eo_blob };
|
|
static const msg_hash_strtab_t msg_hash_strtab_pl =
|
|
{ msg_hash_pl_ids,
|
|
(uint32_t)(sizeof(msg_hash_pl_ids) / sizeof(msg_hash_pl_ids[0])),
|
|
(const char*)&msg_hash_pl_blob };
|
|
static const msg_hash_strtab_t msg_hash_strtab_fi =
|
|
{ msg_hash_fi_ids,
|
|
(uint32_t)(sizeof(msg_hash_fi_ids) / sizeof(msg_hash_fi_ids[0])),
|
|
(const char*)&msg_hash_fi_blob };
|
|
static const msg_hash_strtab_t msg_hash_strtab_hu =
|
|
{ msg_hash_hu_ids,
|
|
(uint32_t)(sizeof(msg_hash_hu_ids) / sizeof(msg_hash_hu_ids[0])),
|
|
(const char*)&msg_hash_hu_blob };
|
|
static const msg_hash_strtab_t msg_hash_strtab_be =
|
|
{ msg_hash_be_ids,
|
|
(uint32_t)(sizeof(msg_hash_be_ids) / sizeof(msg_hash_be_ids[0])),
|
|
(const char*)&msg_hash_be_blob };
|
|
static const msg_hash_strtab_t msg_hash_strtab_en =
|
|
{ msg_hash_en_ids,
|
|
(uint32_t)(sizeof(msg_hash_en_ids) / sizeof(msg_hash_en_ids[0])),
|
|
(const char*)&msg_hash_en_blob };
|
|
static const msg_hash_strtab_t msg_hash_strtab_it =
|
|
{ msg_hash_it_ids,
|
|
(uint32_t)(sizeof(msg_hash_it_ids) / sizeof(msg_hash_it_ids[0])),
|
|
(const char*)&msg_hash_it_blob };
|
|
static const msg_hash_strtab_t msg_hash_strtab_fa =
|
|
{ msg_hash_fa_ids,
|
|
(uint32_t)(sizeof(msg_hash_fa_ids) / sizeof(msg_hash_fa_ids[0])),
|
|
(const char*)&msg_hash_fa_blob };
|
|
static const msg_hash_strtab_t msg_hash_strtab_ast =
|
|
{ msg_hash_ast_ids,
|
|
(uint32_t)(sizeof(msg_hash_ast_ids) / sizeof(msg_hash_ast_ids[0])),
|
|
(const char*)&msg_hash_ast_blob };
|
|
static const msg_hash_strtab_t msg_hash_strtab_nl =
|
|
{ msg_hash_nl_ids,
|
|
(uint32_t)(sizeof(msg_hash_nl_ids) / sizeof(msg_hash_nl_ids[0])),
|
|
(const char*)&msg_hash_nl_blob };
|
|
static const msg_hash_strtab_t msg_hash_strtab_sv =
|
|
{ msg_hash_sv_ids,
|
|
(uint32_t)(sizeof(msg_hash_sv_ids) / sizeof(msg_hash_sv_ids[0])),
|
|
(const char*)&msg_hash_sv_blob };
|
|
static const msg_hash_strtab_t msg_hash_strtab_id =
|
|
{ msg_hash_id_ids,
|
|
(uint32_t)(sizeof(msg_hash_id_ids) / sizeof(msg_hash_id_ids[0])),
|
|
(const char*)&msg_hash_id_blob };
|
|
static const msg_hash_strtab_t msg_hash_strtab_cs =
|
|
{ msg_hash_cs_ids,
|
|
(uint32_t)(sizeof(msg_hash_cs_ids) / sizeof(msg_hash_cs_ids[0])),
|
|
(const char*)&msg_hash_cs_blob };
|
|
static const msg_hash_strtab_t msg_hash_strtab_ar =
|
|
{ msg_hash_ar_ids,
|
|
(uint32_t)(sizeof(msg_hash_ar_ids) / sizeof(msg_hash_ar_ids[0])),
|
|
(const char*)&msg_hash_ar_blob };
|
|
static const msg_hash_strtab_t msg_hash_strtab_fr =
|
|
{ msg_hash_fr_ids,
|
|
(uint32_t)(sizeof(msg_hash_fr_ids) / sizeof(msg_hash_fr_ids[0])),
|
|
(const char*)&msg_hash_fr_blob };
|
|
static const msg_hash_strtab_t msg_hash_strtab_cht =
|
|
{ msg_hash_cht_ids,
|
|
(uint32_t)(sizeof(msg_hash_cht_ids) / sizeof(msg_hash_cht_ids[0])),
|
|
(const char*)&msg_hash_cht_blob };
|
|
static const msg_hash_strtab_t msg_hash_strtab_de =
|
|
{ msg_hash_de_ids,
|
|
(uint32_t)(sizeof(msg_hash_de_ids) / sizeof(msg_hash_de_ids[0])),
|
|
(const char*)&msg_hash_de_blob };
|
|
static const msg_hash_strtab_t msg_hash_strtab_es =
|
|
{ msg_hash_es_ids,
|
|
(uint32_t)(sizeof(msg_hash_es_ids) / sizeof(msg_hash_es_ids[0])),
|
|
(const char*)&msg_hash_es_blob };
|
|
static const msg_hash_strtab_t msg_hash_strtab_ca =
|
|
{ msg_hash_ca_ids,
|
|
(uint32_t)(sizeof(msg_hash_ca_ids) / sizeof(msg_hash_ca_ids[0])),
|
|
(const char*)&msg_hash_ca_blob };
|
|
static const msg_hash_strtab_t msg_hash_strtab_el =
|
|
{ msg_hash_el_ids,
|
|
(uint32_t)(sizeof(msg_hash_el_ids) / sizeof(msg_hash_el_ids[0])),
|
|
(const char*)&msg_hash_el_blob };
|
|
static const msg_hash_strtab_t msg_hash_strtab_jp =
|
|
{ msg_hash_ja_ids,
|
|
(uint32_t)(sizeof(msg_hash_ja_ids) / sizeof(msg_hash_ja_ids[0])),
|
|
(const char*)&msg_hash_ja_blob };
|
|
static const msg_hash_strtab_t msg_hash_strtab_ko =
|
|
{ msg_hash_ko_ids,
|
|
(uint32_t)(sizeof(msg_hash_ko_ids) / sizeof(msg_hash_ko_ids[0])),
|
|
(const char*)&msg_hash_ko_blob };
|
|
static const msg_hash_strtab_t msg_hash_strtab_pt_pt =
|
|
{ msg_hash_pt_pt_ids,
|
|
(uint32_t)(sizeof(msg_hash_pt_pt_ids) / sizeof(msg_hash_pt_pt_ids[0])),
|
|
(const char*)&msg_hash_pt_pt_blob };
|
|
static const msg_hash_strtab_t msg_hash_strtab_ru =
|
|
{ msg_hash_ru_ids,
|
|
(uint32_t)(sizeof(msg_hash_ru_ids) / sizeof(msg_hash_ru_ids[0])),
|
|
(const char*)&msg_hash_ru_blob };
|
|
static const msg_hash_strtab_t msg_hash_strtab_tr =
|
|
{ msg_hash_tr_ids,
|
|
(uint32_t)(sizeof(msg_hash_tr_ids) / sizeof(msg_hash_tr_ids[0])),
|
|
(const char*)&msg_hash_tr_blob };
|
|
static const msg_hash_strtab_t msg_hash_strtab_val =
|
|
{ msg_hash_val_ids,
|
|
(uint32_t)(sizeof(msg_hash_val_ids) / sizeof(msg_hash_val_ids[0])),
|
|
(const char*)&msg_hash_val_blob };
|
|
static const msg_hash_strtab_t msg_hash_strtab_vn =
|
|
{ msg_hash_vn_ids,
|
|
(uint32_t)(sizeof(msg_hash_vn_ids) / sizeof(msg_hash_vn_ids[0])),
|
|
(const char*)&msg_hash_vn_blob };
|
|
static const msg_hash_strtab_t msg_hash_strtab_chs =
|
|
{ msg_hash_chs_ids,
|
|
(uint32_t)(sizeof(msg_hash_chs_ids) / sizeof(msg_hash_chs_ids[0])),
|
|
(const char*)&msg_hash_chs_blob };
|
|
static const msg_hash_strtab_t msg_hash_strtab_pt_br =
|
|
{ msg_hash_pt_br_ids,
|
|
(uint32_t)(sizeof(msg_hash_pt_br_ids) / sizeof(msg_hash_pt_br_ids[0])),
|
|
(const char*)&msg_hash_pt_br_blob };
|
|
static const msg_hash_strtab_t msg_hash_strtab_gl =
|
|
{ msg_hash_gl_ids,
|
|
(uint32_t)(sizeof(msg_hash_gl_ids) / sizeof(msg_hash_gl_ids[0])),
|
|
(const char*)&msg_hash_gl_blob };
|
|
static const msg_hash_strtab_t msg_hash_strtab_no =
|
|
{ msg_hash_no_ids,
|
|
(uint32_t)(sizeof(msg_hash_no_ids) / sizeof(msg_hash_no_ids[0])),
|
|
(const char*)&msg_hash_no_blob };
|
|
static const msg_hash_strtab_t msg_hash_strtab_ga =
|
|
{ msg_hash_ga_ids,
|
|
(uint32_t)(sizeof(msg_hash_ga_ids) / sizeof(msg_hash_ga_ids[0])),
|
|
(const char*)&msg_hash_ga_blob };
|
|
static const msg_hash_strtab_t msg_hash_strtab_th =
|
|
{ msg_hash_th_ids,
|
|
(uint32_t)(sizeof(msg_hash_th_ids) / sizeof(msg_hash_th_ids[0])),
|
|
(const char*)&msg_hash_th_blob };
|
|
|
|
static msg_hash_strtab_index_t msg_hash_lang_index;
|
|
|
|
static const msg_hash_strtab_t *msg_hash_lang_strtab(unsigned lang)
|
|
{
|
|
switch (lang)
|
|
{
|
|
case RETRO_LANGUAGE_FRENCH:
|
|
return &msg_hash_strtab_fr;
|
|
case RETRO_LANGUAGE_GERMAN:
|
|
return &msg_hash_strtab_de;
|
|
case RETRO_LANGUAGE_SPANISH:
|
|
return &msg_hash_strtab_es;
|
|
case RETRO_LANGUAGE_ITALIAN:
|
|
return &msg_hash_strtab_it;
|
|
case RETRO_LANGUAGE_PORTUGUESE_BRAZIL:
|
|
return &msg_hash_strtab_pt_br;
|
|
case RETRO_LANGUAGE_PORTUGUESE_PORTUGAL:
|
|
return &msg_hash_strtab_pt_pt;
|
|
case RETRO_LANGUAGE_DUTCH:
|
|
return &msg_hash_strtab_nl;
|
|
case RETRO_LANGUAGE_ESPERANTO:
|
|
return &msg_hash_strtab_eo;
|
|
case RETRO_LANGUAGE_POLISH:
|
|
return &msg_hash_strtab_pl;
|
|
case RETRO_LANGUAGE_RUSSIAN:
|
|
return &msg_hash_strtab_ru;
|
|
case RETRO_LANGUAGE_JAPANESE:
|
|
return &msg_hash_strtab_jp;
|
|
case RETRO_LANGUAGE_KOREAN:
|
|
return &msg_hash_strtab_ko;
|
|
case RETRO_LANGUAGE_VIETNAMESE:
|
|
return &msg_hash_strtab_vn;
|
|
case RETRO_LANGUAGE_CHINESE_SIMPLIFIED:
|
|
return &msg_hash_strtab_chs;
|
|
case RETRO_LANGUAGE_CHINESE_TRADITIONAL:
|
|
return &msg_hash_strtab_cht;
|
|
case RETRO_LANGUAGE_ARABIC:
|
|
return &msg_hash_strtab_ar;
|
|
case RETRO_LANGUAGE_GREEK:
|
|
return &msg_hash_strtab_el;
|
|
case RETRO_LANGUAGE_TURKISH:
|
|
return &msg_hash_strtab_tr;
|
|
case RETRO_LANGUAGE_SLOVAK:
|
|
return &msg_hash_strtab_sk;
|
|
case RETRO_LANGUAGE_PERSIAN:
|
|
return &msg_hash_strtab_fa;
|
|
case RETRO_LANGUAGE_HEBREW:
|
|
return &msg_hash_strtab_he;
|
|
case RETRO_LANGUAGE_ASTURIAN:
|
|
return &msg_hash_strtab_ast;
|
|
case RETRO_LANGUAGE_FINNISH:
|
|
return &msg_hash_strtab_fi;
|
|
case RETRO_LANGUAGE_INDONESIAN:
|
|
return &msg_hash_strtab_id;
|
|
case RETRO_LANGUAGE_SWEDISH:
|
|
return &msg_hash_strtab_sv;
|
|
case RETRO_LANGUAGE_UKRAINIAN:
|
|
return &msg_hash_strtab_uk;
|
|
case RETRO_LANGUAGE_CZECH:
|
|
return &msg_hash_strtab_cs;
|
|
case RETRO_LANGUAGE_CATALAN_VALENCIA:
|
|
return &msg_hash_strtab_val;
|
|
case RETRO_LANGUAGE_CATALAN:
|
|
return &msg_hash_strtab_ca;
|
|
case RETRO_LANGUAGE_BRITISH_ENGLISH:
|
|
return &msg_hash_strtab_en;
|
|
case RETRO_LANGUAGE_HUNGARIAN:
|
|
return &msg_hash_strtab_hu;
|
|
case RETRO_LANGUAGE_BELARUSIAN:
|
|
return &msg_hash_strtab_be;
|
|
case RETRO_LANGUAGE_GALICIAN:
|
|
return &msg_hash_strtab_gl;
|
|
case RETRO_LANGUAGE_NORWEGIAN:
|
|
return &msg_hash_strtab_no;
|
|
case RETRO_LANGUAGE_IRISH:
|
|
return &msg_hash_strtab_ga;
|
|
case RETRO_LANGUAGE_THAI:
|
|
return &msg_hash_strtab_th;
|
|
default:
|
|
break;
|
|
}
|
|
return NULL;
|
|
}
|
|
#endif
|
|
|
|
const char *msg_hash_to_str(enum msg_hash_enums msg)
|
|
{
|
|
const char *ret = NULL;
|
|
|
|
#ifdef HAVE_LANGEXTRA
|
|
{
|
|
const msg_hash_strtab_t *tab =
|
|
msg_hash_lang_strtab(uint_user_language);
|
|
if (tab)
|
|
ret = msg_hash_strtab_lookup(tab,
|
|
&msg_hash_lang_index, (uint32_t)msg);
|
|
}
|
|
#endif
|
|
/* No strtab row can hold the untranslated marker: json2h.py drops
|
|
* "null" rows at pack time, since a missing row and a "null" row both
|
|
* mean "fall back to the base table" and are indistinguishable at
|
|
* lookup. Verified exhaustively over all 36 language tables x every
|
|
* enum id. So a non-NULL ret is always a real translation, and the
|
|
* strcmp that used to guard this return was dead weight paid on
|
|
* roughly 40 percent of calls for every non-English user. */
|
|
if (ret)
|
|
return ret;
|
|
return msg_hash_to_str_us(msg);
|
|
}
|
|
|
|
/* String-table lookup for menu entry values and file extensions.
|
|
*
|
|
* Historically this dispatched through DJB2 hashes of the value
|
|
* string against ~80 precomputed magic hex constants — readable
|
|
* only via reverse-engineering, prone to silent collisions if a
|
|
* new extension hashed to the same value as an existing one, and
|
|
* carried 79 TOC entries on PPC64 (which contributed to the PS3
|
|
* griffin build crossing the 64K TOC limit).
|
|
*
|
|
* The hash dispatch saved ~2 us/frame in menu-mode file browsing
|
|
* vs a linear strcmp scan — not enough to justify the obscurity.
|
|
*
|
|
* The table below stays that plain declarative list; the speed is
|
|
* recovered instead by a first-byte bucket index built once from the
|
|
* table itself, so there is still exactly one source of truth, no
|
|
* magic constants to collide, and no added TOC entries.
|
|
*
|
|
* Callers passing case-insensitively must lowercase their input
|
|
* before calling this; only the lowercase entries below are
|
|
* checked for those cases (jpg, png, etc. include uppercase
|
|
* variants because the menu callers may receive either). */
|
|
enum msg_file_type msg_hash_to_file_type(const char *value)
|
|
{
|
|
size_t i;
|
|
unsigned short slot;
|
|
struct ft_entry {
|
|
const char *value;
|
|
enum msg_file_type type;
|
|
};
|
|
/* Order roughly by expected hit frequency to minimize average
|
|
* compare count for the common cases (game ROMs hitting CHD,
|
|
* ISO, CUE, ZIP first; image viewers hitting PNG/JPG). */
|
|
static const struct ft_entry table[] = {
|
|
/* disc / archive containers — top of file-browser traffic */
|
|
{ "chd", FILE_TYPE_CHD },
|
|
{ "iso", FILE_TYPE_ISO },
|
|
{ "ISO", FILE_TYPE_ISO },
|
|
{ "cue", FILE_TYPE_CUE },
|
|
{ "CUE", FILE_TYPE_CUE },
|
|
{ "gdi", FILE_TYPE_GDI },
|
|
{ "GDI", FILE_TYPE_GDI },
|
|
{ "zip", FILE_TYPE_COMPRESSED },
|
|
{ "ZIP", FILE_TYPE_COMPRESSED },
|
|
{ "7z", FILE_TYPE_COMPRESSED },
|
|
{ "7Z", FILE_TYPE_COMPRESSED },
|
|
{ "apk", FILE_TYPE_COMPRESSED },
|
|
{ "(COMP)", FILE_TYPE_COMPRESSED },
|
|
{ "lutro", FILE_TYPE_LUTRO },
|
|
/* menu placeholder values from displaylist enumeration */
|
|
{ "(CFILE)", FILE_TYPE_IN_CARCHIVE },
|
|
{ "...", FILE_TYPE_MORE },
|
|
{ "(CORE)", FILE_TYPE_CORE },
|
|
{ "(RDB)", FILE_TYPE_RDB },
|
|
{ "(FILE)", FILE_TYPE_PLAIN },
|
|
{ "(DIR)", FILE_TYPE_DIRECTORY },
|
|
{ "(MUSIC)", FILE_TYPE_MUSIC },
|
|
{ "(IMAGE)", FILE_TYPE_IMAGE },
|
|
{ "(MOVIE)", FILE_TYPE_MOVIE },
|
|
{ "ON", FILE_TYPE_BOOL_ON },
|
|
{ "OFF", FILE_TYPE_BOOL_OFF },
|
|
/* shader source extensions */
|
|
{ "glsl", FILE_TYPE_SHADER_GLSL },
|
|
{ "hlsl", FILE_TYPE_SHADER_HLSL },
|
|
{ "cg", FILE_TYPE_SHADER_CG },
|
|
{ "slang", FILE_TYPE_SHADER_SLANG },
|
|
{ "glslp", FILE_TYPE_SHADER_PRESET_GLSLP },
|
|
{ "hlslp", FILE_TYPE_SHADER_PRESET_HLSLP },
|
|
{ "cgp", FILE_TYPE_SHADER_PRESET_CGP },
|
|
{ "slangp", FILE_TYPE_SHADER_PRESET_SLANGP },
|
|
/* checksum / hash labels */
|
|
{ "crc", FILE_TYPE_CRC },
|
|
{ "sha1", FILE_TYPE_SHA1 },
|
|
{ "md5", FILE_TYPE_MD5 },
|
|
#if defined(HAVE_WEBMPLAYER) && !defined(HAVE_FFMPEG) && !defined(HAVE_MPV)
|
|
/* video containers the built-in WebM/MP4 player handles */
|
|
{ "mkv", FILE_TYPE_MKV },
|
|
{ "webm", FILE_TYPE_WEBM },
|
|
#ifdef HAVE_RMP4
|
|
{ "mp4", FILE_TYPE_MP4 },
|
|
{ "m4v", FILE_TYPE_MP4 },
|
|
{ "mov", FILE_TYPE_MOV },
|
|
#endif
|
|
#endif
|
|
#if defined(HAVE_FFMPEG) || defined(HAVE_MPV)
|
|
/* video containers */
|
|
{ "ogm", FILE_TYPE_OGM },
|
|
{ "mkv", FILE_TYPE_MKV },
|
|
{ "avi", FILE_TYPE_AVI },
|
|
{ "mp4", FILE_TYPE_MP4 },
|
|
{ "m4v", FILE_TYPE_MP4 },
|
|
{ "flv", FILE_TYPE_FLV },
|
|
{ "webm", FILE_TYPE_WEBM },
|
|
{ "3gp", FILE_TYPE_3GP },
|
|
{ "f4f", FILE_TYPE_F4F },
|
|
{ "f4v", FILE_TYPE_F4V },
|
|
{ "mov", FILE_TYPE_MOV },
|
|
{ "wmv", FILE_TYPE_WMV },
|
|
{ "m4a", FILE_TYPE_M4A },
|
|
{ "3g2", FILE_TYPE_3G2 },
|
|
{ "mpg", FILE_TYPE_MPG },
|
|
{ "mpeg", FILE_TYPE_MPEG },
|
|
{ "vob", FILE_TYPE_VOB },
|
|
{ "asf", FILE_TYPE_ASF },
|
|
{ "divx", FILE_TYPE_DIVX },
|
|
{ "m2p", FILE_TYPE_M2P },
|
|
{ "m2ts", FILE_TYPE_M2TS },
|
|
{ "ps", FILE_TYPE_PS },
|
|
{ "ts", FILE_TYPE_TS },
|
|
{ "mxf", FILE_TYPE_MXF },
|
|
{ "wma", FILE_TYPE_WMA },
|
|
#endif
|
|
#if defined(HAVE_FFMPEG) || defined(HAVE_MPV) || defined(HAVE_AUDIOMIXER)
|
|
#if !defined(HAVE_AUDIOMIXER) || defined(HAVE_RMP3)
|
|
{ "mp3", FILE_TYPE_MP3 },
|
|
#endif
|
|
#if !defined(HAVE_AUDIOMIXER) || defined(HAVE_RVORBIS)
|
|
{ "ogg", FILE_TYPE_OGG },
|
|
#endif
|
|
#if !defined(HAVE_AUDIOMIXER) || defined(HAVE_RFLAC)
|
|
{ "flac", FILE_TYPE_FLAC },
|
|
#endif
|
|
#if !defined(HAVE_AUDIOMIXER) || defined(HAVE_RWAV)
|
|
{ "wav", FILE_TYPE_WAV },
|
|
#endif
|
|
#if !defined(HAVE_AUDIOMIXER) || defined(HAVE_RMODTRACKER)
|
|
{ "mod", FILE_TYPE_MOD },
|
|
{ "s3m", FILE_TYPE_S3M },
|
|
{ "xm", FILE_TYPE_XM },
|
|
#endif
|
|
#if defined(HAVE_AUDIOMIXER) && defined(HAVE_RAAC)
|
|
#ifdef HAVE_RMP4
|
|
{ "m4a", FILE_TYPE_M4A },
|
|
#endif
|
|
{ "aac", FILE_TYPE_AAC },
|
|
{ "ac3", FILE_TYPE_AC3 },
|
|
{ "eac3", FILE_TYPE_AC3 },
|
|
{ "ec3", FILE_TYPE_AC3 },
|
|
{ "lpcm", FILE_TYPE_LPCM },
|
|
{ "pcm", FILE_TYPE_LPCM },
|
|
#endif
|
|
#if defined(HAVE_AUDIOMIXER) && defined(HAVE_ROPUS)
|
|
{ "opus", FILE_TYPE_OPUS },
|
|
#endif
|
|
#if defined(HAVE_AUDIOMIXER) && defined(HAVE_RWEBM) \
|
|
&& (defined(HAVE_ROPUS) || defined(HAVE_RVORBIS))
|
|
{ "weba", FILE_TYPE_WEBA },
|
|
#endif
|
|
#endif
|
|
#if defined(HAVE_FFMPEG) || defined(HAVE_MPV)
|
|
/* Further media formats; with FFmpeg, only routed to the player
|
|
* if its build can open them. Extensions claimed above win. */
|
|
{ "ogv", FILE_TYPE_MOVIE_OTHER },
|
|
{ "mk3d", FILE_TYPE_MOVIE_OTHER },
|
|
{ "mts", FILE_TYPE_MOVIE_OTHER },
|
|
{ "m2t", FILE_TYPE_MOVIE_OTHER },
|
|
{ "m2v", FILE_TYPE_MOVIE_OTHER },
|
|
{ "rm", FILE_TYPE_MOVIE_OTHER },
|
|
{ "rmvb", FILE_TYPE_MOVIE_OTHER },
|
|
{ "nut", FILE_TYPE_MOVIE_OTHER },
|
|
{ "dv", FILE_TYPE_MOVIE_OTHER },
|
|
{ "ivf", FILE_TYPE_MOVIE_OTHER },
|
|
{ "oga", FILE_TYPE_MUSIC_OTHER },
|
|
{ "opus", FILE_TYPE_MUSIC_OTHER },
|
|
{ "spx", FILE_TYPE_MUSIC_OTHER },
|
|
{ "mka", FILE_TYPE_MUSIC_OTHER },
|
|
{ "weba", FILE_TYPE_MUSIC_OTHER },
|
|
{ "m4b", FILE_TYPE_MUSIC_OTHER },
|
|
{ "aac", FILE_TYPE_MUSIC_OTHER },
|
|
{ "ac3", FILE_TYPE_MUSIC_OTHER },
|
|
{ "eac3", FILE_TYPE_MUSIC_OTHER },
|
|
{ "dts", FILE_TYPE_MUSIC_OTHER },
|
|
{ "mp2", FILE_TYPE_MUSIC_OTHER },
|
|
{ "aif", FILE_TYPE_MUSIC_OTHER },
|
|
{ "aiff", FILE_TYPE_MUSIC_OTHER },
|
|
{ "wv", FILE_TYPE_MUSIC_OTHER },
|
|
{ "ape", FILE_TYPE_MUSIC_OTHER },
|
|
{ "tta", FILE_TYPE_MUSIC_OTHER },
|
|
{ "mpc", FILE_TYPE_MUSIC_OTHER },
|
|
{ "amr", FILE_TYPE_MUSIC_OTHER },
|
|
{ "au", FILE_TYPE_MUSIC_OTHER },
|
|
{ "caf", FILE_TYPE_MUSIC_OTHER },
|
|
#endif
|
|
#if defined(HAVE_IMAGEVIEWER) || defined(HAVE_MENU)
|
|
{ "jpg", FILE_TYPE_JPEG },
|
|
{ "JPG", FILE_TYPE_JPEG },
|
|
{ "jpeg", FILE_TYPE_JPEG },
|
|
{ "JPEG", FILE_TYPE_JPEG },
|
|
{ "png", FILE_TYPE_PNG },
|
|
{ "PNG", FILE_TYPE_PNG },
|
|
{ "tga", FILE_TYPE_TGA },
|
|
{ "bmp", FILE_TYPE_BMP },
|
|
{ "webp", FILE_TYPE_WEBP },
|
|
{ "WEBP", FILE_TYPE_WEBP },
|
|
#endif
|
|
#ifdef HAVE_EASTEREGG
|
|
{ "gong", FILE_TYPE_GONG },
|
|
{ "GONG", FILE_TYPE_GONG },
|
|
#endif
|
|
};
|
|
|
|
/* First-byte bucket index over table[].
|
|
*
|
|
* ft_head[c] is a 1-based index of the first row whose value starts
|
|
* with byte c; ft_next[] chains the remainder. 0 is the
|
|
* end-of-chain sentinel, so the zero-initialised (BSS) state of
|
|
* both arrays reads as "every bucket empty": a reader that observes
|
|
* a partially built index can only miss - never follow a bogus row,
|
|
* never cycle - and every surviving candidate is still compared in
|
|
* full below before it is returned. The build is idempotent, so
|
|
* concurrent builders store identical bytes. */
|
|
static unsigned short ft_head[256];
|
|
static unsigned short ft_next[ARRAY_SIZE(table)];
|
|
static unsigned char ft_ready;
|
|
|
|
if (!value || !*value)
|
|
return FILE_TYPE_NONE;
|
|
|
|
if (!ft_ready)
|
|
{
|
|
for (i = ARRAY_SIZE(table); i > 0; i--)
|
|
{
|
|
unsigned char c = (unsigned char)table[i - 1].value[0];
|
|
ft_next[i - 1] = ft_head[c];
|
|
ft_head[c] = (unsigned short)i;
|
|
}
|
|
/* Publish only once every bucket is populated. */
|
|
ft_ready = 1;
|
|
}
|
|
|
|
for (slot = ft_head[(unsigned char)*value]; slot; slot = ft_next[slot - 1])
|
|
{
|
|
/* Inline short string compare: most entries are 2-6 bytes so
|
|
* the strcmp/string_is_equal overhead would dominate. Bail
|
|
* on first mismatch. The first byte already matched via the
|
|
* bucket, so start at the second. */
|
|
const char *a = value + 1;
|
|
const char *b = table[slot - 1].value + 1;
|
|
while (*a && *a == *b) { a++; b++; }
|
|
if (!*a && !*b)
|
|
return table[slot - 1].type;
|
|
}
|
|
return FILE_TYPE_NONE;
|
|
}
|
|
|
|
unsigned *msg_hash_get_uint(enum msg_hash_action type)
|
|
{
|
|
switch (type)
|
|
{
|
|
case MSG_HASH_USER_LANGUAGE:
|
|
return &uint_user_language;
|
|
case MSG_HASH_NONE:
|
|
break;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void msg_hash_set_uint(enum msg_hash_action type, unsigned val)
|
|
{
|
|
switch (type)
|
|
{
|
|
case MSG_HASH_USER_LANGUAGE:
|
|
uint_user_language = val;
|
|
#if defined(MSG_HASH_HAVE_STRTAB)
|
|
msg_hash_us_index_init();
|
|
#ifdef HAVE_LANGEXTRA
|
|
msg_hash_strtab_index_build(&msg_hash_lang_index,
|
|
msg_hash_lang_strtab(val));
|
|
#endif
|
|
#endif
|
|
break;
|
|
case MSG_HASH_NONE:
|
|
break;
|
|
}
|
|
}
|
|
|
|
const char *msg_hash_get_wideglyph_str(void)
|
|
{
|
|
#ifdef HAVE_LANGEXTRA
|
|
switch (uint_user_language)
|
|
{
|
|
case RETRO_LANGUAGE_CHINESE_SIMPLIFIED:
|
|
return "菜";
|
|
case RETRO_LANGUAGE_CHINESE_TRADITIONAL:
|
|
return "主";
|
|
case RETRO_LANGUAGE_JAPANESE:
|
|
return "漢";
|
|
case RETRO_LANGUAGE_KOREAN:
|
|
return "메";
|
|
default:
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
return NULL;
|
|
}
|