mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-10-22 00:22:57 -05:00
Unreal titles end in their own libc fatal path with the UTF-16 LowLevelFatalError message in a register, and the crash report shows only the first 64 bytes of it as hex words, so the message is lost (#4238: Demon Slayer PPSA03490, Stray PPSA02100). For every general register that points at readable memory starting with at least four printable ASCII characters, as UTF-16LE or as single bytes, the report now prints the text up to a NUL, the first other character, 512 characters or the end of readable memory (each page is checked with VirtualQuery before it is read). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
60 lines
2.1 KiB
C++
60 lines
2.1 KiB
C++
#ifndef CORE_LIBS_PRX_LIBC_INCLUDE_CRASHTEXT_HPP
|
|
#define CORE_LIBS_PRX_LIBC_INCLUDE_CRASHTEXT_HPP
|
|
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
|
|
namespace crash_text {
|
|
|
|
constexpr std::size_t MaximumCharacters = 512;
|
|
constexpr std::size_t MinimumCharacters = 4;
|
|
constexpr std::size_t MaximumBytes = 2 * (MaximumCharacters + 1);
|
|
|
|
inline std::uint16_t Unit(const std::uint8_t* bytes, std::size_t index, std::size_t width) {
|
|
if (width == 1) return bytes[index];
|
|
return static_cast<std::uint16_t>(bytes[2 * index] | bytes[2 * index + 1] << 8);
|
|
}
|
|
|
|
inline bool IsText(std::uint16_t unit) {
|
|
return (unit >= 0x20 && unit < 0x7f) || unit == '\t' || unit == '\n' || unit == '\r';
|
|
}
|
|
|
|
inline bool StartsWithText(const std::uint8_t* bytes, std::size_t size, std::size_t width) {
|
|
if (size < MinimumCharacters * width) return false;
|
|
for (std::size_t index = 0; index < MinimumCharacters; ++index) {
|
|
if (!IsText(Unit(bytes, index, width))) return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
inline bool Format(const std::uint8_t* bytes, std::size_t size, char* out, std::size_t capacity) {
|
|
const std::size_t width = StartsWithText(bytes, size, 2) ? 2 : StartsWithText(bytes, size, 1) ? 1 : 0;
|
|
if (width == 0 || capacity < 8) return false;
|
|
std::size_t length = 0;
|
|
if (width == 2) out[length++] = 'u';
|
|
out[length++] = '"';
|
|
bool terminated = false;
|
|
for (std::size_t index = 0; (index + 1) * width <= size; ++index) {
|
|
const std::uint16_t unit = Unit(bytes, index, width);
|
|
if (unit == 0) {
|
|
terminated = true;
|
|
break;
|
|
}
|
|
if (index == MaximumCharacters || !IsText(unit)) break;
|
|
const char escape = unit == '\n' ? 'n' : unit == '\r' ? 'r' : unit == '\t' ? 't' : unit == '"' || unit == '\\' ? static_cast<char>(unit) : '\0';
|
|
if (length + (escape != '\0' ? 2 : 1) + 5 > capacity) break;
|
|
if (escape != '\0') out[length++] = '\\';
|
|
out[length++] = escape != '\0' ? escape : static_cast<char>(unit);
|
|
}
|
|
out[length++] = '"';
|
|
if (!terminated) {
|
|
for (int dot = 0; dot < 3; ++dot) out[length++] = '.';
|
|
}
|
|
out[length] = '\0';
|
|
return true;
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|