/* * openmpt123.hpp * -------------- * Purpose: libopenmpt command line player * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #ifndef OPENMPT123_HPP #define OPENMPT123_HPP #include "openmpt123_config.hpp" namespace openmpt123 { struct exception : public openmpt::exception { exception( const std::string & text ) : openmpt::exception(text) { } }; struct show_help_exception { std::string message; bool longhelp; show_help_exception( const std::string & msg = "", bool longhelp_ = true ) : message(msg), longhelp(longhelp_) { } }; struct args_error_exception { args_error_exception() { } }; struct show_help_keyboard_exception { }; #if defined(WIN32) bool IsConsole( DWORD stdHandle ); #endif bool IsTerminal( int fd ); #if defined(WIN32) static inline std::string wstring_to_utf8( const std::wstring & unicode_string ) { std::string utf8_string; int source_length = ( unicode_string.size() < static_cast( std::numeric_limits::max() ) ) ? static_cast( unicode_string.size() ) : std::numeric_limits::max(); int required_size = WideCharToMultiByte( CP_UTF8, 0, unicode_string.data(), source_length, NULL, 0, NULL, NULL ); if ( required_size > 0 ) { utf8_string.resize( required_size ); WideCharToMultiByte( CP_UTF8, 0, unicode_string.data(), source_length, utf8_string.data(), required_size, NULL, NULL ); } return utf8_string; } static inline std::wstring utf8_to_wstring( const std::string & utf8_string ) { std::wstring unicode_string; int source_length = ( utf8_string.size() < static_cast( std::numeric_limits::max() ) ) ? static_cast( utf8_string.size() ) : std::numeric_limits::max(); int required_size = MultiByteToWideChar( CP_UTF8, 0, utf8_string.data(), source_length, NULL, 0 ); if ( required_size > 0 ) { unicode_string.resize( required_size ); MultiByteToWideChar( CP_UTF8, 0, utf8_string.data(), source_length, unicode_string.data(), required_size ); } return unicode_string; } static inline std::string wstring_to_locale( const std::wstring & unicode_string ) { std::string locale_string; int source_length = ( unicode_string.size() < static_cast( std::numeric_limits::max() ) ) ? static_cast( unicode_string.size() ) : std::numeric_limits::max(); int required_size = WideCharToMultiByte( CP_ACP, 0, unicode_string.data(), source_length, NULL, 0, NULL, NULL ); if ( required_size > 0 ) { locale_string.resize( required_size ); WideCharToMultiByte( CP_ACP, 0, unicode_string.data(), source_length, locale_string.data(), required_size, NULL, NULL ); } return locale_string; } static inline std::wstring locale_to_wstring( const std::string & locale_string ) { std::wstring unicode_string; int source_length = ( locale_string.size() < static_cast( std::numeric_limits::max() ) ) ? static_cast( locale_string.size() ) : std::numeric_limits::max(); int required_size = MultiByteToWideChar( CP_ACP, 0, locale_string.data(), source_length, NULL, 0 ); if ( required_size > 0 ) { unicode_string.resize( required_size ); MultiByteToWideChar( CP_ACP, 0, locale_string.data(), source_length, unicode_string.data(), required_size ); } return unicode_string; } #endif /* default 1:1 mapping static constexpr char32_t CharsetTableISO8859_1[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 }; */ static constexpr char32_t CharsetTableISO8859_15[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 }; static constexpr char32_t CharsetTableWindows1252[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 }; static constexpr char32_t CharsetTableCP437[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,0x2302, 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 }; static constexpr char32_t CharsetTableCP850[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,0x2302, 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 }; #if defined(__DJGPP__) using widechar = char32_t; using widestring = std::u32string; static constexpr widechar wide_default_replacement = 0xFFFD; #else // !__DJGPP__ using widechar = wchar_t; using widestring = std::wstring; static constexpr widechar wide_default_replacement = L'\uFFFD'; #endif // !__DJGPP__ static inline widestring From8bit(const std::string &str, const char32_t (&table)[256], widechar replacement = wide_default_replacement) { widestring res; res.reserve(str.length()); for(std::size_t i = 0; i < str.length(); ++i) { std::size_t c = static_cast(static_cast(str[i])); if(c < std::size(table)) { res.push_back(static_cast(table[c])); } else { res.push_back(replacement); } } return res; } static inline std::string To8bit(const widestring &str, const char32_t (&table)[256], char replacement = '?') { std::string res; res.reserve(str.length()); for(std::size_t i = 0; i < str.length(); ++i) { char32_t c = static_cast(str[i]); bool found = false; // Try non-control characters first. // In cases where there are actual characters mirrored in this range (like in AMS/AMS2 character sets), // characters in the common range are preferred this way. for(std::size_t x = 0x20; x < std::size(table); ++x) { if(c == table[x]) { res.push_back(static_cast(static_cast(x))); found = true; break; } } if(!found) { // try control characters for(std::size_t x = 0x00; x < std::size(table) && x < 0x20; ++x) { if(c == table[x]) { res.push_back(static_cast(static_cast(x))); found = true; break; } } } if(!found) { res.push_back(replacement); } } return res; } static inline widestring FromAscii(const std::string &str, widechar replacement = wide_default_replacement) { widestring res; res.reserve(str.length()); for(std::size_t i = 0; i < str.length(); ++i) { std::uint8_t c = str[i]; if(c <= 0x7f) { res.push_back(static_cast(static_cast(c))); } else { res.push_back(replacement); } } return res; } static inline std::string ToAscii(const widestring &str, char replacement = '?') { std::string res; res.reserve(str.length()); for(std::size_t i = 0; i < str.length(); ++i) { char32_t c = static_cast(str[i]); if(c <= 0x7f) { res.push_back(static_cast(static_cast(c))); } else { res.push_back(replacement); } } return res; } static inline widestring FromISO_8859_1(const std::string &str, widechar replacement = wide_default_replacement) { static_cast( replacement ); widestring res; res.reserve(str.length()); for(std::size_t i = 0; i < str.length(); ++i) { std::uint8_t c = str[i]; res.push_back(static_cast(static_cast(c))); } return res; } static inline std::string ToISO_8859_1(const widestring &str, char replacement = '?') { std::string res; res.reserve(str.length()); for(std::size_t i = 0; i < str.length(); ++i) { char32_t c = static_cast(str[i]); if(c <= 0xff) { res.push_back(static_cast(static_cast(c))); } else { res.push_back(replacement); } } return res; } #if !defined(__DJGPP__) && !defined(__EMSCRIPTEN__) // Note: // // std::codecvt::out in LLVM libc++ does not advance in and out pointers when // running into a non-convertible character. This can happen when no locale is // set on FreeBSD or MacOSX. This behaviour violates the C++ standard. // // We apply the following (albeit costly, even on other platforms) work-around: // If the conversion errors out and does not advance the pointers at all, we // retry the conversion with a space character prepended to the string. If it // still does error out, we retry the whole conversion character by character. // This is costly even on other platforms in one single case: The first // character is an invalid Unicode code point or otherwise not convertible. Any // following non-convertible characters are not a problem. static inline std::wstring LocaleDecode(const std::string &str, const std::locale & locale, wchar_t replacement = L'\uFFFD', int retry = 0, bool * progress = nullptr) { if(str.empty()) { return std::wstring(); } std::vector out; using codecvt_type = std::codecvt; std::mbstate_t state = std::mbstate_t(); const codecvt_type & facet = std::use_facet(locale); codecvt_type::result result = codecvt_type::partial; const char * in_begin = str.data(); const char * in_end = in_begin + str.size(); out.resize((in_end - in_begin) * (facet.max_length() + 1)); wchar_t * out_begin = &(out[0]); wchar_t * out_end = &(out[0]) + out.size(); const char * in_next = nullptr; wchar_t * out_next = nullptr; do { if(retry == 2) { for(;;) { in_next = nullptr; out_next = nullptr; result = facet.in(state, in_begin, in_begin + 1, in_next, out_begin, out_end, out_next); if(result == codecvt_type::partial && in_next == in_begin + 1) { in_begin = in_next; out_begin = out_next; continue; } else { break; } } } else { in_next = nullptr; out_next = nullptr; result = facet.in(state, in_begin, in_end, in_next, out_begin, out_end, out_next); } if(result == codecvt_type::partial || (result == codecvt_type::error && out_next == out_end)) { out.resize(out.size() * 2); in_begin = in_next; out_begin = &(out[0]) + (out_next - out_begin); out_end = &(out[0]) + out.size(); continue; } if(retry == 0) { if(result == codecvt_type::error && in_next == in_begin && out_next == out_begin) { bool made_progress = true; LocaleDecode(std::string(" ") + str, locale, replacement, 1, &made_progress); if(!made_progress) { return LocaleDecode(str, locale, replacement, 2); } } } else if(retry == 1) { if(result == codecvt_type::error && in_next == in_begin && out_next == out_begin) { *progress = false; } else { *progress = true; } return std::wstring(); } if(result == codecvt_type::error) { ++in_next; *out_next = replacement; ++out_next; } in_begin = in_next; out_begin = out_next; } while((result == codecvt_type::error && in_next < in_end && out_next < out_end) || (retry == 2 && in_next < in_end)); return std::wstring(&(out[0]), out_next); } static inline std::string LocaleEncode(const std::wstring &str, const std::locale & locale, char replacement = '?', int retry = 0, bool * progress = nullptr) { if(str.empty()) { return std::string(); } std::vector out; using codecvt_type = std::codecvt; std::mbstate_t state = std::mbstate_t(); const codecvt_type & facet = std::use_facet(locale); codecvt_type::result result = codecvt_type::partial; const wchar_t * in_begin = str.data(); const wchar_t * in_end = in_begin + str.size(); out.resize((in_end - in_begin) * (facet.max_length() + 1)); char * out_begin = &(out[0]); char * out_end = &(out[0]) + out.size(); const wchar_t * in_next = nullptr; char * out_next = nullptr; do { if(retry == 2) { for(;;) { in_next = nullptr; out_next = nullptr; result = facet.out(state, in_begin, in_begin + 1, in_next, out_begin, out_end, out_next); if(result == codecvt_type::partial && in_next == in_begin + 1) { in_begin = in_next; out_begin = out_next; continue; } else { break; } } } else { in_next = nullptr; out_next = nullptr; result = facet.out(state, in_begin, in_end, in_next, out_begin, out_end, out_next); } if(result == codecvt_type::partial || (result == codecvt_type::error && out_next == out_end)) { out.resize(out.size() * 2); in_begin = in_next; out_begin = &(out[0]) + (out_next - out_begin); out_end = &(out[0]) + out.size(); continue; } if(retry == 0) { if(result == codecvt_type::error && in_next == in_begin && out_next == out_begin) { bool made_progress = true; LocaleEncode(std::wstring(L" ") + str, locale, replacement, 1, &made_progress); if(!made_progress) { return LocaleEncode(str, locale, replacement, 2); } } } else if(retry == 1) { if(result == codecvt_type::error && in_next == in_begin && out_next == out_begin) { *progress = false; } else { *progress = true; } return std::string(); } if(result == codecvt_type::error) { ++in_next; *out_next = replacement; ++out_next; } in_begin = in_next; out_begin = out_next; } while((result == codecvt_type::error && in_next < in_end && out_next < out_end) || (retry == 2 && in_next < in_end)); return std::string(&(out[0]), out_next); } static inline std::wstring FromLocaleCpp(const std::string &str, wchar_t replacement) { try { std::locale locale(""); // user locale return LocaleDecode(str, locale, replacement); } catch ( const std::bad_alloc & ) { throw; } catch(...) { // nothing } try { std::locale locale; // current c++ locale return LocaleDecode(str, locale, replacement); } catch ( const std::bad_alloc & ) { throw; } catch(...) { // nothing } try { std::locale locale = std::locale::classic(); // "C" locale return LocaleDecode(str, locale, replacement); } catch ( const std::bad_alloc & ) { throw; } catch(...) { // nothing } assert(0); return FromAscii(str, replacement); // fallback } static inline std::string ToLocaleCpp(const std::wstring &str, char replacement) { try { std::locale locale(""); // user locale return LocaleEncode(str, locale, replacement); } catch ( const std::bad_alloc & ) { throw; } catch(...) { // nothing } try { std::locale locale; // current c++ locale return LocaleEncode(str, locale, replacement); } catch ( const std::bad_alloc & ) { throw; } catch(...) { // nothing } try { std::locale locale = std::locale::classic(); // "C" locale return LocaleEncode(str, locale, replacement); } catch ( const std::bad_alloc & ) { throw; } catch(...) { // nothing } assert(0); return ToAscii(str, replacement); // fallback } static inline std::wstring FromLocale(const std::string &str, wchar_t replacement = L'\uFFFD') { return FromLocaleCpp(str, replacement); } static inline std::string ToLocale(const std::wstring &str, char replacement = '?') { return ToLocaleCpp(str, replacement); } #endif // !__DJGPP__ && !__EMSCRIPTEN static inline widestring FromUTF8(const std::string &str, widechar replacement = wide_default_replacement) { const std::string &in = str; widestring out; // state: std::size_t charsleft = 0; char32_t ucs4 = 0; for ( std::uint8_t c : in ) { if ( charsleft == 0 ) { if ( ( c & 0x80 ) == 0x00 ) { out.push_back( (wchar_t)c ); } else if ( ( c & 0xE0 ) == 0xC0 ) { ucs4 = c & 0x1F; charsleft = 1; } else if ( ( c & 0xF0 ) == 0xE0 ) { ucs4 = c & 0x0F; charsleft = 2; } else if ( ( c & 0xF8 ) == 0xF0 ) { ucs4 = c & 0x07; charsleft = 3; } else { out.push_back( replacement ); ucs4 = 0; charsleft = 0; } } else { if ( ( c & 0xC0 ) != 0x80 ) { out.push_back( replacement ); ucs4 = 0; charsleft = 0; } ucs4 <<= 6; ucs4 |= c & 0x3F; charsleft--; if ( charsleft == 0 ) { if constexpr ( sizeof( widechar ) == 2 ) { if ( ucs4 > 0x1fffff ) { out.push_back( replacement ); ucs4 = 0; charsleft = 0; } if ( ucs4 <= 0xffff ) { out.push_back( static_cast(ucs4) ); } else { std::uint32_t surrogate = static_cast(ucs4) - 0x10000; std::uint16_t hi_sur = static_cast( ( 0x36 << 10 ) | ( (surrogate>>10) & ((1<<10)-1) ) ); std::uint16_t lo_sur = static_cast( ( 0x37 << 10 ) | ( (surrogate>> 0) & ((1<<10)-1) ) ); out.push_back( hi_sur ); out.push_back( lo_sur ); } } else { out.push_back( static_cast( ucs4 ) ); } ucs4 = 0; } } } if ( charsleft != 0 ) { out.push_back( replacement ); ucs4 = 0; charsleft = 0; } return out; } static inline std::string ToUTF8(const widestring &str, char replacement = '?') { const widestring &in = str; std::string out; for ( std::size_t i=0; i( wc ); if ( i + 1 < in.length() ) { // check for surrogate pair std::uint16_t hi_sur = in[i+0]; std::uint16_t lo_sur = in[i+1]; if ( hi_sur >> 10 == 0x36 && lo_sur >> 10 == 0x37 ) { // surrogate pair ++i; hi_sur &= (1<<10)-1; lo_sur &= (1<<10)-1; ucs4 = ( static_cast(hi_sur) << 10 ) | ( static_cast(lo_sur) << 0 ); } else { // no surrogate pair ucs4 = static_cast( c ); } } else { // no surrogate possible ucs4 = static_cast( c ); } } else { ucs4 = static_cast( wc ); } if ( ucs4 > 0x1fffff ) { out.push_back( replacement ); continue; } std::uint8_t utf8[6]; std::size_t numchars = 0; for ( numchars = 0; numchars < 6; numchars++ ) { utf8[numchars] = ucs4 & 0x3F; ucs4 >>= 6; if ( ucs4 == 0 ) { break; } } numchars++; if ( numchars == 1 ) { out.push_back( utf8[0] ); continue; } if ( numchars == 2 && utf8[numchars-1] == 0x01 ) { // generate shortest form out.push_back( utf8[0] | 0x40 ); continue; } std::size_t charsleft = numchars; while ( charsleft > 0 ) { if ( charsleft == numchars ) { out.push_back( utf8[ charsleft - 1 ] | ( ((1<( lines ); } }; class textout_dummy : public textout { public: textout_dummy() { return; } virtual ~textout_dummy() { return; } public: void writeout() override { static_cast( pop() ); } }; class textout_ostream : public textout { private: std::ostream & s; #if defined(__DJGPP__) std::uint16_t active_codepage; std::uint16_t system_codepage; #endif public: textout_ostream( std::ostream & s_ ) : s(s_) #if defined(__DJGPP__) , active_codepage(437) , system_codepage(437) #endif { #if defined(__DJGPP__) __dpmi_regs regs; std::memset( ®s, 0, sizeof( __dpmi_regs ) ); regs.x.ax = 0x6601; if ( __dpmi_int( 0x21, ®s ) == 0 ) { int cf = ( regs.x.flags >> 0 ) & 1; if ( cf == 0 ) { active_codepage = regs.x.bx; system_codepage = regs.x.dx; } } #endif return; } virtual ~textout_ostream() { writeout_impl(); } private: void writeout_impl() { std::string text = pop(); if ( text.length() > 0 ) { #if defined(__DJGPP__) if ( active_codepage == 0 ) { s << To8bit( FromUTF8( text ), CharsetTableCP437 ); } else if ( active_codepage == 437 ) { s << To8bit( FromUTF8( text ), CharsetTableCP437 ); } else if ( active_codepage == 850 ) { s << To8bit( FromUTF8( text ), CharsetTableCP850 ); } else if ( system_codepage == 437 ) { s << To8bit( FromUTF8( text ), CharsetTableCP437 ); } else if ( system_codepage == 850 ) { s << To8bit( FromUTF8( text ), CharsetTableCP850 ); } else { s << To8bit( FromUTF8( text ), CharsetTableCP437 ); } #elif defined(__EMSCRIPTEN__) s << text; #elif defined(WIN32) s << ToLocale( FromUTF8( text ) ); #else s << ToLocale( FromUTF8( text ) ); #endif s.flush(); } } public: void writeout() override { writeout_impl(); } void cursor_up( std::size_t lines ) override { s.flush(); for ( std::size_t line = 0; line < lines; ++line ) { *this << "\x1b[1A"; } } }; #if defined(WIN32) class textout_ostream_console : public textout { private: #if defined(UNICODE) std::wostream & s; #else std::ostream & s; #endif HANDLE handle; bool console; public: #if defined(UNICODE) textout_ostream_console( std::wostream & s_, DWORD stdHandle_ ) #else textout_ostream_console( std::ostream & s_, DWORD stdHandle_ ) #endif : s(s_) , handle(GetStdHandle( stdHandle_ )) , console(IsConsole( stdHandle_ )) { return; } virtual ~textout_ostream_console() { writeout_impl(); } private: void writeout_impl() { std::string text = pop(); if ( text.length() > 0 ) { if ( console ) { #if defined(UNICODE) std::wstring wtext = utf8_to_wstring( text ); WriteConsole( handle, wtext.data(), static_cast( wtext.size() ), NULL, NULL ); #else std::string ltext = wstring_to_locale( utf8_to_wstring( text ) ); WriteConsole( handle, ltext.data(), static_cast( ltext.size() ), NULL, NULL ); #endif } else { #if defined(UNICODE) s << utf8_to_wstring( text ); #else s << wstring_to_locale( utf8_to_wstring( text ) ); #endif s.flush(); } } } public: void writeout() override { writeout_impl(); } void cursor_up( std::size_t lines ) override { if ( console ) { s.flush(); CONSOLE_SCREEN_BUFFER_INFO csbi; ZeroMemory( &csbi, sizeof( CONSOLE_SCREEN_BUFFER_INFO ) ); COORD coord_cursor = COORD(); if ( GetConsoleScreenBufferInfo( handle, &csbi ) != FALSE ) { coord_cursor = csbi.dwCursorPosition; coord_cursor.X = 1; coord_cursor.Y -= static_cast( lines ); SetConsoleCursorPosition( handle, coord_cursor ); } } } }; #endif // WIN32 static inline float mpt_round( float val ) { if ( val >= 0.0f ) { return std::floor( val + 0.5f ); } else { return std::ceil( val - 0.5f ); } } static inline long mpt_lround( float val ) { return static_cast< long >( mpt_round( val ) ); } static inline std::string append_software_tag( std::string software ) { std::string openmpt123 = std::string() + "openmpt123 " + OPENMPT123_VERSION_STRING + " (libopenmpt " + openmpt::string::get( "library_version" ) + ", OpenMPT " + openmpt::string::get( "core_version" ) + ")"; if ( software.empty() ) { software = openmpt123; } else { software += " (via " + openmpt123 + ")"; } return software; } static inline std::string get_encoder_tag() { return std::string() + "openmpt123 " + OPENMPT123_VERSION_STRING + " (libopenmpt " + openmpt::string::get( "library_version" ) + ", OpenMPT " + openmpt::string::get( "core_version" ) + ")"; } static inline std::string get_extension( std::string filename ) { if ( filename.find_last_of( "." ) != std::string::npos ) { return filename.substr( filename.find_last_of( "." ) + 1 ); } return ""; } enum class Mode { None, Probe, Info, UI, Batch, Render }; static inline std::string mode_to_string( Mode mode ) { switch ( mode ) { case Mode::None: return "none"; break; case Mode::Probe: return "probe"; break; case Mode::Info: return "info"; break; case Mode::UI: return "ui"; break; case Mode::Batch: return "batch"; break; case Mode::Render: return "render"; break; } return ""; } static const std::int32_t default_low = -2; static const std::int32_t default_high = -1; struct commandlineflags { Mode mode; bool canUI; std::int32_t ui_redraw_interval; bool canProgress; std::string driver; std::string device; std::int32_t buffer; std::int32_t period; std::int32_t samplerate; std::int32_t channels; std::int32_t gain; std::int32_t separation; std::int32_t filtertaps; std::int32_t ramping; // ramping strength : -1:default 0:off 1 2 3 4 5 // roughly milliseconds std::int32_t tempo; std::int32_t pitch; std::int32_t dither; std::int32_t repeatcount; std::int32_t subsong; std::map ctls; double seek_target; double end_time; bool quiet; bool verbose; int terminal_width; int terminal_height; bool show_details; bool show_message; bool show_ui; bool show_progress; bool show_meters; bool show_channel_meters; bool show_pattern; bool use_float; bool use_stdout; bool randomize; bool shuffle; bool restart; std::size_t playlist_index; std::vector filenames; std::string output_filename; std::string output_extension; bool force_overwrite; bool paused; std::string warnings; void apply_default_buffer_sizes() { if ( ui_redraw_interval == default_high ) { ui_redraw_interval = 50; } else if ( ui_redraw_interval == default_low ) { ui_redraw_interval = 10; } if ( buffer == default_high ) { buffer = 250; } else if ( buffer == default_low ) { buffer = 50; } if ( period == default_high ) { period = 50; } else if ( period == default_low ) { period = 10; } } commandlineflags() { mode = Mode::UI; ui_redraw_interval = default_high; driver = ""; device = ""; buffer = default_high; period = default_high; #if defined(__DJGPP__) samplerate = 44100; channels = 2; use_float = false; #else samplerate = 48000; channels = 2; use_float = true; #endif gain = 0; separation = 100; filtertaps = 8; ramping = -1; tempo = 0; pitch = 0; dither = 1; repeatcount = 0; subsong = -1; seek_target = 0.0; end_time = 0.0; quiet = false; verbose = false; #if defined(__DJGPP__) terminal_width = 80; terminal_height = 25; #else terminal_width = 72; terminal_height = 23; #endif #if defined(WIN32) terminal_width = 72; terminal_height = 23; HANDLE hStdOutput = GetStdHandle( STD_OUTPUT_HANDLE ); if ( ( hStdOutput != NULL ) && ( hStdOutput != INVALID_HANDLE_VALUE ) ) { CONSOLE_SCREEN_BUFFER_INFO csbi; ZeroMemory( &csbi, sizeof( CONSOLE_SCREEN_BUFFER_INFO ) ); if ( GetConsoleScreenBufferInfo( hStdOutput, &csbi ) != FALSE ) { terminal_width = std::min( static_cast( 1 + csbi.srWindow.Right - csbi.srWindow.Left ), static_cast( csbi.dwSize.X ) ); terminal_height = std::min( static_cast( 1 + csbi.srWindow.Bottom - csbi.srWindow.Top ), static_cast( csbi.dwSize.Y ) ); } } #else // WIN32 if ( isatty( STDERR_FILENO ) ) { if ( std::getenv( "COLUMNS" ) ) { std::istringstream istr( std::getenv( "COLUMNS" ) ); int tmp = 0; istr >> tmp; if ( tmp > 0 ) { terminal_width = tmp; } } if ( std::getenv( "ROWS" ) ) { std::istringstream istr( std::getenv( "ROWS" ) ); int tmp = 0; istr >> tmp; if ( tmp > 0 ) { terminal_height = tmp; } } #if defined(TIOCGWINSZ) struct winsize ts; if ( ioctl( STDERR_FILENO, TIOCGWINSZ, &ts ) >= 0 ) { terminal_width = ts.ws_col; terminal_height = ts.ws_row; } #elif defined(TIOCGSIZE) struct ttysize ts; if ( ioctl( STDERR_FILENO, TIOCGSIZE, &ts ) >= 0 ) { terminal_width = ts.ts_cols; terminal_height = ts.ts_rows; } #endif } #endif show_details = true; show_message = false; #if defined(WIN32) canUI = IsTerminal( 0 ) ? true : false; canProgress = IsTerminal( 2 ) ? true : false; #else // !WIN32 canUI = isatty( STDIN_FILENO ) ? true : false; canProgress = isatty( STDERR_FILENO ) ? true : false; #endif // WIN32 show_ui = canUI; show_progress = canProgress; show_meters = canUI && canProgress; show_channel_meters = false; show_pattern = false; use_stdout = false; randomize = false; shuffle = false; restart = false; playlist_index = 0; output_extension = "auto"; force_overwrite = false; paused = false; } void check_and_sanitize() { if ( filenames.size() == 0 ) { throw args_error_exception(); } if ( use_stdout && ( device != commandlineflags().device || !output_filename.empty() ) ) { throw args_error_exception(); } if ( !output_filename.empty() && ( device != commandlineflags().device || use_stdout ) ) { throw args_error_exception(); } for ( const auto & filename : filenames ) { if ( filename == "-" ) { canUI = false; } } show_ui = canUI; if ( mode == Mode::None ) { if ( canUI ) { mode = Mode::UI; } else { mode = Mode::Batch; } } if ( mode == Mode::UI && !canUI ) { throw args_error_exception(); } if ( show_progress && !canProgress ) { throw args_error_exception(); } switch ( mode ) { case Mode::None: throw args_error_exception(); break; case Mode::Probe: show_ui = false; show_progress = false; show_meters = false; show_channel_meters = false; show_pattern = false; break; case Mode::Info: show_ui = false; show_progress = false; show_meters = false; show_channel_meters = false; show_pattern = false; break; case Mode::UI: break; case Mode::Batch: show_meters = false; show_channel_meters = false; show_pattern = false; break; case Mode::Render: show_meters = false; show_channel_meters = false; show_pattern = false; show_ui = false; break; } if ( quiet ) { verbose = false; show_ui = false; show_details = false; show_progress = false; show_channel_meters = false; } if ( verbose ) { show_details = true; } if ( channels != 1 && channels != 2 && channels != 4 ) { channels = commandlineflags().channels; } if ( samplerate < 0 ) { samplerate = commandlineflags().samplerate; } if ( output_extension == "auto" ) { output_extension = ""; } if ( mode != Mode::Render && !output_extension.empty() ) { throw args_error_exception(); } if ( mode == Mode::Render && !output_filename.empty() ) { throw args_error_exception(); } if ( mode != Mode::Render && !output_filename.empty() ) { output_extension = get_extension( output_filename ); } if ( output_extension.empty() ) { output_extension = "wav"; } } }; template < typename Tsample > Tsample convert_sample_to( float val ); template < > float convert_sample_to( float val ) { return val; } template < > std::int16_t convert_sample_to( float val ) { std::int32_t tmp = static_cast( val * 32768.0f ); tmp = std::min( tmp, std::int32_t( 32767 ) ); tmp = std::max( tmp, std::int32_t( -32768 ) ); return static_cast( tmp ); } class write_buffers_interface { protected: virtual ~write_buffers_interface() { return; } public: virtual void write_metadata( std::map metadata ) { (void)metadata; return; } virtual void write_updated_metadata( std::map metadata ) { (void)metadata; return; } virtual void write( const std::vector buffers, std::size_t frames ) = 0; virtual void write( const std::vector buffers, std::size_t frames ) = 0; virtual bool pause() { return false; } virtual bool unpause() { return false; } virtual bool sleep( int /*ms*/ ) { return false; } virtual bool is_dummy() const { return false; } }; class write_buffers_polling_wrapper : public write_buffers_interface { protected: std::size_t channels; std::size_t sampleQueueMaxFrames; std::deque sampleQueue; protected: virtual ~write_buffers_polling_wrapper() { return; } protected: write_buffers_polling_wrapper( const commandlineflags & flags ) : channels(flags.channels) , sampleQueueMaxFrames(0) { return; } void set_queue_size_frames( std::size_t frames ) { sampleQueueMaxFrames = frames; } template < typename Tsample > Tsample pop_queue() { float val = 0.0f; if ( !sampleQueue.empty() ) { val = sampleQueue.front(); sampleQueue.pop_front(); } return convert_sample_to( val ); } public: void write( const std::vector buffers, std::size_t frames ) override { for ( std::size_t frame = 0; frame < frames; ++frame ) { for ( std::size_t channel = 0; channel < channels; ++channel ) { sampleQueue.push_back( buffers[channel][frame] ); } while ( sampleQueue.size() >= sampleQueueMaxFrames * channels ) { while ( !forward_queue() ) { sleep( 1 ); } } } } void write( const std::vector buffers, std::size_t frames ) override { for ( std::size_t frame = 0; frame < frames; ++frame ) { for ( std::size_t channel = 0; channel < channels; ++channel ) { sampleQueue.push_back( buffers[channel][frame] * (1.0f/32768.0f) ); } while ( sampleQueue.size() >= sampleQueueMaxFrames * channels ) { while ( !forward_queue() ) { sleep( 1 ); } } } } virtual bool forward_queue() = 0; bool sleep( int ms ) override = 0; }; class write_buffers_polling_wrapper_int : public write_buffers_interface { protected: std::size_t channels; std::size_t sampleQueueMaxFrames; std::deque sampleQueue; protected: virtual ~write_buffers_polling_wrapper_int() { return; } protected: write_buffers_polling_wrapper_int( const commandlineflags & flags ) : channels(flags.channels) , sampleQueueMaxFrames(0) { return; } void set_queue_size_frames( std::size_t frames ) { sampleQueueMaxFrames = frames; } std::int16_t pop_queue() { std::int16_t val = 0; if ( !sampleQueue.empty() ) { val = sampleQueue.front(); sampleQueue.pop_front(); } return val; } public: void write( const std::vector buffers, std::size_t frames ) override { for ( std::size_t frame = 0; frame < frames; ++frame ) { for ( std::size_t channel = 0; channel < channels; ++channel ) { sampleQueue.push_back( convert_sample_to( buffers[channel][frame] ) ); } while ( sampleQueue.size() >= sampleQueueMaxFrames * channels ) { while ( !forward_queue() ) { sleep( 1 ); } } } } void write( const std::vector buffers, std::size_t frames ) override { for ( std::size_t frame = 0; frame < frames; ++frame ) { for ( std::size_t channel = 0; channel < channels; ++channel ) { sampleQueue.push_back( buffers[channel][frame] ); } while ( sampleQueue.size() >= sampleQueueMaxFrames * channels ) { while ( !forward_queue() ) { sleep( 1 ); } } } } virtual bool forward_queue() = 0; bool sleep( int ms ) override = 0; }; class void_audio_stream : public write_buffers_interface { public: virtual ~void_audio_stream() { return; } public: void write( const std::vector buffers, std::size_t frames ) override { (void)buffers; (void)frames; } void write( const std::vector buffers, std::size_t frames ) override { (void)buffers; (void)frames; } bool is_dummy() const override { return true; } }; class file_audio_stream_base : public write_buffers_interface { protected: file_audio_stream_base() { return; } public: void write_metadata( std::map metadata ) override { (void)metadata; return; } void write_updated_metadata( std::map metadata ) override { (void)metadata; return; } void write( const std::vector buffers, std::size_t frames ) override = 0; void write( const std::vector buffers, std::size_t frames ) override = 0; virtual ~file_audio_stream_base() { return; } }; } // namespace openmpt123 #endif // OPENMPT123_HPP