diff --git a/src/libs/libCes.cpp b/src/libs/libCes.cpp new file mode 100644 index 000000000..1e36584cd --- /dev/null +++ b/src/libs/libCes.cpp @@ -0,0 +1,323 @@ +#include "common/abi.h" +#include "common/assert.h" +#include "libs/libs.h" +#include "loader/symbolDatabase.h" + +#include +#include +#include + +namespace Libs { + +namespace LibCes { + +LIB_VERSION("Ces", 1, "Ces", 1, 1); + +static const uint8_t* KYTY_SYSV_ABI CesRefersUcsProfileCp1252() { + PRINT_NAME(); + + static const uint8_t profile = 0; + return &profile; +} + +static uint32_t CesDecodeUtf8(const uint8_t* utf8, uint32_t utf8max, uint32_t* utf8_len) { + if (utf8 == nullptr || utf8max == 0) { + if (utf8_len != nullptr) { + *utf8_len = 0; + } + return 0xfffd; + } + + const uint8_t c0 = utf8[0]; + if (c0 < 0x80) { + if (utf8_len != nullptr) { + *utf8_len = 1; + } + return c0; + } + if ((c0 & 0xe0) == 0xc0 && utf8max >= 2 && (utf8[1] & 0xc0) == 0x80) { + if (utf8_len != nullptr) { + *utf8_len = 2; + } + return ((c0 & 0x1f) << 6u) | (utf8[1] & 0x3fu); + } + if ((c0 & 0xf0) == 0xe0 && utf8max >= 3 && (utf8[1] & 0xc0) == 0x80 && + (utf8[2] & 0xc0) == 0x80) { + if (utf8_len != nullptr) { + *utf8_len = 3; + } + return ((c0 & 0x0f) << 12u) | ((utf8[1] & 0x3fu) << 6u) | (utf8[2] & 0x3fu); + } + if ((c0 & 0xf8) == 0xf0 && utf8max >= 4 && (utf8[1] & 0xc0) == 0x80 && + (utf8[2] & 0xc0) == 0x80 && (utf8[3] & 0xc0) == 0x80) { + if (utf8_len != nullptr) { + *utf8_len = 4; + } + return ((c0 & 0x07) << 18u) | ((utf8[1] & 0x3fu) << 12u) | ((utf8[2] & 0x3fu) << 6u) | + (utf8[3] & 0x3fu); + } + + if (utf8_len != nullptr) { + *utf8_len = 1; + } + return 0xfffd; +} + +struct Cp1252Mapping { + uint8_t cp1252; + uint32_t unicode; +}; + +static constexpr Cp1252Mapping CP1252_EXTENDED_MAP[] = { + {0x80, 0x20ac}, {0x82, 0x201a}, {0x83, 0x0192}, {0x84, 0x201e}, {0x85, 0x2026}, {0x86, 0x2020}, + {0x87, 0x2021}, {0x88, 0x02c6}, {0x89, 0x2030}, {0x8a, 0x0160}, {0x8b, 0x2039}, {0x8c, 0x0152}, + {0x8e, 0x017d}, {0x91, 0x2018}, {0x92, 0x2019}, {0x93, 0x201c}, {0x94, 0x201d}, {0x95, 0x2022}, + {0x96, 0x2013}, {0x97, 0x2014}, {0x98, 0x02dc}, {0x99, 0x2122}, {0x9a, 0x0161}, {0x9b, 0x203a}, + {0x9c, 0x0153}, {0x9e, 0x017e}, {0x9f, 0x0178}, +}; + +static uint8_t CesUnicodeToCp1252(uint32_t code) { + if (code <= 0x7f || (code >= 0xa0 && code <= 0xff)) { + return static_cast(code); + } + + for (const auto& map: CP1252_EXTENDED_MAP) { + if (map.unicode == code) { + return map.cp1252; + } + } + return '?'; +} + +static uint32_t CesCp1252ToUnicode(uint8_t sbc) { + if (sbc <= 0x7f || sbc >= 0xa0) { + return sbc; + } + + for (const auto& map: CP1252_EXTENDED_MAP) { + if (map.cp1252 == sbc) { + return map.unicode; + } + } + return sbc; +} + +static uint32_t CesEncodeUtf8(uint32_t code, uint8_t* utf8, uint32_t utf8max) { + uint8_t tmp[4] {}; + uint32_t len = 0; + + if (code <= 0x7f) { + tmp[0] = static_cast(code); + len = 1; + } else if (code <= 0x7ff) { + tmp[0] = static_cast(0xc0u | ((code >> 6u) & 0x1fu)); + tmp[1] = static_cast(0x80u | (code & 0x3fu)); + len = 2; + } else if (code <= 0xffff) { + tmp[0] = static_cast(0xe0u | ((code >> 12u) & 0x0fu)); + tmp[1] = static_cast(0x80u | ((code >> 6u) & 0x3fu)); + tmp[2] = static_cast(0x80u | (code & 0x3fu)); + len = 3; + } else { + tmp[0] = static_cast(0xf0u | ((code >> 18u) & 0x07u)); + tmp[1] = static_cast(0x80u | ((code >> 12u) & 0x3fu)); + tmp[2] = static_cast(0x80u | ((code >> 6u) & 0x3fu)); + tmp[3] = static_cast(0x80u | (code & 0x3fu)); + len = 4; + } + + if (utf8 != nullptr && utf8max >= len) { + std::memcpy(utf8, tmp, len); + } + + return len; +} + +static int KYTY_SYSV_ABI CesUtf8ToSbc(const uint8_t* utf8, uint32_t utf8max, uint32_t* utf8_len, + const uint8_t* profile, uint8_t* sbc) { + PRINT_NAME(); + + if (sbc == nullptr || profile == nullptr) { + return -1; + } + + uint32_t local_len = 0; + const auto code = CesDecodeUtf8(utf8, utf8max, &local_len); + if (utf8_len != nullptr) { + *utf8_len = local_len; + } + *sbc = CesUnicodeToCp1252(code); + + return 0; +} + +static int KYTY_SYSV_ABI CesSbcToUtf8(const uint8_t* profile, uint8_t sbc, uint8_t* utf8, + uint32_t utf8max, uint32_t* utf8_len) { + PRINT_NAME(); + + if (profile == nullptr || utf8 == nullptr) { + return -1; + } + + const auto code = CesCp1252ToUnicode(sbc); + const auto len = CesEncodeUtf8(code, utf8, utf8max); + if (utf8_len != nullptr) { + *utf8_len = len; + } + + return utf8max >= len ? 0 : -1; +} + +// Profile sheets and contexts each reserve 32 pointers for library-private state. +struct CesProfile { + const char* encoding; + const void* reserved[31]; +}; + +struct CesContext { + const CesProfile* profile; + const void* reserved[31]; +}; + +static_assert(sizeof(CesProfile) == 256 && alignof(CesProfile) == 8); +static_assert(sizeof(CesContext) == 256 && alignof(CesContext) == 8); + +static constexpr char CP932[] = "CP932"; +static constexpr int CES_ERROR_INVALID_PARAMETER = static_cast(0x805c0001u); +static constexpr int CES_ERROR_INVALID_PROFILE = static_cast(0x805c0004u); +static constexpr int CES_ERROR_INVALID_SRC_BUFFER = static_cast(0x805c0010u); +static constexpr int CES_ERROR_SRC_BUFFER_END = static_cast(0x805c0011u); +static constexpr int CES_ERROR_INVALID_ENCODE = static_cast(0x805c0014u); +static constexpr int CES_ERROR_UNASSIGNED_CODE = static_cast(0x805c0020u); +static constexpr int CES_ERROR_INVALID_DST_BUFFER = static_cast(0x805c0030u); +static constexpr int CES_ERROR_DST_BUFFER_END = static_cast(0x805c0031u); + +static CesProfile* KYTY_SYSV_ABI CesUcsProfileInitSJis1997Cp932(CesProfile* sheet) { + PRINT_NAME(); + if (sheet != nullptr) { + *sheet = {}; + sheet->encoding = CP932; + } + return sheet; +} + +static int KYTY_SYSV_ABI CesMbcsUcsContextInit(CesContext* context, const CesProfile* profile) { + PRINT_NAME(); + + if (context == nullptr || profile == nullptr) { + return CES_ERROR_INVALID_PARAMETER; + } + *context = {}; + context->profile = profile; + + return 0; +} + +static int ConvertMbcsToUtf8(CesContext* context, const uint8_t* source, uint32_t source_max, + uint32_t* source_len, uint8_t* destination, uint32_t destination_max, + uint32_t* destination_len, bool measure) { + if (source_len != nullptr) { + *source_len = 0; + } + if (destination_len != nullptr) { + *destination_len = 0; + } + if (destination != nullptr && destination_max != 0) { + destination[0] = 0; + } + if (context == nullptr) { + return CES_ERROR_INVALID_PARAMETER; + } + if (context->profile == nullptr || context->profile->encoding != CP932) { + return CES_ERROR_INVALID_PROFILE; + } + if (source == nullptr) { + return CES_ERROR_INVALID_SRC_BUFFER; + } + if (!measure && destination == nullptr) { + return CES_ERROR_INVALID_DST_BUFFER; + } + if (!measure && destination_max == 0) { + return CES_ERROR_DST_BUFFER_END; + } + + size_t source_size = 0; + while ((source_max == 0 || source_size < source_max) && source[source_size] != 0) { + ++source_size; + } + const bool bounded_end = source_max != 0 && source_size == source_max; + const auto converter = SDL_iconv_open("UTF-8", context->profile->encoding); + EXIT_IF(converter == reinterpret_cast(-1)); + + const char* input = reinterpret_cast(source); + size_t input_left = source_size; + size_t produced = 0; + int result = 0; + std::array scratch; + do { + char* output = measure ? scratch.data() : reinterpret_cast(destination) + produced; + const size_t capacity = measure ? scratch.size() : destination_max - 1 - produced; + size_t output_left = capacity; + const auto status = SDL_iconv(converter, &input, &input_left, &output, &output_left); + produced += capacity - output_left; + if (status == SDL_ICONV_E2BIG) { + if (measure) { + continue; + } + result = CES_ERROR_DST_BUFFER_END; + } else if (status == SDL_ICONV_EINVAL) { + result = bounded_end ? CES_ERROR_SRC_BUFFER_END : CES_ERROR_INVALID_ENCODE; + } else if (status == SDL_ICONV_EILSEQ || status == SDL_ICONV_ERROR) { + const auto* code = reinterpret_cast(input); + const bool lead = + (code[0] >= 0x81 && code[0] <= 0x9f) || (code[0] >= 0xe0 && code[0] <= 0xfc); + const bool trail = input_left >= 2 && ((code[1] >= 0x40 && code[1] <= 0x7e) || + (code[1] >= 0x80 && code[1] <= 0xfc)); + result = lead && trail ? CES_ERROR_UNASSIGNED_CODE : CES_ERROR_INVALID_ENCODE; + } + break; + } while (input_left != 0); + SDL_iconv_close(converter); + + if (!measure) { + destination[produced] = 0; + } + if (source_len != nullptr) { + *source_len = static_cast(source_size - input_left); + } + if (destination_len != nullptr) { + *destination_len = static_cast(produced); + } + return result; +} + +static int KYTY_SYSV_ABI CesMbcsStrGetUtf8Len(CesContext* context, const uint8_t* source, + uint32_t source_max, uint32_t* source_len, + uint32_t* destination_len) { + PRINT_NAME(); + return ConvertMbcsToUtf8(context, source, source_max, source_len, nullptr, 0, destination_len, + true); +} + +static int KYTY_SYSV_ABI CesMbcsStrToUtf8Str(CesContext* context, const uint8_t* source, + uint32_t source_max, uint32_t* source_len, + uint8_t* destination, uint32_t destination_max, + uint32_t* destination_len) { + PRINT_NAME(); + return ConvertMbcsToUtf8(context, source, source_max, source_len, destination, destination_max, + destination_len, false); +} + +LIB_DEFINE(InitCes_1) { + LIB_FUNC("ZiDCxUUGbec", LibCes::CesUcsProfileInitSJis1997Cp932); + LIB_FUNC("538bRGc6Zo8", LibCes::CesMbcsUcsContextInit); + LIB_FUNC("r7Sr1i7KLus", LibCes::CesMbcsStrGetUtf8Len); + LIB_FUNC("yGKn6vdYInc", LibCes::CesMbcsStrToUtf8Str); + LIB_FUNC("LPzYZ+FR0BI", LibCes::CesRefersUcsProfileCp1252); + LIB_FUNC("3Q1gOWWarcw", LibCes::CesUtf8ToSbc); + LIB_FUNC("xTd54EEL1Ao", LibCes::CesSbcToUtf8); +} + +} // namespace LibCes + +} // namespace Libs diff --git a/src/libs/libNet.cpp b/src/libs/libNet.cpp index 2de745481..e3d73ffbc 100644 --- a/src/libs/libNet.cpp +++ b/src/libs/libNet.cpp @@ -3003,186 +3003,6 @@ LIB_DEFINE(InitNet_1_NpUniversalDataSystem) { } // namespace LibNpUniversalDataSystem -namespace LibCes { - -LIB_VERSION("Ces", 1, "Ces", 1, 1); - -static const uint8_t* KYTY_SYSV_ABI CesRefersUcsProfileCp1252() { - PRINT_NAME(); - - static const uint8_t profile = 0; - return &profile; -} - -static uint32_t CesDecodeUtf8(const uint8_t* utf8, uint32_t utf8max, uint32_t* utf8_len) { - if (utf8 == nullptr || utf8max == 0) { - if (utf8_len != nullptr) { - *utf8_len = 0; - } - return 0xfffd; - } - - const uint8_t c0 = utf8[0]; - if (c0 < 0x80) { - if (utf8_len != nullptr) { - *utf8_len = 1; - } - return c0; - } - if ((c0 & 0xe0) == 0xc0 && utf8max >= 2 && (utf8[1] & 0xc0) == 0x80) { - if (utf8_len != nullptr) { - *utf8_len = 2; - } - return ((c0 & 0x1f) << 6u) | (utf8[1] & 0x3fu); - } - if ((c0 & 0xf0) == 0xe0 && utf8max >= 3 && (utf8[1] & 0xc0) == 0x80 && - (utf8[2] & 0xc0) == 0x80) { - if (utf8_len != nullptr) { - *utf8_len = 3; - } - return ((c0 & 0x0f) << 12u) | ((utf8[1] & 0x3fu) << 6u) | (utf8[2] & 0x3fu); - } - if ((c0 & 0xf8) == 0xf0 && utf8max >= 4 && (utf8[1] & 0xc0) == 0x80 && - (utf8[2] & 0xc0) == 0x80 && (utf8[3] & 0xc0) == 0x80) { - if (utf8_len != nullptr) { - *utf8_len = 4; - } - return ((c0 & 0x07) << 18u) | ((utf8[1] & 0x3fu) << 12u) | ((utf8[2] & 0x3fu) << 6u) | - (utf8[3] & 0x3fu); - } - - if (utf8_len != nullptr) { - *utf8_len = 1; - } - return 0xfffd; -} - -struct Cp1252Mapping { - uint8_t cp1252; - uint32_t unicode; -}; - -static constexpr Cp1252Mapping CP1252_EXTENDED_MAP[] = { - {0x80, 0x20ac}, {0x82, 0x201a}, {0x83, 0x0192}, {0x84, 0x201e}, {0x85, 0x2026}, {0x86, 0x2020}, - {0x87, 0x2021}, {0x88, 0x02c6}, {0x89, 0x2030}, {0x8a, 0x0160}, {0x8b, 0x2039}, {0x8c, 0x0152}, - {0x8e, 0x017d}, {0x91, 0x2018}, {0x92, 0x2019}, {0x93, 0x201c}, {0x94, 0x201d}, {0x95, 0x2022}, - {0x96, 0x2013}, {0x97, 0x2014}, {0x98, 0x02dc}, {0x99, 0x2122}, {0x9a, 0x0161}, {0x9b, 0x203a}, - {0x9c, 0x0153}, {0x9e, 0x017e}, {0x9f, 0x0178}, -}; - -static uint8_t CesUnicodeToCp1252(uint32_t code) { - if (code <= 0x7f || (code >= 0xa0 && code <= 0xff)) { - return static_cast(code); - } - - for (const auto& map: CP1252_EXTENDED_MAP) { - if (map.unicode == code) { - return map.cp1252; - } - } - return '?'; -} - -static uint32_t CesCp1252ToUnicode(uint8_t sbc) { - if (sbc <= 0x7f || sbc >= 0xa0) { - return sbc; - } - - for (const auto& map: CP1252_EXTENDED_MAP) { - if (map.cp1252 == sbc) { - return map.unicode; - } - } - return sbc; -} - -static uint32_t CesEncodeUtf8(uint32_t code, uint8_t* utf8, uint32_t utf8max) { - uint8_t tmp[4] {}; - uint32_t len = 0; - - if (code <= 0x7f) { - tmp[0] = static_cast(code); - len = 1; - } else if (code <= 0x7ff) { - tmp[0] = static_cast(0xc0u | ((code >> 6u) & 0x1fu)); - tmp[1] = static_cast(0x80u | (code & 0x3fu)); - len = 2; - } else if (code <= 0xffff) { - tmp[0] = static_cast(0xe0u | ((code >> 12u) & 0x0fu)); - tmp[1] = static_cast(0x80u | ((code >> 6u) & 0x3fu)); - tmp[2] = static_cast(0x80u | (code & 0x3fu)); - len = 3; - } else { - tmp[0] = static_cast(0xf0u | ((code >> 18u) & 0x07u)); - tmp[1] = static_cast(0x80u | ((code >> 12u) & 0x3fu)); - tmp[2] = static_cast(0x80u | ((code >> 6u) & 0x3fu)); - tmp[3] = static_cast(0x80u | (code & 0x3fu)); - len = 4; - } - - if (utf8 != nullptr && utf8max >= len) { - std::memcpy(utf8, tmp, len); - } - - return len; -} - -static int KYTY_SYSV_ABI CesUtf8ToSbc(const uint8_t* utf8, uint32_t utf8max, uint32_t* utf8_len, - const uint8_t* profile, uint8_t* sbc) { - PRINT_NAME(); - - if (sbc == nullptr || profile == nullptr) { - return -1; - } - - uint32_t local_len = 0; - const auto code = CesDecodeUtf8(utf8, utf8max, &local_len); - if (utf8_len != nullptr) { - *utf8_len = local_len; - } - *sbc = CesUnicodeToCp1252(code); - - return 0; -} - -static int KYTY_SYSV_ABI CesSbcToUtf8(const uint8_t* profile, uint8_t sbc, uint8_t* utf8, - uint32_t utf8max, uint32_t* utf8_len) { - PRINT_NAME(); - - if (profile == nullptr || utf8 == nullptr) { - return -1; - } - - const auto code = CesCp1252ToUnicode(sbc); - const auto len = CesEncodeUtf8(code, utf8, utf8max); - if (utf8_len != nullptr) { - *utf8_len = len; - } - - return utf8max >= len ? 0 : -1; -} - -static int KYTY_SYSV_ABI CesStub(uint64_t arg0, uint64_t arg1, uint64_t arg2, uint64_t arg3) { - PRINT_NAME(); - - (void)arg0; - (void)arg1; - (void)arg2; - (void)arg3; - - return 0; -} - -LIB_DEFINE(InitNet_1_Ces) { - LIB_FUNC("ZiDCxUUGbec", LibCes::CesStub); - LIB_FUNC("538bRGc6Zo8", LibCes::CesStub); - LIB_FUNC("LPzYZ+FR0BI", LibCes::CesRefersUcsProfileCp1252); - LIB_FUNC("3Q1gOWWarcw", LibCes::CesUtf8ToSbc); - LIB_FUNC("xTd54EEL1Ao", LibCes::CesSbcToUtf8); -} - -} // namespace LibCes - namespace LibNpGameIntent { LIB_VERSION("NpGameIntent", 1, "NpGameIntent", 1, 1); @@ -3992,7 +3812,6 @@ LIB_DEFINE(InitNet_1) { LibNpAuth::InitNet_1_NpAuth(s); LibNpTrophy2::InitNet_1_NpTrophy2(s); LibNpUniversalDataSystem::InitNet_1_NpUniversalDataSystem(s); - LibCes::InitNet_1_Ces(s); LibNpGameIntent::InitNet_1_NpGameIntent(s); LibNpWebApi2::InitNet_1_NpWebApi2(s); LibGameUpdate::InitNet_1_GameUpdate(s); diff --git a/src/libs/libs.cpp b/src/libs/libs.cpp index f12b3e494..c7da9a186 100644 --- a/src/libs/libs.cpp +++ b/src/libs/libs.cpp @@ -42,6 +42,10 @@ namespace LibPsml { LIB_DEFINE(InitPsml_1); } // namespace LibPsml +namespace LibCes { +LIB_DEFINE(InitCes_1); +} // namespace LibCes + namespace LibC { LIB_DEFINE(InitLibC_1); } // namespace LibC @@ -103,6 +107,7 @@ void InitAll(Loader::SymbolDatabase* s) { LibContentExport::InitContentExport_1(s); LibContentSearch::InitContentSearch_1(s); LibC::InitLibC_1(s); + LibCes::InitCes_1(s); InitDbgAddressSanitizer_1(s); LibRazorCpu::InitRazorCpu_1(s); InitDialog_1(s);