mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-10-27 00:28:14 +00:00
feat(libSceJxr): implement pixel format lookup
This commit is contained in:
1 parent
2cce8bc51d
commit
5aa1f6fd44
12 files changed
+447
-10
No files matched your search
@@ -61,3 +61,6 @@
|
||||
[submodule "3rdparty/vorbis"]
|
||||
path = 3rdparty/vorbis
|
||||
url = https://github.com/xiph/vorbis.git
|
||||
[submodule "3rdparty/jxrlib"]
|
||||
path = 3rdparty/jxrlib
|
||||
url = https://github.com/4creators/jxrlib.git
|
||||
+1
Submodule 3rdparty/jxrlib added at f752187986.
@@ -385,6 +385,7 @@ set_target_properties(usb-1.0 PROPERTIES POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
include(cmake/ZArchive.cmake)
|
||||
include(cmake/Opus.cmake)
|
||||
include(cmake/Jxr.cmake)
|
||||
if(WIN32)
|
||||
set(MI_BUILD_SHARED ON CACHE BOOL "" FORCE)
|
||||
set(MI_BUILD_STATIC OFF CACHE BOOL "" FORCE)
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
enable_language(C)
|
||||
set(JXR_SOURCE_DIR ${CMAKE_SOURCE_DIR}/3rdparty/jxrlib)
|
||||
|
||||
function(configure_jxr_native target)
|
||||
target_include_directories(${target} SYSTEM PRIVATE
|
||||
${JXR_SOURCE_DIR}/common/include ${JXR_SOURCE_DIR}/image/sys ${JXR_SOURCE_DIR}/jxrgluelib)
|
||||
target_compile_definitions(${target} PRIVATE DISABLE_PERF_MEASUREMENT)
|
||||
if(WIN32)
|
||||
target_compile_definitions(${target} PRIVATE WIN32 __in=)
|
||||
target_compile_options(${target} PRIVATE -include ${JXR_SOURCE_DIR}/image/sys/strcodec.h)
|
||||
else()
|
||||
target_compile_definitions(${target} PRIVATE __ANSI__)
|
||||
endif()
|
||||
set_target_properties(${target} PROPERTIES C_STANDARD 99 C_STANDARD_REQUIRED ON
|
||||
POSITION_INDEPENDENT_CODE ON C_VISIBILITY_PRESET hidden)
|
||||
endfunction()
|
||||
|
||||
add_library(jxr STATIC EXCLUDE_FROM_ALL
|
||||
${JXR_SOURCE_DIR}/image/sys/adapthuff.c
|
||||
${JXR_SOURCE_DIR}/image/sys/image.c
|
||||
${JXR_SOURCE_DIR}/image/sys/strcodec.c
|
||||
${JXR_SOURCE_DIR}/image/sys/strPredQuant.c
|
||||
${JXR_SOURCE_DIR}/image/sys/strTransform.c
|
||||
${JXR_SOURCE_DIR}/image/decode/decode.c
|
||||
${JXR_SOURCE_DIR}/image/decode/JXRTranscode.c
|
||||
${JXR_SOURCE_DIR}/image/decode/postprocess.c
|
||||
${JXR_SOURCE_DIR}/image/decode/segdec.c
|
||||
${JXR_SOURCE_DIR}/image/decode/strdec.c
|
||||
${JXR_SOURCE_DIR}/image/decode/strInvTransform.c
|
||||
${JXR_SOURCE_DIR}/image/decode/strPredQuantDec.c
|
||||
${JXR_SOURCE_DIR}/image/encode/encode.c
|
||||
${JXR_SOURCE_DIR}/image/encode/segenc.c
|
||||
${JXR_SOURCE_DIR}/image/encode/strenc.c
|
||||
${JXR_SOURCE_DIR}/image/encode/strFwdTransform.c
|
||||
${JXR_SOURCE_DIR}/image/encode/strPredQuantEnc.c
|
||||
${JXR_SOURCE_DIR}/jxrgluelib/JXRGlue.c
|
||||
${JXR_SOURCE_DIR}/jxrgluelib/JXRGlueJxr.c
|
||||
${JXR_SOURCE_DIR}/jxrgluelib/JXRGluePFC.c
|
||||
${JXR_SOURCE_DIR}/jxrgluelib/JXRMeta.c)
|
||||
configure_jxr_native(jxr)
|
||||
if(UNIX)
|
||||
target_link_libraries(jxr PRIVATE m)
|
||||
endif()
|
||||
@@ -1,8 +1,38 @@
|
||||
add_library(libSceJxr SHARED EXCLUDE_FROM_ALL Export.cpp)
|
||||
add_library(jxr_lookup_native STATIC src/NativeLookup.c)
|
||||
configure_jxr_native(jxr_lookup_native)
|
||||
target_include_directories(jxr_lookup_native PRIVATE ${LIBS_INCLUDE_DIR})
|
||||
target_link_libraries(jxr_lookup_native PRIVATE jxr)
|
||||
add_library(libSceJxr SHARED EXCLUDE_FROM_ALL Export.cpp src/PixelFormatLookup.cpp)
|
||||
target_link_libraries(libSceJxr PRIVATE jxr_lookup_native)
|
||||
if(NOT APPLE)
|
||||
target_link_options(libSceJxr PRIVATE LINKER:--exclude-libs,ALL)
|
||||
endif()
|
||||
target_include_directories(libSceJxr PRIVATE ${LIBS_INCLUDE_DIR})
|
||||
configure_windows_unwind(libSceJxr)
|
||||
|
||||
if(BUILD_TESTING)
|
||||
add_library(jxr_lookup_reference STATIC ${LIBS_INCLUDE_DIR}/tests/GuestJxrLookupReference.c)
|
||||
configure_jxr_native(jxr_lookup_reference)
|
||||
target_link_libraries(jxr_lookup_reference PRIVATE jxr)
|
||||
add_test_executable(guest_jxr_lookup_tests ${LIBS_INCLUDE_DIR}/tests/GuestJxrLookup.cpp)
|
||||
target_include_directories(guest_jxr_lookup_tests PRIVATE ${LIBS_INCLUDE_DIR})
|
||||
target_link_libraries(guest_jxr_lookup_tests PRIVATE jxr_lookup_reference ${CMAKE_DL_LIBS})
|
||||
if(WIN32)
|
||||
target_link_libraries(guest_jxr_lookup_tests PRIVATE libc)
|
||||
endif()
|
||||
configure_windows_unwind(guest_jxr_lookup_tests)
|
||||
add_custom_target(jxr_lookup_patched DEPENDS ${PATCHED_DIR}/libSceJxr.prx ${LIBC_PATCHED})
|
||||
add_dependencies(guest_jxr_lookup_tests jxr_lookup_patched)
|
||||
add_test(NAME guest_jxr_lookup_reference COMMAND guest_jxr_lookup_tests reference)
|
||||
add_test(NAME guest_jxr_lookup COMMAND guest_jxr_lookup_tests $<TARGET_FILE:libSceJxr> raw)
|
||||
add_test(NAME guest_jxr_lookup_patched COMMAND guest_jxr_lookup_tests ${PATCHED_DIR}/libSceJxr.prx patched)
|
||||
set_tests_properties(guest_jxr_lookup_reference guest_jxr_lookup guest_jxr_lookup_patched PROPERTIES TIMEOUT 20)
|
||||
if(WIN32)
|
||||
set_tests_properties(guest_jxr_lookup_reference guest_jxr_lookup PROPERTIES
|
||||
ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceJxr>")
|
||||
set_tests_properties(guest_jxr_lookup_patched PROPERTIES
|
||||
ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:${PATCHED_DIR}")
|
||||
endif()
|
||||
add_test_executable(guest_jxr_tests ${LIBS_INCLUDE_DIR}/tests/GuestJxr.cpp)
|
||||
target_include_directories(guest_jxr_tests PRIVATE ${LIBS_INCLUDE_DIR})
|
||||
target_link_libraries(guest_jxr_tests PRIVATE libSceJxr libc)
|
||||
|
||||
@@ -24,11 +24,6 @@ std::int64_t APS5_VABI PKFree_nid_postfix(void** memory) {
|
||||
return WmpErrSuccess;
|
||||
}
|
||||
|
||||
int APS5_VABI PixelFormatLookup_nid_postfix(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::int64_t APS5_VABI readIS_nid_postfix(void*, Jxr::BitIo* io) {
|
||||
if (!io) NotImplemented_nid_no_patch(__func__);
|
||||
constexpr std::uintptr_t packetSize = 4096;
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct JxrPixelInfo {
|
||||
const void* guid;
|
||||
uint64_t channels;
|
||||
int32_t colorFormat;
|
||||
int32_t bitDepth;
|
||||
uint32_t bitsPerUnit;
|
||||
uint64_t flags;
|
||||
uint32_t interpretation;
|
||||
uint32_t samplesPerPixel;
|
||||
uint32_t bitsPerSample;
|
||||
uint32_t sampleFormat;
|
||||
} JxrPixelInfo;
|
||||
@@ -0,0 +1,40 @@
|
||||
#include "strcodec.h"
|
||||
#include "JXRGlue.h"
|
||||
#include "prx/libSceJxr/GuestPixelInfo.h"
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
_Static_assert(sizeof(GUID) == 16, "GUID size");
|
||||
_Static_assert(sizeof(COLORFORMAT) == 4 && sizeof(BITDEPTH_BITS) == 4, "enum size");
|
||||
_Static_assert(sizeof(U32) == 4 && sizeof(JxrPixelInfo) == 56, "pixel info layout");
|
||||
_Static_assert(offsetof(JxrPixelInfo, flags) == 32 && offsetof(JxrPixelInfo, interpretation) == 40, "LP64 offsets");
|
||||
|
||||
int64_t JxrLookup_nid_no_patch(JxrPixelInfo* value, uint8_t mode) {
|
||||
PKPixelInfo native = {0};
|
||||
GUID guid;
|
||||
ERR result;
|
||||
if (mode > LOOKUP_BACKWARD_TIF) return PixelFormatLookup(NULL, mode);
|
||||
if (mode == LOOKUP_FORWARD) {
|
||||
memcpy(&guid, value->guid, sizeof(guid));
|
||||
native.pGUIDPixFmt = &guid;
|
||||
} else {
|
||||
native.grBit = (WMP_GRBIT)value->flags;
|
||||
native.uInterpretation = value->interpretation;
|
||||
native.uSamplePerPixel = value->samplesPerPixel;
|
||||
native.uBitsPerSample = value->bitsPerSample;
|
||||
native.uSampleFormat = value->sampleFormat;
|
||||
}
|
||||
result = PixelFormatLookup(&native, mode);
|
||||
if (result != WMP_errSuccess) return result;
|
||||
value->guid = native.pGUIDPixFmt;
|
||||
value->channels = native.cChannel;
|
||||
value->colorFormat = native.cfColorFormat;
|
||||
value->bitDepth = native.bdBitDepth;
|
||||
value->bitsPerUnit = native.cbitUnit;
|
||||
value->flags = native.grBit;
|
||||
value->interpretation = native.uInterpretation;
|
||||
value->samplesPerPixel = native.uSamplePerPixel;
|
||||
value->bitsPerSample = native.uBitsPerSample;
|
||||
value->sampleFormat = native.uSampleFormat;
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
#include "prx/libSceJxr/GuestPixelInfo.h"
|
||||
#include "prx/libc/include/general/VabiMacros.hpp"
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <stdexcept>
|
||||
|
||||
extern "C" std::int64_t JxrLookup_nid_no_patch(JxrPixelInfo* value, std::uint8_t mode);
|
||||
|
||||
static_assert(sizeof(JxrPixelInfo) == 56 && alignof(JxrPixelInfo) == 8);
|
||||
static_assert(offsetof(JxrPixelInfo, flags) == 32 && offsetof(JxrPixelInfo, interpretation) == 40);
|
||||
|
||||
extern "C" std::int64_t APS5_VABI PixelFormatLookup_nid_postfix(JxrPixelInfo* value, std::uint8_t mode) {
|
||||
if (mode > 1) return JxrLookup_nid_no_patch(nullptr, mode);
|
||||
if (value == nullptr) throw std::invalid_argument("PixelFormatLookup: null record behavior is unknown");
|
||||
JxrPixelInfo local{};
|
||||
auto* bytes = reinterpret_cast<std::byte*>(value);
|
||||
if (mode == 0) {
|
||||
std::memcpy(&local.guid, bytes, sizeof(local.guid));
|
||||
if (local.guid == nullptr) throw std::invalid_argument("PixelFormatLookup: null GUID behavior is unknown");
|
||||
} else {
|
||||
std::memcpy(&local.flags, bytes + offsetof(JxrPixelInfo, flags), sizeof(local.flags));
|
||||
std::memcpy(&local.interpretation, bytes + offsetof(JxrPixelInfo, interpretation), 4 * sizeof(std::uint32_t));
|
||||
}
|
||||
const auto result = JxrLookup_nid_no_patch(&local, mode);
|
||||
if (result == 0) {
|
||||
std::memcpy(bytes, &local, offsetof(JxrPixelInfo, bitsPerUnit) + sizeof(local.bitsPerUnit));
|
||||
std::memcpy(bytes + offsetof(JxrPixelInfo, flags), &local.flags, sizeof(local) - offsetof(JxrPixelInfo, flags));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,249 @@
|
||||
#include "prx/libSceJxr/GuestPixelInfo.h"
|
||||
#include "prx/libc/include/general/VabiMacros.hpp"
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <exception>
|
||||
#include <stdexcept>
|
||||
#include <thread>
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <dlfcn.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
extern "C" int JxrReferenceCase_nid_no_patch(std::uint8_t, int, int, std::uint8_t*, std::uint64_t*);
|
||||
using Query = std::int64_t (APS5_VABI*)(JxrPixelInfo*, std::uint8_t);
|
||||
static_assert(sizeof(JxrPixelInfo) == 56 && alignof(JxrPixelInfo) == 8);
|
||||
static_assert(offsetof(JxrPixelInfo, guid) == 0 && offsetof(JxrPixelInfo, channels) == 8);
|
||||
static_assert(offsetof(JxrPixelInfo, colorFormat) == 16 && offsetof(JxrPixelInfo, bitDepth) == 20);
|
||||
static_assert(offsetof(JxrPixelInfo, bitsPerUnit) == 24 && offsetof(JxrPixelInfo, flags) == 32);
|
||||
static_assert(offsetof(JxrPixelInfo, interpretation) == 40 && offsetof(JxrPixelInfo, sampleFormat) == 52);
|
||||
|
||||
namespace {
|
||||
|
||||
void Require(bool condition, const char* message) {
|
||||
if (condition) return;
|
||||
std::fprintf(stderr, "JXR lookup: %s\n", message);
|
||||
std::exit(1);
|
||||
}
|
||||
|
||||
struct Reference {
|
||||
std::array<std::uint8_t, 16> guid{};
|
||||
std::array<std::uint64_t, 9> fields{};
|
||||
};
|
||||
|
||||
bool ReadReference(unsigned hash, bool reverse, bool white, Reference& expected) {
|
||||
const auto result = JxrReferenceCase_nid_no_patch(static_cast<std::uint8_t>(hash), reverse, white,
|
||||
expected.guid.data(), expected.fields.data());
|
||||
Require(result >= 0, "native lookup reference failed");
|
||||
return result != 0;
|
||||
}
|
||||
|
||||
JxrPixelInfo BackwardInput(const Reference& source) {
|
||||
JxrPixelInfo input{};
|
||||
input.guid = reinterpret_cast<void*>(std::uintptr_t{1});
|
||||
input.channels = UINT64_MAX;
|
||||
input.colorFormat = -1;
|
||||
input.bitDepth = -1;
|
||||
input.bitsPerUnit = UINT32_MAX;
|
||||
input.flags = source.fields[4] | UINT64_C(0xfedcba9880000040);
|
||||
input.interpretation = static_cast<std::uint32_t>(source.fields[5]);
|
||||
input.samplesPerPixel = static_cast<std::uint32_t>(source.fields[6]);
|
||||
input.bitsPerSample = static_cast<std::uint32_t>(source.fields[7]);
|
||||
input.sampleFormat = static_cast<std::uint32_t>(source.fields[8]);
|
||||
return input;
|
||||
}
|
||||
|
||||
void CheckValue(const JxrPixelInfo& actual, const Reference& expected) {
|
||||
const std::array<std::uint64_t, 9> fields{{actual.channels, static_cast<std::uint32_t>(actual.colorFormat),
|
||||
static_cast<std::uint32_t>(actual.bitDepth), actual.bitsPerUnit, actual.flags, actual.interpretation,
|
||||
actual.samplesPerPixel, actual.bitsPerSample, actual.sampleFormat}};
|
||||
Require(fields == expected.fields, "metadata differs from pinned native reference");
|
||||
Require(actual.guid != nullptr, "successful lookup returned null GUID");
|
||||
Require(std::memcmp(actual.guid, expected.guid.data(), 16) == 0, "GUID differs from pinned native reference");
|
||||
}
|
||||
|
||||
class GuardedTail {
|
||||
void* memory;
|
||||
std::size_t page;
|
||||
void* data;
|
||||
public:
|
||||
GuardedTail(const void* source, std::size_t bytes) {
|
||||
#ifdef _WIN32
|
||||
SYSTEM_INFO info;
|
||||
GetSystemInfo(&info);
|
||||
page = info.dwPageSize;
|
||||
memory = VirtualAlloc(nullptr, page * 2, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
|
||||
Require(memory != nullptr, "guard allocation");
|
||||
DWORD previous;
|
||||
Require(VirtualProtect(static_cast<unsigned char*>(memory) + page, page, PAGE_NOACCESS, &previous) != 0, "guard protection");
|
||||
#else
|
||||
page = static_cast<std::size_t>(sysconf(_SC_PAGESIZE));
|
||||
memory = mmap(nullptr, page * 2, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
Require(memory != MAP_FAILED, "guard allocation");
|
||||
Require(mprotect(static_cast<unsigned char*>(memory) + page, page, PROT_NONE) == 0, "guard protection");
|
||||
#endif
|
||||
Require(bytes <= page, "guard fixture size");
|
||||
data = static_cast<unsigned char*>(memory) + page - bytes;
|
||||
std::memcpy(data, source, bytes);
|
||||
}
|
||||
void ReadOnly() {
|
||||
#ifdef _WIN32
|
||||
DWORD previous;
|
||||
Require(VirtualProtect(memory, page, PAGE_READONLY, &previous) != 0, "read-only protection");
|
||||
#else
|
||||
Require(mprotect(memory, page, PROT_READ) == 0, "read-only protection");
|
||||
#endif
|
||||
}
|
||||
~GuardedTail() {
|
||||
#ifdef _WIN32
|
||||
VirtualFree(memory, 0, MEM_RELEASE);
|
||||
#else
|
||||
munmap(memory, page * 2);
|
||||
#endif
|
||||
}
|
||||
template<class T> T* As() const { return static_cast<T*>(data); }
|
||||
};
|
||||
|
||||
void Cases(Query query) {
|
||||
Require(query(nullptr, 2) == -106, "invalid mode has signed 64-bit unsupported-format result");
|
||||
Reference forward;
|
||||
unsigned formats = 0;
|
||||
for (unsigned hash = 0; hash < 256; ++hash) {
|
||||
if (!ReadReference(hash, false, false, forward)) continue;
|
||||
++formats;
|
||||
JxrPixelInfo input;
|
||||
std::memset(&input, 0xa5, sizeof(input));
|
||||
input.guid = forward.guid.data();
|
||||
const auto originalGuid = forward.guid;
|
||||
Require(query(&input, 0) == 0, "forward lookup");
|
||||
CheckValue(input, forward);
|
||||
Require(forward.guid == originalGuid, "lookup modified input GUID");
|
||||
Reference backward;
|
||||
Require(ReadReference(hash, true, false, backward), "backward native case missing");
|
||||
input = BackwardInput(forward);
|
||||
Require(query(&input, 1) == 0, "backward lookup ignores GUID and high flag bits");
|
||||
CheckValue(input, backward);
|
||||
}
|
||||
Require(formats != 0, "reference format inventory empty");
|
||||
Reference white;
|
||||
Require(ReadReference(5, false, false, forward), "black-white reference");
|
||||
Require(ReadReference(5, true, true, white), "white-is-zero reference");
|
||||
Require(forward.fields[5] == 1 && white.fields[5] == 0, "duplicate GUID reference order");
|
||||
auto input = BackwardInput(forward);
|
||||
input.interpretation = 0;
|
||||
Require(query(&input, 1) == 0, "backward white-is-zero lookup");
|
||||
CheckValue(input, white);
|
||||
Require(query(&input, 0) == 0, "forward lookup selects first duplicate GUID");
|
||||
CheckValue(input, forward);
|
||||
for (unsigned mode = 2; mode < 256; ++mode) {
|
||||
std::memset(&input, 0xa5, sizeof(input));
|
||||
std::array<std::byte, sizeof(input)> before;
|
||||
std::memcpy(before.data(), &input, sizeof(input));
|
||||
Require(query(&input, static_cast<std::uint8_t>(mode)) == -106, "invalid mode error");
|
||||
Require(std::memcmp(&input, before.data(), sizeof(input)) == 0, "invalid mode changed record");
|
||||
Require(query(reinterpret_cast<JxrPixelInfo*>(std::uintptr_t{1}), static_cast<std::uint8_t>(mode)) == -106,
|
||||
"invalid mode must not read inaccessible record");
|
||||
}
|
||||
std::array<std::uint8_t, 16> unknown{};
|
||||
std::memset(&input, 0xff, sizeof(input));
|
||||
input.guid = unknown.data();
|
||||
std::array<std::byte, sizeof(input)> before;
|
||||
std::memcpy(before.data(), &input, sizeof(input));
|
||||
Require(query(&input, 0) == -106 && std::memcmp(&input, before.data(), sizeof(input)) == 0, "unknown GUID unchanged");
|
||||
Require(query(&input, 1) == -106 && std::memcmp(&input, before.data(), sizeof(input)) == 0, "unknown TIFF fields unchanged");
|
||||
GuardedTail failedRecord(&input, sizeof(input));
|
||||
failedRecord.ReadOnly();
|
||||
Require(query(failedRecord.As<JxrPixelInfo>(), 0) == -106, "failed lookup does not write read-only record");
|
||||
const void* unknownPointer = unknown.data();
|
||||
GuardedTail failedPrefix(&unknownPointer, sizeof(unknownPointer));
|
||||
failedPrefix.ReadOnly();
|
||||
Require(query(failedPrefix.As<JxrPixelInfo>(), 0) == -106, "failed forward lookup only reads GUID pointer");
|
||||
Require(ReadReference(8, false, false, forward), "Gray8 reference");
|
||||
Require(forward.fields[0] == 1 && forward.fields[7] == 8, "Gray8 reference values");
|
||||
GuardedTail guid(forward.guid.data(), 16);
|
||||
guid.ReadOnly();
|
||||
input = {};
|
||||
input.guid = guid.As<void>();
|
||||
GuardedTail record(&input, sizeof(input));
|
||||
Require(query(record.As<JxrPixelInfo>(), 0) == 0, "bounded record and read-only GUID");
|
||||
CheckValue(*record.As<JxrPixelInfo>(), forward);
|
||||
input = BackwardInput(forward);
|
||||
input.guid = nullptr;
|
||||
Require(query(&input, 1) == 0, "backward lookup ignores null GUID");
|
||||
CheckValue(input, forward);
|
||||
alignas(16) std::array<std::uint8_t, 17> unalignedGuid{};
|
||||
std::memcpy(unalignedGuid.data() + 1, forward.guid.data(), 16);
|
||||
input = {};
|
||||
input.guid = unalignedGuid.data() + 1;
|
||||
Require(query(&input, 0) == 0, "unaligned GUID lookup");
|
||||
CheckValue(input, forward);
|
||||
const auto borrowedGuid = record.As<JxrPixelInfo>()->guid;
|
||||
std::array<std::thread, 4> threads;
|
||||
for (auto& thread : threads) thread = std::thread([&] {
|
||||
for (unsigned i = 0; i < 32; ++i) {
|
||||
JxrPixelInfo value{};
|
||||
value.guid = forward.guid.data();
|
||||
Require(query(&value, 0) == 0, "concurrent independent lookup");
|
||||
CheckValue(value, forward);
|
||||
Require(value.guid == borrowedGuid, "borrowed GUID pointer changed across calls");
|
||||
}
|
||||
});
|
||||
for (auto& thread : threads) thread.join();
|
||||
for (const bool nullGuid : {false, true}) {
|
||||
bool threw = false;
|
||||
input = {};
|
||||
try { query(nullGuid ? &input : nullptr, 0); } catch (const std::invalid_argument&) { threw = true; }
|
||||
Require(threw, "valid-mode null record/GUID is explicitly unsupported");
|
||||
}
|
||||
bool threw = false;
|
||||
try { query(nullptr, 1); } catch (const std::invalid_argument&) { threw = true; }
|
||||
Require(threw, "backward null record is explicitly unsupported");
|
||||
std::printf("JXR guest lookup cases passed across %u reference hashes\n", formats);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
#ifdef _WIN32
|
||||
SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX);
|
||||
#endif
|
||||
try {
|
||||
if (argc == 2 && std::strcmp(argv[1], "reference") == 0) {
|
||||
Reference gray;
|
||||
Require(ReadReference(8, false, false, gray) && gray.fields[0] == 1 && gray.fields[7] == 8, "native Gray8 reference");
|
||||
unsigned formats = 0;
|
||||
for (unsigned hash = 0; hash < 256; ++hash) { Reference item; if (ReadReference(hash, false, false, item)) ++formats; }
|
||||
std::printf("Pinned native JXR reference passed with %u hashes\n", formats);
|
||||
return 0;
|
||||
}
|
||||
Require(argc == 3, "expected library and export mode");
|
||||
const char* name = std::strcmp(argv[2], "patched") == 0 ? "5Gjz1nLOKvo" : "PixelFormatLookup_nid_postfix";
|
||||
#ifdef _WIN32
|
||||
const auto library = LoadLibraryA(argv[1]);
|
||||
Require(library != nullptr, "load requested library");
|
||||
const auto query = reinterpret_cast<Query>(GetProcAddress(library, name));
|
||||
#else
|
||||
const auto library = dlopen(argv[1], RTLD_NOW | RTLD_LOCAL);
|
||||
Require(library != nullptr, "load requested library");
|
||||
const auto query = reinterpret_cast<Query>(dlsym(library, name));
|
||||
#endif
|
||||
Require(query != nullptr, "missing lookup export");
|
||||
Cases(query);
|
||||
#ifdef _WIN32
|
||||
FreeLibrary(library);
|
||||
#else
|
||||
dlclose(library);
|
||||
#endif
|
||||
return 0;
|
||||
} catch (const std::exception& error) {
|
||||
std::fprintf(stderr, "JXR lookup exception: %s\n", error.what());
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
#include "strcodec.h"
|
||||
#include "JXRGlue.h"
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
_Static_assert(sizeof(GUID) == 16, "GUID size");
|
||||
_Static_assert(sizeof(U32) == 4, "U32 size");
|
||||
|
||||
int JxrReferenceCase_nid_no_patch(uint8_t hash, int reverse, int white, uint8_t* guid, uint64_t* fields) {
|
||||
PKPixelInfo native = {0};
|
||||
native.pGUIDPixFmt = GetPixelFormatFromHash(hash);
|
||||
if (!native.pGUIDPixFmt) return 0;
|
||||
if (PixelFormatLookup(&native, LOOKUP_FORWARD) != 0) return -1;
|
||||
if (reverse) {
|
||||
native.pGUIDPixFmt = NULL;
|
||||
native.grBit |= PK_pixfmtBGR | PK_pixfmtNeedConvert;
|
||||
if (white) native.uInterpretation = 0;
|
||||
if (PixelFormatLookup(&native, LOOKUP_BACKWARD_TIF) != 0) return -1;
|
||||
}
|
||||
memcpy(guid, native.pGUIDPixFmt, 16);
|
||||
fields[0] = native.cChannel;
|
||||
fields[1] = native.cfColorFormat;
|
||||
fields[2] = native.bdBitDepth;
|
||||
fields[3] = native.cbitUnit;
|
||||
fields[4] = native.grBit;
|
||||
fields[5] = native.uInterpretation;
|
||||
fields[6] = native.uSamplePerPixel;
|
||||
fields[7] = native.uBitsPerSample;
|
||||
fields[8] = native.uSampleFormat;
|
||||
return 1;
|
||||
}
|
||||
@@ -1,11 +1,9 @@
|
||||
# libSceJxr technical debt
|
||||
|
||||
### Unknown function info
|
||||
|
||||
- [PixelFormatLookup](../../../core/libs/prx/libSceJxr/Export.cpp) is listed in the [PS5 3.20 export table](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceJxr.c) and was cross-checked against firmware 13.60. Its signature and behaviour remain unverified; the declaration uses the no-parameter unknown-signature convention and throws through `NotImplemented_nid_no_patch`.
|
||||
|
||||
### Functional
|
||||
|
||||
- [PixelFormatLookup](../../../core/libs/prx/libSceJxr/src/PixelFormatLookup.cpp) uses the pinned public jxrlib table and lookup algorithm through a C bridge. The signed 64-bit result and 56-byte LP64 pixel-info model, 16-byte GUID, 32-bit enums and TIFF fields, module/library versions and Sony table/configuration equivalence are inferred or unknown, not console-measured. Modes 0/1 follow forward GUID/reverse TIFF lookup; other modes and misses return the public unsupported-format error without modifying the record. Valid-mode null records and forward null GUIDs throw because console behavior is unknown. Success returns a borrowed read-only static GUID valid while the module remains loaded; the caller must not free it. Closes when: Sony prototypes, layouts, configuration, table and invalid-argument behavior are verified by attributable disassembly or console measurements
|
||||
|
||||
- [PKFree](../../../core/libs/prx/libSceJxr/Export.cpp) (libSceJxr) follows Microsoft's jxrlib [JXRGlue.c](https://github.com/4creators/jxrlib/blob/f7521879862b9085318e814c6157490dd9dbbdb4/jxrgluelib/JXRGlue.c#L53-L62), whose four exported names the module shares: a non-null `ppv` has `*ppv` released with the guest `free` and set to null, and the result is always `WMP_errSuccess` (0) as a 64-bit `ERR` (`long`). That the console's module is built from jxrlib, and that its `PKFree` releases through libc's `free`, is inferred from the export names and not checked against the console's code. Closes when: the console's `PKFree` is disassembled or measured on a PS5
|
||||
|
||||
- [readIS and writeIS](../../../core/libs/prx/libSceJxr/Export.cpp) use the non-ARM, LP64 `BitIOInfo` and `WMPStream` ABI and packet algorithm from [Microsoft's JPEG XR reference implementation](https://github.com/4creators/jxrlib/blob/f7521879862b9085318e814c6157490dd9dbbdb4/image/sys/strcodec.c#L1090-L1177). Console structure layouts and execution remain unverified. Profiling callbacks are omitted as in the reference Unix build with `DISABLE_PERF_MEASUREMENT`; the codec argument is unused. Invalid null state or required callbacks throw instead of dereferencing them. No codec is instantiated by these helpers; callers must supply the reference ring buffer and guest-ABI stream callbacks.
|
||||
Reference in new issue
Block a user