Files
Dean d5e493883f Merge pull request #10738 from rooklz/fix/cescs-big5-box-drawing
fix(libSceCesCs): encode the Big5 box-drawing duplicates into their lower cells as the console does
2026-10-26 21:56:25 +13:00

416 lines
24 KiB
C++

#include "prx/libSceCesCs/include/Api.hpp"
#include "prx/libc/include/general/VabiMacros.hpp"
#include <cstdint>
#include <cstdlib>
#include <cstring>
using ToUtf8 = int (APS5_VABI*)(const void*, const std::uint8_t*, std::uint32_t, std::uint32_t*, std::uint8_t*, std::uint32_t, std::uint32_t*);
using ToUcs2 = int (APS5_VABI*)(const void*, const std::uint8_t*, std::uint32_t, std::uint32_t*, std::uint16_t*);
using ToUtf16 = int (APS5_VABI*)(const void*, const std::uint8_t*, std::uint32_t, std::uint32_t*, std::uint16_t*, std::uint32_t, std::uint32_t*);
using ToUtf32 = int (APS5_VABI*)(const void*, const std::uint8_t*, std::uint32_t, std::uint32_t*, std::uint32_t*);
using FromUcs2 = int (APS5_VABI*)(std::uint16_t, const void*, std::uint8_t*, std::uint32_t, std::uint32_t*);
using FromUtf8 = int (APS5_VABI*)(const std::uint8_t*, std::uint32_t, std::uint32_t*, const void*, std::uint8_t*, std::uint32_t, std::uint32_t*);
using FromUtf16 = int (APS5_VABI*)(const std::uint16_t*, std::uint32_t, std::uint32_t*, const void*, std::uint8_t*, std::uint32_t, std::uint32_t*);
using FromUtf32 = int (APS5_VABI*)(std::uint32_t, const void*, std::uint8_t*, std::uint32_t, std::uint32_t*);
using FromUtf32Unit = int (APS5_VABI*)(const std::uint32_t*, const void*, std::uint8_t*, std::uint32_t, std::uint32_t*);
struct Decoders {
ToUtf8 utf8;
ToUcs2 ucs2;
ToUtf16 utf16[3];
ToUtf32 utf32[3];
};
struct Encoders {
FromUcs2 ucs2;
FromUtf8 utf8;
FromUtf16 utf16[3];
FromUtf32 utf32;
FromUtf32Unit utf32Unit[3];
};
constexpr Decoders eucKrDecoders = {sceCesEucKrToUtf8, sceCesEucKrToUcs2, {sceCesEucKrToUtf16, sceCesEucKrToUtf16be, sceCesEucKrToUtf16le}, {sceCesEucKrToUtf32, sceCesEucKrToUtf32be, sceCesEucKrToUtf32le}};
constexpr Decoders uhcDecoders = {sceCesUhcToUtf8, sceCesUhcToUcs2, {sceCesUhcToUtf16, sceCesUhcToUtf16be, sceCesUhcToUtf16le}, {sceCesUhcToUtf32, sceCesUhcToUtf32be, sceCesUhcToUtf32le}};
constexpr Decoders big5Decoders = {sceCesBig5ToUtf8, sceCesBig5ToUcs2, {sceCesBig5ToUtf16, sceCesBig5ToUtf16be, sceCesBig5ToUtf16le}, {sceCesBig5ToUtf32, sceCesBig5ToUtf32be, sceCesBig5ToUtf32le}};
constexpr Encoders eucKrEncoders = {sceCesUcs2ToEucKr, sceCesUtf8ToEucKr, {sceCesUtf16ToEucKr, sceCesUtf16beToEucKr, sceCesUtf16leToEucKr}, sceCesUtf32ToEucKr, {nullptr, sceCesUtf32beToEucKr, sceCesUtf32leToEucKr}};
constexpr Encoders uhcEncoders = {sceCesUcs2ToUhc, sceCesUtf8ToUhc, {sceCesUtf16ToUhc, sceCesUtf16beToUhc, sceCesUtf16leToUhc}, sceCesUtf32ToUhc, {nullptr, sceCesUtf32beToUhc, sceCesUtf32leToUhc}};
constexpr Encoders big5Encoders = {sceCesUcs2ToBig5, sceCesUtf8ToBig5, {sceCesUtf16ToBig5, sceCesUtf16beToBig5, sceCesUtf16leToBig5}, sceCesUtf32ToBig5, {nullptr, sceCesUtf32beToBig5, sceCesUtf32leToBig5}};
constexpr int invalidParameter = static_cast<int>(0x805C0001);
constexpr int invalidSrcBuffer = static_cast<int>(0x805C0010);
constexpr int srcBufferEnd = static_cast<int>(0x805C0011);
constexpr int invalidEncode = static_cast<int>(0x805C0014);
constexpr int illegalCode = static_cast<int>(0x805C0015);
constexpr int unassignedCode = static_cast<int>(0x805C0020);
constexpr int invalidDstBuffer = static_cast<int>(0x805C0030);
constexpr int dstBufferEnd = static_cast<int>(0x805C0031);
static void Require(bool value) { if (!value) std::abort(); }
static const std::uint8_t* Bytes(const char* text) { return reinterpret_cast<const std::uint8_t*>(text); }
static std::uint32_t Size(const char* text) { return static_cast<std::uint32_t>(std::strlen(text)); }
static std::uint64_t Fnv(std::uint64_t hash, std::uint8_t byte) { return (hash ^ byte) * 0x100000001B3ull; }
static void RequireTable(ToUtf8 convert, const void* profile, std::uint64_t expectedHash, std::uint32_t expectedCells) {
std::uint64_t hash = 0xCBF29CE484222325ull;
std::uint32_t cells = 0;
for (std::uint32_t lead = 0; lead < 256; ++lead) {
for (std::uint32_t trail = 0; trail < 256; ++trail) {
const std::uint8_t source[2] = {static_cast<std::uint8_t>(lead), static_cast<std::uint8_t>(trail)};
std::uint8_t utf8[4] = {};
std::uint32_t consumed = 0;
std::uint32_t produced = 0;
const int result = convert(profile, source, sizeof(source), &consumed, utf8, sizeof(utf8), &produced);
hash = Fnv(hash, static_cast<std::uint8_t>(result & 0xFF));
if (result != 0) continue;
hash = Fnv(hash, static_cast<std::uint8_t>(consumed));
for (std::uint32_t i = 0; i < produced; ++i) hash = Fnv(hash, utf8[i]);
if (consumed == 2) ++cells;
}
}
Require(hash == expectedHash && cells == expectedCells);
}
static void RequireScalar(ToUtf8 convert, const void* profile, const char* source, const char* expected) {
std::uint8_t utf8[4] = {};
std::uint32_t consumed = 0;
std::uint32_t produced = 0;
Require(convert(profile, Bytes(source), Size(source), &consumed, utf8, sizeof(utf8), &produced) == 0);
Require(consumed == Size(source) && produced == Size(expected) && std::memcmp(utf8, expected, produced) == 0);
}
static int Scalar(ToUtf8 convert, const void* profile, const char* source, std::uint32_t sourceMax, std::uint32_t utf8Max) {
std::uint8_t utf8[4] = {0x55, 0x55, 0x55, 0x55};
std::uint32_t consumed = 77;
std::uint32_t produced = 77;
const int result = convert(profile, Bytes(source), sourceMax, &consumed, utf8, utf8Max, &produced);
const bool decodeError = source && sourceMax > 0 && (result == static_cast<int>(0x805C0011) || result == static_cast<int>(0x805C0014) || result == static_cast<int>(0x805C0020));
if (result != 0) Require((decodeError ? consumed != 77 && produced == 0 : consumed == 77 && produced == 77) && std::memcmp(utf8, "\x55\x55\x55\x55", 4) == 0);
return result;
}
static void RequireString(void* context, const char* mbcs, const char* utf8) {
std::uint8_t converted[32] = {};
std::uint32_t read = 0;
std::uint32_t written = 0;
Require(sceCesMbcsStrToUtf8Str(context, Bytes(mbcs), Size(mbcs), &read, converted, sizeof(converted), &written) == 0);
Require(read == Size(mbcs) && written == Size(utf8) && std::memcmp(converted, utf8, written) == 0);
std::uint8_t back[32] = {};
Require(sceCesUtf8StrToMbcsStr(context, Bytes(utf8), Size(utf8), &read, back, sizeof(back), &written) == 0);
Require(read == Size(utf8) && written == Size(mbcs) && std::memcmp(back, mbcs, written) == 0);
}
static int Utf8ToMbcs(void* context, const char* utf8) {
std::uint8_t mbcs[8] = {};
std::uint32_t read = 0;
std::uint32_t written = 0;
return sceCesUtf8StrToMbcsStr(context, Bytes(utf8), Size(utf8), &read, mbcs, sizeof(mbcs), &written);
}
static std::uint32_t RoundTripMismatches(void* context) {
std::uint32_t mismatches = 0;
for (std::uint32_t lead = 0x81; lead < 0xFF; ++lead) {
for (std::uint32_t trail = 0x40; trail < 0xFF; ++trail) {
const std::uint8_t source[2] = {static_cast<std::uint8_t>(lead), static_cast<std::uint8_t>(trail)};
std::uint16_t ucs2[2] = {};
std::uint32_t read = 0;
std::uint32_t written = 0;
if (sceCesMbcsStrToUcs2Str(context, source, sizeof(source), &read, ucs2, 2, &written) != 0) continue;
Require(read == 2 && written == 1 && ucs2[1] == 0);
std::uint8_t back[3] = {};
Require(sceCesUcs2StrToMbcsStr(context, ucs2, 1, &read, back, sizeof(back), &written) == 0);
Require(read == 1 && written == 2 && back[2] == 0);
if (std::memcmp(source, back, sizeof(source)) != 0) ++mismatches;
}
}
return mismatches;
}
static std::uint32_t Load(const void* data, std::uint32_t size, int order) {
if (order == 0) {
std::uint16_t unit = 0;
std::uint32_t wide = 0;
if (size == 2) std::memcpy(&unit, data, 2);
else std::memcpy(&wide, data, 4);
return size == 2 ? unit : wide;
}
const auto* bytes = static_cast<const std::uint8_t*>(data);
std::uint32_t value = 0;
for (std::uint32_t i = 0; i < size; ++i) value |= static_cast<std::uint32_t>(bytes[i]) << (8 * (order == 1 ? size - 1 - i : i));
return value;
}
static void Store(void* data, std::uint32_t size, int order, std::uint32_t value) {
if (order == 0) {
const auto unit = static_cast<std::uint16_t>(value);
if (size == 2) std::memcpy(data, &unit, 2);
else std::memcpy(data, &value, 4);
return;
}
auto* bytes = static_cast<std::uint8_t*>(data);
for (std::uint32_t i = 0; i < size; ++i) bytes[i] = static_cast<std::uint8_t>(value >> (8 * (order == 1 ? size - 1 - i : i)));
}
static std::uint32_t Utf8Code(const std::uint8_t* utf8, std::uint32_t length) {
if (length == 1) return utf8[0];
if (length == 2) return (utf8[0] & 0x1Fu) << 6 | (utf8[1] & 0x3Fu);
return (utf8[0] & 0x0Fu) << 12 | (utf8[1] & 0x3Fu) << 6 | (utf8[2] & 0x3Fu);
}
static std::uint32_t Utf8Bytes(std::uint32_t code, std::uint8_t* utf8) {
if (code < 0x80) {
utf8[0] = static_cast<std::uint8_t>(code);
return 1;
}
const std::uint32_t length = code < 0x800 ? 2 : code < 0x10000 ? 3 : 4;
for (std::uint32_t i = length - 1; i > 0; --i) {
utf8[i] = static_cast<std::uint8_t>(0x80 | (code & 0x3F));
code >>= 6;
}
utf8[0] = static_cast<std::uint8_t>((length == 2 ? 0xC0 : length == 3 ? 0xE0 : 0xF0) | code);
return length;
}
static void RequireDecoders(const Decoders& decoders, const void* profile) {
for (std::uint32_t lead = 0; lead < 256; ++lead) {
for (std::uint32_t trail = 0; trail < 256; ++trail) {
const std::uint8_t source[2] = {static_cast<std::uint8_t>(lead), static_cast<std::uint8_t>(trail)};
std::uint8_t utf8[4] = {};
std::uint32_t consumed = 0;
std::uint32_t produced = 0;
const int expected = decoders.utf8(profile, source, sizeof(source), &consumed, utf8, sizeof(utf8), &produced);
const std::uint32_t code = expected == 0 ? Utf8Code(utf8, produced) : 0;
std::uint16_t ucs2 = 0x5555;
std::uint32_t read = 77;
Require(decoders.ucs2(profile, source, sizeof(source), &read, &ucs2) == expected);
Require(read == consumed && ucs2 == (expected == 0 ? code : 0));
for (int order = 0; order < 3; ++order) {
std::uint16_t utf16 = 0x5555;
std::uint32_t units = 77;
read = 77;
Require(decoders.utf16[order](profile, source, sizeof(source), &read, &utf16, 1, &units) == expected);
Require(read == consumed && (expected == 0 ? units == 1 && Load(&utf16, 2, order) == code : units == 0 && utf16 == 0x5555));
std::uint32_t utf32 = 0x55555555;
read = 77;
Require(decoders.utf32[order](profile, source, sizeof(source), &read, &utf32) == expected);
Require(read == consumed && (expected == 0 ? Load(&utf32, 4, order) == code : utf32 == 0));
}
}
}
}
struct Encoded {
std::uint8_t bytes[2] = {0x55, 0x55};
std::uint32_t size = 77;
std::uint32_t read = 77;
};
static int Encode(const Encoders& encoders, int form, std::uint32_t code, const void* profile, Encoded& out) {
std::uint8_t utf8[4] = {};
std::uint16_t utf16[2] = {};
std::uint32_t utf32 = 0;
if (form == 0) return encoders.ucs2(static_cast<std::uint16_t>(code), profile, out.bytes, sizeof(out.bytes), &out.size);
if (form == 1) return encoders.utf8(utf8, Utf8Bytes(code, utf8), &out.read, profile, out.bytes, sizeof(out.bytes), &out.size);
if (form <= 4) {
const int order = form - 2;
if (code < 0x10000) Store(utf16, 2, order, code);
else {
Store(utf16, 2, order, 0xD800 | (code - 0x10000) >> 10);
Store(utf16 + 1, 2, order, 0xDC00 | (code & 0x3FF));
}
return encoders.utf16[order](utf16, code < 0x10000 ? 1 : 2, &out.read, profile, out.bytes, sizeof(out.bytes), &out.size);
}
if (form == 5) return encoders.utf32(code, profile, out.bytes, sizeof(out.bytes), &out.size);
Store(&utf32, 4, form - 5, code);
return encoders.utf32Unit[form - 5](&utf32, profile, out.bytes, sizeof(out.bytes), &out.size);
}
static std::uint32_t SourceUnits(int form, std::uint32_t code) {
if (form == 1) return code < 0x80 ? 1 : code < 0x800 ? 2 : code < 0x10000 ? 3 : 4;
if (form >= 2 && form <= 4) return code < 0x10000 ? 1 : 2;
return 77;
}
static void RequireEncode(const Encoders& encoders, const void* profile, std::uint32_t code, int expected, const std::uint8_t* bytes, std::uint32_t size) {
for (int form = 0; form < 8; ++form) {
if (form == 0 && code > 0xFFFF) continue;
Encoded out;
Require(Encode(encoders, form, code, profile, out) == expected);
if (expected == 0) Require(out.read == SourceUnits(form, code) && out.size == size && std::memcmp(out.bytes, bytes, size) == 0);
else Require(out.read == 77 && out.size == ((form == 0 || form >= 5) && expected != static_cast<int>(0x805C0001) ? 0u : 77u) && out.bytes[0] == 0x55 && out.bytes[1] == 0x55);
}
}
static void RequireEncoders(const Encoders& encoders, const void* profile, void* context) {
Require(sceCesMbcsUcsContextInit(context, profile) == 0);
for (std::uint32_t code = 1; code < 0x10000; ++code) {
if (code >= 0xD800 && code <= 0xDFFF) continue;
const auto ucs2 = static_cast<std::uint16_t>(code);
std::uint8_t bytes[3] = {};
std::uint32_t read = 0;
std::uint32_t written = 0;
const int expected = sceCesUcs2StrToMbcsStr(context, &ucs2, 1, &read, bytes, sizeof(bytes), &written);
RequireEncode(encoders, profile, code, expected, bytes, written);
}
RequireEncode(encoders, profile, 0, 0, Bytes("\0"), 1);
RequireEncode(encoders, profile, 0x20000, unassignedCode, nullptr, 0);
RequireEncode(encoders, profile, 0x10FFFF, unassignedCode, nullptr, 0);
}
int main() {
alignas(8) std::uint8_t eucKrSheet[64] = {};
alignas(8) std::uint8_t uhcSheet[64] = {};
alignas(8) std::uint8_t big5Sheet[64] = {};
alignas(8) std::uint8_t eucJpSheet[64] = {};
Require(sceCesUcsProfileInitEucKr(nullptr) == nullptr);
Require(sceCesUcsProfileInitUhc(nullptr) == nullptr);
Require(sceCesUcsProfileInitBig5Cp950(nullptr) == nullptr);
void* eucKr = sceCesUcsProfileInitEucKr(eucKrSheet);
void* uhc = sceCesUcsProfileInitUhc(uhcSheet);
void* big5 = sceCesUcsProfileInitBig5Cp950(big5Sheet);
void* eucJp = sceCesUcsProfileInitEucJpCp51932(eucJpSheet);
Require(eucKr == eucKrSheet && uhc == uhcSheet && big5 == big5Sheet && eucJp == eucJpSheet);
RequireTable(sceCesEucKrToUtf8, eucKr, 0x18F4D7FD5B554273ull, 8227);
RequireTable(sceCesUhcToUtf8, uhc, 0x33B12AA5FAB04C43ull, 17049);
RequireScalar(sceCesEucKrToUtf8, eucKr, "\xA2\xE8", "\xE3\x89\xBE");
RequireScalar(sceCesUhcToUtf8, uhc, "\xA2\xE8", "\xE3\x89\xBE");
RequireTable(sceCesBig5ToUtf8, big5, 0xE4D0D6A8019F35D9ull, 13503);
RequireScalar(sceCesEucKrToUtf8, eucKr, "\xB0\xA1", "\xEA\xB0\x80");
RequireScalar(sceCesEucKrToUtf8, eucKr, "\xA2\xE6", "\xE2\x82\xAC");
RequireScalar(sceCesUhcToUtf8, uhc, "\xB0\xA1", "\xEA\xB0\x80");
RequireScalar(sceCesUhcToUtf8, uhc, "\x8C\x63", "\xEB\x98\xA0");
RequireScalar(sceCesBig5ToUtf8, big5, "\xA4\xA4", "\xE4\xB8\xAD");
RequireScalar(sceCesBig5ToUtf8, big5, "A", "A");
std::uint8_t zero[4] = {0x55, 0x55, 0x55, 0x55};
Require(sceCesUhcToUtf8(uhc, Bytes("\0"), 1, nullptr, zero, sizeof(zero), nullptr) == 0 && zero[0] == 0 && zero[1] == 0x55);
Require(Scalar(sceCesEucKrToUtf8, nullptr, "\xB0\xA1", 2, 4) == invalidParameter);
Require(Scalar(sceCesEucKrToUtf8, uhc, "\xB0\xA1", 2, 4) == invalidParameter);
Require(Scalar(sceCesUhcToUtf8, eucKr, "\xB0\xA1", 2, 4) == invalidParameter);
Require(Scalar(sceCesBig5ToUtf8, eucJp, "\xA4\xA4", 2, 4) == invalidParameter);
Require(Scalar(sceCesEucKrToUtf8, eucKr, nullptr, 2, 4) == invalidSrcBuffer);
Require(Scalar(sceCesEucKrToUtf8, eucKr, "\xB0\xA1", 0, 4) == srcBufferEnd);
Require(Scalar(sceCesUhcToUtf8, uhc, "\x8C\x63", 1, 4) == srcBufferEnd);
Require(Scalar(sceCesBig5ToUtf8, big5, "\xA4\xA4", 1, 4) == srcBufferEnd);
Require(Scalar(sceCesEucKrToUtf8, eucKr, "\xB0\xA1", 2, 2) == dstBufferEnd);
Require(Scalar(sceCesEucKrToUtf8, eucKr, "\x8C\x63", 2, 4) == invalidEncode);
Require(Scalar(sceCesUhcToUtf8, uhc, "\x80\xA1", 2, 4) == invalidEncode);
Require(Scalar(sceCesUhcToUtf8, uhc, "\xB0\x5B", 2, 4) == invalidEncode);
Require(Scalar(sceCesUhcToUtf8, uhc, "\xC9\xA1", 2, 4) == unassignedCode);
Require(Scalar(sceCesUhcToUtf8, uhc, "\xC7\x41", 2, 4) == unassignedCode);
Require(Scalar(sceCesBig5ToUtf8, big5, "\xA4\x80", 2, 4) == invalidEncode);
Require(Scalar(sceCesBig5ToUtf8, big5, "\xFF\xA1", 2, 4) == invalidEncode);
Require(Scalar(sceCesBig5ToUtf8, big5, "\x81\x40", 2, 4) == unassignedCode);
Require(Scalar(sceCesBig5ToUtf8, big5, "\xFA\x40", 2, 4) == unassignedCode);
Require(sceCesEucKrToUtf8(eucKr, Bytes("\xB0\xA1"), 2, nullptr, nullptr, 4, nullptr) == invalidDstBuffer);
alignas(8) std::uint8_t context[64] = {};
Require(sceCesMbcsUcsContextInit(context, eucKr) == 0);
RequireString(context, "\xC7\xD1\xB1\xB9\xBE\xEE A", "\xED\x95\x9C\xEA\xB5\xAD\xEC\x96\xB4 A");
Require(Utf8ToMbcs(context, "\xEB\x98\xA0") == unassignedCode);
Require(RoundTripMismatches(context) == 0);
Require(sceCesMbcsUcsContextInit(context, uhc) == 0);
RequireString(context, "\x8C\x63\xC7\xD1", "\xEB\x98\xA0\xED\x95\x9C");
Require(Utf8ToMbcs(context, "\xE4\xB8\xAD") == 0);
Require(Utf8ToMbcs(context, "\xE2\x82\xAC\xEF\xBF\xBF") == unassignedCode);
Require(RoundTripMismatches(context) == 0);
Require(sceCesMbcsUcsContextInit(context, big5) == 0);
RequireString(context, "\xA4\xA4\xA4\xE5", "\xE4\xB8\xAD\xE6\x96\x87");
RequireString(context, "\xA2\xA4\xA2\x7E\xA4\x51\xA4\xCA", "\xE2\x95\x90\xE2\x95\xAD\xE5\x8D\x81\xE5\x8D\x85");
RequireString(context, "\xA2\xA5\xA2\xA7\xA2\xA6\xA2\xA1\xA2\xA3\xA2\xA2", "\xE2\x95\x9E\xE2\x95\xA1\xE2\x95\xAA\xE2\x95\xAE\xE2\x95\xAF\xE2\x95\xB0");
Require(Utf8ToMbcs(context, "\xEA\xB0\x80") == unassignedCode);
Require(RoundTripMismatches(context) == 10);
RequireDecoders(eucKrDecoders, eucKr);
RequireDecoders(uhcDecoders, uhc);
RequireDecoders(big5Decoders, big5);
RequireEncoders(eucKrEncoders, eucKr, context);
RequireEncoders(uhcEncoders, uhc, context);
RequireEncoders(big5Encoders, big5, context);
const std::uint8_t* ga = Bytes("\xB0\xA1");
std::uint16_t unit = 0;
std::uint32_t wide = 0;
std::uint32_t read = 0;
std::uint32_t units = 0;
Require(sceCesEucKrToUtf16be(eucKr, ga, 2, &read, &unit, 1, &units) == 0 && read == 2 && units == 1 && std::memcmp(&unit, "\xAC\x00", 2) == 0);
Require(sceCesEucKrToUtf16le(eucKr, ga, 2, &read, &unit, 1, &units) == 0 && std::memcmp(&unit, "\x00\xAC", 2) == 0);
Require(sceCesEucKrToUtf16(eucKr, ga, 2, nullptr, &unit, 1, nullptr) == 0 && unit == 0xAC00);
Require(sceCesEucKrToUtf32be(eucKr, ga, 2, &read, &wide) == 0 && read == 2 && std::memcmp(&wide, "\x00\x00\xAC\x00", 4) == 0);
Require(sceCesEucKrToUtf32le(eucKr, ga, 2, &read, &wide) == 0 && std::memcmp(&wide, "\x00\xAC\x00\x00", 4) == 0);
Require(sceCesEucKrToUtf32(eucKr, ga, 2, nullptr, &wide) == 0 && wide == 0xAC00);
Require(sceCesEucKrToUcs2(eucKr, Bytes("\0"), 1, &read, &unit) == 0 && read == 1 && unit == 0);
Require(sceCesUhcToUcs2(uhc, Bytes("\x8C\x63"), 2, nullptr, &unit) == 0 && unit == 0xB620);
Require(sceCesBig5ToUtf32le(big5, Bytes("\xA4\xA4"), 2, &read, &wide) == 0 && std::memcmp(&wide, "\x2D\x4E\x00\x00", 4) == 0);
Require(sceCesEucKrToUtf16(uhc, nullptr, 0, nullptr, nullptr, 0, nullptr) == invalidParameter);
Require(sceCesEucKrToUtf16(eucKr, nullptr, 0, nullptr, nullptr, 0, nullptr) == invalidSrcBuffer);
Require(sceCesEucKrToUtf16(eucKr, ga, 0, nullptr, nullptr, 0, nullptr) == srcBufferEnd);
Require(sceCesEucKrToUtf16(eucKr, ga, 2, nullptr, nullptr, 0, nullptr) == invalidDstBuffer);
Require(sceCesEucKrToUtf16(eucKr, Bytes("\x8C\x63"), 2, nullptr, &unit, 0, nullptr) == invalidEncode);
unit = 0x5555;
read = 77;
units = 77;
Require(sceCesEucKrToUtf16(eucKr, ga, 2, &read, &unit, 0, &units) == dstBufferEnd && unit == 0x5555 && read == 77 && units == 77);
Require(sceCesUhcToUcs2(uhc, ga, 2, nullptr, nullptr) == invalidDstBuffer);
Require(sceCesBig5ToUtf32(big5, Bytes("\xA4\xA4"), 1, nullptr, &wide) == srcBufferEnd);
Require(sceCesBig5ToUtf32be(eucJp, Bytes("\xA4\xA4"), 2, nullptr, &wide) == invalidParameter);
Require(sceCesBig5ToUtf32le(big5, Bytes("\xA4\xA4"), 2, nullptr, nullptr) == invalidDstBuffer);
RequireEncode(eucKrEncoders, eucKr, 0x20AC, 0, Bytes("\xA2\xE6"), 2);
RequireEncode(eucKrEncoders, eucKr, 0xB620, unassignedCode, nullptr, 0);
RequireEncode(uhcEncoders, uhc, 0xB620, 0, Bytes("\x8C\x63"), 2);
RequireEncode(big5Encoders, big5, 0x2550, 0, Bytes("\xA2\xA4"), 2);
RequireEncode(big5Encoders, big5, 0x255E, 0, Bytes("\xA2\xA5"), 2);
RequireEncode(big5Encoders, big5, 0x2561, 0, Bytes("\xA2\xA7"), 2);
RequireEncode(big5Encoders, big5, 0x256A, 0, Bytes("\xA2\xA6"), 2);
RequireEncode(big5Encoders, big5, 0x5341, 0, Bytes("\xA4\x51"), 2);
RequireEncode(big5Encoders, big5, 0xAC00, unassignedCode, nullptr, 0);
RequireEncode(eucKrEncoders, uhc, 0xAC00, invalidParameter, nullptr, 0);
RequireEncode(uhcEncoders, nullptr, 0xAC00, invalidParameter, nullptr, 0);
RequireEncode(big5Encoders, eucJp, 0x4E2D, invalidParameter, nullptr, 0);
std::uint8_t out[2] = {0x55, 0x55};
std::uint32_t size = 77;
read = 77;
const std::uint32_t big = 0x0000AC00;
Require(sceCesUcs2ToEucKr(0xAC00, uhc, nullptr, 0, nullptr) == invalidParameter);
Require(sceCesUcs2ToEucKr(0xD800, eucKr, nullptr, 0, nullptr) == invalidDstBuffer);
Require(sceCesUcs2ToEucKr(0xD800, eucKr, out, 2, &size) == illegalCode);
Require(sceCesUtf32ToUhc(0x110000, uhc, out, 2, &size) == invalidEncode);
Require(sceCesUtf32ToUhc(0xDFFF, uhc, out, 2, &size) == illegalCode);
Require(sceCesUtf32beToBig5(nullptr, eucKr, nullptr, 0, nullptr) == invalidParameter);
Require(sceCesUtf32beToBig5(nullptr, big5, nullptr, 0, nullptr) == invalidSrcBuffer);
Require(sceCesUtf32leToBig5(&big, big5, nullptr, 0, nullptr) == invalidDstBuffer);
Require(sceCesUtf8ToEucKr(nullptr, 0, nullptr, uhc, nullptr, 0, nullptr) == invalidParameter);
Require(sceCesUtf8ToEucKr(nullptr, 0, nullptr, eucKr, nullptr, 0, nullptr) == invalidSrcBuffer);
Require(sceCesUtf8ToEucKr(Bytes("\xEA\xB0\x80"), 0, nullptr, eucKr, nullptr, 0, nullptr) == srcBufferEnd);
Require(sceCesUtf8ToEucKr(Bytes("\xEA\xB0\x80"), 3, nullptr, eucKr, nullptr, 0, nullptr) == invalidDstBuffer);
Require(sceCesUtf8ToEucKr(Bytes("\xEA\xB0\x80"), 2, &read, eucKr, out, 2, &size) == srcBufferEnd);
Require(sceCesUtf8ToEucKr(Bytes("\xC0\x80"), 2, &read, eucKr, out, 2, &size) == illegalCode);
Require(sceCesUtf8ToEucKr(Bytes("\xEA\xB0\x80"), 3, &read, eucKr, out, 1, &size) == dstBufferEnd);
const std::uint16_t lowSurrogate[1] = {0xDC00};
const std::uint16_t highSurrogate[2] = {0xD800, 0x0041};
Require(sceCesUtf16ToUhc(lowSurrogate, 1, &read, uhc, out, 2, &size) == invalidEncode);
Require(sceCesUtf16ToUhc(highSurrogate, 1, &read, uhc, out, 2, &size) == srcBufferEnd);
Require(sceCesUtf16ToUhc(highSurrogate, 2, &read, uhc, out, 2, &size) == invalidEncode);
Require(out[0] == 0x55 && out[1] == 0x55 && size == 0 && read == 77);
std::memcpy(&unit, "\xAC\x00", 2);
Require(sceCesUtf16beToEucKr(&unit, 1, &read, eucKr, out, 2, &size) == 0 && read == 1 && size == 2 && std::memcmp(out, "\xB0\xA1", 2) == 0);
std::memcpy(&unit, "\x00\xAC", 2);
Require(sceCesUtf16leToUhc(&unit, 1, nullptr, uhc, out, 2, nullptr) == 0 && std::memcmp(out, "\xB0\xA1", 2) == 0);
std::memcpy(&wide, "\x00\x00\x4E\x2D", 4);
Require(sceCesUtf32beToBig5(&wide, big5, out, 2, &size) == 0 && size == 2 && std::memcmp(out, "\xA4\xA4", 2) == 0);
std::memcpy(&wide, "\x2D\x4E\x00\x00", 4);
Require(sceCesUtf32leToBig5(&wide, big5, out, 2, nullptr) == 0 && std::memcmp(out, "\xA4\xA4", 2) == 0);
Require(sceCesUcs2ToEucKr(0, eucKr, out, 1, &size) == 0 && size == 1 && out[0] == 0);
return 0;
}