mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-11-02 12:56:05 +00:00
148 lines
5.6 KiB
C++
148 lines
5.6 KiB
C++
#include "prx/libSceIcu/libSceIcu.hpp"
|
|
#include "prx/libc/include/General.hpp"
|
|
#include <algorithm>
|
|
#include <array>
|
|
#include <cstdint>
|
|
#include <limits>
|
|
|
|
namespace {
|
|
|
|
bool IsTrail(std::uint8_t value) {
|
|
return value >= 0x80 && value <= 0xbf;
|
|
}
|
|
|
|
int TrailCount(std::int32_t lead) {
|
|
if (lead < 0xc2 || lead > 0xf4) return 0;
|
|
return lead < 0xe0 ? 1 : lead < 0xf0 ? 2 : 3;
|
|
}
|
|
|
|
bool FirstTrail(std::int32_t lead, std::uint8_t trail, bool allowSurrogate) {
|
|
if (!IsTrail(trail)) return false;
|
|
if (lead == 0xe0) return trail >= 0xa0;
|
|
if (lead == 0xed && !allowSurrogate) return trail < 0xa0;
|
|
if (lead == 0xf0) return trail >= 0x90;
|
|
if (lead == 0xf4) return trail < 0x90;
|
|
return true;
|
|
}
|
|
|
|
bool Noncharacter(std::int32_t value) {
|
|
return (value >= 0xfdd0 && value <= 0xfdef) || (value & 0xffff) >= 0xfffe;
|
|
}
|
|
|
|
std::int32_t ErrorValue(int trails, std::int8_t strict) {
|
|
if (strict < 0) return strict == -3 ? 0xfffd : -1;
|
|
constexpr std::array<std::int32_t, 4> values{0x15, 0x9f, 0xffff, 0x10ffff};
|
|
return values[trails];
|
|
}
|
|
|
|
std::uint8_t Read(const std::uint8_t* text, std::int32_t index) {
|
|
if (!text) APS5_INVALID_ARG_EX;
|
|
return text[index];
|
|
}
|
|
|
|
void Advance(std::int32_t& index) {
|
|
if (index == std::numeric_limits<std::int32_t>::max()) APS5_INVALID_ARG_EX;
|
|
++index;
|
|
}
|
|
|
|
struct Prefix {
|
|
std::int32_t index;
|
|
std::int32_t value = 0;
|
|
int trails = 0;
|
|
bool complete = false;
|
|
};
|
|
|
|
Prefix PreviousPrefix(const std::uint8_t* text, std::int32_t start, std::int32_t index, std::uint8_t last, bool allowSurrogate) {
|
|
if (!IsTrail(last)) return {index};
|
|
std::array<std::uint8_t, 3> trails{last};
|
|
auto position = index;
|
|
for (int count = 1; count <= 3 && position > start; ++count) {
|
|
const auto byte = Read(text, --position);
|
|
if (IsTrail(byte)) {
|
|
if (count < 3) trails[count] = byte;
|
|
continue;
|
|
}
|
|
const int needed = TrailCount(byte);
|
|
if (needed < count || !FirstTrail(byte, trails[count - 1], allowSurrogate && count == 2 && needed == 2)) break;
|
|
std::int32_t value = byte & (0x7f >> needed);
|
|
for (int trail = count - 1; trail >= 0; --trail) value = (value << 6) | (trails[trail] & 0x3f);
|
|
return {position, value, count, count == needed};
|
|
}
|
|
return {index};
|
|
}
|
|
|
|
void CheckBackward(std::int32_t start, std::int32_t index) {
|
|
if (start < 0 || index < start) APS5_INVALID_ARG_EX;
|
|
}
|
|
|
|
void Write(std::uint8_t* text, std::int32_t index, std::uint32_t value, int width) {
|
|
if (!text) APS5_INVALID_ARG_EX;
|
|
for (int offset = width - 1; offset > 0; --offset) {
|
|
text[index + offset] = static_cast<std::uint8_t>(0x80 | (value & 0x3f));
|
|
value >>= 6;
|
|
}
|
|
constexpr std::array<std::uint8_t, 5> markers{0, 0, 0xc0, 0xe0, 0xf0};
|
|
text[index] = static_cast<std::uint8_t>(markers[width] | value);
|
|
}
|
|
|
|
}
|
|
|
|
extern "C" {
|
|
|
|
std::int32_t APS5_VABI utf8_nextCharSafeBody_nid_postfix(const std::uint8_t* text, std::int32_t* index, std::int32_t length, std::int32_t lead, std::int8_t strict) {
|
|
if (!index || *index < 0 || (length >= 0 && *index > length)) APS5_INVALID_ARG_EX;
|
|
const auto original = *index;
|
|
auto position = original;
|
|
const int needed = TrailCount(lead);
|
|
if (!needed || position == length) return ErrorValue(0, strict);
|
|
std::int32_t value = lead & (0x7f >> needed);
|
|
for (int trail = 0; trail < needed && position != length; ++trail) {
|
|
const auto byte = Read(text, position);
|
|
if (trail == 0 ? !FirstTrail(lead, byte, strict == -2) : !IsTrail(byte)) break;
|
|
value = (value << 6) | (byte & 0x3f);
|
|
Advance(position);
|
|
}
|
|
*index = position;
|
|
const int consumed = position - original;
|
|
if (consumed == needed && (strict <= 0 || !Noncharacter(value))) return value;
|
|
return ErrorValue(consumed, strict);
|
|
}
|
|
|
|
std::int32_t APS5_VABI utf8_prevCharSafeBody_nid_postfix(const std::uint8_t* text, std::int32_t start, std::int32_t* index, std::int32_t trail, std::int8_t strict) {
|
|
if (!index) APS5_INVALID_ARG_EX;
|
|
CheckBackward(start, *index);
|
|
const auto prefix = PreviousPrefix(text, start, *index, static_cast<std::uint8_t>(trail), strict == -2);
|
|
*index = prefix.index;
|
|
if (prefix.complete && (strict <= 0 || !Noncharacter(prefix.value))) return prefix.value;
|
|
return ErrorValue(prefix.trails, strict);
|
|
}
|
|
|
|
std::int32_t APS5_VABI utf8_appendCharSafeBody_nid_postfix(std::uint8_t* text, std::int32_t index, std::int32_t capacity, std::int32_t value, std::int8_t* isError) {
|
|
if (index < 0) APS5_INVALID_ARG_EX;
|
|
const auto scalar = static_cast<std::uint32_t>(value);
|
|
const int width = scalar <= 0x7ff ? 2 : scalar <= 0xffff ? 3 : 4;
|
|
if (scalar <= 0x10ffff && index > std::numeric_limits<std::int32_t>::max() - (width - 1)) APS5_INVALID_ARG_EX;
|
|
const std::int64_t available = static_cast<std::int64_t>(capacity) - index;
|
|
if (scalar <= 0x10ffff && (scalar < 0xd800 || scalar > 0xdfff) && available >= width) {
|
|
Write(text, index, scalar, width);
|
|
return index + width;
|
|
}
|
|
if (isError) {
|
|
*isError = 1;
|
|
} else {
|
|
if (available < std::numeric_limits<std::int32_t>::min()) APS5_INVALID_ARG_EX;
|
|
if (available <= 0) return index;
|
|
const auto count = static_cast<int>(std::min<std::int64_t>(available, 3));
|
|
Write(text, index, static_cast<std::uint32_t>(ErrorValue(count - 1, 0)), count);
|
|
return index + count;
|
|
}
|
|
return index;
|
|
}
|
|
|
|
std::int32_t APS5_VABI utf8_back1SafeBody_nid_postfix(const std::uint8_t* text, std::int32_t start, std::int32_t index) {
|
|
CheckBackward(start, index);
|
|
return PreviousPrefix(text, start, index, Read(text, index), false).index;
|
|
}
|
|
|
|
}
|