#include "SceTypes.hpp" #include "prx/libSceJpegDec/libSceJpegDec.hpp" #include "prx/libc/include/general/VabiMacros.hpp" #include "Decoder/Jpeg.hpp" #include #include #include #include #include #include #include namespace { void Require(bool value) { if (!value) std::abort(); } struct Source { std::vector bytes; std::size_t chunkSize = 0; std::size_t offset = 0; std::size_t calls = 0; std::int32_t failure = 0; void* deleteHandle = nullptr; }; std::int32_t APS5_VABI ReadChunk(JpegDecInputChunk* chunk, void* argument) { auto* source = static_cast(argument); ++source->calls; Require(chunk->data == nullptr && chunk->size == 0); if (void* handle = std::exchange(source->deleteHandle, nullptr)) Require(sceJpegDecDelete(handle) == 0); if (source->failure != 0) return source->failure; if (source->offset == source->bytes.size()) return 0; const std::size_t count = std::min(source->chunkSize, source->bytes.size() - source->offset); chunk->data = source->bytes.data() + source->offset; chunk->size = static_cast(count); source->offset += count; return 7; } std::vector TestJpeg(std::uint32_t width, std::uint32_t height, std::uint32_t channels) { std::vector pixels(static_cast(width) * height * channels); for (std::size_t i = 0; i < pixels.size(); ++i) pixels[i] = static_cast((i * 7) ^ (i >> 5)); return Decoder::Jpeg::Encode(pixels, width, height, channels, 90); } JpegDecInputControlParam StreamParam(std::uint16_t operation, Source& source) { JpegDecInputControlParam param{}; param.input = ReadChunk; param.input_argument = &source; param.operation = operation; param.down_scale = 1; param.alpha_value = 0x80; return param; } struct JpegHandle { std::vector memory; void* handle = nullptr; explicit JpegHandle(std::uint32_t maxWidth) { const JpegDecCreateParam create{sizeof(JpegDecCreateParam), 2, maxWidth}; memory.resize(static_cast(sceJpegDecQueryMemorySize(&create)) + 64); Require(sceJpegDecCreate(&create, memory.data(), static_cast(memory.size()), &handle) == 0); } }; std::vector Reference(const std::vector& jpeg, std::uint32_t width, std::uint32_t height, std::uint16_t downScale, std::int32_t* result) { JpegHandle decoder(width); std::vector image(static_cast(width) * height * 4, 0xCD); JpegDecDecodeParam param{}; param.jpeg_mem_addr = jpeg.data(); param.jpeg_mem_size = static_cast(jpeg.size()); param.image_mem_addr = image.data(); param.image_mem_size = static_cast(image.size()); param.down_scale = downScale; param.alpha_value = 0x80; *result = sceJpegDecDecode(decoder.handle, ¶m, nullptr); return image; } void RequireStreamMatches(const std::vector& jpeg, std::uint32_t width, std::uint32_t height, std::uint16_t downScale, std::size_t chunkSize) { std::int32_t expected = 0; const std::vector reference = Reference(jpeg, width, height, downScale, &expected); Require(expected > 0); JpegHandle decoder(width); Source header{jpeg, chunkSize}; JpegDecInputControlParam parse = StreamParam(0x10, header); parse.down_scale = downScale; JpegDecImageInfo info{}, expectedInfo{}; Require(sceJpegDecDecodeWithInputControl(decoder.handle, &parse, &info) == 0); const JpegDecParseParam parseHeader{jpeg.data(), static_cast(jpeg.size()), 0, downScale}; Require(sceJpegDecParseHeader(&parseHeader, &expectedInfo) == 0); Require(std::equal(reinterpret_cast(&info), reinterpret_cast(&info + 1), reinterpret_cast(&expectedInfo))); Source rest{std::vector(jpeg.begin() + static_cast(header.offset), jpeg.end()), chunkSize}; std::vector image(reference.size(), 0xCD); JpegDecInputControlParam decode = StreamParam(0x20, rest); decode.down_scale = downScale; decode.image_mem_addr = image.data(); decode.image_mem_size = static_cast(image.size()); JpegDecImageInfo untouched{}; untouched.image_width = 0x1234; Require(sceJpegDecDecodeWithInputControl(decoder.handle, &decode, &untouched) == expected); Require(untouched.image_width == 0x1234); Require(image == reference); Require(sceJpegDecDelete(decoder.handle) == 0); } void RequireStreamOutlivesHandle(const std::vector& jpeg, std::uint32_t width, std::uint32_t height) { constexpr std::int32_t invalidHandle = static_cast(0x80650004); std::int32_t expected = 0; const std::vector reference = Reference(jpeg, width, height, 1, &expected); JpegHandle parsing(width); Source deletedInParse{jpeg, 64}; deletedInParse.deleteHandle = parsing.handle; JpegDecInputControlParam parse = StreamParam(0x10, deletedInParse); JpegDecImageInfo info{}; Require(sceJpegDecDecodeWithInputControl(parsing.handle, &parse, &info) == 0); Require(info.image_width == width && info.image_height == height); std::vector image(reference.size(), 0xCD); JpegDecInputControlParam decode = StreamParam(0x20, deletedInParse); decode.image_mem_addr = image.data(); decode.image_mem_size = static_cast(image.size()); Require(sceJpegDecDecodeWithInputControl(parsing.handle, &decode, nullptr) == invalidHandle); JpegHandle decoding(width); Source deletedInDecode{jpeg, 64}; parse.input_argument = &deletedInDecode; Require(sceJpegDecDecodeWithInputControl(decoding.handle, &parse, &info) == 0); deletedInDecode.deleteHandle = decoding.handle; decode.input_argument = &deletedInDecode; Require(sceJpegDecDecodeWithInputControl(decoding.handle, &decode, nullptr) == expected); Require(image == reference); Require(sceJpegDecDecodeWithInputControl(decoding.handle, &decode, nullptr) == invalidHandle); JpegHandle completed(width); Source outer{jpeg, 64}; parse.input_argument = &outer; Require(sceJpegDecDecodeWithInputControl(completed.handle, &parse, &info) == 0); std::fill(image.begin(), image.end(), 0xCD); decode.input_argument = &outer; Require(sceJpegDecDecodeWithInputControl(completed.handle, &decode, nullptr) == expected); Require(image == reference); Require(sceJpegDecDelete(completed.handle) == 0); } } // namespace int main() { constexpr std::int32_t invalidAddr = static_cast(0x80650001); constexpr std::int32_t invalidSize = static_cast(0x80650002); constexpr std::int32_t invalidParam = static_cast(0x80650003); constexpr std::int32_t invalidHandle = static_cast(0x80650004); constexpr std::int32_t invalidData = static_cast(0x80650010); constexpr std::int32_t decodeError = static_cast(0x80650012); const std::vector jpeg = TestJpeg(96, 64, 3); Require(jpeg.size() > 2 * 0x800); JpegHandle decoder(96); Source source{jpeg, 100}; JpegDecImageInfo info{}; JpegDecInputControlParam parse = StreamParam(0x10, source); Require(sceJpegDecDecodeWithInputControl(nullptr, &parse, &info) == invalidHandle); Require(sceJpegDecDecodeWithInputControl(static_cast(decoder.handle) + 1, &parse, &info) == invalidHandle); std::array notHandle{}; Require(sceJpegDecDecodeWithInputControl(notHandle.data(), &parse, &info) == invalidHandle); Require(sceJpegDecDecodeWithInputControl(decoder.handle, nullptr, &info) == invalidAddr); JpegDecInputControlParam noInput = parse; noInput.input = nullptr; noInput.operation = 0x30; Require(sceJpegDecDecodeWithInputControl(decoder.handle, &noInput, &info) == invalidAddr); for (std::uint16_t operation : {0x00, 0x01, 0x12, 0x22, 0x30, 0x31}) { JpegDecInputControlParam unknown = parse; unknown.operation = operation; Require(sceJpegDecDecodeWithInputControl(decoder.handle, &unknown, nullptr) == invalidParam); } JpegDecInputControlParam reserved = parse; reserved.reserved = 1; Require(sceJpegDecDecodeWithInputControl(decoder.handle, &reserved, nullptr) == invalidParam); Require(sceJpegDecDecodeWithInputControl(decoder.handle, &parse, nullptr) == invalidAddr); Require(source.calls == 0); Require(sceJpegDecDecodeWithInputControl(decoder.handle, &parse, &info) == 0); Require(info.image_width == 96 && info.image_height == 64 && info.output_image_width == 96); Require(source.offset < jpeg.size() && source.offset <= 0x800 + source.chunkSize); const std::size_t parseCalls = source.calls; std::vector image(96 * 64 * 4); JpegDecInputControlParam decode = StreamParam(0x20, source); decode.image_mem_addr = image.data(); decode.image_mem_size = static_cast(image.size()); JpegDecInputControlParam bad = decode; bad.image_mem_addr = nullptr; Require(sceJpegDecDecodeWithInputControl(decoder.handle, &bad, nullptr) == invalidAddr); bad = decode; bad.image_mem_addr = image.data() + 1; Require(sceJpegDecDecodeWithInputControl(decoder.handle, &bad, nullptr) == invalidAddr); bad.pixel_format = 11; bad.image_mem_size = 0; Require(sceJpegDecDecodeWithInputControl(decoder.handle, &bad, nullptr) == invalidSize); bad = decode; bad.image_mem_size = 0; Require(sceJpegDecDecodeWithInputControl(decoder.handle, &bad, nullptr) == invalidSize); bad = decode; bad.down_scale = 3; Require(sceJpegDecDecodeWithInputControl(decoder.handle, &bad, nullptr) == invalidParam); bad = decode; bad.pixel_format = 2; Require(sceJpegDecDecodeWithInputControl(decoder.handle, &bad, nullptr) == invalidParam); bad = decode; bad.alpha_value = 256; Require(sceJpegDecDecodeWithInputControl(decoder.handle, &bad, nullptr) == invalidParam); bad = decode; bad.image_mem_size = 96 * 64 * 4 - 1; Require(sceJpegDecDecodeWithInputControl(decoder.handle, &bad, nullptr) == invalidSize); Require(sceJpegDecDecodeWithInputControl(decoder.handle, &decode, nullptr) == (96 << 16 | 64)); Require(image[3] == 0x80); std::int32_t expected = 0; Require(image == Reference(jpeg, 96, 64, 1, &expected) && expected == (96 << 16 | 64)); Source failing{jpeg, 100}; failing.failure = static_cast(0x8000BEEF); JpegDecInputControlParam failingParse = StreamParam(0x10, failing); Require(sceJpegDecDecodeWithInputControl(decoder.handle, &failingParse, &info) == static_cast(0x8000BEEF)); Require(failing.calls == 1); Source garbage{std::vector(5000, 0x11), 1000}; JpegDecInputControlParam garbageParse = StreamParam(0x10, garbage); Require(sceJpegDecDecodeWithInputControl(decoder.handle, &garbageParse, &info) == decodeError); Require(garbage.calls == 3 && garbage.offset == 3000); std::vector imageEnd{0xFF, 0xD8, 0xFF, 0xD9}; imageEnd.resize(10000, 0x00); Source ended{imageEnd, 1000}; JpegDecInputControlParam endedParse = StreamParam(0x10, ended); Require(sceJpegDecDecodeWithInputControl(decoder.handle, &endedParse, &info) == decodeError); Require(ended.calls == 3); std::vector broken{0xFF, 0xD8, 0x12, 0x34}; broken.resize(10000, 0xFF); Source brokenSource{broken, 1000}; JpegDecInputControlParam brokenParse = StreamParam(0x10, brokenSource); Require(sceJpegDecDecodeWithInputControl(decoder.handle, &brokenParse, &info) == decodeError); Require(brokenSource.calls == 3); Source again{jpeg, 512}; JpegDecInputControlParam againParse = StreamParam(0x10, again); Require(sceJpegDecDecodeWithInputControl(decoder.handle, &againParse, &info) == 0); again.failure = -2; JpegDecInputControlParam againDecode = decode; againDecode.input_argument = &again; Require(sceJpegDecDecodeWithInputControl(decoder.handle, &againDecode, nullptr) == -2); std::vector withApp(jpeg.begin(), jpeg.begin() + 2); const std::array app{0xFF, 0xE1, 0x00, 0x0A, 0xFF, 0xD8, 0x00, 0xFF, 0xD9, 0x00, 0xFF, 0xD9}; withApp.insert(withApp.end(), app.begin(), app.end()); withApp.insert(withApp.end(), jpeg.begin() + 2, jpeg.end()); const std::size_t realEnd = withApp.size(); withApp.resize(realEnd + 0x10000, 0x5A); Source appSource{withApp, 300}; JpegDecInputControlParam appParse = StreamParam(0x10, appSource); Require(sceJpegDecDecodeWithInputControl(decoder.handle, &appParse, &info) == 0); JpegDecInputControlParam appDecode = decode; appDecode.input_argument = &appSource; std::fill(image.begin(), image.end(), 0); Require(sceJpegDecDecodeWithInputControl(decoder.handle, &appDecode, nullptr) == (96 << 16 | 64)); Require(appSource.offset >= realEnd && appSource.offset <= realEnd + 0x800 + appSource.chunkSize); Require(image == Reference(jpeg, 96, 64, 1, &expected)); Require(sceJpegDecDelete(decoder.handle) == 0); Require(sceJpegDecDecodeWithInputControl(decoder.handle, &decode, nullptr) == invalidHandle); RequireStreamOutlivesHandle(jpeg, 96, 64); RequireStreamMatches(TestJpeg(40, 24, 1), 40, 24, 1, 1); RequireStreamMatches(TestJpeg(64, 48, 3), 64, 48, 2, 37); RequireStreamMatches(TestJpeg(64, 48, 3), 64, 48, 8, 0x10000); }