diff options
-rw-r--r-- | scripts/build/png2bdc.py | 109 | ||||
-rw-r--r-- | scripts/font/NotoSans-Bold.bdc | bin | 52993 -> 61317 bytes | |||
-rw-r--r-- | scripts/src/emu.lua | 1 | ||||
-rw-r--r-- | src/emu/rendfont.cpp | 1381 | ||||
-rw-r--r-- | src/emu/rendfont.h | 41 | ||||
-rw-r--r-- | src/emu/ui/cmddata.h | 220 | ||||
-rw-r--r-- | src/emu/ui/cmdrender.h | 150 | ||||
-rw-r--r-- | src/frontend/mame/ui/selmenu.cpp | 47 | ||||
-rw-r--r-- | src/frontend/mame/ui/selmenu.h | 19 | ||||
-rw-r--r-- | src/lib/util/coretmpl.h | 388 | ||||
-rw-r--r-- | uismall.bdf | 7 |
11 files changed, 1716 insertions, 647 deletions
diff --git a/scripts/build/png2bdc.py b/scripts/build/png2bdc.py index 4ffeb4c01ab..a1be5c552d0 100644 --- a/scripts/build/png2bdc.py +++ b/scripts/build/png2bdc.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python ## ## license:BSD-3-Clause ## copyright-holders:Aaron Giles, Andrew Gardner @@ -50,7 +50,7 @@ ## ## Python note: ## This is a near-literal translation of the original C++ code. As such there -## are some very non-pythonic things done throughout. The conversion was done +## are some very non-pythonic things done throughout. The conversion was done ## this way so as to insure compatibility as much as possible given the small ## number of test cases. ## @@ -74,7 +74,7 @@ class RenderFontChar: """ Contains information about a single character in a font. """ - + def __init__(self): """ """ @@ -90,10 +90,11 @@ class RenderFont: """ Contains information about a font """ - + def __init__(self): self.height = 0 # height of the font, from ascent to descent self.yOffs = 0 # y offset from baseline to descent + self.defChar = -1 # default character for glyphs not present self.chars = list() # array of characters for i in range(0, 65536): self.chars.append(RenderFontChar()) @@ -107,7 +108,7 @@ def pixelIsSet(value): return (value & 0xffffff) == 0 -def renderFontSaveCached(font, filename, hash32): +def renderFontSaveCached(font, filename, length64, hash32): """ """ fp = open(filename, "wb") @@ -120,45 +121,61 @@ def renderFontSaveCached(font, filename, hash32): if c.width > 0: numChars += 1 - CACHED_CHAR_SIZE = 12 - CACHED_HEADER_SIZE = 16 - + CACHED_CHAR_SIZE = 16 + CACHED_HEADER_SIZE = 32 + try: + fp.write(b'b') + fp.write(b'd') + fp.write(b'c') fp.write(b'f') - fp.write(b'o') fp.write(b'n') fp.write(b't') + fp.write(b2p(1)) + fp.write(b2p(0)) + fp.write(b2p(length64 >> 56 & 0xff)) + fp.write(b2p(length64 >> 48 & 0xff)) + fp.write(b2p(length64 >> 40 & 0xff)) + fp.write(b2p(length64 >> 32 & 0xff)) + fp.write(b2p(length64 >> 24 & 0xff)) + fp.write(b2p(length64 >> 16 & 0xff)) + fp.write(b2p(length64 >> 8 & 0xff)) + fp.write(b2p(length64 >> 0 & 0xff)) fp.write(b2p(hash32 >> 24 & 0xff)) fp.write(b2p(hash32 >> 16 & 0xff)) fp.write(b2p(hash32 >> 8 & 0xff)) fp.write(b2p(hash32 >> 0 & 0xff)) - fp.write(b2p(font.height >> 8 & 0xff)) - fp.write(b2p(font.height >> 0 & 0xff)) - fp.write(b2p(font.yOffs >> 8 & 0xff)) - fp.write(b2p(font.yOffs >> 0 & 0xff)) fp.write(b2p(numChars >> 24 & 0xff)) fp.write(b2p(numChars >> 16 & 0xff)) fp.write(b2p(numChars >> 8 & 0xff)) fp.write(b2p(numChars >> 0 & 0xff)) - + fp.write(b2p(font.height >> 8 & 0xff)) + fp.write(b2p(font.height >> 0 & 0xff)) + fp.write(b2p(font.yOffs >> 8 & 0xff)) + fp.write(b2p(font.yOffs >> 0 & 0xff)) + fp.write(b2p(font.defChar >> 24 & 0xff)) + fp.write(b2p(font.defChar >> 16 & 0xff)) + fp.write(b2p(font.defChar >> 8 & 0xff)) + fp.write(b2p(font.defChar >> 0 & 0xff)) + # Write a blank table at first (?) charTable = [0]*(numChars * CACHED_CHAR_SIZE) for i in range(numChars * CACHED_CHAR_SIZE): fp.write(b2p(charTable[i])) - + # Loop over all characters tableIndex = 0 - + for i in range(len(font.chars)): c = font.chars[i] if c.width == 0: continue - + if c.bitmap: dBuffer = list() accum = 0 accbit = 7 - + # Bit-encode the character data for y in range(0, c.bmHeight): src = None @@ -173,42 +190,44 @@ def renderFontSaveCached(font, filename, hash32): dBuffer.append(accum) accum = 0 accbit = 7 - + # Flush any extra if accbit != 7: dBuffer.append(accum) - + # Write the data for j in range(len(dBuffer)): fp.write(b2p(dBuffer[j])) - + destIndex = tableIndex * CACHED_CHAR_SIZE - charTable[destIndex + 0] = i >> 8 & 0xff - charTable[destIndex + 1] = i >> 0 & 0xff - charTable[destIndex + 2] = c.width >> 8 & 0xff - charTable[destIndex + 3] = c.width >> 0 & 0xff - charTable[destIndex + 4] = c.xOffs >> 8 & 0xff - charTable[destIndex + 5] = c.xOffs >> 0 & 0xff - charTable[destIndex + 6] = c.yOffs >> 8 & 0xff - charTable[destIndex + 7] = c.yOffs >> 0 & 0xff - charTable[destIndex + 8] = c.bmWidth >> 8 & 0xff - charTable[destIndex + 9] = c.bmWidth >> 0 & 0xff - charTable[destIndex + 10] = c.bmHeight >> 8 & 0xff - charTable[destIndex + 11] = c.bmHeight >> 0 & 0xff + charTable[destIndex + 0] = i >> 24 & 0xff + charTable[destIndex + 1] = i >> 16 & 0xff + charTable[destIndex + 2] = i >> 8 & 0xff + charTable[destIndex + 3] = i >> 0 & 0xff + charTable[destIndex + 4] = c.width >> 8 & 0xff + charTable[destIndex + 5] = c.width >> 0 & 0xff + charTable[destIndex + 8] = c.xOffs >> 8 & 0xff + charTable[destIndex + 9] = c.xOffs >> 0 & 0xff + charTable[destIndex + 10] = c.yOffs >> 8 & 0xff + charTable[destIndex + 11] = c.yOffs >> 0 & 0xff + charTable[destIndex + 12] = c.bmWidth >> 8 & 0xff + charTable[destIndex + 13] = c.bmWidth >> 0 & 0xff + charTable[destIndex + 14] = c.bmHeight >> 8 & 0xff + charTable[destIndex + 15] = c.bmHeight >> 0 & 0xff tableIndex += 1 - + # Seek back to the beginning and rewrite the table fp.seek(CACHED_HEADER_SIZE, 0) for i in range(numChars * CACHED_CHAR_SIZE): fp.write(b2p(charTable[i])) - + fp.close() return 0 except: print(sys.exc_info[1]) return 1 - + def bitmapToChars(pngObject, font): """ @@ -225,7 +244,7 @@ def bitmapToChars(pngObject, font): for r,g,b,a in zip(irpd, irpd, irpd, irpd): cRow.append(a << 24 | r << 16 | g << 8 | b) bitmap.append(cRow) - + rowStart = 0 while rowStart < height: # Find the top of the row @@ -317,7 +336,7 @@ def bitmapToChars(pngObject, font): ch.yOffs = font.yOffs ch.bmWidth = len(ch.bitmap[0]) ch.bmHeight = len(ch.bitmap) - + # Insert the character into the list font.chars[chStart] = ch @@ -327,7 +346,7 @@ def bitmapToChars(pngObject, font): # Next row rowStart = rowEnd + 1 - + # Return non-zero if we errored return rowStart < height @@ -341,7 +360,7 @@ def main(): sys.stderr.write("Usage:\n%s <input.png> [<input2.png> [...]] <output.bdc>\n" % sys.argv[0]) return 1 bdcName = sys.argv[-1] - + font = RenderFont() for i in range(1, len(sys.argv)-1): filename = sys.argv[i] @@ -359,12 +378,12 @@ def main(): error = bitmapToChars(pngObject, font) if error: return 1 - - error = renderFontSaveCached(font, bdcName, 0) + + error = renderFontSaveCached(font, bdcName, 0, 0) return error - - - + + + ######################################## ## Program entry point ######################################## diff --git a/scripts/font/NotoSans-Bold.bdc b/scripts/font/NotoSans-Bold.bdc Binary files differindex 8c13907b86a..2ac5c7e0e59 100644 --- a/scripts/font/NotoSans-Bold.bdc +++ b/scripts/font/NotoSans-Bold.bdc diff --git a/scripts/src/emu.lua b/scripts/src/emu.lua index cd94d54f925..b67226e1576 100644 --- a/scripts/src/emu.lua +++ b/scripts/src/emu.lua @@ -183,7 +183,6 @@ files { MAME_DIR .. "src/emu/video.h", MAME_DIR .. "src/emu/rendersw.hxx", MAME_DIR .. "src/emu/ui/uimain.h", - MAME_DIR .. "src/emu/ui/cmdrender.h", -- TODO: remove MAME_DIR .. "src/emu/ui/cmddata.h", -- TODO: remove MAME_DIR .. "src/emu/debug/debugcmd.cpp", MAME_DIR .. "src/emu/debug/debugcmd.h", diff --git a/src/emu/rendfont.cpp b/src/emu/rendfont.cpp index 9adbbe2dc33..5e7f0329fd2 100644 --- a/src/emu/rendfont.cpp +++ b/src/emu/rendfont.cpp @@ -1,5 +1,5 @@ // license:BSD-3-Clause -// copyright-holders:Aaron Giles +// copyright-holders:Aaron Giles, Vas Crabb /*************************************************************************** rendfont.c @@ -16,10 +16,432 @@ #include "osdepend.h" #include "uismall.fh" -#include "ui/cmdrender.h" +#include "ui/uicmd14.fh" +#include "ui/cmddata.h" +#include <algorithm> #include <cstddef> #include <cstring> +#include <iterator> +#include <limits> + + +#define VERBOSE 0 + +#define LOG(...) do { if (VERBOSE) osd_printf_verbose(__VA_ARGS__); } while (0) + + +namespace { + +template <typename Iterator> +class bdf_helper +{ +public: + bdf_helper(Iterator const &begin, Iterator const &end) + : m_keyword_begin(begin) + , m_keyword_end(begin) + , m_value_begin(begin) + , m_value_end(begin) + , m_line_end(begin) + , m_end(end) + { + next_line(); + } + + bool at_end() const { return m_end == m_keyword_begin; } + + void next_line() + { + m_keyword_begin = m_line_end; + while ((m_end != m_keyword_begin) && (('\r' == *m_keyword_begin) || ('\n' == *m_keyword_begin))) + ++m_keyword_begin; + + m_keyword_end = m_keyword_begin; + while ((m_end != m_keyword_end) && (' ' != *m_keyword_end) && ('\t' != *m_keyword_end) && ('\r' != *m_keyword_end) && ('\n' != *m_keyword_end)) + ++m_keyword_end; + + m_value_begin = m_keyword_end; + while ((m_end != m_value_begin) && ((' ' == *m_value_begin) || ('\t' == *m_value_begin)) && ('\r' != *m_value_begin) && ('\n' != *m_value_begin)) + ++m_value_begin; + + m_value_end = m_line_end = m_value_begin; + while ((m_end != m_line_end) && ('\r' != *m_line_end) && ('\n' != *m_line_end)) + { + if ((' ' != *m_line_end) && ('\t' != *m_line_end)) + m_value_end = ++m_line_end; + else + ++m_line_end; + } + } + + bool is_keyword(char const *keyword) const + { + Iterator pos(m_keyword_begin); + while (true) + { + if (m_keyword_end == pos) + { + return '\0' == *keyword; + } + else if (('\0' == *keyword) || (*pos != *keyword)) + { + return false; + } + else + { + ++pos; + ++keyword; + } + } + } + + Iterator const &keyword_begin() const { return m_keyword_begin; } + Iterator const &keyword_end() const { return m_keyword_end; } + auto keyword_length() const { return std::distance(m_keyword_begin, m_keyword_end); } + + Iterator const &value_begin() const { return m_value_begin; } + Iterator const &value_end() const { return m_value_end; } + auto value_length() const { return std::distance(m_value_begin, m_value_end); } + +private: + Iterator m_keyword_begin; + Iterator m_keyword_end; + Iterator m_value_begin; + Iterator m_value_end; + Iterator m_line_end; + Iterator const m_end; +}; + + +class bdc_header +{ +public: + static constexpr unsigned MAJVERSION = 1; + static constexpr unsigned MINVERSION = 0; + + bool read(emu_file &f) + { + return f.read(m_data, sizeof(m_data)) == sizeof(m_data); + } + bool write(emu_file &f) + { + return f.write(m_data, sizeof(m_data)) == sizeof(m_data); + } + + bool check_magic() const + { + return !std::memcmp(MAGIC, m_data + OFFS_MAGIC, OFFS_MAJVERSION - OFFS_MAGIC); + } + unsigned get_major_version() const + { + return m_data[OFFS_MAJVERSION]; + } + unsigned get_minor_version() const + { + return m_data[OFFS_MINVERSION]; + } + u64 get_original_length() const + { + return + (u64(m_data[OFFS_ORIGLENGTH + 0]) << (7 * 8)) | + (u64(m_data[OFFS_ORIGLENGTH + 1]) << (6 * 8)) | + (u64(m_data[OFFS_ORIGLENGTH + 2]) << (5 * 8)) | + (u64(m_data[OFFS_ORIGLENGTH + 3]) << (4 * 8)) | + (u64(m_data[OFFS_ORIGLENGTH + 4]) << (3 * 8)) | + (u64(m_data[OFFS_ORIGLENGTH + 5]) << (2 * 8)) | + (u64(m_data[OFFS_ORIGLENGTH + 6]) << (1 * 8)) | + (u64(m_data[OFFS_ORIGLENGTH + 7]) << (0 * 8)); + } + u32 get_original_hash() const + { + return + (u32(m_data[OFFS_ORIGHASH + 0]) << (3 * 8)) | + (u32(m_data[OFFS_ORIGHASH + 1]) << (2 * 8)) | + (u32(m_data[OFFS_ORIGHASH + 2]) << (1 * 8)) | + (u32(m_data[OFFS_ORIGHASH + 3]) << (0 * 8)); + } + u32 get_glyph_count() const + { + return + (u32(m_data[OFFS_GLYPHCOUNT + 0]) << (3 * 8)) | + (u32(m_data[OFFS_GLYPHCOUNT + 1]) << (2 * 8)) | + (u32(m_data[OFFS_GLYPHCOUNT + 2]) << (1 * 8)) | + (u32(m_data[OFFS_GLYPHCOUNT + 3]) << (0 * 8)); + } + u16 get_height() const + { + return + (u16(m_data[OFFS_HEIGHT + 0]) << (1 * 8)) | + (u16(m_data[OFFS_HEIGHT + 1]) << (0 * 8)); + } + s16 get_y_offset() const + { + return + (u16(m_data[OFFS_YOFFSET + 0]) << (1 * 8)) | + (u16(m_data[OFFS_YOFFSET + 1]) << (0 * 8)); + } + s32 get_default_character() const + { + return + (u32(m_data[OFFS_DEFCHAR + 0]) << (3 * 8)) | + (u32(m_data[OFFS_DEFCHAR + 1]) << (2 * 8)) | + (u32(m_data[OFFS_DEFCHAR + 2]) << (1 * 8)) | + (u32(m_data[OFFS_DEFCHAR + 3]) << (0 * 8)); + } + + void set_magic() + { + std::memcpy(m_data + OFFS_MAGIC, MAGIC, OFFS_MAJVERSION - OFFS_MAGIC); + } + void set_version() + { + m_data[OFFS_MAJVERSION] = MAJVERSION; + m_data[OFFS_MINVERSION] = MINVERSION; + } + void set_original_length(u64 value) + { + m_data[OFFS_ORIGLENGTH + 0] = u8((value >> (7 * 8)) & 0x00ff); + m_data[OFFS_ORIGLENGTH + 1] = u8((value >> (6 * 8)) & 0x00ff); + m_data[OFFS_ORIGLENGTH + 2] = u8((value >> (5 * 8)) & 0x00ff); + m_data[OFFS_ORIGLENGTH + 3] = u8((value >> (4 * 8)) & 0x00ff); + m_data[OFFS_ORIGLENGTH + 4] = u8((value >> (3 * 8)) & 0x00ff); + m_data[OFFS_ORIGLENGTH + 5] = u8((value >> (2 * 8)) & 0x00ff); + m_data[OFFS_ORIGLENGTH + 6] = u8((value >> (1 * 8)) & 0x00ff); + m_data[OFFS_ORIGLENGTH + 7] = u8((value >> (0 * 8)) & 0x00ff); + } + void set_original_hash(u32 value) + { + m_data[OFFS_ORIGHASH + 0] = u8((value >> (3 * 8)) & 0x00ff); + m_data[OFFS_ORIGHASH + 1] = u8((value >> (2 * 8)) & 0x00ff); + m_data[OFFS_ORIGHASH + 2] = u8((value >> (1 * 8)) & 0x00ff); + m_data[OFFS_ORIGHASH + 3] = u8((value >> (0 * 8)) & 0x00ff); + } + void set_glyph_count(u32 value) + { + m_data[OFFS_GLYPHCOUNT + 0] = u8((value >> (3 * 8)) & 0x00ff); + m_data[OFFS_GLYPHCOUNT + 1] = u8((value >> (2 * 8)) & 0x00ff); + m_data[OFFS_GLYPHCOUNT + 2] = u8((value >> (1 * 8)) & 0x00ff); + m_data[OFFS_GLYPHCOUNT + 3] = u8((value >> (0 * 8)) & 0x00ff); + } + void set_height(u16 value) + { + m_data[OFFS_HEIGHT + 0] = u8((value >> (1 * 8)) & 0x00ff); + m_data[OFFS_HEIGHT + 1] = u8((value >> (0 * 8)) & 0x00ff); + } + void set_y_offset(s16 value) + { + m_data[OFFS_YOFFSET + 0] = u8((value >> (1 * 8)) & 0x00ff); + m_data[OFFS_YOFFSET + 1] = u8((value >> (0 * 8)) & 0x00ff); + } + void set_default_character(s32 value) + { + m_data[OFFS_DEFCHAR + 0] = u8((value >> (3 * 8)) & 0x00ff); + m_data[OFFS_DEFCHAR + 1] = u8((value >> (2 * 8)) & 0x00ff); + m_data[OFFS_DEFCHAR + 2] = u8((value >> (1 * 8)) & 0x00ff); + m_data[OFFS_DEFCHAR + 3] = u8((value >> (0 * 8)) & 0x00ff); + } + +private: + static constexpr std::size_t OFFS_MAGIC = 0x00; // 0x06 bytes + static constexpr std::size_t OFFS_MAJVERSION = 0x06; // 0x01 bytes (binary integer) + static constexpr std::size_t OFFS_MINVERSION = 0x07; // 0x01 bytes (binary integer) + static constexpr std::size_t OFFS_ORIGLENGTH = 0x08; // 0x08 bytes (big-endian binary integer) + static constexpr std::size_t OFFS_ORIGHASH = 0x10; // 0x04 bytes + static constexpr std::size_t OFFS_GLYPHCOUNT = 0x14; // 0x04 bytes (big-endian binary integer) + static constexpr std::size_t OFFS_HEIGHT = 0x18; // 0x02 bytes (big-endian binary integer) + static constexpr std::size_t OFFS_YOFFSET = 0x1a; // 0x02 bytes (big-endian binary integer) + static constexpr std::size_t OFFS_DEFCHAR = 0x1c; // 0x04 bytes (big-endian binary integer) + static constexpr std::size_t OFFS_END = 0x20; + + static u8 const MAGIC[OFFS_MAJVERSION - OFFS_MAGIC]; + + u8 m_data[OFFS_END]; +}; + +u8 const bdc_header::MAGIC[OFFS_MAJVERSION - OFFS_MAGIC] = { 'b', 'd', 'c', 'f', 'n', 't' }; + + +class bdc_table_entry +{ +public: + bdc_table_entry(void *bytes) + : m_ptr(reinterpret_cast<u8 *>(bytes)) + { + } + bdc_table_entry(bdc_table_entry const &that) = default; + bdc_table_entry(bdc_table_entry &&that) = default; + + bdc_table_entry get_next() const + { + return bdc_table_entry(m_ptr + OFFS_END); + } + + u32 get_encoding() const + { + return + (u32(m_ptr[OFFS_ENCODING + 0]) << (3 * 8)) | + (u32(m_ptr[OFFS_ENCODING + 1]) << (2 * 8)) | + (u32(m_ptr[OFFS_ENCODING + 2]) << (1 * 8)) | + (u32(m_ptr[OFFS_ENCODING + 3]) << (0 * 8)); + } + u16 get_x_advance() const + { + return + (u16(m_ptr[OFFS_XADVANCE + 0]) << (1 * 8)) | + (u16(m_ptr[OFFS_XADVANCE + 1]) << (0 * 8)); + } + s16 get_bb_x_offset() const + { + return + (u16(m_ptr[OFFS_BBXOFFSET + 0]) << (1 * 8)) | + (u16(m_ptr[OFFS_BBXOFFSET + 1]) << (0 * 8)); + } + s16 get_bb_y_offset() const + { + return + (u16(m_ptr[OFFS_BBYOFFSET + 0]) << (1 * 8)) | + (u16(m_ptr[OFFS_BBYOFFSET + 1]) << (0 * 8)); + } + u16 get_bb_width() const + { + return + (u16(m_ptr[OFFS_BBWIDTH + 0]) << (1 * 8)) | + (u16(m_ptr[OFFS_BBWIDTH + 1]) << (0 * 8)); + } + u16 get_bb_height() const + { + return + (u16(m_ptr[OFFS_BBHEIGHT + 0]) << (1 * 8)) | + (u16(m_ptr[OFFS_BBHEIGHT + 1]) << (0 * 8)); + } + + void set_encoding(u32 value) + { + m_ptr[OFFS_ENCODING + 0] = u8((value >> (3 * 8)) & 0x00ff); + m_ptr[OFFS_ENCODING + 1] = u8((value >> (2 * 8)) & 0x00ff); + m_ptr[OFFS_ENCODING + 2] = u8((value >> (1 * 8)) & 0x00ff); + m_ptr[OFFS_ENCODING + 3] = u8((value >> (0 * 8)) & 0x00ff); + } + void set_x_advance(u16 value) + { + m_ptr[OFFS_XADVANCE + 0] = u8((value >> (1 * 8)) & 0x00ff); + m_ptr[OFFS_XADVANCE + 1] = u8((value >> (0 * 8)) & 0x00ff); + } + void set_bb_x_offset(s16 value) + { + m_ptr[OFFS_BBXOFFSET + 0] = u8((value >> (1 * 8)) & 0x00ff); + m_ptr[OFFS_BBXOFFSET + 1] = u8((value >> (0 * 8)) & 0x00ff); + } + void set_bb_y_offset(s16 value) + { + m_ptr[OFFS_BBYOFFSET + 0] = u8((value >> (1 * 8)) & 0x00ff); + m_ptr[OFFS_BBYOFFSET + 1] = u8((value >> (0 * 8)) & 0x00ff); + } + void set_bb_width(u16 value) + { + m_ptr[OFFS_BBWIDTH + 0] = u8((value >> (1 * 8)) & 0x00ff); + m_ptr[OFFS_BBWIDTH + 1] = u8((value >> (0 * 8)) & 0x00ff); + } + void set_bb_height(u16 value) + { + m_ptr[OFFS_BBHEIGHT + 0] = u8((value >> (1 * 8)) & 0x00ff); + m_ptr[OFFS_BBHEIGHT + 1] = u8((value >> (0 * 8)) & 0x00ff); + } + + bdc_table_entry &operator=(bdc_table_entry const &that) = default; + bdc_table_entry &operator=(bdc_table_entry &&that) = default; + + static std::size_t size() + { + return OFFS_END; + } + +private: + static constexpr std::size_t OFFS_ENCODING = 0x00; // 0x04 bytes (big-endian binary integer) + static constexpr std::size_t OFFS_XADVANCE = 0x04; // 0x02 bytes (big-endian binary integer) + // two bytes reserved + static constexpr std::size_t OFFS_BBXOFFSET = 0x08; // 0x02 bytes (big-endian binary integer) + static constexpr std::size_t OFFS_BBYOFFSET = 0x0a; // 0x02 bytes (big-endian binary integer) + static constexpr std::size_t OFFS_BBWIDTH = 0x0c; // 0x02 bytes (big-endian binary integer) + static constexpr std::size_t OFFS_BBHEIGHT = 0x0e; // 0x02 bytes (big-endian binary integer) + static constexpr std::size_t OFFS_END = 0x10; + + u8 *m_ptr; +}; + +} // anonymous namespace + + +void convert_command_glyph(std::string &str) +{ + str.c_str(); // force NUL-termination - we depend on it later + std::size_t const len(str.length()); + std::vector<char> buf(2 * (len + 1)); + std::size_t j(0); + for (std::size_t i = 0; len > i; ) + { + // decode UTF-8 + char32_t uchar; + int const codelen(uchar_from_utf8(&uchar, &str[i], len - i)); + if (0 >= codelen) + break; + i += codelen; + + // check for three metacharacters + fix_command_t const *fixcmd(nullptr); + switch (uchar) + { + case COMMAND_CONVERT_TEXT: + for (fix_strings_t *fixtext = convert_text; fixtext->glyph_code; ++fixtext) + { + if (!fixtext->glyph_str_len) + fixtext->glyph_str_len = std::strlen(fixtext->glyph_str); + + if (!std::strncmp(fixtext->glyph_str, &str[i], fixtext->glyph_str_len)) + { + uchar = fixtext->glyph_code + COMMAND_UNICODE; + i += strlen(fixtext->glyph_str); + break; + } + } + break; + + case COMMAND_DEFAULT_TEXT: + fixcmd = default_text; + break; + + case COMMAND_EXPAND_TEXT: + fixcmd = expand_text; + break; + } + + // this substitutes a single character + if (fixcmd) + { + if (str[i] == uchar) + { + ++i; + } + else + { + while (fixcmd->glyph_code && (fixcmd->glyph_char != str[i])) + ++fixcmd; + if (fixcmd->glyph_code) + { + uchar = COMMAND_UNICODE + fixcmd->glyph_code; + ++i; + } + } + } + + // copy character to output + int const outlen(utf8_from_uchar(&buf[j], buf.size() - j, uchar)); + if (0 >= outlen) + break; + j += outlen; + } + str.assign(&buf[0], j); +} const u64 render_font::CACHED_BDF_HASH_SIZE; @@ -58,30 +480,36 @@ inline render_font::glyph &render_font::get_char(char32_t chnum) { static glyph dummy_glyph; - // grab the table; if none, return the dummy character - if ((chnum / 256) >= ARRAY_LENGTH(m_glyphs)) - return dummy_glyph; - if (!m_glyphs[chnum / 256] && m_format == FF_OSD) - m_glyphs[chnum / 256] = new glyph[256]; - if (!m_glyphs[chnum / 256]) + unsigned const page(chnum / 256); + if (page >= ARRAY_LENGTH(m_glyphs)) + { + if ((0 <= m_defchar) && (chnum != m_defchar)) + return get_char(m_defchar); + else + return dummy_glyph; + } + else if (!m_glyphs[page]) { //mamep: make table for command glyph - if (chnum >= COMMAND_UNICODE && chnum < COMMAND_UNICODE + MAX_GLYPH_FONT) - m_glyphs[chnum / 256] = new glyph[256]; + if ((m_format == format::OSD) || ((chnum >= COMMAND_UNICODE) && (chnum < COMMAND_UNICODE + MAX_GLYPH_FONT))) + m_glyphs[page] = new glyph[256]; + else if ((0 <= m_defchar) && (chnum != m_defchar)) + return get_char(m_defchar); else return dummy_glyph; } // if the character isn't generated yet, do it now - glyph &gl = m_glyphs[chnum / 256][chnum % 256]; + glyph &gl = m_glyphs[page][chnum % 256]; if (!gl.bitmap.valid()) { //mamep: command glyph support if (m_height_cmd && chnum >= COMMAND_UNICODE && chnum < COMMAND_UNICODE + MAX_GLYPH_FONT) { - glyph &glyph_ch = m_glyphs_cmd[chnum / 256][chnum % 256]; - float scale = (float)m_height / (float)m_height_cmd; - if (m_format == FF_OSD) scale *= 0.90f; + glyph &glyph_ch = m_glyphs_cmd[page][chnum % 256]; + float scale = float(m_height) / float(m_height_cmd); + if (m_format == format::OSD) + scale *= 0.90f; if (!glyph_ch.bitmap.valid()) char_expand(chnum, glyph_ch); @@ -89,11 +517,11 @@ inline render_font::glyph &render_font::get_char(char32_t chnum) //mamep: for color glyph gl.color = glyph_ch.color; - gl.width = (int)(glyph_ch.width * scale + 0.5f); - gl.xoffs = (int)(glyph_ch.xoffs * scale + 0.5f); - gl.yoffs = (int)(glyph_ch.yoffs * scale + 0.5f); - gl.bmwidth = (int)(glyph_ch.bmwidth * scale + 0.5f); - gl.bmheight = (int)(glyph_ch.bmheight * scale + 0.5f); + gl.width = int(glyph_ch.width * scale + 0.5f); + gl.xoffs = int(glyph_ch.xoffs * scale + 0.5f); + gl.yoffs = int(glyph_ch.yoffs * scale + 0.5f); + gl.bmwidth = int(glyph_ch.bmwidth * scale + 0.5f); + gl.bmheight = int(glyph_ch.bmheight * scale + 0.5f); gl.bitmap.allocate(gl.bmwidth, gl.bmheight); rectangle clip; @@ -107,10 +535,11 @@ inline render_font::glyph &render_font::get_char(char32_t chnum) gl.texture->set_bitmap(gl.bitmap, gl.bitmap.cliprect(), TEXFORMAT_ARGB32); } else + { char_expand(chnum, gl); + } } - // return the resulting character return gl; } @@ -125,55 +554,53 @@ inline render_font::glyph &render_font::get_char(char32_t chnum) //------------------------------------------------- render_font::render_font(render_manager &manager, const char *filename) - : m_manager(manager), - m_format(FF_UNKNOWN), - m_height(0), - m_yoffs(0), - m_scale(1.0f), - m_rawsize(0), - m_osdfont(), - m_height_cmd(0), - m_yoffs_cmd(0) + : m_manager(manager) + , m_format(format::UNKNOWN) + , m_height(0) + , m_yoffs(0) + , m_defchar(-1) + , m_scale(1.0f) + , m_rawsize(0) + , m_osdfont() + , m_height_cmd(0) + , m_yoffs_cmd(0) { memset(m_glyphs, 0, sizeof(m_glyphs)); memset(m_glyphs_cmd, 0, sizeof(m_glyphs_cmd)); // if this is an OSD font, we're done - if (filename != nullptr) + if (filename) { m_osdfont = manager.machine().osd().font_alloc(); - if (m_osdfont) + if (m_osdfont && m_osdfont->open(manager.machine().options().font_path(), filename, m_height)) { - if (m_osdfont->open(manager.machine().options().font_path(), filename, m_height)) - { - m_scale = 1.0f / (float)m_height; - m_format = FF_OSD; + m_scale = 1.0f / float(m_height); + m_format = format::OSD; - //mamep: allocate command glyph font - render_font_command_glyph(); - return; - } - m_osdfont.reset(); + //mamep: allocate command glyph font + render_font_command_glyph(); + return; } + m_osdfont.reset(); } // if the filename is 'default' default to 'ui.bdf' for backwards compatibility - if (filename != nullptr && core_stricmp(filename, "default") == 0) + if (filename && !core_stricmp(filename, "default")) filename = "ui.bdf"; - // attempt to load the cached version of the font first - if (filename != nullptr && load_cached_bdf(filename)) + // attempt to load an external BDF font first + if (filename && load_cached_bdf(filename)) { //mamep: allocate command glyph font render_font_command_glyph(); return; } - // load the raw data instead + // load the compiled in data instead emu_file ramfile(OPEN_FLAG_READ); - osd_file::error filerr = ramfile.open_ram(font_uismall, sizeof(font_uismall)); - if (filerr == osd_file::error::NONE) - load_cached(ramfile, 0); + osd_file::error const filerr(ramfile.open_ram(font_uismall, sizeof(font_uismall))); + if (osd_file::error::NONE == filerr) + load_cached(ramfile, 0, 0); render_font_command_glyph(); } @@ -216,15 +643,15 @@ render_font::~render_font() void render_font::char_expand(char32_t chnum, glyph &gl) { - rgb_t color = rgb_t(0xff,0xff,0xff,0xff); - bool is_cmd = (chnum >= COMMAND_UNICODE && chnum < COMMAND_UNICODE + MAX_GLYPH_FONT); + LOG("render_font::char_expand: expanding character %u\n", unsigned(chnum)); - if (gl.color) - color = gl.color; + rgb_t const fgcol(gl.color ? gl.color : rgb_t(0xff, 0xff, 0xff, 0xff)); + rgb_t const bgcol(0x00, 0xff, 0xff, 0xff); + bool const is_cmd((chnum >= COMMAND_UNICODE) && (chnum < COMMAND_UNICODE + MAX_GLYPH_FONT)); if (is_cmd) { - // punt if nothing there + // abort if nothing there if (gl.bmwidth == 0 || gl.bmheight == 0 || gl.rawdata == nullptr) return; @@ -245,37 +672,47 @@ void render_font::char_expand(char32_t chnum, glyph &gl) if (accumbit == 7) accum = *ptr++; if (dest != nullptr) - *dest++ = (accum & (1 << accumbit)) ? color : rgb_t(0x00,0xff,0xff,0xff); + *dest++ = (accum & (1 << accumbit)) ? fgcol : bgcol; accumbit = (accumbit - 1) & 7; } } } } - // if we're an OSD font, query the info - else if (m_format == FF_OSD) + else if (m_format == format::OSD) { - // we set bmwidth to -1 if we've previously queried and failed - if (gl.bmwidth == -1) + // if we're an OSD font, query the info + if (0 > gl.bmwidth) + { + // we set bmwidth to -1 if we've previously queried and failed + LOG("render_font::char_expand: previously failed to get bitmap from OSD font\n"); return; - - // attempt to get the font bitmap; if we fail, set bmwidth to -1 + } if (!m_osdfont->get_bitmap(chnum, gl.bitmap, gl.width, gl.xoffs, gl.yoffs)) { + // attempt to get the font bitmap failed - set bmwidth to -1 + LOG("render_font::char_expand: get bitmap from OSD font failed\n"); gl.bitmap.reset(); gl.bmwidth = -1; return; } - - // populate the bmwidth/bmheight fields - gl.bmwidth = gl.bitmap.width(); - gl.bmheight = gl.bitmap.height(); + else + { + // populate the bmwidth/bmheight fields + LOG("render_font::char_expand: got %dx%d bitmap from OSD font\n", gl.bitmap.width(), gl.bitmap.height()); + gl.bmwidth = gl.bitmap.width(); + gl.bmheight = gl.bitmap.height(); + } + } + else if (!gl.bmwidth || !gl.bmheight || !gl.rawdata) + { + // abort if nothing there + LOG("render_font::char_expand: empty bitmap bounds or no raw data\n"); + return; } - // other formats need to parse their data else { - // punt if nothing there - if (gl.bmwidth == 0 || gl.bmheight == 0 || gl.rawdata == nullptr) - return; + // other formats need to parse their data + LOG("render_font::char_expand: building bitmap from raw data\n"); // allocate a new bitmap of the size we need gl.bitmap.allocate(gl.bmwidth, m_height); @@ -283,55 +720,55 @@ void render_font::char_expand(char32_t chnum, glyph &gl) // extract the data const char *ptr = gl.rawdata; - u8 accum = 0, accumbit = 7; - for (int y = 0; y < gl.bmheight; y++) + u8 accum(0), accumbit(7); + for (int y = 0; y < gl.bmheight; ++y) { - int desty = y + m_height + m_yoffs - gl.yoffs - gl.bmheight; - u32 *dest = (desty >= 0 && desty < m_height) ? &gl.bitmap.pix32(desty) : nullptr; + int const desty(y + m_height + m_yoffs - gl.yoffs - gl.bmheight); + u32 *dest(((0 <= desty) && (m_height > desty)) ? &gl.bitmap.pix32(desty) : nullptr); - // text format - if (m_format == FF_TEXT) + if (m_format == format::TEXT) { - // loop over bytes - for (int x = 0; x < gl.bmwidth; x += 4) + if (dest) { - // scan for the next hex digit - int bits = -1; - while (*ptr != 13 && bits == -1) + for (int x = 0; gl.bmwidth > x; ) { - if (*ptr >= '0' && *ptr <= '9') - bits = *ptr++ - '0'; - else if (*ptr >= 'A' && *ptr <= 'F') - bits = *ptr++ - 'A' + 10; - else if (*ptr >= 'a' && *ptr <= 'f') - bits = *ptr++ - 'a' + 10; - else - ptr++; - } + // scan for the next hex digit + int bits = -1; + while (('\r' != *ptr) && ('\n' != *ptr) && (0 > bits)) + { + if (*ptr >= '0' && *ptr <= '9') + bits = *ptr++ - '0'; + else if (*ptr >= 'A' && *ptr <= 'F') + bits = *ptr++ - 'A' + 10; + else if (*ptr >= 'a' && *ptr <= 'f') + bits = *ptr++ - 'a' + 10; + else + ptr++; + } - // expand the four bits - if (dest != nullptr) - { - *dest++ = (bits & 8) ? color : rgb_t(0x00,0xff,0xff,0xff); - *dest++ = (bits & 4) ? color : rgb_t(0x00,0xff,0xff,0xff); - *dest++ = (bits & 2) ? color : rgb_t(0x00,0xff,0xff,0xff); - *dest++ = (bits & 1) ? color : rgb_t(0x00,0xff,0xff,0xff); + // expand the four bits + *dest++ = (bits & 8) ? fgcol : bgcol; + if (gl.bmwidth > ++x) + *dest++ = (bits & 4) ? fgcol : bgcol; + if (gl.bmwidth > ++x) + *dest++ = (bits & 2) ? fgcol : bgcol; + if (gl.bmwidth > ++x) + *dest++ = (bits & 1) ? fgcol : bgcol; + ++x; } } // advance to the next line ptr = next_line(ptr); } - - // cached format - else if (m_format == FF_CACHED) + else if (m_format == format::CACHED) { for (int x = 0; x < gl.bmwidth; x++) { if (accumbit == 7) accum = *ptr++; if (dest != nullptr) - *dest++ = (accum & (1 << accumbit)) ? color : rgb_t(0x00,0xff,0xff,0xff); + *dest++ = (accum & (1 << accumbit)) ? fgcol : bgcol; accumbit = (accumbit - 1) & 7; } } @@ -479,27 +916,45 @@ float render_font::utf8string_width(float height, float aspect, const char *utf8 bool render_font::load_cached_bdf(const char *filename) { + osd_file::error filerr; + u32 chunk; + u64 bytes; + // first try to open the BDF itself emu_file file(m_manager.machine().options().font_path(), OPEN_FLAG_READ); - osd_file::error filerr = file.open(filename); + filerr = file.open(filename); if (filerr != osd_file::error::NONE) return false; // determine the file size and allocate memory - m_rawsize = file.size(); - m_rawdata.resize(m_rawsize + 1); - - // read the first chunk - u32 bytes = file.read(&m_rawdata[0], std::min(CACHED_BDF_HASH_SIZE, m_rawsize)); - if (bytes != std::min(CACHED_BDF_HASH_SIZE, m_rawsize)) + try + { + m_rawsize = file.size(); + std::vector<char>::size_type const sz(m_rawsize + 1); + if (u64(sz) != (m_rawsize + 1)) + return false; + m_rawdata.resize(sz); + } + catch (...) + { return false; + } - // has the chunk - u32 hash = core_crc32(0, (const u8 *)&m_rawdata[0], bytes) ^ u32(m_rawsize); + // read the first chunk and hash it + chunk = u32((std::min<u64>)(CACHED_BDF_HASH_SIZE, m_rawsize)); + bytes = file.read(&m_rawdata[0], chunk); + if (bytes != chunk) + { + m_rawdata.clear(); + return false; + } + u32 const hash(core_crc32(0, reinterpret_cast<u8 const *>(&m_rawdata[0]), bytes)); // create the cached filename, changing the 'F' to a 'C' on the extension std::string cachedname(filename); - cachedname.erase(cachedname.length() - 3, 3).append("bdc"); + if ((4U < cachedname.length()) && !core_stricmp(&cachedname[cachedname.length() - 4], ".bdf")) + cachedname.erase(cachedname.length() - 4); + cachedname.append(".bdc"); // attempt to open the cached version of the font { @@ -508,38 +963,36 @@ bool render_font::load_cached_bdf(const char *filename) if (filerr == osd_file::error::NONE) { // if we have a cached version, load it - bool result = load_cached(cachefile, hash); + bool const result = load_cached(cachefile, m_rawsize, hash); // if that worked, we're done if (result) - { - // don't do that - glyphs data point into this array ... - // m_rawdata.reset(); return true; - } } } - // read in the rest of the font - if (bytes < m_rawsize) + // read in the rest of the font and NUL-terminate it + while (bytes < m_rawsize) { - u32 read = file.read(&m_rawdata[bytes], m_rawsize - bytes); - if (read != m_rawsize - bytes) + chunk = u32((std::min<u64>)(std::numeric_limits<u32>::max(), m_rawsize - bytes)); + u32 const read(file.read(&m_rawdata[bytes], chunk)); + bytes += read; + if (read != chunk) { m_rawdata.clear(); return false; } } - - // NULL-terminate the data and attach it to the font - m_rawdata[m_rawsize] = 0; + m_rawdata[m_rawsize] = '\0'; // load the BDF - bool result = load_bdf(); + bool const result = load_bdf(); // if we loaded okay, create a cached one if (result) - save_cached(cachedname.c_str(), hash); + save_cached(cachedname.c_str(), m_rawsize, hash); + else + m_rawdata.clear(); // close the file return result; @@ -553,109 +1006,344 @@ bool render_font::load_cached_bdf(const char *filename) bool render_font::load_bdf() { // set the format to text - m_format = FF_TEXT; + m_format = format::TEXT; - // first find the FONTBOUNDINGBOX tag - const char *ptr; - for (ptr = &m_rawdata[0]; ptr != nullptr; ptr = next_line(ptr)) + bdf_helper<std::vector<char>::const_iterator> helper(std::cbegin(m_rawdata), std::cend(m_rawdata)); + + // the first thing we want to see is the STARTFONT declaration, failing that we can't do much + for ( ; !helper.is_keyword("STARTFONT"); helper.next_line()) + { + if (helper.at_end()) + { + osd_printf_error("render_font::load_bdf: expected STARTFONT\n"); + return false; + } + } + + // parse out the global information we need + bool have_bbox(false); + bool have_properties(false); + bool have_defchar(false); + for (helper.next_line(); !helper.is_keyword("CHARS"); helper.next_line()) { - // we only care about a tiny few fields - if (strncmp(ptr, "FONTBOUNDINGBOX ", 16) == 0) + if (helper.at_end()) { - int dummy1, dummy2; - if (sscanf(ptr + 16, "%d %d %d %d", &dummy1, &m_height, &dummy2, &m_yoffs) != 4) + // font with no characters is useless + osd_printf_error("render_font::load_bdf: no glyph section found\n"); + return false; + } + else if (helper.is_keyword("FONTBOUNDINGBOX")) + { + // check for duplicate bounding box + if (have_bbox) + { + osd_printf_error("render_font::load_bdf: found additional bounding box \"%.*s\"\n", int(helper.value_length()), &*helper.value_begin()); return false; - break; + } + have_bbox = true; + + // parse bounding box and check that it's at least half sane + int width, xoffs; + if (4 == sscanf(&*helper.value_begin(), "%d %d %d %d", &width, &m_height, &xoffs, &m_yoffs)) + { + LOG("render_font::load_bdf: got bounding box %dx%d %d,%d\n", width, m_height, xoffs, m_yoffs); + if ((0 >= m_height) || (0 >= width)) + { + osd_printf_error("render_font::load_bdf: bounding box is invalid\n"); + return false; + } + } + else + { + osd_printf_error("render_font::load_bdf: failed to parse bounding box \"%.*s\"\n", int(helper.value_length()), &*helper.value_begin()); + return false; + } + } + else if (helper.is_keyword("STARTPROPERTIES")) + { + // check for duplicated properties section + if (have_properties) + { + osd_printf_error("render_font::load_bdf: found additional properties\n"); + return false; + } + have_properties = true; + + // get property count for sanity check + int propcount; + if (1 != sscanf(&*helper.value_begin(), "%d", &propcount)) + { + osd_printf_error("render_font::load_bdf: failed to parse property count \"%.*s\"\n", int(helper.value_length()), &*helper.value_begin()); + return false; + } + + int actual(0); + for (helper.next_line(); !helper.is_keyword("ENDPROPERTIES"); helper.next_line()) + { + ++actual; + if (helper.at_end()) + { + // unterminated properties section + osd_printf_error("render_font::load_bdf: end of properties not found\n"); + return false; + } + else if (helper.is_keyword("DEFAULT_CHAR")) + { + // check for duplicate default character + if (have_defchar) + { + osd_printf_error("render_font::load_bdf: found additional default character \"%.*s\"\n", int(helper.value_length()), &*helper.value_begin()); + return false; + } + have_defchar = true; + + // parse default character + if (1 == sscanf(&*helper.value_begin(), "%d", &m_defchar)) + { + LOG("render_font::load_bdf: got default character 0x%x\n", m_defchar); + } + else + { + osd_printf_error("render_font::load_bdf: failed to parse default character \"%.*s\"\n", int(helper.value_length()), &*helper.value_begin()); + return false; + } + } + } + + // sanity check on number of properties + if (actual != propcount) + { + osd_printf_error("render_font::load_bdf: incorrect number of properties %d\n", actual); + return false; + } } } // compute the scale factor - m_scale = 1.0f / (float)m_height; + if (!have_bbox) + { + osd_printf_error("render_font::load_bdf: no bounding box found\n"); + return false; + } + m_scale = 1.0f / float(m_height); + + // get expected character count + int expected; + if (1 == sscanf(&*helper.value_begin(), "%d", &expected)) + { + LOG("render_font::load_bdf: got character count %d\n", expected); + } + else + { + osd_printf_error("render_font::load_bdf: failed to parse character count \"%.*s\"\n", int(helper.value_length()), &*helper.value_begin()); + return false; + } // now scan for characters + auto const nothex([] (char ch) { return (('0' > ch) || ('9' < ch)) && (('A' > ch) || ('Z' < ch)) && (('a' > ch) || ('z' < ch)); }); int charcount = 0; - for ( ; ptr != nullptr; ptr = next_line(ptr)) + for (helper.next_line(); !helper.is_keyword("ENDFONT"); helper.next_line()) { - // stop at ENDFONT - if (strncmp(ptr, "ENDFONT", 7) == 0) - break; - - // once we hit a STARTCHAR, parse until the end - if (strncmp(ptr, "STARTCHAR ", 10) == 0) + if (helper.at_end()) { - int bmwidth = -1, bmheight = -1, xoffs = -1, yoffs = -1; - const char *rawdata = nullptr; - int charnum = -1; - int width = -1; - - // scan for interesting per-character tags - for ( ; ptr != nullptr; ptr = next_line(ptr)) + // unterminated font + osd_printf_error("render_font::load_bdf: end of font not found\n"); + return false; + } + else if (helper.is_keyword("STARTCHAR")) + { + // required glyph properties + bool have_encoding(false); + bool have_advance(false); + bool have_bbounds(false); + int encoding(-1); + int xadvance(-1); + int bbw(-1), bbh(-1), bbxoff(-1), bbyoff(-1); + + // stuff for the bitmap data + bool in_bitmap(false); + int bitmap_rows(0); + char const *bitmap_data(nullptr); + + // parse a glyph + for (helper.next_line(); !helper.is_keyword("ENDCHAR"); helper.next_line()) { - // ENCODING tells us which character - if (strncmp(ptr, "ENCODING ", 9) == 0) + if (helper.at_end()) { - if (sscanf(ptr + 9, "%d", &charnum) != 1) - return 1; + // unterminated glyph + osd_printf_error("render_font::load_bdf: end of glyph not found\n"); + return false; } - - // DWIDTH tells us the width to the next character - else if (strncmp(ptr, "DWIDTH ", 7) == 0) + else if (in_bitmap) { - int dummy1; - if (sscanf(ptr + 7, "%d %d", &width, &dummy1) != 2) - return 1; + // quick sanity check + if ((2 * ((bbw + 7) / 8)) != helper.keyword_length()) + { + osd_printf_error("render_font::load_bdf: incorrect length for bitmap line \"%.*s\"\n", int(helper.keyword_length()), &*helper.keyword_begin()); + return false; + } + else if (std::find_if(helper.keyword_begin(), helper.keyword_end(), nothex) != helper.keyword_end()) + { + osd_printf_error("render_font::load_bdf: found invalid character in bitmap line \"%.*s\"\n", int(helper.keyword_length()), &*helper.keyword_begin()); + return false; + } + + // track number of rows + if (1 == ++bitmap_rows) + bitmap_data = &*helper.keyword_begin(); + } + else if (helper.is_keyword("ENCODING")) + { + // check for duplicate glyph encoding + if (have_encoding) + { + osd_printf_error("render_font::load_bdf: found additional glyph encoding \"%.*s\"\n", int(helper.value_length()), &*helper.value_begin()); + return false; + } + have_encoding = true; - // BBX tells us the height/width of the bitmap and the offsets - else if (strncmp(ptr, "BBX ", 4) == 0) + // need to support Adobe Standard Encoding "123" and non-standard glyph index "-1 123" + std::string const value(helper.value_begin(), helper.value_end()); + int aux; + int const cnt(sscanf(value.c_str(), "%d %d", &encoding, &aux)); + if ((2 == cnt) && (-1 == encoding) && (0 <= aux)) + { + encoding = aux; + } + else if ((1 != cnt) || (0 > encoding)) + { + osd_printf_error("render_font::load_bdf: failed to parse glyph encoding \"%.*s\"\n", int(helper.value_length()), &*helper.value_begin()); + return false; + } + LOG("render_font::load_bdf: got glyph encoding %d\n", encoding); + } + else if (helper.is_keyword("DWIDTH")) { - if (sscanf(ptr + 4, "%d %d %d %d", &bmwidth, &bmheight, &xoffs, &yoffs) != 4) - return 1; + // check for duplicate advance + if (have_advance) + { + osd_printf_error("render_font::load_bdf: found additional pixel width \"%.*s\"\n", int(helper.value_length()), &*helper.value_begin()); + return false; + } + have_advance = true; + + // completely ignore vertical advance + int yadvance; + if (2 == sscanf(&*helper.value_begin(), "%d %d", &xadvance, &yadvance)) + { + LOG("render_font::load_bdf: got pixel width %d,%d\n", xadvance, yadvance); + } + else + { + osd_printf_error("render_font::load_bdf: failed to parse pixel width \"%.*s\"\n", int(helper.value_length()), &*helper.value_begin()); + return false; + } } + else if (helper.is_keyword("BBX")) + { + // check for duplicate black pixel box + if (have_bbounds) + { + osd_printf_error("render_font::load_bdf: found additional pixel width \"%.*s\"\n", int(helper.value_length()), &*helper.value_begin()); + return false; + } + have_bbounds = true; - // BITMAP is the start of the data - else if (strncmp(ptr, "BITMAP", 6) == 0) + // extract position/size of black pixel area + if (4 == sscanf(&*helper.value_begin(), "%d %d %d %d", &bbw, &bbh, &bbxoff, &bbyoff)) + { + LOG("render_font::load_bdf: got black pixel box %dx%d %d,%d\n", bbw, bbh, bbxoff, bbyoff); + if ((0 > bbw) || (0 > bbh)) + { + osd_printf_error("render_font::load_bdf: black pixel box is invalid\n"); + return false; + } + } + else + { + osd_printf_error("render_font::load_bdf: failed to parse black pixel box \"%.*s\"\n", int(helper.value_length()), &*helper.value_begin()); + return false; + } + } + else if (helper.is_keyword("BITMAP")) { - // stash the raw pointer and scan for the end of the character - for (rawdata = ptr = next_line(ptr); ptr != nullptr && strncmp(ptr, "ENDCHAR", 7) != 0; ptr = next_line(ptr)) { } - break; + // this is the bitmap - we need to already have properties before we get here + if (!have_advance) + { + osd_printf_error("render_font::load_bdf: glyph has no pixel width\n"); + return false; + } + else if (!have_bbounds) + { + osd_printf_error("render_font::load_bdf: glyph has no black pixel box\n"); + return false; + } + in_bitmap = true; } } - // if we have everything, allocate a new character - if (charnum >= 0 && charnum < (256 * ARRAY_LENGTH(m_glyphs)) && rawdata != nullptr && bmwidth >= 0 && bmheight >= 0) + // now check that we have what we need + if (!in_bitmap) + { + osd_printf_error("render_font::load_bdf: glyph has no bitmap\n"); + return false; + } + else if (bitmap_rows != bbh) + { + osd_printf_error("render_font::load_bdf: incorrect number of bitmap lines %d\n", bitmap_rows); + return false; + } + + // some kinds of characters will screw us up + if (0 > xadvance) + { + LOG("render_font::load_bdf: ignoring character with negative x advance\n"); + } + else if ((256 * ARRAY_LENGTH(m_glyphs)) <= encoding) + { + LOG("render_font::load_bdf: ignoring character with encoding outside range\n"); + } + else { // if we don't have a subtable yet, make one - if (!m_glyphs[charnum / 256]) - m_glyphs[charnum / 256] = new glyph[256]; + if (!m_glyphs[encoding / 256]) + { + try + { + m_glyphs[encoding / 256] = new glyph[256]; + } + catch (...) + { + osd_printf_error("render_font::load_bdf: allocation failed\n"); + return false; + } + } // fill in the entry - glyph &gl = m_glyphs[charnum / 256][charnum % 256]; - gl.width = width; - gl.bmwidth = bmwidth; - gl.bmheight = bmheight; - gl.xoffs = xoffs; - gl.yoffs = yoffs; - gl.rawdata = rawdata; + glyph &gl = m_glyphs[encoding / 256][encoding % 256]; + gl.width = xadvance; + gl.bmwidth = bbw; + gl.bmheight = bbh; + gl.xoffs = bbxoff; + gl.yoffs = bbyoff; + gl.rawdata = bitmap_data; } // some progress for big fonts - if (++charcount % 256 == 0) + if (0 == (++charcount % 256)) osd_printf_warning("Loading BDF font... (%d characters loaded)\n", charcount); } } - // make sure all the numbers are the same width - if (m_glyphs[0]) + // check number of characters + if (expected != charcount) { - int maxwidth = 0; - for (int ch = '0'; ch <= '9'; ch++) - if (m_glyphs[0][ch].bmwidth > maxwidth) - maxwidth = m_glyphs[0][ch].width; - for (int ch = '0'; ch <= '9'; ch++) - m_glyphs[0][ch].width = maxwidth; + osd_printf_error("render_font::load_bdf: incorrect character count %d\n", charcount); + return false; } + // should have bailed by now if something went wrong return true; } @@ -664,69 +1352,104 @@ bool render_font::load_bdf() // load_cached - load a font in cached format //------------------------------------------------- -bool render_font::load_cached(emu_file &file, u32 hash) +bool render_font::load_cached(emu_file &file, u64 length, u32 hash) { - // get the file size - u64 filesize = file.size(); - - // first read the header - u8 header[CACHED_HEADER_SIZE]; - u32 bytes_read = file.read(header, CACHED_HEADER_SIZE); - if (bytes_read != CACHED_HEADER_SIZE) + // get the file size, read the header, and check that it looks good + u64 const filesize(file.size()); + bdc_header header; + if (!header.read(file)) + { + osd_printf_warning("render_font::load_cached: error reading BDC header\n"); return false; - - // validate the header - if (header[0] != 'f' || header[1] != 'o' || header[2] != 'n' || header[3] != 't') + } + else if (!header.check_magic() || (bdc_header::MAJVERSION != header.get_major_version()) || (bdc_header::MINVERSION != header.get_minor_version())) + { + LOG("render_font::load_cached: incompatible BDC file\n"); return false; - if (hash && (header[4] != u8(hash >> 24) || header[5] != u8(hash >> 16) || header[6] != u8(hash >> 8) || header[7] != u8(hash))) + } + else if (length && ((header.get_original_length() != length) || (header.get_original_hash() != hash))) + { + LOG("render_font::load_cached: BDC file does not match original BDF file\n"); return false; - m_height = (header[8] << 8) | header[9]; - m_scale = 1.0f / (float)m_height; - m_yoffs = s16((header[10] << 8) | header[11]); - u32 numchars = (header[12] << 24) | (header[13] << 16) | (header[14] << 8) | header[15]; - if (filesize - CACHED_HEADER_SIZE < numchars * CACHED_CHAR_SIZE) + } + + // get global properties from the header + m_height = header.get_height(); + m_scale = 1.0f / float(m_height); + m_yoffs = header.get_y_offset(); + m_defchar = header.get_default_character(); + u32 const numchars(header.get_glyph_count()); + if ((file.tell() + (u64(numchars) * bdc_table_entry::size())) > filesize) + { + LOG("render_font::load_cached: BDC file is too small to hold glyph table\n"); return false; + } // now read the rest of the data - m_rawdata.resize(filesize - CACHED_HEADER_SIZE); - bytes_read = file.read(&m_rawdata[0], filesize - CACHED_HEADER_SIZE); - if (bytes_read != filesize - CACHED_HEADER_SIZE) + u64 const remaining(filesize - file.tell()); + try { - m_rawdata.clear(); - return false; + m_rawdata.resize(std::size_t(remaining)); + } + catch (...) + { + osd_printf_error("render_font::load_cached: allocation error\n"); + } + for (u64 bytes_read = 0; remaining > bytes_read; ) + { + u32 const chunk((std::min)(u64(std::numeric_limits<u32>::max()), remaining)); + if (file.read(&m_rawdata[bytes_read], chunk) != chunk) + { + osd_printf_error("render_font::load_cached: error reading BDC data\n"); + m_rawdata.clear(); + return false; + } + bytes_read += chunk; } // extract the data from the data - u64 offset = numchars * CACHED_CHAR_SIZE; - for (int chindex = 0; chindex < numchars; chindex++) + std::size_t offset(std::size_t(numchars) * bdc_table_entry::size()); + bdc_table_entry entry(m_rawdata.empty() ? nullptr : &m_rawdata[0]); + for (unsigned chindex = 0; chindex < numchars; chindex++, entry = entry.get_next()) { - const u8 *info = reinterpret_cast<u8 *>(&m_rawdata[chindex * CACHED_CHAR_SIZE]); - int chnum = (info[0] << 8) | info[1]; - // if we don't have a subtable yet, make one + int const chnum(entry.get_encoding()); + LOG("render_font::load_cached: loading character %d\n", chnum); if (!m_glyphs[chnum / 256]) - m_glyphs[chnum / 256] = new glyph[256]; + { + try + { + m_glyphs[chnum / 256] = new glyph[256]; + } + catch (...) + { + osd_printf_error("render_font::load_cached: allocation error\n"); + m_rawdata.clear(); + return false; + } + } // fill in the entry glyph &gl = m_glyphs[chnum / 256][chnum % 256]; - gl.width = (info[2] << 8) | info[3]; - gl.xoffs = s16((info[4] << 8) | info[5]); - gl.yoffs = s16((info[6] << 8) | info[7]); - gl.bmwidth = (info[8] << 8) | info[9]; - gl.bmheight = (info[10] << 8) | info[11]; + gl.width = entry.get_x_advance(); + gl.xoffs = entry.get_bb_x_offset(); + gl.yoffs = entry.get_bb_y_offset(); + gl.bmwidth = entry.get_bb_width(); + gl.bmheight = entry.get_bb_height(); gl.rawdata = &m_rawdata[offset]; // advance the offset past the character offset += (gl.bmwidth * gl.bmheight + 7) / 8; - if (offset > filesize - CACHED_HEADER_SIZE) + if (m_rawdata.size() < offset) { + osd_printf_verbose("render_font::load_cached: BDC file too small to hold all glyphs\n"); m_rawdata.clear(); return false; } } - // reuse the chartable as a temporary buffer - m_format = FF_CACHED; + // got everything + m_format = format::CACHED; return true; } @@ -735,76 +1458,73 @@ bool render_font::load_cached(emu_file &file, u32 hash) // save_cached - save a font in cached format //------------------------------------------------- -bool render_font::save_cached(const char *filename, u32 hash) +bool render_font::save_cached(const char *filename, u64 length, u32 hash) { osd_printf_warning("Generating cached BDF font...\n"); // attempt to open the file emu_file file(m_manager.machine().options().font_path(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE); - osd_file::error filerr = file.open(filename); - if (filerr != osd_file::error::NONE) + osd_file::error const filerr = file.open(filename); + if (osd_file::error::NONE != filerr) return false; - // determine the number of characters - int numchars = 0; - for (int chnum = 0; chnum < (256 * ARRAY_LENGTH(m_glyphs)); chnum++) + // count glyphs + unsigned numchars = 0; + for (glyph const *const page : m_glyphs) { - if (m_glyphs[chnum / 256]) + for (unsigned chnum = 0; page && (256 > chnum); ++chnum) { - glyph &gl = m_glyphs[chnum / 256][chnum % 256]; - if (gl.width > 0) - numchars++; + if (0 < page[chnum].width) + ++numchars; } } + LOG("render_font::save_cached: %u glyphs with positive advance to save\n", numchars); try { + u32 bytes_written; + + { + LOG("render_font::save_cached: writing header\n"); + bdc_header hdr; + hdr.set_magic(); + hdr.set_version(); + hdr.set_original_length(length); + hdr.set_original_hash(hash); + hdr.set_glyph_count(numchars); + hdr.set_height(m_height); + hdr.set_y_offset(m_yoffs); + hdr.set_default_character(m_defchar); + if (!hdr.write(file)) + throw emu_fatalerror("Error writing cached file"); + } + u64 const table_offs(file.tell()); + // allocate an array to hold the character data - std::vector<u8> chartable(numchars * CACHED_CHAR_SIZE, 0); + std::vector<u8> chartable(std::size_t(numchars) * bdc_table_entry::size(), 0); // allocate a temp buffer to compress into std::vector<u8> tempbuffer(65536); - // write the header - u8 *dest = &tempbuffer[0]; - *dest++ = 'f'; - *dest++ = 'o'; - *dest++ = 'n'; - *dest++ = 't'; - *dest++ = hash >> 24; - *dest++ = hash >> 16; - *dest++ = hash >> 8; - *dest++ = hash & 0xff; - *dest++ = m_height >> 8; - *dest++ = m_height & 0xff; - *dest++ = m_yoffs >> 8; - *dest++ = m_yoffs & 0xff; - *dest++ = numchars >> 24; - *dest++ = numchars >> 16; - *dest++ = numchars >> 8; - *dest++ = numchars & 0xff; - assert(dest == &tempbuffer[CACHED_HEADER_SIZE]); - u32 bytes_written = file.write(&tempbuffer[0], CACHED_HEADER_SIZE); - if (bytes_written != dest - &tempbuffer[0]) - throw emu_fatalerror("Error writing cached file"); - // write the empty table to the beginning of the file - bytes_written = file.write(&chartable[0], numchars * CACHED_CHAR_SIZE); - if (bytes_written != numchars * CACHED_CHAR_SIZE) + bytes_written = file.write(&chartable[0], chartable.size()); + if (bytes_written != chartable.size()) throw emu_fatalerror("Error writing cached file"); // loop over all characters - int tableindex = 0; - for (int chnum = 0; chnum < (256 * ARRAY_LENGTH(m_glyphs)); chnum++) + bdc_table_entry table_entry(chartable.empty() ? nullptr : &chartable[0]); + for (unsigned chnum = 0; chnum < (256 * ARRAY_LENGTH(m_glyphs)); chnum++) { - glyph &gl = get_char(chnum); - if (gl.width > 0) + if (m_glyphs[chnum / 256] && (0 < m_glyphs[chnum / 256][chnum % 256].width)) { + LOG("render_font::save_cached: writing glyph %u\n", chnum); + glyph &gl(get_char(chnum)); + // write out a bit-compressed bitmap if we have one if (gl.bitmap.valid()) { // write the data to the tempbuffer - dest = &tempbuffer[0]; + u8 *dest = &tempbuffer[0]; u8 accum = 0; u8 accbit = 7; @@ -842,27 +1562,34 @@ bool render_font::save_cached(const char *filename, u32 hash) } // compute the table entry - dest = &chartable[tableindex++ * CACHED_CHAR_SIZE]; - *dest++ = chnum >> 8; - *dest++ = chnum & 0xff; - *dest++ = gl.width >> 8; - *dest++ = gl.width & 0xff; - *dest++ = gl.xoffs >> 8; - *dest++ = gl.xoffs & 0xff; - *dest++ = gl.yoffs >> 8; - *dest++ = gl.yoffs & 0xff; - *dest++ = gl.bmwidth >> 8; - *dest++ = gl.bmwidth & 0xff; - *dest++ = gl.bmheight >> 8; - *dest++ = gl.bmheight & 0xff; + table_entry.set_encoding(chnum); + table_entry.set_x_advance(gl.width); + table_entry.set_bb_x_offset(gl.xoffs); + table_entry.set_bb_y_offset(gl.yoffs); + table_entry.set_bb_width(gl.bmwidth); + table_entry.set_bb_height(gl.bmheight); + table_entry = table_entry.get_next(); } } // seek back to the beginning and rewrite the table - file.seek(CACHED_HEADER_SIZE, SEEK_SET); - bytes_written = file.write(&chartable[0], numchars * CACHED_CHAR_SIZE); - if (bytes_written != numchars * CACHED_CHAR_SIZE) - throw emu_fatalerror("Error writing cached file"); + if (!chartable.empty()) + { + LOG("render_font::save_cached: writing character table\n"); + file.seek(table_offs, SEEK_SET); + u8 const *bytes(&chartable[0]); + for (u64 remaining = chartable.size(); remaining; ) + { + u32 const chunk((std::min<u64>)(std::numeric_limits<u32>::max(), remaining)); + bytes_written = file.write(bytes, chunk); + if (chunk != bytes_written) + throw emu_fatalerror("Error writing cached file"); + bytes += chunk; + remaining -= chunk; + } + } + + // no trouble? return true; } catch (...) @@ -871,3 +1598,99 @@ bool render_font::save_cached(const char *filename, u32 hash) return false; } } + + +void render_font::render_font_command_glyph() +{ + // FIXME: this is copy/pasta from the BDC loading, and it shouldn't be injected into every font + emu_file file(OPEN_FLAG_READ); + if (file.open_ram(font_uicmd14, sizeof(font_uicmd14)) == osd_file::error::NONE) + { + // get the file size, read the header, and check that it looks good + u64 const filesize(file.size()); + bdc_header header; + if (!header.read(file)) + { + osd_printf_warning("render_font::render_font_command_glyph: error reading BDC header\n"); + return; + } + else if (!header.check_magic() || (bdc_header::MAJVERSION != header.get_major_version()) || (bdc_header::MINVERSION != header.get_minor_version())) + { + LOG("render_font::render_font_command_glyph: incompatible BDC file\n"); + return; + } + + // get global properties from the header + m_height_cmd = header.get_height(); + m_yoffs_cmd = header.get_y_offset(); + u32 const numchars(header.get_glyph_count()); + if ((file.tell() + (u64(numchars) * bdc_table_entry::size())) > filesize) + { + LOG("render_font::render_font_command_glyph: BDC file is too small to hold glyph table\n"); + return; + } + + // now read the rest of the data + u64 const remaining(filesize - file.tell()); + try + { + m_rawdata_cmd.resize(std::size_t(remaining)); + } + catch (...) + { + osd_printf_error("render_font::render_font_command_glyph: allocation error\n"); + } + for (u64 bytes_read = 0; remaining > bytes_read; ) + { + u32 const chunk((std::min)(u64(std::numeric_limits<u32>::max()), remaining)); + if (file.read(&m_rawdata_cmd[bytes_read], chunk) != chunk) + { + osd_printf_error("render_font::render_font_command_glyph: error reading BDC data\n"); + m_rawdata_cmd.clear(); + return; + } + bytes_read += chunk; + } + + // extract the data from the data + std::size_t offset(std::size_t(numchars) * bdc_table_entry::size()); + bdc_table_entry entry(m_rawdata_cmd.empty() ? nullptr : &m_rawdata_cmd[0]); + for (unsigned chindex = 0; chindex < numchars; chindex++, entry = entry.get_next()) + { + // if we don't have a subtable yet, make one + int const chnum(entry.get_encoding()); + LOG("render_font::render_font_command_glyph: loading character %d\n", chnum); + if (!m_glyphs_cmd[chnum / 256]) + { + try + { + m_glyphs_cmd[chnum / 256] = new glyph[256]; + } + catch (...) + { + osd_printf_error("render_font::render_font_command_glyph: allocation error\n"); + m_rawdata_cmd.clear(); + return; + } + } + + // fill in the entry + glyph &gl = m_glyphs_cmd[chnum / 256][chnum % 256]; + gl.width = entry.get_x_advance(); + gl.xoffs = entry.get_bb_x_offset(); + gl.yoffs = entry.get_bb_y_offset(); + gl.bmwidth = entry.get_bb_width(); + gl.bmheight = entry.get_bb_height(); + gl.rawdata = &m_rawdata_cmd[offset]; + + // advance the offset past the character + offset += (gl.bmwidth * gl.bmheight + 7) / 8; + if (m_rawdata_cmd.size() < offset) + { + osd_printf_verbose("render_font::render_font_command_glyph: BDC file too small to hold all glyphs\n"); + m_rawdata_cmd.clear(); + return; + } + } + } +} diff --git a/src/emu/rendfont.h b/src/emu/rendfont.h index 558b5c55aa5..589388855a6 100644 --- a/src/emu/rendfont.h +++ b/src/emu/rendfont.h @@ -1,5 +1,5 @@ // license:BSD-3-Clause -// copyright-holders:Aaron Giles +// copyright-holders:Aaron Giles, Vas Crabb /*************************************************************************** rendfont.h @@ -8,8 +8,8 @@ ***************************************************************************/ -#ifndef __RENDFONT_H__ -#define __RENDFONT_H__ +#ifndef MAME_EMU_RENDFONT_H +#define MAME_EMU_RENDFONT_H #include "render.h" @@ -53,11 +53,15 @@ private: { public: glyph() - : width(0), - xoffs(0), yoffs(0), - bmwidth(0), bmheight(0), - rawdata(nullptr), - texture(nullptr) { } + : width(-1) + , xoffs(-1), yoffs(-1) + , bmwidth(0), bmheight(0) + , rawdata(nullptr) + , texture(nullptr) + , bitmap() + , color() + { + } s32 width; // width from this character to the next s32 xoffs, yoffs; // X and Y offset from baseline to top,left of bitmap @@ -67,16 +71,15 @@ private: bitmap_argb32 bitmap; // pointer to the bitmap containing the raw data rgb_t color; - }; // internal format - enum format + enum class format { - FF_UNKNOWN, - FF_TEXT, - FF_CACHED, - FF_OSD + UNKNOWN, + TEXT, + CACHED, + OSD }; // helpers @@ -84,9 +87,8 @@ private: void char_expand(char32_t chnum, glyph &ch); bool load_cached_bdf(const char *filename); bool load_bdf(); - bool load_cached(emu_file &file, u32 hash); - bool load_cached_cmd(emu_file &file, u32 hash); - bool save_cached(const char *filename, u32 hash); + bool load_cached(emu_file &file, u64 length, u32 hash); + bool save_cached(const char *filename, u64 length, u32 hash); void render_font_command_glyph(); @@ -95,6 +97,7 @@ private: format m_format; // format of font data int m_height; // height of the font, from ascent to descent int m_yoffs; // y offset from baseline to descent + int m_defchar; // default substitute character float m_scale; // 1 / height precomputed glyph *m_glyphs[17*256]; // array of glyph subtables std::vector<char> m_rawdata; // pointer to the raw data for the font @@ -107,11 +110,9 @@ private: std::vector<char> m_rawdata_cmd; // pointer to the raw data for the font // constants - static const int CACHED_CHAR_SIZE = 12; - static const int CACHED_HEADER_SIZE = 16; static const u64 CACHED_BDF_HASH_SIZE = 1024; }; void convert_command_glyph(std::string &s); -#endif /* __RENDFONT_H__ */ +#endif /* MAME_EMU_RENDFONT_H */ diff --git a/src/emu/ui/cmddata.h b/src/emu/ui/cmddata.h index 14c09c73883..675195f7c76 100644 --- a/src/emu/ui/cmddata.h +++ b/src/emu/ui/cmddata.h @@ -50,7 +50,7 @@ enum #define COMMAND_CONVERT_TEXT '@' // Defined Game Command Font Color Array -static rgb_t color_table[] = +static rgb_t const color_table[] = { 0, // dummy BUTTON_COLOR_RED, // BTN_A @@ -147,19 +147,19 @@ static rgb_t color_table[] = struct fix_command_t { - unsigned char glyph_char; - const int glyph_code; + char glyph_char; + unsigned glyph_code; }; struct fix_strings_t { - const char *glyph_str; - const int glyph_code; - int glyph_str_len; + char const *glyph_str; + int const glyph_code; + unsigned glyph_str_len; }; -static fix_command_t default_text[] = +static fix_command_t const default_text[] = { // Alphabetic Buttons (NeoGeo): A~D,H,Z { 'A', 1 }, // BTN_A @@ -248,7 +248,7 @@ static fix_command_t default_text[] = { 0, 0 } // end of array }; -static fix_command_t expand_text[] = +static fix_command_t const expand_text[] = { // Alphabetic Buttons (NeoGeo): S (Slash Button) { 's', 19 }, // BTN_S @@ -289,114 +289,114 @@ static fix_command_t expand_text[] = static fix_strings_t convert_text[] = { // Alphabetic Buttons: A~Z - { "A-button", 1 }, // BTN_A - { "B-button", 2 }, // BTN_B - { "C-button", 3 }, // BTN_C - { "D-button", 4 }, // BTN_D - { "E-button", 5 }, // BTN_E - { "F-button", 6 }, // BTN_F - { "G-button", 7 }, // BTN_G - { "H-button", 8 }, // BTN_H - { "I-button", 9 }, // BTN_I - { "J-button", 10 }, // BTN_J - { "K-button", 11 }, // BTN_K - { "L-button", 12 }, // BTN_L - { "M-button", 13 }, // BTN_M - { "N-button", 14 }, // BTN_N - { "O-button", 15 }, // BTN_O - { "P-button", 16 }, // BTN_P - { "Q-button", 17 }, // BTN_Q - { "R-button", 18 }, // BTN_R - { "S-button", 19 }, // BTN_S - { "T-button", 20 }, // BTN_T - { "U-button", 21 }, // BTN_U - { "V-button", 22 }, // BTN_V - { "W-button", 23 }, // BTN_W - { "X-button", 24 }, // BTN_X - { "Y-button", 25 }, // BTN_Y - { "Z-button", 26 }, // BTN_Z + { "A-button", 1, 0 }, // BTN_A + { "B-button", 2, 0 }, // BTN_B + { "C-button", 3, 0 }, // BTN_C + { "D-button", 4, 0 }, // BTN_D + { "E-button", 5, 0 }, // BTN_E + { "F-button", 6, 0 }, // BTN_F + { "G-button", 7, 0 }, // BTN_G + { "H-button", 8, 0 }, // BTN_H + { "I-button", 9, 0 }, // BTN_I + { "J-button", 10, 0 }, // BTN_J + { "K-button", 11, 0 }, // BTN_K + { "L-button", 12, 0 }, // BTN_L + { "M-button", 13, 0 }, // BTN_M + { "N-button", 14, 0 }, // BTN_N + { "O-button", 15, 0 }, // BTN_O + { "P-button", 16, 0 }, // BTN_P + { "Q-button", 17, 0 }, // BTN_Q + { "R-button", 18, 0 }, // BTN_R + { "S-button", 19, 0 }, // BTN_S + { "T-button", 20, 0 }, // BTN_T + { "U-button", 21, 0 }, // BTN_U + { "V-button", 22, 0 }, // BTN_V + { "W-button", 23, 0 }, // BTN_W + { "X-button", 24, 0 }, // BTN_X + { "Y-button", 25, 0 }, // BTN_Y + { "Z-button", 26, 0 }, // BTN_Z // Special Moves and Buttons - { "decrease", 37 }, // BTN_DEC - { "increase", 38 }, // BTN_INC - { "BALL", 45 }, // Joystick Ball - { "start", 51 }, // BTN_START - { "select", 52 }, // BTN_SELECT - { "punch", 53 }, // BTN_PUNCH - { "kick", 54 }, // BTN_KICK - { "guard", 55 }, // BTN_GUARD - { "L-punch", 57 }, // Light Punch - { "M-punch", 58 }, // Middle Punch - { "S-punch", 59 }, // Strong Punch - { "L-kick", 60 }, // Light Kick - { "M-kick", 61 }, // Middle Kick - { "S-kick", 62 }, // Strong Kick - { "3-kick", 63 }, // 3 Kick - { "3-punch", 64 }, // 3 Punch - { "2-kick", 65 }, // 2 Kick - { "2-punch", 66 }, // 2 Pick + { "decrease", 37, 0 }, // BTN_DEC + { "increase", 38, 0 }, // BTN_INC + { "BALL", 45, 0 }, // Joystick Ball + { "start", 51, 0 }, // BTN_START + { "select", 52, 0 }, // BTN_SELECT + { "punch", 53, 0 }, // BTN_PUNCH + { "kick", 54, 0 }, // BTN_KICK + { "guard", 55, 0 }, // BTN_GUARD + { "L-punch", 57, 0 }, // Light Punch + { "M-punch", 58, 0 }, // Middle Punch + { "S-punch", 59, 0 }, // Strong Punch + { "L-kick", 60, 0 }, // Light Kick + { "M-kick", 61, 0 }, // Middle Kick + { "S-kick", 62, 0 }, // Strong Kick + { "3-kick", 63, 0 }, // 3 Kick + { "3-punch", 64, 0 }, // 3 Punch + { "2-kick", 65, 0 }, // 2 Kick + { "2-punch", 66, 0 }, // 2 Pick // Custom Buttons and Cursor Buttons - { "custom1", 67 }, // CUSTOM_1 - { "custom2", 68 }, // CUSTOM_2 - { "custom3", 69 }, // CUSTOM_3 - { "custom4", 70 }, // CUSTOM_4 - { "custom5", 71 }, // CUSTOM_5 - { "custom6", 72 }, // CUSTOM_6 - { "custom7", 73 }, // CUSTOM_7 - { "custom8", 74 }, // CUSTOM_8 - { "up", 75 }, // (Cursor Up) - { "down", 76 }, // (Cursor Down) - { "left", 77 }, // (Cursor Left) - { "right", 78 }, // (Cursor Right) + { "custom1", 67, 0 }, // CUSTOM_1 + { "custom2", 68, 0 }, // CUSTOM_2 + { "custom3", 69, 0 }, // CUSTOM_3 + { "custom4", 70, 0 }, // CUSTOM_4 + { "custom5", 71, 0 }, // CUSTOM_5 + { "custom6", 72, 0 }, // CUSTOM_6 + { "custom7", 73, 0 }, // CUSTOM_7 + { "custom8", 74, 0 }, // CUSTOM_8 + { "up", 75, 0 }, // (Cursor Up) + { "down", 76, 0 }, // (Cursor Down) + { "left", 77, 0 }, // (Cursor Left) + { "right", 78, 0 }, // (Cursor Right) // Player Lever - { "lever", 79 }, // Non Player Lever - { "nplayer", 80 }, // Gray Color Lever - { "1player", 81 }, // 1 Player Lever - { "2player", 82 }, // 2 Player Lever - { "3player", 83 }, // 3 Player Lever - { "4player", 84 }, // 4 Player Lever - { "5player", 85 }, // 5 Player Lever - { "6player", 86 }, // 6 Player Lever - { "7player", 87 }, // 7 Player Lever - { "8player", 88 }, // 8 Player Lever + { "lever", 79, 0 }, // Non Player Lever + { "nplayer", 80, 0 }, // Gray Color Lever + { "1player", 81, 0 }, // 1 Player Lever + { "2player", 82, 0 }, // 2 Player Lever + { "3player", 83, 0 }, // 3 Player Lever + { "4player", 84, 0 }, // 4 Player Lever + { "5player", 85, 0 }, // 5 Player Lever + { "6player", 86, 0 }, // 6 Player Lever + { "7player", 87, 0 }, // 7 Player Lever + { "8player", 88, 0 }, // 8 Player Lever // Composition of Arrow Directions - { "-->", 90 }, // Arrow - { "==>", 91 }, // Continue Arrow - { "hcb", 100 }, // Half Circle Back - { "huf", 101 }, // Half Circle Front Up - { "hcf", 102 }, // Half Circle Front - { "hub", 103 }, // Half Circle Back Up - { "qfd", 104 }, // 1/4 Cir For 2 Down - { "qdb", 105 }, // 1/4 Cir Down 2 Back - { "qbu", 106 }, // 1/4 Cir Back 2 Up - { "quf", 107 }, // 1/4 Cir Up 2 For - { "qbd", 108 }, // 1/4 Cir Back 2 Down - { "qdf", 109 }, // 1/4 Cir Down 2 For - { "qfu", 110 }, // 1/4 Cir For 2 Up - { "qub", 111 }, // 1/4 Cir Up 2 Back - { "fdf", 112 }, // Full Clock Forward - { "fub", 113 }, // Full Clock Back - { "fuf", 114 }, // Full Count Forward - { "fdb", 115 }, // Full Count Back - { "xff", 116 }, // 2x Forward - { "xbb", 117 }, // 2x Back - { "dsf", 118 }, // Dragon Screw Forward - { "dsb", 119 }, // Dragon Screw Back + { "-->", 90, 0 }, // Arrow + { "==>", 91, 0 }, // Continue Arrow + { "hcb", 100, 0 }, // Half Circle Back + { "huf", 101, 0 }, // Half Circle Front Up + { "hcf", 102, 0 }, // Half Circle Front + { "hub", 103, 0 }, // Half Circle Back Up + { "qfd", 104, 0 }, // 1/4 Cir For 2 Down + { "qdb", 105, 0 }, // 1/4 Cir Down 2 Back + { "qbu", 106, 0 }, // 1/4 Cir Back 2 Up + { "quf", 107, 0 }, // 1/4 Cir Up 2 For + { "qbd", 108, 0 }, // 1/4 Cir Back 2 Down + { "qdf", 109, 0 }, // 1/4 Cir Down 2 For + { "qfu", 110, 0 }, // 1/4 Cir For 2 Up + { "qub", 111, 0 }, // 1/4 Cir Up 2 Back + { "fdf", 112, 0 }, // Full Clock Forward + { "fub", 113, 0 }, // Full Clock Back + { "fuf", 114, 0 }, // Full Count Forward + { "fdb", 115, 0 }, // Full Count Back + { "xff", 116, 0 }, // 2x Forward + { "xbb", 117, 0 }, // 2x Back + { "dsf", 118, 0 }, // Dragon Screw Forward + { "dsb", 119, 0 }, // Dragon Screw Back // Big letter Text - { "AIR", 121 }, // AIR - { "DIR", 122 }, // DIR - { "MAX", 123 }, // MAX - { "TAP", 124 }, // TAP + { "AIR", 121, 0 }, // AIR + { "DIR", 122, 0 }, // DIR + { "MAX", 123, 0 }, // MAX + { "TAP", 124, 0 }, // TAP // Condition of Positions - { "jump", 125 }, // Jump - { "hold", 126 }, // Hold - { "air", 127 }, // Air - { "sit", 128 }, // Squatting - { "close", 129 }, // Close - { "away", 130 }, // Away - { "charge", 131 }, // Charge - { "tap", 132 }, // Serious Tap - { "button", 133 }, // Any Button - { nullptr, 0 } // end of array + { "jump", 125, 0 }, // Jump + { "hold", 126, 0 }, // Hold + { "air", 127, 0 }, // Air + { "sit", 128, 0 }, // Squatting + { "close", 129, 0 }, // Close + { "away", 130, 0 }, // Away + { "charge", 131, 0 }, // Charge + { "tap", 132, 0 }, // Serious Tap + { "button", 133, 0 }, // Any Button + { nullptr, 0, 0 } // end of array }; #endif /* __UI_CMDDATA_H__ */ diff --git a/src/emu/ui/cmdrender.h b/src/emu/ui/cmdrender.h deleted file mode 100644 index 905b681f746..00000000000 --- a/src/emu/ui/cmdrender.h +++ /dev/null @@ -1,150 +0,0 @@ -// license:BSD-3-Clause -// copyright-holders:Maurizio Petrarota -/*************************************************************************** - - ui/cmdrender.h - - UI command render fonts. - -***************************************************************************/ - -#include "ui/uicmd14.fh" -#include "ui/cmddata.h" - -void convert_command_glyph(std::string &str) -{ - int j; - int len = str.length(); - int buflen = (len + 2) * 2; - char *d = global_alloc_array(char, buflen); - - for (int i = j = 0; i < len;) - { - fix_command_t *fixcmd = nullptr; - char32_t uchar; - int ucharcount = uchar_from_utf8(&uchar, str.substr(i).c_str(), len - i); - if (ucharcount == -1) - break; - else if (ucharcount != 1) - goto process_next; - else if (str[i] == '\n') - uchar = '\n'; - else if (str[i] == COMMAND_CONVERT_TEXT) - { - if (str[i] == str[i + 1]) - ++i; - else - { - fix_strings_t *fixtext = convert_text; - for (; fixtext->glyph_code; ++fixtext) - { - if (!fixtext->glyph_str_len) - fixtext->glyph_str_len = strlen(fixtext->glyph_str); - - if (strncmp(fixtext->glyph_str, str.substr(i + 1).c_str(), fixtext->glyph_str_len) == 0) - { - uchar = fixtext->glyph_code + COMMAND_UNICODE; - i += strlen(fixtext->glyph_str); - break; - } - } - } - } - else if (str[i] == COMMAND_DEFAULT_TEXT) - fixcmd = default_text; - else if (str[i] == COMMAND_EXPAND_TEXT) - fixcmd = expand_text; - - if (fixcmd) - { - if (str[i] == str[i + 1]) - i++; - else - { - for (; fixcmd->glyph_code; ++fixcmd) - if (str[i + 1] == fixcmd->glyph_char) - { - uchar = fixcmd->glyph_code + COMMAND_UNICODE; - ++i; - break; - } - } - } -process_next: - i += ucharcount; - ucharcount = utf8_from_uchar(d + j, buflen - j - 1, uchar); - if (ucharcount == -1) - break; - j += ucharcount; - } - d[j] = '\0'; - str = d; - global_free_array(d); -} - -void render_font::render_font_command_glyph() -{ - emu_file ramfile(OPEN_FLAG_READ); - - if (ramfile.open_ram(font_uicmd14, sizeof(font_uicmd14)) == osd_file::error::NONE) - load_cached_cmd(ramfile, 0); -} - -bool render_font::load_cached_cmd(emu_file &file, u32 hash) -{ - u64 filesize = file.size(); - u8 header[CACHED_HEADER_SIZE]; - u32 bytes_read = file.read(header, CACHED_HEADER_SIZE); - - if (bytes_read != CACHED_HEADER_SIZE) - return false; - - if (header[0] != 'f' || header[1] != 'o' || header[2] != 'n' || header[3] != 't') - return false; - if (header[4] != u8(hash >> 24) || header[5] != u8(hash >> 16) || header[6] != u8(hash >> 8) || header[7] != u8(hash)) - return false; - m_height_cmd = (header[8] << 8) | header[9]; - m_yoffs_cmd = s16((header[10] << 8) | header[11]); - u32 numchars = (header[12] << 24) | (header[13] << 16) | (header[14] << 8) | header[15]; - if (filesize - CACHED_HEADER_SIZE < numchars * CACHED_CHAR_SIZE) - return false; - - m_rawdata_cmd.resize(filesize - CACHED_HEADER_SIZE); - bytes_read = file.read(&m_rawdata_cmd[0], filesize - CACHED_HEADER_SIZE); - if (bytes_read != filesize - CACHED_HEADER_SIZE) - { - m_rawdata_cmd.clear(); - return false; - } - - u64 offset = numchars * CACHED_CHAR_SIZE; - for (int chindex = 0; chindex < numchars; chindex++) - { - const u8 *info = reinterpret_cast<u8 *>(&m_rawdata_cmd[chindex * CACHED_CHAR_SIZE]); - int chnum = (info[0] << 8) | info[1]; - - if (!m_glyphs_cmd[chnum / 256]) - m_glyphs_cmd[chnum / 256] = new glyph[256]; - - glyph &gl = m_glyphs_cmd[chnum / 256][chnum % 256]; - - if (chnum >= COMMAND_UNICODE && chnum < COMMAND_UNICODE + COLOR_BUTTONS) - gl.color = color_table[chnum - COMMAND_UNICODE]; - - gl.width = (info[2] << 8) | info[3]; - gl.xoffs = s16((info[4] << 8) | info[5]); - gl.yoffs = s16((info[6] << 8) | info[7]); - gl.bmwidth = (info[8] << 8) | info[9]; - gl.bmheight = (info[10] << 8) | info[11]; - gl.rawdata = &m_rawdata_cmd[offset]; - - offset += (gl.bmwidth * gl.bmheight + 7) / 8; - if (offset > filesize - CACHED_HEADER_SIZE) - { - m_rawdata_cmd.clear(); - return false; - } - } - - return true; -} diff --git a/src/frontend/mame/ui/selmenu.cpp b/src/frontend/mame/ui/selmenu.cpp index 84c0300d628..c8e598df443 100644 --- a/src/frontend/mame/ui/selmenu.cpp +++ b/src/frontend/mame/ui/selmenu.cpp @@ -139,12 +139,6 @@ menu_select_launch::cache::~cache() menu_select_launch::~menu_select_launch() { - // need to manually clean up icon textures for now - for (auto &texture : m_icons_texture) - { - if (texture) - machine().render().texture_free(texture); - } } @@ -164,6 +158,7 @@ menu_select_launch::menu_select_launch(mame_ui_manager &mui, render_container &c , m_pressed(false) , m_repeat(0) , m_right_visible_lines(0) + , m_icons(MAX_ICONS_RENDER) { // set up persistent cache for machine run { @@ -181,18 +176,6 @@ menu_select_launch::menu_select_launch(mame_ui_manager &mui, render_container &c add_cleanup_callback(&menu_select_launch::exit); } } - - // initialise icon cache - if (is_swlist) - { - std::fill(std::begin(m_icons_texture), std::end(m_icons_texture), nullptr); - } - else - { - std::generate(std::begin(m_icons_texture), std::end(m_icons_texture), [&render = machine().render()]() { return render.texture_alloc(); }); - std::generate(std::begin(m_icons_bitmap), std::end(m_icons_bitmap), []() { return std::make_unique<bitmap_argb32>(); }); - } - std::fill(std::begin(m_old_icons), std::end(m_old_icons), nullptr); } @@ -624,9 +607,15 @@ float menu_select_launch::draw_icon(int linenum, void *selectedref, float x0, fl auto x1 = x0 + ud_arrow_width; auto y1 = y0 + ui().get_line_height(); - if (m_old_icons[linenum] != driver || ui_globals::redraw_icon) + icon_cache::iterator icon(m_icons.find(driver)); + if ((m_icons.end() == icon) || ui_globals::redraw_icon) { - m_old_icons[linenum] = driver; + if (m_icons.end() == icon) + { + texture_ptr texture(machine().render().texture_alloc(), [&render = machine().render()] (render_texture *texture) { render.texture_free(texture); }); + bitmap_ptr bitmap(std::make_unique<bitmap_argb32>()); + icon = m_icons.emplace(std::piecewise_construct, std::forward_as_tuple(driver), std::forward_as_tuple(std::move(texture), std::move(bitmap))).first; + } // set clone status bool cloneof = strcmp(driver->parent, "0"); @@ -657,6 +646,7 @@ float menu_select_launch::draw_icon(int linenum, void *selectedref, float x0, fl render_load_ico(*tmp, snapfile, nullptr, fullname.c_str()); } + bitmap_argb32 &bitmap(*icon->second.second); if (tmp->valid()) { float panel_width = x1 - x0; @@ -697,24 +687,25 @@ float menu_select_launch::draw_icon(int linenum, void *selectedref, float x0, fl else dest_bitmap = tmp; - m_icons_bitmap[linenum]->allocate(panel_width_pixel, panel_height_pixel); - + bitmap.allocate(panel_width_pixel, panel_height_pixel); for (int x = 0; x < dest_xPixel; x++) for (int y = 0; y < dest_yPixel; y++) - m_icons_bitmap[linenum]->pix32(y, x) = dest_bitmap->pix32(y, x); + bitmap.pix32(y, x) = dest_bitmap->pix32(y, x); auto_free(machine(), dest_bitmap); - m_icons_texture[linenum]->set_bitmap(*m_icons_bitmap[linenum], m_icons_bitmap[linenum]->cliprect(), TEXFORMAT_ARGB32); + icon->second.first->set_bitmap(bitmap, bitmap.cliprect(), TEXFORMAT_ARGB32); + } + else + { + bitmap.reset(); } - else if (m_icons_bitmap[linenum] != nullptr) - m_icons_bitmap[linenum]->reset(); auto_free(machine(), tmp); } - if (m_icons_bitmap[linenum] != nullptr && m_icons_bitmap[linenum]->valid()) - container().add_quad(x0, y0, x1, y1, rgb_t::white(), m_icons_texture[linenum], PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA)); + if (icon->second.second->valid()) + container().add_quad(x0, y0, x1, y1, rgb_t::white(), icon->second.first.get(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA)); return ud_arrow_width * 1.5f; } diff --git a/src/frontend/mame/ui/selmenu.h b/src/frontend/mame/ui/selmenu.h index f5d4deac84c..95e8dbfe607 100644 --- a/src/frontend/mame/ui/selmenu.h +++ b/src/frontend/mame/ui/selmenu.h @@ -104,7 +104,9 @@ private: using cache_ptr = std::shared_ptr<cache>; using cache_ptr_map = std::map<running_machine *, cache_ptr>; - static constexpr std::size_t MAX_ICONS_RENDER = 40; + using icon_cache = util::lru_cache_map<game_driver const *, std::pair<texture_ptr, bitmap_ptr> >; + + static constexpr std::size_t MAX_ICONS_RENDER = 128; void reset_pressed() { m_pressed = false; m_repeat = 0; } bool mouse_pressed() const { return (osd_ticks() >= m_repeat); } @@ -115,11 +117,11 @@ private: // draw left panel virtual float draw_left_panel(float x1, float y1, float x2, float y2) = 0; - game_driver const *m_info_driver; - ui_software_info const *m_info_software; - int m_info_view; - std::vector<std::string> m_items_list; - std::string m_info_buffer; + game_driver const *m_info_driver; + ui_software_info const *m_info_software; + int m_info_view; + std::vector<std::string> m_items_list; + std::string m_info_buffer; // draw infos void infos_render(float x1, float y1, float x2, float y2); @@ -187,10 +189,7 @@ private: int m_right_visible_lines; // right box lines - - render_texture *m_icons_texture[MAX_ICONS_RENDER]; - bitmap_ptr m_icons_bitmap[MAX_ICONS_RENDER]; - game_driver const *m_old_icons[MAX_ICONS_RENDER]; + icon_cache m_icons; static std::mutex s_cache_guard; static cache_ptr_map s_caches; diff --git a/src/lib/util/coretmpl.h b/src/lib/util/coretmpl.h index 758e4821aa3..4d17f3c0a4b 100644 --- a/src/lib/util/coretmpl.h +++ b/src/lib/util/coretmpl.h @@ -1,5 +1,5 @@ // license:BSD-3-Clause -// copyright-holders:Aaron Giles +// copyright-holders:Aaron Giles, Vas Crabb /*************************************************************************** coretmpl.h @@ -17,9 +17,18 @@ #include "corealloc.h" #include <array> +#include <cassert> #include <cstddef> +#include <functional> +#include <initializer_list> #include <iterator> +#include <list> +#include <map> +#include <memory> +#include <set> #include <stdexcept> +#include <tuple> +#include <type_traits> #include <utility> #include <vector> @@ -433,6 +442,383 @@ private: }; +// LRU cache that behaves like std::map with differences: +// * drops least-recently used items if necessary on insert to prevent size from exceeding max_size +// * iterates from least- to most-recently used rather than in order by key +// * iterators to dropped items are invalidated +// * not all map interfaces implemented +// * copyable and swappable but not movable +// * swap may invalidate past-the-end iterator, other iterators refer to new container +template <typename Key, typename T, typename Compare = std::less<Key>, class Allocator = std::allocator<std::pair<Key const, T> > > +class lru_cache_map +{ +private: + class iterator_compare; + typedef std::list<std::pair<Key const, T>, Allocator> value_list; + typedef typename std::allocator_traits<Allocator>::template rebind_alloc<typename value_list::iterator> iterator_allocator_type; + typedef std::set<typename value_list::iterator, iterator_compare, iterator_allocator_type> iterator_set; + + class iterator_compare + { + public: + typedef std::true_type is_transparent; + iterator_compare(Compare const &comp) : m_comp(comp) { } + iterator_compare(iterator_compare const &that) = default; + iterator_compare(iterator_compare &&that) = default; + Compare key_comp() const { return m_comp; } + iterator_compare &operator=(iterator_compare const &that) = default; + iterator_compare &operator=(iterator_compare &&that) = default; + bool operator()(typename value_list::iterator const &lhs, typename value_list::iterator const &rhs) const { return m_comp(lhs->first, rhs->first); } + template <typename K> bool operator()(typename value_list::iterator const &lhs, K const &rhs) const { return m_comp(lhs->first, rhs); } + template <typename K> bool operator()(K const &lhs, typename value_list::iterator const &rhs) const { return m_comp(lhs, rhs->first); } + private: + Compare m_comp; + }; + +public: + typedef Key key_type; + typedef T mapped_type; + typedef std::pair<Key const, T> value_type; + typedef typename value_list::size_type size_type; + typedef typename value_list::difference_type difference_type; + typedef Compare key_compare; + typedef Allocator allocator_type; + typedef value_type &reference; + typedef value_type const &const_reference; + typedef typename std::allocator_traits<Allocator>::pointer pointer; + typedef typename std::allocator_traits<Allocator>::const_pointer const_pointer; + typedef typename value_list::iterator iterator; + typedef typename value_list::const_iterator const_iterator; + typedef typename value_list::reverse_iterator reverse_iterator; + typedef typename value_list::const_reverse_iterator const_reverse_iterator; + + explicit lru_cache_map(size_type max_size) + : lru_cache_map(max_size, key_compare()) + { + } + lru_cache_map(size_type max_size, key_compare const &comp, allocator_type const &alloc = allocator_type()) + : m_max_size(max_size) + , m_size(0U) + , m_elements(alloc) + , m_mapping(iterator_compare(comp), iterator_allocator_type(alloc)) + { + assert(0U < m_max_size); + } + lru_cache_map(lru_cache_map const &that) + : m_max_size(that.m_max_size) + , m_size(that.m_size) + , m_elements(that.m_elements) + , m_mapping(that.m_mapping.key_comp(), that.m_mapping.get_allocator()) + { + for (iterator it = m_elements.begin(); it != m_elements.end(); ++it) + m_mapping.insert(it); + assert(m_elements.size() == m_size); + assert(m_mapping.size() == m_size); + } + + allocator_type get_allocator() const { return m_elements.get_allocator(); } + + iterator begin() { return m_elements.begin(); } + const_iterator begin() const { return m_elements.cbegin(); } + const_iterator cbegin() const { return m_elements.cbegin(); } + iterator end() { return m_elements.end(); } + const_iterator end() const { return m_elements.cend(); } + const_iterator cend() const { return m_elements.cend(); } + reverse_iterator rbegin() { return m_elements.rbegin(); } + const_reverse_iterator rbegin() const { return m_elements.crbegin(); } + const_reverse_iterator crbegin() const { return m_elements.crbegin(); } + reverse_iterator rend() { return m_elements.end(); } + const_reverse_iterator rend() const { return m_elements.crend(); } + const_reverse_iterator crend() const { return m_elements.crend(); } + + bool empty() const { return !m_size; } + size_type size() const { return m_size; } + size_type max_size() const { return m_max_size; } + + mapped_type &operator[](key_type const &key) + { + typename iterator_set::iterator existing(m_mapping.lower_bound(key)); + if ((m_mapping.end() != existing) && !m_mapping.key_comp()(key, *existing)) + { + m_elements.splice(m_elements.cend(), m_elements, *existing); + return (*existing)->second; + } + make_space(existing); + iterator const inserted(m_elements.emplace(m_elements.end(), std::piecewise_construct, std::forward_as_tuple(key), std::tuple<>())); + m_mapping.insert(existing, inserted); + ++m_size; + assert(m_elements.size() == m_size); + assert(m_mapping.size() == m_size); + return inserted->second; + } + mapped_type &operator[](key_type &&key) + { + typename iterator_set::iterator existing(m_mapping.lower_bound(key)); + if ((m_mapping.end() != existing) && !m_mapping.key_comp()(key, *existing)) + { + m_elements.splice(m_elements.cend(), m_elements, *existing); + return (*existing)->second; + } + make_space(existing); + iterator const inserted(m_elements.emplace(m_elements.end(), std::piecewise_construct, std::forward_as_tuple(std::move(key)), std::tuple<>())); + m_mapping.insert(existing, inserted); + ++m_size; + assert(m_elements.size() == m_size); + assert(m_mapping.size() == m_size); + return inserted->second; + } + mapped_type &at(key_type const &key) + { + typename iterator_set::iterator existing(m_mapping.find(key)); + if (m_mapping.end() != existing) + { + m_elements.splice(m_elements.cend(), m_elements, *existing); + return (*existing)->second; + } + else + { + throw std::out_of_range("lru_cache_map::at"); + } + } + mapped_type const &at(key_type const &key) const + { + typename iterator_set::iterator existing(m_mapping.find(key)); + if (m_mapping.end() != existing) + { + m_elements.splice(m_elements.cend(), m_elements, *existing); + return (*existing)->second; + } + else + { + throw std::out_of_range("lru_cache_map::at"); + } + } + + void clear() + { + m_size = 0U; + m_elements.clear(); + m_mapping.clear(); + } + std::pair<iterator, bool> insert(value_type const &value) + { + typename iterator_set::iterator existing(m_mapping.lower_bound(value.first)); + if ((m_mapping.end() != existing) && !m_mapping.key_comp()(value.first, *existing)) + { + m_elements.splice(m_elements.cend(), m_elements, *existing); + return std::pair<iterator, bool>(*existing, false); + } + make_space(existing); + iterator const inserted(m_elements.emplace(m_elements.end(), value)); + m_mapping.insert(existing, inserted); + ++m_size; + assert(m_elements.size() == m_size); + assert(m_mapping.size() == m_size); + return std::pair<iterator, bool>(inserted, true); + } + std::pair<iterator, bool> insert(value_type &&value) + { + typename iterator_set::iterator existing(m_mapping.lower_bound(value.first)); + if ((m_mapping.end() != existing) && !m_mapping.key_comp()(value.first, *existing)) + { + m_elements.splice(m_elements.cend(), m_elements, *existing); + return std::pair<iterator, bool>(*existing, false); + } + make_space(existing); + iterator const inserted(m_elements.emplace(m_elements.end(), std::move(value))); + m_mapping.insert(existing, inserted); + ++m_size; + assert(m_elements.size() == m_size); + assert(m_mapping.size() == m_size); + return std::pair<iterator, bool>(inserted, true); + } + template <typename P> + std::pair<iterator, bool> insert(P &&value) + { + // FIXME: should only participate in overload resolution if std::is_constructible<value_type, P&&>::value + return emplace(std::forward<P>(value)); + } + template <typename InputIt> + void insert(InputIt first, InputIt last) + { + while (first != last) + { + insert(*first); + ++first; + } + } + void insert(std::initializer_list<value_type> ilist) + { + for (value_type const &value : ilist) + insert(value); + } + template <typename... Params> + std::pair<iterator, bool> emplace(Params &&... args) + { + // TODO: is there a more efficient way than depending on value_type being efficiently movable? + return insert(value_type(std::forward<Params>(args)...)); + } + iterator erase(const_iterator pos) + { + m_mapping.erase(m_elements.erase(pos, pos)); + iterator const result(m_elements.erase(pos)); + --m_size; + assert(m_elements.size() == m_size); + assert(m_mapping.size() == m_size); + return result; + } + iterator erase(const_iterator first, const_iterator last) + { + iterator pos(m_elements.erase(first, first)); + while (pos != last) + { + m_mapping.erase(pos); + pos = m_elements.erase(pos); + --m_size; + } + assert(m_elements.size() == m_size); + assert(m_mapping.size() == m_size); + return pos; + } + size_type erase(key_type const &key) + { + typename iterator_set::iterator const found(m_mapping.find(key)); + if (m_mapping.end() == found) + { + return 0U; + } + else + { + m_elements.erase(*found); + m_mapping.erase(found); + --m_size; + assert(m_elements.size() == m_size); + assert(m_mapping.size() == m_size); + return 1U; + } + } + void swap(lru_cache_map &that) + { + using std::swap; + swap(m_max_size, that.m_max_size); + swap(m_size, that.m_size); + swap(m_elements, that.m_elements); + swap(m_mapping, that.m_mapping); + } + + size_type count(key_type const &key) const + { + // TODO: perhaps this should freshen an element + return m_mapping.count(key); + } + template <typename K> + size_type count(K const &x) const + { + // FIXME: should only enable this overload if Compare::is_transparent + // TODO: perhaps this should freshen an element + return m_mapping.count(x); + } + iterator find(key_type const &key) + { + typename iterator_set::const_iterator const found(m_mapping.find(key)); + if (m_mapping.end() == found) + { + return m_elements.end(); + } + else + { + m_elements.splice(m_elements.cend(), m_elements, *found); + return *found; + } + } + iterator find(key_type const &key) const + { + typename iterator_set::const_iterator const found(m_mapping.find(key)); + if (m_mapping.end() == found) + { + return m_elements.end(); + } + else + { + m_elements.splice(m_elements.cend(), m_elements, *found); + return *found; + } + } + template <typename K> + iterator find(K const &x) + { + // FIXME: should only enable this overload if Compare::is_transparent + typename iterator_set::const_iterator const found(m_mapping.find(x)); + if (m_mapping.end() == found) + { + return m_elements.end(); + } + else + { + m_elements.splice(m_elements.cend(), m_elements, *found); + return *found; + } + } + template <typename K> + iterator find(K const &x) const + { + // FIXME: should only enable this overload if Compare::is_transparent + typename iterator_set::const_iterator const found(m_mapping.find(x)); + if (m_mapping.end() == found) + { + return m_elements.end(); + } + else + { + m_elements.splice(m_elements.cend(), m_elements, *found); + return *found; + } + } + + key_compare key_comp() const + { + return m_mapping.key_comp().key_comp(); + } + + lru_cache_map &operator=(lru_cache_map const &that) + { + m_max_size = that.m_max_size; + m_size = that.m_size; + m_elements = that.m_elements; + m_mapping.clear(); + for (iterator it = m_elements.begin(); it != m_elements.end(); ++it) + m_mapping.insert(it); + assert(m_elements.size() == m_size); + assert(m_mapping.size() == m_size); + } + +private: + void make_space(typename iterator_set::iterator &existing) + { + while (m_max_size <= m_size) + { + if ((m_mapping.end() != existing) && (m_elements.begin() == *existing)) + existing = m_mapping.erase(existing); + else + m_mapping.erase(m_elements.begin()); + m_elements.erase(m_elements.begin()); + --m_size; + } + } + + size_type m_max_size; + size_type m_size; + mutable value_list m_elements; + iterator_set m_mapping; +}; + +template <typename Key, typename T, typename Compare, class Allocator> +void swap(lru_cache_map<Key, T, Compare, Allocator> &lhs, lru_cache_map<Key, T, Compare, Allocator> &rhs) +{ + lhs.swap(rhs); +} + + template <typename T, std::size_t N, bool WriteWrap = false, bool ReadWrap = WriteWrap> class fifo : protected std::array<T, N> { diff --git a/uismall.bdf b/uismall.bdf index f98f8063bff..526d60ae398 100644 --- a/uismall.bdf +++ b/uismall.bdf @@ -3,7 +3,7 @@ COMMENT Copyright (C) 1997-2016 MAMEDev and contributors FONT -mamedev-uismall-Medium-R-Normal--16-120-96-96-P-100-ISO10646-1 SIZE 12 96 96 FONTBOUNDINGBOX 8 11 0 -2 -STARTPROPERTIES 17 +STARTPROPERTIES 18 POINT_SIZE 120 PIXEL_SIZE 16 RESOLUTION_X 96 @@ -21,6 +21,7 @@ SETWIDTH_NAME "Normal" ADD_STYLE_NAME "" CHARSET_REGISTRY "ISO10646" CHARSET_ENCODING "1" +DEFAULT_CHAR 173 ENDPROPERTIES CHARS 1024 STARTCHAR space @@ -1563,8 +1564,8 @@ E8 ENDCHAR STARTCHAR paragraph ENCODING 182 -SWIDTH 375 0 -DWIDTH 6 0 +SWIDTH 437 0 +DWIDTH 7 0 BBX 6 9 0 -2 BITMAP 7C |