mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-10-22 08:33:12 -05:00
299 lines
11 KiB
C++
299 lines
11 KiB
C++
#include <climits>
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <cstring>
|
|
|
|
#include "prx/libc/include/FileStream.hpp"
|
|
#include "prx/libc/include/general/VabiMacros.hpp"
|
|
#include "prx/libc/include/SonyCrt.hpp"
|
|
|
|
extern "C" {
|
|
std::size_t APS5_VABI mbrtowc_nid_postfix(std::uint16_t* destination, const char* source, std::size_t count, void* state);
|
|
std::size_t APS5_VABI wcrtomb_nid_postfix(char* destination, std::uint16_t value, void* state);
|
|
int APS5_VABI wctomb_nid_postfix(char* destination, std::uint16_t value);
|
|
int APS5_VABI ___mb_cur_max_nid_postfix();
|
|
int APS5_VABI getchar_nid_postfix();
|
|
int APS5_VABI fputs_nid_postfix(const char* str, FileStream* stream);
|
|
int APS5_VABI fputc_nid_postfix(int value, FileStream* stream);
|
|
extern FileStream* __stderrp_nid_postfix;
|
|
[[noreturn]] void abort_nid_postfix(std::uint64_t, std::uint64_t, std::uint64_t, std::uint64_t, std::uint64_t, std::uint64_t);
|
|
}
|
|
|
|
namespace {
|
|
|
|
constexpr int guestEinval = 22;
|
|
constexpr int guestErange = 34;
|
|
constexpr int guestEilseq = 86;
|
|
constexpr int guestEof = -1;
|
|
constexpr std::size_t failed = static_cast<std::size_t>(-1);
|
|
constexpr std::size_t rsizeMax = (std::size_t{1} << 61) - 1;
|
|
constexpr std::size_t guestStateSize = 128;
|
|
|
|
using GuestContextCompare = int (APS5_VABI*)(const void*, const void*, void*);
|
|
|
|
bool usableSize(std::size_t size) {
|
|
return size != 0 && size <= rsizeMax;
|
|
}
|
|
|
|
bool convertFailed(std::size_t length) {
|
|
return length >= failed - 1;
|
|
}
|
|
|
|
int violation(const char* message, int error) {
|
|
SonyInvokeConstraintHandler(message, nullptr, error);
|
|
return error;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
extern "C" {
|
|
|
|
void APS5_VABI abort_handler_s_nid_postfix(const char* message, void*, int) {
|
|
fputs_nid_postfix(message, __stderrp_nid_postfix);
|
|
fputc_nid_postfix('\n', __stderrp_nid_postfix);
|
|
abort_nid_postfix(0, 0, 0, 0, 0, 0);
|
|
}
|
|
|
|
void* APS5_VABI bsearch_s_nid_postfix(const void* key, const void* base, std::size_t count, std::size_t size, GuestContextCompare compare, void* context) {
|
|
if (count > rsizeMax || size > rsizeMax) {
|
|
violation("bsearch_s: bad argument", guestEinval);
|
|
return nullptr;
|
|
}
|
|
if (count == 0) return nullptr;
|
|
if (!key || !base || !compare) {
|
|
violation("bsearch_s: bad argument", guestEinval);
|
|
return nullptr;
|
|
}
|
|
const auto* low = static_cast<const unsigned char*>(base);
|
|
for (std::size_t remaining = count; remaining != 0; remaining >>= 1) {
|
|
const auto* probe = low + (remaining >> 1) * size;
|
|
const int order = compare(key, probe, context);
|
|
if (order == 0) return const_cast<unsigned char*>(probe);
|
|
if (order > 0) {
|
|
low = probe + size;
|
|
--remaining;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
std::uint16_t* APS5_VABI wcstok_s_nid_postfix(std::uint16_t* str, std::size_t* remaining, const std::uint16_t* delimiters, std::uint16_t** context) {
|
|
if (!remaining || !context || !delimiters || *remaining > rsizeMax || (!str && !*context)) {
|
|
violation("wcstok_s: bad argument", guestEinval);
|
|
return nullptr;
|
|
}
|
|
if (!str) str = *context;
|
|
const auto isDelimiter = [delimiters](std::uint16_t value) {
|
|
for (const auto* delimiter = delimiters; *delimiter != 0; ++delimiter)
|
|
if (*delimiter == value) return true;
|
|
return false;
|
|
};
|
|
const std::size_t available = *remaining;
|
|
std::size_t skipped = 0;
|
|
if (available == 0) {
|
|
violation("wcstok_s: missing token", guestEinval);
|
|
return nullptr;
|
|
}
|
|
while (str[skipped] != 0 && isDelimiter(str[skipped])) {
|
|
if (++skipped == available) {
|
|
violation("wcstok_s: missing token", guestEinval);
|
|
return nullptr;
|
|
}
|
|
}
|
|
std::uint16_t* const token = str + skipped;
|
|
const std::size_t left = available - skipped;
|
|
*remaining = left;
|
|
if (*token == 0) {
|
|
*context = token;
|
|
return nullptr;
|
|
}
|
|
std::size_t length = 0;
|
|
while (token[length] != 0 && !isDelimiter(token[length])) {
|
|
if (++length == left) {
|
|
violation("wcstok_s: missing delimiter", guestEinval);
|
|
return nullptr;
|
|
}
|
|
}
|
|
std::uint16_t* next = token + length;
|
|
*remaining = left - length;
|
|
if (*next != 0) {
|
|
*next++ = 0;
|
|
if (left != length) *remaining = left - length - 1;
|
|
}
|
|
*context = next;
|
|
return token;
|
|
}
|
|
|
|
int APS5_VABI wctomb_s_nid_postfix(int* status, char* destination, std::size_t capacity, std::uint16_t wide) {
|
|
if (!status || (!destination && capacity) || (destination && capacity > rsizeMax))
|
|
return violation("wctomb_s: bad argument", guestEinval);
|
|
if (destination && static_cast<std::size_t>(___mb_cur_max_nid_postfix()) > capacity) {
|
|
char bytes[MB_LEN_MAX];
|
|
const int length = wctomb_nid_postfix(bytes, wide);
|
|
if (length > 0 && static_cast<std::size_t>(length) > capacity) return violation("wctomb_s: buffer too small", guestErange);
|
|
if (length > 0) std::memcpy(destination, bytes, static_cast<std::size_t>(length));
|
|
*status = length;
|
|
return length < 0 ? guestEilseq : 0;
|
|
}
|
|
const int length = wctomb_nid_postfix(destination, wide);
|
|
*status = length;
|
|
return destination && length < 0 ? guestEilseq : 0;
|
|
}
|
|
|
|
int APS5_VABI wcrtomb_s_nid_postfix(std::size_t* result, char* destination, std::size_t capacity, std::uint16_t wide, void* state) {
|
|
if (!result || !state) {
|
|
if (destination && usableSize(capacity)) destination[0] = '\0';
|
|
if (result) *result = failed;
|
|
return violation("wcrtomb_s: bad argument", guestEinval);
|
|
}
|
|
if ((!destination && capacity) || (destination && !usableSize(capacity))) {
|
|
*result = failed;
|
|
return violation("wcrtomb_s: bad argument", guestEinval);
|
|
}
|
|
char bytes[MB_LEN_MAX];
|
|
std::size_t length = 0;
|
|
if (!destination) {
|
|
length = wcrtomb_nid_postfix(bytes, 0, state);
|
|
} else if (static_cast<std::size_t>(___mb_cur_max_nid_postfix()) > capacity) {
|
|
length = wcrtomb_nid_postfix(bytes, wide, state);
|
|
if (length != failed && length > capacity) {
|
|
*result = failed;
|
|
destination[0] = '\0';
|
|
return violation("wcrtomb_s: buffer too small", guestErange);
|
|
}
|
|
if (length != failed) std::memcpy(destination, bytes, length);
|
|
} else {
|
|
length = wcrtomb_nid_postfix(destination, wide, state);
|
|
}
|
|
*result = length;
|
|
return length == failed ? guestEilseq : 0;
|
|
}
|
|
|
|
int APS5_VABI mbstowcs_s_nid_postfix(std::size_t* result, std::uint16_t* destination, std::size_t capacity, const char* source, std::size_t limit) {
|
|
if (!result || !source || (!destination && capacity) || (destination && (!usableSize(capacity) || limit > rsizeMax))) {
|
|
if (result) *result = failed;
|
|
if (destination && usableSize(capacity)) destination[0] = 0;
|
|
return violation("mbstowcs_s: bad argument", guestEinval);
|
|
}
|
|
alignas(8) unsigned char state[guestStateSize]{};
|
|
if (!destination) {
|
|
std::size_t count = 0;
|
|
for (;; ++count) {
|
|
std::uint16_t wide = 0;
|
|
const std::size_t length = mbrtowc_nid_postfix(&wide, source, INT_MAX, state);
|
|
if (convertFailed(length)) {
|
|
*result = failed;
|
|
return guestEilseq;
|
|
}
|
|
if (length == 0 && wide == 0) break;
|
|
source += length;
|
|
}
|
|
*result = count;
|
|
return 0;
|
|
}
|
|
const std::size_t total = limit < capacity ? limit : capacity;
|
|
std::size_t index = 0;
|
|
while (index < total) {
|
|
const std::size_t length = mbrtowc_nid_postfix(destination + index, source, INT_MAX, state);
|
|
if (convertFailed(length)) {
|
|
*result = failed;
|
|
destination[index] = 0;
|
|
return guestEilseq;
|
|
}
|
|
if (length == 0 && destination[index] == 0) {
|
|
*result = index;
|
|
return 0;
|
|
}
|
|
source += length;
|
|
++index;
|
|
}
|
|
if (index == capacity) {
|
|
*result = failed;
|
|
destination[0] = 0;
|
|
return violation("mbstowcs_s: buffer overflow", guestErange);
|
|
}
|
|
*result = index;
|
|
destination[index] = 0;
|
|
return 0;
|
|
}
|
|
|
|
int APS5_VABI mbsrtowcs_s_nid_postfix(std::size_t* result, std::uint16_t* destination, std::size_t capacity, const char** source, std::size_t limit, void* state) {
|
|
if (!result || !source || !*source || !state || (!destination && capacity) || (destination && (!usableSize(capacity) || limit > rsizeMax))) {
|
|
if (destination && usableSize(capacity)) destination[0] = 0;
|
|
if (result) *result = failed;
|
|
return violation("mbsrtowcs_s: bad argument", guestEinval);
|
|
}
|
|
const char* cursor = *source;
|
|
if (!destination) {
|
|
std::size_t count = 0;
|
|
for (;; ++count) {
|
|
std::uint16_t wide = 0;
|
|
const std::size_t length = mbrtowc_nid_postfix(&wide, cursor, INT_MAX, state);
|
|
if (convertFailed(length)) {
|
|
*result = failed;
|
|
return guestEilseq;
|
|
}
|
|
if (length == 0 && wide == 0) break;
|
|
cursor += length;
|
|
}
|
|
*result = count;
|
|
return 0;
|
|
}
|
|
const std::size_t total = limit < capacity ? limit : capacity;
|
|
std::size_t index = 0;
|
|
while (index < total) {
|
|
const std::size_t length = mbrtowc_nid_postfix(destination + index, cursor, INT_MAX, state);
|
|
if (convertFailed(length)) {
|
|
*source = cursor;
|
|
destination[index] = 0;
|
|
*result = failed;
|
|
return guestEilseq;
|
|
}
|
|
if (length == 0 && destination[index] == 0) {
|
|
*source = nullptr;
|
|
*result = index;
|
|
return 0;
|
|
}
|
|
cursor += length;
|
|
++index;
|
|
}
|
|
if (total != 0) *source = cursor;
|
|
if (index == capacity) {
|
|
destination[0] = 0;
|
|
*result = failed;
|
|
return violation("mbsrtowcs_s: buffer too small", guestErange);
|
|
}
|
|
destination[index] = 0;
|
|
*result = index;
|
|
return 0;
|
|
}
|
|
|
|
char* APS5_VABI gets_s_nid_postfix(char* buffer, std::size_t size) {
|
|
if (size == 0) {
|
|
violation("gets_s: zero buffer size", guestEinval);
|
|
return nullptr;
|
|
}
|
|
if (!buffer || size > rsizeMax) {
|
|
violation("gets_s: bad argument", guestEinval);
|
|
return nullptr;
|
|
}
|
|
char* cursor = buffer;
|
|
int value = getchar_nid_postfix();
|
|
for (std::size_t left = size - 1; left != 0 && value != guestEof && value != '\n'; --left) {
|
|
*cursor++ = static_cast<char>(value);
|
|
value = getchar_nid_postfix();
|
|
}
|
|
if (value == '\n' || (value == guestEof && cursor != buffer)) {
|
|
*cursor = '\0';
|
|
return buffer;
|
|
}
|
|
if (value != guestEof) {
|
|
violation("gets_s: short buffer", guestErange);
|
|
while (value != guestEof && value != '\n') value = getchar_nid_postfix();
|
|
}
|
|
buffer[0] = '\0';
|
|
return nullptr;
|
|
}
|
|
|
|
}
|