mirror of
https://github.com/libretro/RetroArch.git
synced 2026-11-07 08:36:02 -10:00
The rest of the wiring for AUDIO_TYPE_LPCM: an AUDIO_MIXER_TYPE_LPCM sound and audio_mixer_load_lpcm(), the nine voice dispatch sites beside the AC-3 ones (set_avail, tell, destroy, play in both formats, release, mix in both formats), the loader dispatch in audio_driver_mixer_add_stream, the task's two push paths and its NBIO type, FILE_TYPE_LPCM, the .lpcm and .pcm extensions in the browser's filter and in what retroarch.c treats as a playable sound. The one decision the mixer forced: a sound is a file someone dropped in a directory, and raw linear PCM says nothing about itself, so there is nobody to ask what shape it is. A buffer that opens with a DVD or Blu-ray header keeps what the header says - the arm reads that first. A buffer that does not is taken as 16-bit stereo at 48 kHz, little-endian, which is what a headerless .pcm file usually is; a caller that knows better says so through audio_transfer_lpcm_format() before starting, which is what a core or a disc reader would do. audio_mixer_loop_test gains the two contracts every other codec there has - a stream that replays after a seek to 0, and a repeating voice that keeps playing past its loop point, in float and in int16. Both switches over the two enums were swept with -Wswitch across every file that names them, which is how the AC-3 loader case came to be missing earlier this week. DTS still has no arm: rdts is the frame layer, and playback needs a decoder.
809 lines
27 KiB
C
809 lines
27 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;
|
|
|
|
if (tab->blob)
|
|
{
|
|
/* Packed encoding: 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++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (i = 0; i < tab->count; i++)
|
|
{
|
|
if (tab->ids[i] < (uint32_t)MSG_LAST)
|
|
id_slots[tab->ids[i]] = i + 1;
|
|
}
|
|
}
|
|
|
|
/* 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)
|
|
{
|
|
if (tab->strs)
|
|
return tab->strs[slot - 1];
|
|
return tab->blob + (slot - 1);
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/* Allocation-failure path only. */
|
|
{
|
|
uint32_t i;
|
|
if (tab->strs)
|
|
{
|
|
for (i = 0; i < tab->count; i++)
|
|
if (tab->ids[i] == id)
|
|
return tab->strs[i];
|
|
}
|
|
else
|
|
{
|
|
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, NULL,
|
|
(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, NULL,
|
|
(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, NULL,
|
|
(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, NULL,
|
|
(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, NULL,
|
|
(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, NULL,
|
|
(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, NULL,
|
|
(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, NULL,
|
|
(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, NULL,
|
|
(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, NULL,
|
|
(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, NULL,
|
|
(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, NULL,
|
|
(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, NULL,
|
|
(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, NULL,
|
|
(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, NULL,
|
|
(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, NULL,
|
|
(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, NULL,
|
|
(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, NULL,
|
|
(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, NULL,
|
|
(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, NULL,
|
|
(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, NULL,
|
|
(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, NULL,
|
|
(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, NULL,
|
|
(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, NULL,
|
|
(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, NULL,
|
|
(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, NULL,
|
|
(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, NULL,
|
|
(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, NULL,
|
|
(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, NULL,
|
|
(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, NULL,
|
|
(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, NULL,
|
|
(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, NULL,
|
|
(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, NULL,
|
|
(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, NULL,
|
|
(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, NULL,
|
|
(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, NULL,
|
|
(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
|
|
#ifdef HAVE_IMAGEVIEWER
|
|
{ "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;
|
|
}
|