mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-11-07 13:39:55 +00:00
138 lines
5.5 KiB
C++
138 lines
5.5 KiB
C++
#include "prx/libScePngEnc/include/Api.hpp"
|
|
#include "Decoder/Png.hpp"
|
|
|
|
#include <algorithm>
|
|
#include <array>
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <memory>
|
|
#include <source_location>
|
|
#include <span>
|
|
#include <string>
|
|
#include <vector>
|
|
#ifdef _WIN32
|
|
#include <windows.h>
|
|
#else
|
|
#include <sys/mman.h>
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
static void Require(bool value, std::source_location location = std::source_location::current()) {
|
|
if (!value) {
|
|
std::fprintf(stderr, "boundary check failed at line %u\n", location.line());
|
|
std::abort();
|
|
}
|
|
}
|
|
|
|
class Guarded {
|
|
unsigned char* memory;
|
|
std::size_t page;
|
|
std::size_t writable;
|
|
public:
|
|
explicit Guarded(std::size_t capacity = 1) {
|
|
#ifdef _WIN32
|
|
SYSTEM_INFO info{};
|
|
GetSystemInfo(&info);
|
|
page = info.dwPageSize;
|
|
#else
|
|
const long size = sysconf(_SC_PAGESIZE);
|
|
Require(size > 0);
|
|
page = static_cast<std::size_t>(size);
|
|
#endif
|
|
writable = ((capacity + 128 + page - 1) / page) * page;
|
|
#ifdef _WIN32
|
|
memory = static_cast<unsigned char*>(VirtualAlloc(nullptr, writable + page * 2, MEM_RESERVE | MEM_COMMIT, PAGE_NOACCESS));
|
|
Require(memory != nullptr);
|
|
#else
|
|
memory = static_cast<unsigned char*>(mmap(nullptr, writable + page * 2, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
|
|
Require(memory != MAP_FAILED);
|
|
#endif
|
|
Reset();
|
|
}
|
|
~Guarded() {
|
|
#ifdef _WIN32
|
|
Require(VirtualFree(memory, 0, MEM_RELEASE) != 0);
|
|
#else
|
|
Require(munmap(memory, writable + page * 2) == 0);
|
|
#endif
|
|
}
|
|
Guarded(const Guarded&) = delete;
|
|
Guarded& operator=(const Guarded&) = delete;
|
|
void Protect(bool readOnly) {
|
|
#ifdef _WIN32
|
|
DWORD previous;
|
|
Require(VirtualProtect(memory + page, writable, readOnly ? PAGE_READONLY : PAGE_READWRITE, &previous) != 0);
|
|
#else
|
|
Require(mprotect(memory + page, writable, readOnly ? PROT_READ : PROT_READ | PROT_WRITE) == 0);
|
|
#endif
|
|
}
|
|
void Reset() {
|
|
Protect(false);
|
|
std::memset(memory + page, 0xa5, writable);
|
|
}
|
|
unsigned char* End(std::size_t count) {
|
|
Require(count + 64 <= writable);
|
|
return memory + page + writable - (std::max)(count, std::size_t{1});
|
|
}
|
|
void Prefix(const void* address) const {
|
|
const auto* bytes = static_cast<const unsigned char*>(address);
|
|
for (int index = 1; index <= 32; ++index) Require(bytes[-index] == 0xa5);
|
|
}
|
|
};
|
|
|
|
int main() {
|
|
PngEncCreateParam create{sizeof(create), 0, 32, 4};
|
|
const int required = scePngEncQueryMemorySize(&create);
|
|
Require(required > 0);
|
|
std::vector<std::max_align_t> work((required + sizeof(std::max_align_t) - 1) / sizeof(std::max_align_t));
|
|
void* handle = nullptr;
|
|
Require(scePngEncCreate(&create, work.data(), required, &handle) == 0);
|
|
std::size_t cases = 0;
|
|
for (unsigned width : {1,5,17}) for (unsigned height : {1,2,7}) for (unsigned padding : {0,4,28}) {
|
|
const unsigned pitch = width * 4 + padding;
|
|
const unsigned span = pitch * height;
|
|
Guarded input(span);
|
|
auto* pixels = input.End(span);
|
|
for (unsigned i = 0; i < span; ++i) pixels[i] = static_cast<unsigned char>(i * 53 + i / pitch);
|
|
const std::vector<unsigned char> saved(pixels, pixels + span);
|
|
input.Protect(true);
|
|
for (unsigned format : {0,1}) for (unsigned space : {3,19}) {
|
|
Guarded output(8192);
|
|
auto* png = output.End(8192);
|
|
PngEncEncodeParam p{pixels, png, span, 8192, width, height, pitch, static_cast<std::uint16_t>(format),
|
|
static_cast<std::uint16_t>(space), 8, 0, 15, 6};
|
|
PngEncOutputInfo info{};
|
|
const int size = scePngEncEncode(handle, &p, &info);
|
|
Require(size > 0 && size <= 8192 && info.data_size == static_cast<unsigned>(size) && info.processed_height == height);
|
|
const auto image = Decoder::Png::Decode({png, static_cast<std::size_t>(size)});
|
|
Require(image && image->width == width && image->height == height && image->pixels.size() == width * height * 4);
|
|
for (unsigned y = 0; y < height; ++y) for (unsigned x = 0; x < width; ++x) {
|
|
const auto* source = pixels + y * pitch + x * 4;
|
|
const auto* result = image->pixels.data() + (y * width + x) * 4;
|
|
Require(result[0] == source[format == 1 ? 2 : 0] && result[1] == source[1]);
|
|
Require(result[2] == source[format == 1 ? 0 : 2] && result[3] == (space == 19 ? source[3] : 255));
|
|
}
|
|
Require(std::equal(saved.begin(), saved.end(), pixels));
|
|
input.Prefix(pixels); output.Prefix(png);
|
|
++cases;
|
|
if (padding != 0) {
|
|
const unsigned shortSpan = pitch * (height - 1) + width * 4;
|
|
Guarded shorter(shortSpan);
|
|
auto* shortPixels = shorter.End(shortSpan);
|
|
std::copy(saved.begin(), saved.begin() + shortSpan, shortPixels);
|
|
shorter.Protect(true);
|
|
p.image_mem_addr = shortPixels; p.image_mem_size = shortSpan;
|
|
Require(scePngEncEncode(handle, &p, &info) == static_cast<int>(0x80690103));
|
|
Require(std::equal(saved.begin(), saved.begin() + shortSpan, shortPixels));
|
|
shorter.Prefix(shortPixels); output.Prefix(png);
|
|
++cases;
|
|
}
|
|
}
|
|
}
|
|
Require(scePngEncDelete(handle) == 0);
|
|
std::printf("%zu guarded PNG image extents and short-row rejections passed\n", cases);
|
|
}
|