summaryrefslogtreecommitdiffstatshomepage
path: root/src/lib/util
diff options
context:
space:
mode:
author Vas Crabb <cuavas@users.noreply.github.com>2021-10-31 12:31:16 +1100
committer GitHub <noreply@github.com>2021-10-31 12:31:16 +1100
commitd64ea5331b2312f81df464fb22dcef0191216d86 (patch)
treea3f3784371da401e6a675e5b6b9734b88281a08d /src/lib/util
parentcfffc54b61cabc5ef9533396bf87324eb5eeb63e (diff)
-frontend: Refactored menu event handling and fixed a number of issues. (#8777)
* Moved common code for drawing about box, info viewer, and other text box menus to a base class; removed the last of the info viewer logic and the multi-line item hack from the base menu class. * Added previous/next group navigation for general inputs and plugin input selection menus. * Moved message catalog logic to lib/util, allowing osd and emu to use localised messages. * Made the base menu class use the UI manager’s feature for holding session state rather than a static map and mutex. * Improved menu event handling model, and fixed many issues, particularly with menus behaving badly when hidden/shown. * Added better support for menus that don’t participate in the usual menu stack, like the menuless sliders and the save/load state menus. * Made a number of menus refresh state when being shown after being hidden (fixes MT08121 among other issues). * Fixed indication of mounted slot option in the slot option details menu. * Improved appearance of background menus when emulation isn't running - draw all menus in the stack, and darken the background menus to make the edges of the active menu clearer. * Fixed locale issues in -listxml. -debugger: Made GUI debuggers more uniform. * Added new memory view features to Win32 debugger. * Fixed spelling of hexadecimal in Cocoa debugger and added decimal address option. * Fixed duplicate keyboard shortcut in Cocoa debugger (Shift-Cmd-D was both new device window and 64-bit float format). * Made keyboard shortcuts slightly more consistent across debuggers. -plugins: Moved input selection menu and sequence polling code to a common library. Fixed the issue that prevented keyboard inputs being mapped with -steadykey on. -docs: Started adding some documentation for MAME's internal UI, and updated the list of example front-ends. -Regenerated message catalog sources. For translators, the new strings are mostly: * The names of the inputs provided by the OS-dependent layer for things like fullscreen and video features. These show up in the user interface inputs menu. * The names for automatically generated views. These show up in the video options menu - test with a system with a lot of screens to see more variants. * The input macro plugin UI. * A few format strings for analog input assignments. * A few strings for the about box header.
Diffstat (limited to 'src/lib/util')
-rw-r--r--src/lib/util/language.cpp221
-rw-r--r--src/lib/util/language.h43
2 files changed, 264 insertions, 0 deletions
diff --git a/src/lib/util/language.cpp b/src/lib/util/language.cpp
new file mode 100644
index 00000000000..3a00fe80ad7
--- /dev/null
+++ b/src/lib/util/language.cpp
@@ -0,0 +1,221 @@
+// license:BSD-3-Clause
+// copyright-holders:Vas Crabb
+/***************************************************************************
+
+ language.cpp
+
+ Multi-language support.
+
+***************************************************************************/
+
+#include "language.h"
+
+#include "ioprocs.h"
+
+#include "osdcore.h"
+
+#include <cstdint>
+#include <cstring>
+#include <memory>
+#include <new>
+#include <unordered_map>
+#include <utility>
+
+
+namespace util {
+
+namespace {
+
+constexpr std::uint32_t MO_MAGIC = 0x950412de;
+constexpr std::uint32_t MO_MAGIC_REVERSED = 0xde120495;
+
+std::unique_ptr<std::uint32_t []> f_translation_data;
+std::unordered_map<std::string_view, std::pair<char const *, std::uint32_t> > f_translation_map;
+
+} // anonymous namespace
+
+
+void unload_translation()
+{
+ f_translation_data.reset();
+ f_translation_map.clear();
+}
+
+
+void load_translation(random_read &file)
+{
+ std::unique_ptr<std::uint32_t []> translation_data;
+ std::unordered_map<std::string_view, std::pair<char const *, std::uint32_t> > translation_map;
+
+ std::uint64_t size = 0;
+ if (file.length(size) || (20 > size))
+ {
+ osd_printf_error("Error reading translation file: %u-byte file is too small to contain translation data\n", size);
+ return;
+ }
+
+ translation_data.reset(new (std::nothrow) std::uint32_t [(size + 3) / 4]);
+ if (!translation_data)
+ {
+ osd_printf_error("Failed to allocate %u bytes to load translation data file\n", size);
+ return;
+ }
+
+ std::size_t read;
+ file.read(translation_data.get(), size, read);
+ if (read != size)
+ {
+ osd_printf_error("Error reading translation file: requested %u bytes but got %u bytes\n", size, read);
+ translation_data.reset();
+ return;
+ }
+
+ if ((translation_data[0] != MO_MAGIC) && (translation_data[0] != MO_MAGIC_REVERSED))
+ {
+ osd_printf_error("Error reading translation file: unrecognized magic number 0x%08X\n", translation_data[0]);
+ translation_data.reset();
+ return;
+ }
+
+ auto fetch_word =
+ [reversed = translation_data[0] == MO_MAGIC_REVERSED, words = translation_data.get()] (size_t offset)
+ {
+ return reversed ? swapendian_int32(words[offset]) : words[offset];
+ };
+
+ // FIXME: check major/minor version number
+
+ if ((fetch_word(3) % 4) || (fetch_word(4) % 4))
+ {
+ osd_printf_error("Error reading translation file: table offsets %u and %u are not word-aligned\n", fetch_word(3), fetch_word(4));
+ translation_data.reset();
+ return;
+ }
+
+ std::uint32_t const number_of_strings = fetch_word(2);
+ std::uint32_t const original_table_offset = fetch_word(3) >> 2;
+ std::uint32_t const translation_table_offset = fetch_word(4) >> 2;
+ if ((4 * (original_table_offset + (std::uint64_t(number_of_strings) * 2))) > size)
+ {
+ osd_printf_error("Error reading translation file: %u-entry original string table at offset %u extends past end of %u-byte file\n", number_of_strings, fetch_word(3), size);
+ translation_data.reset();
+ return;
+ }
+ if ((4 * (translation_table_offset + (std::uint64_t(number_of_strings) * 2))) > size)
+ {
+ osd_printf_error("Error reading translation file: %u-entry translated string table at offset %u extends past end of %u-byte file\n", number_of_strings, fetch_word(4), size);
+ translation_data.reset();
+ return;
+ }
+ osd_printf_verbose("Reading translation file: %u strings, original table at word offset %u, translated table at word offset %u\n", number_of_strings, original_table_offset, translation_table_offset);
+
+ char const *const data = reinterpret_cast<char const *>(translation_data.get());
+ for (std::uint32_t i = 1; number_of_strings > i; ++i)
+ {
+ std::uint32_t const original_length = fetch_word(original_table_offset + (2 * i));
+ std::uint32_t const original_offset = fetch_word(original_table_offset + (2 * i) + 1);
+ if ((original_length + original_offset) >= size)
+ {
+ osd_printf_error("Error reading translation file: %u-byte original string %u at offset %u extends past end of %u-byte file\n", original_length, i, original_offset, size);
+ continue;
+ }
+ if (data[original_length + original_offset])
+ {
+ osd_printf_error("Error reading translation file: %u-byte original string %u at offset %u is not correctly NUL-terminated\n", original_length, i, original_offset);
+ continue;
+ }
+
+ std::uint32_t const translation_length = fetch_word(translation_table_offset + (2 * i));
+ std::uint32_t const translation_offset = fetch_word(translation_table_offset + (2 * i) + 1);
+ if ((translation_length + translation_offset) >= size)
+ {
+ osd_printf_error("Error reading translation file: %u-byte translated string %u at offset %u extends past end of %u-byte file\n", translation_length, i, translation_offset, size);
+ continue;
+ }
+ if (data[translation_length + translation_offset])
+ {
+ osd_printf_error("Error reading translation file: %u-byte translated string %u at offset %u is not correctly NUL-terminated\n", translation_length, i, translation_offset);
+ continue;
+ }
+
+ std::string_view const original(&data[original_offset], original_length);
+ char const *const translation(&data[translation_offset]);
+ auto const ins = translation_map.emplace(original, std::make_pair(translation, translation_length));
+ if (!ins.second)
+ {
+ osd_printf_warning(
+ "Loading translation file: translation %u '%s'='%s' conflicts with previous translation '%s'='%s'\n",
+ i,
+ original,
+ translation,
+ ins.first->first,
+ ins.first->second.first);
+ }
+ }
+
+ osd_printf_verbose("Loaded %u translated string from file\n", translation_map.size());
+ f_translation_data = std::move(translation_data);
+ f_translation_map = std::move(translation_map);
+}
+
+
+char const *lang_translate(char const *message)
+{
+ auto const found = f_translation_map.find(message);
+ if (f_translation_map.end() != found)
+ return found->second.first;
+ return message;
+}
+
+
+std::string_view lang_translate(std::string_view message)
+{
+ auto const found = f_translation_map.find(message);
+ if (f_translation_map.end() != found)
+ return std::string_view(found->second.first, found->second.second);
+ return message;
+}
+
+
+char const *lang_translate(char const *context, char const *message)
+{
+ if (!f_translation_map.empty())
+ {
+ auto const ctxlen(std::strlen(context));
+ auto const msglen(std::strlen(message));
+ std::string key;
+ key.reserve(ctxlen + 1 + msglen);
+ key.append(context, ctxlen);
+ key.append(1, '\004');
+ key.append(message, msglen);
+ auto const found = f_translation_map.find(key);
+ if (f_translation_map.end() != found)
+ return found->second.first;
+ }
+ return message;
+}
+
+
+std::string_view lang_translate(char const *context, std::string_view message)
+{
+ return lang_translate(std::string_view(context), message);
+}
+
+
+std::string_view lang_translate(std::string_view context, std::string_view message)
+{
+ if (!f_translation_map.empty())
+ {
+ std::string key;
+ key.reserve(context.length() + 1 + message.length());
+ key.append(context);
+ key.append(1, '\004');
+ key.append(message);
+ auto const found = f_translation_map.find(key);
+ if (f_translation_map.end() != found)
+ return std::string_view(found->second.first, found->second.second);
+ }
+ return message;
+}
+
+} // namespace util
diff --git a/src/lib/util/language.h b/src/lib/util/language.h
new file mode 100644
index 00000000000..6e13def02e8
--- /dev/null
+++ b/src/lib/util/language.h
@@ -0,0 +1,43 @@
+// license:BSD-3-Clause
+// copyright-holders:Vas Crabb
+/***************************************************************************
+
+ language.h
+
+ Multi-language support.
+
+***************************************************************************/
+#ifndef MAME_LIB_UTIL_LANGUAGE_H
+#define MAME_LIB_UTIL_LANGUAGE_H
+
+#pragma once
+
+#include "utilfwd.h"
+
+#include <string_view>
+
+
+//**************************************************************************
+// LOCALIZATION SUPPORT
+//**************************************************************************
+
+#define _(...) lang_translate(__VA_ARGS__)
+
+#define N_(msg) (msg)
+#define N_p(ctx, msg) (msg)
+
+namespace util {
+
+void unload_translation();
+void load_translation(random_read &file);
+
+char const *lang_translate(char const *message);
+std::string_view lang_translate(std::string_view message);
+
+char const *lang_translate(char const *context, char const *message);
+std::string_view lang_translate(char const *context, std::string_view message);
+std::string_view lang_translate(std::string_view context, std::string_view message);
+
+} // namespace util
+
+#endif // MAME_LIB_UTIL_LANGUAGE_H