Merge remote-tracking branch 'origin/main' into HEAD

This commit is contained in:
Kraken committed 2026-10-25 23:41:43 +00:00
commit f6190694de
2622 files changed
+132846 -23172

No files matched your search

+33
View File
@@ -48,6 +48,37 @@ bool isChunkType(const std::uint8_t* bytes, const char* type) {
return std::equal(bytes, bytes + 4, type);
}
std::optional<std::uint16_t> readGrayscaleTransparencyKey(std::span<const std::uint8_t> png) {
std::size_t offset = SIGNATURE.size();
if (png.size() < offset || !std::equal(SIGNATURE.begin(), SIGNATURE.end(), png.begin())) return std::nullopt;
std::optional<std::uint16_t> key;
while (png.size() - offset >= CHUNK_OVERHEAD) {
const std::uint32_t length = readBigEndian32(&png[offset]);
const std::uint8_t* type = &png[offset + 4];
if (length > png.size() - offset - CHUNK_OVERHEAD) return std::nullopt;
if (isChunkType(type, "tRNS")) {
if (length != 2) return std::nullopt;
key = static_cast<std::uint16_t>(png[offset + 8] << 8 | png[offset + 9]);
}
if (isChunkType(type, "IDAT") || isChunkType(type, "IEND")) return key;
offset += CHUNK_OVERHEAD + length;
}
return std::nullopt;
}
void repairGrayscaleTransparency(const Header& header, std::span<const std::uint8_t> png, Image& image) {
if (header.colorType != ColorType::Grayscale || !header.hasTransparency) return;
if (header.bitDepth != 1 && header.bitDepth != 2 && header.bitDepth != 4) return;
const std::optional<std::uint16_t> key = readGrayscaleTransparencyKey(png);
if (!key.has_value()) return;
const std::uint16_t mask = static_cast<std::uint16_t>((1u << header.bitDepth) - 1);
if ((*key & 0xFF) == (*key & mask)) return;
const std::uint8_t transparent = static_cast<std::uint8_t>((*key & mask) * (255u / mask));
for (std::size_t offset = 3; offset < image.pixels.size(); offset += 4) {
image.pixels[offset] = static_cast<std::uint8_t>(image.pixels[offset - 3] == transparent ? 0 : 255);
}
}
bool isValidFormat(std::uint8_t bitDepth, std::uint8_t colorType) {
switch (colorType) {
case 0:
@@ -158,6 +189,8 @@ std::optional<Image> Decode(std::span<const std::uint8_t> png) {
Image image{static_cast<std::uint32_t>(width), static_cast<std::uint32_t>(height), {}};
image.pixels.assign(decoded.get(), decoded.get() + static_cast<std::size_t>(image.width) * image.height * 4);
const std::optional<Header> header = ParseHeader(png);
if (header.has_value()) repairGrayscaleTransparency(*header, png, image);
return image;
}
+172
View File
@@ -3,6 +3,7 @@
#include <algorithm>
#include <cstdint>
#include <cstdlib>
#include <optional>
#include <stdexcept>
#include <vector>
@@ -50,6 +51,17 @@ static const std::uint8_t GRAY1[] = {
0x42, 0x60, 0x82,
};
static const std::uint8_t GRAY1_TRNS[] = {
0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52,
0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0xDC, 0x59, 0x42,
0x27, 0x00, 0x00, 0x00, 0x02, 0x74, 0x52, 0x4E, 0x53, 0x00, 0x02, 0x98, 0x9D, 0xAC, 0x14, 0x00,
0x00, 0x00, 0x0A, 0x49, 0x44, 0x41, 0x54, 0x78, 0xDA, 0x63, 0x70, 0x00, 0x00, 0x00, 0x42, 0x00,
0x41, 0x84, 0xBF, 0x8E, 0x62, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, 0xAE, 0x42, 0x60,
0x82,
};
static const std::uint8_t PNG_SIGNATURE[] = {0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A};
static void AppendBigEndian32(std::vector<std::uint8_t>& bytes, std::uint32_t value) {
for (int shift = 24; shift >= 0; shift -= 8) bytes.push_back(static_cast<std::uint8_t>(value >> shift));
}
@@ -64,6 +76,130 @@ static std::vector<std::uint8_t> HeaderOnly(std::uint32_t width, std::uint32_t h
return bytes;
}
static std::uint32_t Crc32(std::span<const std::uint8_t> bytes) {
std::uint32_t crc = 0xFFFFFFFFu;
for (const std::uint8_t byte : bytes) {
crc ^= byte;
for (int bit = 0; bit < 8; ++bit) crc = (crc >> 1) ^ ((crc & 1) != 0 ? 0xEDB88320u : 0u);
}
return ~crc;
}
static std::uint32_t Adler32(std::span<const std::uint8_t> bytes) {
std::uint32_t low = 1;
std::uint32_t high = 0;
for (const std::uint8_t byte : bytes) {
low = (low + byte) % 65521;
high = (high + low) % 65521;
}
return high << 16 | low;
}
static void AppendTestChunk(std::vector<std::uint8_t>& png, const char* type, std::span<const std::uint8_t> data) {
AppendBigEndian32(png, static_cast<std::uint32_t>(data.size()));
const std::size_t start = png.size();
png.insert(png.end(), type, type + 4);
png.insert(png.end(), data.begin(), data.end());
AppendBigEndian32(png, Crc32(std::span<const std::uint8_t>(png.data() + start, png.size() - start)));
}
static std::vector<std::uint8_t> ZlibStore(std::span<const std::uint8_t> bytes) {
Require(bytes.size() <= 0xFFFF);
const std::uint16_t length = static_cast<std::uint16_t>(bytes.size());
const std::uint16_t inverse = static_cast<std::uint16_t>(~length);
std::vector<std::uint8_t> output{0x78, 0x01, 0x01, static_cast<std::uint8_t>(length), static_cast<std::uint8_t>(length >> 8),
static_cast<std::uint8_t>(inverse), static_cast<std::uint8_t>(inverse >> 8)};
output.insert(output.end(), bytes.begin(), bytes.end());
AppendBigEndian32(output, Adler32(bytes));
return output;
}
static std::vector<std::uint8_t> GrayLowBitPng(std::uint8_t bitDepth, std::span<const std::uint8_t> samples,
std::optional<std::uint16_t> transparency,
std::optional<std::uint16_t> precedingTransparency = std::nullopt) {
Require(bitDepth == 1 || bitDepth == 2 || bitDepth == 4);
std::vector<std::uint8_t> rows(1 + (samples.size() * bitDepth + 7) / 8, 0);
std::size_t bit = 0;
for (const std::uint8_t sample : samples) {
Require(sample < (1u << bitDepth));
rows[1 + bit / 8] |= static_cast<std::uint8_t>(sample << (8 - bitDepth - bit % 8));
bit += bitDepth;
}
std::vector<std::uint8_t> ihdr;
AppendBigEndian32(ihdr, static_cast<std::uint32_t>(samples.size()));
AppendBigEndian32(ihdr, 1);
ihdr.insert(ihdr.end(), {bitDepth, 0, 0, 0, 0});
std::vector<std::uint8_t> png(PNG_SIGNATURE, PNG_SIGNATURE + sizeof(PNG_SIGNATURE));
AppendTestChunk(png, "IHDR", ihdr);
if (precedingTransparency.has_value()) {
const std::uint8_t key[2] = {static_cast<std::uint8_t>(*precedingTransparency >> 8), static_cast<std::uint8_t>(*precedingTransparency)};
AppendTestChunk(png, "tRNS", key);
}
if (transparency.has_value()) {
const std::uint8_t key[2] = {static_cast<std::uint8_t>(*transparency >> 8), static_cast<std::uint8_t>(*transparency)};
AppendTestChunk(png, "tRNS", key);
}
AppendTestChunk(png, "IDAT", ZlibStore(rows));
AppendTestChunk(png, "IEND", std::span<const std::uint8_t>());
return png;
}
static std::uint8_t GrayScale(std::uint8_t bitDepth) {
if (bitDepth == 1) return 255;
if (bitDepth == 2) return 85;
return 17;
}
static void RequireGrayTransparency(std::uint8_t bitDepth, std::span<const std::uint8_t> samples, std::uint16_t key,
std::uint8_t transparentSample, std::optional<std::uint16_t> precedingKey = std::nullopt) {
const std::vector<std::uint8_t> png = GrayLowBitPng(bitDepth, samples, key, precedingKey);
const std::vector<std::uint8_t> original = png;
const auto header = Decoder::Png::ParseHeader(png);
Require(header.has_value());
Require(header->width == static_cast<std::uint32_t>(samples.size()) && header->height == 1 && header->bitDepth == bitDepth);
Require(header->colorType == Decoder::Png::ColorType::Grayscale && header->hasTransparency);
const auto image = Decoder::Png::Decode(png);
Require(image.has_value() && image->pixels.size() == samples.size() * 4);
const std::uint8_t scale = GrayScale(bitDepth);
for (std::size_t i = 0; i < samples.size(); ++i) {
const std::uint8_t gray = static_cast<std::uint8_t>(samples[i] * scale);
Require(image->pixels[i * 4] == gray && image->pixels[i * 4 + 1] == gray && image->pixels[i * 4 + 2] == gray);
Require(image->pixels[i * 4 + 3] == (samples[i] == transparentSample ? 0 : 255));
}
Require(png == original);
}
static void RequireGrayOpaque(std::uint8_t bitDepth, std::span<const std::uint8_t> samples) {
const std::vector<std::uint8_t> png = GrayLowBitPng(bitDepth, samples, std::nullopt);
const std::vector<std::uint8_t> original = png;
const auto header = Decoder::Png::ParseHeader(png);
Require(header.has_value() && !header->hasTransparency);
const auto image = Decoder::Png::Decode(png);
Require(image.has_value() && image->pixels.size() == samples.size() * 4);
const std::uint8_t scale = GrayScale(bitDepth);
for (std::size_t i = 0; i < samples.size(); ++i) {
const std::uint8_t gray = static_cast<std::uint8_t>(samples[i] * scale);
Require(image->pixels[i * 4] == gray && image->pixels[i * 4 + 1] == gray && image->pixels[i * 4 + 2] == gray);
Require(image->pixels[i * 4 + 3] == 255);
}
Require(png == original);
}
static void RequireTruncatedTransparency() {
const std::uint8_t samples[] = {0, 1};
const std::vector<std::uint8_t> png = GrayLowBitPng(1, samples, 0x0002);
const std::size_t transparencyOffset = sizeof(PNG_SIGNATURE) + 25;
const std::vector<std::uint8_t> truncated(png.begin(), png.begin() + transparencyOffset + 9);
const std::vector<std::uint8_t> original = truncated;
const auto header = Decoder::Png::ParseHeader(truncated);
Require(header.has_value() && !header->hasTransparency);
(void)Decoder::Png::Decode(truncated);
Require(truncated == original);
}
static void RequireRoundTrip(std::uint32_t channels, Decoder::Png::ColorType colorType) {
constexpr std::uint32_t width = 20;
constexpr std::uint32_t height = 10;
@@ -176,6 +312,42 @@ int main() {
const std::uint8_t levels[8] = {0, 255, 0, 255, 255, 0, 0, 255};
for (int i = 0; i < 8; ++i) Require(gray1->pixels[i * 4] == levels[i] && gray1->pixels[i * 4 + 3] == 255);
const auto gray1TrnsHeader = Decoder::Png::ParseHeader(GRAY1_TRNS);
Require(gray1TrnsHeader.has_value());
Require(gray1TrnsHeader->width == 2 && gray1TrnsHeader->height == 1 && gray1TrnsHeader->bitDepth == 1);
Require(gray1TrnsHeader->colorType == Decoder::Png::ColorType::Grayscale && gray1TrnsHeader->hasTransparency);
const auto gray1Trns = Decoder::Png::Decode(GRAY1_TRNS);
Require(gray1Trns.has_value());
Require(gray1Trns->pixels == std::vector<std::uint8_t>({0, 0, 0, 0, 255, 255, 255, 255}));
const std::uint8_t gray1Samples[] = {0, 1, 0, 1, 1, 0, 0, 1};
RequireGrayTransparency(1, gray1Samples, 0x0000, 0);
RequireGrayTransparency(1, gray1Samples, 0x0002, 0);
RequireGrayTransparency(1, gray1Samples, 0x0100, 0);
RequireGrayTransparency(1, gray1Samples, 0xFF02, 0);
RequireGrayTransparency(1, gray1Samples, 0xFFFF, 1);
RequireGrayTransparency(1, gray1Samples, 0x0001, 1, 0x0002);
RequireGrayOpaque(1, gray1Samples);
const std::uint8_t gray2Samples[] = {0, 1, 2, 3};
RequireGrayTransparency(2, gray2Samples, 0x0001, 1);
RequireGrayTransparency(2, gray2Samples, 0x0005, 1);
RequireGrayTransparency(2, gray2Samples, 0x0100, 0);
RequireGrayTransparency(2, gray2Samples, 0x0101, 1);
RequireGrayTransparency(2, gray2Samples, 0xFF05, 1);
RequireGrayTransparency(2, gray2Samples, 0xFFFF, 3);
RequireGrayOpaque(2, gray2Samples);
const std::uint8_t gray4Samples[] = {0, 3, 10, 15, 1};
RequireGrayTransparency(4, gray4Samples, 0x0003, 3);
RequireGrayTransparency(4, gray4Samples, 0x0013, 3);
RequireGrayTransparency(4, gray4Samples, 0x0100, 0);
RequireGrayTransparency(4, gray4Samples, 0x0103, 3);
RequireGrayTransparency(4, gray4Samples, 0xFF13, 3);
RequireGrayTransparency(4, gray4Samples, 0xFFFF, 15);
RequireGrayOpaque(4, gray4Samples);
RequireTruncatedTransparency();
const auto interlaced = Decoder::Png::ParseHeader(HeaderOnly(3, 4, 8, 6, 1));
Require(interlaced.has_value() && interlaced->interlaced && !interlaced->hasTransparency);
Require(!Decoder::Png::ParseHeader(HeaderOnly(0, 4, 8, 6, 0)).has_value());
+291 -3
View File
@@ -68,6 +68,18 @@ if(MINGW)
string(REPLACE "${runtimeLinkFlag}" "" CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS}")
endforeach()
if(NOT EXISTS "${CMAKE_RANLIB}")
if(EXISTS "${CMAKE_CXX_COMPILER_RANLIB}")
set(CMAKE_RANLIB "${CMAKE_CXX_COMPILER_RANLIB}")
else()
get_filename_component(_compiler_dir "${CMAKE_CXX_COMPILER}" DIRECTORY)
if(EXISTS "${_compiler_dir}/gcc-ranlib${CMAKE_EXECUTABLE_SUFFIX}")
set(CMAKE_RANLIB "${_compiler_dir}/gcc-ranlib${CMAKE_EXECUTABLE_SUFFIX}")
elseif(EXISTS "${_compiler_dir}/ranlib${CMAKE_EXECUTABLE_SUFFIX}")
set(CMAKE_RANLIB "${_compiler_dir}/ranlib${CMAKE_EXECUTABLE_SUFFIX}")
endif()
endif()
endif()
if(NOT EXISTS "${CMAKE_RANLIB}")
message(FATAL_ERROR "Stable import libraries require ranlib")
endif()
@@ -248,6 +260,15 @@ if(BUILD_TESTING)
set_tests_properties(guest_posix_scalar_io PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libkernel>")
endif()
add_test_executable(guest_kernel_sys_sdk_version_tests tests/GuestKernelSysSdkVersion.cpp)
target_include_directories(guest_kernel_sys_sdk_version_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(guest_kernel_sys_sdk_version_tests PRIVATE libkernel_sys libkernel libc)
configure_windows_unwind(guest_kernel_sys_sdk_version_tests)
add_test(NAME guest_kernel_sys_sdk_version COMMAND guest_kernel_sys_sdk_version_tests)
if(WIN32)
set_tests_properties(guest_kernel_sys_sdk_version PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libkernel_sys>")
endif()
add_test_executable(nid_buffer_validation_tests nid/tests/BufferValidation.cpp)
target_include_directories(nid_buffer_validation_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/nid/include)
target_compile_features(nid_buffer_validation_tests PRIVATE cxx_std_20)
@@ -268,6 +289,25 @@ if(BUILD_TESTING)
find_package(Python3 QUIET COMPONENTS Interpreter)
if(Python3_Interpreter_FOUND)
set(public_header_compiler_args)
if(APPLE)
foreach(architecture IN LISTS CMAKE_OSX_ARCHITECTURES)
list(APPEND public_header_compiler_args --compiler-arg=-arch "--compiler-arg=${architecture}")
endforeach()
if(CMAKE_OSX_SYSROOT)
list(APPEND public_header_compiler_args --compiler-arg=-isysroot "--compiler-arg=${CMAKE_OSX_SYSROOT}")
endif()
endif()
set(public_header_manifest "${CMAKE_CURRENT_BINARY_DIR}/public_headers.json")
add_test(NAME public_headers COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/tests/test_public_headers.py --audit-only --manifest "${public_header_manifest}")
set_tests_properties(public_headers PROPERTIES TIMEOUT 30 RUN_SERIAL TRUE FIXTURES_SETUP public_headers_manifest)
foreach(batch RANGE 0 63)
add_test(NAME public_headers_compile_${batch} COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/tests/test_public_headers.py --compiler ${CMAKE_CXX_COMPILER} ${public_header_compiler_args}
--manifest "${public_header_manifest}" --batch-index ${batch} --batch-count 64)
set_tests_properties(public_headers_compile_${batch} PROPERTIES TIMEOUT 30 FIXTURES_REQUIRED public_headers_manifest)
endforeach()
add_test(NAME nid_gnu_hash COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/nid/tests/test_gnu_hash.py $<TARGET_FILE:nid_patcher>)
add_test(NAME nid_elf_duplicates COMMAND ${Python3_EXECUTABLE}
@@ -315,27 +355,113 @@ if(BUILD_TESTING)
configure_windows_unwind(guest_kernel_sys_thread_defaults_tests)
add_test(NAME guest_kernel_sys_thread_defaults COMMAND guest_kernel_sys_thread_defaults_tests)
set_tests_properties(guest_kernel_sys_thread_defaults PROPERTIES TIMEOUT 20)
add_test_executable(guest_kernel_sys_barrier_tests tests/GuestKernelSysBarrier.cpp)
target_include_directories(guest_kernel_sys_barrier_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(guest_kernel_sys_barrier_tests PRIVATE libkernel_sys libkernel libc)
configure_windows_unwind(guest_kernel_sys_barrier_tests)
add_test(NAME guest_kernel_sys_barrier COMMAND guest_kernel_sys_barrier_tests)
set_tests_properties(guest_kernel_sys_barrier PROPERTIES TIMEOUT 20)
add_test_executable(guest_kernel_sys_pthread_tests tests/GuestKernelSysPthread.cpp)
target_include_directories(guest_kernel_sys_pthread_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(guest_kernel_sys_pthread_tests PRIVATE libkernel_sys libkernel libc)
configure_windows_unwind(guest_kernel_sys_pthread_tests)
add_test(NAME guest_kernel_sys_pthread COMMAND guest_kernel_sys_pthread_tests)
add_test_executable(guest_kernel_sys_constants_tests tests/GuestKernelSysConstants.cpp)
target_include_directories(guest_kernel_sys_constants_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(guest_kernel_sys_constants_tests PRIVATE libkernel_sys libkernel libc)
configure_windows_unwind(guest_kernel_sys_constants_tests)
add_test(NAME guest_kernel_sys_constants COMMAND guest_kernel_sys_constants_tests)
set_tests_properties(guest_kernel_sys_constants PROPERTIES TIMEOUT 20)
add_test_executable(guest_firmware_constant_returns_tests tests/GuestFirmwareConstantReturns.cpp)
target_include_directories(guest_firmware_constant_returns_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(guest_firmware_constant_returns_tests PRIVATE libSceComposite libSceFsInternalForVsh libSceHttpCache libScePlayReady libSceSdma libSceSsl libSceVisionManager libmdbg_syscore libkernel libc)
configure_windows_unwind(guest_firmware_constant_returns_tests)
add_test(NAME guest_firmware_constant_returns COMMAND guest_firmware_constant_returns_tests)
set_tests_properties(guest_firmware_constant_returns PROPERTIES TIMEOUT 20)
if(WIN32)
set_tests_properties(guest_firmware_constant_returns PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceComposite>;PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceFsInternalForVsh>;PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceHttpCache>;PATH=path_list_prepend:$<TARGET_FILE_DIR:libScePlayReady>;PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceSdma>;PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceSsl>;PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceVisionManager>;PATH=path_list_prepend:$<TARGET_FILE_DIR:libmdbg_syscore>")
endif()
if(Python3_Interpreter_FOUND)
add_test(NAME nid_verneed COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/nid/tests/test_verneed.py $<TARGET_FILE:nid_patcher>)
endif()
if(WIN32)
set_tests_properties(guest_kernel_sys guest_kernel_sys_thread_defaults PROPERTIES ENVIRONMENT_MODIFICATION
set_tests_properties(guest_kernel_sys guest_kernel_sys_thread_defaults guest_kernel_sys_barrier guest_kernel_sys_pthread guest_kernel_sys_constants PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libkernel_sys>")
endif()
add_test_executable(guest_kernel_sys_threads_tests tests/GuestKernelSysThreads.cpp
nid/src/NidCompute.cpp nid/src/Sha1.cpp)
target_include_directories(guest_kernel_sys_threads_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/nid/include)
target_link_libraries(guest_kernel_sys_threads_tests PRIVATE ${CMAKE_DL_LIBS})
add_custom_target(guest_kernel_sys_threads_libraries DEPENDS
${PATCHED_DIR}/libkernel_sys.prx ${PATCHED_DIR}/libkernel.prx ${LIBC_PATCHED})
add_dependencies(guest_kernel_sys_threads_tests guest_kernel_sys_threads_libraries)
configure_windows_unwind(guest_kernel_sys_threads_tests)
if(MINGW)
target_compile_options(guest_kernel_sys_threads_tests PRIVATE -fasynchronous-unwind-tables)
target_link_options(guest_kernel_sys_threads_tests PRIVATE -static -static-libgcc -static-libstdc++)
endif()
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
add_test(NAME guest_kernel_sys_threads_main_late_load COMMAND guest_kernel_sys_threads_tests ${UNPATCHED_DIR} unpatched main_late_load)
add_test(NAME guest_kernel_sys_threads_main_late_load_patched COMMAND guest_kernel_sys_threads_tests ${PATCHED_DIR} patched main_late_load)
set_tests_properties(guest_kernel_sys_threads_main_late_load guest_kernel_sys_threads_main_late_load_patched PROPERTIES TIMEOUT 20)
endif()
foreach(case IN ITEMS main)
add_test(NAME guest_kernel_sys_threads_${case} COMMAND guest_kernel_sys_threads_tests ${UNPATCHED_DIR} unpatched ${case})
add_test(NAME guest_kernel_sys_threads_${case}_patched COMMAND guest_kernel_sys_threads_tests ${PATCHED_DIR} patched ${case})
set_tests_properties(guest_kernel_sys_threads_${case} guest_kernel_sys_threads_${case}_patched PROPERTIES TIMEOUT 20)
endforeach()
add_test_executable(guest_gnm_neo_packets_tests tests/GuestGnmNeoPackets.cpp)
target_include_directories(guest_gnm_neo_packets_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(guest_gnm_neo_packets_tests PRIVATE libSceGnmDriverForNeoMode libc)
configure_windows_unwind(guest_gnm_neo_packets_tests)
add_test(NAME guest_gnm_neo_packets COMMAND guest_gnm_neo_packets_tests)
if(WIN32)
set_tests_properties(guest_gnm_neo_packets PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceGnmDriverForNeoMode>")
endif()
if(APPLE)
add_subdirectory(tests/write_watch)
add_subdirectory(tests/guest_arena)
endif()
add_test_executable(guest_kernel_sys_module_internal_tests tests/GuestKernelSysModuleInternal.cpp)
target_include_directories(guest_kernel_sys_module_internal_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(guest_kernel_sys_module_internal_tests PRIVATE libkernel_sys libkernel libc)
configure_windows_unwind(guest_kernel_sys_module_internal_tests)
add_test(NAME guest_kernel_sys_module_internal COMMAND guest_kernel_sys_module_internal_tests)
set_tests_properties(guest_kernel_sys_module_internal PROPERTIES TIMEOUT 20)
if(WIN32)
set_tests_properties(guest_kernel_sys_module_internal PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libkernel_sys>")
endif()
if(WIN32)
add_test_executable(windows_write_watch_tests tests/WindowsWriteWatch.cpp)
target_include_directories(windows_write_watch_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_compile_definitions(windows_write_watch_tests PRIVATE _WIN32_WINNT=0x0A00)
target_compile_definitions(windows_write_watch_tests PRIVATE _WIN32_WINNT=0x0A00 APS5_COUNT_MAPPING_QUERIES=1)
if(MINGW)
target_compile_options(windows_write_watch_tests PRIVATE -fasynchronous-unwind-tables)
endif()
add_test(NAME windows_write_watch COMMAND windows_write_watch_tests)
set_tests_properties(windows_write_watch PROPERTIES TIMEOUT 20)
add_test_executable(windows_shared_map_cost_tests tests/WindowsSharedMapCost.cpp)
target_include_directories(windows_shared_map_cost_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_compile_definitions(windows_shared_map_cost_tests PRIVATE _WIN32_WINNT=0x0A00)
if(MINGW)
target_compile_options(windows_shared_map_cost_tests PRIVATE -fasynchronous-unwind-tables)
endif()
add_test(NAME windows_shared_map_cost COMMAND windows_shared_map_cost_tests)
set_tests_properties(windows_shared_map_cost PROPERTIES TIMEOUT 120)
endif()
add_test_executable(guest_kernel_sys_login_tests tests/GuestKernelSysLogin.cpp)
target_include_directories(guest_kernel_sys_login_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(guest_kernel_sys_login_tests PRIVATE libkernel_sys libkernel libc)
configure_windows_unwind(guest_kernel_sys_login_tests)
add_test(NAME guest_kernel_sys_login COMMAND guest_kernel_sys_login_tests)
if(WIN32)
set_tests_properties(guest_kernel_sys_login PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libkernel_sys>")
endif()
if(WIN32)
@@ -355,6 +481,16 @@ if(BUILD_TESTING)
target_compile_options(crash_text_tests PRIVATE -fasynchronous-unwind-tables)
endif()
add_test(NAME crash_text COMMAND crash_text_tests)
add_test_executable(guest_kernel_sys_pselect_tests tests/GuestKernelSysPselect.cpp)
target_include_directories(guest_kernel_sys_pselect_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(guest_kernel_sys_pselect_tests PRIVATE libkernel_sys libkernel libc)
configure_windows_unwind(guest_kernel_sys_pselect_tests)
add_test(NAME guest_kernel_sys_pselect COMMAND guest_kernel_sys_pselect_tests)
set_tests_properties(guest_kernel_sys_pselect PROPERTIES TIMEOUT 20)
if(WIN32)
set_tests_properties(guest_kernel_sys_pselect PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libkernel_sys>")
endif()
if(WIN32)
add_test_executable(windows_crash_text_tests tests/WindowsCrashText.cpp)
@@ -388,6 +524,16 @@ if(BUILD_TESTING)
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceGnmDriverForNeoMode>")
endif()
add_test_executable(guest_vr_tracker_devices_tests tests/GuestVrTrackerDevices.cpp)
target_include_directories(guest_vr_tracker_devices_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(guest_vr_tracker_devices_tests PRIVATE libSceVrTracker libc)
configure_windows_unwind(guest_vr_tracker_devices_tests)
add_test(NAME guest_vr_tracker_devices COMMAND guest_vr_tracker_devices_tests)
if(WIN32)
set_tests_properties(guest_vr_tracker_devices PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceVrTracker>")
endif()
add_test_executable(guest_move_tests tests/GuestMove.cpp)
target_include_directories(guest_move_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(guest_move_tests PRIVATE libSceMove libc)
@@ -398,6 +544,17 @@ if(BUILD_TESTING)
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceMove>")
endif()
add_test_executable(guest_pthread_create_suspended_tests tests/GuestPthreadCreateSuspended.cpp)
target_include_directories(guest_pthread_create_suspended_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(guest_pthread_create_suspended_tests PRIVATE libkernel_sys libkernel libc)
configure_windows_unwind(guest_pthread_create_suspended_tests)
add_test(NAME guest_pthread_create_suspended COMMAND guest_pthread_create_suspended_tests)
set_tests_properties(guest_pthread_create_suspended PROPERTIES TIMEOUT 20)
if(WIN32)
set_tests_properties(guest_pthread_create_suspended PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libkernel_sys>")
endif()
add_test_executable(guest_np_party_tests tests/GuestNpParty.cpp)
target_include_directories(guest_np_party_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(guest_np_party_tests PRIVATE libSceNpParty libc)
@@ -408,6 +565,40 @@ if(BUILD_TESTING)
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceNpParty>")
endif()
add_test_executable(guest_np_ps4_imports_tests tests/GuestNpPs4Imports.cpp)
target_include_directories(guest_np_ps4_imports_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(guest_np_ps4_imports_tests PRIVATE libSceNpMatching2 libSceNpSignaling libSceNpTrophy libc)
configure_windows_unwind(guest_np_ps4_imports_tests)
add_test(NAME guest_np_ps4_imports COMMAND guest_np_ps4_imports_tests)
if(WIN32)
set_tests_properties(guest_np_ps4_imports PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceNpMatching2>")
endif()
add_test_executable(guest_np_common_tests tests/GuestNpCommon.cpp)
target_include_directories(guest_np_common_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(guest_np_common_tests PRIVATE libSceNpCommon libkernel libc)
configure_windows_unwind(guest_np_common_tests)
foreach(testCase IN ITEMS ids sync clock)
add_test(NAME guest_np_common_${testCase} COMMAND guest_np_common_tests ${testCase})
set_tests_properties(guest_np_common_${testCase} PROPERTIES TIMEOUT 20)
if(WIN32)
set_tests_properties(guest_np_common_${testCase} PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceNpCommon>")
endif()
endforeach()
add_test_executable(guest_np_common_runtime_tests tests/GuestNpCommonRuntime.cpp)
target_include_directories(guest_np_common_runtime_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(guest_np_common_runtime_tests PRIVATE libSceNpCommon libkernel libc)
configure_windows_unwind(guest_np_common_runtime_tests)
add_test(NAME guest_np_common_runtime COMMAND guest_np_common_runtime_tests)
set_tests_properties(guest_np_common_runtime PROPERTIES TIMEOUT 20)
if(WIN32)
set_tests_properties(guest_np_common_runtime PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceNpCommon>")
endif()
add_test_executable(guest_offline_imports_tests tests/GuestOfflineImports.cpp)
target_include_directories(guest_offline_imports_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(guest_offline_imports_tests PRIVATE libSceHttp libSceNpEntitlementAccess libSceRudp libSceVideoRecordingP libc)
@@ -422,6 +613,17 @@ if(BUILD_TESTING)
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceHttp>")
endif()
add_test_executable(guest_entitlements_tests tests/GuestEntitlements.cpp)
target_include_directories(guest_entitlements_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(guest_entitlements_tests PRIVATE libSceNpEntitlementAccess libc)
configure_windows_unwind(guest_entitlements_tests)
add_test(NAME guest_entitlements COMMAND guest_entitlements_tests)
add_test(NAME guest_entitlements_missing COMMAND guest_entitlements_tests missing)
if(WIN32)
set_tests_properties(guest_entitlements guest_entitlements_missing PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceNpEntitlementAccess>")
endif()
add_test_executable(guest_signin_dialog_tests tests/GuestSigninDialog.cpp)
target_include_directories(guest_signin_dialog_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(guest_signin_dialog_tests PRIVATE libSceSigninDialog libc)
@@ -621,6 +823,26 @@ if(BUILD_TESTING)
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceNpCppWebApi>")
endif()
add_test_executable(guest_np_cpp_web_api_signed_out_tests tests/GuestNpCppWebApiSignedOut.cpp)
target_include_directories(guest_np_cpp_web_api_signed_out_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(guest_np_cpp_web_api_signed_out_tests PRIVATE libSceNpCppWebApi libc)
configure_windows_unwind(guest_np_cpp_web_api_signed_out_tests)
add_test(NAME guest_np_cpp_web_api_signed_out COMMAND guest_np_cpp_web_api_signed_out_tests)
if(WIN32)
set_tests_properties(guest_np_cpp_web_api_signed_out PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceNpCppWebApi>")
endif()
add_test_executable(guest_np_cpp_web_api_offline_tests tests/GuestNpCppWebApiOffline.cpp)
target_include_directories(guest_np_cpp_web_api_offline_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(guest_np_cpp_web_api_offline_tests PRIVATE libSceNpCppWebApi libc)
configure_windows_unwind(guest_np_cpp_web_api_offline_tests)
add_test(NAME guest_np_cpp_web_api_offline COMMAND guest_np_cpp_web_api_offline_tests)
if(WIN32)
set_tests_properties(guest_np_cpp_web_api_offline PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceNpCppWebApi>")
endif()
add_test_executable(savedata_core_write_replacement_tests tests/SaveDataWriteReplacement.cpp)
target_include_directories(savedata_core_write_replacement_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(savedata_core_write_replacement_tests PRIVATE libSceSaveData)
@@ -639,6 +861,17 @@ if(BUILD_TESTING)
configure_windows_unwind(savedata_replace_file_failure_tests)
add_test(NAME savedata_replace_file_failure COMMAND savedata_replace_file_failure_tests)
add_test_executable(guest_kernel_sys_ktimer_tests tests/GuestKernelSysKtimer.cpp)
target_include_directories(guest_kernel_sys_ktimer_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(guest_kernel_sys_ktimer_tests PRIVATE libkernel_sys libkernel libc)
configure_windows_unwind(guest_kernel_sys_ktimer_tests)
add_test(NAME guest_kernel_sys_ktimer COMMAND guest_kernel_sys_ktimer_tests)
set_tests_properties(guest_kernel_sys_ktimer PROPERTIES TIMEOUT 20)
if(WIN32)
set_tests_properties(guest_kernel_sys_ktimer PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libkernel_sys>")
endif()
add_test_executable(guest_crt_tests tests/GuestCrt.cpp)
target_include_directories(guest_crt_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(guest_crt_tests PRIVATE libSceAmpr libc)
@@ -661,6 +894,17 @@ if(BUILD_TESTING)
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceSaveData.native>")
endif()
add_test_executable(guest_kernel_sys_aio_tests tests/GuestKernelSysAio.cpp)
target_include_directories(guest_kernel_sys_aio_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(guest_kernel_sys_aio_tests PRIVATE libkernel_sys libkernel libc)
configure_windows_unwind(guest_kernel_sys_aio_tests)
add_test(NAME guest_kernel_sys_aio COMMAND guest_kernel_sys_aio_tests)
set_tests_properties(guest_kernel_sys_aio PROPERTIES TIMEOUT 20)
if(WIN32)
set_tests_properties(guest_kernel_sys_aio PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libkernel_sys>")
endif()
add_test_executable(guest_posix_random_tests tests/GuestPosixRandom.cpp)
target_include_directories(guest_posix_random_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(guest_posix_random_tests PRIVATE libScePosixForWebKit)
@@ -681,6 +925,18 @@ if(BUILD_TESTING)
endif()
add_test(NAME MemoryPoolTest COMMAND MemoryPoolTest)
add_test_executable(guest_kernel_sys_swapcontext_tests tests/GuestKernelSysSwapcontext.cpp)
target_include_directories(guest_kernel_sys_swapcontext_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_compile_options(guest_kernel_sys_swapcontext_tests PRIVATE -fno-omit-frame-pointer)
target_link_libraries(guest_kernel_sys_swapcontext_tests PRIVATE libkernel_sys libSceLibcInternal libkernel libc)
configure_windows_unwind(guest_kernel_sys_swapcontext_tests)
add_test(NAME guest_kernel_sys_swapcontext COMMAND guest_kernel_sys_swapcontext_tests)
set_tests_properties(guest_kernel_sys_swapcontext PROPERTIES TIMEOUT 20)
if(WIN32)
set_tests_properties(guest_kernel_sys_swapcontext PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libkernel_sys>")
endif()
add_test_executable(guest_acm_tests tests/GuestAcm.cpp)
target_include_directories(guest_acm_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(guest_acm_tests PRIVATE libSceAcm libc)
@@ -701,6 +957,16 @@ if(BUILD_TESTING)
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceVoiceChat>")
endif()
add_test_executable(guest_gnm_neo_embedded_tests tests/GuestGnmNeoEmbedded.cpp)
target_include_directories(guest_gnm_neo_embedded_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(guest_gnm_neo_embedded_tests PRIVATE libSceGnmDriverForNeoMode libc)
configure_windows_unwind(guest_gnm_neo_embedded_tests)
add_test(NAME guest_gnm_neo_embedded COMMAND guest_gnm_neo_embedded_tests)
if(WIN32)
set_tests_properties(guest_gnm_neo_embedded PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceGnmDriverForNeoMode>")
endif()
add_test_executable(guest_np_game_intent_tests tests/GuestNpGameIntent.cpp)
target_include_directories(guest_np_game_intent_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(guest_np_game_intent_tests PRIVATE libSceNpGameIntent libc)
@@ -711,13 +977,35 @@ if(BUILD_TESTING)
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceNpGameIntent>")
endif()
add_test_executable(guest_gnm_neo_shaders_tests tests/GuestGnmNeoShaders.cpp)
target_include_directories(guest_gnm_neo_shaders_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(guest_gnm_neo_shaders_tests PRIVATE libSceGnmDriverForNeoMode libc)
configure_windows_unwind(guest_gnm_neo_shaders_tests)
add_test(NAME guest_gnm_neo_shaders COMMAND guest_gnm_neo_shaders_tests)
if(WIN32)
set_tests_properties(guest_gnm_neo_shaders PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceGnmDriverForNeoMode>")
endif()
add_test_executable(guest_vorbis_dec_tests tests/GuestVorbisDec.cpp)
target_include_directories(guest_vorbis_dec_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(guest_vorbis_dec_tests PRIVATE libSceVorbisDec libc)
target_compile_definitions(guest_vorbis_dec_tests PRIVATE OV_EXCLUDE_STATIC_CALLBACKS)
target_link_libraries(guest_vorbis_dec_tests PRIVATE libSceVorbisDec vorbisenc vorbisfile vorbis ogg libc)
configure_windows_unwind(guest_vorbis_dec_tests)
add_test(NAME guest_vorbis_dec COMMAND guest_vorbis_dec_tests)
set_tests_properties(guest_vorbis_dec PROPERTIES TIMEOUT 30)
if(WIN32)
set_tests_properties(guest_vorbis_dec PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceVorbisDec>")
endif()
add_test_executable(guest_gnm_neo_default_state_tests tests/GuestGnmNeoDefaultState.cpp)
target_include_directories(guest_gnm_neo_default_state_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(guest_gnm_neo_default_state_tests PRIVATE libSceGnmDriverForNeoMode libc)
configure_windows_unwind(guest_gnm_neo_default_state_tests)
add_test(NAME guest_gnm_neo_default_state COMMAND guest_gnm_neo_default_state_tests)
if(WIN32)
set_tests_properties(guest_gnm_neo_default_state PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceGnmDriverForNeoMode>")
endif()
endif()
+16
View File
@@ -655,6 +655,13 @@ struct AudioOutPortState {
std::uint64_t activeState;
};
struct AudioOutSystemState {
float loudness;
std::uint8_t reserved8[4];
std::uint64_t reserved[3];
};
static_assert(sizeof(AudioOutSystemState) == 32);
struct Audio3dOpenParameters {
std::uint64_t size_this;
std::uint32_t granularity;
@@ -1495,6 +1502,15 @@ struct SharePlayConnectionInfoA {
std::int32_t visitorUserId;
};
static_assert(sizeof(SharePlayConnectionInfoA) == 72 && offsetof(SharePlayConnectionInfoA, hostAccountId) == 48 && offsetof(SharePlayConnectionInfoA, hostUserId) == 64);
struct SharePlayConnectionInfo {
std::int32_t status;
std::int32_t mode;
NpOnlineId hostOnlineId;
NpOnlineId visitorOnlineId;
std::int32_t hostUserId;
std::int32_t visitorUserId;
};
static_assert(sizeof(SharePlayConnectionInfo) == 56 && offsetof(SharePlayConnectionInfo, hostUserId) == 48);
struct NpId { NpOnlineId online_id; std::uint8_t opaque[4]; };
struct NpCreateAsyncRequestParameter { std::uint8_t opaque[64]; };
struct NpCheckPremiumParameter { std::uint8_t opaque[64]; };
+12 -1
View File
@@ -22,6 +22,17 @@ struct Elf64_Ehdr {
std::uint16_t e_shstrndx;
};
struct Elf64_Phdr {
std::uint32_t p_type;
std::uint32_t p_flags;
std::uint64_t p_offset;
std::uint64_t p_vaddr;
std::uint64_t p_paddr;
std::uint64_t p_filesz;
std::uint64_t p_memsz;
std::uint64_t p_align;
};
struct Elf64_Shdr {
std::uint32_t sh_name;
std::uint32_t sh_type;
@@ -103,7 +114,7 @@ constexpr std::uint16_t kShnUndef = 0u;
class ElfNidPatcher final : public IBinaryPatcher {
public:
void PatchNids(std::vector<std::uint8_t>& binary, const std::string& libraryName, const std::unordered_set<std::string>& excludedExports) const override;
void PatchNids(std::vector<std::uint8_t>& binary, const std::string& libraryName, const std::unordered_set<std::string>& excludedExports, const PatchOptions& options = {}) const override;
};
}
+6 -1
View File
@@ -8,11 +8,16 @@
namespace Nid {
struct PatchOptions {
bool executable = false;
bool onlySce = false;
};
class IBinaryPatcher {
public:
virtual ~IBinaryPatcher() = default;
virtual void PatchNids(std::vector<std::uint8_t>& binary, const std::string& libraryName, const std::unordered_set<std::string>& excludedExports) const = 0;
virtual void PatchNids(std::vector<std::uint8_t>& binary, const std::string& libraryName, const std::unordered_set<std::string>& excludedExports, const PatchOptions& options = {}) const = 0;
};
}
@@ -22,7 +22,7 @@ std::vector<MachOExport> ReadMachOExports(const std::vector<std::uint8_t>& binar
class MachONidPatcher final : public IBinaryPatcher {
public:
void PatchNids(std::vector<std::uint8_t>& binary, const std::string& libraryName, const std::unordered_set<std::string>& excludedExports) const override;
void PatchNids(std::vector<std::uint8_t>& binary, const std::string& libraryName, const std::unordered_set<std::string>& excludedExports, const PatchOptions& options = {}) const override;
};
}
@@ -1,6 +1,7 @@
#ifndef NID_NIDRESOLVER_HPP
#define NID_NIDRESOLVER_HPP
#include <nid/IBinaryPatcher.hpp>
#include <string>
#include <unordered_map>
#include <unordered_set>
@@ -8,6 +9,10 @@
namespace Nid {
bool IsSceImport(const std::string& name);
std::unordered_map<std::string, std::string> ResolveImportNids(const std::vector<std::string>& names, const std::unordered_set<std::string>& excluded, const PatchOptions& options);
std::string ResolveOneName(const std::string& funcName);
std::unordered_map<std::string, std::string> ResolveNids(const std::vector<std::string>& exportedNames, const std::string& libraryName, const std::unordered_set<std::string>& excludedExports);
+1 -1
View File
@@ -39,7 +39,7 @@ struct PeSectionHeader {
class PeNidPatcher final : public IBinaryPatcher {
public:
void PatchNids(std::vector<std::uint8_t>& binary, const std::string& libraryName, const std::unordered_set<std::string>& excludedExports) const override;
void PatchNids(std::vector<std::uint8_t>& binary, const std::string& libraryName, const std::unordered_set<std::string>& excludedExports, const PatchOptions& options = {}) const override;
};
}
+92 -15
View File
@@ -354,7 +354,7 @@ std::vector<std::uint32_t> CollectVerneedNameOffsets(const std::vector<std::uint
}
void ElfNidPatcher::PatchNids(std::vector<std::uint8_t>& elf, const std::string& libraryName, const std::unordered_set<std::string>& excludedExports) const {
void ElfNidPatcher::PatchNids(std::vector<std::uint8_t>& elf, const std::string& libraryName, const std::unordered_set<std::string>& excludedExports, const PatchOptions& options) const {
using namespace Internal;
if (elf.size() < sizeof(Elf64_Ehdr)) throw std::runtime_error("file too small");
@@ -370,11 +370,13 @@ void ElfNidPatcher::PatchNids(std::vector<std::uint8_t>& elf, const std::string&
std::size_t sysvHashOffset = 0u, sysvHashSize = 0u;
std::uint32_t sysvHashLink = 0u;
std::size_t versymOffset = 0u;
bool hasDynamic = false;
std::uint32_t dynSymSectionIndex = 0u;
std::uint32_t gnuHashSymOffset = 0u;
for (std::uint16_t i = 0u; i < ehdr.e_shnum; ++i) {
const std::size_t shOffset = static_cast<std::size_t>(ehdr.e_shoff) + i * sizeof(Elf64_Shdr);
const auto shdr = Read<Elf64_Shdr>(elf, shOffset);
if (shdr.sh_type == kShtDynamic) hasDynamic = true;
if (shdr.sh_type == kShtDynsym) {
if (shdr.sh_entsize != sizeof(Elf64_Sym) || shdr.sh_size % sizeof(Elf64_Sym) || shdr.sh_link >= ehdr.e_shnum)
throw std::runtime_error("invalid ELF dynsym layout");
@@ -398,6 +400,19 @@ void ElfNidPatcher::PatchNids(std::vector<std::uint8_t>& elf, const std::string&
throw std::runtime_error("unsupported .gnu.version_d section: symbol version definitions cannot be preserved");
}
if (options.executable && dynSymOffset == 0u && !hasDynamic) {
if (ehdr.e_phnum) {
if (!ehdr.e_phoff || ehdr.e_phentsize != sizeof(Elf64_Phdr) || ehdr.e_phnum == 0xffffu)
throw std::runtime_error("unsupported ELF program header layout");
RequireElfRange(elf, ehdr.e_phoff, std::uint64_t{ehdr.e_phnum} * sizeof(Elf64_Phdr), "program headers");
for (std::uint16_t index = 0u; index < ehdr.e_phnum; ++index) {
const auto segment = Read<Elf64_Phdr>(elf, ehdr.e_phoff + index * sizeof(Elf64_Phdr));
if (segment.p_type == 2u)
throw std::runtime_error("unsupported ELF PT_DYNAMIC without a .dynsym section");
}
}
return;
}
if (dynSymOffset == 0u) throw std::runtime_error("no .dynsym section");
const std::size_t symCount = dynSymSize / sizeof(Elf64_Sym);
@@ -437,19 +452,26 @@ void ElfNidPatcher::PatchNids(std::vector<std::uint8_t>& elf, const std::string&
elf.begin() + static_cast<std::ptrdiff_t>(dynStrOffset + dynStrSize)
);
std::vector<std::string> importedNames;
std::vector<std::string> exportedNames;
for (std::size_t i = 1u; i < symCount; ++i) {
const std::size_t symOffset = dynSymOffset + i * sizeof(Elf64_Sym);
const auto sym = Read<Elf64_Sym>(elf, symOffset);
if (sym.st_name == 0u) continue;
const std::uint8_t binding = sym.st_info >> 4u;
if (binding == kStbLocal || sym.st_shndx == kShnUndef) continue;
if (binding == kStbLocal) continue;
if (sym.st_shndx == kShnUndef) {
const auto name = ReadCStr(origDynStr, sym.st_name);
if (!name.empty()) importedNames.push_back(name);
continue;
}
const std::string symName = ReadCStr(origDynStr, sym.st_name);
if (symName.empty()) continue;
exportedNames.push_back(symName);
}
const auto nidMap = ResolveNids(exportedNames, libraryName, excludedExports);
const auto nidMap = options.executable ? std::unordered_map<std::string, std::string>{} : ResolveNids(exportedNames, libraryName, excludedExports);
const auto importMap = ResolveImportNids(importedNames, excludedExports, options);
struct SymbolNameUse {
std::size_t symIndex;
@@ -481,14 +503,11 @@ void ElfNidPatcher::PatchNids(std::vector<std::uint8_t>& elf, const std::string&
} else if (binding != kStbLocal && sym.st_shndx == kShnUndef) {
const bool isUnversioned = versymOffset == 0u ||
(Read<std::uint16_t>(elf, versymOffset + i * sizeof(std::uint16_t)) <= 1u);
const bool hasNidPostfix = symName.size() >= kNidPostfixLen &&
symName.compare(symName.size() - kNidPostfixLen, kNidPostfixLen, kNidPostfix) == 0;
const bool hasScePrefix = symName.size() >= 3u &&
std::tolower(static_cast<unsigned char>(symName[0])) == 's' &&
std::tolower(static_cast<unsigned char>(symName[1])) == 'c' &&
std::tolower(static_cast<unsigned char>(symName[2])) == 'e';
if (isUnversioned && (hasNidPostfix || hasScePrefix))
newValue = ResolveOneName(symName);
if (options.executable || isUnversioned) {
newValue = importMap.at(symName);
if (options.executable && newValue != symName && versymOffset != 0u)
Write(elf, versymOffset + i * sizeof(std::uint16_t), std::uint16_t{1});
}
}
uses.push_back(SymbolNameUse{i, sym.st_name, newValue});
@@ -538,11 +557,69 @@ void ElfNidPatcher::PatchNids(std::vector<std::uint8_t>& elf, const std::string&
for (const auto offset : preservedOffsets)
preservedToNewOffset.emplace_back(offset, valueOffsets.at(ReadCStr(origDynStr, offset)));
if (newDynStr.size() > dynStrSize && options.executable) {
if (ehdr.e_phentsize != sizeof(Elf64_Phdr) || ehdr.e_phnum == 0 || ehdr.e_phnum >= 0xfffdu)
throw std::runtime_error("invalid ELF program header table for string growth");
RequireElfRange(elf, ehdr.e_phoff, std::uint64_t{ehdr.e_phnum} * sizeof(Elf64_Phdr), "program headers");
const auto align = [](std::uint64_t value) {
if (value > UINT64_MAX - 4095u) throw std::runtime_error("ELF address overflow");
return (value + 4095u) & ~std::uint64_t{4095u};
};
std::vector<Elf64_Phdr> segments;
std::uint64_t virtualEnd = 0;
bool hasPhdr = false;
for (std::size_t index = 0; index < ehdr.e_phnum; ++index) {
const auto segment = Read<Elf64_Phdr>(elf, ehdr.e_phoff + index * sizeof(Elf64_Phdr));
segments.push_back(segment);
hasPhdr |= segment.p_type == 6;
if (segment.p_type != 1) continue;
if (segment.p_memsz > UINT64_MAX - segment.p_vaddr) throw std::runtime_error("ELF segment address overflow");
virtualEnd = std::max(virtualEnd, segment.p_vaddr + segment.p_memsz);
}
if (!virtualEnd) throw std::runtime_error("ELF executable has no load segment");
if (!hasPhdr) segments.insert(segments.begin(), Elf64_Phdr{6, 4, 0, 0, 0, 0, 0, 8});
const auto fileOffset = align(elf.size());
const auto address = align(virtualEnd);
const auto tableSize = (segments.size() + 1u) * sizeof(Elf64_Phdr);
const auto segmentSize = tableSize + newDynStr.size();
if (segmentSize > UINT64_MAX - address || segmentSize > SIZE_MAX - fileOffset) throw std::runtime_error("ELF string segment overflow");
for (auto& segment : segments) {
if (segment.p_type != 6) continue;
segment = {6, 4, fileOffset, address, address, tableSize, tableSize, 8};
}
segments.push_back({1, 4, fileOffset, address, address, segmentSize, segmentSize, 4096});
elf.resize(static_cast<std::size_t>(fileOffset + segmentSize), 0);
for (std::size_t index = 0; index < segments.size(); ++index)
Write(elf, fileOffset + index * sizeof(Elf64_Phdr), segments[index]);
Write(elf, offsetof(Elf64_Ehdr, e_phoff), fileOffset);
Write(elf, offsetof(Elf64_Ehdr, e_phnum), static_cast<std::uint16_t>(segments.size()));
dynStrOffset = static_cast<std::size_t>(fileOffset + tableSize);
dynStrSize = newDynStr.size();
const auto stringAddress = address + tableSize;
const auto sectionOffset = ehdr.e_shoff + dynStrSectionLink * sizeof(Elf64_Shdr);
auto strings = Read<Elf64_Shdr>(elf, sectionOffset);
strings.sh_offset = dynStrOffset;
strings.sh_addr = stringAddress;
strings.sh_size = dynStrSize;
Write(elf, sectionOffset, strings);
bool foundAddress = false;
bool foundSize = false;
for (std::size_t index = 0; index < ehdr.e_shnum; ++index) {
const auto section = Read<Elf64_Shdr>(elf, ehdr.e_shoff + index * sizeof(Elf64_Shdr));
if (section.sh_type != kShtDynamic) continue;
RequireElfRange(elf, section.sh_offset, section.sh_size, "dynamic");
for (std::uint64_t offset = 0; offset + sizeof(Elf64_Dyn) <= section.sh_size; offset += sizeof(Elf64_Dyn)) {
auto entry = Read<Elf64_Dyn>(elf, section.sh_offset + offset);
if (entry.d_tag == kDtNull) break;
if (entry.d_tag == 5) { entry.d_val = stringAddress; foundAddress = true; }
if (entry.d_tag == 10) { entry.d_val = dynStrSize; foundSize = true; }
Write(elf, section.sh_offset + offset, entry);
}
}
if (!foundAddress || !foundSize) throw std::runtime_error("ELF executable lacks DT_STRTAB or DT_STRSZ");
}
if (newDynStr.size() > dynStrSize) {
throw std::runtime_error(
"rebuilt .dynstr (" + std::to_string(newDynStr.size()) + " bytes) exceeds original section size (" +
std::to_string(dynStrSize) + " bytes) - relinking with a larger section is required"
);
throw std::runtime_error("rebuilt .dynstr exceeds original section size; relinking with a larger section is required");
}
newDynStr.resize(dynStrSize, 0u);
+3 -3
View File
@@ -175,7 +175,7 @@ std::unordered_set<std::string> ReadExportExclusions(const std::string& path) {
}
std::unordered_set<std::string> ReadExportList(const std::string& path) {
std::ifstream input(path, std::ios::binary);
std::ifstream input(Internal::NativePath(path), std::ios::binary);
if (!input) throw std::runtime_error("cannot open export list: " + path);
const auto text = ReadText(path, input);
if (text.empty() || text.back() != '\n') throw std::runtime_error("malformed export list: " + path);
@@ -192,8 +192,8 @@ std::unordered_set<std::string> ReadExportList(const std::string& path) {
bool WriteExportList(const std::string& referencePath, const std::string& path) {
const auto text = FormatExportList(ReadExportExclusions(referencePath));
if (std::ifstream existing(path, std::ios::binary); existing && ReadText(path, existing) == text) return false;
std::ofstream output(path, std::ios::binary | std::ios::trunc);
if (std::ifstream existing(Internal::NativePath(path), std::ios::binary); existing && ReadText(path, existing) == text) return false;
std::ofstream output(Internal::NativePath(path), std::ios::binary | std::ios::trunc);
if (!output) throw std::runtime_error("cannot write export list: " + path);
output.write(text.data(), static_cast<std::streamsize>(text.size()));
output.close();
+100 -57
View File
@@ -341,15 +341,6 @@ std::string StripUnderscore(const std::string& name) {
return !name.empty() && name[0] == '_' ? name.substr(1) : name;
}
bool IsGuestFacingImport(const std::string& name) {
using namespace Internal;
const bool hasNidPostfix = name.size() >= kNidPostfixLen && name.compare(name.size() - kNidPostfixLen, kNidPostfixLen, kNidPostfix) == 0;
const bool hasScePrefix = name.size() >= 3u &&
std::tolower(static_cast<unsigned char>(name[0])) == 's' &&
std::tolower(static_cast<unsigned char>(name[1])) == 'c' &&
std::tolower(static_cast<unsigned char>(name[2])) == 'e';
return hasNidPostfix || hasScePrefix;
}
std::vector<bool> DylibOrdinalsArePrx(const std::vector<std::uint8_t>& binary, const Image& image) {
std::vector<bool> result;
@@ -364,11 +355,48 @@ std::vector<bool> DylibOrdinalsArePrx(const std::vector<std::uint8_t>& binary, c
return result;
}
void PatchChainedImports(std::vector<std::uint8_t>& binary, const Image& image) {
void WriteLinkeditData(std::vector<std::uint8_t>& binary, const Image& image, const TrieLocation& location, const std::vector<std::uint8_t>& trie) {
const auto oldOffset = Read<std::uint32_t>(binary, location.OffsetField);
const auto oldSize = Read<std::uint32_t>(binary, location.SizeField);
if (trie.size() <= oldSize) {
std::copy(trie.begin(), trie.end(), binary.begin() + oldOffset);
std::fill(binary.begin() + oldOffset + static_cast<std::ptrdiff_t>(trie.size()), binary.begin() + oldOffset + oldSize, 0);
Write(binary, location.SizeField, static_cast<std::uint32_t>(trie.size()));
return;
}
std::optional<std::size_t> linkedit;
for (const auto offset : image.Commands) {
const auto cmd = Read<LoadCommand>(binary, offset).Cmd;
if (cmd == kLcCodeSignature) throw std::runtime_error("signed Mach-O images cannot grow linkedit data");
if (cmd != kLcSegment64) continue;
const auto segment = Read<SegmentCommand64>(binary, offset);
if (std::strncmp(segment.Name, "__LINKEDIT", sizeof(segment.Name)) == 0) linkedit = offset;
}
if (!linkedit) throw std::runtime_error("Mach-O image has no __LINKEDIT segment");
auto segment = Read<SegmentCommand64>(binary, *linkedit);
if (segment.FileOffset + segment.FileSize != binary.size()) throw std::runtime_error("__LINKEDIT does not end the file");
const auto newOffset = (binary.size() + 7) & ~std::size_t{7};
if (newOffset + trie.size() > 0xffffffffu) throw std::runtime_error("linkedit data offset overflow");
binary.resize(newOffset, 0);
binary.insert(binary.end(), trie.begin(), trie.end());
std::fill(binary.begin() + oldOffset, binary.begin() + oldOffset + oldSize, 0);
segment.FileSize = binary.size() - segment.FileOffset;
segment.VmSize = std::max(segment.VmSize, (segment.FileSize + kPageSize - 1) & ~(kPageSize - 1));
Write(binary, *linkedit, segment);
Write(binary, location.OffsetField, static_cast<std::uint32_t>(newOffset));
Write(binary, location.SizeField, static_cast<std::uint32_t>(trie.size()));
}
void PatchChainedImports(std::vector<std::uint8_t>& binary, const Image& image, const std::unordered_set<std::string>& excluded, const PatchOptions& options) {
std::size_t commandOffset = 0;
std::optional<LinkeditDataCommand> fixups;
for (const auto offset : image.Commands) {
const auto cmd = Read<LoadCommand>(binary, offset).Cmd;
if (cmd == kLcDyldChainedFixups) fixups = Read<LinkeditDataCommand>(binary, offset);
if (cmd == kLcDyldChainedFixups) {
fixups = Read<LinkeditDataCommand>(binary, offset);
commandOffset = offset;
}
if (options.executable && cmd == kLcCodeSignature) throw std::runtime_error("remove the Mach-O code signature before patching and sign again afterwards");
if ((cmd == kLcDyldInfo || cmd == kLcDyldInfoOnly)) {
const auto info = Read<DyldInfoCommand>(binary, offset);
if (info.BindSize != 0 || info.LazyBindSize != 0 || info.WeakBindSize != 0) throw std::runtime_error("Mach-O bind opcodes are not supported; link with chained fixups");
@@ -400,61 +428,72 @@ void PatchChainedImports(std::vector<std::uint8_t>& binary, const Image& image)
imports.push_back({raw >> 9, raw & 0xff});
}
}
std::set<std::uint32_t> nameOffsets;
for (const auto& item : imports) nameOffsets.insert(item.NameOffset);
std::set<std::uint32_t> rewritten;
std::vector<std::string> names;
std::vector<std::string> originalNames;
std::vector<bool> selected;
for (const auto& item : imports) {
const bool special = header.ImportsFormat == kImportFormatAddend64 ? item.Ordinal >= 0xfff0 : item.Ordinal >= 0xf0;
if (item.Ordinal == 0 || special || item.Ordinal > prx.size() || !prx[item.Ordinal - 1]) continue;
if (rewritten.contains(item.NameOffset)) continue;
const auto at = symbols + item.NameOffset;
if (at >= symbolsEnd) throw std::runtime_error("chained import name out of bounds");
const auto name = Internal::ReadCStr(binary, at);
if (at + name.size() >= symbolsEnd) throw std::runtime_error("unterminated chained import name");
const auto bare = StripUnderscore(name);
if (bare == name || !IsGuestFacingImport(bare)) continue;
const auto patched = "_" + ResolveOneName(bare);
if (patched.size() > name.size()) throw std::runtime_error("import NID longer than original name: " + name);
const auto inside = nameOffsets.upper_bound(item.NameOffset);
if (inside != nameOffsets.end() && *inside <= item.NameOffset + name.size()) throw std::runtime_error("chained import names share storage: " + name);
std::memcpy(binary.data() + at, patched.data(), patched.size());
std::fill(binary.begin() + static_cast<std::ptrdiff_t>(at + patched.size()), binary.begin() + static_cast<std::ptrdiff_t>(at + name.size() + 1), 0);
rewritten.insert(item.NameOffset);
const bool special = header.ImportsFormat == kImportFormatAddend64 ? item.Ordinal >= 0xfff0 : item.Ordinal >= 0xf0;
const bool eligible = options.executable || (bare != name && item.Ordinal != 0 && !special && item.Ordinal <= prx.size() && prx[item.Ordinal - 1]);
originalNames.push_back(name);
selected.push_back(eligible);
if (eligible) names.push_back(bare);
}
}
void WriteTrie(std::vector<std::uint8_t>& binary, const Image& image, const TrieLocation& location, const std::vector<std::uint8_t>& trie) {
const auto oldOffset = Read<std::uint32_t>(binary, location.OffsetField);
const auto oldSize = Read<std::uint32_t>(binary, location.SizeField);
if (trie.size() <= oldSize) {
std::copy(trie.begin(), trie.end(), binary.begin() + oldOffset);
std::fill(binary.begin() + oldOffset + static_cast<std::ptrdiff_t>(trie.size()), binary.begin() + oldOffset + oldSize, 0);
Write(binary, location.SizeField, static_cast<std::uint32_t>(trie.size()));
const auto mapping = ResolveImportNids(names, excluded, options);
if (!options.executable) {
std::set<std::uint32_t> nameOffsets;
for (const auto& item : imports) nameOffsets.insert(item.NameOffset);
std::set<std::uint32_t> rewritten;
for (const auto& item : imports) {
const bool special = header.ImportsFormat == kImportFormatAddend64 ? item.Ordinal >= 0xfff0 : item.Ordinal >= 0xf0;
if (item.Ordinal == 0 || special || item.Ordinal > prx.size() || !prx[item.Ordinal - 1]) continue;
if (rewritten.contains(item.NameOffset)) continue;
const auto at = symbols + item.NameOffset;
if (at >= symbolsEnd) throw std::runtime_error("chained import name out of bounds");
const auto name = Internal::ReadCStr(binary, at);
if (at + name.size() >= symbolsEnd) throw std::runtime_error("unterminated chained import name");
const auto bare = StripUnderscore(name);
if (bare == name) continue;
const auto patched = "_" + mapping.at(bare);
if (patched.size() > name.size()) throw std::runtime_error("import NID longer than original name: " + name);
const auto inside = nameOffsets.upper_bound(item.NameOffset);
if (inside != nameOffsets.end() && *inside <= item.NameOffset + name.size()) throw std::runtime_error("chained import names share storage: " + name);
std::memcpy(binary.data() + at, patched.data(), patched.size());
std::fill(binary.begin() + static_cast<std::ptrdiff_t>(at + patched.size()), binary.begin() + static_cast<std::ptrdiff_t>(at + name.size() + 1), 0);
rewritten.insert(item.NameOffset);
}
return;
}
std::optional<std::size_t> linkedit;
for (const auto offset : image.Commands) {
const auto cmd = Read<LoadCommand>(binary, offset).Cmd;
if (cmd == kLcCodeSignature) throw std::runtime_error("signed Mach-O images cannot grow their export trie");
if (cmd != kLcSegment64) continue;
const auto segment = Read<SegmentCommand64>(binary, offset);
if (std::strncmp(segment.Name, "__LINKEDIT", sizeof(segment.Name)) == 0) linkedit = offset;
if (imports.empty()) return;
std::vector<std::uint8_t> data(binary.begin() + base, binary.begin() + symbolsEnd);
const auto newSymbols = data.size();
if (newSymbols > 0xffffffffu) throw std::runtime_error("chained symbol offset overflow");
Write(data, offsetof(ChainedFixupsHeader, SymbolsOffset), static_cast<std::uint32_t>(newSymbols));
for (std::size_t index = 0; index < imports.size(); ++index) {
const auto& original = originalNames[index];
const auto name = selected[index] ? (original.starts_with("_") ? "_" : "") + mapping.at(StripUnderscore(original)) : original;
const auto offset = data.size() - newSymbols;
const auto at = header.ImportsOffset + index * entrySize;
if (header.ImportsFormat == kImportFormatAddend64) {
if (offset > 0xffffffffu) throw std::runtime_error("chained symbol offset overflow");
const auto raw = Read<std::uint64_t>(data, at);
Write(data, at, (raw & 0xffffffffu) | (std::uint64_t{offset} << 32));
} else {
if (offset > 0x7fffffu) throw std::runtime_error("chained symbol offset overflow");
const auto raw = Read<std::uint32_t>(data, at);
Write(data, at, (raw & 0x1ffu) | (static_cast<std::uint32_t>(offset) << 9));
}
data.insert(data.end(), name.begin(), name.end());
data.push_back(0);
}
if (!linkedit) throw std::runtime_error("Mach-O image has no __LINKEDIT segment");
auto segment = Read<SegmentCommand64>(binary, *linkedit);
if (segment.FileOffset + segment.FileSize != binary.size()) throw std::runtime_error("__LINKEDIT does not end the file");
const auto newOffset = (binary.size() + 7) & ~std::size_t{7};
if (newOffset + trie.size() > 0xffffffffu) throw std::runtime_error("export trie offset overflow");
binary.resize(newOffset, 0);
binary.insert(binary.end(), trie.begin(), trie.end());
std::fill(binary.begin() + oldOffset, binary.begin() + oldOffset + oldSize, 0);
segment.FileSize = binary.size() - segment.FileOffset;
segment.VmSize = std::max(segment.VmSize, (segment.FileSize + kPageSize - 1) & ~(kPageSize - 1));
Write(binary, *linkedit, segment);
Write(binary, location.OffsetField, static_cast<std::uint32_t>(newOffset));
Write(binary, location.SizeField, static_cast<std::uint32_t>(trie.size()));
WriteLinkeditData(binary, image, {commandOffset + 8, commandOffset + 12}, data);
}
}
std::vector<MachOExport> ReadMachOExports(const std::vector<std::uint8_t>& binary) {
@@ -464,8 +503,12 @@ std::vector<MachOExport> ReadMachOExports(const std::vector<std::uint8_t>& binar
return ParseTrie(binary, Read<std::uint32_t>(binary, location->OffsetField), Read<std::uint32_t>(binary, location->SizeField));
}
void MachONidPatcher::PatchNids(std::vector<std::uint8_t>& binary, const std::string& libraryName, const std::unordered_set<std::string>& excludedExports) const {
void MachONidPatcher::PatchNids(std::vector<std::uint8_t>& binary, const std::string& libraryName, const std::unordered_set<std::string>& excludedExports, const PatchOptions& options) const {
const auto image = ParseImage(binary);
if (options.executable) {
PatchChainedImports(binary, image, excludedExports, options);
return;
}
const auto location = FindTrie(binary, image);
if (!location) throw std::runtime_error("no export trie");
auto exports = ParseTrie(binary, Read<std::uint32_t>(binary, location->OffsetField), Read<std::uint32_t>(binary, location->SizeField));
@@ -486,8 +529,8 @@ void MachONidPatcher::PatchNids(std::vector<std::uint8_t>& binary, const std::st
if (found == nidMap.end()) throw std::runtime_error("symbol not in nid map: " + name);
exports[index].Name = "_" + found->second;
}
WriteTrie(binary, image, *location, TrieBuilder(exports).Build());
PatchChainedImports(binary, ParseImage(binary));
WriteLinkeditData(binary, image, *location, TrieBuilder(exports).Build());
PatchChainedImports(binary, ParseImage(binary), excludedExports, options);
}
}
+19 -8
View File
@@ -3,21 +3,32 @@
#include <nid/NidCompute.hpp>
#include <nid/NidPatcherUtils.hpp>
#include <cstdint>
#include <cctype>
#include <stdexcept>
#include <unordered_set>
#include <vector>
namespace Nid {
bool IsSceImport(const std::string& name) {
return name.ends_with(Internal::kNidPostfix) || (name.size() >= 3 &&
std::tolower(static_cast<unsigned char>(name[0])) == 's' &&
std::tolower(static_cast<unsigned char>(name[1])) == 'c' &&
std::tolower(static_cast<unsigned char>(name[2])) == 'e');
}
std::unordered_map<std::string, std::string> ResolveImportNids(const std::vector<std::string>& names, const std::unordered_set<std::string>& excluded, const PatchOptions& options) {
const std::unordered_set<std::string> unique(names.begin(), names.end());
auto result = ResolveNids(std::vector<std::string>(unique.begin(), unique.end()), "", excluded);
for (const auto& name : unique) {
if ((!options.executable || options.onlySce) && !IsSceImport(name)) result.at(name) = name;
else if (!options.executable) result.at(name) = ResolveOneName(name);
}
return result;
}
std::string ResolveOneName(const std::string& funcName) {
using namespace Internal;
if (IsNidNoPatchCut(funcName))
return StripNidNoPatchCut(funcName);
if (IsNidNoPatch(funcName))
return funcName;
return ComputeNid(StripNidPostfix(funcName), "");
return ResolveNids({funcName}, "", {}).at(funcName);
}
std::unordered_map<std::string, std::string> ResolveNids(const std::vector<std::string>& exportedNames, const std::string& libraryName, const std::unordered_set<std::string>& excludedExports) {
+231 -158
View File
@@ -36,7 +36,7 @@ std::size_t RvaToOffset(const std::vector<std::uint8_t>& pe, std::uint32_t rva,
}
void PeNidPatcher::PatchNids(std::vector<std::uint8_t>& pe, const std::string& libraryName, const std::unordered_set<std::string>& excludedExports) const {
void PeNidPatcher::PatchNids(std::vector<std::uint8_t>& pe, const std::string& libraryName, const std::unordered_set<std::string>& excludedExports, const PatchOptions& options) const {
using namespace Internal;
if (pe.size() < 0x40) throw std::runtime_error("file too small");
@@ -54,7 +54,7 @@ void PeNidPatcher::PatchNids(std::vector<std::uint8_t>& pe, const std::string& l
const std::size_t sectionTableOffset = coffHeaderOffset + 20u + sizeOfOptionalHeader;
const auto symbolTable = Read<std::uint32_t>(pe, coffHeaderOffset + 8u);
const auto symbolCount = Read<std::uint32_t>(pe, coffHeaderOffset + 12u);
for (std::uint16_t i = 0; i < numberOfSections; ++i) {
for (std::uint16_t i = 0; !options.executable && i < numberOfSections; ++i) {
const auto offset = sectionTableOffset + i * sizeof(PeSectionHeader);
auto section = Read<PeSectionHeader>(pe, offset);
if (pe[offset] != '/' || !symbolTable) continue;
@@ -79,159 +79,162 @@ void PeNidPatcher::PatchNids(std::vector<std::uint8_t>& pe, const std::string& l
throw std::runtime_error("unsupported optional header magic");
}
const auto exportDir = Read<PeDataDirectory>(pe, dataDirectoryOffset);
if (exportDir.VirtualAddress == 0u) throw std::runtime_error("no export directory");
if (!options.executable) {
const auto exportDir = Read<PeDataDirectory>(pe, dataDirectoryOffset);
if (exportDir.VirtualAddress == 0u) throw std::runtime_error("no export directory");
const std::size_t exportDirOffset = RvaToOffset(pe, exportDir.VirtualAddress, peHeaderOffset, numberOfSections, sizeOfOptionalHeader);
const auto exportTable = Read<PeExportDirectory>(pe, exportDirOffset);
const std::size_t exportDirOffset = RvaToOffset(pe, exportDir.VirtualAddress, peHeaderOffset, numberOfSections, sizeOfOptionalHeader);
const auto exportTable = Read<PeExportDirectory>(pe, exportDirOffset);
if (exportTable.NumberOfNames == 0u) throw std::runtime_error("no exported names");
if (exportTable.NumberOfNames == 0u) throw std::runtime_error("no exported names");
const std::size_t namesArrayOffset = RvaToOffset(pe, exportTable.AddressOfNames, peHeaderOffset, numberOfSections, sizeOfOptionalHeader);
const std::size_t ordinalsArrayOffset = RvaToOffset(pe, exportTable.AddressOfNameOrdinals, peHeaderOffset, numberOfSections, sizeOfOptionalHeader);
const std::size_t namesArrayOffset = RvaToOffset(pe, exportTable.AddressOfNames, peHeaderOffset, numberOfSections, sizeOfOptionalHeader);
const std::size_t ordinalsArrayOffset = RvaToOffset(pe, exportTable.AddressOfNameOrdinals, peHeaderOffset, numberOfSections, sizeOfOptionalHeader);
std::vector<std::uint32_t> nameRvas(exportTable.NumberOfNames);
std::vector<std::string> names(exportTable.NumberOfNames);
for (std::uint32_t i = 0u; i < exportTable.NumberOfNames; ++i) {
const auto nameRva = Read<std::uint32_t>(pe, namesArrayOffset + i * 4u);
const std::size_t nameOffset = RvaToOffset(pe, nameRva, peHeaderOffset, numberOfSections, sizeOfOptionalHeader);
const std::string name = ReadCStr(pe, nameOffset);
if (name.empty()) throw std::runtime_error("empty exported name");
nameRvas[i] = nameRva;
names[i] = name;
}
const auto nidMap = ResolveNids(names, libraryName, excludedExports);
const std::size_t edataSectionOffset = FindSectionOffsetByRva(pe, exportDir.VirtualAddress, peHeaderOffset, numberOfSections, sizeOfOptionalHeader);
const auto edataSection = Read<PeSectionHeader>(pe, edataSectionOffset);
const auto fileAlignment = Read<std::uint32_t>(pe, optionalHeaderOffset + 36u);
if (fileAlignment == 0u) throw std::runtime_error("invalid file alignment");
std::uint32_t stringsRegionStart = std::numeric_limits<std::uint32_t>::max();
for (std::uint32_t i = 0u; i < exportTable.NumberOfNames; ++i) {
const std::uint32_t rvaFromSection = nameRvas[i] - edataSection.VirtualAddress;
if (rvaFromSection < stringsRegionStart) stringsRegionStart = rvaFromSection;
}
if (stringsRegionStart == std::numeric_limits<std::uint32_t>::max())
throw std::runtime_error("could not determine strings region start");
std::uint32_t rawLimit = ((edataSection.SizeOfRawData + fileAlignment - 1u) / fileAlignment) * fileAlignment;
const auto dllName = ReadCStr(pe, RvaToOffset(pe, exportTable.Name, peHeaderOffset, numberOfSections, sizeOfOptionalHeader));
const bool repackDllName = exportTable.Name >= edataSection.VirtualAddress &&
exportTable.Name - edataSection.VirtualAddress < rawLimit;
if (repackDllName) stringsRegionStart = std::min(stringsRegionStart, exportTable.Name - edataSection.VirtualAddress);
struct Forwarder {
std::size_t EntryOffset;
std::string Target;
};
std::vector<Forwarder> forwarders;
std::vector<std::uint32_t> directRvas;
const auto functionsArrayOffset = RvaToOffset(pe, exportTable.AddressOfFunctions, peHeaderOffset, numberOfSections, sizeOfOptionalHeader);
for (std::uint32_t index = 0u; index < exportTable.NumberOfFunctions; ++index) {
const auto entryOffset = functionsArrayOffset + index * sizeof(std::uint32_t);
const auto rva = Read<std::uint32_t>(pe, entryOffset);
if (rva < exportDir.VirtualAddress || std::uint64_t{rva} - exportDir.VirtualAddress >= exportDir.Size) {
directRvas.push_back(rva);
continue;
std::vector<std::uint32_t> nameRvas(exportTable.NumberOfNames);
std::vector<std::string> names(exportTable.NumberOfNames);
for (std::uint32_t i = 0u; i < exportTable.NumberOfNames; ++i) {
const auto nameRva = Read<std::uint32_t>(pe, namesArrayOffset + i * 4u);
const std::size_t nameOffset = RvaToOffset(pe, nameRva, peHeaderOffset, numberOfSections, sizeOfOptionalHeader);
const std::string name = ReadCStr(pe, nameOffset);
if (name.empty()) throw std::runtime_error("empty exported name");
nameRvas[i] = nameRva;
names[i] = name;
}
const auto target = ReadCStr(pe, RvaToOffset(pe, rva, peHeaderOffset, numberOfSections, sizeOfOptionalHeader));
if (target.size() + 1u > exportDir.Size - (rva - exportDir.VirtualAddress))
throw std::runtime_error("forwarder string outside export directory");
forwarders.push_back({entryOffset, target});
stringsRegionStart = std::min(stringsRegionStart, rva - edataSection.VirtualAddress);
}
for (const auto rva : directRvas) {
if (rva >= edataSection.VirtualAddress && rva - edataSection.VirtualAddress >= stringsRegionStart)
rawLimit = std::min(rawLimit, rva - edataSection.VirtualAddress);
}
const auto reserveTable = [&](std::uint32_t rva, std::uint64_t size) {
if (rva < edataSection.VirtualAddress || rva - edataSection.VirtualAddress >= rawLimit) return;
const auto end = std::uint64_t{rva - edataSection.VirtualAddress} + size;
if (end > rawLimit) throw std::runtime_error("export table outside edata section");
stringsRegionStart = std::max(stringsRegionStart, static_cast<std::uint32_t>(end));
};
reserveTable(exportDir.VirtualAddress, sizeof(PeExportDirectory));
reserveTable(exportTable.AddressOfFunctions, std::uint64_t{exportTable.NumberOfFunctions} * 4u);
reserveTable(exportTable.AddressOfNames, std::uint64_t{exportTable.NumberOfNames} * 4u);
reserveTable(exportTable.AddressOfNameOrdinals, std::uint64_t{exportTable.NumberOfNames} * 2u);
std::vector<std::string> finalNames(exportTable.NumberOfNames);
for (std::uint32_t i = 0u; i < exportTable.NumberOfNames; ++i) {
const auto it = nidMap.find(names[i]);
if (it == nidMap.end()) throw std::runtime_error("symbol not in nid map: " + names[i]);
finalNames[i] = it->second;
}
const auto nidMap = ResolveNids(names, libraryName, excludedExports);
std::vector<std::uint8_t> strings;
std::unordered_map<std::string, std::uint32_t> stringRvas;
const auto storeString = [&](const std::string& name) {
const auto existing = stringRvas.find(name);
if (existing != stringRvas.end()) return existing->second;
const auto rva = edataSection.VirtualAddress + stringsRegionStart + static_cast<std::uint32_t>(strings.size());
stringRvas.emplace(name, rva);
strings.insert(strings.end(), name.begin(), name.end());
strings.push_back(0u);
return rva;
};
std::vector<std::uint32_t> forwarderRvas;
for (const auto& forwarder : forwarders) forwarderRvas.push_back(storeString(forwarder.Target));
if (!forwarders.empty() && (std::uint64_t{edataSection.VirtualAddress} + stringsRegionStart < exportDir.VirtualAddress ||
std::uint64_t{edataSection.VirtualAddress} + stringsRegionStart + strings.size() > std::uint64_t{exportDir.VirtualAddress} + exportDir.Size))
throw std::runtime_error("not enough export directory space to repack forwarders");
const auto dllNameRva = repackDllName ? storeString(dllName) : exportTable.Name;
std::vector<std::uint32_t> finalNameRvas;
for (const auto& name : finalNames) finalNameRvas.push_back(storeString(name));
const auto requiredSize = std::uint64_t{stringsRegionStart} + strings.size();
if (requiredSize > rawLimit)
throw std::runtime_error("not enough raw space in edata section to repack export names");
const auto directoryEnd = std::max(std::uint64_t{exportDir.VirtualAddress} + exportDir.Size,
std::uint64_t{edataSection.VirtualAddress} + requiredSize);
for (const auto rva : directRvas) {
if (rva != 0u && rva >= exportDir.VirtualAddress && std::uint64_t{rva} < directoryEnd)
throw std::runtime_error("repacked export directory would cover a direct export");
}
const auto directorySize = directoryEnd - exportDir.VirtualAddress;
if (directorySize > std::numeric_limits<std::uint32_t>::max()) throw std::runtime_error("export directory too large");
const auto stringFileOffset = static_cast<std::size_t>(edataSection.PointerToRawData) + stringsRegionStart;
if (stringFileOffset > pe.size() || strings.size() > pe.size() - stringFileOffset)
throw std::runtime_error("repack write out of file bounds");
std::memcpy(pe.data() + stringFileOffset, strings.data(), strings.size());
for (std::size_t index = 0u; index < finalNameRvas.size(); ++index)
Write(pe, namesArrayOffset + index * 4u, finalNameRvas[index]);
for (std::size_t index = 0u; index < forwarders.size(); ++index)
Write(pe, forwarders[index].EntryOffset, forwarderRvas[index]);
Write(pe, exportDirOffset + offsetof(PeExportDirectory, Name), dllNameRva);
Write(pe, dataDirectoryOffset + offsetof(PeDataDirectory, Size), static_cast<std::uint32_t>(directorySize));
const std::size_t edataSectionOffset = FindSectionOffsetByRva(pe, exportDir.VirtualAddress, peHeaderOffset, numberOfSections, sizeOfOptionalHeader);
const auto edataSection = Read<PeSectionHeader>(pe, edataSectionOffset);
const std::uint32_t newVirtualSize = std::max(edataSection.VirtualSize, static_cast<std::uint32_t>(requiredSize));
auto patchedSection = edataSection;
patchedSection.VirtualSize = newVirtualSize;
Write(pe, edataSectionOffset, patchedSection);
const auto fileAlignment = Read<std::uint32_t>(pe, optionalHeaderOffset + 36u);
if (fileAlignment == 0u) throw std::runtime_error("invalid file alignment");
struct ExportName {
std::string Name;
std::uint32_t Rva;
std::uint16_t Ordinal;
};
std::uint32_t stringsRegionStart = std::numeric_limits<std::uint32_t>::max();
for (std::uint32_t i = 0u; i < exportTable.NumberOfNames; ++i) {
const std::uint32_t rvaFromSection = nameRvas[i] - edataSection.VirtualAddress;
if (rvaFromSection < stringsRegionStart) stringsRegionStart = rvaFromSection;
}
if (stringsRegionStart == std::numeric_limits<std::uint32_t>::max())
throw std::runtime_error("could not determine strings region start");
std::uint32_t rawLimit = ((edataSection.SizeOfRawData + fileAlignment - 1u) / fileAlignment) * fileAlignment;
const auto dllName = ReadCStr(pe, RvaToOffset(pe, exportTable.Name, peHeaderOffset, numberOfSections, sizeOfOptionalHeader));
const bool repackDllName = exportTable.Name >= edataSection.VirtualAddress &&
exportTable.Name - edataSection.VirtualAddress < rawLimit;
if (repackDllName) stringsRegionStart = std::min(stringsRegionStart, exportTable.Name - edataSection.VirtualAddress);
struct Forwarder {
std::size_t EntryOffset;
std::string Target;
};
std::vector<Forwarder> forwarders;
std::vector<std::uint32_t> directRvas;
const auto functionsArrayOffset = RvaToOffset(pe, exportTable.AddressOfFunctions, peHeaderOffset, numberOfSections, sizeOfOptionalHeader);
for (std::uint32_t index = 0u; index < exportTable.NumberOfFunctions; ++index) {
const auto entryOffset = functionsArrayOffset + index * sizeof(std::uint32_t);
const auto rva = Read<std::uint32_t>(pe, entryOffset);
if (rva < exportDir.VirtualAddress || std::uint64_t{rva} - exportDir.VirtualAddress >= exportDir.Size) {
directRvas.push_back(rva);
continue;
}
const auto target = ReadCStr(pe, RvaToOffset(pe, rva, peHeaderOffset, numberOfSections, sizeOfOptionalHeader));
if (target.size() + 1u > exportDir.Size - (rva - exportDir.VirtualAddress))
throw std::runtime_error("forwarder string outside export directory");
forwarders.push_back({entryOffset, target});
stringsRegionStart = std::min(stringsRegionStart, rva - edataSection.VirtualAddress);
}
for (const auto rva : directRvas) {
if (rva >= edataSection.VirtualAddress && rva - edataSection.VirtualAddress >= stringsRegionStart)
rawLimit = std::min(rawLimit, rva - edataSection.VirtualAddress);
}
const auto reserveTable = [&](std::uint32_t rva, std::uint64_t size) {
if (rva < edataSection.VirtualAddress || rva - edataSection.VirtualAddress >= rawLimit) return;
const auto end = std::uint64_t{rva - edataSection.VirtualAddress} + size;
if (end > rawLimit) throw std::runtime_error("export table outside edata section");
stringsRegionStart = std::max(stringsRegionStart, static_cast<std::uint32_t>(end));
};
reserveTable(exportDir.VirtualAddress, sizeof(PeExportDirectory));
reserveTable(exportTable.AddressOfFunctions, std::uint64_t{exportTable.NumberOfFunctions} * 4u);
reserveTable(exportTable.AddressOfNames, std::uint64_t{exportTable.NumberOfNames} * 4u);
reserveTable(exportTable.AddressOfNameOrdinals, std::uint64_t{exportTable.NumberOfNames} * 2u);
std::vector<std::string> finalNames(exportTable.NumberOfNames);
for (std::uint32_t i = 0u; i < exportTable.NumberOfNames; ++i) {
const auto it = nidMap.find(names[i]);
if (it == nidMap.end()) throw std::runtime_error("symbol not in nid map: " + names[i]);
finalNames[i] = it->second;
}
std::vector<std::uint8_t> strings;
std::unordered_map<std::string, std::uint32_t> stringRvas;
const auto storeString = [&](const std::string& name) {
const auto existing = stringRvas.find(name);
if (existing != stringRvas.end()) return existing->second;
const auto rva = edataSection.VirtualAddress + stringsRegionStart + static_cast<std::uint32_t>(strings.size());
stringRvas.emplace(name, rva);
strings.insert(strings.end(), name.begin(), name.end());
strings.push_back(0u);
return rva;
};
std::vector<std::uint32_t> forwarderRvas;
for (const auto& forwarder : forwarders) forwarderRvas.push_back(storeString(forwarder.Target));
if (!forwarders.empty() && (std::uint64_t{edataSection.VirtualAddress} + stringsRegionStart < exportDir.VirtualAddress ||
std::uint64_t{edataSection.VirtualAddress} + stringsRegionStart + strings.size() > std::uint64_t{exportDir.VirtualAddress} + exportDir.Size))
throw std::runtime_error("not enough export directory space to repack forwarders");
const auto dllNameRva = repackDllName ? storeString(dllName) : exportTable.Name;
std::vector<std::uint32_t> finalNameRvas;
for (const auto& name : finalNames) finalNameRvas.push_back(storeString(name));
const auto requiredSize = std::uint64_t{stringsRegionStart} + strings.size();
if (requiredSize > rawLimit)
throw std::runtime_error("not enough raw space in edata section to repack export names");
const auto directoryEnd = std::max(std::uint64_t{exportDir.VirtualAddress} + exportDir.Size,
std::uint64_t{edataSection.VirtualAddress} + requiredSize);
for (const auto rva : directRvas) {
if (rva != 0u && rva >= exportDir.VirtualAddress && std::uint64_t{rva} < directoryEnd)
throw std::runtime_error("repacked export directory would cover a direct export");
}
const auto directorySize = directoryEnd - exportDir.VirtualAddress;
if (directorySize > std::numeric_limits<std::uint32_t>::max()) throw std::runtime_error("export directory too large");
const auto stringFileOffset = static_cast<std::size_t>(edataSection.PointerToRawData) + stringsRegionStart;
if (stringFileOffset > pe.size() || strings.size() > pe.size() - stringFileOffset)
throw std::runtime_error("repack write out of file bounds");
std::memcpy(pe.data() + stringFileOffset, strings.data(), strings.size());
for (std::size_t index = 0u; index < finalNameRvas.size(); ++index)
Write(pe, namesArrayOffset + index * 4u, finalNameRvas[index]);
for (std::size_t index = 0u; index < forwarders.size(); ++index)
Write(pe, forwarders[index].EntryOffset, forwarderRvas[index]);
Write(pe, exportDirOffset + offsetof(PeExportDirectory, Name), dllNameRva);
Write(pe, dataDirectoryOffset + offsetof(PeDataDirectory, Size), static_cast<std::uint32_t>(directorySize));
const std::uint32_t newVirtualSize = std::max(edataSection.VirtualSize, static_cast<std::uint32_t>(requiredSize));
auto patchedSection = edataSection;
patchedSection.VirtualSize = newVirtualSize;
Write(pe, edataSectionOffset, patchedSection);
struct ExportName {
std::string Name;
std::uint32_t Rva;
std::uint16_t Ordinal;
};
std::vector<ExportName> exportNames;
exportNames.reserve(exportTable.NumberOfNames);
for (std::size_t i = 0; i < exportTable.NumberOfNames; ++i) {
const auto nameRva = Read<std::uint32_t>(pe, namesArrayOffset + i * sizeof(std::uint32_t));
const auto ordinal = Read<std::uint16_t>(pe, ordinalsArrayOffset + i * sizeof(std::uint16_t));
if (ordinal >= exportTable.NumberOfFunctions)
throw std::runtime_error("export name ordinal out of bounds");
const auto nameOffset = RvaToOffset(pe, nameRva, peHeaderOffset, numberOfSections, sizeOfOptionalHeader);
exportNames.push_back({ReadCStr(pe, nameOffset), nameRva, ordinal});
}
std::sort(exportNames.begin(), exportNames.end(), [](const ExportName& left, const ExportName& right) { return left.Name < right.Name; });
for (std::size_t i = 0; i < exportNames.size(); ++i) {
if (i != 0 && exportNames[i - 1].Name == exportNames[i].Name)
throw std::runtime_error("duplicate patched export name: " + exportNames[i].Name);
Write(pe, namesArrayOffset + i * sizeof(std::uint32_t), exportNames[i].Rva);
Write(pe, ordinalsArrayOffset + i * sizeof(std::uint16_t), exportNames[i].Ordinal);
}
std::vector<ExportName> exportNames;
exportNames.reserve(exportTable.NumberOfNames);
for (std::size_t i = 0; i < exportTable.NumberOfNames; ++i) {
const auto nameRva = Read<std::uint32_t>(pe, namesArrayOffset + i * sizeof(std::uint32_t));
const auto ordinal = Read<std::uint16_t>(pe, ordinalsArrayOffset + i * sizeof(std::uint16_t));
if (ordinal >= exportTable.NumberOfFunctions)
throw std::runtime_error("export name ordinal out of bounds");
const auto nameOffset = RvaToOffset(pe, nameRva, peHeaderOffset, numberOfSections, sizeOfOptionalHeader);
exportNames.push_back({ReadCStr(pe, nameOffset), nameRva, ordinal});
}
std::sort(exportNames.begin(), exportNames.end(), [](const ExportName& left, const ExportName& right) { return left.Name < right.Name; });
for (std::size_t i = 0; i < exportNames.size(); ++i) {
if (i != 0 && exportNames[i - 1].Name == exportNames[i].Name)
throw std::runtime_error("duplicate patched export name: " + exportNames[i].Name);
Write(pe, namesArrayOffset + i * sizeof(std::uint32_t), exportNames[i].Rva);
Write(pe, ordinalsArrayOffset + i * sizeof(std::uint16_t), exportNames[i].Ordinal);
}
constexpr std::size_t kImportDirIndex = 1u;
@@ -250,6 +253,20 @@ void PeNidPatcher::PatchNids(std::vector<std::uint8_t>& pe, const std::string& l
std::uint32_t FirstThunk;
};
if (options.executable) {
for (const auto index : {4u, 11u, 13u}) {
const auto directory = Read<PeDataDirectory>(pe, dataDirectoryOffset + index * sizeof(PeDataDirectory));
if (directory.VirtualAddress || directory.Size) throw std::runtime_error("signed, bound or delay-loaded PE imports are not supported in --exec mode");
}
}
struct ImportUse {
std::size_t NameOffset;
std::size_t LookupOffset;
std::size_t AddressOffset;
std::string Name;
};
std::vector<ImportUse> imports;
std::vector<std::string> importNames;
std::size_t importDescOffset = RvaToOffset(pe, importDir.VirtualAddress, peHeaderOffset, numberOfSections, sizeOfOptionalHeader);
while (true) {
@@ -286,19 +303,9 @@ void PeNidPatcher::PatchNids(std::vector<std::uint8_t>& pe, const std::string& l
const std::string funcName = ReadCStr(pe, hintNameOffset + 2u);
if (!funcName.empty()) {
const bool hasNidPostfix = funcName.size() >= kNidPostfixLen &&
funcName.compare(funcName.size() - kNidPostfixLen, kNidPostfixLen, kNidPostfix) == 0;
const bool hasScePrefix = funcName.size() >= 3u &&
std::tolower(static_cast<unsigned char>(funcName[0])) == 's' &&
std::tolower(static_cast<unsigned char>(funcName[1])) == 'c' &&
std::tolower(static_cast<unsigned char>(funcName[2])) == 'e';
if (hasNidPostfix || hasScePrefix) {
const std::string nid = ResolveOneName(funcName);
if (nid.size() > funcName.size())
throw std::runtime_error("import NID longer than original name: " + funcName);
std::memcpy(pe.data() + hintNameOffset + 2u, nid.data(), nid.size());
pe[hintNameOffset + 2u + nid.size()] = 0u;
}
const auto addressOffset = RvaToOffset(pe, desc.FirstThunk, peHeaderOffset, numberOfSections, sizeOfOptionalHeader) + thunkOffset - RvaToOffset(pe, thunkRva, peHeaderOffset, numberOfSections, sizeOfOptionalHeader);
imports.push_back({hintNameOffset + 2u, thunkOffset, addressOffset, funcName});
importNames.push_back(funcName);
}
}
@@ -307,6 +314,72 @@ void PeNidPatcher::PatchNids(std::vector<std::uint8_t>& pe, const std::string& l
importDescOffset += sizeof(PeImportDescriptor);
}
const auto importMap = ResolveImportNids(importNames, excludedExports, options);
if (!options.executable) {
for (const auto& item : imports) {
const auto& name = importMap.at(item.Name);
if (name.size() > item.Name.size()) throw std::runtime_error("import NID longer than original name: " + item.Name);
std::memcpy(pe.data() + item.NameOffset, name.c_str(), name.size() + 1);
}
return;
}
std::vector<std::uint8_t> strings;
std::unordered_map<std::string, std::uint32_t> stringOffsets;
for (const auto& item : imports) {
const auto& name = importMap.at(item.Name);
if (name == item.Name || stringOffsets.contains(name)) continue;
if (strings.size() & 1u) strings.push_back(0);
if (strings.size() > 0xffffffffu - name.size() - 3u) throw std::runtime_error("PE import string table overflow");
stringOffsets.emplace(name, static_cast<std::uint32_t>(strings.size()));
strings.insert(strings.end(), {0, 0});
strings.insert(strings.end(), name.begin(), name.end());
strings.push_back(0);
}
if (strings.empty()) return;
const auto fileAlignment = Read<std::uint32_t>(pe, optionalHeaderOffset + 36u);
const auto sectionAlignment = Read<std::uint32_t>(pe, optionalHeaderOffset + 32u);
const auto align = [](std::uint64_t value, std::uint32_t alignment) {
if (!alignment || (alignment & (alignment - 1u))) throw std::runtime_error("invalid PE alignment");
const auto result = (value + alignment - 1u) & ~std::uint64_t{alignment - 1u};
if (result > 0xffffffffu) throw std::runtime_error("PE section overflow");
return static_cast<std::uint32_t>(result);
};
const auto newHeader = sectionTableOffset + numberOfSections * sizeof(PeSectionHeader);
auto headerLimit = std::size_t{Read<std::uint32_t>(pe, optionalHeaderOffset + 60u)};
std::uint64_t virtualEnd = headerLimit;
for (std::uint16_t index = 0; index < numberOfSections; ++index) {
const auto section = Read<PeSectionHeader>(pe, sectionTableOffset + index * sizeof(PeSectionHeader));
if (section.PointerToRawData) headerLimit = std::min(headerLimit, std::size_t{section.PointerToRawData});
virtualEnd = std::max(virtualEnd, std::uint64_t{section.VirtualAddress} + std::max(section.VirtualSize, section.SizeOfRawData));
}
if (numberOfSections == 0xffffu || newHeader + sizeof(PeSectionHeader) > headerLimit || headerLimit > pe.size()) throw std::runtime_error("no room for PE import section header");
PeSectionHeader section{};
const char sectionName[] = ".nidimp";
std::memcpy(section.Name, sectionName, sizeof(sectionName));
section.VirtualSize = static_cast<std::uint32_t>(strings.size());
section.VirtualAddress = align(virtualEnd, sectionAlignment);
section.PointerToRawData = align(pe.size(), fileAlignment);
section.SizeOfRawData = align(strings.size(), fileAlignment);
section.Characteristics = 0x40000040u;
const auto imageSize = align(std::uint64_t{section.VirtualAddress} + section.VirtualSize, sectionAlignment);
const auto rawEnd = std::uint64_t{section.PointerToRawData} + section.SizeOfRawData;
if (rawEnd > 0xffffffffu) throw std::runtime_error("PE file size overflow");
pe.resize(static_cast<std::size_t>(rawEnd), 0);
std::copy(strings.begin(), strings.end(), pe.begin() + section.PointerToRawData);
Write(pe, newHeader, section);
Write(pe, coffHeaderOffset + 2u, static_cast<std::uint16_t>(numberOfSections + 1u));
Write(pe, optionalHeaderOffset + 56u, imageSize);
Write(pe, optionalHeaderOffset + 64u, std::uint32_t{0});
for (const auto& item : imports) {
const auto& name = importMap.at(item.Name);
if (name == item.Name) continue;
const auto rva = section.VirtualAddress + stringOffsets.at(name);
for (const auto offset : {item.LookupOffset, item.AddressOffset}) {
if (magic == 0x20bu) Write(pe, offset, std::uint64_t{rva});
else Write(pe, offset, rva);
}
}
}
}
+10 -3
View File
@@ -28,6 +28,7 @@ void _writeFile(const std::string& path, const std::vector<std::uint8_t>& data)
int Run(int argc, char* argv[]) {
if (argc < 3) {
std::cerr << "Usage: nid_patcher <library_name> [--preserve-exports <unpatched-library> | --preserve-export-list <export-list>] <file> [file2 ...]\n"
" nid_patcher --exec [--only-sce] [--preserve-exports <unpatched-library> | --preserve-export-list <export-list>] <file> [file2 ...]\n"
" nid_patcher --write-export-list <unpatched-library> <export-list>\n";
return 1;
}
@@ -46,14 +47,20 @@ int Run(int argc, char* argv[]) {
return 0;
}
const std::string libraryName = argv[1];
Nid::PatchOptions options;
options.executable = std::string(argv[1]) == "--exec";
const std::string libraryName = options.executable ? "" : argv[1];
std::unordered_set<std::string> excludedExports;
std::vector<std::string> paths;
try {
if (!options.executable && libraryName.starts_with("--")) throw std::runtime_error("unknown option: " + libraryName);
bool hasReference = false;
for (int i = 2; i < argc; ++i) {
const std::string argument = argv[i];
if (argument == "--preserve-exports" || argument == "--preserve-export-list") {
if (argument == "--only-sce") {
if (!options.executable || options.onlySce) throw std::runtime_error("--only-sce requires --exec and may appear once");
options.onlySce = true;
} else if (argument == "--preserve-exports" || argument == "--preserve-export-list") {
if (hasReference || i + 1 == argc) throw std::runtime_error(argument + " requires exactly one reference");
hasReference = true;
excludedExports = argument == "--preserve-exports" ? Nid::ReadExportExclusions(argv[++i]) : Nid::ReadExportList(argv[++i]);
@@ -72,7 +79,7 @@ int Run(int argc, char* argv[]) {
try {
auto binary = _readFile(path);
const auto patcher = Nid::MakePatcher(binary);
patcher->PatchNids(binary, libraryName, excludedExports);
patcher->PatchNids(binary, libraryName, excludedExports, options);
_writeFile(path, binary);
std::cout << "OK: " << path << "\n";
} catch (const std::exception& e) {
+2 -2
View File
@@ -4,7 +4,7 @@ import sys
import unittest
from pathlib import Path
from test_symbol_versions import build_image, patch, read_dynsym, IMPORTS, NID_NAME, SHN_UNDEF, STT_FUNC
from test_symbol_versions import build_image, patch, read_dynsym, IMPORTS, NID_NAME, SHN_UNDEF, STT_FUNC, PATCHER
def nid(name):
@@ -21,6 +21,7 @@ def image(names, addresses=None):
return bytes(build_image(entries, False))
@unittest.skipUnless(PATCHER is not None and PATCHER.is_file(), "nid_patcher executable required")
class ElfDuplicateTests(unittest.TestCase):
def test_exports_patched_to_one_name_are_rejected(self):
cases = (
@@ -65,5 +66,4 @@ class ElfDuplicateTests(unittest.TestCase):
if __name__ == "__main__":
PATCHER = Path(sys.argv.pop(1)).resolve()
unittest.main()
+2 -2
View File
@@ -3,9 +3,10 @@ import sys
import unittest
from pathlib import Path
from test_symbol_versions import build_image, symbols, patch, SHOFF
from test_symbol_versions import build_image, symbols, patch, SHOFF, PATCHER
@unittest.skipUnless(PATCHER is not None and PATCHER.is_file(), "nid_patcher executable required")
class ElfLayoutTests(unittest.TestCase):
def test_declared_layout_must_match_supported_tables(self):
changes = ((5, "B", 2), (0x3a, "H", 32),
@@ -44,5 +45,4 @@ class ElfLayoutTests(unittest.TestCase):
if __name__ == "__main__":
PATCHER = Path(sys.argv.pop(1)).resolve()
unittest.main()
+3 -2
View File
@@ -3,7 +3,7 @@ import sys
import unittest
from pathlib import Path
from test_symbol_versions import build_image, symbols, patch, HASH_OFFSET, SHOFF, read_dynsym, gnu_hash, sections
from test_symbol_versions import build_image, symbols, patch, HASH_OFFSET, SHOFF, read_dynsym, gnu_hash, sections, PATCHER
def sysv_hash(name):
@@ -31,6 +31,7 @@ def sysv_lookup(image, name):
return None
@unittest.skipUnless(PATCHER is not None and PATCHER.is_file(), "nid_patcher executable required")
class GnuHashTests(unittest.TestCase):
def image(self, buckets=4, bloom=1, shift=6, symoffset=3):
image = build_image(symbols(False), False)
@@ -95,6 +96,7 @@ class GnuHashTests(unittest.TestCase):
self.assertEqual(struct.unpack_from("<I", after, HASH_OFFSET)[0], 0)
@unittest.skipUnless(PATCHER is not None and PATCHER.is_file(), "nid_patcher executable required")
class SysvHashTests(unittest.TestCase):
def image(self, both, buckets):
entries = symbols(False)
@@ -153,5 +155,4 @@ class SysvHashTests(unittest.TestCase):
if __name__ == "__main__":
PATCHER = Path(sys.argv.pop(1)).resolve()
unittest.main()
+10 -3
View File
@@ -7,6 +7,7 @@ import subprocess
import sys
import tempfile
from pathlib import Path
from test_symbol_versions import find_patcher, PATCHER
SUFFIX = bytes([0x51, 0x8D, 0x64, 0xA6, 0x35, 0xDE, 0xD8, 0xC1, 0xE6, 0xB0, 0x39, 0xB1, 0xC3, 0xE5, 0x52, 0x30])
BASE64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-"
@@ -262,7 +263,7 @@ def check_export_list(patcher, directory):
def check_refusals(patcher, directory):
growing = [("_ab", 0, 0x100), ("_cd", 0, 0x108)]
result, _ = patch(patcher, directory, "signed.dylib", build_dylib(growing, [], signed=True))
assert result.returncode != 0 and "signed Mach-O images cannot grow their export trie" in result.stdout + result.stderr, (result.stdout, result.stderr)
assert result.returncode != 0 and "signed Mach-O images cannot grow linkedit data" in result.stdout + result.stderr, (result.stdout, result.stderr)
result, _ = patch(patcher, directory, "opcodes.dylib", build_dylib([("_puts_nid_postfix", 0, 0x100)], [], bind_opcodes=True))
assert result.returncode != 0 and "bind opcodes are not supported" in result.stdout + result.stderr, (result.stdout, result.stderr)
result, _ = patch(patcher, directory, "long.dylib", build_dylib([("_puts_nid_postfix", 0, 0x100)], [("_sceXy", 1)]))
@@ -270,8 +271,14 @@ def check_refusals(patcher, directory):
def main():
patcher = Path(sys.argv[1]).resolve()
with tempfile.TemporaryDirectory(prefix="anyps5-macho-nid-") as directory:
if len(sys.argv) > 1:
patcher = Path(sys.argv[1]).resolve()
else:
patcher = PATCHER or find_patcher()
if patcher is None:
print("nid_patcher executable required")
sys.exit(0)
with tempfile.TemporaryDirectory(prefix="anyps5-macho-nid-a\u00e7\u00e3o-\u65e5\u672c-") as directory:
check_shrinking(patcher, directory)
check_growing(patcher, directory)
check_preserved(patcher, directory)
+2 -4
View File
@@ -5,16 +5,14 @@ import tempfile
import unittest
from pathlib import Path
from test_macho_nids import build_dylib, exports_of, nid
from test_macho_nids import build_dylib, exports_of, nid, PATCHER
try:
import resource
except ImportError:
resource = None
PATCHER = Path(sys.argv.pop(1)).resolve()
@unittest.skipUnless(PATCHER is not None and PATCHER.is_file(), "nid_patcher executable required")
class NativePathTests(unittest.TestCase):
def invoke(self, *arguments):
return subprocess.run([str(PATCHER), "libtest", *map(str, arguments)],
+201 -2
View File
@@ -5,7 +5,7 @@ import tempfile
import unittest
from pathlib import Path
from test_macho_nids import build_dylib, exports_of, nid
from test_macho_nids import build_dylib, exports_of, nid, PATCHER
def reference(*, headers=False, address=0x2000, ordinal=0):
@@ -100,6 +100,7 @@ def forwarding_exports(image, bits):
return string(table[4]), names, targets, addresses, (export_rva, export_size)
@unittest.skipUnless(PATCHER is not None and PATCHER.is_file(), "nid_patcher executable required")
class ReferenceTests(unittest.TestCase):
def patch_with_reference(self, data):
with tempfile.TemporaryDirectory() as tmp:
@@ -135,6 +136,7 @@ class ReferenceTests(unittest.TestCase):
self.assertEqual(image, original)
@unittest.skipUnless(PATCHER is not None and PATCHER.is_file(), "nid_patcher executable required")
class ForwardingExportTests(unittest.TestCase):
def patch(self, image):
with tempfile.TemporaryDirectory() as temporary:
@@ -241,6 +243,203 @@ class ForwardingExportTests(unittest.TestCase):
self.assertEqual(after, image)
class ExecutableImportTests(unittest.TestCase):
names = ["foo", "foo_nid_postfix", "z", "sceExample", "helper_nid_no_patch", "plain_nid_no_patch_cut", "SDL_Init", "scePreserved", "hostFunctionLongName", "alias_nid_disambig12_nid_postfix"]
def pe(self, bits=64):
image = bytearray(2048)
image[:2] = b"MZ"
struct.pack_into("<I", image, 0x3c, 0x40)
optional_size = 240 if bits == 64 else 224
struct.pack_into("<IHHIIIHH", image, 0x40, 0x4550, 0x8664 if bits == 64 else 0x14c, 1, 0, 0, 0, optional_size, 0x22)
struct.pack_into("<H", image, 0x58, 0x20b if bits == 64 else 0x10b)
struct.pack_into("<II", image, 0x58 + 32, 4096, 512)
struct.pack_into("<II", image, 0x58 + 56, 8192, 512)
directory = 0x58 + (112 if bits == 64 else 96)
struct.pack_into("<I", image, directory - 4, 16)
struct.pack_into("<II", image, directory + 8, 0x1000, 40)
struct.pack_into("<8sIIIIIIHHI", image, 0x58 + optional_size, b".idata", 1536, 0x1000, 1536, 512, 0, 0, 0, 0, 0xc0000040)
struct.pack_into("<IIIII", image, 512, 0x1040, 0, 0, 0x1028, 0x10c0)
image[552:561] = b"test.prx\0"
offset = 0x380
for index, name in enumerate(self.names):
entry = name.encode() + b"\0"
struct.pack_into("<H", image, offset, 0)
image[offset + 2:offset + 2 + len(entry)] = entry
for table in (576, 704):
struct.pack_into("<Q" if bits == 64 else "<I", image, table + index * (bits // 8), offset - 512 + 0x1000)
offset += len(entry) + 2
return image
def elf(self):
from test_symbol_versions import build_image, SHOFF
entries = [(None, 1, 0, 0, 0)] + [(name, 1, 0, 2, 0) for name in self.names] + [("application_export", 1, 9, 2, 0x400100)]
import test_symbol_versions
from unittest.mock import patch
with patch.object(test_symbol_versions, "DYNSYM_OFFSET", 0xa00):
image = build_image(entries, False)
struct.pack_into("<H", image, 16, 2)
struct.pack_into("<Q", image, 32, 64)
struct.pack_into("<HH", image, 54, 56, 2)
struct.pack_into("<IIQQQQQQ", image, 64, 1, 5, 0, 0x400000, 0x400000, 4096, 8192, 4096)
struct.pack_into("<IIQQQQQQ", image, 120, 2, 6, 0x700, 0x400700, 0x400700, 48, 48, 8)
struct.pack_into("<H", image, 60, 5)
struct.pack_into("<IIQQQQIIQQ", image, SHOFF + 4 * 64, 0, 6, 3, 0x400700, 0x700, 48, 2, 0, 8, 16)
size = struct.unpack_from("<Q", image, SHOFF + 2 * 64 + 32)[0]
struct.pack_into("<qQqQqQ", image, 0x700, 5, 0x400300, 10, size, 0, 0)
return image
def macho(self):
image = bytearray(build_dylib([("_application_export", 0, 0x100)], [("_" + name, 1 if index % 2 else 2) for index, name in enumerate(self.names)]))
struct.pack_into("<I", image, 12, 2)
return image
def read_names(self, image, kind):
if kind == "elf":
from test_symbol_versions import read_dynsym
return [name for name, section, _ in read_dynsym(image) if name and section == 0]
if kind == "macho":
from test_macho_nids import chained_imports
return [name[1:] for name, _ in chained_imports(image)]
bits = 64 if kind == "pe64" else 32
optional_size = 240 if bits == 64 else 224
count = struct.unpack_from("<H", image, 0x46)[0]
sections = [struct.unpack_from("<8sIIIIIIHHI", image, 0x58 + optional_size + index * 40) for index in range(count)]
result = []
for index in range(len(self.names)):
rva = struct.unpack_from("<Q" if bits == 64 else "<I", image, 576 + index * (bits // 8))[0]
self.assertEqual(rva, struct.unpack_from("<Q" if bits == 64 else "<I", image, 704 + index * (bits // 8))[0])
section = next(s for s in sections if s[2] <= rva < s[2] + s[3])
offset = section[4] + rva - section[2] + 2
result.append(image[offset:image.index(0, offset)].decode())
return result
def test_shared_import_rules_and_filter_in_all_formats(self):
for kind, build in (("pe64", self.pe), ("pe32", lambda: self.pe(32)), ("elf", self.elf), ("macho", self.macho)):
for only_sce in (False, True):
with self.subTest(kind=kind, only_sce=only_sce), tempfile.TemporaryDirectory() as directory:
path = Path(directory) / "arbitrary.extension"
image = build()
path.write_bytes(image)
excluded = Path(directory) / "preserved.txt"
excluded.write_bytes(b"scePreserved\n")
args = [str(PATCHER), "--exec", "--preserve-export-list", str(excluded)]
if only_sce:
args.append("--only-sce")
result = subprocess.run([*args, str(path)], capture_output=True, text=True, timeout=20)
self.assertEqual(result.returncode, 0, result.stdout + result.stderr)
expected = ["foo", nid("foo"), nid("z"), nid("sceExample"), "helper_nid_no_patch", "plain", "SDL_Init", "scePreserved", nid("hostFunctionLongName"), nid("alias")]
if only_sce:
expected[2] = "z"
expected[5] = "plain_nid_no_patch_cut"
expected[8] = "hostFunctionLongName"
self.assertCountEqual(self.read_names(path.read_bytes(), kind), expected)
if kind == "macho":
self.assertEqual(exports_of(path.read_bytes()), exports_of(image))
if kind == "elf":
from test_symbol_versions import read_dynsym
self.assertIn(("application_export", 9, 0x400100), read_dynsym(path.read_bytes()))
def test_exec_accepts_macho_without_exports(self):
from test_macho_nids import LC_DYLD_EXPORTS_TRIE, command_fields
image = self.macho()
command = command_fields(image)[LC_DYLD_EXPORTS_TRIE][0]
struct.pack_into("<II", image, command + 8, 0, 0)
with tempfile.TemporaryDirectory() as directory:
path = Path(directory) / "program"
path.write_bytes(image)
result = subprocess.run([str(PATCHER), "--exec", str(path)], capture_output=True, text=True, timeout=20)
self.assertEqual(result.returncode, 0, result.stderr)
def test_elf_without_sections_rejects_dynamic_segment_without_writing(self):
image = self.elf()
struct.pack_into("<Q", image, 40, 0)
struct.pack_into("<HH", image, 60, 0, 0)
for flags in ([], ["--only-sce"]):
with self.subTest(flags=flags), tempfile.TemporaryDirectory() as directory:
path = Path(directory) / "program"
path.write_bytes(image)
result = subprocess.run([str(PATCHER), "--exec", *flags, str(path)], capture_output=True, text=True, timeout=20)
self.assertEqual(result.returncode, 2, result.stdout + result.stderr)
self.assertIn("PT_DYNAMIC without a .dynsym section", result.stderr)
self.assertEqual(path.read_bytes(), image)
def test_elf_without_sections_or_dynamic_segment_is_unchanged(self):
image = self.elf()
struct.pack_into("<Q", image, 40, 0)
struct.pack_into("<HH", image, 60, 0, 0)
struct.pack_into("<H", image, 56, 1)
with tempfile.TemporaryDirectory() as directory:
path = Path(directory) / "program"
path.write_bytes(image)
result = subprocess.run([str(PATCHER), "--exec", str(path)], capture_output=True, text=True, timeout=20)
self.assertEqual(result.returncode, 0, result.stdout + result.stderr)
self.assertEqual(path.read_bytes(), image)
def test_elf_short_imports_grow_without_overwriting_bss(self):
from test_symbol_versions import sections
self.names = ["a", "b", "c", "d"]
image = self.elf()
with tempfile.TemporaryDirectory() as directory:
path = Path(directory) / "program"
path.write_bytes(image)
result = subprocess.run([str(PATCHER), "--exec", str(path)], capture_output=True, text=True, timeout=20)
self.assertEqual(result.returncode, 0, result.stderr)
after = path.read_bytes()
self.assertCountEqual(self.read_names(after, "elf"), [nid(name) for name in self.names])
offset = struct.unpack_from("<Q", after, 32)[0]
count = struct.unpack_from("<H", after, 56)[0]
segments = [struct.unpack_from("<IIQQQQQQ", after, offset + index * 56) for index in range(count)]
original = next(segment for segment in segments if segment[0] == 1 and segment[3] == 0x400000)
self.assertEqual(original[5:7], (4096, 8192))
phdr = next(segment for segment in segments if segment[0] == 6)
load = next(segment for segment in segments if segment[0] == 1 and segment[2] == offset)
self.assertEqual(phdr[2:4], load[2:4])
strings = next(section for section in sections(after) if section[1] == 3)
self.assertEqual(struct.unpack_from("<Q", after, 0x708)[0], strings[3])
self.assertEqual(struct.unpack_from("<Q", after, 0x718)[0], strings[5])
self.assertGreater(strings[4], len(image))
def test_pe_own_exports_remain_unchanged(self):
image = self.pe()
struct.pack_into("<II", image, 0x58 + 112, 0x1500, 96)
struct.pack_into("<IIHHIIIIIII", image, 0x700, 0, 0, 0, 0, 0x1540, 1, 1, 1, 0x1528, 0x152c, 0x1530)
struct.pack_into("<IIH", image, 0x728, 0x1100, 0x1550, 0)
image[0x740:0x748] = b"app.exe\0"
image[0x750:0x75b] = b"own_export\0"
with tempfile.TemporaryDirectory() as directory:
path = Path(directory) / "program"
path.write_bytes(image)
result = subprocess.run([str(PATCHER), "--exec", str(path)], capture_output=True, text=True, timeout=20)
self.assertEqual(result.returncode, 0, result.stderr)
self.assertEqual(path.read_bytes()[0x700:0x760], image[0x700:0x760])
def test_unsupported_exec_layouts_fail_without_writing(self):
signed = bytearray(build_dylib([], [("_sceExample", 1)], signed=True))
struct.pack_into("<I", signed, 12, 2)
pe = self.pe()
struct.pack_into("<II", pe, 0x58 + 112 + 13 * 8, 0x1400, 32)
full_headers = self.pe()
struct.pack_into("<I", full_headers, 0x58 + 60, 0x58 + 240 + 40)
for image, message in ((signed, "signature"), (pe, "delay-loaded"), (full_headers, "section header"), (b"invalid", "unrecognized")):
with self.subTest(message=message), tempfile.TemporaryDirectory() as directory:
path = Path(directory) / "program"
path.write_bytes(image)
result = subprocess.run([str(PATCHER), "--exec", str(path)], capture_output=True, text=True, timeout=20)
self.assertEqual(result.returncode, 2)
self.assertIn(message, result.stderr)
self.assertEqual(path.read_bytes(), image)
def test_only_sce_requires_exec_and_failure_does_not_write(self):
with tempfile.TemporaryDirectory() as directory:
path = Path(directory) / "program"
image = self.pe()
path.write_bytes(image)
for arguments in (["libtest", "--only-sce"], ["--only-sce"], ["--exec", "--only-sce", "--only-sce"]):
result = subprocess.run([str(PATCHER), *arguments, str(path)], capture_output=True, timeout=20)
self.assertNotEqual(result.returncode, 0)
self.assertEqual(path.read_bytes(), image)
if __name__ == "__main__":
PATCHER = Path(sys.argv.pop(1)).resolve()
unittest.main()
+2 -2
View File
@@ -3,9 +3,10 @@ import sys
import unittest
from pathlib import Path
from test_symbol_versions import build_image, symbols, patch, SHOFF, DYNSYM_OFFSET, read_dynsym, sections, DEFINITIONS
from test_symbol_versions import build_image, symbols, patch, SHOFF, DYNSYM_OFFSET, read_dynsym, sections, DEFINITIONS, PATCHER
@unittest.skipUnless(PATCHER is not None and PATCHER.is_file(), "nid_patcher executable required")
class SharedStringTests(unittest.TestCase):
def test_dynamic_names_keep_original_string_when_export_shares_offset(self):
for tag in (1, 14, 15, 29):
@@ -54,5 +55,4 @@ class SharedStringTests(unittest.TestCase):
if __name__ == "__main__":
PATCHER = Path(sys.argv.pop(1)).resolve()
unittest.main()
+30 -1
View File
@@ -1,3 +1,4 @@
import os
import re
import struct
import subprocess
@@ -198,8 +199,36 @@ def test_unversioned_library_is_still_patched(patcher):
assert len({entry[0] for entry in after if entry[0]}) == len([entry for entry in after if entry[0]])
def find_patcher():
for index, argument in enumerate(sys.argv[1:], 1):
candidate = Path(argument)
if candidate.stem == "nid_patcher" and candidate.is_file():
sys.argv.pop(index)
return candidate.resolve()
env = os.environ.get("ANYPS5_NID_PATCHER") or os.environ.get("NID_PATCHER")
if env and Path(env).is_file():
return Path(env).resolve()
repo_root = Path(__file__).resolve().parents[4]
for candidate in (
repo_root / "build" / "core" / "libs" / "nid_patcher.exe",
repo_root / "build" / "core" / "libs" / "nid_patcher",
):
if candidate.is_file():
return candidate
return None
PATCHER = find_patcher()
def main():
patcher = Path(sys.argv[1]).resolve()
if len(sys.argv) > 1:
patcher = Path(sys.argv[1]).resolve()
else:
patcher = PATCHER or find_patcher()
if patcher is None:
print("nid_patcher executable required")
sys.exit(0)
test_versioned_library_is_rejected(patcher)
test_unversioned_library_is_still_patched(patcher)
print("NID patcher symbol version tests passed")
+2 -2
View File
@@ -3,7 +3,7 @@ import sys
import unittest
from pathlib import Path
from test_symbol_versions import build_image, symbols, patch, SHOFF, DYNSTR_OFFSET, sections
from test_symbol_versions import build_image, symbols, patch, SHOFF, DYNSTR_OFFSET, sections, PATCHER
VERNEED = 0x580
@@ -22,6 +22,7 @@ def image(*, auxiliary=16, count=1, size=32, version=1, auxnext=0, nextrecord=0)
return data
@unittest.skipUnless(PATCHER is not None and PATCHER.is_file(), "nid_patcher executable required")
class VersionRequirementsTests(unittest.TestCase):
def test_auxiliary_record_cannot_leave_declared_section(self):
self.reject(image(auxiliary=32))
@@ -72,5 +73,4 @@ class VersionRequirementsTests(unittest.TestCase):
if __name__ == "__main__":
PATCHER = Path(sys.argv.pop(1)).resolve()
unittest.main()
@@ -0,0 +1 @@
#pragma once
@@ -0,0 +1 @@
#pragma once
@@ -0,0 +1 @@
#pragma once
@@ -0,0 +1 @@
#pragma once
@@ -0,0 +1 @@
#pragma once
+1
View File
@@ -1,3 +1,4 @@
#include "prx/libSceAc3Enc/include/Api.hpp"
#include <cstdint>
#include <cstddef>
#include "SceTypes.hpp"
@@ -0,0 +1,15 @@
#pragma once
#include "SceTypes.hpp"
extern "C" {
int APS5_VABI sceAc3EncCreateEncoder();
int APS5_VABI sceAc3EncDeleteEncoder();
int APS5_VABI sceAc3EncEncode();
int APS5_VABI sceAc3EncEncodeAsync();
int APS5_VABI sceAc3EncFlush();
int APS5_VABI sceAc3EncIsEncoding();
int APS5_VABI sceAc3EncWaitEncode();
}
@@ -0,0 +1,3 @@
#pragma once
#include "prx/libSceAc3Enc/include/Api.hpp"
+1 -1
View File
@@ -1,3 +1,4 @@
#include "prx/libSceAcm/include/Api.hpp"
#include <cstdint>
#include <cstddef>
#include <cstring>
@@ -217,7 +218,6 @@ int APS5_VABI sceAcm_IFFT() {
return 0;
}
int APS5_VABI sceAcmBatchJobNotification() {
NotImplemented_nid_no_patch(__func__);
return 0;
+20
View File
@@ -0,0 +1,20 @@
#pragma once
#include "SceTypes.hpp"
extern "C" {
int APS5_VABI sceAcmBatchStartBuffer(AcmContextId context, const void* batchCommands, size_t batchSize, AcmBatchError* batchError, AcmBatchId* batch);
int APS5_VABI sceAcmBatchStartBuffers(AcmContextId context, uint32_t batchInfoCount, const AcmBatchInfo* const batch_info[], AcmBatchError* batchError, AcmBatchId* batch);
int APS5_VABI sceAcmBatchWait(AcmContextId context, AcmBatchId batch, uint32_t timeout);
int APS5_VABI sceAcmContextCreate(AcmContextId* context);
int APS5_VABI sceAcmContextDestroy(AcmContextId context);
int APS5_VABI sceAcm_ConvReverb_SharedInput(AcmBatchInfo* batch, std::uint32_t mode, AcmReverbInput* input, std::uint32_t outputCount, const AcmReverbFilter* const* filters, const float* gains, const AcmReverbOutput* const* outputs);
int APS5_VABI sceAcm_FFT(AcmBatchInfo* batch, std::int32_t transformSize, std::uint32_t transformCount, std::int32_t inputFormat, const void* const* inputs, std::int32_t outputFormat, void* const* outputs, std::int32_t flags);
int APS5_VABI sceAcm_Panner(AcmBatchInfo* batch, std::uint32_t sourceCount, const void* const* sources, std::uint32_t sourceMode, std::uint32_t outputMode, std::uint32_t channelCount, const void* const* sourceParameters, const void* const* sourceMatrices, const void* const* inputChannels, void* const* outputChannels);
int APS5_VABI sceAcmBatchJobPriority(AcmBatchInfo* batch, std::uint32_t priority);
int APS5_VABI sceAcm_ConvReverb_SharedIr();
int APS5_VABI sceAcm_IFFT();
int APS5_VABI sceAcmBatchJobNotification();
}
+3
View File
@@ -0,0 +1,3 @@
#pragma once
#include "prx/libSceAcm/include/Api.hpp"
@@ -1,4 +1,26 @@
#ifndef CORE_LIBS_PRX_LIBSCEAGC_ACB_INCLUDE_CONTROL_HPP
#define CORE_LIBS_PRX_LIBSCEAGC_ACB_INCLUDE_CONTROL_HPP
#include "SceTypes.hpp"
#include "SceShaders.hpp"
extern "C" {
std::uint32_t* APS5_VABI sceAgcAcbAtomicGds_0900(CommandBuffer* buf, std::uint32_t control, std::uint32_t mode, std::uint32_t gdsOp, std::uint32_t src, std::uint16_t offset, std::uint16_t index, std::uint32_t mask, std::uint32_t data, std::uint64_t compareData, std::uint64_t extraData);
std::uint32_t* APS5_VABI sceAgcAcbJump(CommandBuffer* buf, std::uint8_t cachePolicy, const std::uint32_t* target, std::uint32_t sizeInDwords);
uint32_t APS5_VABI sceAgcAcbJumpGetSize(void);
uint32_t* APS5_VABI sceAgcAcbResetQueue(CommandBuffer* buf, uint32_t op, uint32_t value);
std::uint32_t* APS5_VABI sceAgcAcbRewind(CommandBuffer* buf, std::uint32_t initialState);
std::uint32_t APS5_VABI sceAgcAcbRewindGetSize();
std::uint32_t* APS5_VABI sceAgcAcbWaitUntilSafeForRendering(CommandBuffer* buf, std::uint32_t videoOutHandle, std::uint32_t displayBufferIndex);
std::uint32_t* APS5_VABI sceAgcAcbSetFlip(CommandBuffer* buf, std::uint32_t videoOutHandle, std::int32_t displayBufferIndex, std::uint32_t flipMode, std::int64_t flipArg);
std::uint32_t* APS5_VABI sceAgcAcbSetWorkloadsActive(CommandBuffer* buf, std::uint32_t streamId, const std::uint32_t* workloadIds, std::uint32_t workloadCount);
std::uint32_t* APS5_VABI sceAgcAcbSetWorkloadComplete(CommandBuffer* buf, std::uint32_t streamId, std::uint32_t workloadId);
std::uint32_t* APS5_VABI sceAgcAcbSetWorkloadStreamInactive(CommandBuffer* buf, std::uint32_t streamId);
uint32_t* APS5_VABI sceAgcAcbPushMarker(CommandBuffer* buf, const char* str, uint32_t color);
uint32_t* APS5_VABI sceAgcAcbPopMarker(CommandBuffer* buf);
uint32_t* APS5_VABI sceAgcAcbSetMarker(CommandBuffer* buf, const char* str, uint32_t color);
}
#endif
@@ -1,4 +1,14 @@
#ifndef CORE_LIBS_PRX_LIBSCEAGC_ACB_INCLUDE_DISPATCH_HPP
#define CORE_LIBS_PRX_LIBSCEAGC_ACB_INCLUDE_DISPATCH_HPP
#include "SceTypes.hpp"
#include "SceShaders.hpp"
extern "C" {
uint32_t* APS5_VABI sceAgcAcbDispatchIndirect(CommandBuffer* buf, const volatile void* indirectArgs, uint32_t modifier);
std::uint32_t APS5_VABI sceAgcAcbDispatchIndirectGetSize();
}
#endif
@@ -1,4 +1,24 @@
#ifndef CORE_LIBS_PRX_LIBSCEAGC_ACB_INCLUDE_MEMORY_HPP
#define CORE_LIBS_PRX_LIBSCEAGC_ACB_INCLUDE_MEMORY_HPP
#include "SceTypes.hpp"
#include "SceShaders.hpp"
extern "C" {
std::uint32_t* APS5_VABI sceAgcAcbAcquireMem(CommandBuffer* buf, std::uint32_t gcrControl, const volatile void* base, std::uint64_t sizeBytes, std::uint32_t pollCycles);
uint32_t APS5_VABI sceAgcAcbAcquireMemGetSize(void);
uint32_t* APS5_VABI sceAgcAcbCopyData(CommandBuffer* buf, uint8_t dst, uint8_t dstCachePolicy, uint64_t dstAddress, uint8_t src, uint8_t srcCachePolicy, uint64_t srcAddressOrImmediate, uint8_t itemSize, uint8_t writeConfirm);
std::uint64_t APS5_VABI sceAgcAcbCopyDataGetSize();
std::uint32_t* APS5_VABI sceAgcAcbDmaData(CommandBuffer* buf, std::uint8_t dst, std::uint8_t dstCachePolicy, std::uint64_t dstAddress, std::uint8_t src, std::uint8_t srcCachePolicy, std::uint64_t srcAddress, std::uint32_t numBytes, std::uint8_t waitForPrevious, std::uint8_t writeConfirm);
std::uint32_t APS5_VABI sceAgcAcbDmaDataGetSize();
std::uint32_t* APS5_VABI sceAgcAcbAtomicMem(CommandBuffer* buf, std::uint8_t atomicOp, std::uint8_t command, std::uint8_t cachePolicy, const volatile void* address, std::uint64_t srcData, std::uint64_t compareData, std::uint16_t loopInterval);
std::uint32_t APS5_VABI sceAgcAcbAtomicMemGetSize();
std::uint32_t APS5_VABI sceAgcAcbAtomicGdsGetSize();
std::uint32_t* APS5_VABI sceAgcAcbPrimeUtcl2(CommandBuffer* buf, const volatile void* address, std::uint32_t sizeInBytes);
std::uint32_t APS5_VABI sceAgcAcbPrimeUtcl2GetSize();
std::uint32_t* APS5_VABI sceAgcAcbWriteData(CommandBuffer* buf, std::uint8_t dst, std::uint8_t cachePolicy, std::uint64_t address, const void* data, std::uint32_t numDwords, std::uint8_t increment, std::uint8_t writeConfirm);
}
#endif
@@ -1,4 +1,20 @@
#ifndef CORE_LIBS_PRX_LIBSCEAGC_ACB_INCLUDE_SYNC_HPP
#define CORE_LIBS_PRX_LIBSCEAGC_ACB_INCLUDE_SYNC_HPP
#include "SceTypes.hpp"
#include "SceShaders.hpp"
extern "C" {
uint32_t* APS5_VABI sceAgcAcbCondExec(CommandBuffer* buf, const volatile uint32_t* address, uint32_t numDwords);
uint32_t APS5_VABI sceAgcAcbCondExecGetSize(void);
std::uint32_t* APS5_VABI sceAgcAcbWaitRegMem(CommandBuffer* buf, std::uint8_t size, std::uint8_t compareFunction, std::uint8_t cachePolicy, const volatile void* address, std::uint64_t reference, std::uint64_t mask, std::uint32_t pollCycles);
std::uint64_t APS5_VABI sceAgcAcbWaitOnAddressGetSize(std::uint8_t size);
std::uint32_t* APS5_VABI sceAgcAcbEventWrite(CommandBuffer* buf, std::uint8_t eventType, const volatile void* address);
std::uint64_t APS5_VABI sceAgcAcbEventWriteGetSize(std::uint8_t eventType);
std::uint32_t APS5_VABI sceAgcAcbQueueEndOfShaderActionGetSize();
std::uint32_t* APS5_VABI sceAgcAcbMemSemaphore(CommandBuffer* buf, const volatile void* address, std::uint8_t waitForMailbox, std::uint8_t signalType, std::uint8_t operation);
}
#endif
@@ -102,6 +102,18 @@ uint32_t* APS5_VABI sceAgcAcbPushMarker(CommandBuffer* buf, const char* str, uin
return Agc::Marker::Push(buf, str, __func__);
}
uint32_t* APS5_VABI sceAgcAcbPushMarkerSpan(CommandBuffer* buf, const char* str, uint32_t length, uint32_t color) {
(void)color;
return Agc::Marker::PushSpan(buf, str, length, __func__);
}
uint32_t* APS5_VABI sceAgcAcbSetMarkerSpan(CommandBuffer* buf, const char* str, uint32_t length, uint32_t color) {
(void)color;
auto* packet = Agc::Marker::PushSpan(buf, str, length, __func__);
Agc::Marker::Pop(buf, __func__);
return packet;
}
uint32_t* APS5_VABI sceAgcAcbPopMarker(CommandBuffer* buf) {
return Agc::Marker::Pop(buf, __func__);
}
+1 -4
View File
@@ -44,10 +44,7 @@ std::uint32_t APS5_VABI sceAgcAcbQueueEndOfShaderActionGetSize() {
return 32;
}
std::uint32_t* APS5_VABI sceAgcAcbMemSemaphore(CommandBuffer* buf, std::uint8_t action, std::uint8_t clientCode, const volatile void* address) {
const std::uint8_t operation = action != 0 ? 7u : 6u;
const std::uint8_t signalType = clientCode & 1u;
const std::uint8_t waitForMailbox = (clientCode >> 1u) & 1u;
std::uint32_t* APS5_VABI sceAgcAcbMemSemaphore(CommandBuffer* buf, const volatile void* address, std::uint8_t waitForMailbox, std::uint8_t signalType, std::uint8_t operation) {
return Agc::Command::WriteMemSemaphore(buf, address, waitForMailbox, signalType, operation, __func__);
}
+8 -8
View File
@@ -157,7 +157,7 @@ add_test_executable(agc_register_command_tests EXCLUDE_FROM_ALL
Patch/src/IndirectRegisters.cpp
)
target_include_directories(agc_register_command_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(agc_register_command_tests PRIVATE libc)
target_link_libraries(agc_register_command_tests PRIVATE libSceAgcDriver libc)
configure_windows_unwind(agc_register_command_tests)
if(BUILD_TESTING)
set_target_properties(agc_register_command_tests PROPERTIES EXCLUDE_FROM_ALL FALSE)
@@ -176,7 +176,7 @@ add_test_executable(agc_cb_cond_write_tests EXCLUDE_FROM_ALL
Cb/src/Branch.cpp
)
target_include_directories(agc_cb_cond_write_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(agc_cb_cond_write_tests PRIVATE libc)
target_link_libraries(agc_cb_cond_write_tests PRIVATE libSceAgcDriver libc)
configure_windows_unwind(agc_cb_cond_write_tests)
if(BUILD_TESTING)
set_target_properties(agc_cb_cond_write_tests PROPERTIES EXCLUDE_FROM_ALL FALSE)
@@ -220,7 +220,7 @@ add_test_executable(agc_branch_patch_tests EXCLUDE_FROM_ALL
Patch/src/Branch.cpp
)
target_include_directories(agc_branch_patch_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(agc_branch_patch_tests PRIVATE libc)
target_link_libraries(agc_branch_patch_tests PRIVATE libSceAgcDriver libc)
configure_windows_unwind(agc_branch_patch_tests)
if(BUILD_TESTING)
set_target_properties(agc_branch_patch_tests PROPERTIES EXCLUDE_FROM_ALL FALSE)
@@ -240,7 +240,7 @@ add_test_executable(agc_predication_tests EXCLUDE_FROM_ALL
Predication/src/Predication.cpp
)
target_include_directories(agc_predication_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(agc_predication_tests PRIVATE libc)
target_link_libraries(agc_predication_tests PRIVATE libSceAgcDriver libc)
configure_windows_unwind(agc_predication_tests)
if(BUILD_TESTING)
set_target_properties(agc_predication_tests PROPERTIES EXCLUDE_FROM_ALL FALSE)
@@ -370,7 +370,7 @@ add_test_executable(agc_dma_data_tests EXCLUDE_FROM_ALL
DcbFlow/src/Memory.cpp
)
target_include_directories(agc_dma_data_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(agc_dma_data_tests PRIVATE libc)
target_link_libraries(agc_dma_data_tests PRIVATE libSceAgcDriver libc)
configure_windows_unwind(agc_dma_data_tests)
if(BUILD_TESTING)
set_target_properties(agc_dma_data_tests PROPERTIES EXCLUDE_FROM_ALL FALSE)
@@ -390,7 +390,7 @@ add_test_executable(agc_write_data_patch_tests EXCLUDE_FROM_ALL
Patch/src/WaitRegMem.cpp
)
target_include_directories(agc_write_data_patch_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(agc_write_data_patch_tests PRIVATE libc)
target_link_libraries(agc_write_data_patch_tests PRIVATE libSceAgcDriver libc)
configure_windows_unwind(agc_write_data_patch_tests)
if(BUILD_TESTING)
set_target_properties(agc_write_data_patch_tests PROPERTIES EXCLUDE_FROM_ALL FALSE)
@@ -410,7 +410,7 @@ add_test_executable(agc_prime_utcl2_tests EXCLUDE_FROM_ALL
DcbState/src/Display.cpp
)
target_include_directories(agc_prime_utcl2_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(agc_prime_utcl2_tests PRIVATE libc)
target_link_libraries(agc_prime_utcl2_tests PRIVATE libSceAgcDriver libc)
configure_windows_unwind(agc_prime_utcl2_tests)
if(BUILD_TESTING)
set_target_properties(agc_prime_utcl2_tests PROPERTIES EXCLUDE_FROM_ALL FALSE)
@@ -431,7 +431,7 @@ add_test_executable(agc_cond_exec_patch_tests EXCLUDE_FROM_ALL
Patch/src/CondExec.cpp
)
target_include_directories(agc_cond_exec_patch_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(agc_cond_exec_patch_tests PRIVATE libc)
target_link_libraries(agc_cond_exec_patch_tests PRIVATE libSceAgcDriver libc)
configure_windows_unwind(agc_cond_exec_patch_tests)
if(BUILD_TESTING)
set_target_properties(agc_cond_exec_patch_tests PROPERTIES EXCLUDE_FROM_ALL FALSE)
@@ -1,4 +1,16 @@
#ifndef CORE_LIBS_PRX_LIBSCEAGC_CB_INCLUDE_BRANCH_HPP
#define CORE_LIBS_PRX_LIBSCEAGC_CB_INCLUDE_BRANCH_HPP
#include "SceTypes.hpp"
#include "SceShaders.hpp"
extern "C" {
std::uint32_t* APS5_VABI sceAgcCbBranch(CommandBuffer* buf, std::uint8_t mode, std::uint8_t compareFunction, const volatile std::uint64_t* compareAddress, std::uint64_t mask, std::uint64_t reference, std::uint8_t cachePolicy1, const volatile std::uint32_t* buffer1, std::uint32_t sizeInDwords1, std::uint8_t cachePolicy2, const volatile std::uint32_t* buffer2, std::uint32_t sizeInDwords2);
std::uint32_t APS5_VABI sceAgcCbBranchGetSize();
std::uint32_t* APS5_VABI sceAgcCbCondWrite(CommandBuffer* buf, std::uint32_t compareFunction, std::uint32_t writeSpace, const volatile void* writeAddress, std::uint32_t writeData, const volatile void* pollAddress, std::uint32_t reference, std::uint32_t mask);
std::uint32_t APS5_VABI sceAgcCbCondWriteGetSize();
}
#endif
@@ -1,4 +1,14 @@
#ifndef CORE_LIBS_PRX_LIBSCEAGC_CB_INCLUDE_DISPATCH_HPP
#define CORE_LIBS_PRX_LIBSCEAGC_CB_INCLUDE_DISPATCH_HPP
#include "SceTypes.hpp"
#include "SceShaders.hpp"
extern "C" {
std::uint32_t* APS5_VABI sceAgcCbDispatch(CommandBuffer* buf, std::uint32_t threadGroupX, std::uint32_t threadGroupY, std::uint32_t threadGroupZ, std::uint32_t modifier);
uint32_t APS5_VABI sceAgcCbDispatchGetSize(void);
}
#endif
@@ -1,4 +1,20 @@
#ifndef CORE_LIBS_PRX_LIBSCEAGC_CB_INCLUDE_REGISTERS_HPP
#define CORE_LIBS_PRX_LIBSCEAGC_CB_INCLUDE_REGISTERS_HPP
#include "SceTypes.hpp"
#include "SceShaders.hpp"
extern "C" {
std::uint32_t* APS5_VABI sceAgcCbSetUcRegisterRangeDirect(CommandBuffer* buf, std::uint32_t offset, const std::uint32_t* values, std::uint32_t numValues);
std::uint32_t* APS5_VABI sceAgcCbSetShRegisterRangeDirect(CommandBuffer* buf, std::uint32_t offset, const std::uint32_t* values, std::uint32_t numValues);
uint32_t APS5_VABI sceAgcCbSetShRegisterRangeDirectGetSize(uint32_t numValues);
std::uint32_t APS5_VABI sceAgcCbSetUcRegisterRangeDirectGetSize(std::uint32_t numValues);
std::uint32_t* APS5_VABI sceAgcCbSetShRegistersDirect(CommandBuffer* buf, const volatile ShaderRegister* regs, std::uint32_t numRegs);
std::uint32_t APS5_VABI sceAgcCbSetShRegistersDirectGetSize(std::uint32_t numRegs);
std::uint32_t* APS5_VABI sceAgcCbSetUcRegistersDirect(CommandBuffer* buf, const volatile ShaderRegister* regs, std::uint32_t numRegs);
std::uint32_t APS5_VABI sceAgcCbSetUcRegistersDirectGetSize(std::uint32_t numRegs);
}
#endif
+11 -2
View File
@@ -4,10 +4,19 @@
#include "SceTypes.hpp"
#include <cstdint>
struct CommandBuffer;
extern "C" {
std::uint32_t* APS5_VABI sceAgcCbMemSemaphore(CommandBuffer* buf, const volatile void* address, std::uint32_t operation, std::uint32_t modifier, std::uint32_t value);
}
#include "SceShaders.hpp"
extern "C" {
std::uint32_t* APS5_VABI sceAgcCbNop_nid_postfix(CommandBuffer* buf, std::uint32_t sizeInDwords);
uint32_t APS5_VABI sceAgcCbNopGetSize(uint32_t sizeInDwords);
std::uint32_t APS5_VABI sceAgcCbQueueEndOfPipeActionGetSize();
std::uint32_t* APS5_VABI sceAgcCbReleaseMem(CommandBuffer* buf, std::uint8_t action, std::uint16_t gcrControl, std::uint8_t dst, std::uint8_t cachePolicy, const volatile Label* address, std::uint8_t dataSelect, std::uint64_t data, std::uint16_t gdsOffset, std::uint16_t gdsSize, std::uint8_t interrupt, std::uint32_t interruptContextId);
}
#endif
+12 -4
View File
@@ -1,3 +1,4 @@
#include "prx/libSceAgcDriver/Execution/include/Driver.hpp"
#include "prx/libSceAgc/Command/include/Packet.hpp"
#include <algorithm>
@@ -67,6 +68,7 @@ void Reserve(CommandBuffer* buffer, std::uint32_t count, const char* function) {
std::uint32_t* Allocate(CommandBuffer* buffer, std::uint32_t count, const char* function) {
Reserve(buffer, count, function);
auto* result = buffer->cursor_up;
AgcDriverPrepareCommandWrite_nid_no_patch(reinterpret_cast<std::uint64_t>(result), static_cast<std::size_t>(count) * sizeof(std::uint32_t));
buffer->cursor_up += count;
return result;
}
@@ -113,17 +115,23 @@ std::uint32_t* WriteRegisters(CommandBuffer* buffer, std::uint32_t opcode, const
Require(buffer != nullptr, function, "null command buffer");
return buffer->cursor_up;
}
CheckAddress(reinterpret_cast<std::uintptr_t>(registers), 4, function);
CheckAddress(reinterpret_cast<std::uintptr_t>(registers), 1, function);
const auto* bytes = reinterpret_cast<const std::byte*>(const_cast<const ShaderRegister*>(registers));
const auto entryAt = [&](std::uint32_t index) {
ShaderRegister entry;
std::memcpy(&entry, bytes + static_cast<std::size_t>(index) * sizeof(ShaderRegister), sizeof(entry));
return entry;
};
std::vector<ShaderRegister> snapshot;
if (snapshotAll) {
snapshot.reserve(count);
for (std::uint32_t i = 0; i < count; ++i) {
snapshot.push_back({registers[i].offset, registers[i].value});
snapshot.push_back(entryAt(i));
CheckBits(snapshot.back().offset, 0xffffu, function);
}
}
const auto offsetAt = [&](std::uint32_t index) { return snapshotAll ? snapshot[index].offset : registers[index].offset; };
const auto valueAt = [&](std::uint32_t index) { return snapshotAll ? snapshot[index].value : registers[index].value; };
const auto offsetAt = [&](std::uint32_t index) { return snapshotAll ? snapshot[index].offset : entryAt(index).offset; };
const auto valueAt = [&](std::uint32_t index) { return snapshotAll ? snapshot[index].value : entryAt(index).value; };
std::uint32_t* first = nullptr;
std::uint32_t index = 0;
while (index < count) {
@@ -1,4 +1,24 @@
#ifndef CORE_LIBS_PRX_LIBSCEAGC_DCBDRAW_INCLUDE_DRAWINDEXED_HPP
#define CORE_LIBS_PRX_LIBSCEAGC_DCBDRAW_INCLUDE_DRAWINDEXED_HPP
#include "SceTypes.hpp"
#include "SceShaders.hpp"
extern "C" {
std::uint32_t* APS5_VABI sceAgcDcbDrawIndex(CommandBuffer* buf, std::uint32_t indexCount, const volatile void* indexAddress, std::uint64_t modifier);
std::uint32_t APS5_VABI sceAgcDcbDrawIndexGetSize();
std::uint32_t* APS5_VABI sceAgcDcbDrawIndexAuto(CommandBuffer* buf, std::uint32_t indexCount, std::uint64_t modifier);
std::uint32_t APS5_VABI sceAgcDcbDrawIndexAutoGetSize();
std::uint32_t* APS5_VABI sceAgcDcbDrawIndexOffset(CommandBuffer* buf, std::uint32_t indexOffset, std::uint32_t indexCount, std::uint64_t modifier);
uint32_t APS5_VABI sceAgcDcbDrawIndexOffsetGetSize(void);
uint32_t* APS5_VABI sceAgcDcbDrawIndexIndirect(CommandBuffer* buf, uint32_t dataOffsetInBytes, uint64_t modifier);
std::uint32_t APS5_VABI sceAgcDcbDrawIndexIndirectGetSize();
uint32_t* APS5_VABI sceAgcDcbDrawIndexIndirectMulti(CommandBuffer* buf, uint32_t dataOffsetInBytes, uint32_t countIndirect, uint32_t maxCountOrCount, const volatile void* countAddr, uint32_t strideInBytes, uint64_t modifier);
std::uint32_t APS5_VABI sceAgcDcbDrawIndexIndirectMultiGetSize();
uint32_t* APS5_VABI sceAgcDcbDrawIndexMultiInstanced(CommandBuffer* buf, uint32_t indexCount, const volatile void* indexAddr, const volatile void* objectIds, uint32_t instanceCount, uint64_t modifier);
uint32_t APS5_VABI sceAgcDcbDrawIndexMultiInstancedGetSize(void);
}
#endif
@@ -1,4 +1,16 @@
#ifndef CORE_LIBS_PRX_LIBSCEAGC_DCBDRAW_INCLUDE_DRAWNONINDEXED_HPP
#define CORE_LIBS_PRX_LIBSCEAGC_DCBDRAW_INCLUDE_DRAWNONINDEXED_HPP
#include "SceTypes.hpp"
#include "SceShaders.hpp"
extern "C" {
std::uint32_t* APS5_VABI sceAgcDcbDrawIndirect(CommandBuffer* buf, std::uint32_t dataOffsetInBytes, std::uint64_t modifier);
std::uint32_t APS5_VABI sceAgcDcbDrawIndirectGetSize();
std::uint32_t* APS5_VABI sceAgcDcbDrawIndirectMulti(CommandBuffer* buf, std::uint32_t dataOffsetInBytes, std::uint32_t countIndirect, std::uint32_t maxCountOrCount, const volatile void* countAddress, std::uint32_t strideInBytes, std::uint64_t modifier);
std::uint32_t APS5_VABI sceAgcDcbDrawIndirectMultiGetSize();
}
#endif
@@ -1,4 +1,21 @@
#ifndef CORE_LIBS_PRX_LIBSCEAGC_DCBDRAW_INCLUDE_INDEXBUFFER_HPP
#define CORE_LIBS_PRX_LIBSCEAGC_DCBDRAW_INCLUDE_INDEXBUFFER_HPP
#include "SceTypes.hpp"
#include "SceShaders.hpp"
extern "C" {
std::uint32_t* APS5_VABI sceAgcDcbSetIndexBuffer(CommandBuffer* buf, std::uint64_t indexAddress);
std::uint32_t APS5_VABI sceAgcDcbSetIndexBufferGetSize();
std::uint32_t* APS5_VABI sceAgcDcbSetIndexCount(CommandBuffer* buf, std::uint32_t indexCount);
std::uint32_t APS5_VABI sceAgcDcbSetIndexCountGetSize();
std::uint32_t* APS5_VABI sceAgcDcbSetIndexSize(CommandBuffer* buf, std::uint8_t indexSize, std::uint8_t cachePolicy);
std::uint32_t APS5_VABI sceAgcDcbSetIndexSizeGetSize();
std::uint32_t* APS5_VABI sceAgcDcbSetIndexSize_0600(CommandBuffer* buf, std::uint8_t indexSize, std::uint8_t cachePolicy, std::uint8_t perInstanceObjectId);
std::uint32_t* APS5_VABI sceAgcDcbSetIndexIndirectArgs(CommandBuffer* buf, std::uint64_t attributeAddress, std::uint32_t attributeIndex);
std::uint32_t APS5_VABI sceAgcDcbSetIndexIndirectArgsGetSize();
}
#endif
@@ -1,4 +1,15 @@
#ifndef CORE_LIBS_PRX_LIBSCEAGC_DCBDRAW_INCLUDE_INSTANCING_HPP
#define CORE_LIBS_PRX_LIBSCEAGC_DCBDRAW_INCLUDE_INSTANCING_HPP
#include "SceTypes.hpp"
#include "SceShaders.hpp"
extern "C" {
std::uint32_t* APS5_VABI sceAgcDcbSetNumInstances(CommandBuffer* buf, std::uint32_t numInstances);
std::uint32_t APS5_VABI sceAgcDcbSetNumInstancesGetSize();
uint32_t* APS5_VABI sceAgcDcbSetBaseIndirectArgs(CommandBuffer* buf, uint32_t shaderType, const volatile void* indirectBaseAddr);
}
#endif
@@ -1,4 +1,15 @@
#ifndef CORE_LIBS_PRX_LIBSCEAGC_DCBDRAW_INCLUDE_STATS_HPP
#define CORE_LIBS_PRX_LIBSCEAGC_DCBDRAW_INCLUDE_STATS_HPP
#include "SceTypes.hpp"
#include "SceShaders.hpp"
extern "C" {
std::uint32_t* APS5_VABI sceAgcDcbGetLodStats(CommandBuffer* buf, std::uint8_t cachePolicy, const volatile void* buffer, std::uint32_t bufferSizeInBytes, std::uint32_t resetCount, std::uint8_t forceReset, std::uint8_t reportAndReset, std::uint32_t reportingIntervalIn100kClocks);
std::uint32_t APS5_VABI sceAgcDcbBeginOcclusionQueryGetSize(std::int32_t queryType);
std::uint32_t APS5_VABI sceAgcDcbEndOcclusionQueryGetSize();
}
#endif
@@ -9,4 +9,18 @@ std::uint32_t* APS5_VABI sceAgcDcbAtomicGds(CommandBuffer* buf, std::uint32_t co
std::uint32_t* APS5_VABI sceAgcDcbAtomicGds_0900(CommandBuffer* buf, std::uint32_t control, std::uint32_t mode, std::uint32_t gdsOp, std::uint32_t src, std::uint16_t offset, std::uint16_t index, std::uint32_t mask, std::uint32_t data, std::uint64_t compareData, std::uint64_t extraData);
}
#include "SceShaders.hpp"
extern "C" {
uint32_t* APS5_VABI sceAgcDcbJump(CommandBuffer* buf, uint8_t mode, uint8_t cachePolicy, const uint32_t* target, uint32_t sizeInDwords);
std::uint32_t APS5_VABI sceAgcDcbJumpGetSize();
std::uint32_t* APS5_VABI sceAgcDcbResetQueue(CommandBuffer* buf, std::uint32_t op, std::uint32_t state);
std::uint32_t* APS5_VABI sceAgcDcbClearState(CommandBuffer* buf, std::uint32_t command);
uint32_t* APS5_VABI sceAgcDcbRewind(CommandBuffer* buf, uint32_t initialState);
uint32_t APS5_VABI sceAgcDcbRewindGetSize(void);
uint32_t* APS5_VABI sceAgcDcbWaitUntilSafeForRendering(CommandBuffer* buf, uint32_t videoOutHandle, uint32_t displayBufferIndex);
}
#endif
@@ -1,4 +1,14 @@
#ifndef CORE_LIBS_PRX_LIBSCEAGC_DCBFLOW_INCLUDE_DISPATCH_HPP
#define CORE_LIBS_PRX_LIBSCEAGC_DCBFLOW_INCLUDE_DISPATCH_HPP
#include "SceTypes.hpp"
#include "SceShaders.hpp"
extern "C" {
uint32_t* APS5_VABI sceAgcDcbDispatchIndirect(CommandBuffer* buf, uint32_t dataOffsetInBytes, uint32_t flags);
std::uint32_t APS5_VABI sceAgcDcbDispatchIndirectGetSize();
}
#endif
@@ -9,4 +9,20 @@ std::uint32_t* APS5_VABI sceAgcDcbAcquireMem(CommandBuffer* buf, std::uint8_t en
std::uint32_t APS5_VABI sceAgcDcbAtomicGdsGetSize();
}
#include "SceShaders.hpp"
extern "C" {
uint32_t APS5_VABI sceAgcDcbAcquireMemGetSize(void);
uint32_t* APS5_VABI sceAgcDcbCopyData(CommandBuffer* buf, uint8_t dst, uint8_t dstCachePolicy, uint64_t dstAddress, uint8_t src, uint8_t srcCachePolicy, uint64_t srcAddressOrImmediate, uint8_t itemSize, uint8_t writeConfirm);
std::uint64_t APS5_VABI sceAgcDcbCopyDataGetSize();
std::uint32_t* APS5_VABI sceAgcDcbDmaData(CommandBuffer* buf, std::uint8_t engine, std::uint8_t dst, std::uint8_t dstCachePolicy, std::uint64_t dstAddress, std::uint8_t src, std::uint8_t srcCachePolicy, std::uint64_t srcAddress, std::uint32_t numBytes, std::uint8_t waitForPrevious, std::uint8_t writeConfirm, std::uint8_t blockEngine);
std::uint32_t APS5_VABI sceAgcDcbDmaDataGetSize();
std::uint32_t* APS5_VABI sceAgcDcbAtomicMem(CommandBuffer* buf, std::uint8_t atomicOp, std::uint8_t command, std::uint8_t cachePolicy, const volatile void* address, std::uint64_t srcData, std::uint64_t compareData, std::uint16_t loopInterval);
std::uint32_t APS5_VABI sceAgcDcbAtomicMemGetSize();
std::uint32_t* APS5_VABI sceAgcDcbWriteData(CommandBuffer* buf, std::uint8_t dst, std::uint8_t cachePolicy, std::uint64_t address, const void* data, std::uint32_t numDwords, std::uint8_t increment, std::uint8_t writeConfirm);
uint32_t APS5_VABI sceAgcDcbWriteDataGetSize(uint32_t numDwords);
}
#endif
@@ -1,5 +1,21 @@
#ifndef CORE_LIBS_PRX_LIBSCEAGC_DCBFLOW_INCLUDE_SYNC_HPP
#define CORE_LIBS_PRX_LIBSCEAGC_DCBFLOW_INCLUDE_SYNC_HPP
#include "SceTypes.hpp"
#include "SceShaders.hpp"
extern "C" {
uint32_t* APS5_VABI sceAgcDcbCondExec(CommandBuffer* buf, const volatile uint32_t* address, uint32_t numDwords);
uint32_t APS5_VABI sceAgcDcbCondExecGetSize(void);
std::uint32_t* APS5_VABI sceAgcDcbWaitRegMem(CommandBuffer* buf, std::uint8_t size, std::uint8_t compareFunction, std::uint8_t operation, std::uint8_t cachePolicy, const volatile void* address, std::uint64_t reference, std::uint64_t mask, std::uint32_t pollCycles);
uint32_t APS5_VABI sceAgcDcbWaitOnAddressGetSize(uint32_t size);
std::uint32_t* APS5_VABI sceAgcDcbEventWrite(CommandBuffer* buf, std::uint8_t eventType, const volatile void* address);
std::uint64_t APS5_VABI sceAgcDcbEventWriteGetSize(std::uint8_t eventType);
std::uint32_t* APS5_VABI sceAgcDcbStallCommandBufferParser(CommandBuffer* buf);
std::uint32_t APS5_VABI sceAgcDcbStallCommandBufferParserGetSize();
std::uint32_t* APS5_VABI sceAgcDcbMemSemaphore(CommandBuffer* buf, const volatile void* address, std::uint8_t waitForMailbox, std::uint8_t signalType, std::uint8_t operation);
}
#endif
+1 -4
View File
@@ -52,10 +52,7 @@ std::uint32_t APS5_VABI sceAgcDcbStallCommandBufferParserGetSize() {
return 8;
}
std::uint32_t* APS5_VABI sceAgcDcbMemSemaphore(CommandBuffer* buf, std::uint8_t action, std::uint8_t clientCode, const volatile void* address) {
const std::uint8_t operation = action != 0 ? 7u : 6u;
const std::uint8_t signalType = clientCode & 1u;
const std::uint8_t waitForMailbox = (clientCode >> 1u) & 1u;
std::uint32_t* APS5_VABI sceAgcDcbMemSemaphore(CommandBuffer* buf, const volatile void* address, std::uint8_t waitForMailbox, std::uint8_t signalType, std::uint8_t operation) {
return Agc::Command::WriteMemSemaphore(buf, address, waitForMailbox, signalType, operation, __func__);
}
@@ -4,8 +4,6 @@
#include "SceTypes.hpp"
#include <cstdint>
struct CommandBuffer;
extern "C" {
uint32_t* APS5_VABI sceAgcDcbContextStateOp(CommandBuffer* buf, uint32_t operation);
@@ -14,4 +12,15 @@ uint64_t APS5_VABI sceAgcDcbContextStateOpGetSize(uint32_t operation);
}
#include "SceShaders.hpp"
extern "C" {
std::uint32_t APS5_VABI sceAgcDcbSetBaseDispatchIndirectArgsGetSize();
std::uint32_t APS5_VABI sceAgcDcbSetBaseDrawIndirectArgsGetSize();
std::uint32_t APS5_VABI sceAgcDcbQueueEndOfShaderActionGetSize();
std::uint32_t APS5_VABI sceAgcDcbGetLodStatsGetSize();
}
#endif
@@ -1,4 +1,15 @@
#ifndef CORE_LIBS_PRX_LIBSCEAGC_DCBSTATE_INCLUDE_DISPLAY_HPP
#define CORE_LIBS_PRX_LIBSCEAGC_DCBSTATE_INCLUDE_DISPLAY_HPP
#include "SceTypes.hpp"
#include "SceShaders.hpp"
extern "C" {
uint32_t* APS5_VABI sceAgcDcbSetFlip(CommandBuffer* buf, uint32_t videoOutHandle, int32_t displayBufferIndex, uint32_t flipMode, int64_t flipArg);
uint32_t* APS5_VABI sceAgcDcbPrimeUtcl2(CommandBuffer* buf, const volatile void* address, uint32_t sizeInBytes);
std::uint32_t APS5_VABI sceAgcDcbPrimeUtcl2GetSize();
}
#endif
@@ -1,4 +1,14 @@
#ifndef CORE_LIBS_PRX_LIBSCEAGC_DCBSTATE_INCLUDE_INIT_HPP
#define CORE_LIBS_PRX_LIBSCEAGC_DCBSTATE_INCLUDE_INIT_HPP
#include "SceTypes.hpp"
#include "SceShaders.hpp"
extern "C" {
int APS5_VABI sceAgcInit(std::uint32_t version);
int APS5_VABI sceAgcInit_0090(std::uint32_t* state, std::uint32_t version);
}
#endif
@@ -7,8 +7,19 @@
namespace Agc::Marker {
std::uint32_t* Push(CommandBuffer* buf, const char* str, const char* function);
std::uint32_t* PushSpan(CommandBuffer* buf, const char* str, std::uint32_t length, const char* function);
std::uint32_t* Pop(CommandBuffer* buf, const char* function);
}
#include "SceShaders.hpp"
extern "C" {
uint32_t* APS5_VABI sceAgcDcbSetMarker(CommandBuffer* buf, const char* str, uint32_t color);
uint32_t* APS5_VABI sceAgcDcbPopMarker(CommandBuffer* buf);
uint32_t* APS5_VABI sceAgcDcbPushMarker(CommandBuffer* buf, const char* str, uint32_t color);
}
#endif
@@ -1,4 +1,16 @@
#ifndef CORE_LIBS_PRX_LIBSCEAGC_DCBSTATE_INCLUDE_PREDICATION_HPP
#define CORE_LIBS_PRX_LIBSCEAGC_DCBSTATE_INCLUDE_PREDICATION_HPP
#include "SceTypes.hpp"
#include "SceShaders.hpp"
extern "C" {
uint32_t* APS5_VABI sceAgcDcbSetPredication(CommandBuffer* buf, uint8_t condition, uint8_t op, uint8_t waitOp, const volatile void* address, uint32_t countInDwords);
std::uint32_t APS5_VABI sceAgcDcbSetZPassPredicationEnableGetSize();
std::uint32_t APS5_VABI sceAgcDcbSetPredicationDisableGetSize();
std::uint32_t APS5_VABI sceAgcDcbSetBoolPredicationEnableGetSize();
}
#endif
@@ -1,4 +1,26 @@
#ifndef CORE_LIBS_PRX_LIBSCEAGC_DCBSTATE_INCLUDE_REGISTERS_HPP
#define CORE_LIBS_PRX_LIBSCEAGC_DCBSTATE_INCLUDE_REGISTERS_HPP
#include "SceTypes.hpp"
#include "SceShaders.hpp"
extern "C" {
std::uint32_t* APS5_VABI sceAgcDcbSetCfRegisterDirect(CommandBuffer* buf, ShaderRegister reg);
std::uint32_t* APS5_VABI sceAgcDcbSetCfRegisterRangeDirect(CommandBuffer* buf, std::uint32_t offset, const std::uint32_t* values, std::uint32_t numValues);
uint32_t* APS5_VABI sceAgcDcbSetCxRegisterDirect(CommandBuffer* buf, ShaderRegister reg);
uint32_t APS5_VABI sceAgcDcbSetCxRegisterDirectGetSize(void);
std::uint32_t* APS5_VABI sceAgcDcbSetCxRegistersIndirect(CommandBuffer* buf, const volatile ShaderRegister* regs, std::uint32_t numRegs);
std::uint32_t APS5_VABI sceAgcDcbSetCxRegistersIndirectGetSize(std::uint32_t numRegs);
uint32_t* APS5_VABI sceAgcDcbSetShRegisterDirect(CommandBuffer* buf, ShaderRegister reg);
std::uint32_t APS5_VABI sceAgcDcbSetShRegisterDirectGetSize();
std::uint32_t* APS5_VABI sceAgcDcbSetShRegistersIndirect(CommandBuffer* buf, const volatile ShaderRegister* regs, std::uint32_t numRegs);
std::uint32_t APS5_VABI sceAgcDcbSetShRegistersIndirectGetSize(std::uint32_t numRegs);
uint32_t* APS5_VABI sceAgcDcbSetUcRegisterDirect(CommandBuffer* buf, ShaderRegister reg);
std::uint32_t APS5_VABI sceAgcDcbSetUcRegisterDirectGetSize();
std::uint32_t* APS5_VABI sceAgcDcbSetUcRegistersIndirect(CommandBuffer* buf, const volatile ShaderRegister* regs, std::uint32_t numRegs);
std::uint32_t APS5_VABI sceAgcDcbSetUcRegistersIndirectGetSize(std::uint32_t numRegs);
}
#endif
@@ -1,4 +1,15 @@
#ifndef CORE_LIBS_PRX_LIBSCEAGC_DCBSTATE_INCLUDE_WORKLOAD_HPP
#define CORE_LIBS_PRX_LIBSCEAGC_DCBSTATE_INCLUDE_WORKLOAD_HPP
#include "SceTypes.hpp"
#include "SceShaders.hpp"
extern "C" {
uint32_t* APS5_VABI sceAgcDcbSetWorkloadComplete(CommandBuffer* buf, uint32_t streamId, uint32_t workloadId);
uint32_t* APS5_VABI sceAgcDcbSetWorkloadsActive(CommandBuffer* buf, uint32_t streamId, const uint32_t* workloadIds, uint32_t workloadCount);
uint32_t* APS5_VABI sceAgcDcbSetWorkloadStreamInactive(CommandBuffer* buf, uint32_t streamId);
}
#endif
@@ -22,13 +22,21 @@ namespace Agc::Marker {
std::uint32_t* Push(CommandBuffer* buf, const char* str, const char* function) {
Agc::Command::Require(buf != nullptr, function, "null command buffer");
const char* text = str ? str : "";
const std::size_t bytes = std::strlen(text) + 1;
const std::size_t length = std::strlen(text);
Agc::Command::Require(length < 0x10000u, function, "marker text too long");
return PushSpan(buf, text, static_cast<std::uint32_t>(length), function);
}
std::uint32_t* PushSpan(CommandBuffer* buf, const char* str, std::uint32_t length, const char* function) {
Agc::Command::Require(buf != nullptr, function, "null command buffer");
Agc::Command::Require(str != nullptr || length == 0, function, "null marker text");
const std::size_t bytes = std::size_t{length} + 1;
const auto payload = static_cast<std::uint32_t>((bytes + 3) / 4);
Agc::Command::Require(payload <= 0x4000u, function, "marker text too long");
auto* packet = Agc::Command::Allocate(buf, payload + 1, function);
packet[0] = Agc::Command::Header(OpcodeNop, payload + 1, CustomPushMarker << 2);
std::memset(packet + 1, 0, payload * sizeof(std::uint32_t));
std::memcpy(packet + 1, text, bytes);
if (length != 0) std::memcpy(packet + 1, str, length);
return packet;
}
@@ -60,4 +68,16 @@ uint32_t* APS5_VABI sceAgcDcbPushMarker(CommandBuffer* buf, const char* str, uin
return Agc::Marker::Push(buf, str, __func__);
}
uint32_t* APS5_VABI sceAgcDcbPushMarkerSpan(CommandBuffer* buf, const char* str, uint32_t length, uint32_t color) {
(void)color;
return Agc::Marker::PushSpan(buf, str, length, __func__);
}
uint32_t* APS5_VABI sceAgcDcbSetMarkerSpan(CommandBuffer* buf, const char* str, uint32_t length, uint32_t color) {
(void)color;
auto* packet = Agc::Marker::PushSpan(buf, str, length, __func__);
Agc::Marker::Pop(buf, __func__);
return packet;
}
}
+1
View File
@@ -1,3 +1,4 @@
#include "prx/libSceAgc/include/Agc.hpp"
#include <cstdint>
#include <cstddef>
#include "SceTypes.hpp"
@@ -1,4 +1,15 @@
#ifndef CORE_LIBS_PRX_LIBSCEAGC_MISC_INCLUDE_DEBUG_HPP
#define CORE_LIBS_PRX_LIBSCEAGC_MISC_INCLUDE_DEBUG_HPP
#include "SceTypes.hpp"
#include "SceShaders.hpp"
extern "C" {
int APS5_VABI sceAgcDebugRaiseException(std::uint32_t exceptionId);
int APS5_VABI sceAgcSetShaderInstrumentation(std::uint32_t flags);
std::uint32_t APS5_VABI sceAgcGetShaderInstrumentation();
}
#endif
@@ -14,4 +14,14 @@ int APS5_VABI sceAgcGetDataPacketPayloadAddress(uint32_t** addr, uint32_t* cmd,
int APS5_VABI sceAgcGetDataPacketPayloadAddress_0090(uint32_t** addr, uint32_t* cmd, int type);
}
#include "SceShaders.hpp"
extern "C" {
uint32_t APS5_VABI sceAgcGetPacketSize(uint32_t* packet);
int APS5_VABI sceAgcGetDataPacketPayloadRange(SceAgcMemoryRange* range, uint32_t* cmd, int type);
int APS5_VABI sceAgcDcbGetSystemSoftwareVersion(uint32_t* destination, const uint32_t* packet);
}
#endif
@@ -1,4 +1,13 @@
#ifndef CORE_LIBS_PRX_LIBSCEAGC_MISC_INCLUDE_PLATFORM_HPP
#define CORE_LIBS_PRX_LIBSCEAGC_MISC_INCLUDE_PLATFORM_HPP
#include "SceTypes.hpp"
#include "SceShaders.hpp"
extern "C" {
int APS5_VABI sceAgcGetIsTrinityMode(bool* isTrinityMode);
}
#endif
@@ -4,6 +4,18 @@
#include <cstdint>
extern "C" {
int APS5_VABI sceAgcGetDefaultCxStateFlat(void* out_state, std::uint32_t size);
int APS5_VABI sceAgcGetDefaultCxStateFlat(void* outState, std::uint32_t size);
}
#include "SceShaders.hpp"
extern "C" {
void* APS5_VABI sceAgcGetRegisterDefaults();
void* APS5_VABI sceAgcGetRegisterDefaultsInternal();
void* APS5_VABI sceAgcGetRegisterDefaults2(std::uint32_t version);
void* APS5_VABI sceAgcGetRegisterDefaults2Internal(std::uint32_t version);
}
#endif
@@ -1,4 +1,14 @@
#ifndef CORE_LIBS_PRX_LIBSCEAGC_MISC_INCLUDE_SEMAPHORE_HPP
#define CORE_LIBS_PRX_LIBSCEAGC_MISC_INCLUDE_SEMAPHORE_HPP
#include "SceTypes.hpp"
#include "SceShaders.hpp"
extern "C" {
int APS5_VABI sceAgcSetAmmSemaphoreMemory(void* memory, std::uint64_t sizeInBytes);
int APS5_VABI sceAgcGetSemaphoreLabel(std::uint32_t index, void** label);
}
#endif
@@ -10,7 +10,16 @@ struct SizeAlign {
std::size_t m_align;
};
extern "C" int APS5_VABI sceAgcFuseShaderHalves_0200(Shader* fused_result, const Shader* front, const Shader* back, void* scratch_mem);
extern "C" int APS5_VABI sceAgcFuseShaderHalves_0200(Shader* fusedResult, const Shader* front, const Shader* back, void* scratchMem);
extern "C" int APS5_VABI sceAgcGetFusedShaderSize_0080(SizeAlign* dst, const Shader* front, const Shader* back);
#include "SceShaders.hpp"
extern "C" {
int APS5_VABI sceAgcFuseShaderHalves_nid_postfix(Shader* fusedResult, const Shader* front, const Shader* back, void* scratchMem);
int APS5_VABI sceAgcUpdateInterpolantMapping_0100(ShaderRegister* regs, const Shader* vs, const Shader* ps);
}
#endif
@@ -1,4 +1,14 @@
#ifndef CORE_LIBS_PRX_LIBSCEAGC_MISC_INCLUDE_SUSPEND_HPP
#define CORE_LIBS_PRX_LIBSCEAGC_MISC_INCLUDE_SUSPEND_HPP
#include "SceTypes.hpp"
#include "SceShaders.hpp"
extern "C" {
int APS5_VABI sceAgcSuspendPoint(void);
int APS5_VABI sceAgcSetSubmitMode(std::uint32_t mode);
}
#endif
+31 -13
View File
@@ -11,37 +11,55 @@
namespace {
constexpr int GRAPHICS5_ERROR_INVALID_ALIGNMENT = static_cast<int>(0x8a6c0002u);
constexpr int GRAPHICS5_ERROR_OUT_OF_RANGE = static_cast<int>(0x8a6c000bu);
constexpr int GRAPHICS5_ERROR_SEMAPHORE_MEMORY_REGISTERED = static_cast<int>(0x8a6c0048u);
constexpr int GRAPHICS5_ERROR_SEMAPHORE_MEMORY_UNREGISTERED = static_cast<int>(0x8a6c0049u);
constexpr std::uint64_t semaphoreMemoryAlignment = 0x4000u;
constexpr std::uint64_t semaphoreMemoryLimit = 0x40000000u;
constexpr std::uint64_t semaphoreLabelBytes = 32u;
std::mutex semaphoreMutex;
std::uint8_t* semaphoreMemory = nullptr;
std::uint64_t semaphoreBytes = 0;
bool aligned(std::uint64_t value) {
return (value & (semaphoreMemoryAlignment - 1u)) == 0;
}
int registerSemaphoreMemory(void* memory, std::uint64_t sizeInBytes, bool limited, const char* name) {
const auto address = reinterpret_cast<std::uintptr_t>(memory);
std::lock_guard lock(semaphoreMutex);
if (semaphoreBytes != 0) return GRAPHICS5_ERROR_SEMAPHORE_MEMORY_REGISTERED;
if (!aligned(sizeInBytes)) return GRAPHICS5_ERROR_INVALID_ALIGNMENT;
if (limited && sizeInBytes > semaphoreMemoryLimit) return GRAPHICS5_ERROR_OUT_OF_RANGE;
if (!aligned(address)) return GRAPHICS5_ERROR_INVALID_ALIGNMENT;
if (sizeInBytes != 0) {
Agc::Command::Require(memory != nullptr, name, "null semaphore memory with a non-zero size");
Agc::Command::Require(sizeInBytes - 1u <= std::numeric_limits<std::uintptr_t>::max() - address, name, "semaphore memory wraps the address space");
std::memset(memory, 0, sizeInBytes);
}
semaphoreMemory = static_cast<std::uint8_t*>(memory);
semaphoreBytes = sizeInBytes;
return 0;
}
}
extern "C" {
int APS5_VABI sceAgcSetAmmSemaphoreMemory(void* memory, std::uint64_t sizeInBytes) {
const auto address = reinterpret_cast<std::uintptr_t>(memory);
std::lock_guard lock(semaphoreMutex);
if (semaphoreBytes != 0) return static_cast<int>(0x8a6c0048u);
if (((address | sizeInBytes) & (semaphoreMemoryAlignment - 1u)) != 0) return static_cast<int>(0x8a6c0002u);
Agc::Command::Require(memory != nullptr && sizeInBytes != 0, __func__, "null or empty AMM semaphore memory; the console's error is unknown");
Agc::Command::Require(sizeInBytes - 1u <= std::numeric_limits<std::uintptr_t>::max() - address, __func__, "AMM semaphore memory wraps the address space");
auto* labels = static_cast<std::uint8_t*>(memory);
std::memset(labels, 0, sizeInBytes);
semaphoreMemory = labels;
semaphoreBytes = sizeInBytes;
return 0;
return registerSemaphoreMemory(memory, sizeInBytes, false, __func__);
}
int APS5_VABI sceAgcSetSemaphoreMemory(void* memory, std::uint64_t sizeInBytes) {
return registerSemaphoreMemory(memory, sizeInBytes, true, __func__);
}
int APS5_VABI sceAgcGetSemaphoreLabel(std::uint32_t index, void** label) {
std::lock_guard lock(semaphoreMutex);
if (semaphoreBytes == 0) return static_cast<int>(0x8a6c0049u);
if (index >= semaphoreBytes / semaphoreLabelBytes) return static_cast<int>(0x8a6c000bu);
if (semaphoreBytes == 0) return GRAPHICS5_ERROR_SEMAPHORE_MEMORY_UNREGISTERED;
if (index >= semaphoreBytes / semaphoreLabelBytes) return GRAPHICS5_ERROR_OUT_OF_RANGE;
Agc::Command::Require(label != nullptr, __func__, "null semaphore label output");
*label = semaphoreMemory + static_cast<std::uint64_t>(index) * semaphoreLabelBytes;
return 0;
@@ -1,4 +1,17 @@
#ifndef CORE_LIBS_PRX_LIBSCEAGC_PATCH_INCLUDE_BRANCH_HPP
#define CORE_LIBS_PRX_LIBSCEAGC_PATCH_INCLUDE_BRANCH_HPP
#include "SceTypes.hpp"
#include "SceShaders.hpp"
extern "C" {
int APS5_VABI sceAgcBranchPatchSetCompareAddress(std::uint32_t* cmd, const volatile std::uint64_t* compareAddress);
int APS5_VABI sceAgcBranchPatchSetThenTarget(std::uint32_t* cmd, const volatile std::uint32_t* target, std::uint32_t sizeInDwords);
int APS5_VABI sceAgcBranchPatchSetElseTarget(std::uint32_t* cmd, const volatile std::uint32_t* target, std::uint32_t sizeInDwords);
int APS5_VABI sceAgcBranchPatchSetThenTarget_0300(std::uint32_t* cmd, std::uint8_t cachePolicy, const volatile std::uint32_t* target, std::uint32_t sizeInDwords);
int APS5_VABI sceAgcBranchPatchSetElseTarget_0300(std::uint32_t* cmd, std::uint8_t cachePolicy, const volatile std::uint32_t* target, std::uint32_t sizeInDwords);
}
#endif
@@ -1,4 +1,16 @@
#ifndef CORE_LIBS_PRX_LIBSCEAGC_PATCH_INCLUDE_CONDEXEC_HPP
#define CORE_LIBS_PRX_LIBSCEAGC_PATCH_INCLUDE_CONDEXEC_HPP
#include "SceTypes.hpp"
#include "SceShaders.hpp"
extern "C" {
int APS5_VABI sceAgcCondExecPatchSetCommandAddress(uint32_t* cmd, const volatile uint32_t* command);
int APS5_VABI sceAgcCondExecPatchSetEnd(uint32_t* cmd, const volatile uint32_t* buffer);
int APS5_VABI sceAgcAsyncCondExecPatchSetCommandAddress(std::uint32_t* cmd, const volatile std::uint32_t* command);
int APS5_VABI sceAgcAsyncCondExecPatchSetEnd(std::uint32_t* cmd, const volatile std::uint32_t* buffer);
}
#endif
@@ -1,4 +1,14 @@
#ifndef CORE_LIBS_PRX_LIBSCEAGC_PATCH_INCLUDE_DMA_HPP
#define CORE_LIBS_PRX_LIBSCEAGC_PATCH_INCLUDE_DMA_HPP
#include "SceTypes.hpp"
#include "SceShaders.hpp"
extern "C" {
int APS5_VABI sceAgcDmaDataPatchSetDstAddressOrOffset(std::uint32_t* cmd, std::uint64_t address);
int APS5_VABI sceAgcDmaDataPatchSetSrcAddressOrOffsetOrImmediate(std::uint32_t* cmd, std::uint64_t address);
}
#endif
@@ -1,4 +1,21 @@
#ifndef CORE_LIBS_PRX_LIBSCEAGC_PATCH_INCLUDE_INDIRECTREGISTERS_HPP
#define CORE_LIBS_PRX_LIBSCEAGC_PATCH_INCLUDE_INDIRECTREGISTERS_HPP
#include "SceTypes.hpp"
#include "SceShaders.hpp"
extern "C" {
int APS5_VABI sceAgcSetCxRegIndirectPatchAddRegisters(std::uint32_t* cmd, std::uint32_t numRegs);
int APS5_VABI sceAgcSetCxRegIndirectPatchSetAddress(std::uint32_t* cmd, const volatile ShaderRegister* regs);
int APS5_VABI sceAgcSetCxRegIndirectPatchSetNumRegisters(std::uint32_t* cmd, std::uint32_t numRegs);
int APS5_VABI sceAgcSetShRegIndirectPatchAddRegisters(std::uint32_t* cmd, std::uint32_t numRegs);
int APS5_VABI sceAgcSetShRegIndirectPatchSetAddress(std::uint32_t* cmd, const volatile ShaderRegister* regs);
int APS5_VABI sceAgcSetShRegIndirectPatchSetNumRegisters(std::uint32_t* cmd, std::uint32_t numRegs);
int APS5_VABI sceAgcSetUcRegIndirectPatchAddRegisters(std::uint32_t* cmd, std::uint32_t numRegs);
int APS5_VABI sceAgcSetUcRegIndirectPatchSetAddress(std::uint32_t* cmd, const volatile ShaderRegister* regs);
int APS5_VABI sceAgcSetUcRegIndirectPatchSetNumRegisters(std::uint32_t* cmd, std::uint32_t numRegs);
}
#endif
@@ -1,4 +1,14 @@
#ifndef CORE_LIBS_PRX_LIBSCEAGC_PATCH_INCLUDE_JUMP_HPP
#define CORE_LIBS_PRX_LIBSCEAGC_PATCH_INCLUDE_JUMP_HPP
#include "SceTypes.hpp"
#include "SceShaders.hpp"
extern "C" {
int APS5_VABI sceAgcJumpPatchSetTarget(uint32_t* cmd, const volatile uint32_t* target, uint32_t sizeInDwords);
int APS5_VABI sceAgcJumpPatchSetTarget_0300(uint32_t* cmd, uint64_t cachePolicy, const volatile uint32_t* target, uint32_t sizeInDwords);
}
#endif
@@ -1,4 +1,13 @@
#ifndef CORE_LIBS_PRX_LIBSCEAGC_PATCH_INCLUDE_NOP_HPP
#define CORE_LIBS_PRX_LIBSCEAGC_PATCH_INCLUDE_NOP_HPP
#include "SceTypes.hpp"
#include "SceShaders.hpp"
extern "C" {
std::uint32_t* APS5_VABI sceAgcSetNop(CommandBuffer* buf, std::uint32_t sizeDw);
}
#endif
@@ -1,4 +1,16 @@
#ifndef CORE_LIBS_PRX_LIBSCEAGC_PATCH_INCLUDE_QUEUEENDOFPIPEACTION_HPP
#define CORE_LIBS_PRX_LIBSCEAGC_PATCH_INCLUDE_QUEUEENDOFPIPEACTION_HPP
#include "SceTypes.hpp"
#include "SceShaders.hpp"
extern "C" {
int APS5_VABI sceAgcQueueEndOfPipeActionPatchAddress(std::uint32_t* cmd, const volatile Label* address);
int APS5_VABI sceAgcQueueEndOfPipeActionPatchData(uint32_t* cmd, uint64_t data);
int APS5_VABI sceAgcQueueEndOfPipeActionPatchGcrCntl(std::uint32_t* cmd, std::uint16_t gcrControl);
int APS5_VABI sceAgcQueueEndOfPipeActionPatchType(std::uint32_t* cmd, std::uint8_t action);
}
#endif
@@ -1,4 +1,14 @@
#ifndef CORE_LIBS_PRX_LIBSCEAGC_PATCH_INCLUDE_REWIND_HPP
#define CORE_LIBS_PRX_LIBSCEAGC_PATCH_INCLUDE_REWIND_HPP
#include "SceTypes.hpp"
#include "SceShaders.hpp"
extern "C" {
int APS5_VABI sceAgcRewindPatchSetRewindState(uint32_t* cmd, uint8_t state);
int APS5_VABI sceAgcAsyncRewindPatchSetRewindState(std::uint32_t* cmd, std::uint8_t state);
}
#endif
@@ -1,4 +1,22 @@
#ifndef CORE_LIBS_PRX_LIBSCEAGC_PATCH_INCLUDE_WAITREGMEM_HPP
#define CORE_LIBS_PRX_LIBSCEAGC_PATCH_INCLUDE_WAITREGMEM_HPP
#include "SceTypes.hpp"
#include "SceShaders.hpp"
extern "C" {
int APS5_VABI sceAgcWaitRegMemPatchAddress(std::uint32_t* cmd, const volatile void* address);
int APS5_VABI sceAgcWaitRegMemPatchReference(std::uint32_t* cmd, std::uint32_t reference);
int APS5_VABI sceAgcWaitRegMemPatchMask(std::uint32_t* cmd, std::uint32_t mask);
int APS5_VABI sceAgcWaitRegMemPatchCompareFunction(std::uint32_t* cmd, std::uint8_t compareFunction);
int APS5_VABI sceAgcWriteDataPatchSetAddressOrOffset(std::uint32_t* cmd, std::uint64_t address);
int APS5_VABI sceAgcAsyncWriteDataPatchSetAddressOrOffset(std::uint32_t* cmd, std::uint64_t address);
int APS5_VABI sceAgcWriteDataPatchSetDst(std::uint32_t* cmd, std::uint8_t dst);
int APS5_VABI sceAgcAsyncWriteDataPatchSetDst(std::uint32_t* cmd, std::uint8_t dst);
int APS5_VABI sceAgcWriteDataPatchSetCachePolicy(std::uint32_t* cmd, std::uint8_t cachePolicy);
int APS5_VABI sceAgcAsyncWriteDataPatchSetCachePolicy(std::uint32_t* cmd, std::uint8_t cachePolicy);
}
#endif
@@ -1,4 +1,14 @@
#ifndef CORE_LIBS_PRX_LIBSCEAGC_PREDICATION_INCLUDE_PREDICATION_HPP
#define CORE_LIBS_PRX_LIBSCEAGC_PREDICATION_INCLUDE_PREDICATION_HPP
#include "SceTypes.hpp"
#include "SceShaders.hpp"
extern "C" {
int APS5_VABI sceAgcSetPacketPredication(uint32_t* packet, uint32_t predication);
int APS5_VABI sceAgcSetRangePredication(uint32_t* start, const volatile uint32_t* end, uint32_t predication);
}
#endif
@@ -1,4 +1,13 @@
#ifndef CORE_LIBS_PRX_LIBSCEAGC_SHADER_INCLUDE_CREATESHADER_HPP
#define CORE_LIBS_PRX_LIBSCEAGC_SHADER_INCLUDE_CREATESHADER_HPP
#include "SceTypes.hpp"
#include "SceShaders.hpp"
extern "C" {
int APS5_VABI sceAgcCreateShader(Shader** dst, void* header, const volatile void* code);
}
#endif
@@ -0,0 +1,10 @@
#pragma once
#include "SceTypes.hpp"
#include "SceShaders.hpp"
extern "C" {
int APS5_VABI sceAgcGetGsOversubscription(ShaderRegister* regs, const Shader* gs, std::uint32_t budget, float factor);
}
@@ -0,0 +1,10 @@
#pragma once
#include "SceTypes.hpp"
#include "SceShaders.hpp"
extern "C" {
int APS5_VABI sceAgcGetGsPrimPayload(std::uint32_t* payload, const Shader* gs);
}
@@ -1,4 +1,17 @@
#ifndef CORE_LIBS_PRX_LIBSCEAGC_SHADER_INCLUDE_INTERPOLANTMAPPING_HPP
#define CORE_LIBS_PRX_LIBSCEAGC_SHADER_INCLUDE_INTERPOLANTMAPPING_HPP
#include "SceTypes.hpp"
#include "SceShaders.hpp"
extern "C" {
int APS5_VABI sceAgcCreateInterpolantMapping_0100(ShaderRegister* regs, const Shader* gs, const Shader* ps);
int APS5_VABI sceAgcCreateInterpolantMapping_10000(ShaderRegister* regs, const Shader* gs, const Shader* ps);
int APS5_VABI sceAgcUpdateInterpolantMapping_10000(ShaderRegister* regs, const Shader* gs, const Shader* ps);
int APS5_VABI sceAgcCreateInterpolantMapping_nid_postfix(ShaderRegister* regs, const Shader* gs, const Shader* ps);
int APS5_VABI sceAgcUpdateInterpolantMapping_nid_postfix(ShaderRegister* regs, const Shader* gs, const Shader* ps);
}
#endif
@@ -0,0 +1,10 @@
#pragma once
#include "SceTypes.hpp"
#include "SceShaders.hpp"
extern "C" {
int APS5_VABI sceAgcLinkShaders(ShaderRegister* context, ShaderRegister* primitive, const void* reserved, const Shader* vertex, const Shader* pixel, std::uint32_t primitiveType);
}
@@ -1,4 +1,14 @@
#ifndef CORE_LIBS_PRX_LIBSCEAGC_SHADER_INCLUDE_PRIMSTATE_HPP
#define CORE_LIBS_PRX_LIBSCEAGC_SHADER_INCLUDE_PRIMSTATE_HPP
#include "SceTypes.hpp"
#include "SceShaders.hpp"
extern "C" {
int APS5_VABI sceAgcCreatePrimState(ShaderRegister* cxRegs, ShaderRegister* ucRegs, const Shader* hs, const Shader* gs, std::uint32_t primType);
int APS5_VABI sceAgcUpdatePrimState(ShaderRegister* cxRegs, ShaderRegister* ucRegs, std::uint32_t primType);
}
#endif
@@ -1,3 +1,4 @@
#include "prx/libSceAgc/Shader/include/GsOversubscription.hpp"
#include <algorithm>
#include <cmath>
#include <cstdint>
@@ -1,3 +1,4 @@
#include "prx/libSceAgc/Shader/include/GsPrimPayload.hpp"
#include <cstdint>
#include <stdexcept>
#include <string>
@@ -1,3 +1,6 @@
#include "prx/libSceAgc/Shader/include/PrimState.hpp"
#include "prx/libSceAgc/Shader/include/LinkShaders.hpp"
#include "prx/libSceAgc/Shader/include/InterpolantMapping.hpp"
#include "SceShaders.hpp"
#include "prx/libSceAgcDriver/Execution/include/ShaderPreparationScope.hpp"
#include "prx/libSceAgcDriver/Execution/include/Driver.hpp"
@@ -6,9 +9,6 @@
#include <array>
#include <algorithm>
extern "C" int APS5_VABI sceAgcCreatePrimState(ShaderRegister*, ShaderRegister*, const Shader*, const Shader*, std::uint32_t);
extern "C" int APS5_VABI sceAgcCreateInterpolantMapping_0100(ShaderRegister*, const Shader*, const Shader*);
extern "C" int APS5_VABI sceAgcLinkShaders(ShaderRegister* context, ShaderRegister* primitive,
const void* reserved, const Shader* vertex, const Shader* pixel, std::uint32_t primitiveType) {
using namespace ShaderRegs;
+6 -25
View File
@@ -1,3 +1,4 @@
#include "prx/libSceAgc/include/Agc.hpp"
#include <cstdint>
#include <cstddef>
#include "SceTypes.hpp"
@@ -5,39 +6,19 @@
extern "C" {
std::uint32_t* APS5_VABI sceAgcAcbPushMarkerSpan(CommandBuffer* buffer, const char* text, std::uint32_t length, std::uint32_t color) {
NotImplemented_nid_no_patch(__func__);
return 0;
}
std::uint32_t* APS5_VABI sceAgcAcbSetMarkerSpan(CommandBuffer* buffer, const char* text, std::uint32_t length, std::uint32_t color) {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAgcAcquireMemSetEngine(std::uint32_t* packet, std::uint8_t engine) {
NotImplemented_nid_no_patch(__func__);
return 0;
}
std::uint32_t* APS5_VABI sceAgcDcbPushMarkerSpan(CommandBuffer* buffer, const char* text, std::uint32_t length, std::uint32_t color) {
NotImplemented_nid_no_patch(__func__);
return 0;
}
std::uint32_t* APS5_VABI sceAgcDcbSetMarkerSpan(CommandBuffer* buffer, const char* text, std::uint32_t length, std::uint32_t color) {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAgcSetSemaphoreMemory(void* memory, std::uint64_t size) {
NotImplemented_nid_no_patch(__func__);
return 0;
}
std::uint32_t* APS5_VABI sceAgcCbMemsetExclusive(CommandBuffer* buffer, void* destination, const void* pattern, std::uint64_t size) {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAgcAcbInitializeDefaultHardwareState() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
}
+19
View File
@@ -0,0 +1,19 @@
#pragma once
#include "SceTypes.hpp"
#include "SceShaders.hpp"
extern "C" {
int APS5_VABI sceAgcSuspendPointAndCheckStatus(void);
std::uint32_t* APS5_VABI sceAgcAcbPushMarkerSpan(CommandBuffer* buffer, const char* text, std::uint32_t length, std::uint32_t color);
std::uint32_t* APS5_VABI sceAgcAcbSetMarkerSpan(CommandBuffer* buffer, const char* text, std::uint32_t length, std::uint32_t color);
int APS5_VABI sceAgcAcquireMemSetEngine(std::uint32_t* packet, std::uint8_t engine);
std::uint32_t* APS5_VABI sceAgcDcbPushMarkerSpan(CommandBuffer* buffer, const char* text, std::uint32_t length, std::uint32_t color);
std::uint32_t* APS5_VABI sceAgcDcbSetMarkerSpan(CommandBuffer* buffer, const char* text, std::uint32_t length, std::uint32_t color);
int APS5_VABI sceAgcSetSemaphoreMemory(void* memory, std::uint64_t size);
std::uint32_t* APS5_VABI sceAgcCbMemsetExclusive(CommandBuffer* buffer, void* destination, const void* pattern, std::uint64_t size);
int APS5_VABI sceAgcAcbInitializeDefaultHardwareState();
}
+51
View File
@@ -0,0 +1,51 @@
#pragma once
#include "prx/libSceAgc/Acb/include/Control.hpp"
#include "prx/libSceAgc/Acb/include/Dispatch.hpp"
#include "prx/libSceAgc/Acb/include/Memory.hpp"
#include "prx/libSceAgc/Acb/include/Sync.hpp"
#include "prx/libSceAgc/Cb/include/Branch.hpp"
#include "prx/libSceAgc/Cb/include/Dispatch.hpp"
#include "prx/libSceAgc/Cb/include/Registers.hpp"
#include "prx/libSceAgc/Cb/include/Sync.hpp"
#include "prx/libSceAgc/DcbDraw/include/DrawIndexed.hpp"
#include "prx/libSceAgc/DcbDraw/include/DrawNonIndexed.hpp"
#include "prx/libSceAgc/DcbDraw/include/IndexBuffer.hpp"
#include "prx/libSceAgc/DcbDraw/include/Instancing.hpp"
#include "prx/libSceAgc/DcbDraw/include/Stats.hpp"
#include "prx/libSceAgc/DcbFlow/include/Control.hpp"
#include "prx/libSceAgc/DcbFlow/include/Dispatch.hpp"
#include "prx/libSceAgc/DcbFlow/include/Memory.hpp"
#include "prx/libSceAgc/DcbFlow/include/Sync.hpp"
#include "prx/libSceAgc/DcbState/include/ContextState.hpp"
#include "prx/libSceAgc/DcbState/include/Display.hpp"
#include "prx/libSceAgc/DcbState/include/Init.hpp"
#include "prx/libSceAgc/DcbState/include/Marker.hpp"
#include "prx/libSceAgc/DcbState/include/Predication.hpp"
#include "prx/libSceAgc/DcbState/include/Registers.hpp"
#include "prx/libSceAgc/DcbState/include/Workload.hpp"
#include "prx/libSceAgc/include/Agc.hpp"
#include "prx/libSceAgc/Misc/include/Debug.hpp"
#include "prx/libSceAgc/Misc/include/PacketInfo.hpp"
#include "prx/libSceAgc/Misc/include/Platform.hpp"
#include "prx/libSceAgc/Misc/include/RegisterDefaults.hpp"
#include "prx/libSceAgc/Misc/include/Semaphore.hpp"
#include "prx/libSceAgc/Misc/include/ShaderFusion.hpp"
#include "prx/libSceAgc/Misc/include/StaticBuffer.hpp"
#include "prx/libSceAgc/Misc/include/Suspend.hpp"
#include "prx/libSceAgc/Patch/include/Branch.hpp"
#include "prx/libSceAgc/Patch/include/CondExec.hpp"
#include "prx/libSceAgc/Patch/include/Dma.hpp"
#include "prx/libSceAgc/Patch/include/IndirectRegisters.hpp"
#include "prx/libSceAgc/Patch/include/Jump.hpp"
#include "prx/libSceAgc/Patch/include/Nop.hpp"
#include "prx/libSceAgc/Patch/include/QueueEndOfPipeAction.hpp"
#include "prx/libSceAgc/Patch/include/Rewind.hpp"
#include "prx/libSceAgc/Patch/include/WaitRegMem.hpp"
#include "prx/libSceAgc/Predication/include/Predication.hpp"
#include "prx/libSceAgc/Shader/include/CreateShader.hpp"
#include "prx/libSceAgc/Shader/include/GsOversubscription.hpp"
#include "prx/libSceAgc/Shader/include/GsPrimPayload.hpp"
#include "prx/libSceAgc/Shader/include/InterpolantMapping.hpp"
#include "prx/libSceAgc/Shader/include/LinkShaders.hpp"
#include "prx/libSceAgc/Shader/include/PrimState.hpp"
@@ -1,14 +1,11 @@
#include "prx/libSceAgc/Acb/include/Memory.hpp"
#include "prx/libSceAgc/Acb/include/Control.hpp"
#include "prx/libSceAgc/Command/include/Packet.hpp"
#include <array>
#include <cstdio>
#include <cstdint>
extern "C" std::uint32_t APS5_VABI sceAgcAcbAtomicGdsGetSize();
extern "C" std::uint32_t* APS5_VABI sceAgcAcbAtomicGds_0900(CommandBuffer* buf, std::uint32_t control, std::uint32_t mode,
std::uint32_t gdsOp, std::uint32_t src, std::uint16_t offset, std::uint16_t index, std::uint32_t mask,
std::uint32_t data, std::uint64_t compareData, std::uint64_t extraData);
int main() {
constexpr std::uint32_t expectedSize = 11u * sizeof(std::uint32_t);
if (sceAgcAcbAtomicGdsGetSize() != expectedSize) {
+2 -10
View File
@@ -1,3 +1,5 @@
#include "prx/libSceAgc/Patch/include/Branch.hpp"
#include "prx/libSceAgc/Cb/include/Branch.hpp"
#include "prx/libSceAgc/Command/include/Packet.hpp"
#include <algorithm>
@@ -7,16 +9,6 @@
#include <cstdio>
#include <stdexcept>
extern "C" {
std::uint32_t* APS5_VABI sceAgcCbBranch(CommandBuffer*, std::uint8_t, std::uint8_t, const volatile std::uint64_t*, std::uint64_t, std::uint64_t, std::uint8_t, const volatile std::uint32_t*, std::uint32_t, std::uint8_t, const volatile std::uint32_t*, std::uint32_t);
std::uint32_t APS5_VABI sceAgcCbBranchGetSize();
int APS5_VABI sceAgcBranchPatchSetCompareAddress(std::uint32_t*, const volatile std::uint64_t*);
int APS5_VABI sceAgcBranchPatchSetThenTarget(std::uint32_t*, const volatile std::uint32_t*, std::uint32_t);
int APS5_VABI sceAgcBranchPatchSetElseTarget(std::uint32_t*, const volatile std::uint32_t*, std::uint32_t);
int APS5_VABI sceAgcBranchPatchSetThenTarget_0300(std::uint32_t*, std::uint8_t, const volatile std::uint32_t*, std::uint32_t);
int APS5_VABI sceAgcBranchPatchSetElseTarget_0300(std::uint32_t*, std::uint8_t, const volatile std::uint32_t*, std::uint32_t);
}
namespace {
void check(bool condition, const char* message) {
@@ -1,3 +1,4 @@
#include "prx/libSceAgc/Cb/include/Branch.hpp"
#include "prx/libSceAgc/Command/include/Packet.hpp"
#include <array>
@@ -5,11 +6,6 @@
#include <cstdio>
#include <stdexcept>
extern "C" {
std::uint32_t* APS5_VABI sceAgcCbCondWrite(CommandBuffer*, std::uint32_t, std::uint32_t, const volatile void*, std::uint32_t, const volatile void*, std::uint32_t, std::uint32_t);
std::uint32_t APS5_VABI sceAgcCbCondWriteGetSize();
}
namespace {
constexpr std::uint32_t Sentinel = 0xabcdef01u;
+62 -58
View File
@@ -1,3 +1,23 @@
#include "prx/libSceAgc/include/Agc.hpp"
#include "prx/libSceAgcDriver/Resource/include/Registration.hpp"
#include "prx/libSceAgc/Patch/include/WaitRegMem.hpp"
#include "prx/libSceAgc/Patch/include/Nop.hpp"
#include "prx/libSceAgc/Misc/include/Suspend.hpp"
#include "prx/libSceAgc/Misc/include/RegisterDefaults.hpp"
#include "prx/libSceAgc/Misc/include/PacketInfo.hpp"
#include "prx/libSceAgc/Misc/include/Debug.hpp"
#include "prx/libSceAgc/DcbState/include/Workload.hpp"
#include "prx/libSceAgc/DcbState/include/Marker.hpp"
#include "prx/libSceAgc/DcbState/include/Init.hpp"
#include "prx/libSceAgc/DcbState/include/Display.hpp"
#include "prx/libSceAgc/DcbState/include/ContextState.hpp"
#include "prx/libSceAgc/DcbFlow/include/Sync.hpp"
#include "prx/libSceAgc/DcbFlow/include/Control.hpp"
#include "prx/libSceAgc/DcbDraw/include/Instancing.hpp"
#include "prx/libSceAgc/DcbDraw/include/IndexBuffer.hpp"
#include "prx/libSceAgc/DcbDraw/include/DrawIndexed.hpp"
#include "prx/libSceAgc/Cb/include/Registers.hpp"
#include "prx/libSceAgc/Acb/include/Control.hpp"
#include "prx/libSceAgc/Command/include/Packet.hpp"
#include "prx/libSceAgc/Command/include/Memory.hpp"
#include "prx/libSceAgc/Command/include/RegisterDefaults.hpp"
@@ -10,53 +30,6 @@
#include <cstring>
#include <string>
extern "C" std::uint32_t* APS5_VABI sceAgcDcbEventWrite(CommandBuffer* buf, std::uint8_t eventType, const volatile void* address);
extern "C" std::uint64_t APS5_VABI sceAgcDcbEventWriteGetSize(std::uint8_t eventType);
extern "C" std::uint32_t* APS5_VABI sceAgcDcbResetQueue(CommandBuffer* buf, std::uint32_t op, std::uint32_t state);
extern "C" std::uint32_t* APS5_VABI sceAgcDcbClearState(CommandBuffer* buf, std::uint32_t command);
extern "C" std::uint32_t* APS5_VABI sceAgcDcbSetFlip(CommandBuffer* buf, std::uint32_t handle, std::int32_t index, std::uint32_t mode, std::int64_t argument);
extern "C" std::uint32_t* APS5_VABI sceAgcAcbWaitUntilSafeForRendering(CommandBuffer* buf, std::uint32_t handle, std::uint32_t index);
extern "C" std::uint32_t* APS5_VABI sceAgcAcbSetFlip(CommandBuffer* buf, std::uint32_t handle, std::int32_t index, std::uint32_t mode, std::int64_t argument);
extern "C" int APS5_VABI sceAgcSuspendPoint();
extern "C" int APS5_VABI sceAgcInit(std::uint32_t version);
extern "C" void* APS5_VABI sceAgcGetRegisterDefaults();
extern "C" void* APS5_VABI sceAgcGetRegisterDefaults2(std::uint32_t version);
extern "C" void* APS5_VABI sceAgcGetRegisterDefaultsInternal();
extern "C" void* APS5_VABI sceAgcGetRegisterDefaults2Internal(std::uint32_t version);
extern "C" int APS5_VABI sceAgcGetDefaultCxStateFlat(void* out_state, std::uint32_t size);
extern "C" int APS5_VABI sceAgcInit_0090(std::uint32_t* state, std::uint32_t version);
extern "C" std::uint32_t* APS5_VABI sceAgcDcbDrawIndexAuto(CommandBuffer* buf, std::uint32_t indexCount, std::uint64_t modifier);
extern "C" std::uint32_t* APS5_VABI sceAgcDcbDrawIndexIndirect(CommandBuffer* buf, std::uint32_t dataOffsetInBytes, std::uint64_t modifier);
extern "C" std::uint32_t* APS5_VABI sceAgcDcbDrawIndexIndirectMulti(CommandBuffer* buf, std::uint32_t dataOffsetInBytes, std::uint32_t countIndirect, std::uint32_t maxCountOrCount, const volatile void* countAddress, std::uint32_t strideInBytes, std::uint64_t modifier);
extern "C" int APS5_VABI sceAgcWaitRegMemPatchAddress(std::uint32_t* cmd, const volatile void* address);
extern "C" int APS5_VABI sceAgcWaitRegMemPatchCompareFunction(std::uint32_t* cmd, std::uint8_t compareFunction);
extern "C" int APS5_VABI sceAgcWaitRegMemPatchReference(std::uint32_t* cmd, std::uint32_t reference);
extern "C" int APS5_VABI sceAgcWaitRegMemPatchMask(std::uint32_t* cmd, std::uint32_t mask);
extern "C" int APS5_VABI sceAgcGetDataPacketPayloadAddress_0090(std::uint32_t** addr, std::uint32_t* cmd, int type);
extern "C" std::uint32_t* APS5_VABI sceAgcCbSetShRegisterRangeDirect(CommandBuffer* buf, std::uint32_t offset, const std::uint32_t* values, std::uint32_t numValues);
extern "C" std::uint32_t* APS5_VABI sceAgcDcbContextStateOp_0100(CommandBuffer* buf, std::uint32_t operation);
extern "C" std::uint32_t* APS5_VABI sceAgcDcbPushMarker(CommandBuffer* buf, const char* str, std::uint32_t color);
extern "C" std::uint32_t* APS5_VABI sceAgcDcbPopMarker(CommandBuffer* buf);
extern "C" std::uint32_t* APS5_VABI sceAgcDcbSetWorkloadComplete(CommandBuffer* buf, std::uint32_t stream_id, std::uint32_t workload_id);
extern "C" int APS5_VABI sceAgcDriverRegisterWorkloadStream(std::uint32_t stream_id, const void* stream);
extern "C" int APS5_VABI sceAgcDriverUnregisterWorkloadStream(std::uint32_t stream_id);
extern "C" std::uint32_t* APS5_VABI sceAgcDcbSetWorkloadsActive(CommandBuffer* buf, std::uint32_t stream_id, const std::uint32_t* workload_ids, std::uint32_t workload_count);
extern "C" std::uint32_t* APS5_VABI sceAgcDcbSetWorkloadStreamInactive(CommandBuffer* buf, std::uint32_t streamId);
extern "C" std::uint32_t* APS5_VABI sceAgcAcbSetWorkloadsActive(CommandBuffer* buf, std::uint32_t streamId, const std::uint32_t* workloadIds, std::uint32_t workloadCount);
extern "C" std::uint32_t* APS5_VABI sceAgcAcbSetWorkloadComplete(CommandBuffer* buf, std::uint32_t streamId, std::uint32_t workloadId);
extern "C" std::uint32_t* APS5_VABI sceAgcAcbSetWorkloadStreamInactive(CommandBuffer* buf, std::uint32_t streamId);
extern "C" std::uint32_t* APS5_VABI sceAgcDcbSetMarker(CommandBuffer* buf, const char* str, std::uint32_t color);
extern "C" std::uint32_t* APS5_VABI sceAgcAcbPushMarker(CommandBuffer* buf, const char* str, std::uint32_t color);
extern "C" std::uint32_t* APS5_VABI sceAgcAcbPopMarker(CommandBuffer* buf);
extern "C" std::uint32_t* APS5_VABI sceAgcAcbSetMarker(CommandBuffer* buf, const char* str, std::uint32_t color);
extern "C" std::uint32_t* APS5_VABI sceAgcDcbSetIndexBuffer(CommandBuffer* buf, std::uint64_t indexAddress);
extern "C" std::uint32_t* APS5_VABI sceAgcDcbSetIndexIndirectArgs(CommandBuffer* buf, std::uint64_t attributeAddress, std::uint32_t attributeIndex);
extern "C" std::uint32_t APS5_VABI sceAgcDcbSetIndexIndirectArgsGetSize();
extern "C" std::uint32_t* APS5_VABI sceAgcDcbSetBaseIndirectArgs(CommandBuffer* buf, std::uint32_t shaderType, const volatile void* indirectBaseAddr);
extern "C" std::uint32_t* APS5_VABI sceAgcSetNop(CommandBuffer* buf, std::uint32_t sizeDw);
extern "C" int APS5_VABI sceAgcDebugRaiseException(std::uint32_t exceptionId);
extern "C" std::uint32_t* APS5_VABI sceAgcAcbAtomicGds_0900(CommandBuffer* buf, std::uint32_t control, std::uint32_t mode, std::uint32_t gdsOp, std::uint32_t src, std::uint16_t offset, std::uint16_t index, std::uint32_t mask, std::uint32_t data, std::uint64_t compareData, std::uint64_t extraData);
namespace {
void check(bool condition, const char* message) {
@@ -225,6 +198,29 @@ void testMarkers() {
expectFailure([] { sceAgcAcbSetMarker(nullptr, "frame", 0); });
}
void testMarkerSpans() {
Storage dcb;
Storage acb;
const char text[] = "framebuffer";
const auto* acbPush = sceAgcAcbPushMarkerSpan(&acb.buffer, text, 5, 0x00ff00u);
const auto* dcbPush = sceAgcDcbPushMarkerSpan(&dcb.buffer, text, 5, 0xff0000u);
check(acbPush == acb.words.data() && acbPush[0] == Agc::Command::Header(0x10, 3, 0x0bu << 2u), "ACB push marker span header mismatch");
check(std::strcmp(reinterpret_cast<const char*>(acbPush + 1), "frame") == 0, "ACB push marker span does not stop at its length");
check(dcbPush == dcb.words.data(), "DCB push marker span did not return its packet");
const auto* acbSet = sceAgcAcbSetMarkerSpan(&acb.buffer, nullptr, 0, 0);
const auto* dcbSet = sceAgcDcbSetMarkerSpan(&dcb.buffer, nullptr, 0, 0);
check(acbSet == acb.words.data() + 3 && dcbSet == dcb.words.data() + 3, "set marker span did not return its push packet");
check(acbSet[0] == Agc::Command::Header(0x10, 2, 0x0bu << 2u) && acbSet[1] == 0 && acbSet[2] == Agc::Command::Header(0x10, 2, 0x0cu << 2u), "ACB set marker span is not a push and pop pair");
check(dcb.words == acb.words && acb.buffer.cursor_up == acb.words.data() + 7, "ACB and DCB marker spans differ");
Storage plain;
sceAgcAcbPushMarker(&plain.buffer, "frame", 0);
check(std::equal(plain.words.begin(), plain.words.begin() + 3, acb.words.begin()), "marker span differs from the same text pushed whole");
expectFailure([] { sceAgcAcbPushMarkerSpan(nullptr, "frame", 5, 0); });
expectFailure([] { sceAgcAcbPushMarker(nullptr, reinterpret_cast<const char*>(std::uintptr_t{0x10}), 0); });
expectFailure([] { sceAgcDcbSetMarkerSpan(nullptr, "frame", 5, 0); });
expectFailure([&] { sceAgcDcbPushMarkerSpan(&dcb.buffer, nullptr, 4, 0); });
}
void testWorkloads() {
Storage dcb;
const std::uint32_t ids[2] = {3, 4};
@@ -703,15 +699,13 @@ void testDefaults() {
expectFailure([] { Agc::Command::GetRegisterDefaults(0xffffffffu, true, __func__); });
}
struct DefaultsType {
std::uint32_t key;
std::uint32_t bankIndex;
};
constexpr std::size_t PairEntryBytes = 8;
constexpr std::size_t CompactEntryBytes = 12;
std::uint32_t colourTargetBlock(const void* root) {
std::uint32_t colourTargetBlock(const void* root, std::size_t entryBytes) {
const auto* bytes = static_cast<const unsigned char*>(root);
ShaderRegister** contextBlocks = nullptr;
const DefaultsType* types = nullptr;
const unsigned char* types = nullptr;
std::uint32_t count = 0;
std::memcpy(&contextBlocks, bytes, sizeof(contextBlocks));
std::memcpy(&types, bytes + 0x30, sizeof(types));
@@ -721,12 +715,17 @@ std::uint32_t colourTargetBlock(const void* root) {
std::uint32_t matches = 0;
std::uint32_t block = 0;
for (std::uint32_t entry = 0; entry < count; ++entry) {
if ((types[entry].bankIndex & 3u) == 0)
std::uint32_t key = 0;
std::uint32_t bankIndex = 0;
std::memcpy(&key, types + entry * entryBytes, sizeof(key));
std::memcpy(&bankIndex, types + entry * entryBytes + 4, sizeof(bankIndex));
bankIndex &= 0x3ffu;
if ((bankIndex & 3u) == 0)
++contexts;
if (types[entry].key != 0x38e92c91u)
if (key != 0x38e92c91u)
continue;
check((types[entry].bankIndex & 3u) == 0, "MRT0 defaults are not in the context bank");
block = types[entry].bankIndex >> 2;
check((bankIndex & 3u) == 0, "MRT0 defaults are not in the context bank");
block = bankIndex >> 2;
++matches;
}
check(matches == 1 && block < contexts && contextBlocks[block] != nullptr, "MRT0 defaults key is missing, repeated or outside the context blocks");
@@ -739,7 +738,11 @@ std::uint32_t colourTargetBlock(const void* root) {
void testDefaultsTypes() {
const auto* legacy = sceAgcGetRegisterDefaults();
check(legacy != sceAgcGetRegisterDefaults2(13), "legacy register defaults reused the compact versioned table");
check(colourTargetBlock(legacy) == 78, "MRT0 defaults are not block 78 of the 137-entry table");
check(colourTargetBlock(legacy, PairEntryBytes) == 78, "MRT0 defaults are not block 78 of the 137-entry table");
check(colourTargetBlock(sceAgcGetRegisterDefaults2(13), CompactEntryBytes) == 78, "versioned MRT0 defaults are not block 78 of the compact 137-entry table");
check(colourTargetBlock(sceAgcGetRegisterDefaults2(10), CompactEntryBytes) == 73, "versioned MRT0 defaults are not block 73 of the compact 128-entry table");
for (std::uint32_t version = 4; version < 14; ++version)
colourTargetBlock(sceAgcGetRegisterDefaults2(version), CompactEntryBytes);
const auto checkCompactTypes = [](const void* root, std::uint32_t count, std::uint32_t key,
std::uint32_t packedType, std::uint32_t extra) {
@@ -815,6 +818,7 @@ int main(int argc, char** argv) {
testClearState();
testIndexedIndirectDraws();
testMarkers();
testMarkerSpans();
testWorkloads();
testWorkloadStreams();
testIndexBuffer();
Loaded 100 of 2622 files, more files were not shown because too many files have changed in this diff. Show more