Merge main into fix/libscesysmodule-public-ids-measured-ps5

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
orezraeyandClaude Opus 5.5 committed 2026-10-26 11:30:18 -03:00
commit db2e99e83d
2627 files changed
+124048 -22824

No files matched your search

@@ -6,7 +6,7 @@ body:
One thread per title, for every platform: search this category for the title ID first. If a thread exists, reply there, also when your OS or GPU differs from the one it reports.
Name the discussion `Game name (title ID)`, for example `DOOM Shareware (PPSA99666)`, without the platform.
Bug reports and implementation work belong in issues and pull requests; link them here as the blocker.
Per-game progress is also tracked on the [blocker map](https://alxcbtw.github.io/anyps5-roadmap/), built from public GitHub data. Only share material you have the right to distribute.
Only share material you have the right to distribute.
- type: checkboxes
id: searched
attributes:
+3 -3
View File
@@ -33,7 +33,7 @@ permissions:
concurrency:
group: build-${{ github.event_name }}-${{ github.event.pull_request.number || inputs.pr || github.ref }}
cancel-in-progress: true
cancel-in-progress: ${{ github.event_name != 'push' }}
env:
CONFIGURE: >-
@@ -421,7 +421,7 @@ jobs:
linux:
needs: eligibility
if: ${{ (github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && inputs.linux)) && needs.eligibility.outputs.full == 'true' }}
if: ${{ !github.event.repository.fork && (github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && inputs.linux)) && needs.eligibility.outputs.full == 'true' }}
runs-on: [self-hosted, linux, x64, anyps5]
timeout-minutes: 60
env:
@@ -641,7 +641,7 @@ jobs:
uses: actions/cache/restore@v6
with:
path: configure-seed.cmake
key: configure-seed-${{ env.WINLIBS_ZIP }}-${{ steps.submodules.outputs.key }}-${{ hashFiles('CMakeLists.txt', 'cmake/**', 'core/libs/CMakeLists.txt', '.github/workflows/build.yml') }}
key: configure-seed-${{ env.WINLIBS_ZIP }}-${{ steps.submodules.outputs.key }}-${{ hashFiles('CMakeLists.txt', 'cmake/**', 'core/libs/CMakeLists.txt', 'tools/configure_seed.py', '.github/workflows/build.yml') }}
- shell: cmd
run: if exist configure-seed.cmake (cmake -C configure-seed.cmake -S . -B build %CONFIGURE% "-DANYPS5_AGC_EXECUTION_TESTS=F16ToF32Unpack;F64FmaSubnormals;RegistrationPrepareShutdown") else (cmake -S . -B build %CONFIGURE% "-DANYPS5_AGC_EXECUTION_TESTS=F16ToF32Unpack;F64FmaSubnormals;RegistrationPrepareShutdown")
- if: steps.seed-cache.outputs.cache-hit != 'true'
+29
View File
@@ -0,0 +1,29 @@
name: Input mapper
on:
push:
branches: [main]
pull_request:
branches: [main]
paths:
- '.github/workflows/input-mapper.yml'
- 'tools/input-mapper/**'
- 'core/libs/prx/libScePad/include/**'
- 'core/libs/prx/libSceVideoOut/src/InputMapping.cpp'
permissions:
contents: read
jobs:
input-mapper:
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@v7
with:
persist-credentials: false
- uses: actions/setup-node@v7
with:
node-version: '22'
package-manager-cache: false
- run: npm --prefix tools/input-mapper test
+1
View File
@@ -18,6 +18,7 @@ concurrency:
jobs:
progress:
if: ${{ !github.event.repository.fork }}
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
+1 -1
View File
@@ -33,7 +33,7 @@ concurrency:
jobs:
pages:
if: github.event_name != 'workflow_run' || github.event.workflow_run.conclusion == 'success'
if: ${{ !github.event.repository.fork && (github.event_name != 'workflow_run' || github.event.workflow_run.conclusion == 'success') }}
runs-on: ubuntu-latest
environment:
name: github-pages
+1 -1
View File
@@ -36,4 +36,4 @@ jobs:
node --check demo/web/output-directory.mjs
node --check demo/web/worker.mjs
- name: Test browser converter helpers
run: node --test demo/web/tests/batch.test.mjs demo/web/tests/output-directory.test.mjs
run: node --test demo/web/tests/app.test.mjs demo/web/tests/batch.test.mjs demo/web/tests/output-directory.test.mjs
+46 -10
View File
@@ -6,13 +6,12 @@ endif()
project(Relinker CXX)
set(ANYPS5_TEST_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/tests")
set(ANYPS5_WINDOWS_TEST_EXIT "${CMAKE_CURRENT_SOURCE_DIR}/cmake/WindowsTestExit.cpp")
function(add_test_executable target)
add_executable(${target} ${ARGN})
set_target_properties(${target} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "$<1:${ANYPS5_TEST_OUTPUT_DIRECTORY}>")
if(MINGW)
target_sources(${target} PRIVATE "${ANYPS5_WINDOWS_TEST_EXIT}")
target_sources(${target} PRIVATE $<TARGET_OBJECTS:anyps5_windows_test_exit>)
target_link_options(${target} PRIVATE LINKER:--wrap=main)
endif()
endfunction()
@@ -96,6 +95,10 @@ if(APPLE AND NOT ANYPS5_RELINKER_ONLY)
add_link_options(-fexperimental-library)
endif()
if(MINGW)
add_library(anyps5_windows_test_exit OBJECT cmake/WindowsTestExit.cpp)
endif()
if(BUILD_TESTING)
add_test_executable(shader_cache_file_read_tests
${CMAKE_CURRENT_SOURCE_DIR}/core/shader/recompiler/tests/CacheFileReadTests.cpp)
@@ -133,10 +136,17 @@ if(BUILD_TESTING)
${CMAKE_CURRENT_SOURCE_DIR}/core/shader/recompiler/tests/test_shader_cache_native_paths.py
${CMAKE_CURRENT_SOURCE_DIR}/core/shader/recompiler/ShaderCacheVersion.cmake ${CMAKE_COMMAND})
set_tests_properties(shader_cache_native_paths PROPERTIES TIMEOUT 20)
add_test(NAME shader_cache_runtime_sources COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/tools/tests/test_shader_cache_runtime_sources.py
${CMAKE_CURRENT_SOURCE_DIR}/core/shader/recompiler ${CMAKE_COMMAND})
set_tests_properties(shader_cache_runtime_sources PROPERTIES TIMEOUT 180)
foreach(shard RANGE 0 6)
if(shard EQUAL 0)
set(shader_cache_sources_test_name shader_cache_runtime_sources)
else()
set(shader_cache_sources_test_name shader_cache_runtime_sources_shard_${shard})
endif()
add_test(NAME ${shader_cache_sources_test_name} COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/tools/tests/test_shader_cache_runtime_sources.py
${CMAKE_CURRENT_SOURCE_DIR}/core/shader/recompiler ${CMAKE_COMMAND} --shard ${shard}/7)
set_tests_properties(${shader_cache_sources_test_name} PROPERTIES TIMEOUT 180)
endforeach()
add_test(NAME pr_overlap_encoding COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/tools/tests/test_pr_overlap_encoding.py)
add_test(NAME pr_overlap_dependencies COMMAND ${Python3_EXECUTABLE}
@@ -161,6 +171,8 @@ if(BUILD_TESTING)
${CMAKE_CURRENT_SOURCE_DIR}/tools/tests/test_import_audit.py)
add_test(NAME unwrap_self COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/tools/tests/test_unwrap_self.py)
add_test(NAME audit_dump COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/tools/tests/test_audit_dump.py)
foreach(shard RANGE 0 11)
if(shard EQUAL 0)
set(conventions_test_name conventions_function_bodies)
@@ -238,27 +250,51 @@ if(BUILD_TESTING)
add_test(NAME pad_timeline COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/tools/tests/test_pad_timeline.py)
set_tests_properties(pad_timeline PROPERTIES TIMEOUT 20)
set(check_compile_modes_source "${CMAKE_CURRENT_SOURCE_DIR}/tools/tests/test_check_compile_modes.py")
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${check_compile_modes_source}")
file(STRINGS "${check_compile_modes_source}" check_compile_modes_cases REGEX "^ def test_")
foreach(test_case IN LISTS check_compile_modes_cases)
string(REGEX REPLACE "^ def (test_[A-Za-z0-9_]+).*" "\\1" test_case "${test_case}")
add_test(NAME check_compile_modes_${test_case} COMMAND ${Python3_EXECUTABLE}
"${CMAKE_CURRENT_SOURCE_DIR}/tools/tests/test_check_compile_modes.py" CheckCompileModeTests.${test_case})
set_tests_properties(check_compile_modes_${test_case} PROPERTIES TIMEOUT 30)
endforeach()
add_test(NAME trace_digest COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/tools/tests/test_trace_digest.py)
add_test(NAME symbolize_samples COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/tools/tests/test_symbolize_samples.py)
add_test(NAME conventions_bom_shebang COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/tools/tests/test_check_conventions_bom_shebang.py)
set_tests_properties(conventions_bom_shebang PROPERTIES TIMEOUT 20)
add_test(NAME env_reference COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/tools/tests/test_env_reference.py)
set_tests_properties(env_reference PROPERTIES TIMEOUT 20)
add_test(NAME conventions_env_reference COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/tools/tests/test_check_conventions_env_reference.py)
set_tests_properties(conventions_env_reference PROPERTIES TIMEOUT 60)
set(env_reference_test_source "${CMAKE_CURRENT_SOURCE_DIR}/tools/tests/test_check_conventions_env_reference.py")
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${env_reference_test_source}")
file(STRINGS "${env_reference_test_source}" env_reference_cases REGEX "^ def test_")
foreach(test_case IN LISTS env_reference_cases)
string(REGEX REPLACE "^ def (test_[A-Za-z0-9_]+).*" "\\1" test_case "${test_case}")
add_test(NAME conventions_env_reference_${test_case} COMMAND ${Python3_EXECUTABLE}
"${CMAKE_CURRENT_SOURCE_DIR}/tools/tests/test_check_conventions_env_reference.py" "EnvReferenceDiffTests.${test_case}")
set_tests_properties(conventions_env_reference_${test_case} PROPERTIES TIMEOUT 30)
endforeach()
add_test(NAME conventions_body_bom COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/tools/tests/test_check_conventions_body_bom.py)
add_test(NAME conventions_blob_reads COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/tools/tests/test_check_conventions_blob_reads.py)
add_test(NAME conventions_agent_files COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/tools/tests/test_check_conventions_agent_files.py)
add_test(NAME conventions_alignment COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/tools/tests/test_check_conventions_alignment.py)
add_test(NAME conventions_pr_template COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/tools/tests/test_check_conventions_pr_template.py)
add_test(NAME check_compile_preprocessor COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/tools/tests/test_check_compile_preprocessor.py)
set_tests_properties(check_compile_preprocessor PROPERTIES TIMEOUT 20)
add_test(NAME check_compile COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/tools/tests/test_check_compile.py)
${CMAKE_CURRENT_SOURCE_DIR}/tools/tests/test_check_compile.py CheckCompileTests)
add_test(NAME check_compile_outputs COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/tools/tests/test_check_compile.py CheckCompileOutputTests)
add_test(NAME performance_compare COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/tools/tests/test_performance_compare.py)
set_tests_properties(performance_compare PROPERTIES TIMEOUT 20)
+2
View File
@@ -3,6 +3,7 @@
## Code
- Follow the [coding conventions](docs/dev/CONVENTIONS.md): naming, no comments except [technical debt](docs/dev/TechnicalDebt.md), Conventional Commits.
- **Every exported PS5 function with `APS5_VABI` must have exactly one declaration in a header reachable through the library's `<library>.hpp`. Duplicate declarations in headers, `.cpp` files and tests are forbidden. This is checked by the Python test [`public_headers`](core/libs/tests/test_public_headers.py).**
- Every function either does exactly what it is supposed to or throws. Unimplemented exports call `NotImplemented_nid_no_patch(__func__)` (see [libSceAudioIn](core/libs/prx/libSceAudioIn/Export.cpp)).
- Pull requests that add or change shader instruction semantics say where they come from: measured on hardware (which GPU and what was checked, e.g. with the [hardware oracle](docs/dev/HW_ORACLE.md)) or the exact source (ISA section, LLVM, ACO or Mesa file). Reviewers check the semantics on hardware. Cases the source doesn't settle throw, and behaviour not verified on hardware is recorded in [technical debt](docs/dev/TechnicalDebt.md).
- An export's signature or behaviour taken from a title's call sites or from a library's disassembly follows [reading titles and libraries](docs/dev/REVERSE_ENGINEERING.md): a call site settles the arguments, not what the library does.
@@ -11,6 +12,7 @@
- Implement the general behaviour of a function, not what one title happens to need.
- Don't add replacements for modules that titles ship themselves in `sce_module/`, `sce_modules/` or `prx/`: engine or middleware modules (Cohtml, FMOD, GOG Galaxy) and SDK libraries that only wrap other system libraries. The relinker converts and loads the title's own module, and a host library with the same name is left out of `DT_NEEDED`. Only system libraries, which reach the kernel or the hardware, are reimplemented in [core/libs/prx](core/libs/prx). A host copy may still exist for a wrapped library that some dumps lack entirely, like libSceNpCppWebApi: the title's own module wins whenever one is shipped, so it is only ever consulted by titles that ship nothing.
- Avoid non-standard extensions (`__attribute__`, etc.) where standard C++ is enough. Helper symbols that must not become NIDs use the `_nid_no_patch` or `_nid_no_patch_cut` suffix.
- `alignas` takes at most 8192: MinGW GCC, the Windows compiler, rejects static objects aligned above it. A larger block is aligned at run time inside a bigger buffer.
- The relinker uses only the C++20 standard library.
- Third-party code is added as a submodule under `3rdparty/` and built from source, not found on the system. With `ANYPS5_USE_SYSTEM_DEPS=ON` the packages packaged by the distributions (SDL2, freetype, Vulkan-Headers, SPIRV-Headers, glslang and SPIRV-Tools) come from the system instead; that resolution lives in [cmake/SystemDeps.cmake](cmake/SystemDeps.cmake) and the default build never reads the system.
- Don't add tests that only check that a symbol is exported: a missing export already fails at startup.
+1 -1
View File
@@ -28,7 +28,7 @@ The [shader recompiler](core/shader/recompiler/Recompiler.cpp) successfully prod
## Compatibility
Per-game progress (stage reached, open blockers, libraries and GPU coverage) is tracked on the [blocker map](https://alxcbtw.github.io/anyps5-roadmap/), a community project built from public GitHub data ([source](https://github.com/AlxcBTW/anyps5-roadmap)). Test reports go in the [Game bring-up](https://github.com/boykopovar/AnyPS5/discussions/categories/game-bring-up) discussions, one thread per title.
Per-game status and test reports live in the [Game bring-up](https://github.com/boykopovar/AnyPS5/discussions/categories/game-bring-up) discussions, one thread per title.
## Input mapping
+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());
+207 -2
View File
@@ -269,6 +269,15 @@ if(BUILD_TESTING)
set_tests_properties(guest_kernel_sys_sdk_version PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libkernel_sys>")
endif()
add_test_executable(guest_kernel_sys_console_identity_tests tests/GuestKernelSysConsoleIdentity.cpp)
target_include_directories(guest_kernel_sys_console_identity_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(guest_kernel_sys_console_identity_tests PRIVATE libkernel_sys libkernel libc)
configure_windows_unwind(guest_kernel_sys_console_identity_tests)
add_test(NAME guest_kernel_sys_console_identity COMMAND guest_kernel_sys_console_identity_tests)
if(WIN32)
set_tests_properties(guest_kernel_sys_console_identity 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)
@@ -278,6 +287,32 @@ if(BUILD_TESTING)
add_test(NAME nid_buffer_validation COMMAND nid_buffer_validation_tests)
set_tests_properties(nid_buffer_validation PROPERTIES TIMEOUT 10)
add_test_executable(guest_dinkumware_fetch_tests ${CMAKE_CURRENT_SOURCE_DIR}/tests/GuestDinkumwareFetch.cpp
${CMAKE_CURRENT_SOURCE_DIR}/nid/src/NidCompute.cpp ${CMAKE_CURRENT_SOURCE_DIR}/nid/src/Sha1.cpp)
target_include_directories(guest_dinkumware_fetch_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/nid/include)
target_link_libraries(guest_dinkumware_fetch_tests PRIVATE ${CMAKE_DL_LIBS})
configure_windows_unwind(guest_dinkumware_fetch_tests)
if(MINGW)
target_link_libraries(guest_dinkumware_fetch_tests PRIVATE libc)
endif()
add_custom_target(guest_dinkumware_fetch_libraries DEPENDS ${LIBC_PATCHED})
add_dependencies(guest_dinkumware_fetch_tests guest_dinkumware_fetch_libraries)
foreach(operation IN ITEMS and or xor)
foreach(width IN ITEMS 1 2 4)
add_test(NAME guest_dinkumware_fetch_${operation}_${width} COMMAND guest_dinkumware_fetch_tests
$<TARGET_FILE_DIR:libc> unpatched ${operation} ${width})
add_test(NAME guest_dinkumware_fetch_${operation}_${width}_patched COMMAND guest_dinkumware_fetch_tests
${PATCHED_DIR} patched ${operation} ${width})
set_tests_properties(guest_dinkumware_fetch_${operation}_${width}
guest_dinkumware_fetch_${operation}_${width}_patched PROPERTIES TIMEOUT 20)
if(WIN32)
set_tests_properties(guest_dinkumware_fetch_${operation}_${width} PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
set_tests_properties(guest_dinkumware_fetch_${operation}_${width}_patched PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:${PATCHED_DIR}")
endif()
endforeach()
endforeach()
add_test_executable(pe_import_first_tests allocator/tests/PeImportFirstTests.cpp allocator/PeImportFirst.cpp)
target_include_directories(pe_import_first_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_compile_features(pe_import_first_tests PRIVATE cxx_std_20)
@@ -289,6 +324,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}
@@ -347,14 +401,52 @@ if(BUILD_TESTING)
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 guest_kernel_sys_barrier guest_kernel_sys_pthread 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})
@@ -369,6 +461,18 @@ if(BUILD_TESTING)
add_subdirectory(tests/write_watch)
add_subdirectory(tests/guest_arena)
endif()
add_test_executable(guest_kernel_sys_pthread_np_tests tests/GuestKernelSysPthreadNp.cpp)
target_include_directories(guest_kernel_sys_pthread_np_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(guest_kernel_sys_pthread_np_tests PRIVATE libkernel_sys libkernel libc)
configure_windows_unwind(guest_kernel_sys_pthread_np_tests)
foreach(pthreadNpMode IN ITEMS rwlockattr kind)
add_test(NAME guest_kernel_sys_pthread_np_${pthreadNpMode} COMMAND guest_kernel_sys_pthread_np_tests ${pthreadNpMode})
set_tests_properties(guest_kernel_sys_pthread_np_${pthreadNpMode} PROPERTIES TIMEOUT 20)
if(WIN32)
set_tests_properties(guest_kernel_sys_pthread_np_${pthreadNpMode} PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libkernel_sys>")
endif()
endforeach()
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)
@@ -382,12 +486,29 @@ if(BUILD_TESTING)
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)
@@ -407,6 +528,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)
@@ -460,6 +591,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)
@@ -470,6 +612,16 @@ 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)
@@ -483,6 +635,17 @@ if(BUILD_TESTING)
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)
@@ -717,6 +880,16 @@ if(BUILD_TESTING)
"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)
@@ -799,6 +972,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)
@@ -819,6 +1004,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)
@@ -829,6 +1024,16 @@ 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_compile_definitions(guest_vorbis_dec_tests PRIVATE OV_EXCLUDE_STATIC_CALLBACKS)
+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);
+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()
+9 -2
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,7 +271,13 @@ def check_refusals(patcher, directory):
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)
with tempfile.TemporaryDirectory(prefix="anyps5-macho-nid-a\u00e7\u00e3o-\u65e5\u672c-") as directory:
check_shrinking(patcher, directory)
check_growing(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>
Loaded 100 of 2627 files, more files were not shown because too many files have changed in this diff. Show more