diff --git a/CMakeLists.txt b/CMakeLists.txt index 87b3c80e..9b9742ac 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -108,6 +108,9 @@ option(WITH_SHORTEN "Build with Shorten" ON) option(WITH_TRUEAUDIO "Build with TrueAudio" ON) option(WITH_VORBIS "Build with Vorbis, FLAC, Ogg, Opus" ON) +# Include character tables for legacy code pages. +option(WITH_CHARSETTABLES "Build with code page charset tables" ON) + # Determine whether zlib is installed. option(WITH_ZLIB "Build with ZLIB" ON) @@ -219,6 +222,9 @@ endif() if(WITH_VORBIS) set(TAGLIB_WITH_VORBIS TRUE) endif() +if(WITH_CHARSETTABLES) + set(TAGLIB_WITH_CHARSETTABLES TRUE) +endif() configure_file(taglib/taglib_config.h.cmake "${CMAKE_CURRENT_BINARY_DIR}/taglib_config.h") diff --git a/INSTALL.md b/INSTALL.md index 9d419131..534266a5 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -44,6 +44,7 @@ CMakeLists.txt file. | `BUILD_FRAMEWORK` | Build a macOS framework | | `TESTS_DIR` | Where to find unit test data (with data appended) | | `TESTS_TMPDIR` | Where to create temporary files in unit tests | +| `WITH_CHARSETTABLES` | Include code page character tables (default ON) | If you want to install TagLib 2 alongside TagLib 1, you can use diff --git a/taglib/CMakeLists.txt b/taglib/CMakeLists.txt index 15c2b944..7d5b03d0 100644 --- a/taglib/CMakeLists.txt +++ b/taglib/CMakeLists.txt @@ -72,7 +72,9 @@ if(WITH_MATROSKA) endif() include_directories(${tag_HDR_DIRS}) -set(tag_PRIVATE_HDRS) +set(tag_PRIVATE_HDRS + toolkit/charsettables.h +) set(tag_HDRS tag.h fileref.h @@ -96,6 +98,7 @@ set(tag_HDRS toolkit/tpicturetype.h toolkit/tpropertymap.h toolkit/tdebuglistener.h + toolkit/tcharset.h toolkit/tversionnumber.h mpeg/mpegfile.h mpeg/mpegproperties.h @@ -517,14 +520,22 @@ set(toolkit_SRCS toolkit/tdebuglistener.cpp toolkit/tzlib.cpp toolkit/tversionnumber.cpp + toolkit/tcharset.cpp ) +if(WITH_CHARSETTABLES) + set(charsettables_SRCS + toolkit/charsettables.cpp + ) +endif() + set(tag_LIB_SRCS ${mpeg_SRCS} ${id3v1_SRCS} ${id3v2_SRCS} ${frames_SRCS} ${ogg_SRCS} ${vorbis_SRCS} ${oggflacs_SRCS} ${mpc_SRCS} ${ape_SRCS} ${toolkit_SRCS} ${flacs_SRCS} ${wavpack_SRCS} ${speex_SRCS} ${trueaudio_SRCS} ${riff_SRCS} ${aiff_SRCS} ${wav_SRCS} ${asf_SRCS} ${mp4_SRCS} ${mod_SRCS} ${s3m_SRCS} ${it_SRCS} ${xm_SRCS} ${opus_SRCS} ${dsf_SRCS} ${dsdiff_SRCS} ${shorten_SRCS} ${matroska_SRCS} ${ebml_SRCS} + ${charsettables_SRCS} tag.cpp tagunion.cpp fileref.cpp diff --git a/taglib/mpeg/id3v1/id3v1tag.cpp b/taglib/mpeg/id3v1/id3v1tag.cpp index cd8cceb9..813a1780 100644 --- a/taglib/mpeg/id3v1/id3v1tag.cpp +++ b/taglib/mpeg/id3v1/id3v1tag.cpp @@ -25,7 +25,11 @@ #include "id3v1tag.h" +#include + +#include "tcharset.h" #include "tdebug.h" +#include "tutils.h" #include "tfile.h" #include "id3v1genres.h" @@ -36,6 +40,54 @@ namespace { const ID3v1::StringHandler defaultStringHandler; const ID3v1::StringHandler *stringHandler = &defaultStringHandler; + + //! The code page ID3v1::Tag::setAssumedCodePage() states. + //! + //! Used when a tag is read without an explicit one. + //! 0 keeps the ISO 8859-1 the ID3v1 specification prescribes. + unsigned int codePageAssumed = 0; + + //! Look up charset for \a codePage. + //! + //! The charset a tag's code page selects. ID3v1 declares no charset, so the + //! code page is only ever what the caller states: the constructor's, or the + //! assumed one for the unspecified 0. An unsupported number falls back to + //! the assumed one too, rather than dropping the tag over it. + Charset::Type charsetForCodePage(unsigned int codePage) + { + if(codePage == 0) { + codePage = codePageAssumed; + } + else if(!Charset::typeForCodePage(codePage)) { + debug(Utils::formatString( + "ID3v1::Tag::Tag() - Unsupported code page %u, using the assumed one.", + codePage)); + codePage = codePageAssumed; + } + + // Fast path without lookup for the likely default case. + if(codePage == 0) + return Charset::Type::Latin1; + + // If we get here, setAssumedCodePage() stored a verified code page, not 0, + // the condition will always be true. + if(const auto charset = Charset::typeForCodePage(codePage)) + return *charset; + + return Charset::Type::Latin1; + } + + String decodeField(const ByteVector &data, Charset::Type charset) + { + // ID3v1 fields are fixed width and space padded, so the padding is stripped + // here rather than in Charset::decode, which the RIFF decoder shares. + return Charset::decode(data, charset).stripWhiteSpace(); + } + + ByteVector encodeField(const String &s, Charset::Type charset) + { + return Charset::encode(s, charset); + } } // namespace class ID3v1::Tag::TagPrivate @@ -49,8 +101,30 @@ public: String album; String year; String comment; + + //! The charset the tag's code page selects, resolved once when the tag is + //! read. + Charset::Type charset { Charset::Type::Latin1 }; + unsigned char track { 0 }; unsigned char genre { 255 }; + + String parseField(const ByteVector &data) const + { + // A handler the user installed with setStringHandler() is an explicit + // override, so it takes precedence over the code page that was stated. With + // no handler installed the fields are decoded with the charset. + if(stringHandler != &defaultStringHandler) + return stringHandler->parse(data); + return decodeField(data, charset); + } + + ByteVector renderField(const String &s) const + { + if(stringHandler != &defaultStringHandler) + return stringHandler->render(s); + return encodeField(s, charset); + } }; class ID3v1::StringHandler::StringHandlerPrivate @@ -87,10 +161,16 @@ ID3v1::Tag::Tag() : } ID3v1::Tag::Tag(File *file, offset_t tagOffset) : + ID3v1::Tag(file, tagOffset, 0) +{ +} + +ID3v1::Tag::Tag(File *file, offset_t tagOffset, unsigned int codePage) : d(std::make_unique()) { d->file = file; d->tagOffset = tagOffset; + d->charset = charsetForCodePage(codePage); read(); } @@ -102,11 +182,11 @@ ByteVector ID3v1::Tag::render() const ByteVector data; data.append(fileIdentifier()); - data.append(stringHandler->render(d->title).resize(30)); - data.append(stringHandler->render(d->artist).resize(30)); - data.append(stringHandler->render(d->album).resize(30)); - data.append(stringHandler->render(d->year).resize(4)); - data.append(stringHandler->render(d->comment).resize(28)); + data.append(d->renderField(d->title).resize(30)); + data.append(d->renderField(d->artist).resize(30)); + data.append(d->renderField(d->album).resize(30)); + data.append(d->renderField(d->year).resize(4)); + data.append(d->renderField(d->comment).resize(28)); data.append(static_cast(0)); data.append(static_cast(d->track)); data.append(static_cast(d->genre)); @@ -207,6 +287,23 @@ void ID3v1::Tag::setStringHandler(const StringHandler *handler) stringHandler = &defaultStringHandler; } +unsigned int ID3v1::Tag::assumedCodePage() +{ + return codePageAssumed; +} + +void ID3v1::Tag::setAssumedCodePage(unsigned int codePage) +{ + if(codePage != 0 && !Charset::typeForCodePage(codePage)) { + debug(Utils::formatString( + "ID3v1::Tag::setAssumedCodePage() - Unsupported code page %u, ignored.", + codePage)); + return; + } + + codePageAssumed = codePage; +} + //////////////////////////////////////////////////////////////////////////////// // protected methods //////////////////////////////////////////////////////////////////////////////// @@ -229,16 +326,16 @@ void ID3v1::Tag::parse(const ByteVector &data) { int offset = 3; - d->title = stringHandler->parse(data.mid(offset, 30)); + d->title = d->parseField(data.mid(offset, 30)); offset += 30; - d->artist = stringHandler->parse(data.mid(offset, 30)); + d->artist = d->parseField(data.mid(offset, 30)); offset += 30; - d->album = stringHandler->parse(data.mid(offset, 30)); + d->album = d->parseField(data.mid(offset, 30)); offset += 30; - d->year = stringHandler->parse(data.mid(offset, 4)); + d->year = d->parseField(data.mid(offset, 4)); offset += 4; // Check for ID3v1.1 -- Note that ID3v1 *does not* support "track zero" -- this @@ -249,11 +346,11 @@ void ID3v1::Tag::parse(const ByteVector &data) if(data[offset + 28] == 0 && data[offset + 29] != 0) { // ID3v1.1 detected - d->comment = stringHandler->parse(data.mid(offset, 28)); + d->comment = d->parseField(data.mid(offset, 28)); d->track = static_cast(data[offset + 29]); } else - d->comment = data.mid(offset, 30); + d->comment = d->parseField(data.mid(offset, 30)); offset += 30; diff --git a/taglib/mpeg/id3v1/id3v1tag.h b/taglib/mpeg/id3v1/id3v1tag.h index e85023ef..a4d85fa9 100644 --- a/taglib/mpeg/id3v1/id3v1tag.h +++ b/taglib/mpeg/id3v1/id3v1tag.h @@ -51,11 +51,16 @@ namespace TagLib { * ID3v1::Tag::setStringHandler() you can define how you would like these * transformations to be done. * + * An alternative for reading is a Windows code page number, which + * ID3v1::Tag supports directly and which needs no subclass; see + * ID3v1::Tag::setAssumedCodePage() and the ID3v1::Tag constructor taking one. + * * \warning It is advisable not to write non-ISO-8859-1 data to ID3v1 * tags. Please consider disabling the writing of ID3v1 tags in the case * that the data is not ISO-8859-1. * * \see ID3v1::Tag::setStringHandler() + * \see ID3v1::Tag::setAssumedCodePage() */ class TAGLIB_EXPORT StringHandler @@ -125,6 +130,32 @@ namespace TagLib { */ Tag(File *file, offset_t tagOffset); + /*! + * Create an ID3v1 tag and parse the data in \a file starting at + * \a tagOffset, decoding the text with the Windows code page \a codePage. + * + * \a codePage is a Windows code page number in the same numbering a CSET + * chunk of a RIFF file uses: 1252 reads Windows-1252, 866 reads IBM866, + * 65001 reads UTF-8, and 0 is the default, which is the code page + * assumedCodePage() names and ISO 8859-1 unless the caller has stated + * another one. + * + * An ID3v1 tag has no place to declare a charset, so unlike a RIFF CSET + * chunk this number is nothing but what the caller states about its files. + * A code page this library has no table for falls back to + * assumedCodePage(), and the fields are still read rather than dropped + * over a number that could not be followed. + * + * \a codePage decides how the text is read and written. + * + * \note A handler installed with setStringHandler() takes precedence over + * \a codePage, because installing one is an explicit override. + * + * \see assumedCodePage() + * \see setStringHandler() + */ + Tag(File *file, offset_t tagOffset, unsigned int codePage); + /*! * Destroys this Tag instance. */ @@ -184,6 +215,10 @@ namespace TagLib { * If the parameter \a handler is null, the previous handler is * released and default ISO-8859-1 handler is restored. * + * \note Installing a handler is an explicit override, so it is used for + * every ID3v1 tag, including those read with a code page. Pass null to + * hand those tags back to their own code page. + * * \note The caller is responsible for deleting the previous handler * as needed after it is released. * @@ -191,6 +226,39 @@ namespace TagLib { */ static void setStringHandler(const StringHandler *handler); + /*! + * Returns the code page assumed for an ID3v1 tag read without an explicit + * one, which is 0 until setAssumedCodePage() states another one. + * + * \see setAssumedCodePage() + */ + static unsigned int assumedCodePage(); + + /*! + * Sets the code page \a codePage assumed for an ID3v1 tag read without an + * explicit one: a tag read through the two argument constructor, or one + * read with a \a codePage of 0 or of a value this library cannot convert. + * + * \a codePage is a Windows code page number, the same numbering a CSET + * chunk of a RIFF file uses: 65001 assumes UTF-8, 1252 assumes + * Windows-1252, and 0, the initial value, assumes the ISO 8859-1 the + * ID3v1 specification prescribes. + * + * ID3v1 has no way to declare a charset, so a file that holds UTF-8 or + * any Windows code page looks exactly like one holding ISO 8859-1. This + * is where the caller says which of them its files hold. + * + * \note This is a process wide setting, like setStringHandler(), and + * applies to the tags read after the call. + * + * \note A code page this library cannot convert is refused: the call then + * has no effect and a debug message is emitted. + * + * \see assumedCodePage() + * \see setStringHandler() + */ + static void setAssumedCodePage(unsigned int codePage); + protected: /*! * Reads from the file specified in the constructor. diff --git a/taglib/riff/wav/infotag.cpp b/taglib/riff/wav/infotag.cpp index 6c43e1fc..6c241541 100644 --- a/taglib/riff/wav/infotag.cpp +++ b/taglib/riff/wav/infotag.cpp @@ -25,9 +25,13 @@ #include "infotag.h" +#include #include +#include "tcharset.h" #include "tbytevector.h" +#include "tdebug.h" +#include "tutils.h" #include "tpropertymap.h" #include "riffutils.h" @@ -38,12 +42,75 @@ namespace { const RIFF::Info::StringHandler defaultStringHandler; const RIFF::Info::StringHandler *stringHandler = &defaultStringHandler; + + //! The code page RIFF::Info::Tag::setAssumedCodePage() states. + //! + //! Used when a file declares none. 0 is the RIFF "unspecified" value and + //! keeps the ISO 8859-1 the specification prescribes for it. + unsigned int codePageAssumed = 0; + + //! Look up charset for \a codePage. + //! + //! The charset a file's CSET code page selects. A number with no table is + //! the file declaring something we cannot follow, so it falls to the RIFF + //! default Latin-1 rather than the assumed page: a declared number is + //! evidence about that file, not a caller hint. 0 means the file declared + //! nothing, so the assumed page applies. + Charset::Type charsetForCodePage(unsigned int codePage) + { + if(codePage == 0) { + codePage = codePageAssumed; + } + else if(const auto charset = Charset::typeForCodePage(codePage)) { + return *charset; + } + else { + debug(Utils::formatString( + "RIFF::Info::Tag::parse() - Unsupported CSET code page %u, " + "using the RIFF default ISO 8859-1.", codePage)); + return Charset::Type::Latin1; + } + + // The file declares nothing. + // Fast path without lookup for the likely default case. + if(codePage == 0) + return Charset::Type::Latin1; + + // If we get here, setAssumedCodePage() stored a verified code page, not 0, + // the condition will always be true. + if(const auto charset = Charset::typeForCodePage(codePage)) + return *charset; + + return Charset::Type::Latin1; + } + + String decodeField(const ByteVector &data, Charset::Type charset) + { + // RIFF INFO fields are NUL terminated, which Charset::decode already + // handles, so unlike the ID3v1 decoder nothing is stripped here. + return Charset::decode(data, charset); + } } // namespace class RIFF::Info::Tag::TagPrivate { public: FieldListMap fieldListMap; + + //! The charset the file's CSET chunk declares, resolved once when the tag is + //! parsed. RIFF says nothing about the charset without one, so ISO 8859-1 is + //! the default. + Charset::Type charset { Charset::Type::Latin1 }; + + String parseField(const ByteVector &data) const + { + // A handler the user installed with setStringHandler() is an explicit + // override, so it takes precedence over what the file declares. With no + // handler installed the field is decoded with the charset. + if(stringHandler != &defaultStringHandler) + return stringHandler->parse(data); + return decodeField(data, charset); + } }; class RIFF::Info::StringHandler::StringHandlerPrivate @@ -60,12 +127,12 @@ StringHandler::~StringHandler() = default; String RIFF::Info::StringHandler::parse(const ByteVector &data) const { - return String(data, String::UTF8); + return Charset::decode(data, Charset::Type::Latin1); } ByteVector RIFF::Info::StringHandler::render(const String &s) const { - return s.data(String::UTF8); + return Charset::encode(s, Charset::Type::UTF8); } //////////////////////////////////////////////////////////////////////////////// @@ -73,8 +140,14 @@ ByteVector RIFF::Info::StringHandler::render(const String &s) const //////////////////////////////////////////////////////////////////////////////// RIFF::Info::Tag::Tag(const ByteVector &data) : + Tag(data, 0) +{ +} + +RIFF::Info::Tag::Tag(const ByteVector &data, unsigned int codePage) : d(std::make_unique()) { + d->charset = charsetForCodePage(codePage); parse(data); } @@ -307,6 +380,23 @@ void RIFF::Info::Tag::setStringHandler(const StringHandler *handler) stringHandler = &defaultStringHandler; } +unsigned int RIFF::Info::Tag::assumedCodePage() +{ + return codePageAssumed; +} + +void RIFF::Info::Tag::setAssumedCodePage(unsigned int codePage) +{ + if(codePage != 0 && !Charset::typeForCodePage(codePage)) { + debug(Utils::formatString( + "RIFF::Info::Tag::setAssumedCodePage() - Unsupported code page %u, ignored.", + codePage)); + return; + } + + codePageAssumed = codePage; +} + //////////////////////////////////////////////////////////////////////////////// // protected members //////////////////////////////////////////////////////////////////////////////// @@ -320,10 +410,8 @@ void RIFF::Info::Tag::parse(const ByteVector &data) if(size > data.size() - p - 8) break; - if(const ByteVector id = data.mid(p, 4); isValidChunkName(id)) { - const String text = stringHandler->parse(data.mid(p + 8, size)); - d->fieldListMap[id] = text; - } + if(const ByteVector id = data.mid(p, 4); isValidChunkName(id)) + d->fieldListMap[id] = d->parseField(data.mid(p + 8, size)); p += ((size + 1) & ~1) + 8; } diff --git a/taglib/riff/wav/infotag.h b/taglib/riff/wav/infotag.h index b9ab4352..dee22e76 100644 --- a/taglib/riff/wav/infotag.h +++ b/taglib/riff/wav/infotag.h @@ -45,17 +45,13 @@ namespace TagLib { //! An abstraction for the string to data encoding in Info tags. /*! - * RIFF INFO tag has no clear definitions about character encodings. - * In practice, local encoding of each system is largely used and UTF-8 is - * popular too. - * - * Here is an option to read and write tags in your preferred encoding - * by subclassing this class, reimplementing parse() and render() and setting - * your reimplementation as the default with Info::Tag::setStringHandler(). - * - * \see ID3v1::Tag::setStringHandler() + * Per the RIFF specification, the character encoding of the INFO tag is + * declared with a top-level CSET chunk; when it is absent, the default is + * the ISO 8859-1 (Latin-1) ANSI code page. A declared code page is applied + * as the tag is read, so the text is a normal in-memory String from there + * on, and render() writes UTF-8. + * https://www.robotplanet.dk/audio/wav_meta_data/riff_mci.pdf#page=25 */ - class TAGLIB_EXPORT StringHandler { public: @@ -67,7 +63,8 @@ namespace TagLib { /*! * Decode a string from \a data. The default implementation assumes that - * \a data is an UTF-8 character array. + * \a data is an ISO 8859-1 (Latin-1) character array, which is the RIFF + * default when the file declares no CSET code page. */ virtual String parse(const ByteVector &data) const; @@ -102,9 +99,40 @@ namespace TagLib { /*! * Constructs an INFO tag read from \a data which is the contents of the "LIST" chunk. + * + * \a data comes from a file that declares no code page, so the text is decoded + * with assumedCodePage(), unless a handler installed with setStringHandler() + * takes over. + * + * \see assumedCodePage() */ Tag(const ByteVector &data); + /*! + * Constructs an INFO tag read from \a data which is the contents of the "LIST" chunk, + * decoding the text with the code page \a codePage declared by the file's top-level + * CSET chunk. + * + * \a codePage only decides how \a data is read. The text becomes a normal + * in-memory String the moment it is read, and render() writes UTF-8, which is + * what RIFF::WAV::File::save() declares in the CSET chunk it writes. A tag is + * never converted back into the code page it was read from. + * + * Code page 0 is the RIFF "unspecified" value, used when a file has no CSET + * chunk, and means the code page assumedCodePage() names, which is ISO 8859-1 + * (Latin-1) unless the caller has stated another one. A code page this library + * has no table for also means ISO 8859-1: the number is a hint about how the + * text was written, and the fields are still read rather than dropped over a + * hint that could not be followed. + * + * \note A handler installed with setStringHandler() takes precedence over + * \a codePage, because installing one is an explicit override. + * + * \see assumedCodePage() + * \see setStringHandler() + */ + Tag(const ByteVector &data, unsigned int codePage); + ~Tag() override; Tag(const Tag &) = delete; @@ -177,7 +205,12 @@ namespace TagLib { * Sets the string handler that decides how the text data will be * converted to and from binary data. * If the parameter \a handler is null, the previous handler is - * released and default UTF-8 handler is restored. + * released and the default (ISO 8859-1 decode, UTF-8 encode) handler + * is restored. + * + * \note Installing a handler is an explicit override, so it is used for + * every INFO tag, including those read from a file that declares a CSET + * code page. Pass null to hand those files back to their own code page. * * \note The caller is responsible for deleting the previous handler * as needed after it is released. @@ -186,6 +219,44 @@ namespace TagLib { */ static void setStringHandler(const StringHandler *handler); + /*! + * Returns the code page assumed for an INFO tag read from a file that declares + * none, which is 0 until setAssumedCodePage() states another one. + * + * \see setAssumedCodePage() + */ + static unsigned int assumedCodePage(); + + /*! + * Sets the code page \a codePage assumed for an INFO tag whose file declares no + * charset: a file without a CSET chunk, a CSET chunk whose wCodePage is zero, or + * an INFO list read without its CSET chunk, as in Tag(const ByteVector &data). + * + * \a codePage is a Windows code page number, the same numbering the CSET chunk + * uses: 65001 assumes UTF-8, 1252 assumes Windows-1252, and 0, the initial value, + * assumes the ISO 8859-1 the RIFF specification prescribes for a file that says + * nothing. + * https://www.robotplanet.dk/audio/wav_meta_data/riff_mci.pdf#page=25 + * + * The specification cannot tell a file holding UTF-8 or an ANSI code page without + * a CSET chunk from one holding ISO 8859-1, because all three look the same on + * disk. This is where the caller says which of them its files hold. + * + * \note This is a process wide setting, like setStringHandler(), and applies to + * the tags read after the call. It never overrides a code page a file declares + * itself, not even one this library has no table for. + * + * \note It only decides how text is read. save() writes UTF-8 and declares that + * in the CSET chunk it writes, whatever is assumed here. + * + * \note A code page this library cannot convert is refused: the call then has no + * effect and a debug message is emitted. + * + * \see assumedCodePage() + * \see setStringHandler() + */ + static void setAssumedCodePage(unsigned int codePage); + protected: /*! * Parses the body of the tag in \a data. diff --git a/taglib/riff/wav/wavfile.cpp b/taglib/riff/wav/wavfile.cpp index 821a70b8..ed7dfa78 100644 --- a/taglib/riff/wav/wavfile.cpp +++ b/taglib/riff/wav/wavfile.cpp @@ -25,6 +25,9 @@ #include "wavfile.h" +#include + +#include "tcharset.h" #include "tdebug.h" #include "tpropertymap.h" #include "tagutils.h" @@ -36,6 +39,40 @@ using namespace TagLib; namespace { enum { ID3v2Index = 0, InfoIndex = 1 }; + + /*! + * Render CSET chunk. + * + * A CSET chunk declares the charset of the file's text and is laid out as + * -> CSET( + * ) + * so the code page is its first 16 bits. The other three fields name the + * country, language and dialect the text was written for; zero is the + * spec's "no opinion" value for each of them. + * https://www.robotplanet.dk/audio/wav_meta_data/riff_mci.pdf#page=25 + */ + ByteVector renderCSET(unsigned int codePage) + { + constexpr unsigned int CSETChunkSize = 8; + + ByteVector cset = ByteVector::fromShort(static_cast(codePage), false); + cset.resize(CSETChunkSize, 0); + return cset; + } + + /*! + * Reads the code page out of a CSET payload. Two bytes are enough: that is + * the whole of wCodePage, and the three fields behind it only add detail the + * code page does not depend on. A payload shorter than that is malformed, as + * it cannot hold the number the chunk exists to declare, so it is rejected + * rather than guessed at and the file keeps the RIFF default. + */ + std::optional parseCSET(const ByteVector &data) + { + if(data.size() >= 2) + return static_cast(data.toShort(0, false)); + return std::nullopt; + } } // namespace class RIFF::WAV::File::FilePrivate @@ -55,6 +92,7 @@ public: bool hasID3v2 { false }; bool hasInfo { false }; + bool hasCSET { false }; bool hasiXML { false }; bool hasBEXT { false }; @@ -219,9 +257,17 @@ bool RIFF::WAV::File::save(TagTypes tags, StripTags strip, ID3v2::Version versio if(tags & Info) { removeTagChunks(Info); - if(InfoTag() && !InfoTag()->isEmpty()) { - setChunkData("LIST", InfoTag()->render(), true); + // render() is empty when no field survives, which is also when there is + // nothing for a CSET chunk to describe. + const ByteVector info = InfoTag() ? InfoTag()->render() : ByteVector(); + if(!info.isEmpty()) { + setChunkData("LIST", info, true); + + // The INFO tag was just rendered as UTF-8, so say so. + // Without this a reader would fall back to the RIFF default of Latin1 and decode wrongly. + setChunkData("CSET", renderCSET(Charset::codePageForType(Charset::Type::UTF8))); d->hasInfo = true; + d->hasCSET = true; } } @@ -254,6 +300,9 @@ bool RIFF::WAV::File::hasBEXTData() const void RIFF::WAV::File::read(bool readProperties) { + std::optional codePage; + ByteVector infoData; + for(unsigned int i = 0; i < chunkCount(); ++i) { if(const ByteVector name = chunkName(i); name == "ID3 " || name == "id3 ") { if(!d->tag[ID3v2Index]) { @@ -265,14 +314,25 @@ void RIFF::WAV::File::read(bool readProperties) debug("RIFF::WAV::File::read() - Duplicate ID3v2 tag found."); } } + else if(name == "CSET") { + if(d->hasCSET) { + debug("RIFF::WAV::File::read() - Duplicate CSET chunk found."); + } + else if(const ByteVector data = chunkData(i); const auto page = parseCSET(data)) { + codePage = *page; + d->hasCSET = true; + } + else { + debug("RIFF::WAV::File::read() - Invalid CSET chunk found."); + } + } else if(name == "LIST") { if(const ByteVector data = chunkData(i); data.startsWith("INFO")) { - if(!d->tag[InfoIndex]) { - d->tag.set(InfoIndex, new RIFF::Info::Tag(data)); - d->hasInfo = true; + if(!infoData.isEmpty()) { + debug("RIFF::WAV::File::read() - Duplicate INFO tag found."); } else { - debug("RIFF::WAV::File::read() - Duplicate INFO tag found."); + infoData = data; } } } @@ -286,6 +346,11 @@ void RIFF::WAV::File::read(bool readProperties) } } + if(!infoData.isEmpty()) { + d->tag.set(InfoIndex, new RIFF::Info::Tag(infoData, codePage.value_or(0))); + d->hasInfo = true; + } + if(!d->tag[ID3v2Index]) d->tag.set(ID3v2Index, new ID3v2::Tag(nullptr, 0, d->ID3v2FrameFactory)); @@ -305,12 +370,17 @@ void RIFF::WAV::File::removeTagChunks(TagTypes tags) d->hasID3v2 = false; } - if((tags & Info) && d->hasInfo) { + if((tags & Info) && (d->hasInfo || d->hasCSET)) { for(int i = static_cast(chunkCount()) - 1; i >= 0; --i) { if(chunkName(i) == "LIST" && chunkData(i).startsWith("INFO")) removeChunk(i); } + // A CSET chunk is only meaningful together with an INFO tag, so it goes + // when the INFO tag goes. save() writes a new one if it writes the tag back. + removeChunk("CSET"); + d->hasInfo = false; + d->hasCSET = false; } } diff --git a/taglib/taglib_config.h.cmake b/taglib/taglib_config.h.cmake index 06e51756..ff669020 100644 --- a/taglib/taglib_config.h.cmake +++ b/taglib/taglib_config.h.cmake @@ -13,5 +13,6 @@ #cmakedefine TAGLIB_WITH_SHORTEN 1 #cmakedefine TAGLIB_WITH_TRUEAUDIO 1 #cmakedefine TAGLIB_WITH_VORBIS 1 +#cmakedefine TAGLIB_WITH_CHARSETTABLES 1 #endif diff --git a/taglib/toolkit/charsettables-generator.py b/taglib/toolkit/charsettables-generator.py new file mode 100755 index 00000000..82500aa2 --- /dev/null +++ b/taglib/toolkit/charsettables-generator.py @@ -0,0 +1,264 @@ +#!/usr/bin/env python3 + +""" +Charset table generator. Run it like + python3 taglib/toolkit/charsettables-generator.py +It invokes iconv to generate the mapping tables from codepage byte to unicode +for each codepage. + +See also: +* Microsoft Code Pages https://learn.microsoft.com/en-us/windows/win32/intl/code-page-identifiers +""" + +import os +import subprocess +import sys + +HERE = os.path.dirname(os.path.abspath(__file__)) # folder where this script resides + +REPLACEMENT_CHAR = 0xFFFD +DELIMITER = 0x0000 # U+0000, see via_iconv() + +# Table: Microsoft Codepage, Enum, Description, iconv(1) name +PAGES = [ + (37, "EBCDIC037", "IBM EBCDIC US-Canada", "IBM037"), + (437, "OEM437", "IBM437 OEM US", "CP437"), + (500, "EBCDIC500", "IBM EBCDIC International", "CP500"), + (708, "OEM708", "ASMO-708 Arabic", "ASMO-708"), + (737, "OEM737", "IBM737 OEM Greek", "CP737"), + (775, "OEM775", "IBM775 OEM Baltic", "CP775"), + (850, "OEM850", "IBM850 OEM Latin 1", "CP850"), + (852, "OEM852", "IBM852 OEM Latin 2", "CP852"), + (855, "OEM855", "IBM855 OEM Cyrillic", "CP855"), + (857, "OEM857", "IBM857 OEM Turkish", "CP857"), + (858, "OEM858", "IBM858 OEM Latin 1 with Euro", "CP858"), + (860, "OEM860", "IBM860 OEM Portuguese", "CP860"), + (861, "OEM861", "IBM861 OEM Icelandic", "CP861"), + (862, "OEM862", "DOS-862 OEM Hebrew", "CP862"), + (863, "OEM863", "IBM863 OEM French Canadian", "CP863"), + (864, "OEM864", "IBM864 OEM Arabic", "CP864"), + (865, "OEM865", "IBM865 OEM Nordic", "CP865"), + (866, "OEM866", "IBM866 OEM Russian", "CP866"), + (869, "OEM869", "IBM869 OEM Modern Greek", "CP869"), + (874, "Thai874", "Thai (Windows-874)", "CP874"), + (1047, "EBCDIC1047", "IBM1047 EBCDIC Latin 1/Open System", "IBM1047"), # https://public.dhe.ibm.com/software/globalization/gcoc/attachments/CP01047.txt + (1250, "Windows1250", "Windows-1250 Central European", "CP1250"), + (1251, "Windows1251", "Windows-1251 Cyrillic", "CP1251"), + (1252, "Windows1252", "Windows-1252 Western European", "CP1252"), + (1253, "Windows1253", "Windows-1253 Greek", "CP1253"), + (1254, "Windows1254", "Windows-1254 Turkish", "CP1254"), + (1255, "Windows1255", "Windows-1255 Hebrew", "CP1255"), + (1256, "Windows1256", "Windows-1256 Arabic", "CP1256"), + (1257, "Windows1257", "Windows-1257 Baltic", "CP1257"), + (1258, "Windows1258", "Windows-1258 Vietnamese", "CP1258"), + (20127, "ASCII", "US-ASCII", "ASCII"), + (20273, "EBCDIC273", "IBM273 EBCDIC Germany", "IBM273"), + (20277, "EBCDIC277", "IBM277 EBCDIC Denmark-Norway", "IBM277"), + (20278, "EBCDIC278", "IBM278 EBCDIC Finland-Sweden", "IBM278"), + (20280, "EBCDIC280", "IBM280 EBCDIC Italy", "IBM280"), + (20284, "EBCDIC284", "IBM284 EBCDIC Latin America-Spain", "IBM284"), + (20285, "EBCDIC285", "IBM285 EBCDIC United Kingdom", "IBM285"), + (20290, "EBCDIC290", "IBM290 EBCDIC Japanese Katakana Extended", "IBM290"), + (20297, "EBCDIC297", "IBM297 EBCDIC France", "IBM297"), + (20420, "EBCDIC420", "IBM420 EBCDIC Arabic", "IBM420"), + (20423, "EBCDIC423", "IBM423 EBCDIC Greek", "IBM423"), + (20866, "KOI8R", "KOI8-R", "KOI8-R"), + (20871, "EBCDIC871", "IBM871 EBCDIC Icelandic", "IBM871"), + (21025, "EBCDIC1025", "IBM1025 EBCDIC Cyrillic Serbian-Bulgarian", "CP1025"), + (21866, "KOI8U", "KOI8-U", "KOI8-U"), + (28591, "Latin1", "ISO 8859-1", "ISO-8859-1"), + (28592, "Latin2", "ISO 8859-2", "ISO-8859-2"), + (28593, "Latin3", "ISO 8859-3", "ISO-8859-3"), + (28594, "Latin4", "ISO 8859-4", "ISO-8859-4"), + (28595, "Latin5", "ISO 8859-5", "ISO-8859-5"), + (28596, "Latin6", "ISO 8859-6", "ISO-8859-6"), + (28597, "Latin7", "ISO 8859-7", "ISO-8859-7"), + (28598, "Latin8", "ISO 8859-8 Hebrew (logical)", "ISO-8859-8"), + (28599, "Latin9", "ISO 8859-9", "ISO-8859-9"), + (28603, "Latin13", "ISO 8859-13", "ISO-8859-13"), + (28605, "Latin15", "ISO 8859-15", "ISO-8859-15"), + + # The "Euro" EBCDIC pages, which iconv(1) knows natively. + (1140, "EBCDIC1140", "IBM037 EBCDIC US-Canada with Euro", "CP1140"), + (1141, "EBCDIC1141", "IBM273 EBCDIC Germany with Euro", "CP1141"), + (1142, "EBCDIC1142", "IBM277 EBCDIC Denmark-Norway with Euro", "CP1142"), + (1143, "EBCDIC1143", "IBM278 EBCDIC Finland-Sweden with Euro", "CP1143"), + (1144, "EBCDIC1144", "IBM280 EBCDIC Italy with Euro", "CP1144"), + (1145, "EBCDIC1145", "IBM284 EBCDIC Latin America-Spain with Euro", "CP1145"), + (1146, "EBCDIC1146", "IBM285 EBCDIC UK with Euro", "CP1146"), + (1147, "EBCDIC1147", "IBM297 EBCDIC France with Euro", "CP1147"), + (1148, "EBCDIC1148", "IBM500 EBCDIC International with Euro", "CP1148"), + (1149, "EBCDIC1149", "IBM871 EBCDIC Icelandic with Euro", "CP1149"), +] + +def via_iconv(name): + """The whole page in one iconv(1) run. + + The input is the 256 bytes with a 0x00 in front of every one of them, and -c + makes iconv(1) drop the bytes the page leaves undefined. 0x00 is what + separates them because it is the one byte every page here maps to U+0000 and + the only one that does, so a byte that got dropped leaves its two 0x00 next + to each other and the run comes back as one U+0000 per byte with the code + point of every byte the page does define in between. Byte 0x00 itself is the + one byte the probe cannot ask about, since it is the delimiter: it is NUL in + every page here, and finalize() below checks that it is the only byte that + maps to U+0000. A page that gave some other byte U+0000 as well would lose + that value to the delimiter without complaint, so every table the generator + writes asserts in finalize() that byte 0x00 is its only U+0000. + """ + probe = b"".join(bytes((0x00, b)) for b in range(1, 256)) + r = subprocess.run(["iconv","-f",name,"-t","UCS-4LE","-c"], + input=probe, capture_output=True) + if r.returncode != 0: + sys.exit("iconv(1) does not know the code page %s: %s" + % (name, r.stderr.decode("utf-8", "replace").strip())) + assert len(r.stdout) % 4 == 0, (name, len(r.stdout)) + units = [int.from_bytes(r.stdout[i:i + 4], "little") + for i in range(0, len(r.stdout), 4)] + t = [DELIMITER] + [None]*255 # byte 0x00 is NUL, the delimiter's own value + pos = 0 + for b in range(1, 256): + assert pos < len(units) and units[pos] == DELIMITER, (name, hex(b)) + pos += 1 + if pos < len(units) and units[pos] != DELIMITER: + t[b] = units[pos] + pos += 1 + assert pos == len(units), (name, "trailing code points", pos, len(units)) + return t + +_CACHE = {} +def build(name): + if name not in _CACHE: + _CACHE[name] = via_iconv(name) + return list(_CACHE[name]) + +def finalize(t): + """Fill in the bytes a code page leaves empty, so that every byte becomes a + character and no table has a gap. + + A code page need not give a meaning to every byte. A byte with no meaning + in 0x80-0x9f becomes the character with the same number, so 0x81 becomes + U+0081. Windows does this, and so do browsers: the standard that defines + how browsers read these pages fills the gaps the same way. See section 9.1 + at https://encoding.spec.whatwg.org , and for a full table + https://encoding.spec.whatwg.org/index-windows-1252.txt + + An empty byte anywhere else becomes U+FFFD, the replacement character that + stands for input we could not read. + """ + out = [] + for b in range(256): + v = t[b] + if v is None: + v = b if 0x80 <= b <= 0x9f else REPLACEMENT_CHAR + assert (v == 0) == (b == 0), (b, hex(v)) + assert v <= 0xFFFF, (b, hex(v)) + out.append(v) + return out + +LIC = """ +/*************************************************************************** + * This library is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Lesser General Public License version * + * 2.1 as published by the Free Software Foundation. * + * * + * This library is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * + * 02110-1301 USA * + * * + * Alternatively, this file is available under the Mozilla Public * + * License Version 1.1. You may obtain a copy of the License at * + * http://www.mozilla.org/MPL/ * + ***************************************************************************/ + +/* + THIS FILE IS NOT A PART OF THE TAGLIB API + + Generated data, one table per single byte code page. Each table maps all 256 + byte values to a Unicode code point, so decoding and encoding never has to ask + whether a byte is defined: a byte the code page leaves undefined is mapped to + the C1 control of the same value in 0x80-0x9f (which is what the Windows ANSI + pages do) and to U+FFFD anywhere else. + + Generated by taglib/toolkit/charsettables-generator.py, which asks iconv(1) for + each page in a single conversion of its 256 bytes. + */ +""" + +def emit_cpp(pages): + """pages: list of (code page, enum name, description, 256 values).""" + out = [LIC, "", '#include "charsettables.h"', "", "#include ", + "", "namespace TagLib::Charset::detail {", ""] + for num, name, desc, table in pages: + out.append(" // %d %s" % (num, desc)) + out.append(" constexpr unsigned short %sTable[256] = {" % name) + for row in range(16): + cells = ", ".join("0x%04X" % table[row * 16 + col] for col in range(16)) + out.append(" %s," % cells) + out.append(" };") + out.append("") + out.append(" constexpr CodePageEntry codePageTable[] = {") + for num, name, desc, table in pages: + out.append(' { Type::%s, %d, %sTable },' % (name, num, name)) + out.append(" };") + out.append("") + out.append(" // Every Type but UTF8 has exactly one row.") + out.append(" static_assert(") + out.append(" std::size(codePageTable) == %d," % len(pages)) + out.append(' "the code page table and Charset::Type disagree");') + out.append("") + out.append(" // A Type and the CSET code page number name the same row, so the") + out.append(" // code page of every row has to equal the value of its Type.") + out.append(" static_assert([] {") + out.append(" for(auto entry : codePageTable) {") + out.append(" if(entry.codePage != static_cast(entry.type))") + out.append(" return false;") + out.append(" }") + out.append(" return true;") + out.append(" }(), \"a row's code page does not match the value of its Type\");") + out.append("") + out.append(" // A row is found by its code page, which is also the value of its Type.") + out.append(" // The index is built once, from the table above, and never changes, so") + out.append(" // a lookup cannot move it and the pointer entryForCodePage() hands out") + out.append(" // stays valid. The local static guards that one time build.") + out.append(" static const std::unordered_map &codePageIndex()") + out.append(" {") + out.append(" static const std::unordered_map index = [] {") + out.append(" std::unordered_map m;") + out.append(" m.reserve(std::size(codePageTable));") + out.append(" for(const auto &entry : codePageTable)") + out.append(" m.emplace(entry.codePage, &entry);") + out.append(" return m;") + out.append(" }();") + out.append("") + out.append(" return index;") + out.append(" }") + out.append("") + out.append(" const CodePageEntry *entryForCodePage(unsigned int codePage)") + out.append(" {") + out.append(" const auto &index = codePageIndex();") + out.append(" const auto it = index.find(codePage);") + out.append(" return it != index.end() ? it->second : nullptr;") + out.append(" }") + out.append("") + out.append("} // namespace TagLib::Charset::detail") + out.append("") + path = os.path.join(HERE, "charsettables.cpp") + open(path, "w").write("\n".join(out)) + print("wrote taglib/toolkit/charsettables.cpp (%d code pages, %d values)" + % (len(pages), len(pages) * 256)) + +def main(): + pages = [(num, name, desc, finalize(build(gen))) + for num, name, desc, gen in PAGES] + pages.sort(key=lambda p: p[0]) + emit_cpp(pages) + +if __name__ == "__main__": + main() diff --git a/taglib/toolkit/charsettables.cpp b/taglib/toolkit/charsettables.cpp new file mode 100644 index 00000000..cfaf7f9c --- /dev/null +++ b/taglib/toolkit/charsettables.cpp @@ -0,0 +1,1470 @@ + +/*************************************************************************** + * This library is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Lesser General Public License version * + * 2.1 as published by the Free Software Foundation. * + * * + * This library is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * + * 02110-1301 USA * + * * + * Alternatively, this file is available under the Mozilla Public * + * License Version 1.1. You may obtain a copy of the License at * + * http://www.mozilla.org/MPL/ * + ***************************************************************************/ + +/* + THIS FILE IS NOT A PART OF THE TAGLIB API + + Generated data, one table per single byte code page. Each table maps all 256 + byte values to a Unicode code point, so decoding and encoding never has to ask + whether a byte is defined: a byte the code page leaves undefined is mapped to + the C1 control of the same value in 0x80-0x9f (which is what the Windows ANSI + pages do) and to U+FFFD anywhere else. + + Generated by taglib/toolkit/charsettables-generator.py, which asks iconv(1) for + each page in a single conversion of its 256 bytes. + */ + + +#include "charsettables.h" + +#include + +namespace TagLib::Charset::detail { + + // 37 IBM EBCDIC US-Canada + constexpr unsigned short EBCDIC037Table[256] = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x009C, 0x0009, 0x0086, 0x007F, 0x0097, 0x008D, 0x008E, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x009D, 0x0085, 0x0008, 0x0087, 0x0018, 0x0019, 0x0092, 0x008F, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x000A, 0x0017, 0x001B, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x0005, 0x0006, 0x0007, + 0x0090, 0x0091, 0x0016, 0x0093, 0x0094, 0x0095, 0x0096, 0x0004, 0x0098, 0x0099, 0x009A, 0x009B, 0x0014, 0x0015, 0x009E, 0x001A, + 0x0020, 0x00A0, 0x00E2, 0x00E4, 0x00E0, 0x00E1, 0x00E3, 0x00E5, 0x00E7, 0x00F1, 0x00A2, 0x002E, 0x003C, 0x0028, 0x002B, 0x007C, + 0x0026, 0x00E9, 0x00EA, 0x00EB, 0x00E8, 0x00ED, 0x00EE, 0x00EF, 0x00EC, 0x00DF, 0x0021, 0x0024, 0x002A, 0x0029, 0x003B, 0x00AC, + 0x002D, 0x002F, 0x00C2, 0x00C4, 0x00C0, 0x00C1, 0x00C3, 0x00C5, 0x00C7, 0x00D1, 0x00A6, 0x002C, 0x0025, 0x005F, 0x003E, 0x003F, + 0x00F8, 0x00C9, 0x00CA, 0x00CB, 0x00C8, 0x00CD, 0x00CE, 0x00CF, 0x00CC, 0x0060, 0x003A, 0x0023, 0x0040, 0x0027, 0x003D, 0x0022, + 0x00D8, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x00AB, 0x00BB, 0x00F0, 0x00FD, 0x00FE, 0x00B1, + 0x00B0, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070, 0x0071, 0x0072, 0x00AA, 0x00BA, 0x00E6, 0x00B8, 0x00C6, 0x00A4, + 0x00B5, 0x007E, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x00A1, 0x00BF, 0x00D0, 0x00DD, 0x00DE, 0x00AE, + 0x005E, 0x00A3, 0x00A5, 0x00B7, 0x00A9, 0x00A7, 0x00B6, 0x00BC, 0x00BD, 0x00BE, 0x005B, 0x005D, 0x00AF, 0x00A8, 0x00B4, 0x00D7, + 0x007B, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x00AD, 0x00F4, 0x00F6, 0x00F2, 0x00F3, 0x00F5, + 0x007D, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050, 0x0051, 0x0052, 0x00B9, 0x00FB, 0x00FC, 0x00F9, 0x00FA, 0x00FF, + 0x005C, 0x00F7, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x00B2, 0x00D4, 0x00D6, 0x00D2, 0x00D3, 0x00D5, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x00B3, 0x00DB, 0x00DC, 0x00D9, 0x00DA, 0x009F, + }; + + // 437 IBM437 OEM US + constexpr unsigned short OEM437Table[256] = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, + 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, + 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, + 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, + 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, + 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5, + 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, 0x00FF, 0x00D6, 0x00DC, 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192, + 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, + 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, + 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229, + 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0, + }; + + // 500 IBM EBCDIC International + constexpr unsigned short EBCDIC500Table[256] = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x009C, 0x0009, 0x0086, 0x007F, 0x0097, 0x008D, 0x008E, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x009D, 0x0085, 0x0008, 0x0087, 0x0018, 0x0019, 0x0092, 0x008F, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x000A, 0x0017, 0x001B, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x0005, 0x0006, 0x0007, + 0x0090, 0x0091, 0x0016, 0x0093, 0x0094, 0x0095, 0x0096, 0x0004, 0x0098, 0x0099, 0x009A, 0x009B, 0x0014, 0x0015, 0x009E, 0x001A, + 0x0020, 0x00A0, 0x00E2, 0x00E4, 0x00E0, 0x00E1, 0x00E3, 0x00E5, 0x00E7, 0x00F1, 0x005B, 0x002E, 0x003C, 0x0028, 0x002B, 0x0021, + 0x0026, 0x00E9, 0x00EA, 0x00EB, 0x00E8, 0x00ED, 0x00EE, 0x00EF, 0x00EC, 0x00DF, 0x005D, 0x0024, 0x002A, 0x0029, 0x003B, 0x005E, + 0x002D, 0x002F, 0x00C2, 0x00C4, 0x00C0, 0x00C1, 0x00C3, 0x00C5, 0x00C7, 0x00D1, 0x00A6, 0x002C, 0x0025, 0x005F, 0x003E, 0x003F, + 0x00F8, 0x00C9, 0x00CA, 0x00CB, 0x00C8, 0x00CD, 0x00CE, 0x00CF, 0x00CC, 0x0060, 0x003A, 0x0023, 0x0040, 0x0027, 0x003D, 0x0022, + 0x00D8, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x00AB, 0x00BB, 0x00F0, 0x00FD, 0x00FE, 0x00B1, + 0x00B0, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070, 0x0071, 0x0072, 0x00AA, 0x00BA, 0x00E6, 0x00B8, 0x00C6, 0x00A4, + 0x00B5, 0x007E, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x00A1, 0x00BF, 0x00D0, 0x00DD, 0x00DE, 0x00AE, + 0x00A2, 0x00A3, 0x00A5, 0x00B7, 0x00A9, 0x00A7, 0x00B6, 0x00BC, 0x00BD, 0x00BE, 0x00AC, 0x007C, 0x00AF, 0x00A8, 0x00B4, 0x00D7, + 0x007B, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x00AD, 0x00F4, 0x00F6, 0x00F2, 0x00F3, 0x00F5, + 0x007D, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050, 0x0051, 0x0052, 0x00B9, 0x00FB, 0x00FC, 0x00F9, 0x00FA, 0x00FF, + 0x005C, 0x00F7, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x00B2, 0x00D4, 0x00D6, 0x00D2, 0x00D3, 0x00D5, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x00B3, 0x00DB, 0x00DC, 0x00D9, 0x00DA, 0x009F, + }; + + // 708 ASMO-708 Arabic + constexpr unsigned short OEM708Table[256] = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, + 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, + 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, + 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, + 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, + 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F, + 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F, + 0x00A0, 0xFFFD, 0xFFFD, 0xFFFD, 0x00A4, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0x060C, 0x00AD, 0xFFFD, 0xFFFD, + 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0x061B, 0xFFFD, 0xFFFD, 0xFFFD, 0x061F, + 0xFFFD, 0x0621, 0x0622, 0x0623, 0x0624, 0x0625, 0x0626, 0x0627, 0x0628, 0x0629, 0x062A, 0x062B, 0x062C, 0x062D, 0x062E, 0x062F, + 0x0630, 0x0631, 0x0632, 0x0633, 0x0634, 0x0635, 0x0636, 0x0637, 0x0638, 0x0639, 0x063A, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, + 0x0640, 0x0641, 0x0642, 0x0643, 0x0644, 0x0645, 0x0646, 0x0647, 0x0648, 0x0649, 0x064A, 0x064B, 0x064C, 0x064D, 0x064E, 0x064F, + 0x0650, 0x0651, 0x0652, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, + }; + + // 737 IBM737 OEM Greek + constexpr unsigned short OEM737Table[256] = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, + 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, + 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, + 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, + 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, + 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398, 0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F, 0x03A0, + 0x03A1, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8, + 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, 0x03C0, 0x03C1, 0x03C3, 0x03C2, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, + 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, + 0x03C9, 0x03AC, 0x03AD, 0x03AE, 0x03CA, 0x03AF, 0x03CC, 0x03CD, 0x03CB, 0x03CE, 0x0386, 0x0388, 0x0389, 0x038A, 0x038C, 0x038E, + 0x038F, 0x00B1, 0x2265, 0x2264, 0x03AA, 0x03AB, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0, + }; + + // 775 IBM775 OEM Baltic + constexpr unsigned short OEM775Table[256] = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, + 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, + 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, + 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, + 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, + 0x0106, 0x00FC, 0x00E9, 0x0101, 0x00E4, 0x0123, 0x00E5, 0x0107, 0x0142, 0x0113, 0x0156, 0x0157, 0x012B, 0x0179, 0x00C4, 0x00C5, + 0x00C9, 0x00E6, 0x00C6, 0x014D, 0x00F6, 0x0122, 0x00A2, 0x015A, 0x015B, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x00D7, 0x00A4, + 0x0100, 0x012A, 0x00F3, 0x017B, 0x017C, 0x017A, 0x201D, 0x00A6, 0x00A9, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x0141, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x0104, 0x010C, 0x0118, 0x0116, 0x2563, 0x2551, 0x2557, 0x255D, 0x012E, 0x0160, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x0172, 0x016A, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x017D, + 0x0105, 0x010D, 0x0119, 0x0117, 0x012F, 0x0161, 0x0173, 0x016B, 0x017E, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, + 0x00D3, 0x00DF, 0x014C, 0x0143, 0x00F5, 0x00D5, 0x00B5, 0x0144, 0x0136, 0x0137, 0x013B, 0x013C, 0x0146, 0x0112, 0x0145, 0x2019, + 0x00AD, 0x00B1, 0x201C, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x201E, 0x00B0, 0x2219, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0, + }; + + // 850 IBM850 OEM Latin 1 + constexpr unsigned short OEM850Table[256] = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, + 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, + 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, + 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, + 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, + 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5, + 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, 0x00FF, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x00D7, 0x0192, + 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x00C0, 0x00A9, 0x2563, 0x2551, 0x2557, 0x255D, 0x00A2, 0x00A5, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x00E3, 0x00C3, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4, + 0x00F0, 0x00D0, 0x00CA, 0x00CB, 0x00C8, 0x0131, 0x00CD, 0x00CE, 0x00CF, 0x2518, 0x250C, 0x2588, 0x2584, 0x00A6, 0x00CC, 0x2580, + 0x00D3, 0x00DF, 0x00D4, 0x00D2, 0x00F5, 0x00D5, 0x00B5, 0x00FE, 0x00DE, 0x00DA, 0x00DB, 0x00D9, 0x00FD, 0x00DD, 0x00AF, 0x00B4, + 0x00AD, 0x00B1, 0x2017, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x00B8, 0x00B0, 0x00A8, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0, + }; + + // 852 IBM852 OEM Latin 2 + constexpr unsigned short OEM852Table[256] = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, + 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, + 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, + 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, + 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, + 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x016F, 0x0107, 0x00E7, 0x0142, 0x00EB, 0x0150, 0x0151, 0x00EE, 0x0179, 0x00C4, 0x0106, + 0x00C9, 0x0139, 0x013A, 0x00F4, 0x00F6, 0x013D, 0x013E, 0x015A, 0x015B, 0x00D6, 0x00DC, 0x0164, 0x0165, 0x0141, 0x00D7, 0x010D, + 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x0104, 0x0105, 0x017D, 0x017E, 0x0118, 0x0119, 0x00AC, 0x017A, 0x010C, 0x015F, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x011A, 0x015E, 0x2563, 0x2551, 0x2557, 0x255D, 0x017B, 0x017C, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x0102, 0x0103, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4, + 0x0111, 0x0110, 0x010E, 0x00CB, 0x010F, 0x0147, 0x00CD, 0x00CE, 0x011B, 0x2518, 0x250C, 0x2588, 0x2584, 0x0162, 0x016E, 0x2580, + 0x00D3, 0x00DF, 0x00D4, 0x0143, 0x0144, 0x0148, 0x0160, 0x0161, 0x0154, 0x00DA, 0x0155, 0x0170, 0x00FD, 0x00DD, 0x0163, 0x00B4, + 0x00AD, 0x02DD, 0x02DB, 0x02C7, 0x02D8, 0x00A7, 0x00F7, 0x00B8, 0x00B0, 0x00A8, 0x02D9, 0x0171, 0x0158, 0x0159, 0x25A0, 0x00A0, + }; + + // 855 IBM855 OEM Cyrillic + constexpr unsigned short OEM855Table[256] = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, + 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, + 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, + 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, + 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, + 0x0452, 0x0402, 0x0453, 0x0403, 0x0451, 0x0401, 0x0454, 0x0404, 0x0455, 0x0405, 0x0456, 0x0406, 0x0457, 0x0407, 0x0458, 0x0408, + 0x0459, 0x0409, 0x045A, 0x040A, 0x045B, 0x040B, 0x045C, 0x040C, 0x045E, 0x040E, 0x045F, 0x040F, 0x044E, 0x042E, 0x044A, 0x042A, + 0x0430, 0x0410, 0x0431, 0x0411, 0x0446, 0x0426, 0x0434, 0x0414, 0x0435, 0x0415, 0x0444, 0x0424, 0x0433, 0x0413, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x0445, 0x0425, 0x0438, 0x0418, 0x2563, 0x2551, 0x2557, 0x255D, 0x0439, 0x0419, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x043A, 0x041A, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4, + 0x043B, 0x041B, 0x043C, 0x041C, 0x043D, 0x041D, 0x043E, 0x041E, 0x043F, 0x2518, 0x250C, 0x2588, 0x2584, 0x041F, 0x044F, 0x2580, + 0x042F, 0x0440, 0x0420, 0x0441, 0x0421, 0x0442, 0x0422, 0x0443, 0x0423, 0x0436, 0x0416, 0x0432, 0x0412, 0x044C, 0x042C, 0x2116, + 0x00AD, 0x044B, 0x042B, 0x0437, 0x0417, 0x0448, 0x0428, 0x044D, 0x042D, 0x0449, 0x0429, 0x0447, 0x0427, 0x00A7, 0x25A0, 0x00A0, + }; + + // 857 IBM857 OEM Turkish + constexpr unsigned short OEM857Table[256] = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, + 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, + 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, + 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, + 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, + 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x0131, 0x00C4, 0x00C5, + 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, 0x0130, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x015E, 0x015F, + 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x011E, 0x011F, 0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x00C0, 0x00A9, 0x2563, 0x2551, 0x2557, 0x255D, 0x00A2, 0x00A5, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x00E3, 0x00C3, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4, + 0x00BA, 0x00AA, 0x00CA, 0x00CB, 0x00C8, 0xFFFD, 0x00CD, 0x00CE, 0x00CF, 0x2518, 0x250C, 0x2588, 0x2584, 0x00A6, 0x00CC, 0x2580, + 0x00D3, 0x00DF, 0x00D4, 0x00D2, 0x00F5, 0x00D5, 0x00B5, 0xFFFD, 0x00D7, 0x00DA, 0x00DB, 0x00D9, 0x00EC, 0x00FF, 0x00AF, 0x00B4, + 0x00AD, 0x00B1, 0xFFFD, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x00B8, 0x00B0, 0x00A8, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0, + }; + + // 858 IBM858 OEM Latin 1 with Euro + constexpr unsigned short OEM858Table[256] = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, + 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, + 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, + 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, + 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, + 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5, + 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, 0x00FF, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x00D7, 0x0192, + 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x00C0, 0x00A9, 0x2563, 0x2551, 0x2557, 0x255D, 0x00A2, 0x00A5, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x00E3, 0x00C3, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4, + 0x00F0, 0x00D0, 0x00CA, 0x00CB, 0x00C8, 0x20AC, 0x00CD, 0x00CE, 0x00CF, 0x2518, 0x250C, 0x2588, 0x2584, 0x00A6, 0x00CC, 0x2580, + 0x00D3, 0x00DF, 0x00D4, 0x00D2, 0x00F5, 0x00D5, 0x00B5, 0x00FE, 0x00DE, 0x00DA, 0x00DB, 0x00D9, 0x00FD, 0x00DD, 0x00AF, 0x00B4, + 0x00AD, 0x00B1, 0x2017, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x00B8, 0x00B0, 0x00A8, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0, + }; + + // 860 IBM860 OEM Portuguese + constexpr unsigned short OEM860Table[256] = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, + 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, + 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, + 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, + 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, + 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E3, 0x00E0, 0x00C1, 0x00E7, 0x00EA, 0x00CA, 0x00E8, 0x00CD, 0x00D4, 0x00EC, 0x00C3, 0x00C2, + 0x00C9, 0x00C0, 0x00C8, 0x00F4, 0x00F5, 0x00F2, 0x00DA, 0x00F9, 0x00CC, 0x00D5, 0x00DC, 0x00A2, 0x00A3, 0x00D9, 0x20A7, 0x00D3, + 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x00D2, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, + 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, + 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229, + 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0, + }; + + // 861 IBM861 OEM Icelandic + constexpr unsigned short OEM861Table[256] = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, + 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, + 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, + 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, + 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, + 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00D0, 0x00F0, 0x00DE, 0x00C4, 0x00C5, + 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00FE, 0x00FB, 0x00DD, 0x00FD, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x20A7, 0x0192, + 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00C1, 0x00CD, 0x00D3, 0x00DA, 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, + 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, + 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229, + 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0, + }; + + // 862 DOS-862 OEM Hebrew + constexpr unsigned short OEM862Table[256] = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, + 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, + 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, + 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, + 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, + 0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7, 0x05D8, 0x05D9, 0x05DA, 0x05DB, 0x05DC, 0x05DD, 0x05DE, 0x05DF, + 0x05E0, 0x05E1, 0x05E2, 0x05E3, 0x05E4, 0x05E5, 0x05E6, 0x05E7, 0x05E8, 0x05E9, 0x05EA, 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192, + 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, + 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, + 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229, + 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0, + }; + + // 863 IBM863 OEM French Canadian + constexpr unsigned short OEM863Table[256] = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, + 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, + 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, + 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, + 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, + 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00C2, 0x00E0, 0x00B6, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x2017, 0x00C0, 0x00A7, + 0x00C9, 0x00C8, 0x00CA, 0x00F4, 0x00CB, 0x00CF, 0x00FB, 0x00F9, 0x00A4, 0x00D4, 0x00DC, 0x00A2, 0x00A3, 0x00D9, 0x00DB, 0x0192, + 0x00A6, 0x00B4, 0x00F3, 0x00FA, 0x00A8, 0x00B8, 0x00B3, 0x00AF, 0x00CE, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00BE, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, + 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, + 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229, + 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0, + }; + + // 864 IBM864 OEM Arabic + constexpr unsigned short OEM864Table[256] = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x066A, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, + 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, + 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, + 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, + 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, + 0x00B0, 0x00B7, 0x2219, 0x221A, 0x2592, 0x2500, 0x2502, 0x253C, 0x2524, 0x252C, 0x251C, 0x2534, 0x2510, 0x250C, 0x2514, 0x2518, + 0x03B2, 0x221E, 0x03C6, 0x00B1, 0x00BD, 0x00BC, 0x2248, 0x00AB, 0x00BB, 0xFEF7, 0xFEF8, 0x009B, 0x009C, 0xFEFB, 0xFEFC, 0x009F, + 0x00A0, 0x00AD, 0xFE82, 0x00A3, 0x00A4, 0xFE84, 0xFFFD, 0xFFFD, 0xFE8E, 0xFE8F, 0xFE95, 0xFE99, 0x060C, 0xFE9D, 0xFEA1, 0xFEA5, + 0x0660, 0x0661, 0x0662, 0x0663, 0x0664, 0x0665, 0x0666, 0x0667, 0x0668, 0x0669, 0xFED1, 0x061B, 0xFEB1, 0xFEB5, 0xFEB9, 0x061F, + 0x00A2, 0xFE80, 0xFE81, 0xFE83, 0xFE85, 0xFECA, 0xFE8B, 0xFE8D, 0xFE91, 0xFE93, 0xFE97, 0xFE9B, 0xFE9F, 0xFEA3, 0xFEA7, 0xFEA9, + 0xFEAB, 0xFEAD, 0xFEAF, 0xFEB3, 0xFEB7, 0xFEBB, 0xFEBF, 0xFEC1, 0xFEC5, 0xFECB, 0xFECF, 0x00A6, 0x00AC, 0x00F7, 0x00D7, 0xFEC9, + 0x0640, 0xFED3, 0xFED7, 0xFEDB, 0xFEDF, 0xFEE3, 0xFEE7, 0xFEEB, 0xFEED, 0xFEEF, 0xFEF3, 0xFEBD, 0xFECC, 0xFECE, 0xFECD, 0xFEE1, + 0xFE7D, 0x0651, 0xFEE5, 0xFEE9, 0xFEEC, 0xFEF0, 0xFEF2, 0xFED0, 0xFED5, 0xFEF5, 0xFEF6, 0xFEDD, 0xFED9, 0xFEF1, 0x25A0, 0xFFFD, + }; + + // 865 IBM865 OEM Nordic + constexpr unsigned short OEM865Table[256] = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, + 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, + 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, + 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, + 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, + 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5, + 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, 0x00FF, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x20A7, 0x0192, + 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00A4, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, + 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, + 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229, + 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0, + }; + + // 866 IBM866 OEM Russian + constexpr unsigned short OEM866Table[256] = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, + 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, + 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, + 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, + 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, + 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F, + 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, 0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F, + 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, + 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, + 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F, + 0x0401, 0x0451, 0x0404, 0x0454, 0x0407, 0x0457, 0x040E, 0x045E, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x2116, 0x00A4, 0x25A0, 0x00A0, + }; + + // 869 IBM869 OEM Modern Greek + constexpr unsigned short OEM869Table[256] = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, + 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, + 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, + 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, + 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, + 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0386, 0x0087, 0x00B7, 0x00AC, 0x00A6, 0x2018, 0x2019, 0x0388, 0x2015, 0x0389, + 0x038A, 0x03AA, 0x038C, 0x0093, 0x0094, 0x038E, 0x03AB, 0x00A9, 0x038F, 0x00B2, 0x00B3, 0x03AC, 0x00A3, 0x03AD, 0x03AE, 0x03AF, + 0x03CA, 0x0390, 0x03CC, 0x03CD, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x00BD, 0x0398, 0x0399, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x039A, 0x039B, 0x039C, 0x039D, 0x2563, 0x2551, 0x2557, 0x255D, 0x039E, 0x039F, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x03A0, 0x03A1, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x03A3, + 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9, 0x03B1, 0x03B2, 0x03B3, 0x2518, 0x250C, 0x2588, 0x2584, 0x03B4, 0x03B5, 0x2580, + 0x03B6, 0x03B7, 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, 0x03C0, 0x03C1, 0x03C3, 0x03C2, 0x03C4, 0x0384, + 0x00AD, 0x00B1, 0x03C5, 0x03C6, 0x03C7, 0x00A7, 0x03C8, 0x0385, 0x00B0, 0x00A8, 0x03C9, 0x03CB, 0x03B0, 0x03CE, 0x25A0, 0x00A0, + }; + + // 874 Thai (Windows-874) + constexpr unsigned short Thai874Table[256] = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, + 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, + 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, + 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, + 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, + 0x20AC, 0x0081, 0x0082, 0x0083, 0x0084, 0x2026, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F, + 0x0090, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F, + 0x00A0, 0x0E01, 0x0E02, 0x0E03, 0x0E04, 0x0E05, 0x0E06, 0x0E07, 0x0E08, 0x0E09, 0x0E0A, 0x0E0B, 0x0E0C, 0x0E0D, 0x0E0E, 0x0E0F, + 0x0E10, 0x0E11, 0x0E12, 0x0E13, 0x0E14, 0x0E15, 0x0E16, 0x0E17, 0x0E18, 0x0E19, 0x0E1A, 0x0E1B, 0x0E1C, 0x0E1D, 0x0E1E, 0x0E1F, + 0x0E20, 0x0E21, 0x0E22, 0x0E23, 0x0E24, 0x0E25, 0x0E26, 0x0E27, 0x0E28, 0x0E29, 0x0E2A, 0x0E2B, 0x0E2C, 0x0E2D, 0x0E2E, 0x0E2F, + 0x0E30, 0x0E31, 0x0E32, 0x0E33, 0x0E34, 0x0E35, 0x0E36, 0x0E37, 0x0E38, 0x0E39, 0x0E3A, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0x0E3F, + 0x0E40, 0x0E41, 0x0E42, 0x0E43, 0x0E44, 0x0E45, 0x0E46, 0x0E47, 0x0E48, 0x0E49, 0x0E4A, 0x0E4B, 0x0E4C, 0x0E4D, 0x0E4E, 0x0E4F, + 0x0E50, 0x0E51, 0x0E52, 0x0E53, 0x0E54, 0x0E55, 0x0E56, 0x0E57, 0x0E58, 0x0E59, 0x0E5A, 0x0E5B, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, + }; + + // 1047 IBM1047 EBCDIC Latin 1/Open System + constexpr unsigned short EBCDIC1047Table[256] = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x009C, 0x0009, 0x0086, 0x007F, 0x0097, 0x008D, 0x008E, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x009D, 0x0085, 0x0008, 0x0087, 0x0018, 0x0019, 0x0092, 0x008F, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x000A, 0x0017, 0x001B, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x0005, 0x0006, 0x0007, + 0x0090, 0x0091, 0x0016, 0x0093, 0x0094, 0x0095, 0x0096, 0x0004, 0x0098, 0x0099, 0x009A, 0x009B, 0x0014, 0x0015, 0x009E, 0x001A, + 0x0020, 0x00A0, 0x00E2, 0x00E4, 0x00E0, 0x00E1, 0x00E3, 0x00E5, 0x00E7, 0x00F1, 0x00A2, 0x002E, 0x003C, 0x0028, 0x002B, 0x007C, + 0x0026, 0x00E9, 0x00EA, 0x00EB, 0x00E8, 0x00ED, 0x00EE, 0x00EF, 0x00EC, 0x00DF, 0x0021, 0x0024, 0x002A, 0x0029, 0x003B, 0x005E, + 0x002D, 0x002F, 0x00C2, 0x00C4, 0x00C0, 0x00C1, 0x00C3, 0x00C5, 0x00C7, 0x00D1, 0x00A6, 0x002C, 0x0025, 0x005F, 0x003E, 0x003F, + 0x00F8, 0x00C9, 0x00CA, 0x00CB, 0x00C8, 0x00CD, 0x00CE, 0x00CF, 0x00CC, 0x0060, 0x003A, 0x0023, 0x0040, 0x0027, 0x003D, 0x0022, + 0x00D8, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x00AB, 0x00BB, 0x00F0, 0x00FD, 0x00FE, 0x00B1, + 0x00B0, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070, 0x0071, 0x0072, 0x00AA, 0x00BA, 0x00E6, 0x00B8, 0x00C6, 0x00A4, + 0x00B5, 0x007E, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x00A1, 0x00BF, 0x00D0, 0x005B, 0x00DE, 0x00AE, + 0x00AC, 0x00A3, 0x00A5, 0x00B7, 0x00A9, 0x00A7, 0x00B6, 0x00BC, 0x00BD, 0x00BE, 0x00DD, 0x00A8, 0x00AF, 0x005D, 0x00B4, 0x00D7, + 0x007B, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x00AD, 0x00F4, 0x00F6, 0x00F2, 0x00F3, 0x00F5, + 0x007D, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050, 0x0051, 0x0052, 0x00B9, 0x00FB, 0x00FC, 0x00F9, 0x00FA, 0x00FF, + 0x005C, 0x00F7, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x00B2, 0x00D4, 0x00D6, 0x00D2, 0x00D3, 0x00D5, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x00B3, 0x00DB, 0x00DC, 0x00D9, 0x00DA, 0x009F, + }; + + // 1140 IBM037 EBCDIC US-Canada with Euro + constexpr unsigned short EBCDIC1140Table[256] = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x009C, 0x0009, 0x0086, 0x007F, 0x0097, 0x008D, 0x008E, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x009D, 0x0085, 0x0008, 0x0087, 0x0018, 0x0019, 0x0092, 0x008F, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x000A, 0x0017, 0x001B, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x0005, 0x0006, 0x0007, + 0x0090, 0x0091, 0x0016, 0x0093, 0x0094, 0x0095, 0x0096, 0x0004, 0x0098, 0x0099, 0x009A, 0x009B, 0x0014, 0x0015, 0x009E, 0x001A, + 0x0020, 0x00A0, 0x00E2, 0x00E4, 0x00E0, 0x00E1, 0x00E3, 0x00E5, 0x00E7, 0x00F1, 0x00A2, 0x002E, 0x003C, 0x0028, 0x002B, 0x007C, + 0x0026, 0x00E9, 0x00EA, 0x00EB, 0x00E8, 0x00ED, 0x00EE, 0x00EF, 0x00EC, 0x00DF, 0x0021, 0x0024, 0x002A, 0x0029, 0x003B, 0x00AC, + 0x002D, 0x002F, 0x00C2, 0x00C4, 0x00C0, 0x00C1, 0x00C3, 0x00C5, 0x00C7, 0x00D1, 0x00A6, 0x002C, 0x0025, 0x005F, 0x003E, 0x003F, + 0x00F8, 0x00C9, 0x00CA, 0x00CB, 0x00C8, 0x00CD, 0x00CE, 0x00CF, 0x00CC, 0x0060, 0x003A, 0x0023, 0x0040, 0x0027, 0x003D, 0x0022, + 0x00D8, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x00AB, 0x00BB, 0x00F0, 0x00FD, 0x00FE, 0x00B1, + 0x00B0, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070, 0x0071, 0x0072, 0x00AA, 0x00BA, 0x00E6, 0x00B8, 0x00C6, 0x20AC, + 0x00B5, 0x007E, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x00A1, 0x00BF, 0x00D0, 0x00DD, 0x00DE, 0x00AE, + 0x005E, 0x00A3, 0x00A5, 0x00B7, 0x00A9, 0x00A7, 0x00B6, 0x00BC, 0x00BD, 0x00BE, 0x005B, 0x005D, 0x00AF, 0x00A8, 0x00B4, 0x00D7, + 0x007B, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x00AD, 0x00F4, 0x00F6, 0x00F2, 0x00F3, 0x00F5, + 0x007D, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050, 0x0051, 0x0052, 0x00B9, 0x00FB, 0x00FC, 0x00F9, 0x00FA, 0x00FF, + 0x005C, 0x00F7, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x00B2, 0x00D4, 0x00D6, 0x00D2, 0x00D3, 0x00D5, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x00B3, 0x00DB, 0x00DC, 0x00D9, 0x00DA, 0x009F, + }; + + // 1141 IBM273 EBCDIC Germany with Euro + constexpr unsigned short EBCDIC1141Table[256] = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x009C, 0x0009, 0x0086, 0x007F, 0x0097, 0x008D, 0x008E, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x009D, 0x0085, 0x0008, 0x0087, 0x0018, 0x0019, 0x0092, 0x008F, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x000A, 0x0017, 0x001B, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x0005, 0x0006, 0x0007, + 0x0090, 0x0091, 0x0016, 0x0093, 0x0094, 0x0095, 0x0096, 0x0004, 0x0098, 0x0099, 0x009A, 0x009B, 0x0014, 0x0015, 0x009E, 0x001A, + 0x0020, 0x00A0, 0x00E2, 0x007B, 0x00E0, 0x00E1, 0x00E3, 0x00E5, 0x00E7, 0x00F1, 0x00C4, 0x002E, 0x003C, 0x0028, 0x002B, 0x0021, + 0x0026, 0x00E9, 0x00EA, 0x00EB, 0x00E8, 0x00ED, 0x00EE, 0x00EF, 0x00EC, 0x007E, 0x00DC, 0x0024, 0x002A, 0x0029, 0x003B, 0x005E, + 0x002D, 0x002F, 0x00C2, 0x005B, 0x00C0, 0x00C1, 0x00C3, 0x00C5, 0x00C7, 0x00D1, 0x00F6, 0x002C, 0x0025, 0x005F, 0x003E, 0x003F, + 0x00F8, 0x00C9, 0x00CA, 0x00CB, 0x00C8, 0x00CD, 0x00CE, 0x00CF, 0x00CC, 0x0060, 0x003A, 0x0023, 0x00A7, 0x0027, 0x003D, 0x0022, + 0x00D8, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x00AB, 0x00BB, 0x00F0, 0x00FD, 0x00FE, 0x00B1, + 0x00B0, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070, 0x0071, 0x0072, 0x00AA, 0x00BA, 0x00E6, 0x00B8, 0x00C6, 0x20AC, + 0x00B5, 0x00DF, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x00A1, 0x00BF, 0x00D0, 0x00DD, 0x00DE, 0x00AE, + 0x00A2, 0x00A3, 0x00A5, 0x00B7, 0x00A9, 0x0040, 0x00B6, 0x00BC, 0x00BD, 0x00BE, 0x00AC, 0x007C, 0x00AF, 0x00A8, 0x00B4, 0x00D7, + 0x00E4, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x00AD, 0x00F4, 0x00A6, 0x00F2, 0x00F3, 0x00F5, + 0x00FC, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050, 0x0051, 0x0052, 0x00B9, 0x00FB, 0x007D, 0x00F9, 0x00FA, 0x00FF, + 0x00D6, 0x00F7, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x00B2, 0x00D4, 0x005C, 0x00D2, 0x00D3, 0x00D5, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x00B3, 0x00DB, 0x005D, 0x00D9, 0x00DA, 0x009F, + }; + + // 1142 IBM277 EBCDIC Denmark-Norway with Euro + constexpr unsigned short EBCDIC1142Table[256] = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x009C, 0x0009, 0x0086, 0x007F, 0x0097, 0x008D, 0x008E, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x009D, 0x0085, 0x0008, 0x0087, 0x0018, 0x0019, 0x0092, 0x008F, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x000A, 0x0017, 0x001B, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x0005, 0x0006, 0x0007, + 0x0090, 0x0091, 0x0016, 0x0093, 0x0094, 0x0095, 0x0096, 0x0004, 0x0098, 0x0099, 0x009A, 0x009B, 0x0014, 0x0015, 0x009E, 0x001A, + 0x0020, 0x00A0, 0x00E2, 0x00E4, 0x00E0, 0x00E1, 0x00E3, 0x007D, 0x00E7, 0x00F1, 0x0023, 0x002E, 0x003C, 0x0028, 0x002B, 0x0021, + 0x0026, 0x00E9, 0x00EA, 0x00EB, 0x00E8, 0x00ED, 0x00EE, 0x00EF, 0x00EC, 0x00DF, 0x20AC, 0x00C5, 0x002A, 0x0029, 0x003B, 0x005E, + 0x002D, 0x002F, 0x00C2, 0x00C4, 0x00C0, 0x00C1, 0x00C3, 0x0024, 0x00C7, 0x00D1, 0x00F8, 0x002C, 0x0025, 0x005F, 0x003E, 0x003F, + 0x00A6, 0x00C9, 0x00CA, 0x00CB, 0x00C8, 0x00CD, 0x00CE, 0x00CF, 0x00CC, 0x0060, 0x003A, 0x00C6, 0x00D8, 0x0027, 0x003D, 0x0022, + 0x0040, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x00AB, 0x00BB, 0x00F0, 0x00FD, 0x00FE, 0x00B1, + 0x00B0, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070, 0x0071, 0x0072, 0x00AA, 0x00BA, 0x007B, 0x00B8, 0x005B, 0x005D, + 0x00B5, 0x00FC, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x00A1, 0x00BF, 0x00D0, 0x00DD, 0x00DE, 0x00AE, + 0x00A2, 0x00A3, 0x00A5, 0x00B7, 0x00A9, 0x00A7, 0x00B6, 0x00BC, 0x00BD, 0x00BE, 0x00AC, 0x007C, 0x00AF, 0x00A8, 0x00B4, 0x00D7, + 0x00E6, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x00AD, 0x00F4, 0x00F6, 0x00F2, 0x00F3, 0x00F5, + 0x00E5, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050, 0x0051, 0x0052, 0x00B9, 0x00FB, 0x007E, 0x00F9, 0x00FA, 0x00FF, + 0x005C, 0x00F7, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x00B2, 0x00D4, 0x00D6, 0x00D2, 0x00D3, 0x00D5, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x00B3, 0x00DB, 0x00DC, 0x00D9, 0x00DA, 0x009F, + }; + + // 1143 IBM278 EBCDIC Finland-Sweden with Euro + constexpr unsigned short EBCDIC1143Table[256] = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x009C, 0x0009, 0x0086, 0x007F, 0x0097, 0x008D, 0x008E, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x009D, 0x0085, 0x0008, 0x0087, 0x0018, 0x0019, 0x0092, 0x008F, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x000A, 0x0017, 0x001B, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x0005, 0x0006, 0x0007, + 0x0090, 0x0091, 0x0016, 0x0093, 0x0094, 0x0095, 0x0096, 0x0004, 0x0098, 0x0099, 0x009A, 0x009B, 0x0014, 0x0015, 0x009E, 0x001A, + 0x0020, 0x00A0, 0x00E2, 0x007B, 0x00E0, 0x00E1, 0x00E3, 0x007D, 0x00E7, 0x00F1, 0x00A7, 0x002E, 0x003C, 0x0028, 0x002B, 0x0021, + 0x0026, 0x0060, 0x00EA, 0x00EB, 0x00E8, 0x00ED, 0x00EE, 0x00EF, 0x00EC, 0x00DF, 0x20AC, 0x00C5, 0x002A, 0x0029, 0x003B, 0x005E, + 0x002D, 0x002F, 0x00C2, 0x0023, 0x00C0, 0x00C1, 0x00C3, 0x0024, 0x00C7, 0x00D1, 0x00F6, 0x002C, 0x0025, 0x005F, 0x003E, 0x003F, + 0x00F8, 0x005C, 0x00CA, 0x00CB, 0x00C8, 0x00CD, 0x00CE, 0x00CF, 0x00CC, 0x00E9, 0x003A, 0x00C4, 0x00D6, 0x0027, 0x003D, 0x0022, + 0x00D8, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x00AB, 0x00BB, 0x00F0, 0x00FD, 0x00FE, 0x00B1, + 0x00B0, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070, 0x0071, 0x0072, 0x00AA, 0x00BA, 0x00E6, 0x00B8, 0x00C6, 0x005D, + 0x00B5, 0x00FC, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x00A1, 0x00BF, 0x00D0, 0x00DD, 0x00DE, 0x00AE, + 0x00A2, 0x00A3, 0x00A5, 0x00B7, 0x00A9, 0x005B, 0x00B6, 0x00BC, 0x00BD, 0x00BE, 0x00AC, 0x007C, 0x00AF, 0x00A8, 0x00B4, 0x00D7, + 0x00E4, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x00AD, 0x00F4, 0x00A6, 0x00F2, 0x00F3, 0x00F5, + 0x00E5, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050, 0x0051, 0x0052, 0x00B9, 0x00FB, 0x007E, 0x00F9, 0x00FA, 0x00FF, + 0x00C9, 0x00F7, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x00B2, 0x00D4, 0x0040, 0x00D2, 0x00D3, 0x00D5, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x00B3, 0x00DB, 0x00DC, 0x00D9, 0x00DA, 0x009F, + }; + + // 1144 IBM280 EBCDIC Italy with Euro + constexpr unsigned short EBCDIC1144Table[256] = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x009C, 0x0009, 0x0086, 0x007F, 0x0097, 0x008D, 0x008E, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x009D, 0x0085, 0x0008, 0x0087, 0x0018, 0x0019, 0x0092, 0x008F, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x000A, 0x0017, 0x001B, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x0005, 0x0006, 0x0007, + 0x0090, 0x0091, 0x0016, 0x0093, 0x0094, 0x0095, 0x0096, 0x0004, 0x0098, 0x0099, 0x009A, 0x009B, 0x0014, 0x0015, 0x009E, 0x001A, + 0x0020, 0x00A0, 0x00E2, 0x00E4, 0x007B, 0x00E1, 0x00E3, 0x00E5, 0x005C, 0x00F1, 0x00B0, 0x002E, 0x003C, 0x0028, 0x002B, 0x0021, + 0x0026, 0x005D, 0x00EA, 0x00EB, 0x007D, 0x00ED, 0x00EE, 0x00EF, 0x007E, 0x00DF, 0x00E9, 0x0024, 0x002A, 0x0029, 0x003B, 0x005E, + 0x002D, 0x002F, 0x00C2, 0x00C4, 0x00C0, 0x00C1, 0x00C3, 0x00C5, 0x00C7, 0x00D1, 0x00F2, 0x002C, 0x0025, 0x005F, 0x003E, 0x003F, + 0x00F8, 0x00C9, 0x00CA, 0x00CB, 0x00C8, 0x00CD, 0x00CE, 0x00CF, 0x00CC, 0x00F9, 0x003A, 0x00A3, 0x00A7, 0x0027, 0x003D, 0x0022, + 0x00D8, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x00AB, 0x00BB, 0x00F0, 0x00FD, 0x00FE, 0x00B1, + 0x005B, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070, 0x0071, 0x0072, 0x00AA, 0x00BA, 0x00E6, 0x00B8, 0x00C6, 0x20AC, + 0x00B5, 0x00EC, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x00A1, 0x00BF, 0x00D0, 0x00DD, 0x00DE, 0x00AE, + 0x00A2, 0x0023, 0x00A5, 0x00B7, 0x00A9, 0x0040, 0x00B6, 0x00BC, 0x00BD, 0x00BE, 0x00AC, 0x007C, 0x00AF, 0x00A8, 0x00B4, 0x00D7, + 0x00E0, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x00AD, 0x00F4, 0x00F6, 0x00A6, 0x00F3, 0x00F5, + 0x00E8, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050, 0x0051, 0x0052, 0x00B9, 0x00FB, 0x00FC, 0x0060, 0x00FA, 0x00FF, + 0x00E7, 0x00F7, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x00B2, 0x00D4, 0x00D6, 0x00D2, 0x00D3, 0x00D5, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x00B3, 0x00DB, 0x00DC, 0x00D9, 0x00DA, 0x009F, + }; + + // 1145 IBM284 EBCDIC Latin America-Spain with Euro + constexpr unsigned short EBCDIC1145Table[256] = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x009C, 0x0009, 0x0086, 0x007F, 0x0097, 0x008D, 0x008E, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x009D, 0x0085, 0x0008, 0x0087, 0x0018, 0x0019, 0x0092, 0x008F, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x000A, 0x0017, 0x001B, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x0005, 0x0006, 0x0007, + 0x0090, 0x0091, 0x0016, 0x0093, 0x0094, 0x0095, 0x0096, 0x0004, 0x0098, 0x0099, 0x009A, 0x009B, 0x0014, 0x0015, 0x009E, 0x001A, + 0x0020, 0x00A0, 0x00E2, 0x00E4, 0x00E0, 0x00E1, 0x00E3, 0x00E5, 0x00E7, 0x00A6, 0x005B, 0x002E, 0x003C, 0x0028, 0x002B, 0x007C, + 0x0026, 0x00E9, 0x00EA, 0x00EB, 0x00E8, 0x00ED, 0x00EE, 0x00EF, 0x00EC, 0x00DF, 0x005D, 0x0024, 0x002A, 0x0029, 0x003B, 0x00AC, + 0x002D, 0x002F, 0x00C2, 0x00C4, 0x00C0, 0x00C1, 0x00C3, 0x00C5, 0x00C7, 0x0023, 0x00F1, 0x002C, 0x0025, 0x005F, 0x003E, 0x003F, + 0x00F8, 0x00C9, 0x00CA, 0x00CB, 0x00C8, 0x00CD, 0x00CE, 0x00CF, 0x00CC, 0x0060, 0x003A, 0x00D1, 0x0040, 0x0027, 0x003D, 0x0022, + 0x00D8, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x00AB, 0x00BB, 0x00F0, 0x00FD, 0x00FE, 0x00B1, + 0x00B0, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070, 0x0071, 0x0072, 0x00AA, 0x00BA, 0x00E6, 0x00B8, 0x00C6, 0x20AC, + 0x00B5, 0x00A8, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x00A1, 0x00BF, 0x00D0, 0x00DD, 0x00DE, 0x00AE, + 0x00A2, 0x00A3, 0x00A5, 0x00B7, 0x00A9, 0x00A7, 0x00B6, 0x00BC, 0x00BD, 0x00BE, 0x005E, 0x0021, 0x00AF, 0x007E, 0x00B4, 0x00D7, + 0x007B, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x00AD, 0x00F4, 0x00F6, 0x00F2, 0x00F3, 0x00F5, + 0x007D, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050, 0x0051, 0x0052, 0x00B9, 0x00FB, 0x00FC, 0x00F9, 0x00FA, 0x00FF, + 0x005C, 0x00F7, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x00B2, 0x00D4, 0x00D6, 0x00D2, 0x00D3, 0x00D5, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x00B3, 0x00DB, 0x00DC, 0x00D9, 0x00DA, 0x009F, + }; + + // 1146 IBM285 EBCDIC UK with Euro + constexpr unsigned short EBCDIC1146Table[256] = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x009C, 0x0009, 0x0086, 0x007F, 0x0097, 0x008D, 0x008E, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x009D, 0x0085, 0x0008, 0x0087, 0x0018, 0x0019, 0x0092, 0x008F, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x000A, 0x0017, 0x001B, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x0005, 0x0006, 0x0007, + 0x0090, 0x0091, 0x0016, 0x0093, 0x0094, 0x0095, 0x0096, 0x0004, 0x0098, 0x0099, 0x009A, 0x009B, 0x0014, 0x0015, 0x009E, 0x001A, + 0x0020, 0x00A0, 0x00E2, 0x00E4, 0x00E0, 0x00E1, 0x00E3, 0x00E5, 0x00E7, 0x00F1, 0x0024, 0x002E, 0x003C, 0x0028, 0x002B, 0x007C, + 0x0026, 0x00E9, 0x00EA, 0x00EB, 0x00E8, 0x00ED, 0x00EE, 0x00EF, 0x00EC, 0x00DF, 0x0021, 0x00A3, 0x002A, 0x0029, 0x003B, 0x00AC, + 0x002D, 0x002F, 0x00C2, 0x00C4, 0x00C0, 0x00C1, 0x00C3, 0x00C5, 0x00C7, 0x00D1, 0x00A6, 0x002C, 0x0025, 0x005F, 0x003E, 0x003F, + 0x00F8, 0x00C9, 0x00CA, 0x00CB, 0x00C8, 0x00CD, 0x00CE, 0x00CF, 0x00CC, 0x0060, 0x003A, 0x0023, 0x0040, 0x0027, 0x003D, 0x0022, + 0x00D8, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x00AB, 0x00BB, 0x00F0, 0x00FD, 0x00FE, 0x00B1, + 0x00B0, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070, 0x0071, 0x0072, 0x00AA, 0x00BA, 0x00E6, 0x00B8, 0x00C6, 0x20AC, + 0x00B5, 0x00AF, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x00A1, 0x00BF, 0x00D0, 0x00DD, 0x00DE, 0x00AE, + 0x00A2, 0x005B, 0x00A5, 0x00B7, 0x00A9, 0x00A7, 0x00B6, 0x00BC, 0x00BD, 0x00BE, 0x005E, 0x005D, 0x007E, 0x00A8, 0x00B4, 0x00D7, + 0x007B, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x00AD, 0x00F4, 0x00F6, 0x00F2, 0x00F3, 0x00F5, + 0x007D, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050, 0x0051, 0x0052, 0x00B9, 0x00FB, 0x00FC, 0x00F9, 0x00FA, 0x00FF, + 0x005C, 0x00F7, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x00B2, 0x00D4, 0x00D6, 0x00D2, 0x00D3, 0x00D5, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x00B3, 0x00DB, 0x00DC, 0x00D9, 0x00DA, 0x009F, + }; + + // 1147 IBM297 EBCDIC France with Euro + constexpr unsigned short EBCDIC1147Table[256] = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x009C, 0x0009, 0x0086, 0x007F, 0x0097, 0x008D, 0x008E, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x009D, 0x0085, 0x0008, 0x0087, 0x0018, 0x0019, 0x0092, 0x008F, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x000A, 0x0017, 0x001B, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x0005, 0x0006, 0x0007, + 0x0090, 0x0091, 0x0016, 0x0093, 0x0094, 0x0095, 0x0096, 0x0004, 0x0098, 0x0099, 0x009A, 0x009B, 0x0014, 0x0015, 0x009E, 0x001A, + 0x0020, 0x00A0, 0x00E2, 0x00E4, 0x0040, 0x00E1, 0x00E3, 0x00E5, 0x005C, 0x00F1, 0x00B0, 0x002E, 0x003C, 0x0028, 0x002B, 0x0021, + 0x0026, 0x007B, 0x00EA, 0x00EB, 0x007D, 0x00ED, 0x00EE, 0x00EF, 0x00EC, 0x00DF, 0x00A7, 0x0024, 0x002A, 0x0029, 0x003B, 0x005E, + 0x002D, 0x002F, 0x00C2, 0x00C4, 0x00C0, 0x00C1, 0x00C3, 0x00C5, 0x00C7, 0x00D1, 0x00F9, 0x002C, 0x0025, 0x005F, 0x003E, 0x003F, + 0x00F8, 0x00C9, 0x00CA, 0x00CB, 0x00C8, 0x00CD, 0x00CE, 0x00CF, 0x00CC, 0x00B5, 0x003A, 0x00A3, 0x00E0, 0x0027, 0x003D, 0x0022, + 0x00D8, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x00AB, 0x00BB, 0x00F0, 0x00FD, 0x00FE, 0x00B1, + 0x005B, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070, 0x0071, 0x0072, 0x00AA, 0x00BA, 0x00E6, 0x00B8, 0x00C6, 0x20AC, + 0x0060, 0x00A8, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x00A1, 0x00BF, 0x00D0, 0x00DD, 0x00DE, 0x00AE, + 0x00A2, 0x0023, 0x00A5, 0x00B7, 0x00A9, 0x005D, 0x00B6, 0x00BC, 0x00BD, 0x00BE, 0x00AC, 0x007C, 0x00AF, 0x007E, 0x00B4, 0x00D7, + 0x00E9, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x00AD, 0x00F4, 0x00F6, 0x00F2, 0x00F3, 0x00F5, + 0x00E8, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050, 0x0051, 0x0052, 0x00B9, 0x00FB, 0x00FC, 0x00A6, 0x00FA, 0x00FF, + 0x00E7, 0x00F7, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x00B2, 0x00D4, 0x00D6, 0x00D2, 0x00D3, 0x00D5, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x00B3, 0x00DB, 0x00DC, 0x00D9, 0x00DA, 0x009F, + }; + + // 1148 IBM500 EBCDIC International with Euro + constexpr unsigned short EBCDIC1148Table[256] = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x009C, 0x0009, 0x0086, 0x007F, 0x0097, 0x008D, 0x008E, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x009D, 0x0085, 0x0008, 0x0087, 0x0018, 0x0019, 0x0092, 0x008F, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x000A, 0x0017, 0x001B, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x0005, 0x0006, 0x0007, + 0x0090, 0x0091, 0x0016, 0x0093, 0x0094, 0x0095, 0x0096, 0x0004, 0x0098, 0x0099, 0x009A, 0x009B, 0x0014, 0x0015, 0x009E, 0x001A, + 0x0020, 0x00A0, 0x00E2, 0x00E4, 0x00E0, 0x00E1, 0x00E3, 0x00E5, 0x00E7, 0x00F1, 0x005B, 0x002E, 0x003C, 0x0028, 0x002B, 0x0021, + 0x0026, 0x00E9, 0x00EA, 0x00EB, 0x00E8, 0x00ED, 0x00EE, 0x00EF, 0x00EC, 0x00DF, 0x005D, 0x0024, 0x002A, 0x0029, 0x003B, 0x005E, + 0x002D, 0x002F, 0x00C2, 0x00C4, 0x00C0, 0x00C1, 0x00C3, 0x00C5, 0x00C7, 0x00D1, 0x00A6, 0x002C, 0x0025, 0x005F, 0x003E, 0x003F, + 0x00F8, 0x00C9, 0x00CA, 0x00CB, 0x00C8, 0x00CD, 0x00CE, 0x00CF, 0x00CC, 0x0060, 0x003A, 0x0023, 0x0040, 0x0027, 0x003D, 0x0022, + 0x00D8, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x00AB, 0x00BB, 0x00F0, 0x00FD, 0x00FE, 0x00B1, + 0x00B0, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070, 0x0071, 0x0072, 0x00AA, 0x00BA, 0x00E6, 0x00B8, 0x00C6, 0x20AC, + 0x00B5, 0x007E, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x00A1, 0x00BF, 0x00D0, 0x00DD, 0x00DE, 0x00AE, + 0x00A2, 0x00A3, 0x00A5, 0x00B7, 0x00A9, 0x00A7, 0x00B6, 0x00BC, 0x00BD, 0x00BE, 0x00AC, 0x007C, 0x00AF, 0x00A8, 0x00B4, 0x00D7, + 0x007B, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x00AD, 0x00F4, 0x00F6, 0x00F2, 0x00F3, 0x00F5, + 0x007D, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050, 0x0051, 0x0052, 0x00B9, 0x00FB, 0x00FC, 0x00F9, 0x00FA, 0x00FF, + 0x005C, 0x00F7, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x00B2, 0x00D4, 0x00D6, 0x00D2, 0x00D3, 0x00D5, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x00B3, 0x00DB, 0x00DC, 0x00D9, 0x00DA, 0x009F, + }; + + // 1149 IBM871 EBCDIC Icelandic with Euro + constexpr unsigned short EBCDIC1149Table[256] = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x009C, 0x0009, 0x0086, 0x007F, 0x0097, 0x008D, 0x008E, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x009D, 0x0085, 0x0008, 0x0087, 0x0018, 0x0019, 0x0092, 0x008F, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x000A, 0x0017, 0x001B, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x0005, 0x0006, 0x0007, + 0x0090, 0x0091, 0x0016, 0x0093, 0x0094, 0x0095, 0x0096, 0x0004, 0x0098, 0x0099, 0x009A, 0x009B, 0x0014, 0x0015, 0x009E, 0x001A, + 0x0020, 0x00A0, 0x00E2, 0x00E4, 0x00E0, 0x00E1, 0x00E3, 0x00E5, 0x00E7, 0x00F1, 0x00DE, 0x002E, 0x003C, 0x0028, 0x002B, 0x0021, + 0x0026, 0x00E9, 0x00EA, 0x00EB, 0x00E8, 0x00ED, 0x00EE, 0x00EF, 0x00EC, 0x00DF, 0x00C6, 0x0024, 0x002A, 0x0029, 0x003B, 0x00D6, + 0x002D, 0x002F, 0x00C2, 0x00C4, 0x00C0, 0x00C1, 0x00C3, 0x00C5, 0x00C7, 0x00D1, 0x00A6, 0x002C, 0x0025, 0x005F, 0x003E, 0x003F, + 0x00F8, 0x00C9, 0x00CA, 0x00CB, 0x00C8, 0x00CD, 0x00CE, 0x00CF, 0x00CC, 0x00F0, 0x003A, 0x0023, 0x00D0, 0x0027, 0x003D, 0x0022, + 0x00D8, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x00AB, 0x00BB, 0x0060, 0x00FD, 0x007B, 0x00B1, + 0x00B0, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070, 0x0071, 0x0072, 0x00AA, 0x00BA, 0x007D, 0x00B8, 0x005D, 0x20AC, + 0x00B5, 0x00F6, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x00A1, 0x00BF, 0x0040, 0x00DD, 0x005B, 0x00AE, + 0x00A2, 0x00A3, 0x00A5, 0x00B7, 0x00A9, 0x00A7, 0x00B6, 0x00BC, 0x00BD, 0x00BE, 0x00AC, 0x007C, 0x00AF, 0x00A8, 0x005C, 0x00D7, + 0x00FE, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x00AD, 0x00F4, 0x007E, 0x00F2, 0x00F3, 0x00F5, + 0x00E6, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050, 0x0051, 0x0052, 0x00B9, 0x00FB, 0x00FC, 0x00F9, 0x00FA, 0x00FF, + 0x00B4, 0x00F7, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x00B2, 0x00D4, 0x005E, 0x00D2, 0x00D3, 0x00D5, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x00B3, 0x00DB, 0x00DC, 0x00D9, 0x00DA, 0x009F, + }; + + // 1250 Windows-1250 Central European + constexpr unsigned short Windows1250Table[256] = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, + 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, + 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, + 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, + 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, + 0x20AC, 0x0081, 0x201A, 0x0083, 0x201E, 0x2026, 0x2020, 0x2021, 0x0088, 0x2030, 0x0160, 0x2039, 0x015A, 0x0164, 0x017D, 0x0179, + 0x0090, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x0098, 0x2122, 0x0161, 0x203A, 0x015B, 0x0165, 0x017E, 0x017A, + 0x00A0, 0x02C7, 0x02D8, 0x0141, 0x00A4, 0x0104, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x015E, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x017B, + 0x00B0, 0x00B1, 0x02DB, 0x0142, 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00B8, 0x0105, 0x015F, 0x00BB, 0x013D, 0x02DD, 0x013E, 0x017C, + 0x0154, 0x00C1, 0x00C2, 0x0102, 0x00C4, 0x0139, 0x0106, 0x00C7, 0x010C, 0x00C9, 0x0118, 0x00CB, 0x011A, 0x00CD, 0x00CE, 0x010E, + 0x0110, 0x0143, 0x0147, 0x00D3, 0x00D4, 0x0150, 0x00D6, 0x00D7, 0x0158, 0x016E, 0x00DA, 0x0170, 0x00DC, 0x00DD, 0x0162, 0x00DF, + 0x0155, 0x00E1, 0x00E2, 0x0103, 0x00E4, 0x013A, 0x0107, 0x00E7, 0x010D, 0x00E9, 0x0119, 0x00EB, 0x011B, 0x00ED, 0x00EE, 0x010F, + 0x0111, 0x0144, 0x0148, 0x00F3, 0x00F4, 0x0151, 0x00F6, 0x00F7, 0x0159, 0x016F, 0x00FA, 0x0171, 0x00FC, 0x00FD, 0x0163, 0x02D9, + }; + + // 1251 Windows-1251 Cyrillic + constexpr unsigned short Windows1251Table[256] = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, + 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, + 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, + 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, + 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, + 0x0402, 0x0403, 0x201A, 0x0453, 0x201E, 0x2026, 0x2020, 0x2021, 0x20AC, 0x2030, 0x0409, 0x2039, 0x040A, 0x040C, 0x040B, 0x040F, + 0x0452, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x0098, 0x2122, 0x0459, 0x203A, 0x045A, 0x045C, 0x045B, 0x045F, + 0x00A0, 0x040E, 0x045E, 0x0408, 0x00A4, 0x0490, 0x00A6, 0x00A7, 0x0401, 0x00A9, 0x0404, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x0407, + 0x00B0, 0x00B1, 0x0406, 0x0456, 0x0491, 0x00B5, 0x00B6, 0x00B7, 0x0451, 0x2116, 0x0454, 0x00BB, 0x0458, 0x0405, 0x0455, 0x0457, + 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F, + 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, 0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F, + 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F, + 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F, + }; + + // 1252 Windows-1252 Western European + constexpr unsigned short Windows1252Table[256] = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, + 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, + 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, + 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, + 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, + 0x20AC, 0x0081, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, 0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, 0x008D, 0x017D, 0x008F, + 0x0090, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x02DC, 0x2122, 0x0161, 0x203A, 0x0153, 0x009D, 0x017E, 0x0178, + 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, + 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, + 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, + 0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF, + 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, + 0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF, + }; + + // 1253 Windows-1253 Greek + constexpr unsigned short Windows1253Table[256] = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, + 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, + 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, + 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, + 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, + 0x20AC, 0x0081, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, 0x0088, 0x2030, 0x008A, 0x2039, 0x008C, 0x008D, 0x008E, 0x008F, + 0x0090, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x0098, 0x2122, 0x009A, 0x203A, 0x009C, 0x009D, 0x009E, 0x009F, + 0x00A0, 0x0385, 0x0386, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0xFFFD, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x2015, + 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x0384, 0x00B5, 0x00B6, 0x00B7, 0x0388, 0x0389, 0x038A, 0x00BB, 0x038C, 0x00BD, 0x038E, 0x038F, + 0x0390, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398, 0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F, + 0x03A0, 0x03A1, 0xFFFD, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9, 0x03AA, 0x03AB, 0x03AC, 0x03AD, 0x03AE, 0x03AF, + 0x03B0, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, + 0x03C0, 0x03C1, 0x03C2, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8, 0x03C9, 0x03CA, 0x03CB, 0x03CC, 0x03CD, 0x03CE, 0xFFFD, + }; + + // 1254 Windows-1254 Turkish + constexpr unsigned short Windows1254Table[256] = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, + 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, + 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, + 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, + 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, + 0x20AC, 0x0081, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, 0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, 0x008D, 0x008E, 0x008F, + 0x0090, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x02DC, 0x2122, 0x0161, 0x203A, 0x0153, 0x009D, 0x009E, 0x0178, + 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, + 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, + 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, + 0x011E, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x0130, 0x015E, 0x00DF, + 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, + 0x011F, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x0131, 0x015F, 0x00FF, + }; + + // 1255 Windows-1255 Hebrew + constexpr unsigned short Windows1255Table[256] = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, + 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, + 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, + 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, + 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, + 0x20AC, 0x0081, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, 0x02C6, 0x2030, 0x008A, 0x2039, 0x008C, 0x008D, 0x008E, 0x008F, + 0x0090, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x02DC, 0x2122, 0x009A, 0x203A, 0x009C, 0x009D, 0x009E, 0x009F, + 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x20AA, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x00D7, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, + 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00B8, 0x00B9, 0x00F7, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, + 0x05B0, 0x05B1, 0x05B2, 0x05B3, 0x05B4, 0x05B5, 0x05B6, 0x05B7, 0x05B8, 0x05B9, 0xFFFD, 0x05BB, 0x05BC, 0x05BD, 0x05BE, 0x05BF, + 0x05C0, 0x05C1, 0x05C2, 0x05C3, 0x05F0, 0x05F1, 0x05F2, 0x05F3, 0x05F4, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, + 0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7, 0x05D8, 0x05D9, 0x05DA, 0x05DB, 0x05DC, 0x05DD, 0x05DE, 0x05DF, + 0x05E0, 0x05E1, 0x05E2, 0x05E3, 0x05E4, 0x05E5, 0x05E6, 0x05E7, 0x05E8, 0x05E9, 0x05EA, 0xFFFD, 0xFFFD, 0x200E, 0x200F, 0xFFFD, + }; + + // 1256 Windows-1256 Arabic + constexpr unsigned short Windows1256Table[256] = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, + 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, + 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, + 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, + 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, + 0x20AC, 0x067E, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, 0x02C6, 0x2030, 0x0679, 0x2039, 0x0152, 0x0686, 0x0698, 0x0688, + 0x06AF, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x06A9, 0x2122, 0x0691, 0x203A, 0x0153, 0x200C, 0x200D, 0x06BA, + 0x00A0, 0x060C, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x06BE, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, + 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00B8, 0x00B9, 0x061B, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x061F, + 0x06C1, 0x0621, 0x0622, 0x0623, 0x0624, 0x0625, 0x0626, 0x0627, 0x0628, 0x0629, 0x062A, 0x062B, 0x062C, 0x062D, 0x062E, 0x062F, + 0x0630, 0x0631, 0x0632, 0x0633, 0x0634, 0x0635, 0x0636, 0x00D7, 0x0637, 0x0638, 0x0639, 0x063A, 0x0640, 0x0641, 0x0642, 0x0643, + 0x00E0, 0x0644, 0x00E2, 0x0645, 0x0646, 0x0647, 0x0648, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x0649, 0x064A, 0x00EE, 0x00EF, + 0x064B, 0x064C, 0x064D, 0x064E, 0x00F4, 0x064F, 0x0650, 0x00F7, 0x0651, 0x00F9, 0x0652, 0x00FB, 0x00FC, 0x200E, 0x200F, 0x06D2, + }; + + // 1257 Windows-1257 Baltic + constexpr unsigned short Windows1257Table[256] = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, + 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, + 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, + 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, + 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, + 0x20AC, 0x0081, 0x201A, 0x0083, 0x201E, 0x2026, 0x2020, 0x2021, 0x0088, 0x2030, 0x008A, 0x2039, 0x008C, 0x00A8, 0x02C7, 0x00B8, + 0x0090, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x0098, 0x2122, 0x009A, 0x203A, 0x009C, 0x00AF, 0x02DB, 0x009F, + 0x00A0, 0xFFFD, 0x00A2, 0x00A3, 0x00A4, 0xFFFD, 0x00A6, 0x00A7, 0x00D8, 0x00A9, 0x0156, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00C6, + 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00F8, 0x00B9, 0x0157, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00E6, + 0x0104, 0x012E, 0x0100, 0x0106, 0x00C4, 0x00C5, 0x0118, 0x0112, 0x010C, 0x00C9, 0x0179, 0x0116, 0x0122, 0x0136, 0x012A, 0x013B, + 0x0160, 0x0143, 0x0145, 0x00D3, 0x014C, 0x00D5, 0x00D6, 0x00D7, 0x0172, 0x0141, 0x015A, 0x016A, 0x00DC, 0x017B, 0x017D, 0x00DF, + 0x0105, 0x012F, 0x0101, 0x0107, 0x00E4, 0x00E5, 0x0119, 0x0113, 0x010D, 0x00E9, 0x017A, 0x0117, 0x0123, 0x0137, 0x012B, 0x013C, + 0x0161, 0x0144, 0x0146, 0x00F3, 0x014D, 0x00F5, 0x00F6, 0x00F7, 0x0173, 0x0142, 0x015B, 0x016B, 0x00FC, 0x017C, 0x017E, 0x02D9, + }; + + // 1258 Windows-1258 Vietnamese + constexpr unsigned short Windows1258Table[256] = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, + 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, + 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, + 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, + 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, + 0x20AC, 0x0081, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, 0x02C6, 0x2030, 0x008A, 0x2039, 0x0152, 0x008D, 0x008E, 0x008F, + 0x0090, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x02DC, 0x2122, 0x009A, 0x203A, 0x0153, 0x009D, 0x009E, 0x0178, + 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, + 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, + 0x00C0, 0x00C1, 0x00C2, 0x0102, 0x00C4, 0x00C5, 0x00C6, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x0300, 0x00CD, 0x00CE, 0x00CF, + 0x0110, 0x00D1, 0x0309, 0x00D3, 0x00D4, 0x01A0, 0x00D6, 0x00D7, 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x01AF, 0x0303, 0x00DF, + 0x00E0, 0x00E1, 0x00E2, 0x0103, 0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x0301, 0x00ED, 0x00EE, 0x00EF, + 0x0111, 0x00F1, 0x0323, 0x00F3, 0x00F4, 0x01A1, 0x00F6, 0x00F7, 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x01B0, 0x20AB, 0x00FF, + }; + + // 20127 US-ASCII + constexpr unsigned short ASCIITable[256] = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, + 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, + 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, + 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, + 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, + 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F, + 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F, + 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, + 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, + 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, + 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, + 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, + 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, + }; + + // 20273 IBM273 EBCDIC Germany + constexpr unsigned short EBCDIC273Table[256] = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x009C, 0x0009, 0x0086, 0x007F, 0x0097, 0x008D, 0x008E, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x009D, 0x0085, 0x0008, 0x0087, 0x0018, 0x0019, 0x0092, 0x008F, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x000A, 0x0017, 0x001B, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x0005, 0x0006, 0x0007, + 0x0090, 0x0091, 0x0016, 0x0093, 0x0094, 0x0095, 0x0096, 0x0004, 0x0098, 0x0099, 0x009A, 0x009B, 0x0014, 0x0015, 0x009E, 0x001A, + 0x0020, 0x00A0, 0x00E2, 0x007B, 0x00E0, 0x00E1, 0x00E3, 0x00E5, 0x00E7, 0x00F1, 0x00C4, 0x002E, 0x003C, 0x0028, 0x002B, 0x0021, + 0x0026, 0x00E9, 0x00EA, 0x00EB, 0x00E8, 0x00ED, 0x00EE, 0x00EF, 0x00EC, 0x007E, 0x00DC, 0x0024, 0x002A, 0x0029, 0x003B, 0x005E, + 0x002D, 0x002F, 0x00C2, 0x005B, 0x00C0, 0x00C1, 0x00C3, 0x00C5, 0x00C7, 0x00D1, 0x00F6, 0x002C, 0x0025, 0x005F, 0x003E, 0x003F, + 0x00F8, 0x00C9, 0x00CA, 0x00CB, 0x00C8, 0x00CD, 0x00CE, 0x00CF, 0x00CC, 0x0060, 0x003A, 0x0023, 0x00A7, 0x0027, 0x003D, 0x0022, + 0x00D8, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x00AB, 0x00BB, 0x00F0, 0x00FD, 0x00FE, 0x00B1, + 0x00B0, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070, 0x0071, 0x0072, 0x00AA, 0x00BA, 0x00E6, 0x00B8, 0x00C6, 0x00A4, + 0x00B5, 0x00DF, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x00A1, 0x00BF, 0x00D0, 0x00DD, 0x00DE, 0x00AE, + 0x00A2, 0x00A3, 0x00A5, 0x00B7, 0x00A9, 0x0040, 0x00B6, 0x00BC, 0x00BD, 0x00BE, 0x00AC, 0x007C, 0x00AF, 0x00A8, 0x00B4, 0x00D7, + 0x00E4, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x00AD, 0x00F4, 0x00A6, 0x00F2, 0x00F3, 0x00F5, + 0x00FC, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050, 0x0051, 0x0052, 0x00B9, 0x00FB, 0x007D, 0x00F9, 0x00FA, 0x00FF, + 0x00D6, 0x00F7, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x00B2, 0x00D4, 0x005C, 0x00D2, 0x00D3, 0x00D5, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x00B3, 0x00DB, 0x005D, 0x00D9, 0x00DA, 0x009F, + }; + + // 20277 IBM277 EBCDIC Denmark-Norway + constexpr unsigned short EBCDIC277Table[256] = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x009C, 0x0009, 0x0086, 0x007F, 0x0097, 0x008D, 0x008E, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x009D, 0x0085, 0x0008, 0x0087, 0x0018, 0x0019, 0x0092, 0x008F, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x000A, 0x0017, 0x001B, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x0005, 0x0006, 0x0007, + 0x0090, 0x0091, 0x0016, 0x0093, 0x0094, 0x0095, 0x0096, 0x0004, 0x0098, 0x0099, 0x009A, 0x009B, 0x0014, 0x0015, 0x009E, 0x001A, + 0x0020, 0x00A0, 0x00E2, 0x00E4, 0x00E0, 0x00E1, 0x00E3, 0x007D, 0x00E7, 0x00F1, 0x0023, 0x002E, 0x003C, 0x0028, 0x002B, 0x0021, + 0x0026, 0x00E9, 0x00EA, 0x00EB, 0x00E8, 0x00ED, 0x00EE, 0x00EF, 0x00EC, 0x00DF, 0x00A4, 0x00C5, 0x002A, 0x0029, 0x003B, 0x005E, + 0x002D, 0x002F, 0x00C2, 0x00C4, 0x00C0, 0x00C1, 0x00C3, 0x0024, 0x00C7, 0x00D1, 0x00F8, 0x002C, 0x0025, 0x005F, 0x003E, 0x003F, + 0x00A6, 0x00C9, 0x00CA, 0x00CB, 0x00C8, 0x00CD, 0x00CE, 0x00CF, 0x00CC, 0x0060, 0x003A, 0x00C6, 0x00D8, 0x0027, 0x003D, 0x0022, + 0x0040, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x00AB, 0x00BB, 0x00F0, 0x00FD, 0x00FE, 0x00B1, + 0x00B0, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070, 0x0071, 0x0072, 0x00AA, 0x00BA, 0x007B, 0x00B8, 0x005B, 0x005D, + 0x00B5, 0x00FC, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x00A1, 0x00BF, 0x00D0, 0x00DD, 0x00DE, 0x00AE, + 0x00A2, 0x00A3, 0x00A5, 0x00B7, 0x00A9, 0x00A7, 0x00B6, 0x00BC, 0x00BD, 0x00BE, 0x00AC, 0x007C, 0x00AF, 0x00A8, 0x00B4, 0x00D7, + 0x00E6, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x00AD, 0x00F4, 0x00F6, 0x00F2, 0x00F3, 0x00F5, + 0x00E5, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050, 0x0051, 0x0052, 0x00B9, 0x00FB, 0x007E, 0x00F9, 0x00FA, 0x00FF, + 0x005C, 0x00F7, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x00B2, 0x00D4, 0x00D6, 0x00D2, 0x00D3, 0x00D5, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x00B3, 0x00DB, 0x00DC, 0x00D9, 0x00DA, 0x009F, + }; + + // 20278 IBM278 EBCDIC Finland-Sweden + constexpr unsigned short EBCDIC278Table[256] = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x009C, 0x0009, 0x0086, 0x007F, 0x0097, 0x008D, 0x008E, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x009D, 0x0085, 0x0008, 0x0087, 0x0018, 0x0019, 0x0092, 0x008F, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x000A, 0x0017, 0x001B, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x0005, 0x0006, 0x0007, + 0x0090, 0x0091, 0x0016, 0x0093, 0x0094, 0x0095, 0x0096, 0x0004, 0x0098, 0x0099, 0x009A, 0x009B, 0x0014, 0x0015, 0x009E, 0x001A, + 0x0020, 0x00A0, 0x00E2, 0x007B, 0x00E0, 0x00E1, 0x00E3, 0x007D, 0x00E7, 0x00F1, 0x00A7, 0x002E, 0x003C, 0x0028, 0x002B, 0x0021, + 0x0026, 0x0060, 0x00EA, 0x00EB, 0x00E8, 0x00ED, 0x00EE, 0x00EF, 0x00EC, 0x00DF, 0x00A4, 0x00C5, 0x002A, 0x0029, 0x003B, 0x005E, + 0x002D, 0x002F, 0x00C2, 0x0023, 0x00C0, 0x00C1, 0x00C3, 0x0024, 0x00C7, 0x00D1, 0x00F6, 0x002C, 0x0025, 0x005F, 0x003E, 0x003F, + 0x00F8, 0x00C9, 0x00CA, 0x00CB, 0x00C8, 0x00CD, 0x00CE, 0x00CF, 0x00CC, 0x00E9, 0x003A, 0x00C4, 0x00D6, 0x0027, 0x003D, 0x0022, + 0x00D8, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x00AB, 0x00BB, 0x00F0, 0x00FD, 0x00FE, 0x00B1, + 0x00B0, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070, 0x0071, 0x0072, 0x00AA, 0x00BA, 0x00E6, 0x00B8, 0x00C6, 0x005D, + 0x00B5, 0x00FC, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x00A1, 0x00BF, 0x00D0, 0x00DD, 0x00DE, 0x00AE, + 0x00A2, 0x00A3, 0x00A5, 0x00B7, 0x00A9, 0x005B, 0x00B6, 0x00BC, 0x00BD, 0x00BE, 0x00AC, 0x007C, 0x00AF, 0x00A8, 0x00B4, 0x00D7, + 0x00E4, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x00AD, 0x00F4, 0x00A6, 0x00F2, 0x00F3, 0x00F5, + 0x00E5, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050, 0x0051, 0x0052, 0x00B9, 0x00FB, 0x007E, 0x00F9, 0x00FA, 0x00FF, + 0x005C, 0x00F7, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x00B2, 0x00D4, 0x0040, 0x00D2, 0x00D3, 0x00D5, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x00B3, 0x00DB, 0x00DC, 0x00D9, 0x00DA, 0x009F, + }; + + // 20280 IBM280 EBCDIC Italy + constexpr unsigned short EBCDIC280Table[256] = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x009C, 0x0009, 0x0086, 0x007F, 0x0097, 0x008D, 0x008E, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x009D, 0x0085, 0x0008, 0x0087, 0x0018, 0x0019, 0x0092, 0x008F, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x000A, 0x0017, 0x001B, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x0005, 0x0006, 0x0007, + 0x0090, 0x0091, 0x0016, 0x0093, 0x0094, 0x0095, 0x0096, 0x0004, 0x0098, 0x0099, 0x009A, 0x009B, 0x0014, 0x0015, 0x009E, 0x001A, + 0x0020, 0x00A0, 0x00E2, 0x00E4, 0x007B, 0x00E1, 0x00E3, 0x00E5, 0x005C, 0x00F1, 0x00B0, 0x002E, 0x003C, 0x0028, 0x002B, 0x0021, + 0x0026, 0x005D, 0x00EA, 0x00EB, 0x007D, 0x00ED, 0x00EE, 0x00EF, 0x007E, 0x00DF, 0x00E9, 0x0024, 0x002A, 0x0029, 0x003B, 0x005E, + 0x002D, 0x002F, 0x00C2, 0x00C4, 0x00C0, 0x00C1, 0x00C3, 0x00C5, 0x00C7, 0x00D1, 0x00F2, 0x002C, 0x0025, 0x005F, 0x003E, 0x003F, + 0x00F8, 0x00C9, 0x00CA, 0x00CB, 0x00C8, 0x00CD, 0x00CE, 0x00CF, 0x00CC, 0x00F9, 0x003A, 0x00A3, 0x00A7, 0x0027, 0x003D, 0x0022, + 0x00D8, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x00AB, 0x00BB, 0x00F0, 0x00FD, 0x00FE, 0x00B1, + 0x005B, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070, 0x0071, 0x0072, 0x00AA, 0x00BA, 0x00E6, 0x00B8, 0x00C6, 0x00A4, + 0x00B5, 0x00EC, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x00A1, 0x00BF, 0x00D0, 0x00DD, 0x00DE, 0x00AE, + 0x00A2, 0x0023, 0x00A5, 0x00B7, 0x00A9, 0x0040, 0x00B6, 0x00BC, 0x00BD, 0x00BE, 0x00AC, 0x007C, 0x00AF, 0x00A8, 0x00B4, 0x00D7, + 0x00E0, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x00AD, 0x00F4, 0x00F6, 0x00A6, 0x00F3, 0x00F5, + 0x00E8, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050, 0x0051, 0x0052, 0x00B9, 0x00FB, 0x00FC, 0x0060, 0x00FA, 0x00FF, + 0x00E7, 0x00F7, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x00B2, 0x00D4, 0x00D6, 0x00D2, 0x00D3, 0x00D5, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x00B3, 0x00DB, 0x00DC, 0x00D9, 0x00DA, 0x009F, + }; + + // 20284 IBM284 EBCDIC Latin America-Spain + constexpr unsigned short EBCDIC284Table[256] = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x009C, 0x0009, 0x0086, 0x007F, 0x0097, 0x008D, 0x008E, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x009D, 0x0085, 0x0008, 0x0087, 0x0018, 0x0019, 0x0092, 0x008F, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x000A, 0x0017, 0x001B, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x0005, 0x0006, 0x0007, + 0x0090, 0x0091, 0x0016, 0x0093, 0x0094, 0x0095, 0x0096, 0x0004, 0x0098, 0x0099, 0x009A, 0x009B, 0x0014, 0x0015, 0x009E, 0x001A, + 0x0020, 0x00A0, 0x00E2, 0x00E4, 0x00E0, 0x00E1, 0x00E3, 0x00E5, 0x00E7, 0x00A6, 0x005B, 0x002E, 0x003C, 0x0028, 0x002B, 0x007C, + 0x0026, 0x00E9, 0x00EA, 0x00EB, 0x00E8, 0x00ED, 0x00EE, 0x00EF, 0x00EC, 0x00DF, 0x005D, 0x0024, 0x002A, 0x0029, 0x003B, 0x00AC, + 0x002D, 0x002F, 0x00C2, 0x00C4, 0x00C0, 0x00C1, 0x00C3, 0x00C5, 0x00C7, 0x0023, 0x00F1, 0x002C, 0x0025, 0x005F, 0x003E, 0x003F, + 0x00F8, 0x00C9, 0x00CA, 0x00CB, 0x00C8, 0x00CD, 0x00CE, 0x00CF, 0x00CC, 0x0060, 0x003A, 0x00D1, 0x0040, 0x0027, 0x003D, 0x0022, + 0x00D8, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x00AB, 0x00BB, 0x00F0, 0x00FD, 0x00FE, 0x00B1, + 0x00B0, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070, 0x0071, 0x0072, 0x00AA, 0x00BA, 0x00E6, 0x00B8, 0x00C6, 0x00A4, + 0x00B5, 0x00A8, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x00A1, 0x00BF, 0x00D0, 0x00DD, 0x00DE, 0x00AE, + 0x00A2, 0x00A3, 0x00A5, 0x00B7, 0x00A9, 0x00A7, 0x00B6, 0x00BC, 0x00BD, 0x00BE, 0x005E, 0x0021, 0x00AF, 0x007E, 0x00B4, 0x00D7, + 0x007B, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x00AD, 0x00F4, 0x00F6, 0x00F2, 0x00F3, 0x00F5, + 0x007D, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050, 0x0051, 0x0052, 0x00B9, 0x00FB, 0x00FC, 0x00F9, 0x00FA, 0x00FF, + 0x005C, 0x00F7, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x00B2, 0x00D4, 0x00D6, 0x00D2, 0x00D3, 0x00D5, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x00B3, 0x00DB, 0x00DC, 0x00D9, 0x00DA, 0x009F, + }; + + // 20285 IBM285 EBCDIC United Kingdom + constexpr unsigned short EBCDIC285Table[256] = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x009C, 0x0009, 0x0086, 0x007F, 0x0097, 0x008D, 0x008E, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x009D, 0x0085, 0x0008, 0x0087, 0x0018, 0x0019, 0x0092, 0x008F, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x000A, 0x0017, 0x001B, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x0005, 0x0006, 0x0007, + 0x0090, 0x0091, 0x0016, 0x0093, 0x0094, 0x0095, 0x0096, 0x0004, 0x0098, 0x0099, 0x009A, 0x009B, 0x0014, 0x0015, 0x009E, 0x001A, + 0x0020, 0x00A0, 0x00E2, 0x00E4, 0x00E0, 0x00E1, 0x00E3, 0x00E5, 0x00E7, 0x00F1, 0x0024, 0x002E, 0x003C, 0x0028, 0x002B, 0x007C, + 0x0026, 0x00E9, 0x00EA, 0x00EB, 0x00E8, 0x00ED, 0x00EE, 0x00EF, 0x00EC, 0x00DF, 0x0021, 0x00A3, 0x002A, 0x0029, 0x003B, 0x00AC, + 0x002D, 0x002F, 0x00C2, 0x00C4, 0x00C0, 0x00C1, 0x00C3, 0x00C5, 0x00C7, 0x00D1, 0x00A6, 0x002C, 0x0025, 0x005F, 0x003E, 0x003F, + 0x00F8, 0x00C9, 0x00CA, 0x00CB, 0x00C8, 0x00CD, 0x00CE, 0x00CF, 0x00CC, 0x0060, 0x003A, 0x0023, 0x0040, 0x0027, 0x003D, 0x0022, + 0x00D8, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x00AB, 0x00BB, 0x00F0, 0x00FD, 0x00FE, 0x00B1, + 0x00B0, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070, 0x0071, 0x0072, 0x00AA, 0x00BA, 0x00E6, 0x00B8, 0x00C6, 0x00A4, + 0x00B5, 0x203E, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x00A1, 0x00BF, 0x00D0, 0x00DD, 0x00DE, 0x00AE, + 0x00A2, 0x005B, 0x00A5, 0x00B7, 0x00A9, 0x00A7, 0x00B6, 0x00BC, 0x00BD, 0x00BE, 0x005E, 0x005D, 0x007E, 0x00A8, 0x00B4, 0x00D7, + 0x007B, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x00AD, 0x00F4, 0x00F6, 0x00F2, 0x00F3, 0x00F5, + 0x007D, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050, 0x0051, 0x0052, 0x00B9, 0x00FB, 0x00FC, 0x00F9, 0x00FA, 0x00FF, + 0x005C, 0x00F7, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x00B2, 0x00D4, 0x00D6, 0x00D2, 0x00D3, 0x00D5, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x00B3, 0x00DB, 0x00DC, 0x00D9, 0x00DA, 0x009F, + }; + + // 20290 IBM290 EBCDIC Japanese Katakana Extended + constexpr unsigned short EBCDIC290Table[256] = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x009C, 0x0009, 0x0086, 0x007F, 0x0097, 0x008D, 0x008E, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x009D, 0x0085, 0x0008, 0x0087, 0x0018, 0x0019, 0x0092, 0x008F, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x000A, 0x0017, 0x001B, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x0005, 0x0006, 0x0007, + 0x0090, 0x0091, 0x0016, 0x0093, 0x0094, 0x0095, 0x0096, 0x0004, 0x0098, 0x0099, 0x009A, 0x009B, 0x0014, 0x0015, 0x009E, 0x001A, + 0x0020, 0x3002, 0x300C, 0x300D, 0x3001, 0x30FB, 0x30F2, 0x30A1, 0x30A3, 0x30A5, 0x00A3, 0x002E, 0x003C, 0x0028, 0x002B, 0x007C, + 0x0026, 0x30A7, 0x30A9, 0x30E3, 0x30E5, 0x30E7, 0x30C3, 0xFFFD, 0x30FC, 0xFFFD, 0x0021, 0x00A5, 0x002A, 0x0029, 0x003B, 0x00AC, + 0x002D, 0x002F, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0x00A6, 0x002C, 0x0025, 0x005F, 0x003E, 0x003F, + 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0x0060, 0x003A, 0x0023, 0x0040, 0x0027, 0x003D, 0x0022, + 0x0080, 0x30A2, 0x30A4, 0x30A6, 0x30A8, 0x30AA, 0x30AB, 0x30AD, 0x30AF, 0x30B1, 0x30B3, 0x008B, 0x30B5, 0x30B7, 0x30B9, 0x30BB, + 0x30BD, 0x30BF, 0x30C1, 0x30C4, 0x30C6, 0x30C8, 0x30CA, 0x30CB, 0x30CC, 0x30CD, 0x30CE, 0x009B, 0x009C, 0x30CF, 0x30D2, 0x30D5, + 0xFFFD, 0x203E, 0x30D8, 0x30DB, 0x30DE, 0x30DF, 0x30E0, 0x30E1, 0x30E2, 0x30E4, 0x30E6, 0xFFFD, 0x30E8, 0x30E9, 0x30EA, 0x30EB, + 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0x30EC, 0x30ED, 0x30EF, 0x30F3, 0x309B, 0x309C, + 0xFFFD, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, + 0xFFFD, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050, 0x0051, 0x0052, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, + 0x0024, 0xFFFD, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0x009F, + }; + + // 20297 IBM297 EBCDIC France + constexpr unsigned short EBCDIC297Table[256] = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x009C, 0x0009, 0x0086, 0x007F, 0x0097, 0x008D, 0x008E, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x009D, 0x0085, 0x0008, 0x0087, 0x0018, 0x0019, 0x0092, 0x008F, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x000A, 0x0017, 0x001B, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x0005, 0x0006, 0x0007, + 0x0090, 0x0091, 0x0016, 0x0093, 0x0094, 0x0095, 0x0096, 0x0004, 0x0098, 0x0099, 0x009A, 0x009B, 0x0014, 0x0015, 0x009E, 0x001A, + 0x0020, 0x00A0, 0x00E2, 0x00E4, 0x0040, 0x00E1, 0x00E3, 0x00E5, 0x005C, 0x00F1, 0x00B0, 0x002E, 0x003C, 0x0028, 0x002B, 0x0021, + 0x0026, 0x007B, 0x00EA, 0x00EB, 0x007D, 0x00ED, 0x00EE, 0x00EF, 0x00EC, 0x00DF, 0x00A7, 0x0024, 0x002A, 0x0029, 0x003B, 0x005E, + 0x002D, 0x002F, 0x00C2, 0x00C4, 0x00C0, 0x00C1, 0x00C3, 0x00C5, 0x00C7, 0x00D1, 0x00F9, 0x002C, 0x0025, 0x005F, 0x003E, 0x003F, + 0x00F8, 0x00C9, 0x00CA, 0x00CB, 0x00C8, 0x00CD, 0x00CE, 0x00CF, 0x00CC, 0x00B5, 0x003A, 0x00A3, 0x00E0, 0x0027, 0x003D, 0x0022, + 0x00D8, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x00AB, 0x00BB, 0x00F0, 0x00FD, 0x00FE, 0x00B1, + 0x005B, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070, 0x0071, 0x0072, 0x00AA, 0x00BA, 0x00E6, 0x00B8, 0x00C6, 0x00A4, + 0x0060, 0x00A8, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x00A1, 0x00BF, 0x00D0, 0x00DD, 0x00DE, 0x00AE, + 0x00A2, 0x0023, 0x00A5, 0x00B7, 0x00A9, 0x005D, 0x00B6, 0x00BC, 0x00BD, 0x00BE, 0x00AC, 0x007C, 0x00AF, 0x007E, 0x00B4, 0x00D7, + 0x00E9, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x00AD, 0x00F4, 0x00F6, 0x00F2, 0x00F3, 0x00F5, + 0x00E8, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050, 0x0051, 0x0052, 0x00B9, 0x00FB, 0x00FC, 0x00A6, 0x00FA, 0x00FF, + 0x00E7, 0x00F7, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x00B2, 0x00D4, 0x00D6, 0x00D2, 0x00D3, 0x00D5, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x00B3, 0x00DB, 0x00DC, 0x00D9, 0x00DA, 0x009F, + }; + + // 20420 IBM420 EBCDIC Arabic + constexpr unsigned short EBCDIC420Table[256] = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x009C, 0x0009, 0x0086, 0x007F, 0x0097, 0x008D, 0x008E, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x009D, 0x0085, 0x0008, 0x0087, 0x0018, 0x0019, 0x0092, 0x008F, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x000A, 0x0017, 0x001B, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x0005, 0x0006, 0x0007, + 0x0090, 0x0091, 0x0016, 0x0093, 0x0094, 0x0095, 0x0096, 0x0004, 0x0098, 0x0099, 0x009A, 0x009B, 0x0014, 0x0015, 0x009E, 0x001A, + 0x0020, 0x00A0, 0x0651, 0xFE7D, 0x0640, 0xFFFD, 0x0621, 0x0622, 0xFE82, 0x0623, 0x00A2, 0x002E, 0x003C, 0x0028, 0x002B, 0x007C, + 0x0026, 0xFE84, 0x0624, 0xFFFD, 0xFFFD, 0x0626, 0x0627, 0xFE8E, 0x0628, 0xFE91, 0x0021, 0x0024, 0x002A, 0x0029, 0x003B, 0x00AC, + 0x002D, 0x002F, 0x0629, 0x062A, 0xFE97, 0x062B, 0xFE9B, 0x062C, 0xFE9F, 0x062D, 0x00A6, 0x002C, 0x0025, 0x005F, 0x003E, 0x003F, + 0xFEA3, 0x062E, 0xFEA7, 0x062F, 0x0630, 0x0631, 0x0632, 0x0633, 0xFEB3, 0x060C, 0x003A, 0x0023, 0x0040, 0x0027, 0x003D, 0x0022, + 0x0634, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0xFEB7, 0x0635, 0xFEBB, 0x0636, 0xFEBF, 0x0637, + 0x0638, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070, 0x0071, 0x0072, 0x0639, 0xFECA, 0xFECB, 0xFECC, 0x063A, 0xFECE, + 0xFECF, 0x00F7, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0xFED0, 0x0641, 0xFED3, 0x0642, 0xFED7, 0x0643, + 0xFEDB, 0x0644, 0xFEF5, 0xFEF6, 0xFEF7, 0xFEF8, 0xFFFD, 0xFFFD, 0xFEFB, 0xFEFC, 0xFEDF, 0x0645, 0xFEE3, 0x0646, 0xFEE7, 0x0647, + 0x061B, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x00AD, 0xFEEB, 0xFFFD, 0xFEEC, 0xFFFD, 0x0648, + 0x061F, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050, 0x0051, 0x0052, 0x0649, 0xFEF0, 0x064A, 0xFEF2, 0xFEF3, 0x0660, + 0x00D7, 0xFFFD, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x0661, 0x0662, 0xFFFD, 0x0663, 0x0664, 0x0665, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0xFFFD, 0x0666, 0x0667, 0x0668, 0x0669, 0x009F, + }; + + // 20423 IBM423 EBCDIC Greek + constexpr unsigned short EBCDIC423Table[256] = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x009C, 0x0009, 0x0086, 0x007F, 0x0097, 0x008D, 0x008E, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x009D, 0x0085, 0x0008, 0x0087, 0x0018, 0x0019, 0x0092, 0x008F, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x000A, 0x0017, 0x001B, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x0005, 0x0006, 0x0007, + 0x0090, 0x0091, 0x0016, 0x0093, 0x0094, 0x0095, 0x0096, 0x0004, 0x0098, 0x0099, 0x009A, 0x009B, 0x0014, 0x0015, 0x009E, 0x001A, + 0x0020, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398, 0x0399, 0x005B, 0x002E, 0x003C, 0x0028, 0x002B, 0x0021, + 0x0026, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F, 0x03A0, 0x03A1, 0x03A3, 0x005D, 0x0024, 0x002A, 0x0029, 0x003B, 0x005E, + 0x002D, 0x002F, 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9, 0xFFFD, 0xFFFD, 0xFFFD, 0x002C, 0x0025, 0x005F, 0x003E, 0x003F, + 0xFFFD, 0x0386, 0x0388, 0x0389, 0xFFFD, 0x038A, 0x038C, 0x038E, 0x038F, 0x0060, 0x003A, 0x00A3, 0x00A7, 0x0027, 0x003D, 0x0022, + 0x00C4, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, + 0x00D6, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070, 0x0071, 0x0072, 0x03B7, 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, + 0x00DC, 0x00A8, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x03BD, 0x03BE, 0x03BF, 0x03C0, 0x03C1, 0x03C2, + 0xFFFD, 0x03AC, 0x03AD, 0x03AE, 0x03CA, 0x03AF, 0x03CC, 0x03CD, 0x03CB, 0x03CE, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8, + 0x00B8, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0xFFFD, 0x03C9, 0x00C2, 0x00E0, 0x00E4, 0x00EA, + 0x00B4, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050, 0x0051, 0x0052, 0x00B1, 0x00E9, 0x00E8, 0x00EB, 0x00EE, 0x00EF, + 0x00B0, 0xFFFD, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x00BD, 0x00F6, 0x00F4, 0x00FB, 0x00F9, 0x00FC, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x00FF, 0x00E7, 0x00C7, 0xFFFD, 0xFFFD, 0x009F, + }; + + // 20866 KOI8-R + constexpr unsigned short KOI8RTable[256] = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, + 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, + 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, + 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, + 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, + 0x2500, 0x2502, 0x250C, 0x2510, 0x2514, 0x2518, 0x251C, 0x2524, 0x252C, 0x2534, 0x253C, 0x2580, 0x2584, 0x2588, 0x258C, 0x2590, + 0x2591, 0x2592, 0x2593, 0x2320, 0x25A0, 0x2219, 0x221A, 0x2248, 0x2264, 0x2265, 0x00A0, 0x2321, 0x00B0, 0x00B2, 0x00B7, 0x00F7, + 0x2550, 0x2551, 0x2552, 0x0451, 0x2553, 0x2554, 0x2555, 0x2556, 0x2557, 0x2558, 0x2559, 0x255A, 0x255B, 0x255C, 0x255D, 0x255E, + 0x255F, 0x2560, 0x2561, 0x0401, 0x2562, 0x2563, 0x2564, 0x2565, 0x2566, 0x2567, 0x2568, 0x2569, 0x256A, 0x256B, 0x256C, 0x00A9, + 0x044E, 0x0430, 0x0431, 0x0446, 0x0434, 0x0435, 0x0444, 0x0433, 0x0445, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, + 0x043F, 0x044F, 0x0440, 0x0441, 0x0442, 0x0443, 0x0436, 0x0432, 0x044C, 0x044B, 0x0437, 0x0448, 0x044D, 0x0449, 0x0447, 0x044A, + 0x042E, 0x0410, 0x0411, 0x0426, 0x0414, 0x0415, 0x0424, 0x0413, 0x0425, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, + 0x041F, 0x042F, 0x0420, 0x0421, 0x0422, 0x0423, 0x0416, 0x0412, 0x042C, 0x042B, 0x0417, 0x0428, 0x042D, 0x0429, 0x0427, 0x042A, + }; + + // 20871 IBM871 EBCDIC Icelandic + constexpr unsigned short EBCDIC871Table[256] = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x009C, 0x0009, 0x0086, 0x007F, 0x0097, 0x008D, 0x008E, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x009D, 0x0085, 0x0008, 0x0087, 0x0018, 0x0019, 0x0092, 0x008F, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x000A, 0x0017, 0x001B, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x0005, 0x0006, 0x0007, + 0x0090, 0x0091, 0x0016, 0x0093, 0x0094, 0x0095, 0x0096, 0x0004, 0x0098, 0x0099, 0x009A, 0x009B, 0x0014, 0x0015, 0x009E, 0x001A, + 0x0020, 0x00A0, 0x00E2, 0x00E4, 0x00E0, 0x00E1, 0x00E3, 0x00E5, 0x00E7, 0x00F1, 0x00FE, 0x002E, 0x003C, 0x0028, 0x002B, 0x0021, + 0x0026, 0x00E9, 0x00EA, 0x00EB, 0x00E8, 0x00ED, 0x00EE, 0x00EF, 0x00EC, 0x00DF, 0x00C6, 0x0024, 0x002A, 0x0029, 0x003B, 0x00D6, + 0x002D, 0x002F, 0x00C2, 0x00C4, 0x00C0, 0x00C1, 0x00C3, 0x00C5, 0x00C7, 0x00D1, 0x00A6, 0x002C, 0x0025, 0x005F, 0x003E, 0x003F, + 0x00F8, 0x00C9, 0x00CA, 0x00CB, 0x00C8, 0x00CD, 0x00CE, 0x00CF, 0x00CC, 0x00F0, 0x003A, 0x0023, 0x00D0, 0x0027, 0x003D, 0x0022, + 0x00D8, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x00AB, 0x00BB, 0x0060, 0x00FD, 0x007B, 0x00B1, + 0x00B0, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070, 0x0071, 0x0072, 0x00AA, 0x00BA, 0x007D, 0x00B8, 0x005D, 0x00A4, + 0x00B5, 0x00F6, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x00A1, 0x00BF, 0x0040, 0x00DD, 0x005B, 0x00AE, + 0x00A2, 0x00A3, 0x00A5, 0x00B7, 0x00A9, 0x00A7, 0x00B6, 0x00BC, 0x00BD, 0x00BE, 0x00AC, 0x007C, 0x00AF, 0x00A8, 0x005C, 0x00D7, + 0x00DE, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x00AD, 0x00F4, 0x007E, 0x00F2, 0x00F3, 0x00F5, + 0x00E6, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050, 0x0051, 0x0052, 0x00B9, 0x00FB, 0x00FC, 0x00F9, 0x00FA, 0x00FF, + 0x00B4, 0x00F7, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x00B2, 0x00D4, 0x005E, 0x00D2, 0x00D3, 0x00D5, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x00B3, 0x00DB, 0x00DC, 0x00D9, 0x00DA, 0x009F, + }; + + // 21025 IBM1025 EBCDIC Cyrillic Serbian-Bulgarian + constexpr unsigned short EBCDIC1025Table[256] = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x009C, 0x0009, 0x0086, 0x007F, 0x0097, 0x008D, 0x008E, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x009D, 0x0085, 0x0008, 0x0087, 0x0018, 0x0019, 0x0092, 0x008F, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x000A, 0x0017, 0x001B, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x0005, 0x0006, 0x0007, + 0x0090, 0x0091, 0x0016, 0x0093, 0x0094, 0x0095, 0x0096, 0x0004, 0x0098, 0x0099, 0x009A, 0x009B, 0x0014, 0x0015, 0x009E, 0x001A, + 0x0020, 0x00A0, 0x0452, 0x0453, 0x0451, 0x0454, 0x0455, 0x0456, 0x0457, 0x0458, 0x005B, 0x002E, 0x003C, 0x0028, 0x002B, 0x0021, + 0x0026, 0x0459, 0x045A, 0x045B, 0x045C, 0x045E, 0x045F, 0x042A, 0x2116, 0x0402, 0x005D, 0x0024, 0x002A, 0x0029, 0x003B, 0x005E, + 0x002D, 0x002F, 0x0403, 0x0401, 0x0404, 0x0405, 0x0406, 0x0407, 0x0408, 0x0409, 0x007C, 0x002C, 0x0025, 0x005F, 0x003E, 0x003F, + 0x040A, 0x040B, 0x040C, 0x00AD, 0x040E, 0x040F, 0x044E, 0x0430, 0x0431, 0x0060, 0x003A, 0x0023, 0x0040, 0x0027, 0x003D, 0x0022, + 0x0446, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x0434, 0x0435, 0x0444, 0x0433, 0x0445, 0x0438, + 0x0439, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070, 0x0071, 0x0072, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F, + 0x044F, 0x007E, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x0440, 0x0441, 0x0442, 0x0443, 0x0436, 0x0432, + 0x044C, 0x044B, 0x0437, 0x0448, 0x044D, 0x0449, 0x0447, 0x044A, 0x042E, 0x0410, 0x0411, 0x0426, 0x0414, 0x0415, 0x0424, 0x0413, + 0x007B, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x0425, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, + 0x007D, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050, 0x0051, 0x0052, 0x041D, 0x041E, 0x041F, 0x042F, 0x0420, 0x0421, + 0x005C, 0x00A7, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x0422, 0x0423, 0x0416, 0x0412, 0x042C, 0x042B, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x0417, 0x0428, 0x042D, 0x0429, 0x0427, 0x009F, + }; + + // 21866 KOI8-U + constexpr unsigned short KOI8UTable[256] = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, + 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, + 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, + 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, + 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, + 0x2500, 0x2502, 0x250C, 0x2510, 0x2514, 0x2518, 0x251C, 0x2524, 0x252C, 0x2534, 0x253C, 0x2580, 0x2584, 0x2588, 0x258C, 0x2590, + 0x2591, 0x2592, 0x2593, 0x2320, 0x25A0, 0x2219, 0x221A, 0x2248, 0x2264, 0x2265, 0x00A0, 0x2321, 0x00B0, 0x00B2, 0x00B7, 0x00F7, + 0x2550, 0x2551, 0x2552, 0x0451, 0x0454, 0x2554, 0x0456, 0x0457, 0x2557, 0x2558, 0x2559, 0x255A, 0x255B, 0x0491, 0x255D, 0x255E, + 0x255F, 0x2560, 0x2561, 0x0401, 0x0404, 0x2563, 0x0406, 0x0407, 0x2566, 0x2567, 0x2568, 0x2569, 0x256A, 0x0490, 0x256C, 0x00A9, + 0x044E, 0x0430, 0x0431, 0x0446, 0x0434, 0x0435, 0x0444, 0x0433, 0x0445, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, + 0x043F, 0x044F, 0x0440, 0x0441, 0x0442, 0x0443, 0x0436, 0x0432, 0x044C, 0x044B, 0x0437, 0x0448, 0x044D, 0x0449, 0x0447, 0x044A, + 0x042E, 0x0410, 0x0411, 0x0426, 0x0414, 0x0415, 0x0424, 0x0413, 0x0425, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, + 0x041F, 0x042F, 0x0420, 0x0421, 0x0422, 0x0423, 0x0416, 0x0412, 0x042C, 0x042B, 0x0417, 0x0428, 0x042D, 0x0429, 0x0427, 0x042A, + }; + + // 28591 ISO 8859-1 + constexpr unsigned short Latin1Table[256] = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, + 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, + 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, + 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, + 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, + 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F, + 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F, + 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, + 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, + 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, + 0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF, + 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, + 0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF, + }; + + // 28592 ISO 8859-2 + constexpr unsigned short Latin2Table[256] = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, + 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, + 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, + 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, + 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, + 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F, + 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F, + 0x00A0, 0x0104, 0x02D8, 0x0141, 0x00A4, 0x013D, 0x015A, 0x00A7, 0x00A8, 0x0160, 0x015E, 0x0164, 0x0179, 0x00AD, 0x017D, 0x017B, + 0x00B0, 0x0105, 0x02DB, 0x0142, 0x00B4, 0x013E, 0x015B, 0x02C7, 0x00B8, 0x0161, 0x015F, 0x0165, 0x017A, 0x02DD, 0x017E, 0x017C, + 0x0154, 0x00C1, 0x00C2, 0x0102, 0x00C4, 0x0139, 0x0106, 0x00C7, 0x010C, 0x00C9, 0x0118, 0x00CB, 0x011A, 0x00CD, 0x00CE, 0x010E, + 0x0110, 0x0143, 0x0147, 0x00D3, 0x00D4, 0x0150, 0x00D6, 0x00D7, 0x0158, 0x016E, 0x00DA, 0x0170, 0x00DC, 0x00DD, 0x0162, 0x00DF, + 0x0155, 0x00E1, 0x00E2, 0x0103, 0x00E4, 0x013A, 0x0107, 0x00E7, 0x010D, 0x00E9, 0x0119, 0x00EB, 0x011B, 0x00ED, 0x00EE, 0x010F, + 0x0111, 0x0144, 0x0148, 0x00F3, 0x00F4, 0x0151, 0x00F6, 0x00F7, 0x0159, 0x016F, 0x00FA, 0x0171, 0x00FC, 0x00FD, 0x0163, 0x02D9, + }; + + // 28593 ISO 8859-3 + constexpr unsigned short Latin3Table[256] = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, + 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, + 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, + 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, + 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, + 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F, + 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F, + 0x00A0, 0x0126, 0x02D8, 0x00A3, 0x00A4, 0xFFFD, 0x0124, 0x00A7, 0x00A8, 0x0130, 0x015E, 0x011E, 0x0134, 0x00AD, 0xFFFD, 0x017B, + 0x00B0, 0x0127, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x0125, 0x00B7, 0x00B8, 0x0131, 0x015F, 0x011F, 0x0135, 0x00BD, 0xFFFD, 0x017C, + 0x00C0, 0x00C1, 0x00C2, 0xFFFD, 0x00C4, 0x010A, 0x0108, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, + 0xFFFD, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x0120, 0x00D6, 0x00D7, 0x011C, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x016C, 0x015C, 0x00DF, + 0x00E0, 0x00E1, 0x00E2, 0xFFFD, 0x00E4, 0x010B, 0x0109, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, + 0xFFFD, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x0121, 0x00F6, 0x00F7, 0x011D, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x016D, 0x015D, 0x02D9, + }; + + // 28594 ISO 8859-4 + constexpr unsigned short Latin4Table[256] = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, + 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, + 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, + 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, + 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, + 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F, + 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F, + 0x00A0, 0x0104, 0x0138, 0x0156, 0x00A4, 0x0128, 0x013B, 0x00A7, 0x00A8, 0x0160, 0x0112, 0x0122, 0x0166, 0x00AD, 0x017D, 0x00AF, + 0x00B0, 0x0105, 0x02DB, 0x0157, 0x00B4, 0x0129, 0x013C, 0x02C7, 0x00B8, 0x0161, 0x0113, 0x0123, 0x0167, 0x014A, 0x017E, 0x014B, + 0x0100, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x012E, 0x010C, 0x00C9, 0x0118, 0x00CB, 0x0116, 0x00CD, 0x00CE, 0x012A, + 0x0110, 0x0145, 0x014C, 0x0136, 0x00D4, 0x00D5, 0x00D6, 0x00D7, 0x00D8, 0x0172, 0x00DA, 0x00DB, 0x00DC, 0x0168, 0x016A, 0x00DF, + 0x0101, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x012F, 0x010D, 0x00E9, 0x0119, 0x00EB, 0x0117, 0x00ED, 0x00EE, 0x012B, + 0x0111, 0x0146, 0x014D, 0x0137, 0x00F4, 0x00F5, 0x00F6, 0x00F7, 0x00F8, 0x0173, 0x00FA, 0x00FB, 0x00FC, 0x0169, 0x016B, 0x02D9, + }; + + // 28595 ISO 8859-5 + constexpr unsigned short Latin5Table[256] = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, + 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, + 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, + 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, + 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, + 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F, + 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F, + 0x00A0, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0406, 0x0407, 0x0408, 0x0409, 0x040A, 0x040B, 0x040C, 0x00AD, 0x040E, 0x040F, + 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F, + 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, 0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F, + 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F, + 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F, + 0x2116, 0x0451, 0x0452, 0x0453, 0x0454, 0x0455, 0x0456, 0x0457, 0x0458, 0x0459, 0x045A, 0x045B, 0x045C, 0x00A7, 0x045E, 0x045F, + }; + + // 28596 ISO 8859-6 + constexpr unsigned short Latin6Table[256] = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, + 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, + 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, + 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, + 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, + 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F, + 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F, + 0x00A0, 0xFFFD, 0xFFFD, 0xFFFD, 0x00A4, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0x060C, 0x00AD, 0xFFFD, 0xFFFD, + 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0x061B, 0xFFFD, 0xFFFD, 0xFFFD, 0x061F, + 0xFFFD, 0x0621, 0x0622, 0x0623, 0x0624, 0x0625, 0x0626, 0x0627, 0x0628, 0x0629, 0x062A, 0x062B, 0x062C, 0x062D, 0x062E, 0x062F, + 0x0630, 0x0631, 0x0632, 0x0633, 0x0634, 0x0635, 0x0636, 0x0637, 0x0638, 0x0639, 0x063A, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, + 0x0640, 0x0641, 0x0642, 0x0643, 0x0644, 0x0645, 0x0646, 0x0647, 0x0648, 0x0649, 0x064A, 0x064B, 0x064C, 0x064D, 0x064E, 0x064F, + 0x0650, 0x0651, 0x0652, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, + }; + + // 28597 ISO 8859-7 + constexpr unsigned short Latin7Table[256] = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, + 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, + 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, + 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, + 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, + 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F, + 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F, + 0x00A0, 0x2018, 0x2019, 0x00A3, 0x20AC, 0x20AF, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x037A, 0x00AB, 0x00AC, 0x00AD, 0xFFFD, 0x2015, + 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x0384, 0x0385, 0x0386, 0x00B7, 0x0388, 0x0389, 0x038A, 0x00BB, 0x038C, 0x00BD, 0x038E, 0x038F, + 0x0390, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398, 0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F, + 0x03A0, 0x03A1, 0xFFFD, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9, 0x03AA, 0x03AB, 0x03AC, 0x03AD, 0x03AE, 0x03AF, + 0x03B0, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, + 0x03C0, 0x03C1, 0x03C2, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8, 0x03C9, 0x03CA, 0x03CB, 0x03CC, 0x03CD, 0x03CE, 0xFFFD, + }; + + // 28598 ISO 8859-8 Hebrew (logical) + constexpr unsigned short Latin8Table[256] = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, + 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, + 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, + 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, + 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, + 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F, + 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F, + 0x00A0, 0xFFFD, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x00D7, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, + 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00B8, 0x00B9, 0x00F7, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0xFFFD, + 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, + 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0x2017, + 0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7, 0x05D8, 0x05D9, 0x05DA, 0x05DB, 0x05DC, 0x05DD, 0x05DE, 0x05DF, + 0x05E0, 0x05E1, 0x05E2, 0x05E3, 0x05E4, 0x05E5, 0x05E6, 0x05E7, 0x05E8, 0x05E9, 0x05EA, 0xFFFD, 0xFFFD, 0x200E, 0x200F, 0xFFFD, + }; + + // 28599 ISO 8859-9 + constexpr unsigned short Latin9Table[256] = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, + 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, + 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, + 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, + 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, + 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F, + 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F, + 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, + 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, + 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, + 0x011E, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x0130, 0x015E, 0x00DF, + 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, + 0x011F, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x0131, 0x015F, 0x00FF, + }; + + // 28603 ISO 8859-13 + constexpr unsigned short Latin13Table[256] = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, + 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, + 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, + 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, + 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, + 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F, + 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F, + 0x00A0, 0x201D, 0x00A2, 0x00A3, 0x00A4, 0x201E, 0x00A6, 0x00A7, 0x00D8, 0x00A9, 0x0156, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00C6, + 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x201C, 0x00B5, 0x00B6, 0x00B7, 0x00F8, 0x00B9, 0x0157, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00E6, + 0x0104, 0x012E, 0x0100, 0x0106, 0x00C4, 0x00C5, 0x0118, 0x0112, 0x010C, 0x00C9, 0x0179, 0x0116, 0x0122, 0x0136, 0x012A, 0x013B, + 0x0160, 0x0143, 0x0145, 0x00D3, 0x014C, 0x00D5, 0x00D6, 0x00D7, 0x0172, 0x0141, 0x015A, 0x016A, 0x00DC, 0x017B, 0x017D, 0x00DF, + 0x0105, 0x012F, 0x0101, 0x0107, 0x00E4, 0x00E5, 0x0119, 0x0113, 0x010D, 0x00E9, 0x017A, 0x0117, 0x0123, 0x0137, 0x012B, 0x013C, + 0x0161, 0x0144, 0x0146, 0x00F3, 0x014D, 0x00F5, 0x00F6, 0x00F7, 0x0173, 0x0142, 0x015B, 0x016B, 0x00FC, 0x017C, 0x017E, 0x2019, + }; + + // 28605 ISO 8859-15 + constexpr unsigned short Latin15Table[256] = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, + 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, + 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, + 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, + 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, + 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F, + 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F, + 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x20AC, 0x00A5, 0x0160, 0x00A7, 0x0161, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, + 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x017D, 0x00B5, 0x00B6, 0x00B7, 0x017E, 0x00B9, 0x00BA, 0x00BB, 0x0152, 0x0153, 0x0178, 0x00BF, + 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, + 0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF, + 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, + 0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF, + }; + + constexpr CodePageEntry codePageTable[] = { + { Type::EBCDIC037, 37, EBCDIC037Table }, + { Type::OEM437, 437, OEM437Table }, + { Type::EBCDIC500, 500, EBCDIC500Table }, + { Type::OEM708, 708, OEM708Table }, + { Type::OEM737, 737, OEM737Table }, + { Type::OEM775, 775, OEM775Table }, + { Type::OEM850, 850, OEM850Table }, + { Type::OEM852, 852, OEM852Table }, + { Type::OEM855, 855, OEM855Table }, + { Type::OEM857, 857, OEM857Table }, + { Type::OEM858, 858, OEM858Table }, + { Type::OEM860, 860, OEM860Table }, + { Type::OEM861, 861, OEM861Table }, + { Type::OEM862, 862, OEM862Table }, + { Type::OEM863, 863, OEM863Table }, + { Type::OEM864, 864, OEM864Table }, + { Type::OEM865, 865, OEM865Table }, + { Type::OEM866, 866, OEM866Table }, + { Type::OEM869, 869, OEM869Table }, + { Type::Thai874, 874, Thai874Table }, + { Type::EBCDIC1047, 1047, EBCDIC1047Table }, + { Type::EBCDIC1140, 1140, EBCDIC1140Table }, + { Type::EBCDIC1141, 1141, EBCDIC1141Table }, + { Type::EBCDIC1142, 1142, EBCDIC1142Table }, + { Type::EBCDIC1143, 1143, EBCDIC1143Table }, + { Type::EBCDIC1144, 1144, EBCDIC1144Table }, + { Type::EBCDIC1145, 1145, EBCDIC1145Table }, + { Type::EBCDIC1146, 1146, EBCDIC1146Table }, + { Type::EBCDIC1147, 1147, EBCDIC1147Table }, + { Type::EBCDIC1148, 1148, EBCDIC1148Table }, + { Type::EBCDIC1149, 1149, EBCDIC1149Table }, + { Type::Windows1250, 1250, Windows1250Table }, + { Type::Windows1251, 1251, Windows1251Table }, + { Type::Windows1252, 1252, Windows1252Table }, + { Type::Windows1253, 1253, Windows1253Table }, + { Type::Windows1254, 1254, Windows1254Table }, + { Type::Windows1255, 1255, Windows1255Table }, + { Type::Windows1256, 1256, Windows1256Table }, + { Type::Windows1257, 1257, Windows1257Table }, + { Type::Windows1258, 1258, Windows1258Table }, + { Type::ASCII, 20127, ASCIITable }, + { Type::EBCDIC273, 20273, EBCDIC273Table }, + { Type::EBCDIC277, 20277, EBCDIC277Table }, + { Type::EBCDIC278, 20278, EBCDIC278Table }, + { Type::EBCDIC280, 20280, EBCDIC280Table }, + { Type::EBCDIC284, 20284, EBCDIC284Table }, + { Type::EBCDIC285, 20285, EBCDIC285Table }, + { Type::EBCDIC290, 20290, EBCDIC290Table }, + { Type::EBCDIC297, 20297, EBCDIC297Table }, + { Type::EBCDIC420, 20420, EBCDIC420Table }, + { Type::EBCDIC423, 20423, EBCDIC423Table }, + { Type::KOI8R, 20866, KOI8RTable }, + { Type::EBCDIC871, 20871, EBCDIC871Table }, + { Type::EBCDIC1025, 21025, EBCDIC1025Table }, + { Type::KOI8U, 21866, KOI8UTable }, + { Type::Latin1, 28591, Latin1Table }, + { Type::Latin2, 28592, Latin2Table }, + { Type::Latin3, 28593, Latin3Table }, + { Type::Latin4, 28594, Latin4Table }, + { Type::Latin5, 28595, Latin5Table }, + { Type::Latin6, 28596, Latin6Table }, + { Type::Latin7, 28597, Latin7Table }, + { Type::Latin8, 28598, Latin8Table }, + { Type::Latin9, 28599, Latin9Table }, + { Type::Latin13, 28603, Latin13Table }, + { Type::Latin15, 28605, Latin15Table }, + }; + + // Every Type but UTF8 has exactly one row. + static_assert( + std::size(codePageTable) == 66, + "the code page table and Charset::Type disagree"); + + // A Type and the CSET code page number name the same row, so the + // code page of every row has to equal the value of its Type. + static_assert([] { + for(auto entry : codePageTable) { + if(entry.codePage != static_cast(entry.type)) + return false; + } + return true; + }(), "a row's code page does not match the value of its Type"); + + // A row is found by its code page, which is also the value of its Type. + // The index is built once, from the table above, and never changes, so + // a lookup cannot move it and the pointer entryForCodePage() hands out + // stays valid. The local static guards that one time build. + static const std::unordered_map &codePageIndex() + { + static const std::unordered_map index = [] { + std::unordered_map m; + m.reserve(std::size(codePageTable)); + for(const auto &entry : codePageTable) + m.emplace(entry.codePage, &entry); + return m; + }(); + + return index; + } + + const CodePageEntry *entryForCodePage(unsigned int codePage) + { + const auto &index = codePageIndex(); + const auto it = index.find(codePage); + return it != index.end() ? it->second : nullptr; + } + +} // namespace TagLib::Charset::detail diff --git a/taglib/toolkit/charsettables.h b/taglib/toolkit/charsettables.h new file mode 100644 index 00000000..55b1648f --- /dev/null +++ b/taglib/toolkit/charsettables.h @@ -0,0 +1,62 @@ +/*************************************************************************** + * This library is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Lesser General Public License version * + * 2.1 as published by the Free Software Foundation. * + * * + * This library is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * + * 02110-1301 USA * + * * + * Alternatively, this file is available under the Mozilla Public * + * License Version 1.1. You may obtain a copy of the License at * + * http://www.mozilla.org/MPL/ * + ***************************************************************************/ + +#ifndef TAGLIB_CHARSETTABLES_H +#define TAGLIB_CHARSETTABLES_H + +// THIS FILE IS NOT A PART OF THE TAGLIB API + +#ifndef DO_NOT_DOCUMENT // tell Doxygen not to document this header + +#include "tcharset.h" +#include "taglib_config.h" + +namespace TagLib { + namespace Charset { + namespace detail { + + /*! + * One row of the generated code page table, sorted by code page. UTF8 + * has no row because it is not a single byte code page. + */ + struct CodePageEntry + { + Type type; + unsigned int codePage; + const unsigned short *toUnicode; + }; + + /*! + * Returns the row for \a codePage, or null if there is no table for it. + * A Type and the code page number a CSET chunk states are the same value, + * so they name the same row. UTF8 has no row and is never found here. + */ +#ifdef TAGLIB_WITH_CHARSETTABLES + const CodePageEntry *entryForCodePage(unsigned int codePage); +#else + inline const CodePageEntry *entryForCodePage(unsigned int) { return nullptr; } +#endif + } // namespace detail + } // namespace Charset +} // namespace TagLib + +#endif // DO_NOT_DOCUMENT + +#endif diff --git a/taglib/toolkit/tcharset.cpp b/taglib/toolkit/tcharset.cpp new file mode 100644 index 00000000..7228acc1 --- /dev/null +++ b/taglib/toolkit/tcharset.cpp @@ -0,0 +1,99 @@ +/*************************************************************************** + * This library is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Lesser General Public License version * + * 2.1 as published by the Free Software Foundation. * + * * + * This library is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * + * 02110-1301 USA * + * * + * Alternatively, this file is available under the Mozilla Public * + * License Version 1.1. You may obtain a copy of the License at * + * http://www.mozilla.org/MPL/ * + ***************************************************************************/ + +#include "tcharset.h" + +#include +#include + +#include "charsettables.h" +#include "tbytevector.h" +#include "tstring.h" + +using namespace TagLib; + +std::optional TagLib::Charset::typeForCodePage(unsigned int codePage) +{ + if(codePage == 65001) + return Type::UTF8; + const auto *entry = detail::entryForCodePage(codePage); + if(entry) + return entry->type; + + return std::nullopt; +} + +unsigned int TagLib::Charset::codePageForType(Type type) +{ + return static_cast(type); +} + +String TagLib::Charset::decode(const ByteVector &data, Type type) +{ + if(type == Type::UTF8) + return String(data, String::UTF8); + + // Fast path for likely Latin-1 case + if(type == Type::Latin1) + return String(data, String::Latin1); + + const auto *entry = detail::entryForCodePage(static_cast(type)); + if(!entry) + return String(); + + std::wstring text; + text.reserve(data.size()); + for(const char c : data) { + if(c == '\0') + break; + text.push_back(static_cast(entry->toUnicode[static_cast(c)])); + } + return String(text); +} + +ByteVector TagLib::Charset::encode(const String &s, Type type) +{ + if(type == Type::UTF8) + return s.data(String::UTF8); + + // Fast path for likely Latin-1 case + if(type == Type::Latin1) + return s.data(String::Latin1); + + const auto *entry = detail::entryForCodePage(static_cast(type)); + if(!entry) + return {}; + + const unsigned short *unicodes = entry->toUnicode; + ByteVector data(s.size(), '?'); + int i = 0; + for(auto it = s.begin(); it != s.end(); ++it, ++i) { + const wchar_t wc = *it; + if(wc > std::numeric_limits::max()) + continue; + for(int ch = 0; ch < 256; ++ch) { + if(unicodes[ch] == wc) { + data[i] = static_cast(ch); + break; + } + } + } + return data; +} diff --git a/taglib/toolkit/tcharset.h b/taglib/toolkit/tcharset.h new file mode 100644 index 00000000..6eeab68e --- /dev/null +++ b/taglib/toolkit/tcharset.h @@ -0,0 +1,164 @@ +/*************************************************************************** + * This library is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Lesser General Public License version * + * 2.1 as published by the Free Software Foundation. * + * * + * This library is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * + * 02110-1301 USA * + * * + * Alternatively, this file is available under the Mozilla Public * + * License Version 1.1. You may obtain a copy of the License at * + * http://www.mozilla.org/MPL/ * + ***************************************************************************/ + +#ifndef TAGLIB_TCHARSET_H +#define TAGLIB_TCHARSET_H + +#include "taglib_export.h" + +#include + +namespace TagLib { + + class ByteVector; + class String; + + /*! + * Conversions between the byte encodings found in files and TagLib's + * internal string representation, which is UTF-16 based and therefore + * independent of all of them. A decoded string is a normal String from + * there on; no charset is remembered beyond the call. + * + * These are kept out of String because String::Type only knows about the + * codecs that TagLib's own formats need, whereas the Windows code page + * numbering and the EBCDIC and DOS pages are used by none of them and only + * show up because a foreign container format asks for them by number. + */ + namespace Charset { + + /*! + * One of the supported encodings. Every value is the Microsoft code page + * number of a single byte encoding, which is the same numbering a CSET + * chunk of a RIFF file carries, so a caller can hand in the number a file + * states. UTF8 is the one exception: it is not a single byte code page, + * so it takes 65001, the number Windows uses for UTF-8 in place of a code + * page, and has no byte table of its own. + * + * \note The values are code page numbers and are therefore neither + * contiguous nor ordered; they are 37, 437, 1250, ... Nothing may use a + * value as an index. + */ + enum class Type : unsigned int + { + EBCDIC037 = 37, //!< IBM EBCDIC US-Canada + OEM437 = 437, //!< IBM437 OEM US + EBCDIC500 = 500, //!< IBM EBCDIC International + OEM708 = 708, //!< ASMO-708 Arabic + OEM737 = 737, //!< IBM737 OEM Greek + OEM775 = 775, //!< IBM775 OEM Baltic + OEM850 = 850, //!< IBM850 OEM Latin 1 + OEM852 = 852, //!< IBM852 OEM Latin 2 + OEM855 = 855, //!< IBM855 OEM Cyrillic + OEM857 = 857, //!< IBM857 OEM Turkish + OEM858 = 858, //!< IBM858 OEM Latin 1 with Euro + OEM860 = 860, //!< IBM860 OEM Portuguese + OEM861 = 861, //!< IBM861 OEM Icelandic + OEM862 = 862, //!< DOS-862 OEM Hebrew + OEM863 = 863, //!< IBM863 OEM French Canadian + OEM864 = 864, //!< IBM864 OEM Arabic + OEM865 = 865, //!< IBM865 OEM Nordic + OEM866 = 866, //!< IBM866 OEM Russian + OEM869 = 869, //!< IBM869 OEM Modern Greek + Thai874 = 874, //!< Thai (Windows-874) + EBCDIC1047 = 1047, //!< IBM1047 EBCDIC Latin 1/Open System + EBCDIC1140 = 1140, //!< IBM037 EBCDIC US-Canada with Euro + EBCDIC1141 = 1141, //!< IBM273 EBCDIC Germany with Euro + EBCDIC1142 = 1142, //!< IBM277 EBCDIC Denmark-Norway with Euro + EBCDIC1143 = 1143, //!< IBM278 EBCDIC Finland-Sweden with Euro + EBCDIC1144 = 1144, //!< IBM280 EBCDIC Italy with Euro + EBCDIC1145 = 1145, //!< IBM284 EBCDIC Latin America-Spain with Euro + EBCDIC1146 = 1146, //!< IBM285 EBCDIC UK with Euro + EBCDIC1147 = 1147, //!< IBM297 EBCDIC France with Euro + EBCDIC1148 = 1148, //!< IBM500 EBCDIC International with Euro + EBCDIC1149 = 1149, //!< IBM871 EBCDIC Icelandic with Euro + Windows1250= 1250, //!< Windows-1250 Central European + Windows1251= 1251, //!< Windows-1251 Cyrillic + Windows1252= 1252, //!< Windows-1252 Western European + Windows1253= 1253, //!< Windows-1253 Greek + Windows1254= 1254, //!< Windows-1254 Turkish + Windows1255= 1255, //!< Windows-1255 Hebrew + Windows1256= 1256, //!< Windows-1256 Arabic + Windows1257= 1257, //!< Windows-1257 Baltic + Windows1258= 1258, //!< Windows-1258 Vietnamese + ASCII = 20127, //!< US-ASCII + EBCDIC273 = 20273, //!< IBM273 EBCDIC Germany + EBCDIC277 = 20277, //!< IBM277 EBCDIC Denmark-Norway + EBCDIC278 = 20278, //!< IBM278 EBCDIC Finland-Sweden + EBCDIC280 = 20280, //!< IBM280 EBCDIC Italy + EBCDIC284 = 20284, //!< IBM284 EBCDIC Latin America-Spain + EBCDIC285 = 20285, //!< IBM285 EBCDIC United Kingdom + EBCDIC290 = 20290, //!< IBM290 EBCDIC Japanese Katakana Extended + EBCDIC297 = 20297, //!< IBM297 EBCDIC France + EBCDIC420 = 20420, //!< IBM420 EBCDIC Arabic + EBCDIC423 = 20423, //!< IBM423 EBCDIC Greek + KOI8R = 20866, //!< KOI8-R + EBCDIC871 = 20871, //!< IBM871 EBCDIC Icelandic + EBCDIC1025 = 21025, //!< IBM1025 EBCDIC Cyrillic Serbian-Bulgarian + KOI8U = 21866, //!< KOI8-U + Latin1 = 28591, //!< ISO 8859-1 + Latin2 = 28592, //!< ISO 8859-2 + Latin3 = 28593, //!< ISO 8859-3 + Latin4 = 28594, //!< ISO 8859-4 + Latin5 = 28595, //!< ISO 8859-5 + Latin6 = 28596, //!< ISO 8859-6 + Latin7 = 28597, //!< ISO 8859-7 + Latin8 = 28598, //!< ISO 8859-8 Hebrew (logical) + Latin9 = 28599, //!< ISO 8859-9 + Latin13 = 28603, //!< ISO 8859-13 + Latin15 = 28605, //!< ISO 8859-15 + UTF8 = 65001 //!< UTF-8 + }; + + /*! + * Returns the encoding identified by the Microsoft code page \a codePage, + * or nothing if this module cannot convert it. + * + * Note that code page 0 is not a code page. It is conventionally used to + * mean "the encoding was not specified", so substituting a default for it + * belongs to whoever read the number, not here. + */ + TAGLIB_EXPORT std::optional typeForCodePage(unsigned int codePage); + + /*! + * Returns the Microsoft code page identifying \a type. + */ + TAGLIB_EXPORT unsigned int codePageForType(Type type); + + /*! + * Decodes \a data according to \a type. Decoding stops at the first NUL, + * which is how the single byte code pages terminate a string. + */ + TAGLIB_EXPORT String decode(const ByteVector &data, Type type); + + /*! + * Encodes \a s according to \a type. + * + * A string is decoded into TagLib's internal representation, which is UTF-16 + * and not a byte encoding at all, so to encode all characters, UTF-8 has + * to be selected as the \a type. + * + * When encoding into a legacy code page, characters which cannot be + * represented by the code page are replaced by '?'. + */ + TAGLIB_EXPORT ByteVector encode(const String &s, Type type); + } // namespace Charset +} // namespace TagLib + +#endif diff --git a/tests/test_id3v1.cpp b/tests/test_id3v1.cpp index a99add2a..e71d70bd 100644 --- a/tests/test_id3v1.cpp +++ b/tests/test_id3v1.cpp @@ -26,22 +26,54 @@ #include #include +#include "taglib_config.h" #include "tstring.h" #include "mpegfile.h" #include "id3v1tag.h" #include "id3v1genres.h" #include +#include "plainfile.h" #include "utils.h" using namespace std; using namespace TagLib; +namespace +{ + // The bytes of a Windows code page an ID3v1 field cannot tell from ISO 8859-1 + // on disk, and the text they stand for. 0x80 is a Euro sign in every ANSI + // code page in use and a control character in ISO 8859-1. + const ByteVector kAnsiArtist("A \x80 B", 5); + const String kAnsiArtistText(L"A \x20AC B"); + + // A Cyrillic byte, which ISO 8859-1 cannot represent at all and which is once + // again a control character there. + const ByteVector kCyrillicArtist("A \xC0 B", 5); + const String kCyrillicArtistText(L"A \x0410 B"); + + // The same text in UTF-8, as software that writes UTF-8 into ID3v1 produces + // it, plus the ISO 8859-1 reading of those same bytes. + const ByteVector kUtf8Artist("Caf\xC3\xA9", 5); + const String kUtf8ArtistText(L"Caf\xE9"); + const String kUtf8ArtistLatin1(L"Caf\xC3\xA9"); +} + class TestID3v1 : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(TestID3v1); CPPUNIT_TEST(testStripWhiteSpace); CPPUNIT_TEST(testGenres); CPPUNIT_TEST(testRenamedGenres); +#ifdef TAGLIB_WITH_CHARSETTABLES + CPPUNIT_TEST(testAssumedCodePageDefaultIsLatin1); + CPPUNIT_TEST(testAssumedCodePage); + CPPUNIT_TEST(testConstructorCodePage); + CPPUNIT_TEST(testUnsupportedCodePage); + CPPUNIT_TEST(testCommentUsesCodePage); + CPPUNIT_TEST(testUserHandlerBeatsCodePage); + CPPUNIT_TEST(testMp3ReadsWithIso88591); + CPPUNIT_TEST(testMp3ReadsWithWindows1252); +#endif CPPUNIT_TEST_SUITE_END(); public: @@ -84,6 +116,360 @@ public: CPPUNIT_ASSERT_EQUAL(129U, tag.genreNumber()); } +#ifdef TAGLIB_WITH_CHARSETTABLES + //! A temporary MP3 file which is automatically deleted when it goes out of scope. + class ScopedMp3FileCopyWithId3v1Tag : public ScopedFileCopy + { + public: + static constexpr unsigned int ARTIST_OFFSET = 3 + 30; + static constexpr unsigned int COMMENT_OFFSET = 3 + 30 + 30 + 30 + 4; + + //! Construct MP3 file with an ID3v1 tag which has one raw \a value at + //! \a offset, which points by default to the artist field. + explicit ScopedMp3FileCopyWithId3v1Tag(const ByteVector &value, + unsigned int offset = ARTIST_OFFSET) + : ScopedFileCopy("xing", ".mp3") + { + ByteVector tag = ID3v1::Tag::fileIdentifier(); + // Leave empty fields up to the given offset. + tag.resize(offset, 0); + tag.append(value); + tag.resize(128, 0); + // 0xff in the genre byte is the ID3v1 value for "no genre". + tag[127] = static_cast(0xff); + + std::ofstream file(fileName().c_str(), std::ios::in | std::ios::out | std::ios::binary); + // The tag goes where a file already ending in one would carry it: the last + // 128 bytes. The source file is longer than that, so a shorter tag would + // not be found at all. + file.seekp(-128, std::ios::end); + file.write(tag.data(), tag.size()); + } + + //! The file offset of the ID3v1 tag. + offset_t tagOffset() const + { + std::ifstream file(fileName().c_str(), std::ios::binary | std::ios::ate); + return static_cast(file.tellg()) - 128; + } + + //! Get the raw field value from the ID3v1 tag starting at \a offset to + //! the next zero byte. + ByteVector getRawValue(unsigned int offset = ARTIST_OFFSET) + { + std::ifstream file(fileName().c_str(), std::ios::binary | std::ios::ate); + file.seekg(-128, std::ios::end); + ByteVector tag(128U, 0); + file.read(tag.data(), tag.size()); + return extractRawValue(tag, offset); + } + + static ByteVector extractRawValue(const ByteVector &tag, unsigned int offset = ARTIST_OFFSET) + { + if(tag.size() != 128) + return {}; + const char *str = tag.data(); + const char *end = std::find(str + offset, str + 128, '\0'); + return tag.mid(offset, static_cast(end - str - offset)); + } + }; + + // Puts back the code page a tag is assumed to have when none is stated, so + // that a test stating another one does not carry it into the next test. + class RestoreAssumedCodePage + { + public: + RestoreAssumedCodePage() : + m_previous(ID3v1::Tag::assumedCodePage()) + { + } + + ~RestoreAssumedCodePage() + { + ID3v1::Tag::setAssumedCodePage(m_previous); + } + + private: + const unsigned int m_previous; + }; + + // A handler the user installed is an explicit override, so it outranks any + // code page, once again because installing one is a deliberate act. + class FixedStringHandler : public ID3v1::StringHandler + { + public: + String parse(const ByteVector &) const override { return String(L"from the handler"); } + }; + + // The ID3v1 specification says ISO 8859-1, and that is what an untouched + // library assumes: the bytes of a file that holds another encoding are read + // as the Latin-1 characters they also happen to be. + void testAssumedCodePageDefaultIsLatin1() + { + CPPUNIT_ASSERT_EQUAL(0u, ID3v1::Tag::assumedCodePage()); + + ScopedMp3FileCopyWithId3v1Tag copy(kUtf8Artist); + const string name = copy.fileName(); + + MPEG::File f(name.c_str()); + CPPUNIT_ASSERT(f.ID3v1Tag(false)); + CPPUNIT_ASSERT_EQUAL(kUtf8ArtistLatin1, f.ID3v1Tag(false)->artist()); + } + + // Software that writes UTF-8 or an ANSI code page without any way of saying + // so is what the option is for. Stated once, it is what every tag read + // afterwards is decoded with. + void testAssumedCodePage() + { + const RestoreAssumedCodePage restore; + + ID3v1::Tag::setAssumedCodePage(65001); + CPPUNIT_ASSERT_EQUAL(65001u, ID3v1::Tag::assumedCodePage()); + + { + ScopedMp3FileCopyWithId3v1Tag copy(kUtf8Artist); + const string name = copy.fileName(); + MPEG::File f(name.c_str()); + CPPUNIT_ASSERT_EQUAL(kUtf8ArtistText, f.ID3v1Tag(false)->artist()); + f.save(); + CPPUNIT_ASSERT_EQUAL(kUtf8Artist, copy.getRawValue()); + } + + ID3v1::Tag::setAssumedCodePage(1252); + CPPUNIT_ASSERT_EQUAL(1252u, ID3v1::Tag::assumedCodePage()); + + { + ScopedMp3FileCopyWithId3v1Tag copy(kAnsiArtist); + const string name = copy.fileName(); + MPEG::File f(name.c_str()); + CPPUNIT_ASSERT_EQUAL(kAnsiArtistText, f.ID3v1Tag(false)->artist()); + f.save(); + CPPUNIT_ASSERT_EQUAL(kAnsiArtist, copy.getRawValue()); + } + + ID3v1::Tag::setAssumedCodePage(1251); + CPPUNIT_ASSERT_EQUAL(1251u, ID3v1::Tag::assumedCodePage()); + + { + ScopedMp3FileCopyWithId3v1Tag copy(kCyrillicArtist); + const string name = copy.fileName(); + { + MPEG::File f(name.c_str()); + CPPUNIT_ASSERT_EQUAL(kCyrillicArtistText, f.ID3v1Tag(false)->artist()); + f.save(); + CPPUNIT_ASSERT_EQUAL(kCyrillicArtist, copy.getRawValue()); + } + { + MPEG::File f(name.c_str()); + ID3v1::Tag *tag = f.ID3v1Tag(false); + CPPUNIT_ASSERT_EQUAL(kCyrillicArtistText, tag->artist()); + // The character 03B1 is a greek alpha, which is not represented in 1251 + tag->setArtist((L"A \x0410\x03B1 B")); + CPPUNIT_ASSERT(f.save()); + } + { + MPEG::File f(name.c_str()); + CPPUNIT_ASSERT_EQUAL(String(L"A \x0410? B"), f.ID3v1Tag(false)->artist()); + } + } + + // Stating 0 again is going back to what the specification says. + ID3v1::Tag::setAssumedCodePage(0); + CPPUNIT_ASSERT_EQUAL(0u, ID3v1::Tag::assumedCodePage()); + + { + ScopedMp3FileCopyWithId3v1Tag copy(kUtf8Artist); + const string name = copy.fileName(); + MPEG::File f(name.c_str()); + CPPUNIT_ASSERT_EQUAL(kUtf8ArtistLatin1, f.ID3v1Tag(false)->artist()); + f.save(); + CPPUNIT_ASSERT_EQUAL(kUtf8Artist, copy.getRawValue()); + } + } + + // A tag can be given its own code page as well, for callers that read files + // from more than one source and cannot state a single process wide default. + void testConstructorCodePage() + { + const RestoreAssumedCodePage restore; + + // Even with another assumption in place, the code page handed to the + // constructor is the one that decides. + ID3v1::Tag::setAssumedCodePage(65001); + + { + ScopedMp3FileCopyWithId3v1Tag copy(kAnsiArtist); + const string name = copy.fileName(); + PlainFile file(name.c_str()); + CPPUNIT_ASSERT(file.isValid()); + ID3v1::Tag tag(&file, copy.tagOffset(), 1252); + CPPUNIT_ASSERT_EQUAL(kAnsiArtistText, tag.artist()); + CPPUNIT_ASSERT_EQUAL(kAnsiArtist, + ScopedMp3FileCopyWithId3v1Tag::extractRawValue(tag.render())); + } + + { + ScopedMp3FileCopyWithId3v1Tag copy(kCyrillicArtist); + const string name = copy.fileName(); + PlainFile file(name.c_str()); + ID3v1::Tag tag(&file, copy.tagOffset(), 1251); + CPPUNIT_ASSERT_EQUAL(kCyrillicArtistText, tag.artist()); + CPPUNIT_ASSERT_EQUAL(kCyrillicArtist, + ScopedMp3FileCopyWithId3v1Tag::extractRawValue(tag.render())); + } + + // Code page 0 is the "not stated" value, which means the assumption. + { + ScopedMp3FileCopyWithId3v1Tag copy(kUtf8Artist); + const string name = copy.fileName(); + PlainFile file(name.c_str()); + ID3v1::Tag tag(&file, copy.tagOffset(), 0); + CPPUNIT_ASSERT_EQUAL(kUtf8ArtistText, tag.artist()); + CPPUNIT_ASSERT_EQUAL(kUtf8Artist, + ScopedMp3FileCopyWithId3v1Tag::extractRawValue(tag.render())); + } + } + + // A code page this library cannot decode falls back to what is assumed: the + // number is a hint about how the text was written, and dropping the whole tag + // over a hint that could not be followed would lose the fields as well. + void testUnsupportedCodePage() + { + const RestoreAssumedCodePage restore; + + // A code page this library has no table for, here a Japanese one, is + // refused as the assumption and the previous one stays. + ID3v1::Tag::setAssumedCodePage(1252); + ID3v1::Tag::setAssumedCodePage(932); + CPPUNIT_ASSERT_EQUAL(1252u, ID3v1::Tag::assumedCodePage()); + + { + ScopedMp3FileCopyWithId3v1Tag copy(kAnsiArtist); + const string name = copy.fileName(); + PlainFile file(name.c_str()); + // Handing the same unsupported number to the constructor falls back to + // the assumption rather than refusing to read the tag. + ID3v1::Tag tag(&file, copy.tagOffset(), 932); + CPPUNIT_ASSERT_EQUAL(kAnsiArtistText, tag.artist()); + CPPUNIT_ASSERT_EQUAL(kAnsiArtist, + ScopedMp3FileCopyWithId3v1Tag::extractRawValue(tag.render())); + } + } + + // The charset covers the whole tag, the ID3v1.0 comment included. Its 30 + // bytes are a single field there, so it goes through the same decoding even + // though ID3v1.1 would have cut the track number out of it. + void testCommentUsesCodePage() + { + const RestoreAssumedCodePage restore; + ID3v1::Tag::setAssumedCodePage(1252); + + ScopedMp3FileCopyWithId3v1Tag copy(kAnsiArtist, + ScopedMp3FileCopyWithId3v1Tag::COMMENT_OFFSET); + const string name = copy.fileName(); + MPEG::File f(name.c_str()); + CPPUNIT_ASSERT(f.ID3v1Tag(false)); + CPPUNIT_ASSERT_EQUAL(kAnsiArtistText, f.ID3v1Tag(false)->comment()); + CPPUNIT_ASSERT_EQUAL(kAnsiArtist, copy.getRawValue( + ScopedMp3FileCopyWithId3v1Tag::COMMENT_OFFSET)); + } + + // A real MP3 carrying an ID3v1 tag whose bytes are only meaningful as ISO + // 8859-1, which is what the ID3v1 specification prescribes and what a caller + // states explicitly. The bytes are one of Latin-1's own characters, so the + // reading is the text it spells; a code page that reassigns this range would + // not produce it. + void testMp3ReadsWithIso88591() + { + const RestoreAssumedCodePage restore; + + // Keep any assumption out of it: this tag is read as ISO 8859-1 because + // the code page says so, not because the default happens to agree. + ID3v1::Tag::setAssumedCodePage(65001); + + const ByteVector latin1Artist("Caf\xE9", 4); + const String latin1ArtistText(L"Caf\xE9"); + + ScopedMp3FileCopyWithId3v1Tag copy(latin1Artist); + const string name = copy.fileName(); + PlainFile file(name.c_str()); + CPPUNIT_ASSERT(file.isValid()); + ID3v1::Tag tag(&file, copy.tagOffset(), 28591); + CPPUNIT_ASSERT_EQUAL(latin1ArtistText, tag.artist()); + } + + // The same MP3 shape read as Windows-1252 instead. The bytes below are a + // Euro sign and a pair of curly quotes in 1252, and C1 controls in ISO + // 8859-1, so the two readings differ and only one of them is right. + void testMp3ReadsWithWindows1252() + { + const RestoreAssumedCodePage restore; + + // An assumption the tag contradicts, so the reading has to come from the + // code page handed to the constructor. + ID3v1::Tag::setAssumedCodePage(28591); + + const ByteVector windows1252Artist("A \x80 \x93q" "\x94", 7); + const String windows1252ArtistText(L"A \x20AC \x201Cq\x201D"); + + ScopedMp3FileCopyWithId3v1Tag copy(windows1252Artist); + const string name = copy.fileName(); + PlainFile file(name.c_str()); + CPPUNIT_ASSERT(file.isValid()); + ID3v1::Tag tag(&file, copy.tagOffset(), 1252); + CPPUNIT_ASSERT_EQUAL(windows1252ArtistText, tag.artist()); + } + + // An installed handler is used for every tag, whatever code page is stated, + // and removing it hands the tags back to that code page. + void testUserHandlerBeatsCodePage() + { + const RestoreAssumedCodePage restore; + + class RestoreHandler + { + public: + ~RestoreHandler() { ID3v1::Tag::setStringHandler(nullptr); } + } restoreHandler; + + ID3v1::Tag::setAssumedCodePage(1252); + + { + ScopedMp3FileCopyWithId3v1Tag copy(kAnsiArtist); + const string name = copy.fileName(); + MPEG::File f(name.c_str()); + CPPUNIT_ASSERT_EQUAL(kAnsiArtistText, f.ID3v1Tag(false)->artist()); + } + + const FixedStringHandler handler; + ID3v1::Tag::setStringHandler(&handler); + + { + ScopedMp3FileCopyWithId3v1Tag copy(kAnsiArtist); + const string name = copy.fileName(); + MPEG::File f(name.c_str()); + CPPUNIT_ASSERT_EQUAL(String(L"from the handler"), f.ID3v1Tag(false)->artist()); + } + + // Also for a tag whose code page was stated explicitly. + { + ScopedMp3FileCopyWithId3v1Tag copy(kAnsiArtist); + const string name = copy.fileName(); + PlainFile file(name.c_str()); + ID3v1::Tag tag(&file, copy.tagOffset(), 1252); + CPPUNIT_ASSERT_EQUAL(String(L"from the handler"), tag.artist()); + } + + ID3v1::Tag::setStringHandler(nullptr); + + { + ScopedMp3FileCopyWithId3v1Tag copy(kAnsiArtist); + const string name = copy.fileName(); + MPEG::File f(name.c_str()); + CPPUNIT_ASSERT_EQUAL(kAnsiArtistText, f.ID3v1Tag(false)->artist()); + } + } +#endif // TAGLIB_WITH_CHARSETTABLES }; CPPUNIT_TEST_SUITE_REGISTRATION(TestID3v1); diff --git a/tests/test_wav.cpp b/tests/test_wav.cpp index 309e844b..a7c861af 100644 --- a/tests/test_wav.cpp +++ b/tests/test_wav.cpp @@ -23,9 +23,12 @@ * http://www.mozilla.org/MPL/ * ***************************************************************************/ +#include #include #include +#include "taglib_config.h" +#include "tcharset.h" #include "id3v2tag.h" #include "infotag.h" #include "tbytevectorlist.h" @@ -40,6 +43,253 @@ using namespace std; using namespace TagLib; +#ifdef TAGLIB_WITH_CHARSETTABLES +namespace +{ + // The bytes of a chunk: its id, its size, its payload, and the pad byte that + // brings an odd sized payload to an even length. + ByteVector riffChunk(const char *id, const ByteVector &payload) + { + ByteVector chunk = ByteVector(id, 4) + ByteVector::fromUInt(payload.size(), false) + payload; + if(payload.size() % 2) + chunk.append(static_cast(0)); + return chunk; + } + + // The payload of a conforming CSET chunk, which the RIFF specification lays + // out as wCodePage, wCountryCode, wLanguageCode and wDialect, one WORD each. + // Only the code page matters here, so the other three are the zero that means + // "no opinion". + ByteVector riffCsetPayload(unsigned int codePage) + { + return ByteVector::fromShort(static_cast(codePage), false) + ByteVector(6, '\0'); + } + + // The same layout, with the country, language and dialect the file was written + // for filled in as well. These are what a reader that took the code page for a + // 32 bit number mixed into its result. + ByteVector riffCsetPayloadWithLanguage(unsigned int codePage) + { + return ByteVector::fromShort(static_cast(codePage), false) + + ByteVector::fromShort(1, false) + ByteVector::fromShort(9, false) + + ByteVector::fromShort(1, false); + } + + // One field of an INFO list, as the reader will find it: an id and the text + // in whatever encoding the file declares, without the NUL that ends it. + struct InfoField + { + const char *id; + ByteVector text; + }; + + // A WAV file with an INFO tag holding \a fields, and a CSET chunk whose raw + // payload is \a cset. An empty \a cset means no CSET chunk at all, which is + // what a file that declares no charset looks like. The CSET is written in + // front of the LIST it describes, unless \a csetBehindList is set, which is + // where a file TagLib did not write tends to put it. + ByteVector riffWithInfoCset(std::initializer_list fields, + const ByteVector &cset, bool csetBehindList = false) + { + ByteVector list("INFO", 4); + for(const auto &field : fields) + list.append(riffChunk(field.id, field.text + ByteVector(1, '\0'))); + + // fmt: PCM, one channel, 22050 Hz, 8 bits, 22050 bytes per second, and the + // 8 bytes of audio the data chunk below has to hold for the file to parse. + ByteVector body = + riffChunk("fmt ", ByteVector("\x01\x00\x01\x00\x3E\x56\x00\x00" + "\x3E\x56\x00\x00\x01\x00\x08\x00", 16)); + body.append(riffChunk("data", ByteVector(8, static_cast(0x80)))); + + if(!cset.isEmpty() && !csetBehindList) + body.append(riffChunk("CSET", cset)); + body.append(riffChunk("LIST", list)); + if(!cset.isEmpty() && csetBehindList) + body.append(riffChunk("CSET", cset)); + + return ByteVector("RIFF", 4) + ByteVector::fromUInt(body.size() + 4, false) + + ByteVector("WAVE", 4) + body; + } + + // The same file, with the charset declared by \a codePage in the conforming + // 8 byte CSET layout, or with no CSET chunk at all when \a codePage is 0. + ByteVector riffWithInfo(std::initializer_list fields, + unsigned int codePage = 0, bool csetBehindList = false) + { + return riffWithInfoCset(fields, codePage ? riffCsetPayload(codePage) : ByteVector(), + csetBehindList); + } + + // An INFO tag with no CSET chunk, which is what a reader has to fall back to + // Latin-1 on. + ByteVector latin1Riff() + { + return riffWithInfo({{"IART", ByteVector("A \xAE B", 5)}, + {"IPRD", ByteVector("A \xAE B", 5)}, + {"ICMT", ByteVector("Caf\xE9", 4)}, + {"ICRD", ByteVector("2024-01-01", 10)}}); + } + + // An INFO tag that declares UTF-8, with the CSET behind the LIST it describes. + ByteVector utf8Riff() + { + return riffWithInfo({{"IPRD", ByteVector("Caf\xC3\xA9", 5)}, + {"ICRD", ByteVector("2024-03-03", 10)}}, + 65001, true); + } + + // A WAV file whose INFO text is in \a codePage, holding samples that a + // different table would decode differently: 0x80 is the Euro sign in 1252 + // and U+0080 in Latin-1, 0x93/0x94 are curly quotes in 1252 and C1 controls + // in Latin-1, 0xc0..0xc2 are Cyrillic capitals in 1251 and accented Latin + // letters in Latin-1. So the expected strings below only come out right + // when the page the CSET declares is the table actually used. + ByteVector windows1252Riff() + { + return riffWithInfo({{"IART", ByteVector("A \x80 B", 5)}, + {"IPRD", ByteVector("Caf\xE9 \x80", 6)}, + {"ICMT", ByteVector("\x93quoted\x94", 10)}}, + 1252); + } + + // The same file, with the country, language and dialect of the CSET chunk + // filled in instead of left at their zero defaults. + ByteVector windows1252WithLanguageRiff() + { + return riffWithInfoCset({{"IART", ByteVector("A \x80 B", 5)}, + {"IPRD", ByteVector("Caf\xE9 \x80", 6)}, + {"ICMT", ByteVector("\x93quoted\x94", 10)}}, + riffCsetPayloadWithLanguage(1252)); + } + + ByteVector windows1251Riff() + { + return riffWithInfo({{"IART", ByteVector("A \xC0 B", 5)}, + {"IPRD", ByteVector("Caf\xE9", 4)}, + {"ICMT", ByteVector("\xC0\xC1\xC2", 3)}}, + 1251); + } + + // The same text in a file whose CSET chunk is truncated: its payload holds a + // single byte, where wCodePage alone needs 2, so it cannot hold the number the + // chunk exists to declare and the file is malformed. + ByteVector truncatedCsetRiff() + { + return riffWithInfoCset({{"IART", ByteVector("A \x80 B", 5)}}, + ByteVector("\xA4", 1)); + } + + // The same text in a file whose CSET holds the code page and nothing else: the + // three WORD fields behind wCodePage are what a conforming writer fills in, and + // a file that leaves them out entirely still says all there is to say. + ByteVector codePageOnlyCsetRiff() + { + return riffWithInfoCset({{"IART", ByteVector("A \x80 B", 5)}}, + ByteVector::fromShort(1252, false)); + } + + // A file whose CSET declares a code page this library has no table for, here a + // Japanese one. Its text is written in the RIFF default ISO 8859-1, so it reads + // back as itself: the number is a hint, and a hint that cannot be followed must + // not cost the tag its fields. + ByteVector unsupportedCsetRiff() + { + return riffWithInfo({{"IART", ByteVector("A \xAE B", 5)}, + {"ICMT", ByteVector("Caf\xE9", 4)}}, + 932); + } + + // A file whose INFO text is UTF-8 and which declares no charset at all. The + // specification cannot recognize this combination, because a file holding + // ISO 8859-1 and one holding UTF-8 look the same without a CSET chunk; only + // the reader's application knows which of them it wrote. + ByteVector utf8WithoutCsetRiff() + { + return riffWithInfo({{"IART", ByteVector("Caf\xC3\xA9", 5)}}, 0); + } + + // Puts back the code page an INFO tag assumes when a file declares none, so + // that a test stating another one does not carry it into the next test. + class RestoreAssumedCodePage + { + public: + RestoreAssumedCodePage() : + m_previous(RIFF::Info::Tag::assumedCodePage()) + { + } + + ~RestoreAssumedCodePage() + { + RIFF::Info::Tag::setAssumedCodePage(m_previous); + } + + private: + const unsigned int m_previous; + }; + + // Validates the raw chunk layout of the RIFF file in \a data against the + // format itself instead of against TagLib, so that a file TagLib has just + // written is checked independently of how TagLib would read it back. Returns + // the data of the first top level chunk called \a name. + ByteVector checkedRawChunk(const ByteVector &data, const char *name) + { + const ByteVector wanted(name); + + CPPUNIT_ASSERT(data.size() >= 12u); + CPPUNIT_ASSERT(data.startsWith("RIFF")); + + // The size in the header covers everything after the size field itself, + // so the file must be exactly that long: no gap and no trailing junk. + const offset_t declaredEnd = static_cast(data.toUInt(4, false)) + 8; + CPPUNIT_ASSERT_EQUAL(declaredEnd, static_cast(data.size())); + + ByteVector found; + offset_t offset = 12; + while(offset + 8 <= declaredEnd) { + const ByteVector chunkHeader = data.mid(static_cast(offset), 8); + CPPUNIT_ASSERT_EQUAL(8u, chunkHeader.size()); + + const unsigned int size = chunkHeader.toUInt(4, false); + const ByteVector payload = data.mid(static_cast(offset + 8), size); + CPPUNIT_ASSERT_EQUAL(size, payload.size()); + + if(found.isEmpty() && chunkHeader.startsWith(wanted)) + found = payload; + + // Chunks are padded to an even length, so an odd size is followed by a + // pad byte which is not counted in the size. + offset += 8 + size + (size % 2); + } + + // The chunks must tile the file exactly, up to and including its end. + CPPUNIT_ASSERT_EQUAL(declaredEnd, offset); + + return found; + } + + // The CSET chunk of a saved file must declare UTF-8 and nothing else, in the + // full 8 byte layout of four WORD fields: wCodePage is UTF-8's 65001, and the + // country, language and dialect behind it are the zero that means the default. + void assertRawCsetDeclaresUtf8(const ByteVector &data) + { + const ByteVector cset = checkedRawChunk(data, "CSET"); + CPPUNIT_ASSERT_EQUAL(8u, cset.size()); + CPPUNIT_ASSERT_EQUAL(65001u, + static_cast( + static_cast(cset.toShort(0, false)))); + CPPUNIT_ASSERT_EQUAL(ByteVector(6, '\0'), cset.mid(2, 6)); + } + + // Tags no single byte charset could have represented, as the strings and as + // the UTF-8 bytes they have to end up as on disk. + const String kNewTitle(L"Title \x4F60\x597D \x20AC"); + const ByteVector kNewTitleUtf8("Title \xE4\xBD\xA0\xE5\xA5\xBD \xE2\x82\xAC", 16); + const String kNewComment(L"\x4F60\x597D"); + const ByteVector kNewCommentUtf8("\xE4\xBD\xA0\xE5\xA5\xBD", 6); +} +#endif // TAGLIB_WITH_CHARSETTABLES + class TestWAV : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(TestWAV); @@ -69,6 +319,30 @@ class TestWAV : public CppUnit::TestFixture CPPUNIT_TEST(testBEXTTagWithOtherTags); CPPUNIT_TEST(testiXMLTag); CPPUNIT_TEST(testiXMLTagWithOtherTags); +#ifdef TAGLIB_WITH_CHARSETTABLES + CPPUNIT_TEST(testInfoLatin1Default); + CPPUNIT_TEST(testInfoWindows1252); + CPPUNIT_TEST(testInfoUtf8); + CPPUNIT_TEST(testInfoLatin1SaveAsUtf8); + CPPUNIT_TEST(testInfoWindows1252SaveAsUtf8); + CPPUNIT_TEST(testInfoUtf8SaveAsUtf8); + CPPUNIT_TEST(testInfoSaveWithoutFieldsWritesNoCset); + CPPUNIT_TEST(testInfoSaveWithoutModificationPreservesTag); + CPPUNIT_TEST(testInfoStripRemovesCset); + CPPUNIT_TEST(testInfoTruncatedCsetIsFormatError); + CPPUNIT_TEST(testInfoCsetWithLanguageFields); + CPPUNIT_TEST(testInfoCodePageOnlyCset); + CPPUNIT_TEST(testInfoUnsupportedCsetKeepsFields); + CPPUNIT_TEST(testCharsetDecodeStopsAtNul); + CPPUNIT_TEST(testCharsetEncodeIsUtf8Only); + CPPUNIT_TEST(testInfoUserHandlerBeatsCodePage); + CPPUNIT_TEST(testInfoCodePageReturnsWithoutUserHandler); + CPPUNIT_TEST(testInfoAssumedCodePageDefaultIsLatin1); + CPPUNIT_TEST(testInfoAssumedCodePageAcceptsUtf8); + CPPUNIT_TEST(testInfoAssumedCodePageWindows1252); + CPPUNIT_TEST(testInfoAssumedCodePageOnlyWhenUndeclared); + CPPUNIT_TEST(testInfoAssumedCodePageRejectsUnsupported); +#endif CPPUNIT_TEST_SUITE_END(); public: @@ -236,6 +510,565 @@ public: } } +#ifdef TAGLIB_WITH_CHARSETTABLES + void testInfoLatin1Default() + { + ByteVectorStream stream(latin1Riff()); + RIFF::WAV::File f(&stream); + CPPUNIT_ASSERT(f.isValid()); + CPPUNIT_ASSERT(f.hasInfoTag()); + CPPUNIT_ASSERT_EQUAL(String(L"A \xAE B"), f.InfoTag()->album()); + CPPUNIT_ASSERT_EQUAL(String(L"A \xAE B"), f.InfoTag()->artist()); + CPPUNIT_ASSERT_EQUAL(String(L"Caf\xE9"), f.InfoTag()->comment()); + CPPUNIT_ASSERT_EQUAL(static_cast(2024), f.InfoTag()->year()); + } + + void testInfoWindows1252() + { + ByteVectorStream stream(windows1252Riff()); + RIFF::WAV::File f(&stream); + CPPUNIT_ASSERT(f.isValid()); + CPPUNIT_ASSERT(f.hasInfoTag()); + CPPUNIT_ASSERT_EQUAL(String(L"Caf\xE9 \x20AC"), f.InfoTag()->album()); + CPPUNIT_ASSERT_EQUAL(String(L"A \x20AC B"), f.InfoTag()->artist()); + CPPUNIT_ASSERT_EQUAL(String(L"\x201Cquoted\x201D"), f.InfoTag()->comment()); + } + + void testInfoUtf8() + { + ByteVectorStream stream(utf8Riff()); + RIFF::WAV::File f(&stream); + CPPUNIT_ASSERT(f.isValid()); + CPPUNIT_ASSERT(f.hasInfoTag()); + CPPUNIT_ASSERT_EQUAL(String(L"Caf\xE9"), f.InfoTag()->album()); + CPPUNIT_ASSERT_EQUAL(static_cast(2024), f.InfoTag()->year()); + } + + // Each of the three files is put through the same two steps. First it must + // parse, with the charset its own CSET chunk declares. Then its tags are + // changed and written back, which rewrites the INFO chunk as UTF-8 and + // declares that in a CSET chunk, and the result must be read back correctly. + + void testInfoLatin1SaveAsUtf8() + { + // Step 1: the file has no CSET chunk, so it is Latin-1 by default. + { + ByteVectorStream stream(latin1Riff()); + RIFF::WAV::File f(&stream); + CPPUNIT_ASSERT(f.isValid()); + CPPUNIT_ASSERT(f.hasInfoTag()); + CPPUNIT_ASSERT_EQUAL(String(L"A \xAE B"), f.InfoTag()->album()); + CPPUNIT_ASSERT_EQUAL(String(L"A \xAE B"), f.InfoTag()->artist()); + CPPUNIT_ASSERT_EQUAL(String(L"Caf\xE9"), f.InfoTag()->comment()); + CPPUNIT_ASSERT_EQUAL(2024u, f.InfoTag()->year()); + } + + // Step 2: change the tags and write it out. + ByteVectorStream stream(latin1Riff()); + { + RIFF::WAV::File f(&stream); + CPPUNIT_ASSERT(f.isValid()); + + f.InfoTag()->setTitle(kNewTitle); + f.InfoTag()->setComment(kNewComment); + CPPUNIT_ASSERT(f.save()); + CPPUNIT_ASSERT(f.hasInfoTag()); + } + const ByteVector saved = *stream.data(); + + // What was written must be a well formed RIFF file that declares UTF-8. + assertRawCsetDeclaresUtf8(saved); + const ByteVector list = checkedRawChunk(saved, "LIST"); + CPPUNIT_ASSERT(list.startsWith("INFO")); + CPPUNIT_ASSERT(list.find(kNewTitleUtf8) >= 0); + CPPUNIT_ASSERT(list.find(kNewCommentUtf8) >= 0); + + // Reading it back must give the changed tags, plus the fields that were left + // alone unchanged by the Latin-1 to UTF-8 rewrite. + { + ByteVectorStream savedStream(saved); + RIFF::WAV::File f(&savedStream); + CPPUNIT_ASSERT(f.isValid()); + CPPUNIT_ASSERT(f.hasInfoTag()); + CPPUNIT_ASSERT_EQUAL(kNewTitle, f.InfoTag()->title()); + CPPUNIT_ASSERT_EQUAL(kNewComment, f.InfoTag()->comment()); + CPPUNIT_ASSERT_EQUAL(String(L"A \xAE B"), f.InfoTag()->album()); + CPPUNIT_ASSERT_EQUAL(String(L"A \xAE B"), f.InfoTag()->artist()); + CPPUNIT_ASSERT_EQUAL(2024u, f.InfoTag()->year()); + } + } + + void testInfoWindows1252SaveAsUtf8() + { + // Step 1: the file declares code page 1252 in its CSET chunk. + { + ByteVectorStream stream(windows1252Riff()); + RIFF::WAV::File f(&stream); + CPPUNIT_ASSERT(f.isValid()); + CPPUNIT_ASSERT(f.hasInfoTag()); + CPPUNIT_ASSERT_EQUAL(String(L"Caf\xE9 \x20AC"), f.InfoTag()->album()); + } + + // Step 2: change the tags and write it out. + ByteVectorStream stream(windows1252Riff()); + { + RIFF::WAV::File f(&stream); + CPPUNIT_ASSERT(f.isValid()); + + f.InfoTag()->setTitle(kNewTitle); + f.InfoTag()->setComment(kNewComment); + CPPUNIT_ASSERT(f.save()); + } + const ByteVector saved = *stream.data(); + + // It used to declare code page 1252 and now declares UTF-8. + assertRawCsetDeclaresUtf8(saved); + const ByteVector list = checkedRawChunk(saved, "LIST"); + CPPUNIT_ASSERT(list.find(kNewTitleUtf8) >= 0); + CPPUNIT_ASSERT(list.find(kNewCommentUtf8) >= 0); + + { + ByteVectorStream savedStream(saved); + RIFF::WAV::File f(&savedStream); + CPPUNIT_ASSERT(f.isValid()); + CPPUNIT_ASSERT(f.hasInfoTag()); + CPPUNIT_ASSERT_EQUAL(kNewTitle, f.InfoTag()->title()); + CPPUNIT_ASSERT_EQUAL(kNewComment, f.InfoTag()->comment()); + // The album was never touched and survived the rewrite unchanged. + CPPUNIT_ASSERT_EQUAL(String(L"Caf\xE9 \x20AC"), f.InfoTag()->album()); + } + } + + void testInfoUtf8SaveAsUtf8() + { + // Step 1: the file already declares UTF-8, and its CSET chunk comes + // after the LIST chunk it applies to. + { + ByteVectorStream stream(utf8Riff()); + RIFF::WAV::File f(&stream); + CPPUNIT_ASSERT(f.isValid()); + CPPUNIT_ASSERT(f.hasInfoTag()); + CPPUNIT_ASSERT_EQUAL(String(L"Caf\xE9"), f.InfoTag()->album()); + CPPUNIT_ASSERT_EQUAL(2024u, f.InfoTag()->year()); + } + + // Step 2: change the tags and write it out. + ByteVectorStream stream(utf8Riff()); + { + RIFF::WAV::File f(&stream); + CPPUNIT_ASSERT(f.isValid()); + + f.InfoTag()->setTitle(kNewTitle); + f.InfoTag()->setComment(kNewComment); + CPPUNIT_ASSERT(f.save()); + } + const ByteVector saved = *stream.data(); + + assertRawCsetDeclaresUtf8(saved); + const ByteVector list = checkedRawChunk(saved, "LIST"); + CPPUNIT_ASSERT(list.find(kNewTitleUtf8) >= 0); + CPPUNIT_ASSERT(list.find(kNewCommentUtf8) >= 0); + + { + ByteVectorStream savedStream(saved); + RIFF::WAV::File f(&savedStream); + CPPUNIT_ASSERT(f.isValid()); + CPPUNIT_ASSERT(f.hasInfoTag()); + CPPUNIT_ASSERT_EQUAL(kNewTitle, f.InfoTag()->title()); + CPPUNIT_ASSERT_EQUAL(kNewComment, f.InfoTag()->comment()); + CPPUNIT_ASSERT_EQUAL(String(L"Caf\xE9"), f.InfoTag()->album()); + CPPUNIT_ASSERT_EQUAL(2024u, f.InfoTag()->year()); + } + } + + void testInfoSaveWithoutFieldsWritesNoCset() + { + ByteVectorStream stream(latin1Riff()); + { + RIFF::WAV::File f(&stream); + CPPUNIT_ASSERT(f.isValid()); + CPPUNIT_ASSERT(f.hasInfoTag()); + + for(const auto &[id, value] : f.InfoTag()->fieldListMap()) + f.InfoTag()->removeField(id); + + CPPUNIT_ASSERT(f.save()); + // Nothing is left to describe, so no CSET chunk is written either. + CPPUNIT_ASSERT(!f.hasInfoTag()); + } + + const ByteVector saved = *stream.data(); + CPPUNIT_ASSERT(checkedRawChunk(saved, "CSET").isEmpty()); + CPPUNIT_ASSERT(checkedRawChunk(saved, "LIST").isEmpty()); + } + + // Saving a file that already has an INFO tag must keep that tag. It comes + // back in UTF-8 with a CSET chunk saying so, whatever it was read as. + void testInfoSaveWithoutModificationPreservesTag() + { + struct { + ByteVector file; + const wchar_t *album; + } cases[] = { + {latin1Riff(), L"A \xAE B"}, + {windows1251Riff(), L"Caf\x0439"}, + {windows1252Riff(), L"Caf\xE9 \x20AC"}, + {utf8Riff(), L"Caf\xE9"} + }; + + for(const auto &c : cases) { + // Save without touching a single field. + ByteVectorStream stream(c.file); + { + RIFF::WAV::File f(&stream); + CPPUNIT_ASSERT(f.isValid()); + CPPUNIT_ASSERT(f.hasInfoTag()); + CPPUNIT_ASSERT(f.save()); + } + + // The tag survived, and the file now declares UTF-8 for it. + const ByteVector saved = *stream.data(); + assertRawCsetDeclaresUtf8(saved); + CPPUNIT_ASSERT(checkedRawChunk(saved, "LIST").startsWith("INFO")); + + ByteVectorStream savedStream(saved); + RIFF::WAV::File f(&savedStream); + CPPUNIT_ASSERT(f.isValid()); + CPPUNIT_ASSERT(f.hasInfoTag()); + CPPUNIT_ASSERT_EQUAL(String(c.album), f.InfoTag()->album()); + } + } + + // Stripping the INFO tag takes the CSET chunk with it. A CSET that outlived + // its LIST would describe a charset for text the file no longer has. + void testInfoStripRemovesCset() + { + const ByteVector files[] = {latin1Riff(), utf8Riff(), windows1251Riff(), windows1252Riff()}; + + for(const ByteVector &file : files) { + ByteVectorStream stream(file); + { + RIFF::WAV::File f(&stream); + CPPUNIT_ASSERT(f.isValid()); + CPPUNIT_ASSERT(f.hasInfoTag()); + + f.strip(RIFF::WAV::File::Info); + CPPUNIT_ASSERT(f.InfoTag()->isEmpty()); + + CPPUNIT_ASSERT(f.save(RIFF::WAV::File::Info)); + CPPUNIT_ASSERT(!f.hasInfoTag()); + } + + const ByteVector saved = *stream.data(); + CPPUNIT_ASSERT(checkedRawChunk(saved, "LIST").isEmpty()); + CPPUNIT_ASSERT(checkedRawChunk(saved, "CSET").isEmpty()); + } + } + + // A CSET chunk too short to hold its own wCodePage is a file format error. + // Two bytes are the whole of wCodePage, so there is nothing shorter to read, + // and the chunk is ignored instead of guessed at: the INFO text keeps the RIFF + // default of Latin-1, under which 0x80 is U+0080 and not the Euro sign that the + // page the chunk tried to declare would have made of it. + void testInfoTruncatedCsetIsFormatError() + { + // Step 1: the chunk is ignored, and the text it failed to declare the + // charset of decodes as the Latin-1 default. + { + ByteVectorStream stream(truncatedCsetRiff()); + RIFF::WAV::File f(&stream); + CPPUNIT_ASSERT(f.isValid()); + CPPUNIT_ASSERT(f.hasInfoTag()); + CPPUNIT_ASSERT_EQUAL(String(L"A \x80 B"), f.InfoTag()->artist()); + } + + // Step 2: the malformed chunk never declared a charset, so saving replaces + // it with the one valid CSET chunk TagLib writes, instead of keeping it. + ByteVectorStream stream(truncatedCsetRiff()); + { + RIFF::WAV::File f(&stream); + CPPUNIT_ASSERT(f.isValid()); + CPPUNIT_ASSERT(f.save()); + } + assertRawCsetDeclaresUtf8(*stream.data()); + } + + // The code page of a CSET chunk is wCodePage, one WORD of the four the layout + // has. The country, language and dialect behind it are not part of that number: + // a file that fills them in declares the page its first WORD holds, and has to + // be read with that page. + void testInfoCsetWithLanguageFields() + { + { + ByteVectorStream stream(windows1252WithLanguageRiff()); + RIFF::WAV::File f(&stream); + CPPUNIT_ASSERT(f.isValid()); + CPPUNIT_ASSERT(f.hasInfoTag()); + CPPUNIT_ASSERT_EQUAL(String(L"A \x20AC B"), f.InfoTag()->artist()); + CPPUNIT_ASSERT_EQUAL(String(L"Caf\xE9 \x20AC"), f.InfoTag()->album()); + CPPUNIT_ASSERT_EQUAL(String(L"\x201Cquoted\x201D"), f.InfoTag()->comment()); + } + + // Saving rewrites the chunk the way TagLib writes it: the code page, and the + // three fields behind it back at their zero defaults. + ByteVectorStream stream(windows1252WithLanguageRiff()); + { + RIFF::WAV::File f(&stream); + CPPUNIT_ASSERT(f.isValid()); + CPPUNIT_ASSERT(f.save()); + } + assertRawCsetDeclaresUtf8(*stream.data()); + } + + // wCodePage on its own is all the chunk has to say. A file that leaves the + // three fields behind it out entirely still declares a charset. + void testInfoCodePageOnlyCset() + { + ByteVectorStream stream(codePageOnlyCsetRiff()); + RIFF::WAV::File f(&stream); + CPPUNIT_ASSERT(f.isValid()); + CPPUNIT_ASSERT(f.hasInfoTag()); + CPPUNIT_ASSERT_EQUAL(String(L"A \x20AC B"), f.InfoTag()->artist()); + } + + // A code page this library has no table for is a hint it cannot follow, not a + // reason to throw the tag away. The text keeps the RIFF default of ISO 8859-1, + // so every field is still read, and a save still writes it back. + void testInfoUnsupportedCsetKeepsFields() + { + { + ByteVectorStream stream(unsupportedCsetRiff()); + RIFF::WAV::File f(&stream); + CPPUNIT_ASSERT(f.isValid()); + CPPUNIT_ASSERT(f.hasInfoTag()); + CPPUNIT_ASSERT_EQUAL(String(L"A \xAE B"), f.InfoTag()->artist()); + CPPUNIT_ASSERT_EQUAL(String(L"Caf\xE9"), f.InfoTag()->comment()); + } + + ByteVectorStream stream(unsupportedCsetRiff()); + { + RIFF::WAV::File f(&stream); + CPPUNIT_ASSERT(f.isValid()); + f.InfoTag()->setTitle(kNewTitle); + CPPUNIT_ASSERT(f.save()); + } + + // The field was read as ISO 8859-1 and is written back as UTF-8, so the + // character it held is in the saved LIST as its two UTF-8 bytes. + const ByteVector saved = *stream.data(); + assertRawCsetDeclaresUtf8(saved); + CPPUNIT_ASSERT(checkedRawChunk(saved, "LIST").find(ByteVector("A \xC2\xAE B", 6)) >= 0); + + ByteVectorStream savedStream(saved); + RIFF::WAV::File f(&savedStream); + CPPUNIT_ASSERT(f.isValid()); + CPPUNIT_ASSERT_EQUAL(kNewTitle, f.InfoTag()->title()); + CPPUNIT_ASSERT_EQUAL(String(L"A \xAE B"), f.InfoTag()->artist()); + CPPUNIT_ASSERT_EQUAL(String(L"Caf\xE9"), f.InfoTag()->comment()); + } + + // Byte 0x00 is the one byte of a code page's repertoire that cannot be part + // of a field's text, because decoding stops at the first NUL: that is how a + // string ends. It has to end the string and not turn into a character. + void testCharsetDecodeStopsAtNul() + { + const ByteVector data("\x41\x00\x42", 3); + CPPUNIT_ASSERT_EQUAL(String(L"A"), Charset::decode(data, Charset::Type::Windows1251)); + CPPUNIT_ASSERT_EQUAL(String(), Charset::decode(ByteVector("\x00\x41", 2), Charset::Type::Windows1251)); + } + + // A string is decoded into TagLib's internal UTF-16 representation, which is + // not a byte encoding at all, so there is no way to encode back into a legacy + // code page. That is precisely what keeps text a code page cannot hold from + // being quietly replaced. + void testCharsetEncodeIsUtf8Only() + { + CPPUNIT_ASSERT_EQUAL(Charset::Type::UTF8, Charset::typeForCodePage(65001).value()); + + // Encoding is UTF-8 and nothing else, so text no single byte code page could + // ever represent survives instead of turning into '?'. + CPPUNIT_ASSERT_EQUAL(ByteVector("\xE4\xBD\xA0", 3), Charset::encode(String(L"\x4F60"), Charset::Type::UTF8)); + CPPUNIT_ASSERT_EQUAL(kNewTitleUtf8, Charset::encode(kNewTitle, Charset::Type::UTF8)); + + // And it is a plain UTF-8 round trip, so nothing is lost on the way through. + const String sample(L"\x0416\x0490\x05D0\x0625\x0E01\x20A7"); + CPPUNIT_ASSERT_EQUAL(sample, Charset::decode(Charset::encode(sample, Charset::Type::UTF8), Charset::Type::UTF8)); + } + + // A handler the user installed is an explicit override, so it outranks the + // code page the file declares. Otherwise setStringHandler() would do nothing + // at all for every file TagLib itself writes, since those all carry a CSET. + class FixedStringHandler : public RIFF::Info::StringHandler + { + public: + String parse(const ByteVector &) const override { return String(L"from the handler"); } + ByteVector render(const String &) const override { return ByteVector("from the handler"); } + }; + + void testInfoUserHandlerBeatsCodePage() + { + class Restore + { + public: + ~Restore() { RIFF::Info::Tag::setStringHandler(nullptr); } + } restore; + + // The file declares 1252, and the default path reads it with that table. + { + ByteVectorStream stream(windows1252Riff()); + RIFF::WAV::File f(&stream); + CPPUNIT_ASSERT(f.isValid()); + CPPUNIT_ASSERT(f.hasInfoTag()); + CPPUNIT_ASSERT(f.InfoTag()->artist() != String(L"from the handler")); + } + + const FixedStringHandler handler; + RIFF::Info::Tag::setStringHandler(&handler); + + // With a handler installed it is used even though the file declares 1252, + // on the way in ... + { + ByteVectorStream stream(windows1252Riff()); + RIFF::WAV::File f(&stream); + CPPUNIT_ASSERT(f.isValid()); + CPPUNIT_ASSERT(f.hasInfoTag()); + CPPUNIT_ASSERT_EQUAL(String(L"from the handler"), f.InfoTag()->artist()); + } + + // ... and on the way out, so the handler decides the bytes as well. + ByteVectorStream stream(windows1252Riff()); + { + RIFF::WAV::File f(&stream); + CPPUNIT_ASSERT(f.isValid()); + CPPUNIT_ASSERT(f.hasInfoTag()); + CPPUNIT_ASSERT_EQUAL(String(L"from the handler"), f.InfoTag()->artist()); + CPPUNIT_ASSERT(f.save()); + } + CPPUNIT_ASSERT(checkedRawChunk(*stream.data(), "LIST") + .find(ByteVector("from the handler")) >= 0); + } + + // Removing the handler again must put the file's own code page back in charge. + void testInfoCodePageReturnsWithoutUserHandler() + { + ByteVectorStream stream(windows1251Riff()); + RIFF::WAV::File f(&stream); + CPPUNIT_ASSERT(f.isValid()); + CPPUNIT_ASSERT(f.hasInfoTag()); + CPPUNIT_ASSERT(f.InfoTag()->artist() != String(L"from the handler")); + CPPUNIT_ASSERT_EQUAL(String(L"A \x0410 B"), f.InfoTag()->artist()); + } + + // The specification makes ISO 8859-1 the charset of a file that declares + // none, and that is what an untouched library assumes: the UTF-8 bytes of a + // file that never said what they are read as the Latin-1 characters they + // also happen to be. + void testInfoAssumedCodePageDefaultIsLatin1() + { + CPPUNIT_ASSERT_EQUAL(0u, RIFF::Info::Tag::assumedCodePage()); + + ByteVectorStream stream(utf8WithoutCsetRiff()); + RIFF::WAV::File f(&stream); + CPPUNIT_ASSERT(f.isValid()); + CPPUNIT_ASSERT(f.hasInfoTag()); + CPPUNIT_ASSERT_EQUAL(String(L"Caf\xC3\xA9"), f.InfoTag()->artist()); + } + + // Software that writes UTF-8 without declaring it is what the option is for. + // Stated once, it is what every file that declares nothing is read with. + void testInfoAssumedCodePageAcceptsUtf8() + { + const RestoreAssumedCodePage restore; + + RIFF::Info::Tag::setAssumedCodePage(65001); + CPPUNIT_ASSERT_EQUAL(65001u, RIFF::Info::Tag::assumedCodePage()); + + { + ByteVectorStream stream(utf8WithoutCsetRiff()); + RIFF::WAV::File f(&stream); + CPPUNIT_ASSERT(f.isValid()); + CPPUNIT_ASSERT_EQUAL(String(L"Caf\xE9"), f.InfoTag()->artist()); + } + + // An INFO list read without its file, as in Tag(const ByteVector &data), + // declares nothing either, so it is read the same way. + const ByteVector list = ByteVector("INFO", 4) + + riffChunk("IART", ByteVector("Caf\xC3\xA9", 5) + ByteVector(1, '\0')); + RIFF::Info::Tag tag(list); + CPPUNIT_ASSERT_EQUAL(String(L"Caf\xE9"), tag.artist()); + + // Stating 0 again is going back to what the specification says. + RIFF::Info::Tag::setAssumedCodePage(0); + CPPUNIT_ASSERT_EQUAL(0u, RIFF::Info::Tag::assumedCodePage()); + ByteVectorStream stream(utf8WithoutCsetRiff()); + RIFF::WAV::File f(&stream); + CPPUNIT_ASSERT_EQUAL(String(L"Caf\xC3\xA9"), f.InfoTag()->artist()); + } + + // The other real world answer is an ANSI code page: such a file keeps its + // typographic characters in 0x80-0x9f, where ISO 8859-1 has control + // characters that no amount of faithful decoding turns into text. + void testInfoAssumedCodePageWindows1252() + { + const RestoreAssumedCodePage restore; + + RIFF::Info::Tag::setAssumedCodePage(1252); + CPPUNIT_ASSERT_EQUAL(1252u, RIFF::Info::Tag::assumedCodePage()); + + ByteVectorStream stream(riffWithInfo({{"IART", ByteVector("A \x80 B", 5)}})); + RIFF::WAV::File f(&stream); + CPPUNIT_ASSERT(f.isValid()); + CPPUNIT_ASSERT_EQUAL(String(L"A \x20AC B"), f.InfoTag()->artist()); + } + + // The option is for files that declare nothing. A file declaring a code page + // keeps it, whether or not this library has a table for it, and a CSET whose + // wCodePage is zero is the specification's way of declaring nothing, so that + // is where the assumption applies. + void testInfoAssumedCodePageOnlyWhenUndeclared() + { + const RestoreAssumedCodePage restore; + + RIFF::Info::Tag::setAssumedCodePage(65001); + + { + ByteVectorStream stream(windows1252Riff()); + RIFF::WAV::File f(&stream); + CPPUNIT_ASSERT(f.isValid()); + CPPUNIT_ASSERT_EQUAL(String(L"Caf\xE9 \x20AC"), f.InfoTag()->album()); + CPPUNIT_ASSERT_EQUAL(String(L"A \x20AC B"), f.InfoTag()->artist()); + } + + { + ByteVectorStream stream(unsupportedCsetRiff()); + RIFF::WAV::File f(&stream); + CPPUNIT_ASSERT(f.isValid()); + CPPUNIT_ASSERT_EQUAL(String(L"A \xAE B"), f.InfoTag()->artist()); + } + + { + ByteVectorStream stream(riffWithInfoCset( + {{"IART", ByteVector("Caf\xC3\xA9", 5)}}, ByteVector(8, '\0'))); + RIFF::WAV::File f(&stream); + CPPUNIT_ASSERT(f.isValid()); + CPPUNIT_ASSERT_EQUAL(String(L"Caf\xE9"), f.InfoTag()->artist()); + } + } + + // A code page this library cannot decode would quietly do nothing if it were + // accepted as the assumption, so it is refused and the previous one stays. + void testInfoAssumedCodePageRejectsUnsupported() + { + const RestoreAssumedCodePage restore; + + RIFF::Info::Tag::setAssumedCodePage(1252); + CPPUNIT_ASSERT_EQUAL(1252u, RIFF::Info::Tag::assumedCodePage()); + + RIFF::Info::Tag::setAssumedCodePage(932); + CPPUNIT_ASSERT_EQUAL(1252u, RIFF::Info::Tag::assumedCodePage()); + } +#endif // TAGLIB_WITH_CHARSETTABLES + void testStripTags() { ScopedFileCopy copy("empty", ".wav"); @@ -291,7 +1124,9 @@ public: CPPUNIT_ASSERT_EQUAL(String("Title1"), f.InfoTag()->title()); f.save(); - CPPUNIT_ASSERT_EQUAL(static_cast(15898), f.length()); + // Saving the INFO tag also writes the 16 byte CSET chunk (8 byte header + // plus the 8 byte payload) which declares that the tag is UTF-8. + CPPUNIT_ASSERT_EQUAL(static_cast(15914), f.length()); CPPUNIT_ASSERT_EQUAL(static_cast(-1), f.find("Title2")); }