Files
ppsspp/Common/Data/Encoding/Utf16.h
T
mariobob a42ed5e312 Encoding: Report bad UTF-16 surrogates and Shift-JIS lead bytes as invalid
UTF16_Type::next() assumed every surrogate came in a valid pair, so a lone
high surrogate swallowed the unit after it, and one right before the
terminator stepped over it and kept reading. It now returns INVALID and
consumes only the bad unit, which sceCccDecodeUTF16() already checks for.

ShiftJIS::next() returned 0xA0 and 0xFD-0xFF as single-byte characters.
Like 0x80, which it already rejected, they encode nothing (the PSP's JIS
to UCS table in pspautotests/tests/ccc/jis2ucs.bin has no entry for any of
them), so they're INVALID too.

Only sceCcc uses these decoders.
2026-10-16 22:05:12 +02:00

100 lines
2.0 KiB
C++

#pragma once
#include <cstdint>
#include "Common/BitSet.h"
// Should optimize out.
#define UTF16_IS_LITTLE_ENDIAN (*(const uint16_t *)"\0\xff" >= 0x100)
template <bool is_little>
uint16_t UTF16_Swap(uint16_t u) {
if (is_little) {
return UTF16_IS_LITTLE_ENDIAN ? u : swap16(u);
} else {
return UTF16_IS_LITTLE_ENDIAN ? swap16(u) : u;
}
}
template <bool is_little>
struct UTF16_Type {
public:
static const char32_t INVALID = (char32_t)-1;
UTF16_Type(const char16_t *c) : c_(c), index_(0) {}
char32_t next() {
const char32_t u = UTF16_Swap<is_little>(c_[index_++]);
if ((u & 0xF800) != 0xD800) {
return u;
}
// A surrogate is only valid as a high one followed by a low one.
const char32_t low = UTF16_Swap<is_little>(c_[index_]);
if ((u & 0xFC00) != 0xD800 || (low & 0xFC00) != 0xDC00) {
return INVALID;
}
index_++;
return 0x10000 + (((u & 0x3FF) << 10) | (low & 0x3FF));
}
bool end() const {
return c_[index_] == 0;
}
int length() const {
int len = 0;
for (UTF16_Type<is_little> dec(c_); !dec.end(); dec.next())
++len;
return len;
}
int shortIndex() const {
return index_;
}
static int encode(char16_t *dest, char32_t u) {
if (u >= 0x10000) {
u -= 0x10000;
*dest++ = UTF16_Swap<is_little>(0xD800 + ((u >> 10) & 0x3FF));
*dest = UTF16_Swap<is_little>(0xDC00 + ((u >> 0) & 0x3FF));
return 2;
} else {
*dest = UTF16_Swap<is_little>((char16_t)u);
return 1;
}
}
// Rejects non-UCS2 codepoints.
static int encodeUCS2(char16_t *dest, char32_t u) {
if (u >= 0x10000 || (u >= 0xD800 && u <= 0xDFFF)) {
return 0;
} else {
*dest = UTF16_Swap<is_little>((char16_t)u);
return 1;
}
}
static int encodeUnits(char32_t u) {
if (u >= 0x10000) {
return 2;
} else {
return 1;
}
}
static int encodeUnitsUCS2(char32_t u) {
if (u >= 0x10000 || (u >= 0xD800 && u <= 0xDFFF)) {
return 0;
} else {
return 1;
}
}
private:
const char16_t *c_;
int index_;
};
typedef UTF16_Type<true> UTF16LE;
typedef UTF16_Type<false> UTF16BE;