mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-11-07 13:39:55 +00:00
Convert UTF-16 to Java modified UTF-8 following ICU 78.1 ustrtrns.cpp. Every code unit is encoded on its own, so surrogates become three bytes each and U+0000 becomes C0 80. Output stops at the first unit that does not fit and the remaining units are only counted for the preflight length; the terminator and warning or overflow codes follow u_terminateChars. The sceIcuUnknown stub with this NID is removed.
146 lines
5.9 KiB
C++
146 lines
5.9 KiB
C++
#include "prx/libSceIcu/libSceIcu.hpp"
|
|
#include "prx/libSceIcu/Status.hpp"
|
|
#include <cstdint>
|
|
#include <cstring>
|
|
|
|
namespace {
|
|
|
|
bool Continuation(std::uint8_t byte) {
|
|
return byte >= 0x80 && byte <= 0xbf;
|
|
}
|
|
|
|
bool ThreeByteSecond(std::uint8_t lead, std::uint8_t byte) {
|
|
if (lead == 0xe0) return byte >= 0xa0 && byte <= 0xbf;
|
|
if (lead == 0xed) return byte >= 0x80 && byte <= 0x9f;
|
|
return Continuation(byte);
|
|
}
|
|
|
|
bool FourByteSecond(std::uint8_t lead, std::uint8_t byte) {
|
|
if (lead == 0xf0) return byte >= 0x90 && byte <= 0xbf;
|
|
if (lead == 0xf4) return byte >= 0x80 && byte <= 0x8f;
|
|
return lead >= 0xf1 && lead <= 0xf3 && Continuation(byte);
|
|
}
|
|
|
|
std::int64_t Skipped(const std::uint8_t* source, std::int64_t lead, std::int64_t length) {
|
|
const auto first = source[lead];
|
|
const auto at = [&](std::int64_t offset) { return lead + offset < length ? source[lead + offset] : 0; };
|
|
if (first >= 0xf0 && first <= 0xf4) {
|
|
if (lead + 1 >= length || !FourByteSecond(first, source[lead + 1])) return 1;
|
|
if (!Continuation(at(2))) return 2;
|
|
if (!Continuation(at(3))) return 3;
|
|
return 4;
|
|
}
|
|
if (first >= 0xe0 && first <= 0xef) {
|
|
if (lead + 1 >= length || !ThreeByteSecond(first, source[lead + 1])) return 1;
|
|
return 2;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int Encoded(char16_t unit) {
|
|
return unit != 0 && unit <= 0x7f ? 1 : unit <= 0x7ff ? 2 : 3;
|
|
}
|
|
|
|
}
|
|
|
|
extern "C" {
|
|
|
|
char16_t* APS5_VABI u_strFromJavaModifiedUTF8WithSub_nid_postfix(char16_t* destination, std::int32_t capacity, std::int32_t* destinationLength,
|
|
const char* text, std::int32_t sourceLength, std::int32_t substitution,
|
|
std::int32_t* substitutions, std::int32_t* status) {
|
|
if (Icu::Failure(status)) return nullptr;
|
|
if ((!text && sourceLength != 0) || sourceLength < -1 || (!destination && capacity != 0) || capacity < 0 ||
|
|
substitution > 0x10ffff || Icu::Surrogate(substitution)) {
|
|
*status = Icu::IllegalArgument;
|
|
return nullptr;
|
|
}
|
|
if (substitutions) *substitutions = 0;
|
|
const auto* source = reinterpret_cast<const std::uint8_t*>(text);
|
|
const std::int64_t length = sourceLength < 0 ? static_cast<std::int64_t>(std::strlen(text)) : sourceLength;
|
|
std::int64_t written = 0;
|
|
std::int64_t extra = 0;
|
|
std::int32_t replaced = 0;
|
|
for (std::int64_t index = 0; index < length;) {
|
|
const auto lead = source[index];
|
|
const bool writing = written < capacity;
|
|
char16_t unit = lead;
|
|
std::int64_t used = 1;
|
|
bool valid = true;
|
|
if (lead >= 0xc0 && lead <= 0xdf && index + 1 < length && Continuation(source[index + 1])) {
|
|
unit = static_cast<char16_t>(((lead & 0x1f) << 6) | (source[index + 1] & 0x3f));
|
|
used = 2;
|
|
} else if (lead >= 0xe0 && lead <= 0xef && index + 2 < length && Continuation(source[index + 1]) && Continuation(source[index + 2])) {
|
|
unit = static_cast<char16_t>(((lead & 0x0f) << 12) | ((source[index + 1] & 0x3f) << 6) | (source[index + 2] & 0x3f));
|
|
used = 3;
|
|
} else if (lead >= 0x80) {
|
|
valid = false;
|
|
}
|
|
if (valid) {
|
|
index += used;
|
|
if (writing) destination[written++] = unit;
|
|
else ++extra;
|
|
continue;
|
|
}
|
|
if (substitution < 0) {
|
|
*status = Icu::InvalidCharacter;
|
|
return nullptr;
|
|
}
|
|
index += Skipped(source, index, length);
|
|
++replaced;
|
|
if (!writing) {
|
|
++extra;
|
|
} else if (substitution <= 0xffff) {
|
|
destination[written++] = static_cast<char16_t>(substitution);
|
|
} else {
|
|
destination[written++] = static_cast<char16_t>(0xd7c0 + (substitution >> 10));
|
|
if (written < capacity) destination[written++] = static_cast<char16_t>(0xdc00 + (substitution & 0x3ff));
|
|
else ++extra;
|
|
}
|
|
}
|
|
const auto required = Icu::Checked(written + extra);
|
|
if (substitutions) *substitutions = replaced;
|
|
if (destinationLength) *destinationLength = required;
|
|
Icu::Terminate(destination, capacity, required, status);
|
|
return destination;
|
|
}
|
|
|
|
|
|
char* APS5_VABI u_strToJavaModifiedUTF8_nid_postfix(char* destination, std::int32_t capacity, std::int32_t* destinationLength,
|
|
const char16_t* source, std::int32_t sourceLength, std::int32_t* status) {
|
|
if (Icu::Failure(status)) return nullptr;
|
|
if ((!source && sourceLength != 0) || sourceLength < -1 || (!destination && capacity != 0) || capacity < 0) {
|
|
*status = Icu::IllegalArgument;
|
|
return nullptr;
|
|
}
|
|
std::int64_t length = sourceLength;
|
|
if (length < 0) for (length = 0; source[length] != 0; ++length) {}
|
|
std::int64_t written = 0;
|
|
std::int64_t extra = 0;
|
|
bool full = false;
|
|
for (std::int64_t index = 0; index < length; ++index) {
|
|
const auto unit = source[index];
|
|
const auto size = Encoded(unit);
|
|
if (!full && capacity - written >= size) {
|
|
if (size == 1) {
|
|
destination[written++] = static_cast<char>(unit);
|
|
} else if (size == 2) {
|
|
destination[written++] = static_cast<char>(0xc0 | (unit >> 6));
|
|
destination[written++] = static_cast<char>(0x80 | (unit & 0x3f));
|
|
} else {
|
|
destination[written++] = static_cast<char>(0xe0 | (unit >> 12));
|
|
destination[written++] = static_cast<char>(0x80 | ((unit >> 6) & 0x3f));
|
|
destination[written++] = static_cast<char>(0x80 | (unit & 0x3f));
|
|
}
|
|
} else {
|
|
full = true;
|
|
extra += size;
|
|
}
|
|
}
|
|
const auto required = Icu::Checked(written + extra);
|
|
if (destinationLength) *destinationLength = required;
|
|
Icu::Terminate(destination, capacity, required, status);
|
|
return destination;
|
|
}
|
|
|
|
}
|