diff options
author | 2021-10-31 12:31:16 +1100 | |
---|---|---|
committer | 2021-10-31 12:31:16 +1100 | |
commit | d64ea5331b2312f81df464fb22dcef0191216d86 (patch) | |
tree | a3f3784371da401e6a675e5b6b9734b88281a08d /src/lib/util/language.cpp | |
parent | cfffc54b61cabc5ef9533396bf87324eb5eeb63e (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/language.cpp')
-rw-r--r-- | src/lib/util/language.cpp | 221 |
1 files changed, 221 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 |