#include "prx/libSceVorbisDec/include/Api.hpp" #include "prx/libSceVorbisDec/include/GuestVorbisFile.hpp" #include "prx/libc/include/general/VabiMacros.hpp" #include #include #include #include #include #include #include #include #include #include #include #include using namespace VorbisDec; namespace { constexpr int FirstSerial = 0x1234abcd; constexpr int SecondSerial = -0x61c88647; constexpr int FirstChannels = 3; constexpr int SecondChannels = 2; constexpr long FirstRate = 44100; constexpr long SecondRate = 22050; constexpr long FirstSamples = 44100; constexpr long SecondSamples = 11025; constexpr std::int64_t DecodedBytes = (FirstSamples * FirstChannels + SecondSamples * SecondChannels) * 2; constexpr unsigned char Pattern = 0xA5; void Check(bool value, int line) { if (value) return; std::fprintf(stderr, "VorbisDec check failed at line %d\n", line); std::abort(); } #define Require(value) Check((value), __LINE__) template bool Throws(TCall call) { try { call(); } catch (const TException&) { return true; } return false; } struct Source { const std::vector* bytes = nullptr; std::size_t position = 0; int closes = 0; void* probe = nullptr; std::int64_t probed = -1; bool throwOnRead = false; bool throwOnClose = false; }; std::size_t ReadBytes(Source& source, void* buffer, std::size_t size, std::size_t count) { if (source.throwOnRead) throw std::runtime_error("read callback"); if (source.probe) source.probed = sceVorbisDecStreams(source.probe); if (size == 0) return 0; const std::size_t items = std::min(count, (source.bytes->size() - source.position) / size); if (items != 0) std::memcpy(buffer, source.bytes->data() + source.position, items * size); source.position += items * size; return items; } int SeekBytes(Source& source, std::int64_t offset, int whence) { const auto size = static_cast(source.bytes->size()); const std::int64_t base = whence == SEEK_SET ? 0 : whence == SEEK_CUR ? static_cast(source.position) : size; if (offset < -base || offset > size - base) return -1; source.position = static_cast(base + offset); return 0; } std::size_t APS5_VABI GuestRead(void* buffer, std::size_t size, std::size_t count, void* source) { return ReadBytes(*static_cast(source), buffer, size, count); } int APS5_VABI GuestSeek(void* source, std::int64_t offset, int whence) { return SeekBytes(*static_cast(source), offset, whence); } int APS5_VABI GuestClose(void* source) { auto& closing = *static_cast(source); ++closing.closes; if (closing.throwOnClose) throw std::runtime_error("close callback"); return 0; } std::int64_t APS5_VABI GuestTell(void* source) { return static_cast(static_cast(source)->position); } std::size_t HostRead(void* buffer, std::size_t size, std::size_t count, void* source) { return ReadBytes(*static_cast(source), buffer, size, count); } int HostSeek(void* source, ogg_int64_t offset, int whence) { return SeekBytes(*static_cast(source), offset, whence); } int HostClose(void* source) { ++static_cast(source)->closes; return 0; } long HostTell(void* source) { return static_cast(static_cast(source)->position); } constexpr GuestCallbacks SeekableCallbacks{GuestRead, GuestSeek, GuestClose, GuestTell}; constexpr GuestCallbacks StreamingCallbacks{GuestRead, nullptr, GuestClose, nullptr}; const ov_callbacks HostSeekableCallbacks{HostRead, HostSeek, HostClose, HostTell}; const ov_callbacks HostStreamingCallbacks{HostRead, nullptr, HostClose, nullptr}; struct GuestFile { alignas(16) std::array bytes; GuestFile() { bytes.fill(Pattern); } void* Get() { return bytes.data(); } bool Untouched() const { return std::all_of(bytes.begin(), bytes.end(), [](unsigned char value) { return value == Pattern; }); } }; struct FilterLog { int calls = 0; std::int64_t firstChannels = 0; std::int64_t lastChannels = 0; std::int64_t samples = 0; }; void Halve(float** pcm, std::int64_t channels, std::int64_t samples) { for (std::int64_t channel = 0; channel < channels; ++channel) for (std::int64_t index = 0; index < samples; ++index) pcm[channel][index] *= 0.5f; } void APS5_VABI GuestHalve(float** pcm, std::int64_t channels, std::int64_t samples, void* param) { auto* log = static_cast(param); if (log->calls++ == 0) log->firstChannels = channels; log->lastChannels = channels; log->samples += samples; Halve(pcm, channels, samples); } void HostHalve(float** pcm, long channels, long samples, void*) { Halve(pcm, channels, samples); } void AppendPage(std::vector& out, const ogg_page& page) { out.insert(out.end(), page.header, page.header + page.header_len); out.insert(out.end(), page.body, page.body + page.body_len); } void EncodeLink(std::vector& out, int serial, int channels, long rate, long samples, const char* title) { vorbis_info info; vorbis_info_init(&info); Require(vorbis_encode_init_vbr(&info, channels, rate, 0.1f) == 0); vorbis_comment comment; vorbis_comment_init(&comment); vorbis_comment_add_tag(&comment, "TITLE", title); vorbis_comment_add_tag(&comment, "ARTIST", "AnyPS5"); vorbis_dsp_state dsp; Require(vorbis_analysis_init(&dsp, &info) == 0); vorbis_block block; vorbis_block_init(&dsp, &block); ogg_stream_state stream; ogg_stream_init(&stream, serial); ogg_packet identification; ogg_packet comments; ogg_packet codebooks; vorbis_analysis_headerout(&dsp, &comment, &identification, &comments, &codebooks); ogg_stream_packetin(&stream, &identification); ogg_stream_packetin(&stream, &comments); ogg_stream_packetin(&stream, &codebooks); ogg_page page; while (ogg_stream_flush(&stream, &page) != 0) AppendPage(out, page); for (long done = 0;;) { const long count = std::min(1024L, samples - done); if (count > 0) { float** buffer = vorbis_analysis_buffer(&dsp, static_cast(count)); for (int channel = 0; channel < channels; ++channel) { const double frequency = 220.0 * (channel + 1) + 37.0 * serial / 1e9; for (long index = 0; index < count; ++index) { buffer[channel][index] = static_cast(0.3 * std::sin(6.283185307179586 * frequency * static_cast(done + index) / static_cast(rate))); } } } vorbis_analysis_wrote(&dsp, static_cast(count)); done += count; while (vorbis_analysis_blockout(&dsp, &block) == 1) { vorbis_analysis(&block, nullptr); vorbis_bitrate_addblock(&block); ogg_packet packet; while (vorbis_bitrate_flushpacket(&dsp, &packet) == 1) { ogg_stream_packetin(&stream, &packet); while (ogg_stream_pageout_fill(&stream, &page, 1024) != 0) AppendPage(out, page); } } if (count == 0) break; } while (ogg_stream_flush(&stream, &page) != 0) AppendPage(out, page); ogg_stream_clear(&stream); vorbis_block_clear(&block); vorbis_dsp_clear(&dsp); vorbis_comment_clear(&comment); vorbis_info_clear(&info); } std::int64_t RequireSameRead(void* vf, OggVorbis_File& reference, int bigEndian = 0, int word = 2, int isSigned = 1) { std::array guest{}; std::array host{}; int guestStream = -1; int hostStream = -1; const std::int64_t got = sceVorbisDecRead(vf, guest.data(), static_cast(guest.size()), bigEndian, word, isSigned, &guestStream); const long expected = ov_read(&reference, host.data(), static_cast(host.size()), bigEndian, word, isSigned, &hostStream); Require(got == expected); Require(got <= 0 || (guestStream == hostStream && std::memcmp(guest.data(), host.data(), static_cast(got)) == 0)); return got; } std::int64_t DecodeTogether(void* vf, OggVorbis_File& reference) { std::int64_t total = 0; for (;;) { const std::int64_t got = RequireSameRead(vf, reference); Require(got >= 0 || got == OV_HOLE); if (got == 0) return total; if (got > 0) total += got; } } void RequireSameTells(void* vf, OggVorbis_File& reference) { Require(sceVorbisDecPcmTell(vf) == ov_pcm_tell(&reference)); Require(sceVorbisDecRawTell(vf) == ov_raw_tell(&reference)); Require(sceVorbisDecTimeTell(vf) == ov_time_tell(&reference)); } void TestSeekable(const std::vector& stream) { GuestFile guest; GuestFile probe; void* vf = guest.Get(); Source source{&stream}; source.probe = probe.Get(); Require(sceVorbisDecOpenCallbacks(&source, vf, nullptr, 0, SeekableCallbacks) == 0); Require(source.probed == 0 && source.closes == 0); Source referenceSource{&stream}; OggVorbis_File reference{}; Require(ov_open_callbacks(&referenceSource, &reference, nullptr, 0, HostSeekableCallbacks) == 0); Require(sceVorbisDecStreams(vf) == 2 && sceVorbisDecSeekable(vf) == 1); Require(sceVorbisDecSerialNumber(vf, 0) == FirstSerial && sceVorbisDecSerialNumber(vf, 1) == SecondSerial); Require(sceVorbisDecSerialNumber(vf, -1) == FirstSerial && sceVorbisDecSerialNumber(vf, 7) == SecondSerial); Require(sceVorbisDecPcmTotal(vf, 0) == FirstSamples && sceVorbisDecPcmTotal(vf, 1) == SecondSamples); Require(sceVorbisDecPcmTotal(vf, -1) == FirstSamples + SecondSamples && sceVorbisDecPcmTotal(vf, 2) == OV_EINVAL); Require(sceVorbisDecRawTotal(vf, -1) == ov_raw_total(&reference, -1) && sceVorbisDecRawTotal(vf, -1) > 0); Require(sceVorbisDecRawTotal(vf, 0) == ov_raw_total(&reference, 0) && sceVorbisDecRawTotal(vf, 1) == ov_raw_total(&reference, 1)); Require(sceVorbisDecTimeTotal(vf, 0) == 1.0 && sceVorbisDecTimeTotal(vf, 1) == 0.5 && sceVorbisDecTimeTotal(vf, -1) == 1.5); Require(sceVorbisDecBitrate(vf, -1) == ov_bitrate(&reference, -1) && sceVorbisDecBitrate(vf, 1) == ov_bitrate(&reference, 1)); Require(sceVorbisDecBitrate(vf, -1) > 0 && sceVorbisDecBitrate(vf, 2) == OV_EINVAL); Require(sceVorbisDecBitrateInstant(vf) == OV_FALSE); GuestInfo* first = sceVorbisDecInfo(vf, 0); GuestInfo* second = sceVorbisDecInfo(vf, 1); Require(first && second && first != second && sceVorbisDecInfo(vf, 0) == first); Require(sceVorbisDecInfo(vf, -1) == first && sceVorbisDecInfo(vf, 2) == nullptr); Require(first->version == 0 && first->channels == FirstChannels && first->rate == FirstRate); Require(second->version == 0 && second->channels == SecondChannels && second->rate == SecondRate); for (int link = 0; link < 2; ++link) { const GuestInfo* info = sceVorbisDecInfo(vf, link); const vorbis_info* host = ov_info(&reference, link); Require(info->bitrate_upper == host->bitrate_upper && info->bitrate_nominal == host->bitrate_nominal); Require(info->bitrate_lower == host->bitrate_lower && info->bitrate_window == host->bitrate_window && info->codec_setup != nullptr); } const GuestComment* comment = sceVorbisDecComment(vf, 1); Require(comment && comment->comments == 2 && comment->comment_lengths[0] == 12 && comment->comment_lengths[1] == 13); Require(std::strcmp(comment->user_comments[0], "TITLE=second") == 0 && std::strcmp(comment->user_comments[1], "ARTIST=AnyPS5") == 0); Require(std::strcmp(comment->vendor, ov_comment(&reference, 1)->vendor) == 0); Require(sceVorbisDecComment(vf, -1) == sceVorbisDecComment(vf, 0) && sceVorbisDecComment(vf, 2) == nullptr); Require(std::strcmp(sceVorbisDecComment(vf, 0)->user_comments[0], "TITLE=first") == 0); const std::int64_t firstRead = RequireSameRead(vf, reference); Require(firstRead > 0 && sceVorbisDecBitrateInstant(vf) == ov_bitrate_instant(&reference)); Require(sceVorbisDecInfo(vf, -1) == first); Require(firstRead + DecodeTogether(vf, reference) == DecodedBytes); Require(sceVorbisDecPcmTell(vf) == FirstSamples + SecondSamples && sceVorbisDecTimeTell(vf) == 1.5); RequireSameTells(vf, reference); Require(sceVorbisDecInfo(vf, -1) == second && sceVorbisDecSerialNumber(vf, -1) == SecondSerial); Require(sceVorbisDecPcmSeek(vf, 0) == 0 && ov_pcm_seek(&reference, 0) == 0); Require(DecodeTogether(vf, reference) == DecodedBytes); Require(sceVorbisDecPcmSeek(vf, 1000) == 0 && ov_pcm_seek(&reference, 1000) == 0); Require(sceVorbisDecPcmTell(vf) == 1000); RequireSameTells(vf, reference); Require(RequireSameRead(vf, reference) > 0 && RequireSameRead(vf, reference, 1, 2, 0) > 0 && RequireSameRead(vf, reference, 0, 1, 0) > 0); Require(sceVorbisDecPcmSeekPage(vf, FirstSamples + 3000) == ov_pcm_seek_page(&reference, FirstSamples + 3000)); RequireSameTells(vf, reference); Require(RequireSameRead(vf, reference) > 0 && sceVorbisDecInfo(vf, -1) == second); Require(sceVorbisDecTimeSeek(vf, 0.25) == 0 && ov_time_seek(&reference, 0.25) == 0); Require(sceVorbisDecPcmTell(vf) == FirstRate / 4); RequireSameTells(vf, reference); Require(RequireSameRead(vf, reference) > 0 && sceVorbisDecInfo(vf, -1) == first); Require(sceVorbisDecTimeSeekPage(vf, 1.2) == ov_time_seek_page(&reference, 1.2)); RequireSameTells(vf, reference); Require(RequireSameRead(vf, reference) > 0); const auto middle = static_cast(stream.size() / 2); Require(sceVorbisDecRawSeek(vf, middle) == 0 && ov_raw_seek(&reference, middle) == 0); RequireSameTells(vf, reference); Require(RequireSameRead(vf, reference) > 0); Require(sceVorbisDecPcmSeekLap(vf, 5000) == 0 && ov_pcm_seek_lap(&reference, 5000) == 0); RequireSameTells(vf, reference); Require(RequireSameRead(vf, reference) > 0 && RequireSameRead(vf, reference) > 0); Require(sceVorbisDecPcmSeekPageLap(vf, 30000) == ov_pcm_seek_page_lap(&reference, 30000)); RequireSameTells(vf, reference); Require(RequireSameRead(vf, reference) > 0); Require(sceVorbisDecRawSeekLap(vf, middle / 2) == ov_raw_seek_lap(&reference, middle / 2)); RequireSameTells(vf, reference); Require(RequireSameRead(vf, reference) > 0); Require(sceVorbisDecTimeSeekLap(vf, 1.1) == 0 && ov_time_seek_lap(&reference, 1.1) == 0); RequireSameTells(vf, reference); Require(RequireSameRead(vf, reference) > 0 && sceVorbisDecInfo(vf, -1) == second); Require(sceVorbisDecTimeSeekPageLap(vf, 0.75) == ov_time_seek_page_lap(&reference, 0.75)); RequireSameTells(vf, reference); Require(RequireSameRead(vf, reference) > 0 && sceVorbisDecInfo(vf, -1) == first); Require(sceVorbisDecPcmSeek(vf, -1) == OV_EINVAL && sceVorbisDecPcmSeek(vf, FirstSamples + SecondSamples + 1) == OV_EINVAL); Require(sceVorbisDecTimeSeek(vf, -0.5) == OV_EINVAL && sceVorbisDecTimeSeek(vf, 2.0) == OV_EINVAL); Require(sceVorbisDecRawSeek(vf, -1) == OV_EINVAL && sceVorbisDecRawSeek(vf, middle * 3) == OV_EINVAL); Require(source.closes == 0 && sceVorbisDecClear(vf) == 0 && source.closes == 1); Require(sceVorbisDecStreams(vf) == 0 && sceVorbisDecInfo(vf, 0) == nullptr && sceVorbisDecPcmTell(vf) == OV_EINVAL); Require(sceVorbisDecClear(vf) == 0 && source.closes == 1); Require(ov_clear(&reference) == 0 && referenceSource.closes == 1); Require(source.probed == 0 && guest.Untouched() && probe.Untouched()); } void TestStreaming(const std::vector& stream) { GuestFile guest; void* vf = guest.Get(); const auto* initial = reinterpret_cast(stream.data()); Source source{&stream, 64}; Require(sceVorbisDecOpenCallbacks(&source, vf, initial, 64, StreamingCallbacks) == 0); Source referenceSource{&stream, 64}; OggVorbis_File reference{}; Require(ov_open_callbacks(&referenceSource, &reference, initial, 64, HostStreamingCallbacks) == 0); Require(sceVorbisDecSeekable(vf) == 0 && sceVorbisDecStreams(vf) == 1); Require(sceVorbisDecPcmTotal(vf, -1) == OV_EINVAL && sceVorbisDecRawTotal(vf, 0) == OV_EINVAL && sceVorbisDecTimeTotal(vf, -1) == OV_EINVAL); Require(sceVorbisDecRawSeek(vf, 0) == OV_ENOSEEK && sceVorbisDecPcmSeek(vf, 0) == OV_ENOSEEK && sceVorbisDecTimeSeekPage(vf, 0.0) == OV_ENOSEEK); Require(sceVorbisDecBitrate(vf, 0) == ov_bitrate(&reference, 0) && sceVorbisDecBitrate(vf, 1) == ov_bitrate(&reference, 1)); Require(sceVorbisDecSerialNumber(vf, 3) == FirstSerial); GuestInfo* info = sceVorbisDecInfo(vf, 0); Require(info && info == sceVorbisDecInfo(vf, 5) && info == sceVorbisDecInfo(vf, -1)); Require(info->channels == FirstChannels && info->rate == FirstRate); const GuestComment* comment = sceVorbisDecComment(vf, 4); Require(comment && std::strcmp(comment->user_comments[0], "TITLE=first") == 0); Require(DecodeTogether(vf, reference) == DecodedBytes); Require(info->channels == SecondChannels && info->rate == SecondRate && sceVorbisDecInfo(vf, 0) == info); Require(std::strcmp(comment->user_comments[0], "TITLE=second") == 0); Require(sceVorbisDecSerialNumber(vf, 0) == SecondSerial); RequireSameTells(vf, reference); Require(sceVorbisDecClear(vf) == 0 && source.closes == 1); Require(ov_clear(&reference) == 0 && referenceSource.closes == 1); Require(guest.Untouched()); } void TestFloatAndFilter(const std::vector& stream) { GuestFile guest; void* vf = guest.Get(); Source source{&stream}; Require(sceVorbisDecOpenCallbacks(&source, vf, nullptr, 0, SeekableCallbacks) == 0); Source referenceSource{&stream}; OggVorbis_File reference{}; Require(ov_open_callbacks(&referenceSource, &reference, nullptr, 0, HostSeekableCallbacks) == 0); for (int round = 0; round < 6; ++round) { float** pcm = nullptr; float** hostPcm = nullptr; int guestStream = -1; int hostStream = -1; const std::int64_t got = sceVorbisDecReadFloat(vf, &pcm, 300, &guestStream); const long expected = ov_read_float(&reference, &hostPcm, 300, &hostStream); Require(got == expected && got > 0 && got <= 300 && guestStream == hostStream && guestStream == 0); for (int channel = 0; channel < FirstChannels; ++channel) { Require(std::memcmp(pcm[channel], hostPcm[channel], static_cast(got) * sizeof(float)) == 0); } } Require(sceVorbisDecReadFloat(vf, nullptr, 50, nullptr) == ov_read_float(&reference, nullptr, 50, nullptr)); RequireSameTells(vf, reference); FilterLog log; std::int64_t total = 0; for (;;) { std::array guestBytes{}; std::array hostBytes{}; int guestStream = -1; int hostStream = -1; const std::int64_t got = sceVorbisDecReadFilter(vf, guestBytes.data(), static_cast(guestBytes.size()), 0, 2, 1, &guestStream, GuestHalve, &log); const long expected = ov_read_filter(&reference, hostBytes.data(), static_cast(hostBytes.size()), 0, 2, 1, &hostStream, HostHalve, nullptr); Require(got == expected && got >= 0); if (got == 0) break; Require(guestStream == hostStream && std::memcmp(guestBytes.data(), hostBytes.data(), static_cast(got)) == 0); total += got; } Require(total > 0 && log.calls > 0 && log.samples > 0 && log.firstChannels == FirstChannels && log.lastChannels == SecondChannels); Require(sceVorbisDecPcmSeek(vf, 0) == 0 && ov_pcm_seek(&reference, 0) == 0); std::array filtered{}; std::array plain{}; int bitstream = -1; const int calls = log.calls; const std::int64_t got = sceVorbisDecReadFilter(vf, filtered.data(), static_cast(filtered.size()), 0, 2, 1, &bitstream, nullptr, &log); Require(got > 0 && got == ov_read(&reference, plain.data(), static_cast(plain.size()), 0, 2, 1, &bitstream)); Require(std::memcmp(filtered.data(), plain.data(), static_cast(got)) == 0 && log.calls == calls); Require(sceVorbisDecClear(vf) == 0 && source.closes == 1 && ov_clear(&reference) == 0); Require(guest.Untouched()); } void TestHalfrate(const std::vector& stream) { GuestFile guest; void* vf = guest.Get(); Source source{&stream}; Require(sceVorbisDecOpenCallbacks(&source, vf, nullptr, 0, SeekableCallbacks) == 0); Source referenceSource{&stream}; OggVorbis_File reference{}; Require(ov_open_callbacks(&referenceSource, &reference, nullptr, 0, HostSeekableCallbacks) == 0); Require(sceVorbisDecHalfrateP(vf) == 0); Require(RequireSameRead(vf, reference) > 0 && RequireSameRead(vf, reference) > 0); Require(sceVorbisDecHalfrate(vf, 1) == 0 && ov_halfrate(&reference, 1) == 0); Require(sceVorbisDecHalfrateP(vf) == 1 && ov_halfrate_p(&reference) == 1); RequireSameTells(vf, reference); const std::int64_t half = DecodeTogether(vf, reference); Require(half > 0 && half < DecodedBytes / 2); RequireSameTells(vf, reference); Require(sceVorbisDecPcmTell(vf) > FirstSamples); Require(sceVorbisDecHalfrate(vf, 0) == 0 && sceVorbisDecHalfrateP(vf) == 0); Require(sceVorbisDecClear(vf) == 0 && source.closes == 1 && ov_clear(&reference) == 0); Require(guest.Untouched()); } void TestCrossLap(const std::vector& stream) { GuestFile first; GuestFile second; GuestFile closed; GuestFile otherClosed; Source firstSource{&stream}; Source secondSource{&stream}; Require(sceVorbisDecOpenCallbacks(&secondSource, second.Get(), nullptr, 0, SeekableCallbacks) == 0); firstSource.probe = second.Get(); Require(sceVorbisDecOpenCallbacks(&firstSource, first.Get(), nullptr, 0, SeekableCallbacks) == 0); Require(firstSource.probed == 2); Source firstReferenceSource{&stream}; Source secondReferenceSource{&stream}; OggVorbis_File firstReference{}; OggVorbis_File secondReference{}; Require(ov_open_callbacks(&firstReferenceSource, &firstReference, nullptr, 0, HostSeekableCallbacks) == 0); Require(ov_open_callbacks(&secondReferenceSource, &secondReference, nullptr, 0, HostSeekableCallbacks) == 0); Require(RequireSameRead(first.Get(), firstReference) > 0 && RequireSameRead(first.Get(), firstReference) > 0); Require(sceVorbisDecPcmSeek(second.Get(), FirstSamples + 2000) == 0 && ov_pcm_seek(&secondReference, FirstSamples + 2000) == 0); Require(sceVorbisDecCrossLap(first.Get(), second.Get()) == 0 && ov_crosslap(&firstReference, &secondReference) == 0); for (int round = 0; round < 3; ++round) { Require(RequireSameRead(second.Get(), secondReference) > 0); Require(RequireSameRead(first.Get(), firstReference) > 0); } RequireSameTells(first.Get(), firstReference); RequireSameTells(second.Get(), secondReference); Require(sceVorbisDecCrossLap(first.Get(), first.Get()) == 0); Require(sceVorbisDecCrossLap(first.Get(), closed.Get()) == OV_EINVAL && sceVorbisDecCrossLap(closed.Get(), second.Get()) == OV_EINVAL); Require(sceVorbisDecCrossLap(closed.Get(), otherClosed.Get()) == OV_EINVAL && sceVorbisDecCrossLap(closed.Get(), closed.Get()) == 0); Require(sceVorbisDecCrossLap(nullptr, nullptr) == 0); OggVorbis_File zeroed{}; Require(sceVorbisDecCrossLap(closed.Get(), nullptr) == OV_EINVAL && ov_crosslap(&zeroed, nullptr) == OV_EINVAL); Require(Throws([&] { sceVorbisDecCrossLap(first.Get(), nullptr); })); Require(Throws([&] { sceVorbisDecCrossLap(nullptr, closed.Get()); })); Require(sceVorbisDecClear(first.Get()) == 0 && sceVorbisDecClear(second.Get()) == 0); Require(firstSource.closes == 1 && secondSource.closes == 1); Require(ov_clear(&firstReference) == 0 && ov_clear(&secondReference) == 0); Require(first.Untouched() && second.Untouched() && closed.Untouched() && otherClosed.Untouched()); } void TestNeverOpened() { GuestFile guest; void* vf = guest.Get(); Require(sceVorbisDecStreams(vf) == 0 && sceVorbisDecSeekable(vf) == 0); Require(sceVorbisDecSerialNumber(vf, -1) == 0 && sceVorbisDecSerialNumber(vf, 0) == 0 && sceVorbisDecSerialNumber(vf, 3) == 0); Require(sceVorbisDecBitrate(vf, -1) == OV_EINVAL && sceVorbisDecBitrate(vf, 0) == OV_EINVAL && sceVorbisDecBitrateInstant(vf) == OV_EINVAL); Require(sceVorbisDecRawTotal(vf, -1) == OV_EINVAL && sceVorbisDecPcmTotal(vf, 0) == OV_EINVAL && sceVorbisDecTimeTotal(vf, -1) == OV_EINVAL); Require(sceVorbisDecRawSeek(vf, 0) == OV_EINVAL && sceVorbisDecPcmSeek(vf, 0) == OV_EINVAL && sceVorbisDecPcmSeekPage(vf, 0) == OV_EINVAL); Require(sceVorbisDecTimeSeek(vf, 0.0) == OV_EINVAL && sceVorbisDecTimeSeekPage(vf, 0.0) == OV_EINVAL); Require(sceVorbisDecRawSeekLap(vf, 0) == OV_EINVAL && sceVorbisDecPcmSeekLap(vf, 0) == OV_EINVAL && sceVorbisDecPcmSeekPageLap(vf, 0) == OV_EINVAL); Require(sceVorbisDecTimeSeekLap(vf, 0.0) == OV_EINVAL && sceVorbisDecTimeSeekPageLap(vf, 0.0) == OV_EINVAL); Require(sceVorbisDecRawTell(vf) == OV_EINVAL && sceVorbisDecPcmTell(vf) == OV_EINVAL && sceVorbisDecTimeTell(vf) == OV_EINVAL); Require(sceVorbisDecInfo(vf, -1) == nullptr && sceVorbisDecInfo(vf, 0) == nullptr); Require(sceVorbisDecComment(vf, -1) == nullptr && sceVorbisDecComment(vf, 0) == nullptr); std::array buffer{}; int bitstream = 7; Require(sceVorbisDecRead(vf, buffer.data(), static_cast(buffer.size()), 0, 2, 1, &bitstream) == OV_EINVAL && bitstream == 7); float** pcm = nullptr; Require(sceVorbisDecReadFloat(vf, &pcm, 64, &bitstream) == OV_EINVAL && pcm == nullptr && bitstream == 7); FilterLog log; Require(sceVorbisDecReadFilter(vf, buffer.data(), static_cast(buffer.size()), 0, 2, 1, &bitstream, GuestHalve, &log) == OV_EINVAL); Require(log.calls == 0 && bitstream == 7); Require(sceVorbisDecHalfrate(vf, 1) == OV_EINVAL && sceVorbisDecHalfrateP(vf) == OV_EINVAL); Require(sceVorbisDecClear(vf) == 0 && sceVorbisDecClear(nullptr) == 0); Require(guest.Untouched()); Source source; Require(Throws([&] { sceVorbisDecOpenCallbacks(&source, nullptr, nullptr, 0, SeekableCallbacks); })); Require(Throws([] { sceVorbisDecStreams(nullptr); })); Require(Throws([] { sceVorbisDecInfo(nullptr, 0); })); Require(Throws([] { sceVorbisDecPcmSeek(nullptr, 0); })); } void TestFailedOpens(const std::vector& stream) { GuestFile guest; void* vf = guest.Get(); std::vector noise(8192); std::uint32_t state = 0x2545f491; for (auto& value : noise) { state = state * 1664525u + 1013904223u; value = static_cast(state >> 24); } Source noiseSource{&noise}; Require(sceVorbisDecOpenCallbacks(&noiseSource, vf, nullptr, 0, SeekableCallbacks) == OV_ENOTVORBIS); Require(noiseSource.closes == 0 && sceVorbisDecStreams(vf) == 0 && sceVorbisDecInfo(vf, 0) == nullptr); Require(sceVorbisDecClear(vf) == 0 && noiseSource.closes == 0); Source untold{&stream}; constexpr GuestCallbacks NoTell{GuestRead, GuestSeek, GuestClose, nullptr}; Require(sceVorbisDecOpenCallbacks(&untold, vf, nullptr, 0, NoTell) == OV_EINVAL); Require(untold.closes == 0 && sceVorbisDecStreams(vf) == 0 && sceVorbisDecClear(vf) == 0 && untold.closes == 0); constexpr GuestCallbacks NoRead{nullptr, GuestSeek, GuestClose, GuestTell}; Source unread{&stream}; OggVorbis_File reference{}; const ov_callbacks hostNoRead{nullptr, HostSeek, HostClose, HostTell}; Require(sceVorbisDecOpenCallbacks(&unread, vf, nullptr, 0, NoRead) == ov_open_callbacks(&unread, &reference, nullptr, 0, hostNoRead)); Require(unread.closes == 0 && sceVorbisDecStreams(vf) == 0); Require(sceVorbisDecOpenCallbacks(nullptr, vf, nullptr, 0, SeekableCallbacks) == OV_ENOTVORBIS); Source previous{&stream}; Source next{&stream}; Require(sceVorbisDecOpenCallbacks(&previous, vf, nullptr, 0, SeekableCallbacks) == 0); Require(sceVorbisDecOpenCallbacks(&next, vf, nullptr, 0, StreamingCallbacks) == 0); Require(previous.closes == 0 && sceVorbisDecSeekable(vf) == 0 && sceVorbisDecStreams(vf) == 1); Require(sceVorbisDecClear(vf) == 0 && previous.closes == 0 && next.closes == 1); Source unsized{&stream}; Require(sceVorbisDecOpenCallbacks(&unsized, vf, nullptr, std::int64_t{1} << 32, SeekableCallbacks) == 0); Require(sceVorbisDecStreams(vf) == 2 && sceVorbisDecClear(vf) == 0 && unsized.closes == 1); const auto* initial = reinterpret_cast(stream.data()); const auto size = static_cast(stream.size()); Require(sceVorbisDecOpenCallbacks(nullptr, vf, initial, size, SeekableCallbacks) == 0); Require(ov_open_callbacks(nullptr, &reference, initial, static_cast(size), HostSeekableCallbacks) == 0); Require(sceVorbisDecSeekable(vf) == 0 && DecodeTogether(vf, reference) == DecodedBytes); Require(sceVorbisDecClear(vf) == 0 && ov_clear(&reference) == 0); Require(guest.Untouched()); } void TestThrowingCallbacks(const std::vector& stream) { GuestFile guest; Source source{&stream}; Require(sceVorbisDecOpenCallbacks(&source, guest.Get(), nullptr, 0, SeekableCallbacks) == 0); std::array buffer{}; int bitstream = -1; source.throwOnRead = true; bool threw = false; for (int call = 0; call < 1024 && !threw; ++call) { threw = Throws([&] { sceVorbisDecRead(guest.Get(), buffer.data(), static_cast(buffer.size()), 0, 2, 1, &bitstream); }); } Require(threw); source.throwOnRead = false; source.throwOnClose = true; Require(Throws([&] { sceVorbisDecClear(guest.Get()); })); Require(source.closes == 1 && sceVorbisDecStreams(guest.Get()) == 0); Require(sceVorbisDecClear(guest.Get()) == 0 && source.closes == 1); Require(guest.Untouched()); } } int main() { std::vector stream; EncodeLink(stream, FirstSerial, FirstChannels, FirstRate, FirstSamples, "first"); EncodeLink(stream, SecondSerial, SecondChannels, SecondRate, SecondSamples, "second"); TestSeekable(stream); TestStreaming(stream); TestFloatAndFilter(stream); TestHalfrate(stream); TestCrossLap(stream); TestNeverOpened(); TestFailedOpens(stream); TestThrowingCallbacks(stream); Require(Throws([] { sceVorbisDecGetDecodePriority(); })); Require(Throws([] { sceVorbisDecSetDecodePriority(); })); Require(Throws([] { sceVorbisDecGetLastDecodeError(); })); std::puts("All VorbisDec guest tests passed"); return 0; }