text: #include <rsys/macstrings.h>
  #include <utility>
  #include <array>
  #include <unordered_map>
  #include <algorithm>
  
  #include <OSUtil.h>
  
  using namespace Executor;
  
  static std::pair<unsigned char, char32_t> macroman[] = {
      { 0x11, 0x2318 },   // COMMAND KEY SIGN
      { 0x12, 0x21E7 },   // SHIFT KEY (UPWARDS WHITE ARROW)
      { 0x13, 0x2325 },   // OPTION KEY
      { 0x14, 0x2303 },   // CONTROL KEY (UP ARROWHEAD)
      { 0x80, 0x00C4 },	// LATIN CAPITAL LETTER A WITH DIAERESIS
      { 0x81, 0x00C5 },	// LATIN CAPITAL LETTER A WITH RING ABOVE
      { 0x82, 0x00C7 },	// LATIN CAPITAL LETTER C WITH CEDILLA
      { 0x83, 0x00C9 },	// LATIN CAPITAL LETTER E WITH ACUTE
      { 0x84, 0x00D1 },	// LATIN CAPITAL LETTER N WITH TILDE
      { 0x85, 0x00D6 },	// LATIN CAPITAL LETTER O WITH DIAERESIS
      { 0x86, 0x00DC },	// LATIN CAPITAL LETTER U WITH DIAERESIS
      { 0x87, 0x00E1 },	// LATIN SMALL LETTER A WITH ACUTE
      { 0x88, 0x00E0 },	// LATIN SMALL LETTER A WITH GRAVE
      { 0x89, 0x00E2 },	// LATIN SMALL LETTER A WITH CIRCUMFLEX
      { 0x8A, 0x00E4 },	// LATIN SMALL LETTER A WITH DIAERESIS
      { 0x8B, 0x00E3 },	// LATIN SMALL LETTER A WITH TILDE
      { 0x8C, 0x00E5 },	// LATIN SMALL LETTER A WITH RING ABOVE
      { 0x8D, 0x00E7 },	// LATIN SMALL LETTER C WITH CEDILLA
      { 0x8E, 0x00E9 },	// LATIN SMALL LETTER E WITH ACUTE
      { 0x8F, 0x00E8 },	// LATIN SMALL LETTER E WITH GRAVE
      { 0x90, 0x00EA },	// LATIN SMALL LETTER E WITH CIRCUMFLEX
      { 0x91, 0x00EB },	// LATIN SMALL LETTER E WITH DIAERESIS
      { 0x92, 0x00ED },	// LATIN SMALL LETTER I WITH ACUTE
      { 0x93, 0x00EC },	// LATIN SMALL LETTER I WITH GRAVE
      { 0x94, 0x00EE },	// LATIN SMALL LETTER I WITH CIRCUMFLEX
      { 0x95, 0x00EF },	// LATIN SMALL LETTER I WITH DIAERESIS
      { 0x96, 0x00F1 },	// LATIN SMALL LETTER N WITH TILDE
      { 0x97, 0x00F3 },	// LATIN SMALL LETTER O WITH ACUTE
      { 0x98, 0x00F2 },	// LATIN SMALL LETTER O WITH GRAVE
      { 0x99, 0x00F4 },	// LATIN SMALL LETTER O WITH CIRCUMFLEX
      { 0x9A, 0x00F6 },	// LATIN SMALL LETTER O WITH DIAERESIS
      { 0x9B, 0x00F5 },	// LATIN SMALL LETTER O WITH TILDE
      { 0x9C, 0x00FA },	// LATIN SMALL LETTER U WITH ACUTE
      { 0x9D, 0x00F9 },	// LATIN SMALL LETTER U WITH GRAVE
      { 0x9E, 0x00FB },	// LATIN SMALL LETTER U WITH CIRCUMFLEX
      { 0x9F, 0x00FC },	// LATIN SMALL LETTER U WITH DIAERESIS
      { 0xA0, 0x2020 },	// DAGGER
      { 0xA1, 0x00B0 },	// DEGREE SIGN
      { 0xA2, 0x00A2 },	// CENT SIGN
      { 0xA3, 0x00A3 },	// POUND SIGN
      { 0xA4, 0x00A7 },	// SECTION SIGN
      { 0xA5, 0x2022 },	// BULLET
      { 0xA6, 0x00B6 },	// PILCROW SIGN
      { 0xA7, 0x00DF },	// LATIN SMALL LETTER SHARP S
      { 0xA8, 0x00AE },	// REGISTERED SIGN
      { 0xA9, 0x00A9 },	// COPYRIGHT SIGN
      { 0xAA, 0x2122 },	// TRADE MARK SIGN
      { 0xAB, 0x00B4 },	// ACUTE ACCENT
      { 0xAC, 0x00A8 },	// DIAERESIS
      { 0xAD, 0x2260 },	// NOT EQUAL TO
      { 0xAE, 0x00C6 },	// LATIN CAPITAL LETTER AE
      { 0xAF, 0x00D8 },	// LATIN CAPITAL LETTER O WITH STROKE
      { 0xB0, 0x221E },	// INFINITY
      { 0xB1, 0x00B1 },	// PLUS-MINUS SIGN
      { 0xB2, 0x2264 },	// LESS-THAN OR EQUAL TO
      { 0xB3, 0x2265 },	// GREATER-THAN OR EQUAL TO
      { 0xB4, 0x00A5 },	// YEN SIGN
      { 0xB5, 0x00B5 },	// MICRO SIGN
      { 0xB6, 0x2202 },	// PARTIAL DIFFERENTIAL
      { 0xB7, 0x2211 },	// N-ARY SUMMATION
      { 0xB8, 0x220F },	// N-ARY PRODUCT
      { 0xB9, 0x03C0 },	// GREEK SMALL LETTER PI
      { 0xBA, 0x222B },	// INTEGRAL
      { 0xBB, 0x00AA },	// FEMININE ORDINAL INDICATOR
      { 0xBC, 0x00BA },	// MASCULINE ORDINAL INDICATOR
      { 0xBD, 0x03A9 },	// GREEK CAPITAL LETTER OMEGA
      { 0xBE, 0x00E6 },	// LATIN SMALL LETTER AE
      { 0xBF, 0x00F8 },	// LATIN SMALL LETTER O WITH STROKE
      { 0xC0, 0x00BF },	// INVERTED QUESTION MARK
      { 0xC1, 0x00A1 },	// INVERTED EXCLAMATION MARK
      { 0xC2, 0x00AC },	// NOT SIGN
      { 0xC3, 0x221A },	// SQUARE ROOT
      { 0xC4, 0x0192 },	// LATIN SMALL LETTER F WITH HOOK
      { 0xC5, 0x2248 },	// ALMOST EQUAL TO
      { 0xC6, 0x2206 },	// INCREMENT
      { 0xC7, 0x00AB },	// LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
      { 0xC8, 0x00BB },	// RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
      { 0xC9, 0x2026 },	// HORIZONTAL ELLIPSIS
      { 0xCA, 0x00A0 },	// NO-BREAK SPACE
      { 0xCB, 0x00C0 },	// LATIN CAPITAL LETTER A WITH GRAVE
      { 0xCC, 0x00C3 },	// LATIN CAPITAL LETTER A WITH TILDE
      { 0xCD, 0x00D5 },	// LATIN CAPITAL LETTER O WITH TILDE
      { 0xCE, 0x0152 },	// LATIN CAPITAL LIGATURE OE
      { 0xCF, 0x0153 },	// LATIN SMALL LIGATURE OE
      { 0xD0, 0x2013 },	// EN DASH
      { 0xD1, 0x2014 },	// EM DASH
      { 0xD2, 0x201C },	// LEFT DOUBLE QUOTATION MARK
      { 0xD3, 0x201D },	// RIGHT DOUBLE QUOTATION MARK
      { 0xD4, 0x2018 },	// LEFT SINGLE QUOTATION MARK
      { 0xD5, 0x2019 },	// RIGHT SINGLE QUOTATION MARK
      { 0xD6, 0x00F7 },	// DIVISION SIGN
      { 0xD7, 0x25CA },	// LOZENGE
      { 0xD8, 0x00FF },	// LATIN SMALL LETTER Y WITH DIAERESIS
      { 0xD9, 0x0178 },	// LATIN CAPITAL LETTER Y WITH DIAERESIS
      { 0xDA, 0x2044 },	// FRACTION SLASH
      { 0xDB, 0x20AC },	// EURO SIGN
      { 0xDC, 0x2039 },	// SINGLE LEFT-POINTING ANGLE QUOTATION MARK
      { 0xDD, 0x203A },	// SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
      { 0xDE, 0xFB01 },	// LATIN SMALL LIGATURE FI
      { 0xDF, 0xFB02 },	// LATIN SMALL LIGATURE FL
      { 0xE0, 0x2021 },	// DOUBLE DAGGER
      { 0xE1, 0x00B7 },	// MIDDLE DOT
      { 0xE2, 0x201A },	// SINGLE LOW-9 QUOTATION MARK
      { 0xE3, 0x201E },	// DOUBLE LOW-9 QUOTATION MARK
      { 0xE4, 0x2030 },	// PER MILLE SIGN
      { 0xE5, 0x00C2 },	// LATIN CAPITAL LETTER A WITH CIRCUMFLEX
      { 0xE6, 0x00CA },	// LATIN CAPITAL LETTER E WITH CIRCUMFLEX
      { 0xE7, 0x00C1 },	// LATIN CAPITAL LETTER A WITH ACUTE
      { 0xE8, 0x00CB },	// LATIN CAPITAL LETTER E WITH DIAERESIS
      { 0xE9, 0x00C8 },	// LATIN CAPITAL LETTER E WITH GRAVE
      { 0xEA, 0x00CD },	// LATIN CAPITAL LETTER I WITH ACUTE
      { 0xEB, 0x00CE },	// LATIN CAPITAL LETTER I WITH CIRCUMFLEX
      { 0xEC, 0x00CF },	// LATIN CAPITAL LETTER I WITH DIAERESIS
      { 0xED, 0x00CC },	// LATIN CAPITAL LETTER I WITH GRAVE
      { 0xEE, 0x00D3 },	// LATIN CAPITAL LETTER O WITH ACUTE
      { 0xEF, 0x00D4 },	// LATIN CAPITAL LETTER O WITH CIRCUMFLEX
      { 0xF0, 0xF8FF },	// Apple logo    - U+1F34E Red Apple U+1F34F Green Apple
      { 0xF1, 0x00D2 },	// LATIN CAPITAL LETTER O WITH GRAVE
      { 0xF2, 0x00DA },	// LATIN CAPITAL LETTER U WITH ACUTE
      { 0xF3, 0x00DB },	// LATIN CAPITAL LETTER U WITH CIRCUMFLEX
      { 0xF4, 0x00D9 },	// LATIN CAPITAL LETTER U WITH GRAVE
      { 0xF5, 0x0131 },	// LATIN SMALL LETTER DOTLESS I
      { 0xF6, 0x02C6 },	// MODIFIER LETTER CIRCUMFLEX ACCENT
      { 0xF7, 0x02DC },	// SMALL TILDE
      { 0xF8, 0x00AF },	// MACRON
      { 0xF9, 0x02D8 },	// BREVE
      { 0xFA, 0x02D9 },	// DOT ABOVE
      { 0xFB, 0x02DA },	// RING ABOVE
      { 0xFC, 0x00B8 },	// CEDILLA
      { 0xFD, 0x02DD },	// DOUBLE ACUTE ACCENT
      { 0xFE, 0x02DB },	// OGONEK
      { 0xFF, 0x02C7 }	// CARON
  };
  
  static std::array<char32_t, 256> macToUniTable = [] {
          std::array<char32_t, 256> arr;
          for(int i = 0; i < 256; i++)
              arr[i] = i;
          for(auto [mac, uni] : macroman)
              arr[mac] = uni;
          return arr;
      } ();
  
  static std::unordered_map<char32_t, unsigned char> uniToMacTable = [] {
          std::unordered_map<char32_t, unsigned char> table;
          for(int i = 0; i < 256; i++)
              table[macToUniTable[i]] = i;
          return table;
      } ();
  
  
  std::u32string Executor::toUnicode(mac_string_view sv)
  {
      std::u32string s;
      s.reserve(sv.size());
      for(unsigned char c : sv)
          s.push_back(macToUniTable[c]);
  
      return s;
  }
  
  mac_string Executor::toMacRoman(const std::u32string& src)
  {
      mac_string dst;
      dst.reserve(src.size());
      for(char32_t c : src)
      {
          auto it = uniToMacTable.find(c);
          if(it == uniToMacTable.end())
              dst.push_back('?');
          else
              dst.push_back(it->second);
      }
  
      return dst;
  }
  
  
  fs::path Executor::toUnicodeFilename(mac_string_view sv)
  {
      std::wstring s;
      s.reserve(sv.size());
      for(unsigned char c : sv)
      {
          if(c == '/')
              s.push_back(':');
          else
              s.push_back(macToUniTable[c]);
      }
      return s;
  }
  
  mac_string Executor::toMacRomanFilename(const fs::path& p, int index)
  {
      mac_string dst;
      std::wstring ws;
      
      char idxbuf[16];
      mac_string_view suffix;
      if(index)
      {
          int n = sprintf(idxbuf, "#%d", index);
          if(n > 0)
              suffix = mac_string_view(reinterpret_cast<unsigned char*>(idxbuf), n);
      }
  
      try
      {
          ws = p.wstring();
      }
      catch(const boost::system::system_error&)
      {
          // conversion error:
          // incorrectly-encoded filename on a unix system
  
          const std::string& s = p.string();
          for(char c : s)
          {
              if(dst.size() == 30-suffix.size() && s.size() > 31-suffix.size())
              {
                  dst.push_back(0xC9); // ellipsis
                  break;
              }
          
              if(c == ':')
                  dst.push_back('/');
              else if(c & 0x80)
                  dst.push_back('?');
              else
                  dst.push_back(c);
          }
          return dst + mac_string(suffix);
      }
  
      dst.reserve(std::min((size_t)31, ws.size() + suffix.size()));
      for(wchar_t c : ws)
      {
          if(dst.size() == 30-suffix.size() && ws.size() > 31-suffix.size())
          {
              dst.push_back(0xC9); // ellipsis
              break;
          }
  
          if(c == ':')
              dst.push_back('/');
          else
          {
              auto it = uniToMacTable.find(c);
              if(it == uniToMacTable.end())
                  dst.push_back('?');
              else
                  dst.push_back(it->second);
          }
      }
  
      return dst + mac_string(suffix);
  }
  
  bool Executor::matchesMacRomanFilename(const fs::path& s, mac_string_view sv)
  {
      mac_string converted = toMacRomanFilename(s);
      return ROMlib_RelString(converted.data(), sv.data(),
                              false, true,
                              converted.size() << 16 | sv.size()) == 0;
  }
  
  
  unsigned char* Executor::assignPString(unsigned char* s, mac_string_view v, int max)
  {
      if(v.size() > max)
          v = {v.begin(), v.begin() + max};
      
      std::copy(v.begin(), v.end(), s+1);
      s[0] = v.size();
      return s;
  }
  