AnyPS5/core/libs/tests/GuestAvPlayer.cpp

1008 lines
67 KiB
C++

#include <algorithm>
#include <array>
#include <chrono>
#include <cmath>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <deque>
#include <filesystem>
#include <fstream>
#include <functional>
#include <map>
#include <mutex>
#include <new>
#include <optional>
#include <stdexcept>
#include <string>
#include <thread>
#include <vector>
#include "SceTypes.hpp"
#include "prx/libc/include/general/VabiMacros.hpp"
extern "C" {
AvPlayerInternal* APS5_VABI sceAvPlayerInit(AvPlayerInitData*);
int APS5_VABI sceAvPlayerInitEx(const AvPlayerInitDataEx*, AvPlayerInternal**);
int APS5_VABI sceAvPlayerPostInit(AvPlayerInternal*, const AvPlayerPostInitData*);
int APS5_VABI sceAvPlayerSetLogCallback(void*, void*);
int APS5_VABI sceAvPlayerAddSource(AvPlayerInternal*, const char*);
int APS5_VABI sceAvPlayerStreamCount(AvPlayerInternal*);
int APS5_VABI sceAvPlayerGetStreamInfo(AvPlayerInternal*, std::uint32_t, AvPlayerStreamInfo*);
int APS5_VABI sceAvPlayerGetStreamInfoEx(AvPlayerInternal*, std::uint32_t, AvPlayerStreamInfoEx*);
int APS5_VABI sceAvPlayerEnableStream(AvPlayerInternal*, std::uint32_t);
int APS5_VABI sceAvPlayerChangeStream(AvPlayerInternal*, std::uint32_t, std::uint32_t);
int APS5_VABI sceAvPlayerSetAvailableBandwidth(AvPlayerInternal*, std::uint32_t, std::uint32_t, std::uint32_t);
int APS5_VABI sceAvPlayerStart(AvPlayerInternal*);
int APS5_VABI sceAvPlayerStartEx(AvPlayerInternal*, const void*);
int APS5_VABI sceAvPlayerStop(AvPlayerInternal*);
int APS5_VABI sceAvPlayerPause(AvPlayerInternal*);
int APS5_VABI sceAvPlayerResume(AvPlayerInternal*);
int APS5_VABI sceAvPlayerJumpToTime(AvPlayerInternal*, std::uint64_t);
int APS5_VABI sceAvPlayerSetLooping(AvPlayerInternal*, Bool);
int APS5_VABI sceAvPlayerSetAvSyncMode(AvPlayerInternal*, std::uint32_t);
int APS5_VABI sceAvPlayerSetTrickSpeed(AvPlayerInternal*, std::int32_t);
Bool APS5_VABI sceAvPlayerGetVideoDataEx(AvPlayerInternal*, AvPlayerFrameInfoEx*);
Bool APS5_VABI sceAvPlayerGetVideoData(AvPlayerInternal*, AvPlayerFrameInfo*);
Bool APS5_VABI sceAvPlayerGetAudioData(AvPlayerInternal*, AvPlayerFrameInfo*);
Bool APS5_VABI sceAvPlayerIsActive(AvPlayerInternal*);
std::uint64_t APS5_VABI sceAvPlayerCurrentTime(AvPlayerInternal*);
int APS5_VABI sceAvPlayerClose(AvPlayerInternal*);
}
namespace {
constexpr int Width = 100;
constexpr int Height = 60;
constexpr int Pitch = 256;
constexpr int FrameRate = 30;
constexpr int FrameCount = 30;
constexpr int SampleRate = 48000;
constexpr std::uint32_t VideoStream = 0;
constexpr std::uint32_t EnglishAudioStream = 1;
constexpr std::uint32_t FrenchAudioStream = 2;
constexpr int InvalidParams = static_cast<int>(0x806A0001u);
constexpr int OperationFailed = static_cast<int>(0x806A0002u);
constexpr std::int32_t LoopingBack = static_cast<std::int32_t>(0x806A00A1u);
constexpr std::int32_t JumpComplete = static_cast<std::int32_t>(0x806A00A3u);
constexpr std::int32_t EventStop = 1;
constexpr std::int32_t EventReady = 2;
constexpr std::int32_t EventPlay = 3;
constexpr std::int32_t EventPause = 4;
constexpr std::int32_t EventWarning = 0x20;
const char* const MoviePath = "app0/avplayer.mp4";
constexpr std::array<std::uint8_t, 5986> Movie{
0x00, 0x00, 0x00, 0x20, 0x66, 0x74, 0x79, 0x70, 0x69, 0x73, 0x6f, 0x6d, 0x00, 0x00, 0x02, 0x00,
0x69, 0x73, 0x6f, 0x6d, 0x69, 0x73, 0x6f, 0x32, 0x61, 0x76, 0x63, 0x31, 0x6d, 0x70, 0x34, 0x31,
0x00, 0x00, 0x0b, 0x56, 0x6d, 0x6f, 0x6f, 0x76, 0x00, 0x00, 0x00, 0x6c, 0x6d, 0x76, 0x68, 0x64,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbb, 0x80,
0x00, 0x00, 0xbb, 0x80, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 0xfb, 0x74, 0x72, 0x61, 0x6b, 0x00, 0x00, 0x00, 0x5c,
0x74, 0x6b, 0x68, 0x64, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbb, 0x80, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
0x00, 0x64, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x65, 0x64, 0x74, 0x73,
0x00, 0x00, 0x00, 0x1c, 0x65, 0x6c, 0x73, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0xbb, 0x80, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x03, 0x73,
0x6d, 0x64, 0x69, 0x61, 0x00, 0x00, 0x00, 0x20, 0x6d, 0x64, 0x68, 0x64, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x3c, 0x00,
0x15, 0xc7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x68, 0x64, 0x6c, 0x72, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x76, 0x69, 0x64, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x56, 0x69, 0x64, 0x65, 0x6f, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72,
0x00, 0x00, 0x00, 0x03, 0x1e, 0x6d, 0x69, 0x6e, 0x66, 0x00, 0x00, 0x00, 0x14, 0x76, 0x6d, 0x68,
0x64, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x24, 0x64, 0x69, 0x6e, 0x66, 0x00, 0x00, 0x00, 0x1c, 0x64, 0x72, 0x65, 0x66, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x75, 0x72, 0x6c, 0x20, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x02, 0xde, 0x73, 0x74, 0x62, 0x6c, 0x00, 0x00, 0x00, 0xb2, 0x73, 0x74, 0x73,
0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xa2, 0x61, 0x76, 0x63,
0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x3c, 0x00, 0x48, 0x00,
0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0c, 0x4c, 0x61, 0x76, 0x63,
0x20, 0x6c, 0x69, 0x62, 0x78, 0x32, 0x36, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0xff, 0xff, 0x00,
0x00, 0x00, 0x38, 0x61, 0x76, 0x63, 0x43, 0x01, 0x64, 0x00, 0x0b, 0xff, 0xe1, 0x00, 0x1a, 0x67,
0x64, 0x00, 0x0b, 0xac, 0x76, 0x11, 0x1c, 0x9e, 0x7b, 0x84, 0x00, 0x00, 0x03, 0x00, 0x04, 0x00,
0x00, 0x03, 0x00, 0xf0, 0x3c, 0x50, 0xa6, 0x11, 0x80, 0x01, 0x00, 0x07, 0x68, 0xe8, 0x43, 0x81,
0x0c, 0xb2, 0x2c, 0xfd, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x14, 0x62, 0x74, 0x72, 0x74, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0xb0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x73,
0x74, 0x74, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1e, 0x00,
0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x73, 0x74, 0x73, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x15, 0x00,
0x00, 0x00, 0xb8, 0x63, 0x74, 0x74, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x00,
0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x08, 0x00, 0x00,
0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x08, 0x00, 0x00,
0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x08, 0x00, 0x00,
0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00,
0x00, 0x00, 0x01, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00,
0x00, 0x00, 0x01, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00,
0x00, 0x00, 0x01, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00,
0x00, 0x00, 0x02, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x08, 0x00, 0x00,
0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x08, 0x00, 0x00,
0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x08, 0x00, 0x00,
0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x28, 0x73, 0x74, 0x73, 0x63, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00,
0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
0x00, 0x00, 0x8c, 0x73, 0x74, 0x73, 0x7a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0d, 0x00,
0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x0e, 0x00,
0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x30, 0x00,
0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x1f, 0x00,
0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x0e, 0x00,
0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x0d, 0x00,
0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x0e, 0x00,
0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x84, 0x73,
0x74, 0x63, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x0b, 0x86, 0x00,
0x00, 0x0b, 0xfc, 0x00, 0x00, 0x0c, 0x87, 0x00, 0x00, 0x0c, 0xf0, 0x00, 0x00, 0x0d, 0x3e, 0x00,
0x00, 0x0d, 0xaf, 0x00, 0x00, 0x0d, 0xea, 0x00, 0x00, 0x0e, 0x67, 0x00, 0x00, 0x0e, 0xad, 0x00,
0x00, 0x0f, 0x21, 0x00, 0x00, 0x0f, 0xad, 0x00, 0x00, 0x0f, 0xf8, 0x00, 0x00, 0x10, 0x5f, 0x00,
0x00, 0x10, 0x9f, 0x00, 0x00, 0x11, 0x1a, 0x00, 0x00, 0x11, 0x5f, 0x00, 0x00, 0x11, 0xf9, 0x00,
0x00, 0x12, 0x40, 0x00, 0x00, 0x12, 0xaa, 0x00, 0x00, 0x13, 0x25, 0x00, 0x00, 0x13, 0x83, 0x00,
0x00, 0x13, 0xfe, 0x00, 0x00, 0x14, 0x3a, 0x00, 0x00, 0x14, 0xa8, 0x00, 0x00, 0x14, 0xfb, 0x00,
0x00, 0x15, 0x64, 0x00, 0x00, 0x15, 0xd1, 0x00, 0x00, 0x16, 0x28, 0x00, 0x00, 0x16, 0x95, 0x00,
0x00, 0x02, 0xf5, 0x74, 0x72, 0x61, 0x6b, 0x00, 0x00, 0x00, 0x5c, 0x74, 0x6b, 0x68, 0x64, 0x00,
0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xbb, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x65, 0x64, 0x74, 0x73, 0x00, 0x00, 0x00, 0x1c, 0x65,
0x6c, 0x73, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0xbb, 0x80, 0x00,
0x00, 0x04, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x6d, 0x6d, 0x64, 0x69, 0x61, 0x00,
0x00, 0x00, 0x20, 0x6d, 0x64, 0x68, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xbb, 0x80, 0x00, 0x00, 0xbf, 0x80, 0x15, 0xc7, 0x00, 0x00, 0x00,
0x00, 0x00, 0x2d, 0x68, 0x64, 0x6c, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x73,
0x6f, 0x75, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x53,
0x6f, 0x75, 0x6e, 0x64, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x00, 0x00, 0x00, 0x02, 0x18,
0x6d, 0x69, 0x6e, 0x66, 0x00, 0x00, 0x00, 0x10, 0x73, 0x6d, 0x68, 0x64, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x64, 0x69, 0x6e, 0x66, 0x00, 0x00, 0x00, 0x1c,
0x64, 0x72, 0x65, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c,
0x75, 0x72, 0x6c, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0xdc, 0x73, 0x74, 0x62, 0x6c,
0x00, 0x00, 0x00, 0x7e, 0x73, 0x74, 0x73, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x6e, 0x6d, 0x70, 0x34, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,
0xbb, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x65, 0x73, 0x64, 0x73, 0x00, 0x00, 0x00, 0x00,
0x03, 0x80, 0x80, 0x80, 0x25, 0x00, 0x02, 0x00, 0x04, 0x80, 0x80, 0x80, 0x17, 0x40, 0x15, 0x00,
0x00, 0x00, 0x00, 0x00, 0x1f, 0x40, 0x00, 0x00, 0x08, 0xcf, 0x05, 0x80, 0x80, 0x80, 0x05, 0x11,
0x90, 0x56, 0xe5, 0x00, 0x06, 0x80, 0x80, 0x80, 0x01, 0x02, 0x00, 0x00, 0x00, 0x14, 0x62, 0x74,
0x72, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x40, 0x00, 0x00, 0x08, 0xcf, 0x00, 0x00,
0x00, 0x20, 0x73, 0x74, 0x74, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00,
0x00, 0x2f, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0x80, 0x00, 0x00,
0x00, 0x1c, 0x73, 0x74, 0x73, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x73, 0x74,
0x73, 0x7a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00,
0x00, 0xd0, 0x73, 0x74, 0x63, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00,
0x0b, 0xcb, 0x00, 0x00, 0x0c, 0x09, 0x00, 0x00, 0x0c, 0x58, 0x00, 0x00, 0x0c, 0x94, 0x00, 0x00,
0x0c, 0xc3, 0x00, 0x00, 0x0d, 0x0f, 0x00, 0x00, 0x0d, 0x4c, 0x00, 0x00, 0x0d, 0x7a, 0x00, 0x00,
0x0d, 0xbd, 0x00, 0x00, 0x0e, 0x0b, 0x00, 0x00, 0x0e, 0x39, 0x00, 0x00, 0x0e, 0x75, 0x00, 0x00,
0x0e, 0xbb, 0x00, 0x00, 0x0e, 0xf1, 0x00, 0x00, 0x0f, 0x51, 0x00, 0x00, 0x0f, 0x7e, 0x00, 0x00,
0x0f, 0xc1, 0x00, 0x00, 0x10, 0x05, 0x00, 0x00, 0x10, 0x31, 0x00, 0x00, 0x10, 0x6c, 0x00, 0x00,
0x10, 0xbe, 0x00, 0x00, 0x10, 0xec, 0x00, 0x00, 0x11, 0x27, 0x00, 0x00, 0x11, 0x9e, 0x00, 0x00,
0x11, 0xcc, 0x00, 0x00, 0x12, 0x0b, 0x00, 0x00, 0x12, 0x4e, 0x00, 0x00, 0x12, 0x7b, 0x00, 0x00,
0x12, 0xca, 0x00, 0x00, 0x12, 0xf8, 0x00, 0x00, 0x13, 0x55, 0x00, 0x00, 0x13, 0x98, 0x00, 0x00,
0x13, 0xc7, 0x00, 0x00, 0x14, 0x0b, 0x00, 0x00, 0x14, 0x47, 0x00, 0x00, 0x14, 0x7b, 0x00, 0x00,
0x14, 0xc7, 0x00, 0x00, 0x15, 0x09, 0x00, 0x00, 0x15, 0x38, 0x00, 0x00, 0x15, 0x72, 0x00, 0x00,
0x15, 0xa2, 0x00, 0x00, 0x15, 0xf1, 0x00, 0x00, 0x16, 0x36, 0x00, 0x00, 0x16, 0x68, 0x00, 0x00,
0x16, 0xa3, 0x00, 0x00, 0x16, 0xd2, 0x00, 0x00, 0x17, 0x06, 0x00, 0x00, 0x17, 0x35, 0x00, 0x00,
0x00, 0x1a, 0x73, 0x67, 0x70, 0x64, 0x01, 0x00, 0x00, 0x00, 0x72, 0x6f, 0x6c, 0x6c, 0x00, 0x00,
0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0x00, 0x00, 0x00, 0x1c, 0x73, 0x62, 0x67, 0x70,
0x00, 0x00, 0x00, 0x00, 0x72, 0x6f, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x30,
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0xb5, 0x74, 0x72, 0x61, 0x6b, 0x00, 0x00, 0x00, 0x5c,
0x74, 0x6b, 0x68, 0x64, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbb, 0x80, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x65, 0x64, 0x74, 0x73,
0x00, 0x00, 0x00, 0x1c, 0x65, 0x6c, 0x73, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0xbb, 0x80, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x03, 0x2d,
0x6d, 0x64, 0x69, 0x61, 0x00, 0x00, 0x00, 0x20, 0x6d, 0x64, 0x68, 0x64, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbb, 0x80, 0x00, 0x00, 0xbf, 0x80,
0x1a, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x68, 0x64, 0x6c, 0x72, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x73, 0x6f, 0x75, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x53, 0x6f, 0x75, 0x6e, 0x64, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72,
0x00, 0x00, 0x00, 0x02, 0xd8, 0x6d, 0x69, 0x6e, 0x66, 0x00, 0x00, 0x00, 0x10, 0x73, 0x6d, 0x68,
0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x64, 0x69, 0x6e,
0x66, 0x00, 0x00, 0x00, 0x1c, 0x64, 0x72, 0x65, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x0c, 0x75, 0x72, 0x6c, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02,
0x9c, 0x73, 0x74, 0x62, 0x6c, 0x00, 0x00, 0x00, 0x7e, 0x73, 0x74, 0x73, 0x64, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x6e, 0x6d, 0x70, 0x34, 0x61, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
0x10, 0x00, 0x00, 0x00, 0x00, 0xbb, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x65, 0x73, 0x64,
0x73, 0x00, 0x00, 0x00, 0x00, 0x03, 0x80, 0x80, 0x80, 0x25, 0x00, 0x03, 0x00, 0x04, 0x80, 0x80,
0x80, 0x17, 0x40, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0xd8, 0x00, 0x00, 0x3e, 0xd8, 0x05,
0x80, 0x80, 0x80, 0x05, 0x11, 0x88, 0x56, 0xe5, 0x00, 0x06, 0x80, 0x80, 0x80, 0x01, 0x02, 0x00,
0x00, 0x00, 0x14, 0x62, 0x74, 0x72, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0xd8, 0x00,
0x00, 0x3e, 0xd8, 0x00, 0x00, 0x00, 0x20, 0x73, 0x74, 0x74, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
0x00, 0x03, 0x80, 0x00, 0x00, 0x00, 0x1c, 0x73, 0x74, 0x73, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
0x00, 0x00, 0xd4, 0x73, 0x74, 0x73, 0x7a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x49, 0x00, 0x00, 0x00, 0x29, 0x00,
0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x28, 0x00,
0x00, 0x00, 0x2f, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x28, 0x00,
0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x27, 0x00,
0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x28, 0x00,
0x00, 0x00, 0x2d, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x32, 0x00,
0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x00, 0x27, 0x00,
0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x28, 0x00,
0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x2e, 0x00,
0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x26, 0x00,
0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x2c, 0x00,
0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x00, 0x29, 0x00,
0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0xd0, 0x73, 0x74, 0x63, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x30, 0x00, 0x00, 0x0b, 0xd1, 0x00, 0x00, 0x0c, 0x0f, 0x00, 0x00, 0x0c, 0x5e, 0x00,
0x00, 0x0c, 0x9a, 0x00, 0x00, 0x0c, 0xc9, 0x00, 0x00, 0x0d, 0x15, 0x00, 0x00, 0x0d, 0x52, 0x00,
0x00, 0x0d, 0x80, 0x00, 0x00, 0x0d, 0xc3, 0x00, 0x00, 0x0e, 0x11, 0x00, 0x00, 0x0e, 0x3f, 0x00,
0x00, 0x0e, 0x7b, 0x00, 0x00, 0x0e, 0xc1, 0x00, 0x00, 0x0e, 0xf7, 0x00, 0x00, 0x0f, 0x57, 0x00,
0x00, 0x0f, 0x84, 0x00, 0x00, 0x0f, 0xc7, 0x00, 0x00, 0x10, 0x0b, 0x00, 0x00, 0x10, 0x37, 0x00,
0x00, 0x10, 0x72, 0x00, 0x00, 0x10, 0xc4, 0x00, 0x00, 0x10, 0xf2, 0x00, 0x00, 0x11, 0x2d, 0x00,
0x00, 0x11, 0xa4, 0x00, 0x00, 0x11, 0xd2, 0x00, 0x00, 0x12, 0x11, 0x00, 0x00, 0x12, 0x54, 0x00,
0x00, 0x12, 0x81, 0x00, 0x00, 0x12, 0xd0, 0x00, 0x00, 0x12, 0xfe, 0x00, 0x00, 0x13, 0x5b, 0x00,
0x00, 0x13, 0x9e, 0x00, 0x00, 0x13, 0xcd, 0x00, 0x00, 0x14, 0x11, 0x00, 0x00, 0x14, 0x4d, 0x00,
0x00, 0x14, 0x81, 0x00, 0x00, 0x14, 0xcd, 0x00, 0x00, 0x15, 0x0f, 0x00, 0x00, 0x15, 0x3e, 0x00,
0x00, 0x15, 0x78, 0x00, 0x00, 0x15, 0xa8, 0x00, 0x00, 0x15, 0xf7, 0x00, 0x00, 0x16, 0x3c, 0x00,
0x00, 0x16, 0x6e, 0x00, 0x00, 0x16, 0xa9, 0x00, 0x00, 0x16, 0xd8, 0x00, 0x00, 0x17, 0x0c, 0x00,
0x00, 0x17, 0x3b, 0x00, 0x00, 0x00, 0x1a, 0x73, 0x67, 0x70, 0x64, 0x01, 0x00, 0x00, 0x00, 0x72,
0x6f, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0x00, 0x00, 0x00,
0x1c, 0x73, 0x62, 0x67, 0x70, 0x00, 0x00, 0x00, 0x00, 0x72, 0x6f, 0x6c, 0x6c, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x3d, 0x75, 0x64, 0x74,
0x61, 0x00, 0x00, 0x00, 0x35, 0x6d, 0x65, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x21, 0x68, 0x64, 0x6c, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6d, 0x64, 0x69,
0x72, 0x61, 0x70, 0x70, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x08, 0x69, 0x6c, 0x73, 0x74, 0x00, 0x00, 0x00, 0x08, 0x66, 0x72, 0x65, 0x65, 0x00, 0x00,
0x0b, 0xe4, 0x6d, 0x64, 0x61, 0x74, 0x00, 0x00, 0x00, 0x2d, 0x65, 0x88, 0x81, 0x00, 0x0a, 0xff,
0xfe, 0xf7, 0xd4, 0xb7, 0xcc, 0xb2, 0xed, 0x78, 0x7e, 0xb6, 0x0b, 0xa8, 0xf7, 0xa0, 0xe3, 0x03,
0xf2, 0x84, 0x24, 0x3b, 0x57, 0xd1, 0x76, 0x6b, 0xa8, 0xfa, 0xfc, 0x01, 0x87, 0xd1, 0x55, 0x97,
0xbb, 0x94, 0x2d, 0x98, 0xbc, 0x9d, 0x1d, 0x00, 0x00, 0x00, 0x10, 0x41, 0x9a, 0x08, 0xd8, 0x46,
0x03, 0x38, 0x19, 0x10, 0xaf, 0xfe, 0xda, 0xa6, 0x58, 0xce, 0x80, 0x21, 0x10, 0x04, 0x60, 0x8c,
0x1c, 0x01, 0x50, 0x4a, 0x0a, 0x8a, 0xae, 0xfe, 0x78, 0xf6, 0xe3, 0x5a, 0xb6, 0xa5, 0xdc, 0x97,
0x63, 0xe9, 0xf4, 0xfa, 0x27, 0x02, 0x9a, 0x69, 0xb2, 0xf1, 0xaa, 0xa1, 0x34, 0xd3, 0x14, 0x55,
0x55, 0x56, 0x59, 0xa6, 0x8a, 0x2e, 0x3c, 0x78, 0xfc, 0xb2, 0xe5, 0xe0, 0x00, 0x00, 0x00, 0x09,
0x41, 0x9e, 0x10, 0x4e, 0x21, 0x5f, 0x00, 0x05, 0x3d, 0x21, 0x10, 0x04, 0x60, 0x8c, 0x1c, 0x01,
0x3c, 0x88, 0xda, 0x65, 0x91, 0x76, 0x49, 0xc1, 0x46, 0x9f, 0x8f, 0x3f, 0xd7, 0xc7, 0xef, 0xe7,
0xf4, 0xf5, 0xfb, 0xf9, 0xff, 0x5b, 0xfc, 0x79, 0x58, 0xf5, 0x63, 0xc0, 0x06, 0x3d, 0x58, 0xf2,
0x00, 0x78, 0x8f, 0x8e, 0xe0, 0xa5, 0x35, 0x96, 0xcb, 0x3d, 0xc2, 0x8b, 0xdf, 0xec, 0xa4, 0x58,
0x76, 0x3f, 0x96, 0xcb, 0xeb, 0x9e, 0xb4, 0x58, 0xb5, 0x49, 0xfc, 0xdb, 0x74, 0x88, 0x43, 0x13,
0x3e, 0x51, 0x32, 0xcb, 0x9e, 0xb3, 0x81, 0x3c, 0x21, 0x10, 0x04, 0x60, 0x8c, 0x1c, 0x01, 0x3a,
0xea, 0x0a, 0x8c, 0x9f, 0xb7, 0xf5, 0xfd, 0x7a, 0xbe, 0x3c, 0xea, 0xfa, 0x9a, 0x9a, 0x6a, 0xc1,
0xfe, 0x0c, 0x97, 0xdc, 0xf8, 0x6d, 0xb3, 0x0f, 0x7d, 0x00, 0x7c, 0x66, 0x03, 0x3e, 0x80, 0x32,
0x7c, 0x01, 0x9f, 0xdc, 0x0f, 0x8c, 0xdc, 0x00, 0x00, 0x00, 0x09, 0x01, 0x9e, 0x18, 0x9a, 0x90,
0x9f, 0x00, 0x0b, 0x28, 0x21, 0x10, 0x04, 0x60, 0x8c, 0x1c, 0x01, 0x08, 0x2a, 0x06, 0x8e, 0xf9,
0xff, 0x5f, 0xfe, 0xbf, 0xf9, 0x7f, 0xa7, 0xfc, 0xca, 0xb9, 0xaa, 0xad, 0x6f, 0xdb, 0x90, 0xad,
0x6b, 0x6b, 0x78, 0xa3, 0x1d, 0x5a, 0xb5, 0x43, 0x54, 0x35, 0x15, 0x42, 0x15, 0xad, 0x5a, 0xb1,
0x56, 0xa4, 0x1c, 0x21, 0x10, 0x04, 0x60, 0x8c, 0x1c, 0x01, 0x0a, 0x2a, 0x06, 0x8d, 0xf7, 0xfd,
0xff, 0xfa, 0xff, 0xb7, 0xfd, 0x7f, 0xe6, 0x54, 0xba, 0xb7, 0x14, 0xaa, 0x1f, 0xcb, 0x2d, 0xab,
0x5b, 0xc2, 0x1b, 0x2d, 0xb3, 0x64, 0xf6, 0x46, 0x36, 0x23, 0x6f, 0x5a, 0x64, 0x6d, 0x61, 0x4e,
0x00, 0x00, 0x00, 0x1b, 0x41, 0x9a, 0x19, 0x92, 0xea, 0x04, 0x08, 0x10, 0x16, 0x88, 0x23, 0x01,
0x84, 0x0f, 0x90, 0x18, 0x40, 0xf0, 0x02, 0x15, 0xff, 0xfe, 0xda, 0xa6, 0x58, 0xce, 0x81, 0x21,
0x10, 0x04, 0x60, 0x8c, 0x1c, 0x01, 0x0c, 0x2a, 0x06, 0x8e, 0x79, 0xfe, 0x9f, 0xfd, 0x7f, 0x9f,
0xfe, 0xdf, 0xe9, 0x95, 0x77, 0x5c, 0x15, 0xd7, 0x76, 0x37, 0xd2, 0x92, 0xa4, 0xf0, 0xd3, 0x7e,
0xfd, 0xfb, 0xe9, 0x7a, 0x2f, 0xed, 0xbd, 0x29, 0x4d, 0xea, 0x5e, 0xeb, 0x11, 0xaf, 0x00, 0x00,
0x00, 0x0a, 0x41, 0x9e, 0x21, 0x0b, 0x88, 0x57, 0xff, 0x00, 0x05, 0x3d, 0x21, 0x10, 0x04, 0x60,
0x8c, 0x1c, 0x01, 0x0a, 0x2a, 0x06, 0x8e, 0x7b, 0xfe, 0xdf, 0xfe, 0xdf, 0xcf, 0xff, 0x7f, 0xf3,
0x8b, 0x92, 0xcc, 0xeb, 0x9a, 0x12, 0x94, 0xa9, 0x2b, 0xc7, 0xa6, 0xbd, 0x7a, 0xf5, 0xca, 0x53,
0x9f, 0xa7, 0xd3, 0x32, 0x49, 0xcb, 0x5c, 0x93, 0x99, 0x2e, 0x21, 0x10, 0x04, 0x60, 0x8c, 0x1c,
0x01, 0x06, 0x2a, 0x06, 0x8e, 0xfb, 0xfe, 0xff, 0xfd, 0x7f, 0xdf, 0xfe, 0x7f, 0xde, 0xea, 0x4e,
0x29, 0x2e, 0xaf, 0x6d, 0x08, 0xc8, 0xd8, 0xfe, 0x11, 0xb1, 0xf0, 0x21, 0x6b, 0x5a, 0xb5, 0x9c,
0x21, 0x6d, 0x9b, 0x36, 0x5d, 0xb5, 0x64, 0x63, 0x14, 0x7d, 0x47, 0x68, 0xbd, 0x46, 0xde, 0x00,
0x00, 0x00, 0x0a, 0x01, 0x9e, 0x29, 0x59, 0x24, 0x27, 0xff, 0x00, 0x0b, 0x29, 0x21, 0x10, 0x04,
0x60, 0x8c, 0x1c, 0x01, 0x0a, 0x2a, 0x06, 0x8e, 0x79, 0xff, 0x5f, 0xfe, 0xbf, 0xf9, 0x7f, 0x9f,
0xf6, 0xba, 0x92, 0x47, 0x5e, 0x3c, 0xf7, 0xc0, 0xd5, 0x5a, 0xda, 0xdf, 0x78, 0xc7, 0x56, 0xad,
0x4d, 0x48, 0x41, 0x54, 0x2b, 0xaa, 0xa2, 0xaa, 0x8c, 0x5c, 0x00, 0x00, 0x00, 0x1d, 0x41, 0x9a,
0x2a, 0x53, 0xea, 0x04, 0x08, 0x10, 0x16, 0xd6, 0x88, 0x23, 0x01, 0x5c, 0x05, 0x24, 0x05, 0x70,
0x14, 0x00, 0x02, 0x15, 0xff, 0xfe, 0xda, 0xa6, 0x58, 0xce, 0x81, 0x21, 0x10, 0x04, 0x60, 0x8c,
0x1c, 0x01, 0x0c, 0x2a, 0x06, 0x8d, 0xf3, 0xfd, 0xff, 0xfa, 0x7f, 0x3f, 0xf9, 0xff, 0xce, 0x2f,
0x2f, 0x85, 0xe4, 0x72, 0x3c, 0x72, 0x96, 0x2c, 0xf7, 0x84, 0x25, 0xaf, 0xf9, 0x78, 0xe6, 0x93,
0x5c, 0xe7, 0xd3, 0x59, 0xc1, 0x29, 0x49, 0x62, 0x5c, 0x21, 0x10, 0x04, 0x60, 0x8c, 0x1c, 0x01,
0x0c, 0x2a, 0x06, 0x8e, 0x79, 0xfe, 0xdf, 0xfe, 0xdf, 0xbf, 0xff, 0x8f, 0xfa, 0x65, 0x5b, 0x4e,
0x2b, 0x7d, 0x6e, 0x0a, 0x52, 0x92, 0xa4, 0xe7, 0x4d, 0xfb, 0xf7, 0xaf, 0x7b, 0xdf, 0x7b, 0x7d,
0x28, 0xb2, 0x97, 0x53, 0x25, 0xd0, 0xb7, 0x00, 0x00, 0x00, 0x0a, 0x41, 0x9e, 0x31, 0xc9, 0x62,
0x15, 0xff, 0x00, 0x05, 0x3c, 0x21, 0x10, 0x04, 0x60, 0x8c, 0x1c, 0x01, 0x02, 0x2a, 0x0a, 0x8f,
0x1d, 0xff, 0x7f, 0xfe, 0xbf, 0xe3, 0xff, 0x6f, 0xf9, 0x84, 0xb6, 0xab, 0x38, 0xca, 0x68, 0x46,
0x47, 0xc3, 0xe0, 0x8d, 0x92, 0x00, 0x04, 0xc9, 0x26, 0x0e, 0xa8, 0xc6, 0xfd, 0xf7, 0xf2, 0x64,
0x4c, 0x79, 0x01, 0x08, 0xc4, 0x83, 0xc1, 0x89, 0x24, 0xad, 0x40, 0x1f, 0x80, 0x00, 0x00, 0x00,
0x0a, 0x01, 0x9e, 0x3a, 0x19, 0xa4, 0x27, 0xff, 0x00, 0x0b, 0x28, 0x21, 0x10, 0x04, 0x60, 0x8c,
0x1c, 0x01, 0x0a, 0x2a, 0x06, 0x8e, 0x7b, 0xfe, 0xff, 0xfd, 0x7f, 0xf2, 0xff, 0x4f, 0xf9, 0x27,
0x15, 0x75, 0xd7, 0x3a, 0x72, 0xd0, 0x8c, 0x8e, 0x40, 0xe1, 0x1b, 0x00, 0xde, 0x16, 0xb5, 0xab,
0x5e, 0x50, 0x85, 0xb6, 0x6c, 0x6c, 0xd9, 0x68, 0xec, 0x23, 0x68, 0xad, 0x72, 0x9c, 0x2e, 0x8b,
0x80, 0x21, 0x10, 0x04, 0x60, 0x8c, 0x1c, 0x01, 0x0c, 0x2a, 0x0a, 0x8c, 0xe7, 0xfd, 0x7f, 0xfa,
0xff, 0x9f, 0xfc, 0xbf, 0xe4, 0x9a, 0xc8, 0xe3, 0x9e, 0x39, 0xe0, 0x7c, 0xb6, 0x3e, 0xea, 0xd8,
0x63, 0x65, 0xcb, 0x14, 0x59, 0x62, 0x98, 0xe8, 0x40, 0x28, 0xa6, 0x42, 0x88, 0x10, 0x40, 0x04,
0x9e, 0x00, 0x00, 0x00, 0x2c, 0x65, 0x88, 0x80, 0x80, 0x02, 0xbf, 0xfe, 0xf7, 0xd4, 0xb7, 0xcc,
0xb2, 0xcd, 0xa7, 0xeb, 0x60, 0xba, 0x8f, 0x7a, 0x0e, 0x30, 0x3d, 0xb0, 0x24, 0x3b, 0x57, 0xd1,
0x76, 0x6b, 0xa8, 0xfa, 0xfc, 0x01, 0x87, 0xd1, 0x55, 0x97, 0xbb, 0x94, 0x2d, 0x98, 0xbc, 0x9d,
0x1d, 0x21, 0x10, 0x04, 0x60, 0x8c, 0x1c, 0x01, 0x0c, 0x2a, 0x06, 0x8e, 0x79, 0xfe, 0x9f, 0xfd,
0x7f, 0x9f, 0xff, 0x1f, 0xf1, 0x95, 0x35, 0x1a, 0xaa, 0xbc, 0xb1, 0x29, 0x4a, 0x92, 0xbd, 0xe5,
0xaf, 0x5c, 0xe7, 0x29, 0xcf, 0x5a, 0x7a, 0xe3, 0x29, 0x58, 0x49, 0x59, 0xa1, 0x4e, 0x21, 0x10,
0x04, 0x60, 0x8c, 0x1c, 0x01, 0x08, 0x2a, 0x06, 0x8e, 0xf9, 0xff, 0x5f, 0xff, 0x0f, 0xdf, 0xff,
0x9f, 0xf3, 0x51, 0x5d, 0x5a, 0x77, 0xc6, 0x41, 0x4a, 0x52, 0x58, 0xa7, 0x08, 0x53, 0x7e, 0xf5,
0xdb, 0xfc, 0xbe, 0x5a, 0x5e, 0x9f, 0x87, 0x96, 0xd7, 0xba, 0x8b, 0x91, 0x70, 0x00, 0x00, 0x00,
0x10, 0x41, 0x9a, 0x08, 0xd8, 0xb8, 0xe0, 0x12, 0x04, 0x2b, 0xff, 0xfe, 0xda, 0xa6, 0x58, 0xce,
0x80, 0x21, 0x10, 0x04, 0x60, 0x8c, 0x1c, 0x01, 0x06, 0x2a, 0x0a, 0x8e, 0xfb, 0xff, 0x8f, 0xff,
0x6f, 0xfa, 0xff, 0xbf, 0xfb, 0x5d, 0x5a, 0xe6, 0xaa, 0xad, 0xcb, 0x42, 0x32, 0x31, 0x27, 0x04,
0x6c, 0x02, 0x40, 0x21, 0x08, 0x5a, 0xe1, 0x31, 0xa2, 0x8a, 0x28, 0xa2, 0x08, 0x8a, 0x24, 0x45,
0x10, 0x42, 0x10, 0xc0, 0x48, 0x5b, 0x00, 0x4f, 0x00, 0x00, 0x00, 0x09, 0x41, 0x9e, 0x10, 0x4e,
0x21, 0x3f, 0x00, 0x0b, 0x28, 0x21, 0x10, 0x04, 0x60, 0x8c, 0x1c, 0x01, 0x0a, 0x2a, 0x06, 0x8e,
0x79, 0xff, 0x5f, 0xfe, 0xbf, 0xf7, 0xff, 0x6f, 0xf7, 0x89, 0x25, 0xa4, 0x6e, 0xc7, 0xf2, 0xb5,
0xab, 0x5d, 0x10, 0x85, 0xb6, 0x46, 0x3e, 0xa8, 0xf4, 0xb4, 0x51, 0x8c, 0x6d, 0xc9, 0x11, 0x61,
0x6e, 0x21, 0x10, 0x04, 0x60, 0x8c, 0x1c, 0x01, 0x0c, 0x2a, 0x06, 0x8d, 0xf7, 0xfd, 0x3f, 0xfd,
0xbf, 0x7f, 0xfe, 0x7f, 0xdb, 0x11, 0x7a, 0x6b, 0x9d, 0x66, 0x0d, 0xf4, 0xa6, 0x5d, 0xb3, 0x8f,
0x6d, 0xfb, 0xf7, 0xae, 0x53, 0x7d, 0x6f, 0x83, 0x7e, 0x9d, 0xf5, 0x85, 0x05, 0xca, 0x70, 0x00,
0x00, 0x00, 0x09, 0x01, 0x9e, 0x18, 0x9a, 0x90, 0x9f, 0x00, 0x0b, 0x29, 0x21, 0x10, 0x04, 0x60,
0x8c, 0x1c, 0x01, 0x0a, 0x2a, 0x06, 0x8e, 0xf9, 0xfe, 0xdf, 0xfe, 0xdf, 0xcf, 0xff, 0x8f, 0xf8,
0xdc, 0x69, 0x2e, 0x61, 0x4d, 0x08, 0xc8, 0xe8, 0x0e, 0x11, 0xb0, 0x4f, 0xc1, 0x29, 0x4a, 0x92,
0xbd, 0xe5, 0x2d, 0x7a, 0xd2, 0x94, 0xe5, 0xae, 0x73, 0x96, 0xb5, 0xa7, 0x31, 0x51, 0x2e, 0x00,
0x00, 0x00, 0x1b, 0x41, 0x9a, 0x19, 0x52, 0xea, 0x04, 0x08, 0x10, 0x16, 0x88, 0x23, 0x04, 0xc0,
0x36, 0x90, 0x4c, 0x03, 0x60, 0x02, 0x15, 0xff, 0xfe, 0xda, 0xa6, 0x58, 0xce, 0x81, 0x21, 0x10,
0x04, 0x60, 0x8c, 0x1c, 0x01, 0x02, 0x2a, 0x09, 0xa2, 0x77, 0x8e, 0xff, 0xbf, 0xff, 0xc5, 0xfe,
0x7f, 0xf2, 0xff, 0x7b, 0xa9, 0xaa, 0xb9, 0x26, 0xc0, 0xbc, 0x30, 0x91, 0x0c, 0x22, 0xa2, 0x83,
0x1a, 0xbe, 0x9c, 0x3a, 0x62, 0xd8, 0xb4, 0x6c, 0xba, 0x85, 0xcb, 0x70, 0x21, 0x10, 0x04, 0x60,
0x8c, 0x1c, 0x01, 0x0a, 0x2a, 0x09, 0xe2, 0x57, 0x3d, 0xff, 0x7f, 0xfe, 0xbf, 0xf9, 0x7f, 0xa7,
0xfc, 0xc3, 0x4b, 0x95, 0xbe, 0xab, 0x02, 0x10, 0x85, 0xae, 0x53, 0x1a, 0x28, 0x8a, 0x28, 0xa2,
0x8a, 0x28, 0xa2, 0x88, 0xa0, 0xab, 0x52, 0xa5, 0x4a, 0x70, 0x00, 0x00, 0x00, 0x09, 0x01, 0x9e,
0x21, 0x0b, 0x90, 0x9f, 0x00, 0x0b, 0x29, 0x21, 0x10, 0x04, 0x60, 0x8c, 0x1c, 0x01, 0x0c, 0x2a,
0x11, 0x62, 0x96, 0x73, 0xfe, 0xbf, 0xfe, 0xdf, 0xe3, 0xff, 0x6f, 0xfa, 0xd2, 0x24, 0xbe, 0x3c,
0x6b, 0x9b, 0x68, 0x46, 0x46, 0x24, 0x60, 0x8d, 0xf3, 0xf0, 0xe0, 0x98, 0x61, 0x8a, 0x3c, 0xd4,
0xa1, 0x25, 0x42, 0x57, 0xb1, 0x1b, 0x5a, 0x6c, 0x3c, 0x30, 0xa7, 0x17, 0x45, 0x88, 0xb8, 0x00,
0x00, 0x00, 0x3b, 0x41, 0x9a, 0x22, 0x12, 0xf8, 0x10, 0x29, 0x68, 0x99, 0x4c, 0x02, 0x15, 0xff,
0xe3, 0x43, 0xff, 0x9f, 0xd8, 0xff, 0xc0, 0xf8, 0xb6, 0xdf, 0xc2, 0x52, 0x94, 0x69, 0x14, 0xc9,
0x47, 0x2f, 0x8e, 0x9b, 0x7e, 0x4d, 0x5f, 0x02, 0xd9, 0x86, 0xa1, 0x7a, 0x02, 0x92, 0x01, 0xc9,
0x0c, 0x46, 0x22, 0xe8, 0xf7, 0x55, 0xf6, 0xfe, 0x47, 0xfd, 0xed, 0x00, 0x0c, 0x98, 0x21, 0x10,
0x04, 0x60, 0x8c, 0x1c, 0x01, 0x0c, 0x2a, 0x06, 0x8e, 0x79, 0xfe, 0xdf, 0xfe, 0xdf, 0xbf, 0xff,
0x8f, 0xfa, 0x60, 0xe2, 0xd7, 0xba, 0xd5, 0x0a, 0x52, 0x92, 0xa4, 0xe7, 0x4d, 0xfb, 0xf7, 0xde,
0xeb, 0xef, 0xdf, 0x4a, 0x7b, 0xb7, 0xdf, 0xca, 0xb9, 0x4b, 0x09, 0x70, 0x21, 0x10, 0x04, 0x60,
0x8c, 0x1c, 0x01, 0x06, 0x2a, 0x06, 0x8e, 0xfb, 0xfe, 0xff, 0xfd, 0x3f, 0x9f, 0xfc, 0xff, 0xda,
0x49, 0x97, 0x3a, 0xac, 0x50, 0x4a, 0x52, 0xdb, 0x96, 0xf1, 0x8e, 0xbf, 0x4f, 0xe5, 0x29, 0xa5,
0x24, 0xfd, 0x29, 0x57, 0x5c, 0xe6, 0x27, 0x34, 0x1c, 0x00, 0x00, 0x00, 0x0e, 0x41, 0x9e, 0x29,
0x89, 0x22, 0x15, 0xff, 0x1e, 0xa6, 0xf6, 0x3e, 0xe2, 0xb7, 0xf8, 0x21, 0x10, 0x04, 0x60, 0x8c,
0x1c, 0x01, 0x08, 0x2a, 0x06, 0x8e, 0x7b, 0xff, 0x5f, 0xfe, 0xbf, 0xf9, 0x7f, 0x9f, 0xf6, 0xba,
0xb6, 0x9a, 0xac, 0xb4, 0x68, 0x46, 0x47, 0xe0, 0x60, 0x8d, 0xf3, 0xc1, 0x0b, 0x5a, 0xd5, 0xaf,
0x86, 0x10, 0xb4, 0x76, 0x6c, 0xd8, 0xb4, 0x6c, 0xd9, 0x68, 0xd9, 0x68, 0xa2, 0xad, 0x91, 0xa7,
0x00, 0x00, 0x00, 0x0a, 0x01, 0x9e, 0x31, 0xd9, 0x64, 0x27, 0xff, 0x00, 0x0b, 0x29, 0x21, 0x10,
0x04, 0x60, 0x8c, 0x1c, 0x01, 0x0a, 0x2a, 0x06, 0x8d, 0xf7, 0xfd, 0xff, 0xfa, 0xff, 0xbf, 0xfc,
0xff, 0xbc, 0x8b, 0xae, 0x23, 0x9e, 0x2a, 0x87, 0xf3, 0xad, 0x6d, 0x69, 0xc6, 0x3a, 0xb5, 0x6a,
0x85, 0xd0, 0x42, 0x1a, 0xbc, 0xea, 0xd5, 0x80, 0x69, 0x12, 0xe0, 0x21, 0x10, 0x04, 0x60, 0x8c,
0x1c, 0x01, 0x0c, 0x2a, 0x06, 0x8e, 0x79, 0xfe, 0x9f, 0xfd, 0x7f, 0x9f, 0xfe, 0xff, 0xed, 0x95,
0x72, 0xea, 0xd3, 0x38, 0xc1, 0xae, 0x52, 0xa4, 0xaf, 0x09, 0x6b, 0xd7, 0xae, 0x52, 0x9c, 0xf0,
0xce, 0x72, 0xf8, 0x25, 0x6b, 0x4c, 0x5a, 0xe8, 0x5b, 0x80, 0x00, 0x00, 0x00, 0x1c, 0x41, 0x9a,
0x32, 0x51, 0x1a, 0x81, 0x02, 0x04, 0x05, 0xaa, 0x5a, 0x21, 0x30, 0xb0, 0x18, 0x50, 0xb0, 0x18,
0xc0, 0x00, 0x85, 0x7f, 0xfe, 0xda, 0xa6, 0x58, 0xce, 0x80, 0x21, 0x10, 0x04, 0x60, 0x8c, 0x1c,
0x01, 0x0a, 0x2a, 0x06, 0x8e, 0xf9, 0xfe, 0xdf, 0xfe, 0xdf, 0xcf, 0xff, 0x6f, 0xf3, 0xcc, 0x97,
0x57, 0xaa, 0xde, 0xb2, 0x85, 0x29, 0x49, 0x52, 0x79, 0xa9, 0x4b, 0xdf, 0x7d, 0x37, 0xd1, 0xbd,
0xbe, 0xea, 0x5a, 0xf7, 0x51, 0x6b, 0x94, 0xe0, 0x21, 0x10, 0x04, 0x60, 0x8c, 0x1c, 0x01, 0x04,
0x2a, 0x06, 0x8e, 0xfb, 0xff, 0xa7, 0xff, 0x4f, 0xf6, 0xff, 0xaf, 0xfc, 0xca, 0x97, 0x2e, 0x2a,
0x02, 0xb5, 0xad, 0xad, 0x78, 0xc6, 0xba, 0xb5, 0x27, 0x0a, 0xc2, 0x19, 0xb5, 0x21, 0xd7, 0x5a,
0x05, 0x67, 0x54, 0x73, 0xf0, 0x00, 0x00, 0x00, 0x2c, 0x65, 0x88, 0x81, 0x00, 0x0a, 0xff, 0xfe,
0xf7, 0xd4, 0xb7, 0xcc, 0xb2, 0xc0, 0x26, 0xfb, 0x60, 0xba, 0x8f, 0x7a, 0x0e, 0x30, 0x3d, 0x2e,
0x65, 0x3b, 0x57, 0xd1, 0x76, 0x6b, 0xa8, 0xfa, 0xfc, 0x01, 0x87, 0xd1, 0x55, 0x97, 0xbb, 0x94,
0x2d, 0x98, 0xbc, 0x9d, 0x1d, 0x21, 0x10, 0x04, 0x60, 0x8c, 0x1c, 0x01, 0x0a, 0x2a, 0x06, 0x8e,
0x79, 0xff, 0x5f, 0xfe, 0xbf, 0xf9, 0x7f, 0xa7, 0xfc, 0xc2, 0x5d, 0xcb, 0xa4, 0xc1, 0xb2, 0xd6,
0xad, 0x7e, 0x30, 0x85, 0xb6, 0x6c, 0xb6, 0xc4, 0x51, 0xd9, 0xb1, 0x68, 0xf2, 0xbe, 0x14, 0xad,
0x31, 0x97, 0x80, 0x00, 0x00, 0x00, 0x11, 0x41, 0x9a, 0x08, 0xd8, 0xf8, 0x0d, 0x20, 0x14, 0xa4,
0x2b, 0xff, 0xfe, 0xda, 0xa6, 0x58, 0xce, 0x81, 0x21, 0x10, 0x04, 0x60, 0x8c, 0x1c, 0x01, 0x0c,
0x2a, 0x09, 0xe2, 0x56, 0xf9, 0xfe, 0xff, 0xfd, 0x3f, 0x9f, 0xfc, 0xff, 0xeb, 0x89, 0x5c, 0x45,
0xed, 0x90, 0x6c, 0x9c, 0xee, 0xe0, 0xc3, 0x57, 0xf2, 0xe6, 0xe6, 0x52, 0x87, 0x18, 0x76, 0x09,
0xd5, 0x81, 0x7d, 0x37, 0x15, 0x14, 0xe0, 0x21, 0x10, 0x04, 0x60, 0x8c, 0x1c, 0x01, 0x0c, 0x2a,
0x06, 0x8e, 0x79, 0xfe, 0xdf, 0xfe, 0xdf, 0xbf, 0xff, 0x8f, 0xf9, 0xe6, 0x4a, 0x97, 0x7a, 0xe7,
0xae, 0xf4, 0xd0, 0x8c, 0x8e, 0x00, 0xc1, 0x1b, 0xe7, 0x82, 0x12, 0x94, 0xa9, 0x2b, 0xde, 0x52,
0xd7, 0xae, 0x73, 0x48, 0x9c, 0xe7, 0x39, 0xd9, 0xaf, 0x5e, 0x62, 0x73, 0x8d, 0x78, 0x00, 0x00,
0x00, 0x09, 0x41, 0x9e, 0x10, 0x5e, 0x21, 0x5f, 0x00, 0x05, 0x3c, 0x21, 0x10, 0x04, 0x60, 0x8c,
0x1c, 0x01, 0x04, 0x2a, 0x09, 0xe2, 0x57, 0x7d, 0xff, 0x7f, 0xfe, 0xbf, 0xe3, 0xff, 0x3f, 0xf6,
0xb2, 0x5c, 0xb6, 0x4a, 0xa0, 0x73, 0x9e, 0x47, 0xd8, 0x45, 0xd1, 0xd1, 0xfa, 0x5d, 0x07, 0x68,
0xcb, 0x5f, 0xea, 0x7f, 0xc1, 0x64, 0x46, 0x4b, 0x12, 0xe0, 0x00, 0x00, 0x00, 0x09, 0x01, 0x9e,
0x18, 0x9a, 0x90, 0x9f, 0x00, 0x0b, 0x29, 0x21, 0x10, 0x04, 0x60, 0x8c, 0x1c, 0x01, 0x0a, 0x2a,
0x06, 0x8e, 0x79, 0xff, 0x5f, 0xfe, 0xbf, 0xf9, 0x7f, 0xa7, 0xfb, 0xda, 0x4d, 0x55, 0xce, 0x78,
0x6d, 0xa1, 0x19, 0x1f, 0xbe, 0x42, 0x37, 0xc0, 0x24, 0x2b, 0x5a, 0xda, 0xdb, 0xa3, 0x1d, 0x5a,
0xb5, 0x41, 0x0d, 0x4a, 0xb5, 0x2b, 0x0a, 0xae, 0x82, 0xc4, 0x1c, 0x21, 0x10, 0x04, 0x60, 0x8c,
0x1c, 0x01, 0x0a, 0x2a, 0x09, 0x62, 0x96, 0xf9, 0xfe, 0xff, 0xff, 0x17, 0xfb, 0x7f, 0xdf, 0xfd,
0xae, 0xae, 0x2e, 0xd9, 0x6a, 0x1f, 0x4d, 0xce, 0xbe, 0x85, 0x11, 0x3a, 0x6a, 0x35, 0xa3, 0x18,
0xb8, 0x5a, 0x71, 0x92, 0x73, 0x1a, 0xc5, 0x38, 0x00, 0x00, 0x00, 0x1b, 0x41, 0x9a, 0x19, 0x92,
0xea, 0x04, 0x08, 0x10, 0x16, 0x88, 0x7c, 0x05, 0x90, 0x19, 0x50, 0x16, 0x40, 0x67, 0x00, 0x85,
0x7f, 0xfe, 0xda, 0xa6, 0x58, 0xce, 0x81, 0x21, 0x10, 0x04, 0x60, 0x8c, 0x1c, 0x01, 0x0c, 0x2a,
0x06, 0x8e, 0x79, 0xfe, 0xdf, 0xfe, 0xdf, 0xcf, 0xff, 0x8f, 0xf8, 0xc3, 0x59, 0xc5, 0x6b, 0x2f,
0x7a, 0x68, 0x46, 0x46, 0x24, 0xf0, 0x8d, 0xf0, 0x7f, 0x0a, 0x52, 0x92, 0xa4, 0xf0, 0xd3, 0x7e,
0xfd, 0xea, 0x5e, 0x8d, 0xe5, 0xd4, 0xde, 0x52, 0x85, 0xef, 0x17, 0x00, 0x00, 0x00, 0x0a, 0x41,
0x9e, 0x21, 0x1b, 0x88, 0x57, 0xff, 0x00, 0x05, 0x3c, 0x21, 0x10, 0x04, 0x60, 0x8c, 0x1c, 0x01,
0x08, 0x2a, 0x0a, 0x8e, 0xfb, 0xfe, 0x9f, 0xfd, 0x7f, 0x9f, 0xfe, 0x7f, 0xdf, 0x07, 0x09, 0x37,
0xe7, 0xc6, 0x82, 0x52, 0x9c, 0x1d, 0x51, 0xa0, 0x79, 0x8f, 0x31, 0x81, 0x97, 0x3c, 0xc4, 0xbc,
0xc4, 0x86, 0x62, 0xc4, 0x04, 0x06, 0x5c, 0x9e, 0x21, 0x10, 0x04, 0x60, 0x8c, 0x1c, 0x01, 0x06,
0x2a, 0x06, 0x8e, 0xfb, 0xff, 0x8f, 0xff, 0x6f, 0xfa, 0xff, 0xbf, 0xfc, 0x92, 0x5b, 0x4a, 0xd6,
0x6c, 0x5a, 0xd6, 0xad, 0x70, 0xc2, 0x1b, 0x23, 0x18, 0xfb, 0x0b, 0x46, 0xd6, 0xb2, 0xcb, 0x4e,
0x91, 0xb5, 0xc5, 0x38, 0x00, 0x00, 0x00, 0x0a, 0x01, 0x9e, 0x29, 0x59, 0x24, 0x27, 0xff, 0x00,
0x0b, 0x29, 0x21, 0x10, 0x04, 0x60, 0x8c, 0x1c, 0x01, 0x0a, 0x2a, 0x0a, 0x8e, 0x79, 0xfe, 0xff,
0xff, 0x17, 0xfd, 0xff, 0xdb, 0xfe, 0x48, 0xbd, 0x4a, 0xe7, 0xe3, 0x30, 0x71, 0x42, 0x16, 0xbb,
0x8c, 0x68, 0xa2, 0x8a, 0x28, 0x81, 0x14, 0x45, 0x12, 0x09, 0x08, 0x0a, 0xca, 0x10, 0xd7, 0x20,
0x11, 0xc0, 0x21, 0x10, 0x04, 0x60, 0x8c, 0x1c, 0x01, 0x0c, 0x2a, 0x06, 0x8d, 0xf7, 0xfd, 0x3f,
0xfd, 0xbf, 0x7f, 0xfd, 0x7f, 0xe7, 0x0a, 0xbe, 0x2a, 0xf3, 0xaf, 0x14, 0x35, 0xca, 0x58, 0xb3,
0xde, 0x12, 0xd7, 0xaf, 0x5c, 0xe6, 0x94, 0xbc, 0x7e, 0x34, 0xe5, 0x65, 0xa5, 0x38, 0xad, 0x32,
0x5c, 0x00, 0x00, 0x00, 0x1c, 0x41, 0x9a, 0x2a, 0x53, 0xea, 0x04, 0x08, 0x10, 0x16, 0xd6, 0x88,
0x6c, 0x0a, 0x40, 0x49, 0x40, 0xa4, 0x04, 0xb0, 0x00, 0x84, 0xff, 0xfe, 0xd6, 0xa5, 0x50, 0xce,
0x80, 0x21, 0x10, 0x04, 0x60, 0x8c, 0x1c, 0x01, 0x0a, 0x2a, 0x06, 0x8e, 0xf9, 0xfe, 0xdf, 0xfe,
0xdf, 0xbf, 0xff, 0xa7, 0xf8, 0xdc, 0x92, 0x71, 0x9a, 0xe7, 0x57, 0xb6, 0x84, 0x64, 0x5e, 0x41,
0x08, 0xd9, 0x1c, 0x82, 0xa5, 0x29, 0x2a, 0x4e, 0x74, 0xdf, 0x7d, 0xf4, 0xa5, 0xef, 0x4d, 0xfe,
0x5a, 0x5e, 0xf7, 0xb0, 0xa1, 0x64, 0x29, 0xc0, 0x00, 0x00, 0x00, 0x0a, 0x41, 0x9e, 0x31, 0xd9,
0x62, 0x15, 0xff, 0x00, 0x05, 0x3d, 0x21, 0x10, 0x04, 0x60, 0x8c, 0x1c, 0x01, 0x00, 0x2a, 0x0a,
0x8f, 0x5d, 0xff, 0x7f, 0xff, 0x8b, 0xfc, 0xff, 0xe5, 0xfe, 0xda, 0xa9, 0xaa, 0xb9, 0xae, 0xf5,
0xcf, 0x02, 0x49, 0x24, 0xc1, 0xd5, 0x18, 0xd2, 0x11, 0x45, 0xdd, 0xfb, 0x08, 0x88, 0x72, 0x64,
0x57, 0x3c, 0x00, 0x90, 0x20, 0x31, 0x2f, 0xf0, 0x21, 0x10, 0x04, 0x60, 0x8c, 0x1c, 0x01, 0x0a,
0x2a, 0x06, 0x8e, 0x7b, 0xfe, 0xff, 0xfd, 0x7f, 0xf2, 0xff, 0x4f, 0xf7, 0xb3, 0x46, 0x89, 0x36,
0x2d, 0x6b, 0x56, 0xbc, 0xa1, 0x0d, 0x9b, 0x36, 0x23, 0xb3, 0x65, 0xad, 0x6d, 0x88, 0x99, 0x08,
0xda, 0xbe, 0xc4, 0x65, 0xc0, 0x00, 0x00, 0x00, 0x0a, 0x01, 0x9e, 0x3a, 0x19, 0xa4, 0x27, 0xff,
0x00, 0x0b, 0x29, 0x21, 0x10, 0x04, 0x60, 0x8c, 0x1c, 0x01, 0x0c, 0x2a, 0x0a, 0x8c, 0xe7, 0xfb,
0xff, 0xf5, 0xff, 0x4f, 0xfc, 0xbf, 0xde, 0x24, 0x70, 0x9b, 0xeb, 0xb0, 0xf9, 0x3e, 0xfb, 0xb8,
0x30, 0xc6, 0xe3, 0xc7, 0xe5, 0x14, 0x42, 0x60, 0x45, 0xc5, 0x4f, 0x95, 0x4a, 0x04, 0x70, 0xa5,
0x02, 0x1c, 0x21, 0x10, 0x04, 0x60, 0x8c, 0x1c, 0x01, 0x0c, 0x2a, 0x06, 0x8e, 0x7b, 0xfe, 0x9f,
0xfe, 0xdf, 0xbf, 0xff, 0x8f, 0xfa, 0x6e, 0x45, 0xea, 0xaf, 0x35, 0xb8, 0xd0, 0x8c, 0x8f, 0x88,
0xc1, 0x1b, 0x24, 0x7e, 0x12, 0x94, 0xa9, 0x2b, 0xde, 0x52, 0x9e, 0xbd, 0x73, 0x25, 0xaf, 0x5e,
0xb9, 0xeb, 0x58, 0x91, 0x61, 0x4e, 0x21, 0x10, 0x04, 0x60, 0x8c, 0x1c, 0x01, 0x30, 0x2a, 0x0a,
0x8a, 0xad, 0xff, 0xf5, 0xfd, 0xff, 0xdf, 0x89, 0xab, 0xbb, 0xbb, 0x5c, 0x90, 0x44, 0xf2, 0xe4,
0x7d, 0x84, 0xc3, 0x2a, 0x26, 0x50, 0xa8, 0x99, 0x42, 0xa2, 0x7a, 0x85, 0x44, 0xf5, 0x13, 0xa1,
0xea, 0x15, 0x13, 0xc3, 0x80, 0x21, 0x10, 0x04, 0x60, 0x8c, 0x1c, 0x01, 0x56, 0x2a, 0x0a, 0x8a,
0xac, 0xf9, 0xeb, 0xdb, 0x57, 0x77, 0xab, 0x97, 0x24, 0x96, 0x27, 0x5b, 0xc8, 0xce, 0x4a, 0xa1,
0x91, 0x4e, 0x1e, 0x8a, 0x7a, 0x1e, 0x44, 0xf4, 0x3c, 0x89, 0xd6, 0xf2, 0x27, 0x5b, 0xc8, 0xe6,
0x5b, 0xdc
};
void Check(bool condition, const std::string& message) {
if (!condition) throw std::runtime_error(message);
}
int LumaFor(int frame) {
return 20 + frame * 7;
}
void WriteMovie() {
std::filesystem::create_directories("app0");
std::ofstream file(MoviePath, std::ios::binary | std::ios::trunc);
file.write(reinterpret_cast<const char*>(Movie.data()), static_cast<std::streamsize>(Movie.size()));
Check(static_cast<bool>(file), "cannot write the movie");
}
struct Block {
bool texture;
std::align_val_t alignment;
};
struct Allocations {
std::mutex mutex;
std::map<void*, Block> blocks;
int textures = 0;
std::optional<int> textureLimit;
};
Allocations allocations;
int ActiveTextureCountLocked() {
return static_cast<int>(std::count_if(allocations.blocks.begin(), allocations.blocks.end(), [](const auto& allocation) {
return allocation.second.texture;
}));
}
void* Allocate(std::uint32_t alignment, std::uint32_t size, bool texture) {
const auto aligned = static_cast<std::align_val_t>(std::max<std::uint32_t>(alignment, 16));
{
std::lock_guard lock(allocations.mutex);
if (texture && allocations.textureLimit && ActiveTextureCountLocked() >= *allocations.textureLimit) return nullptr;
}
void* memory = ::operator new(size, aligned);
std::lock_guard lock(allocations.mutex);
allocations.blocks[memory] = {texture, aligned};
if (texture) ++allocations.textures;
return memory;
}
void Release(void* memory, bool texture) {
std::align_val_t aligned{};
{
std::lock_guard lock(allocations.mutex);
const auto found = allocations.blocks.find(memory);
if (found == allocations.blocks.end() || found->second.texture != texture) std::abort();
aligned = found->second.alignment;
allocations.blocks.erase(found);
}
::operator delete(memory, aligned);
}
void* APS5_VABI AllocateMemory(void*, std::uint32_t alignment, std::uint32_t size) { return Allocate(alignment, size, false); }
void APS5_VABI DeallocateMemory(void*, void* memory) { Release(memory, false); }
void* APS5_VABI AllocateTexture(void*, std::uint32_t alignment, std::uint32_t size) { return Allocate(alignment, size, true); }
void APS5_VABI DeallocateTexture(void*, void* memory) { Release(memory, true); }
bool IsAllocation(const void* memory, bool texture) {
std::lock_guard lock(allocations.mutex);
const auto found = allocations.blocks.find(const_cast<void*>(memory));
return found != allocations.blocks.end() && found->second.texture == texture;
}
void SetTextureLimit(std::optional<int> limit) {
std::lock_guard lock(allocations.mutex);
allocations.textureLimit = limit;
}
int TextureCount() {
std::lock_guard lock(allocations.mutex);
return ActiveTextureCountLocked();
}
struct Events {
std::mutex mutex;
std::vector<std::pair<std::int32_t, std::int32_t>> received;
bool Seen(std::int32_t id, std::int32_t warning = 0) {
std::lock_guard lock(mutex);
return std::any_of(received.begin(), received.end(), [&](const auto& event) { return event.first == id && event.second == warning; });
}
void Clear() {
std::lock_guard lock(mutex);
received.clear();
}
};
void APS5_VABI OnEvent(void* object, std::int32_t id, std::int32_t source, void* data) {
auto* events = static_cast<Events*>(object);
if (source != 0 || (id == EventWarning) != (data != nullptr)) std::abort();
std::lock_guard lock(events->mutex);
events->received.emplace_back(id, id == EventWarning ? *static_cast<std::int32_t*>(data) : 0);
}
void APS5_VABI OnLog(void*, const char*) {}
struct HostFile {
std::ifstream stream;
std::uint64_t size = 0;
};
std::int32_t APS5_VABI OpenFile(void* object, const char* path) {
auto* file = static_cast<HostFile*>(object);
if (std::strcmp(path, "/app0/replaced.mp4") != 0) return -1;
file->stream.open(MoviePath, std::ios::binary);
file->size = std::filesystem::file_size(MoviePath);
return file->stream ? 0 : -1;
}
std::int32_t APS5_VABI CloseFile(void* object) {
static_cast<HostFile*>(object)->stream.close();
return 0;
}
std::int32_t APS5_VABI ReadFile(void* object, std::uint8_t* buffer, std::uint64_t position, std::uint32_t length) {
auto* file = static_cast<HostFile*>(object);
file->stream.clear();
file->stream.seekg(static_cast<std::streamoff>(position));
file->stream.read(reinterpret_cast<char*>(buffer), length);
return static_cast<std::int32_t>(file->stream.gcount());
}
std::uint64_t APS5_VABI FileSize(void* object) {
return static_cast<HostFile*>(object)->size;
}
bool WaitFor(const std::function<bool()>& condition, int milliseconds = 5000) {
const auto deadline = std::chrono::steady_clock::now() + std::chrono::milliseconds(milliseconds);
while (std::chrono::steady_clock::now() < deadline) {
if (condition()) return true;
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
return condition();
}
AvPlayerInitData InitData(Events* events) {
AvPlayerInitData init{};
init.memory_replacement = {nullptr, AllocateMemory, DeallocateMemory, AllocateTexture, DeallocateTexture};
init.event_replacement = {events, events ? OnEvent : nullptr};
init.num_output_video_framebuffers = 4;
init.default_language = "eng";
return init;
}
int FrameIndex(std::uint64_t timestamp) {
return static_cast<int>((timestamp * FrameRate + 500) / 1000);
}
void CheckVideoPicture(const AvPlayerFrameInfoEx& info) {
const auto& video = info.details.video;
Check(video.width == 112 && video.height == 64 && video.pitch == Pitch, "unexpected frame geometry");
Check(video.crop_left_offset == 0 && video.crop_top_offset == 0 && video.crop_right_offset == Pitch - Width && video.crop_bottom_offset == 4, "unexpected crop offsets");
Check(video.luma_bit_depth == 8 && video.chroma_bit_depth == 8, "unexpected bit depth");
Check(std::fabs(video.aspect_ratio - static_cast<float>(Width) / Height) < 0.01f, "unexpected aspect ratio");
Check(std::memcmp(video.language_code, "eng", 4) == 0, "missing video language");
const int index = FrameIndex(info.timestamp);
Check(index >= 0 && index < FrameCount, "frame timestamp out of range");
const auto* luma = static_cast<const std::uint8_t*>(info.p_data);
const auto* chroma = luma + Pitch * 64;
for (const int row : {2, 30, 57, 63}) {
Check(std::abs(luma[row * Pitch + 10] - LumaFor(index)) <= 6, "left luma mismatch for frame " + std::to_string(index));
Check(std::abs(luma[row * Pitch + 90] - (255 - LumaFor(index))) <= 6, "right luma mismatch for frame " + std::to_string(index));
}
for (const int row : {0, 15, 31}) {
Check(std::abs(chroma[row * Pitch + 20] - 128) <= 6 && std::abs(chroma[row * Pitch + 21] - 128) <= 6, "chroma mismatch");
}
}
void CheckVideoFrame(const AvPlayerFrameInfoEx& info) {
CheckVideoPicture(info);
Check(IsAllocation(info.p_data, true), "video frame is not a texture allocation");
}
int Loudest(const AvPlayerFrameInfo& info) {
const auto* samples = reinterpret_cast<const std::int16_t*>(info.p_data);
int loudest = 0;
for (std::uint32_t index = 0; index < info.details.audio.size / sizeof(std::int16_t); ++index) loudest = std::max(loudest, std::abs(static_cast<int>(samples[index])));
return loudest;
}
void CheckAudioFrame(const AvPlayerFrameInfo& info, std::uint16_t channels, const char* language) {
Check(info.details.audio.channel_count == channels && info.details.audio.sample_rate == SampleRate, "unexpected audio format");
Check(info.details.audio.size > 0 && info.details.audio.size <= 1024u * channels * 2 && info.details.audio.size % (channels * 2u) == 0, "unexpected audio size");
Check(std::memcmp(info.details.audio.language_code, language, 4) == 0, "unexpected audio language");
Check(IsAllocation(info.p_data, false), "audio frame is not a plain allocation");
}
void CheckEnglishAudio(const AvPlayerFrameInfo& info) {
CheckAudioFrame(info, 2, "eng");
Check(Loudest(info) <= 16, "the English track is not silent");
}
void CheckStreamInfo(AvPlayerInternal* player) {
Check(sceAvPlayerStreamCount(player) == 3, "unexpected stream count");
for (std::uint32_t index = 0; index < 3; ++index) {
AvPlayerStreamInfo info{};
Check(sceAvPlayerGetStreamInfo(player, index, &info) == 0, "stream info failed");
Check(info.duration >= 950 && info.duration <= 1100, "unexpected stream duration");
if (index == VideoStream) {
Check(info.type == 1, "video stream type");
Check(info.details.video.width == 112 && info.details.video.height == 64, "unexpected video stream size");
Check(std::strcmp(info.details.video.language_code, "eng") == 0, "unexpected video language");
} else {
const bool english = index == EnglishAudioStream;
Check(info.type == 0, "audio stream type");
Check(info.details.audio.channel_count == (english ? 2 : 1) && info.details.audio.sample_rate == SampleRate, "unexpected audio stream");
Check(std::strcmp(info.details.audio.language_code, english ? "eng" : "fra") == 0, "unexpected audio language");
}
}
AvPlayerStreamInfo unused{};
Check(sceAvPlayerGetStreamInfo(player, 3, &unused) == OperationFailed, "invalid stream accepted");
Check(sceAvPlayerGetStreamInfo(player, 0, nullptr) == InvalidParams, "null stream info accepted");
}
void CheckStreamInfoEx(AvPlayerInternal* player) {
for (std::uint32_t index = 0; index < 3; ++index) {
AvPlayerStreamInfoEx info{};
info.this_size = sizeof(info);
Check(sceAvPlayerGetStreamInfoEx(player, index, &info) == 0, "extended stream info failed");
Check(info.this_size == sizeof(info), "extended stream info size overwritten");
Check(info.duration >= 950 && info.duration <= 1100, "unexpected extended stream duration");
if (index == VideoStream) {
const auto& video = info.details.video;
Check(info.type == 1, "extended video stream type");
Check(video.width == 112 && video.height == 64 && video.pitch == Pitch, "unexpected extended video geometry");
Check(video.crop_left_offset == 0 && video.crop_top_offset == 0 && video.crop_right_offset == Pitch - Width && video.crop_bottom_offset == 4, "unexpected extended crop offsets");
Check(video.luma_bit_depth == 8 && video.chroma_bit_depth == 8 && !video.video_full_range_flag, "unexpected extended video format");
Check(std::fabs(video.aspect_ratio - static_cast<float>(Width) / Height) < 0.01f, "unexpected extended aspect ratio");
Check(std::memcmp(video.language_code, "eng", 4) == 0, "unexpected extended video language");
} else {
const bool english = index == EnglishAudioStream;
const auto& audio = info.details.audio;
Check(info.type == 0, "extended audio stream type");
Check(audio.channel_count == (english ? 2 : 1) && audio.sample_rate == SampleRate, "unexpected extended audio stream");
Check(std::memcmp(audio.language_code, english ? "eng" : "fra", 4) == 0, "unexpected extended audio language");
}
}
AvPlayerStreamInfoEx unused{};
Check(sceAvPlayerGetStreamInfoEx(player, 3, &unused) == OperationFailed, "invalid extended stream accepted");
Check(sceAvPlayerGetStreamInfoEx(player, 0, nullptr) == InvalidParams, "null extended stream info accepted");
Check(sceAvPlayerGetStreamInfoEx(nullptr, 0, &unused) == InvalidParams, "null player accepted");
}
void TestPlayback() {
Events events;
AvPlayerInitData init = InitData(&events);
auto* player = sceAvPlayerInit(&init);
Check(player != nullptr, "init failed");
AvPlayerPostInitData post{};
post.demux_video_buffer_size = 64 * 1024;
Check(sceAvPlayerPostInit(player, &post) == 0, "post init failed");
Check(sceAvPlayerPostInit(player, nullptr) == InvalidParams, "post init accepted null data");
Check(sceAvPlayerStreamCount(player) == OperationFailed, "stream count without a source");
Check(!sceAvPlayerIsActive(player), "active without a source");
Check(sceAvPlayerAddSource(player, "/app0/missing.mp4") == OperationFailed, "missing file accepted");
Check(sceAvPlayerAddSource(player, "/app0/avplayer.mp4") == 0, "add source failed");
Check(sceAvPlayerAddSource(player, "/app0/avplayer.mp4") == OperationFailed, "second source accepted");
Check(WaitFor([&] { return events.Seen(EventReady); }), "ready event missing");
CheckStreamInfo(player);
Check(sceAvPlayerEnableStream(player, 7) == OperationFailed, "invalid stream enabled");
Check(sceAvPlayerEnableStream(player, VideoStream) == 0, "enable video failed");
Check(sceAvPlayerEnableStream(player, EnglishAudioStream) == 0, "enable audio failed");
Check(sceAvPlayerIsActive(player), "not active before start");
Check(sceAvPlayerPause(player) == OperationFailed, "pause accepted before start");
Check(sceAvPlayerStart(player) == 0, "start failed");
Check(WaitFor([&] { return events.Seen(EventPlay); }), "play event missing");
AvPlayerFrameInfoEx frame{};
AvPlayerFrameInfo sound{};
int frames = 0;
int sounds = 0;
std::uint64_t last = 0;
Check(WaitFor([&] {
if (sceAvPlayerGetAudioData(player, &sound)) {
CheckEnglishAudio(sound);
++sounds;
}
if (sceAvPlayerGetVideoDataEx(player, &frame)) {
CheckVideoFrame(frame);
Check(frames == 0 || frame.timestamp > last, "video timestamps not increasing");
last = frame.timestamp;
++frames;
}
return frames >= 8 && sounds >= 8;
}), "playback stalled");
Check(sceAvPlayerCurrentTime(player) > 0, "clock not running");
Check(sceAvPlayerSetTrickSpeed(player, 0) == InvalidParams, "zero trick speed accepted");
Check(sceAvPlayerSetTrickSpeed(player, 100) == 0, "normal trick speed rejected");
Check(sceAvPlayerPause(player) == 0, "pause failed");
Check(WaitFor([&] { return events.Seen(EventPause); }), "pause event missing");
const auto paused = sceAvPlayerCurrentTime(player);
std::this_thread::sleep_for(std::chrono::milliseconds(100));
Check(!sceAvPlayerGetVideoDataEx(player, &frame) && !sceAvPlayerGetAudioData(player, &sound), "data delivered while paused");
Check(sceAvPlayerCurrentTime(player) == paused, "clock moved while paused");
Check(sceAvPlayerIsActive(player), "inactive while paused");
events.Clear();
Check(sceAvPlayerResume(player) == 0, "resume failed");
Check(sceAvPlayerResume(player) == OperationFailed, "resume accepted while playing");
Check(WaitFor([&] { return events.Seen(EventPlay); }), "resume event missing");
Check(sceAvPlayerJumpToTime(player, 700) == 0, "jump failed");
Check(WaitFor([&] { return events.Seen(EventWarning, JumpComplete); }), "jump completion missing");
Check(WaitFor([&] { return sceAvPlayerGetVideoDataEx(player, &frame) != 0; }), "no frame after the jump");
CheckVideoFrame(frame);
Check(frame.timestamp >= 700 && frame.timestamp <= 767, "jump landed at " + std::to_string(frame.timestamp));
events.Clear();
const auto forward = sceAvPlayerCurrentTime(player);
Check(sceAvPlayerSetTrickSpeed(player, -100) == 0, "reverse trick speed rejected");
std::this_thread::sleep_for(std::chrono::milliseconds(200));
Check(!sceAvPlayerGetVideoDataEx(player, &frame) && !sceAvPlayerGetAudioData(player, &sound), "data delivered in reverse");
const auto rewound = sceAvPlayerCurrentTime(player);
Check(rewound + 150 <= forward, "clock did not run backwards: " + std::to_string(forward) + " to " + std::to_string(rewound));
Check(sceAvPlayerSetTrickSpeed(player, 100) == 0, "forward trick speed rejected after reverse");
Check(WaitFor([&] { return sceAvPlayerGetVideoDataEx(player, &frame) != 0; }), "no frame after reverse");
CheckVideoFrame(frame);
Check(frame.timestamp + 100 >= rewound && frame.timestamp <= rewound + 100, "playback resumed at " + std::to_string(frame.timestamp) + " after rewinding to " + std::to_string(rewound));
Check(!events.Seen(EventWarning, JumpComplete), "rewinding reported a jump");
Check(sceAvPlayerSetAvSyncMode(player, 2) == InvalidParams, "invalid sync mode accepted");
Check(sceAvPlayerSetAvSyncMode(player, 1) == 0, "sync mode rejected");
Check(WaitFor([&] {
if (sceAvPlayerGetVideoDataEx(player, &frame)) {
CheckVideoFrame(frame);
last = frame.timestamp;
}
while (sceAvPlayerGetAudioData(player, &sound)) CheckEnglishAudio(sound);
return !sceAvPlayerIsActive(player);
}), "playback never ended");
Check(FrameIndex(last) >= FrameCount - 2, "last frame missing");
Check(WaitFor([&] { return events.Seen(EventStop); }), "end of stream stop event missing");
Check(sceAvPlayerCurrentTime(player) >= 950, "clock not at the end");
Check(sceAvPlayerStop(player) == 0, "stop after the end failed");
events.Clear();
Check(sceAvPlayerSetLooping(player, 1) == 0, "looping rejected");
Check(sceAvPlayerStart(player) == 0, "restart failed");
bool wrapped = false;
last = 0;
frames = 0;
Check(WaitFor([&] {
while (sceAvPlayerGetAudioData(player, &sound)) CheckEnglishAudio(sound);
if (sceAvPlayerGetVideoDataEx(player, &frame)) {
CheckVideoFrame(frame);
if (frames > 0 && frame.timestamp < last) wrapped = true;
last = frame.timestamp;
++frames;
}
return wrapped && frames > FrameCount + 5;
}), "looping never wrapped");
Check(events.Seen(EventWarning, LoopingBack), "looping warning missing");
Check(sceAvPlayerIsActive(player), "looping player went inactive");
Check(sceAvPlayerStop(player) == 0, "stop failed");
Check(sceAvPlayerStop(player) == OperationFailed, "second stop accepted");
Check(!sceAvPlayerIsActive(player), "active after stop");
Check(WaitFor([&] { return events.Seen(EventStop); }), "stop event missing");
Check(sceAvPlayerClose(player) == 0, "close failed");
Check(sceAvPlayerClose(nullptr) == InvalidParams, "null close accepted");
std::lock_guard lock(allocations.mutex);
Check(allocations.blocks.empty(), "allocations leaked");
Check(allocations.textures >= 4, "video buffers not allocated as textures");
}
void TestChangeStream() {
Events events;
AvPlayerInitData init = InitData(&events);
auto* player = sceAvPlayerInit(&init);
Check(player != nullptr, "init failed");
Check(sceAvPlayerChangeStream(player, EnglishAudioStream, FrenchAudioStream) == OperationFailed, "stream changed without a source");
Check(sceAvPlayerAddSource(player, "/app0/avplayer.mp4") == 0, "add source failed");
Check(WaitFor([&] { return events.Seen(EventReady); }), "ready event missing");
Check(sceAvPlayerChangeStream(nullptr, EnglishAudioStream, FrenchAudioStream) == InvalidParams, "null player accepted");
Check(sceAvPlayerEnableStream(player, VideoStream) == 0, "enable video failed");
Check(sceAvPlayerEnableStream(player, EnglishAudioStream) == 0, "enable audio failed");
Check(sceAvPlayerChangeStream(player, VideoStream, FrenchAudioStream) == OperationFailed, "video changed to audio");
Check(sceAvPlayerChangeStream(player, EnglishAudioStream, 9) == OperationFailed, "changed to a missing stream");
Check(sceAvPlayerChangeStream(player, FrenchAudioStream, EnglishAudioStream) == OperationFailed, "changed from a disabled stream");
Check(sceAvPlayerChangeStream(player, EnglishAudioStream, FrenchAudioStream) == 0, "change before start failed");
Check(sceAvPlayerChangeStream(player, FrenchAudioStream, EnglishAudioStream) == 0, "change back before start failed");
Check(sceAvPlayerStart(player) == 0, "start failed");
AvPlayerFrameInfo sound{};
AvPlayerFrameInfoEx frame{};
int english = 0;
Check(WaitFor([&] {
if (sceAvPlayerGetAudioData(player, &sound)) {
CheckEnglishAudio(sound);
++english;
}
return english >= 2;
}), "no English audio");
events.Clear();
Check(sceAvPlayerChangeStream(player, EnglishAudioStream, FrenchAudioStream) == 0, "change to the French track failed");
int french = 0;
int loudest = 0;
Check(WaitFor([&] {
if (sceAvPlayerGetAudioData(player, &sound)) {
if (french == 0 && std::memcmp(sound.details.audio.language_code, "eng", 4) == 0) {
CheckEnglishAudio(sound);
} else {
CheckAudioFrame(sound, 1, "fra");
loudest = std::max(loudest, Loudest(sound));
++french;
}
}
return french >= 4;
}), "no French audio after the change");
Check(loudest > 1000, "the French track decoded silent");
Check(WaitFor([&] { return sceAvPlayerGetVideoDataEx(player, &frame) != 0; }), "no video after the change");
CheckVideoFrame(frame);
Check(!events.Seen(EventWarning, JumpComplete), "changing streams reported a jump");
Check(sceAvPlayerChangeStream(player, EnglishAudioStream, FrenchAudioStream) == OperationFailed, "changed from the replaced stream");
Check(sceAvPlayerClose(player) == 0, "close failed");
std::lock_guard lock(allocations.mutex);
Check(allocations.blocks.empty(), "allocations leaked");
}
void TestExtendedExports() {
Events events;
AvPlayerInitData init = InitData(&events);
auto* player = sceAvPlayerInit(&init);
Check(player != nullptr, "init failed");
Check(sceAvPlayerSetLogCallback(reinterpret_cast<void*>(&OnLog), nullptr) == 0, "log callback rejected");
Check(sceAvPlayerSetAvailableBandwidth(player, 4000, 1000, 8000) == 0, "bandwidth rejected");
Check(sceAvPlayerSetAvailableBandwidth(nullptr, 4000, 1000, 8000) == InvalidParams, "bandwidth accepted for a null player");
Check(sceAvPlayerAddSource(player, "/app0/avplayer.mp4") == 0, "add source failed");
Check(WaitFor([&] { return events.Seen(EventReady); }), "ready event missing");
CheckStreamInfoEx(player);
Check(sceAvPlayerEnableStream(player, VideoStream) == 0, "enable video failed");
Check(sceAvPlayerStartEx(nullptr, nullptr) == InvalidParams, "start accepted a null player");
Check(sceAvPlayerStartEx(player, nullptr) == 0, "extended start failed");
Check(WaitFor([&] { return events.Seen(EventPlay); }), "play event missing");
AvPlayerFrameInfoEx frame{};
Check(WaitFor([&] { return sceAvPlayerGetVideoDataEx(player, &frame) != 0; }), "no frame after the extended start");
CheckVideoFrame(frame);
Check(sceAvPlayerClose(player) == 0, "close failed");
}
void TestWithoutAllocators() {
AvPlayerInitData init = InitData(nullptr);
init.memory_replacement = {};
auto* player = sceAvPlayerInit(&init);
Check(player != nullptr, "init without allocators failed");
Check(sceAvPlayerAddSource(player, "/app0/avplayer.mp4") == 0, "add source failed");
AvPlayerFrameInfoEx frame{};
Check(WaitFor([&] { return sceAvPlayerGetVideoDataEx(player, &frame) != 0; }), "no frame without allocators");
CheckVideoPicture(frame);
Check(!IsAllocation(frame.p_data, true) && !IsAllocation(frame.p_data, false), "frame came from a missing allocator");
Check(sceAvPlayerClose(player) == 0, "close failed");
AvPlayerInitDataEx extended{};
extended.this_size = sizeof(extended);
AvPlayerInternal* handle = nullptr;
Check(sceAvPlayerInitEx(&extended, &handle) == 0 && handle != nullptr, "extended init without allocators failed");
Check(sceAvPlayerInitEx(nullptr, &handle) == InvalidParams, "extended init accepted null data");
Check(sceAvPlayerClose(handle) == 0, "close failed");
}
void TestOptionalVideoBuffersRespectMemoryLimit() {
const auto run = [](int textureLimit, int expectedTextures) {
SetTextureLimit(textureLimit);
AvPlayerInitData init = InitData(nullptr);
init.num_output_video_framebuffers = 2;
auto* player = sceAvPlayerInit(&init);
Check(player != nullptr, "init with limited texture memory failed");
Check(sceAvPlayerAddSource(player, "/app0/avplayer.mp4") == 0, "add source with limited texture memory failed");
Check(WaitFor([&] { return TextureCount() == expectedTextures && sceAvPlayerIsActive(player); }), "unexpected optional video buffer count");
AvPlayerFrameInfoEx frame{};
Check(WaitFor([&] { return sceAvPlayerGetVideoDataEx(player, &frame) != 0; }), "no frame with limited texture memory");
CheckVideoPicture(frame);
Check(sceAvPlayerClose(player) == 0, "close with limited texture memory failed");
Check(TextureCount() == 0, "limited texture allocations were not released");
};
run(2, 2);
run(4, 2);
run(6, 6);
SetTextureLimit(std::nullopt);
}
void TestFileReplacementAutoStart() {
HostFile file;
AvPlayerInitData init = InitData(nullptr);
init.file_replacement = {&file, OpenFile, CloseFile, ReadFile, FileSize};
auto* player = sceAvPlayerInit(&init);
Check(player != nullptr, "init failed");
Check(sceAvPlayerAddSource(player, "/app0/replaced.mp4") == 0, "replaced source failed");
AvPlayerFrameInfo frame{};
Check(WaitFor([&] { return sceAvPlayerGetVideoData(player, &frame) != 0; }), "auto start never produced a frame");
Check(frame.details.video.width == 112 && frame.details.video.height == 64 && IsAllocation(frame.p_data, true), "unexpected auto start frame");
Check(sceAvPlayerClose(player) == 0, "close failed");
Check(!file.stream.is_open(), "replaced file left open");
}
void TestHandedOutFramesStayIntact() {
constexpr int Buffers = 6;
constexpr int Retained = Buffers - 2;
AvPlayerInitData init = InitData(nullptr);
init.num_output_video_framebuffers = Buffers;
auto* player = sceAvPlayerInit(&init);
Check(player != nullptr, "init failed");
Check(sceAvPlayerSetAvSyncMode(player, 1) == 0, "sync mode rejected");
Check(sceAvPlayerAddSource(player, "/app0/avplayer.mp4") == 0, "add source failed");
struct Taken {
const std::uint8_t* luma;
int index;
};
std::deque<Taken> taken;
for (int round = 0; round < 16; ++round) {
AvPlayerFrameInfoEx frame{};
Check(WaitFor([&] { return sceAvPlayerGetVideoDataEx(player, &frame) != 0; }), "no frame for round " + std::to_string(round));
CheckVideoFrame(frame);
taken.push_back({static_cast<const std::uint8_t*>(frame.p_data), FrameIndex(frame.timestamp)});
if (taken.size() > Retained) taken.pop_front();
std::this_thread::sleep_for(std::chrono::milliseconds(30));
for (std::size_t k = 0; k < taken.size(); ++k) {
for (std::size_t other = k + 1; other < taken.size(); ++other) Check(taken[k].luma != taken[other].luma, "one buffer handed out twice among the last " + std::to_string(Retained) + " frames");
const int index = taken[k].index;
Check(std::abs(taken[k].luma[30 * Pitch + 10] - LumaFor(index)) <= 6, "frame " + std::to_string(index) + " was overwritten " + std::to_string(taken.size() - 1 - k) + " frames after it was handed out");
}
}
Check(sceAvPlayerClose(player) == 0, "close failed");
}
}
int main() {
try {
WriteMovie();
TestPlayback();
TestChangeStream();
TestExtendedExports();
TestWithoutAllocators();
TestOptionalVideoBuffersRespectMemoryLimit();
TestFileReplacementAutoStart();
TestHandedOutFramesStayIntact();
std::puts("AvPlayer tests passed");
return 0;
} catch (const std::exception& error) {
std::fprintf(stderr, "%s\n", error.what());
return 1;
}
}