cellVdec: reimplement cellVdecQueryAttr()

This commit is contained in:
capriots 2026-04-25 11:14:38 +02:00 committed by Elad
parent 631e7ef979
commit 61e1c0f1fb
2 changed files with 798 additions and 262 deletions

View File

@ -30,6 +30,14 @@ extern "C"
#endif
#include "cellPamf.h"
#include "libavcdec.h"
#include "libdivx311dec.h"
#include "libdivxdec.h"
#include "libmvcdec.h"
#include "libsjvtd.h"
#include "libsmvd2.h"
#include "libsmvd4.h"
#include "libsvc1d.h"
#include "cellVdec.h"
#include <mutex>
@ -56,6 +64,7 @@ void fmt_class_string<CellVdecError>::format(std::string& out, u64 arg)
STR_CASE(CELL_VDEC_ERROR_EMPTY);
STR_CASE(CELL_VDEC_ERROR_AU);
STR_CASE(CELL_VDEC_ERROR_PIC);
STR_CASE(CELL_VDEC_ERROR_UNK);
STR_CASE(CELL_VDEC_ERROR_FATAL);
}
@ -663,279 +672,688 @@ extern void vdecEntry(ppu_thread& ppu, u32 vid)
ppu.state += cpu_flag::exit;
}
static error_code vdecQueryAttr(s32 type, u32 profile, u32 spec_addr /* may be 0 */, CellVdecAttr* attr)
template <VdecSceDecoderType decoder_type>
static consteval auto get_sce_decoder_ops()
{
// Write 0 at start
attr->memSize = 0;
u32 decoderVerLower;
u32 memSize = 0;
const bool new_sdk = g_ps3_process_info.sdk_ver > 0x20FFFF;
switch (type)
if constexpr (decoder_type == VdecSceDecoderType::mpeg2)
{
case CELL_VDEC_CODEC_TYPE_AVC:
{
cellVdec.warning("cellVdecQueryAttr: AVC (profile=%d)", profile);
const vm::ptr<CellVdecAvcSpecificInfo> sinfo = vm::cast(spec_addr);
if (sinfo)
{
if (sinfo->thisSize != sizeof(CellVdecAvcSpecificInfo))
{
return { CELL_VDEC_ERROR_ARG, "Invalid AVC specific info size %d", sinfo->thisSize };
}
}
// TODO: sinfo
switch (profile)
{
case CELL_VDEC_AVC_LEVEL_1P0: memSize = new_sdk ? 0x70167D : 0xA014FD ; break;
case CELL_VDEC_AVC_LEVEL_1P1: memSize = new_sdk ? 0x86CB7D : 0xB6C9FD ; break;
case CELL_VDEC_AVC_LEVEL_1P2: memSize = new_sdk ? 0x9E307D : 0xCE2D7D ; break;
case CELL_VDEC_AVC_LEVEL_1P3: memSize = new_sdk ? 0xA057FD : 0xD054FD ; break;
case CELL_VDEC_AVC_LEVEL_2P0: memSize = new_sdk ? 0xA057FD : 0xD054FD ; break;
case CELL_VDEC_AVC_LEVEL_2P1: memSize = new_sdk ? 0xE90DFD : 0x1190AFD; break;
case CELL_VDEC_AVC_LEVEL_2P2: memSize = new_sdk ? 0x14E49FD : 0x17E46FD; break;
case CELL_VDEC_AVC_LEVEL_3P0: memSize = new_sdk ? 0x155B5FD : 0x185B17D; break;
case CELL_VDEC_AVC_LEVEL_3P1: memSize = new_sdk ? 0x1CD327D : 0x1FD2AFD; break;
case CELL_VDEC_AVC_LEVEL_3P2: memSize = new_sdk ? 0x2397B7D : 0x2696F7D; break;
case CELL_VDEC_AVC_LEVEL_4P0: memSize = new_sdk ? 0x33A5FFD : 0x36A527D; break;
case CELL_VDEC_AVC_LEVEL_4P1: memSize = new_sdk ? 0x33A5FFD : 0x36A527D; break;
case CELL_VDEC_AVC_LEVEL_4P2: memSize = new_sdk ? 0x33A5FFD : 0x36A527D; break;
default: return { CELL_VDEC_ERROR_ARG, "Invalid AVC profile level %d", profile };
}
decoderVerLower = 0x11300;
break;
}
case CELL_VDEC_CODEC_TYPE_MPEG2:
{
cellVdec.warning("cellVdecQueryAttr: MPEG2 (profile=%d)", profile);
const vm::ptr<CellVdecMpeg2SpecificInfo> sinfo = vm::cast(spec_addr);
if (sinfo)
{
if (sinfo->thisSize != sizeof(CellVdecMpeg2SpecificInfo))
{
return { CELL_VDEC_ERROR_ARG, "Invalid MPEG2 specific info size %d", sinfo->thisSize };
}
}
const u32 maxDecH = sinfo ? +sinfo->maxDecodedFrameHeight : 0;
const u32 maxDecW = sinfo ? +sinfo->maxDecodedFrameWidth : 0;
switch (profile)
{
case CELL_VDEC_MPEG2_MP_LL:
{
if (maxDecW > 352 || maxDecH > 288)
{
return { CELL_VDEC_ERROR_ARG, "Invalid max decoded frame size %dx%d for profile %d", maxDecH, maxDecW, profile };
}
memSize = new_sdk ? 0x11290B : 0x2A610B;
break;
}
case CELL_VDEC_MPEG2_MP_ML:
{
if (maxDecW > 720 || maxDecH > 576)
{
return { CELL_VDEC_ERROR_ARG, "Invalid max decoded frame size %dx%d for profile %d", maxDecH, maxDecW, profile };
}
memSize = new_sdk ? 0x2DFB8B : 0x47110B;
break;
}
case CELL_VDEC_MPEG2_MP_H14:
{
if (maxDecW > 1440 || maxDecH > 1152)
{
return { CELL_VDEC_ERROR_ARG, "Invalid max decoded frame size %dx%d for profile %d", maxDecH, maxDecW, profile };
}
memSize = new_sdk ? 0xA0270B : 0xB8F90B;
break;
}
case CELL_VDEC_MPEG2_MP_HL:
{
if (maxDecW > 1920 || maxDecH > 1152)
{
return { CELL_VDEC_ERROR_ARG, "Invalid max decoded frame size %dx%d for profile %d", maxDecH, maxDecW, profile };
}
memSize = new_sdk ? 0xD2F40B : 0xEB990B;
break;
}
default: return { CELL_VDEC_ERROR_ARG, "Invalid MPEG2 profile %d", profile };
}
decoderVerLower = 0x1030000;
break;
}
case CELL_VDEC_CODEC_TYPE_MPEG4:
{
cellVdec.warning("cellVdecQueryAttr: MPEG4 (profile=%d)", profile);
const vm::ptr<CellVdecMpeg4SpecificInfo> sinfo = vm::cast(spec_addr);
if (sinfo)
{
if (sinfo->thisSize != sizeof(CellVdecMpeg4SpecificInfo))
{
return { CELL_VDEC_ERROR_ARG, "Invalid MPEG4 specific info size %d", sinfo->thisSize };
}
}
const u32 maxDecH = sinfo ? +sinfo->maxDecodedFrameHeight : 0;
const u32 maxDecW = sinfo ? +sinfo->maxDecodedFrameWidth : 0;
switch (profile)
{
case CELL_VDEC_MPEG4_SP_L1:
{
if (maxDecW > 176 || maxDecH > 144)
{
return { CELL_VDEC_ERROR_ARG, "Invalid max decoded frame size %dx%d for profile %d", maxDecH, maxDecW, profile };
}
memSize = new_sdk ? 0x8B78B : 0xBB70B;
break;
}
case CELL_VDEC_MPEG4_SP_L2:
case CELL_VDEC_MPEG4_SP_L3:
{
if (maxDecW > 352 || maxDecH > 288)
{
return { CELL_VDEC_ERROR_ARG, "Invalid max decoded frame size %dx%d for profile %d", maxDecH, maxDecW, profile };
}
memSize = new_sdk ? 0xEFE0B : 0x11FD8B;
break;
}
case CELL_VDEC_MPEG4_SP_D1_NTSC:
{
if (maxDecW > 720 || maxDecH > 480)
{
return { CELL_VDEC_ERROR_ARG, "Invalid max decoded frame size %dx%d for profile %d", maxDecH, maxDecW, profile };
}
memSize = new_sdk ? 0x22DB0B : 0x25DA8B;
break;
}
case CELL_VDEC_MPEG4_SP_VGA:
{
if (maxDecW > 640 || maxDecH > 480)
{
return { CELL_VDEC_ERROR_ARG, "Invalid max decoded frame size %dx%d for profile %d", maxDecH, maxDecW, profile };
}
memSize = new_sdk ? 0x1FC00B : 0x22BF8B;
break;
}
case CELL_VDEC_MPEG4_SP_D1_PAL:
{
if (maxDecW > 720 || maxDecH > 576)
{
return { CELL_VDEC_ERROR_ARG, "Invalid max decoded frame size %dx%d for profile %d", maxDecH, maxDecW, profile };
}
memSize = new_sdk ? 0x28570B : 0x2B568B;
break;
}
default: return { CELL_VDEC_ERROR_ARG, "Invalid MPEG4 profile %d", profile };
}
decoderVerLower = 0x1080000;
break;
}
case CELL_VDEC_CODEC_TYPE_DIVX:
{
cellVdec.warning("cellVdecQueryAttr: DivX (profile=%d)", profile);
const vm::ptr<CellVdecDivxSpecificInfo2> sinfo = vm::cast(spec_addr);
if (sinfo)
{
if (sinfo->thisSize != sizeof(CellVdecDivxSpecificInfo) && sinfo->thisSize != sizeof(CellVdecDivxSpecificInfo2))
{
return { CELL_VDEC_ERROR_ARG, "Invalid DIVX specific info size %d", sinfo->thisSize };
}
}
// TODO: sinfo
//const u32 maxDecH = sinfo ? +sinfo->maxDecodedFrameHeight : 0;
//const u32 maxDecW = sinfo ? +sinfo->maxDecodedFrameWidth : 0;
u32 nrOfBuf = sinfo && sinfo->thisSize == sizeof(CellVdecDivxSpecificInfo2) ? +sinfo->numberOfDecodedFrameBuffer : 0;
if (nrOfBuf == 0)
{
nrOfBuf = 4;
}
else if (nrOfBuf == 2)
{
if (profile != CELL_VDEC_DIVX_QMOBILE && profile != CELL_VDEC_DIVX_MOBILE)
{
return { CELL_VDEC_ERROR_ARG, "Invalid number of decoded frame buffers %d for DIVX profile %d", nrOfBuf, profile };
}
}
else if (nrOfBuf != 4 && nrOfBuf != 3)
{
return { CELL_VDEC_ERROR_ARG, "Invalid number of decoded frame buffers %d for DIVX", nrOfBuf };
}
// TODO: change memSize based on buffercount.
switch (profile)
{
case CELL_VDEC_DIVX_QMOBILE : memSize = new_sdk ? 0x11B720 : 0x1DEF30; break;
case CELL_VDEC_DIVX_MOBILE : memSize = new_sdk ? 0x19A740 : 0x26DED0; break;
case CELL_VDEC_DIVX_HOME_THEATER: memSize = new_sdk ? 0x386A60 : 0x498060; break;
case CELL_VDEC_DIVX_HD_720 : memSize = new_sdk ? 0x692070 : 0x805690; break;
case CELL_VDEC_DIVX_HD_1080 : memSize = new_sdk ? 0xD78100 : 0xFC9870; break;
default: return { CELL_VDEC_ERROR_ARG, "Invalid DIVX profile %d", profile };
}
decoderVerLower = 0x30806;
break;
}
default: return { CELL_VDEC_ERROR_ARG, "Invalid codec type %d", type };
return VDEC_SCE_DECODER_OPS_MPEG2;
}
if constexpr (decoder_type == VdecSceDecoderType::mpeg4)
{
return VDEC_SCE_DECODER_OPS_MPEG4;
}
if constexpr (decoder_type == VdecSceDecoderType::vc1)
{
return VDEC_SCE_DECODER_OPS_VC1;
}
if constexpr (decoder_type == VdecSceDecoderType::jvt)
{
return VDEC_SCE_DECODER_OPS_JVT;
}
}
template <VdecSceDecoderType decoder_type>
static std::optional<u32> get_internal_profile_level(u32 profile_level)
{
if constexpr (decoder_type == VdecSceDecoderType::jvt)
{
switch (profile_level)
{
case CELL_VDEC_AVC_LEVEL_1P0: return 1;
case CELL_VDEC_AVC_LEVEL_1P1: return 2;
case CELL_VDEC_AVC_LEVEL_1P2: return 3;
case CELL_VDEC_AVC_LEVEL_1P3: return 4;
case CELL_VDEC_AVC_LEVEL_2P0: return 5;
case CELL_VDEC_AVC_LEVEL_2P1: return 6;
case CELL_VDEC_AVC_LEVEL_2P2: return 7;
case CELL_VDEC_AVC_LEVEL_3P0: return 8;
case CELL_VDEC_AVC_LEVEL_3P1: return 9;
case CELL_VDEC_AVC_LEVEL_3P2: return 10;
case CELL_VDEC_AVC_LEVEL_4P0: return 11;
case CELL_VDEC_AVC_LEVEL_4P1: return 12;
case CELL_VDEC_AVC_LEVEL_4P2: return 13;
default: return std::nullopt;
}
}
constexpr u32 max_profile_level =
decoder_type == VdecSceDecoderType::mpeg2 ? CELL_VDEC_MPEG2_MP_HL
: decoder_type == VdecSceDecoderType::mpeg4 ? CELL_VDEC_MPEG4_SP_D1_PAL
: decoder_type == VdecSceDecoderType::jvt ? CELL_VDEC_VC1_AP_L4 : 0;
return profile_level <= max_profile_level ? static_cast<std::optional<u32>>(profile_level + 1) : std::nullopt;
}
static bool check_frame_dimensions_mpeg2(u32 profile_level, be_t<u32>& max_decoded_frame_width, be_t<u32>& max_decoded_frame_height)
{
const auto [max_width, max_height] = [&] -> std::pair<u32, u32>
{
switch (profile_level)
{
case SMVD2_MP_LL: return { 352, 288 };
case SMVD2_MP_ML: return { 720, 576 };
case SMVD2_MP_H14: return { 1440, 1152 };
case SMVD2_MP_HL: return { 1920, 1152 };
default: fmt::throw_exception("Invalid profile level");
}
}();
if (max_decoded_frame_width > max_width || max_decoded_frame_height > max_height)
{
return false;
}
if (!max_decoded_frame_width || !max_decoded_frame_height)
{
max_decoded_frame_width = max_width;
max_decoded_frame_height = max_height;
}
return true;
}
template <VdecSceDecoderType decoder_type>
static u32 get_version(ppu_thread& ppu)
{
const vm::var<u32> version;
get_sce_decoder_ops<decoder_type>().get_version_number(ppu, +version);
return *version;
}
template <VdecSceDecoderType decoder_type, typename specific_info_t>
static error_code get_memory_size(ppu_thread& ppu, u32 profile_level, const vm::var<u32>& mem_size, const specific_info_t* codec_specific_info)
{
const std::optional profile_level_internal = get_internal_profile_level<decoder_type>(profile_level);
if (!profile_level_internal)
{
return CELL_VDEC_ERROR_ARG;
}
const auto ret = [&] -> std::optional<error_code>
{
if (!codec_specific_info)
{
return get_sce_decoder_ops<decoder_type>().get_memory_size(ppu, +mem_size, *profile_level_internal);
}
if (codec_specific_info->thisSize != sizeof(specific_info_t))
{
return std::nullopt;
}
if constexpr (decoder_type == VdecSceDecoderType::mpeg4)
{
if (codec_specific_info->maxDecodedFrameWidth == 0 || codec_specific_info->maxDecodedFrameHeight == 0)
{
return ppu_execute<&smvd4GetMemorySize>(ppu, +mem_size, *profile_level_internal);
}
}
const auto params
{
[&]
{
if constexpr (decoder_type == VdecSceDecoderType::mpeg2)
{
return vm::var<Smvd2Params>
{{
.unk1 = 5,
.unk2 = 2,
.unk3 = 0,
.max_decoded_frame_width = static_cast<u32>(codec_specific_info->maxDecodedFrameWidth),
.max_decoded_frame_height = static_cast<u32>(codec_specific_info->maxDecodedFrameHeight)
}};
}
if constexpr (decoder_type == VdecSceDecoderType::mpeg4)
{
return vm::var<Smvd4Params>
{{
.unk1 = 5,
.unk2 = 1,
.unk3 = 0,
.max_decoded_frame_width = static_cast<u32>(codec_specific_info->maxDecodedFrameWidth),
.max_decoded_frame_height = static_cast<u32>(codec_specific_info->maxDecodedFrameHeight)
}};
}
if constexpr (decoder_type == VdecSceDecoderType::vc1)
{
return vm::var<Svc1dParams>
{{
.unk1 = 5,
.unk2 = 3,
.unk3 = 0,
.max_decoded_frame_width = codec_specific_info->maxDecodedFrameWidth != 0 ? static_cast<u32>(codec_specific_info->maxDecodedFrameWidth) : umax,
.max_decoded_frame_height = codec_specific_info->maxDecodedFrameHeight != 0 ? static_cast<u32>(codec_specific_info->maxDecodedFrameHeight) : umax
}};
}
if constexpr (decoder_type == VdecSceDecoderType::jvt)
{
return vm::var<SjvtdParams>
{{
.unk1 = 6,
.unk2 = 4,
.unk3 = 0,
.max_decoded_frame_width = codec_specific_info->maxDecodedFrameWidth != 0 ? static_cast<s32>(codec_specific_info->maxDecodedFrameWidth) : -1,
.max_decoded_frame_height = codec_specific_info->maxDecodedFrameHeight != 0 ? static_cast<s32>(codec_specific_info->maxDecodedFrameHeight) : -1,
.enable_deblocking_filter = !codec_specific_info->disableDeblockingFilter,
.unk = umax,
.number_of_decoded_frame_buffer = codec_specific_info->numberOfDecodedFrameBuffer != 0 ? codec_specific_info->numberOfDecodedFrameBuffer : umax
}};
}
}()
};
if constexpr (decoder_type == VdecSceDecoderType::mpeg2)
{
if (!check_frame_dimensions_mpeg2(*profile_level_internal, params->max_decoded_frame_width, params->max_decoded_frame_height))
{
return std::nullopt;
}
}
return get_sce_decoder_ops<decoder_type>().get_memory_size_2(ppu, +mem_size, *profile_level_internal, +params);
}();
if (!ret)
{
return CELL_VDEC_ERROR_ARG;
}
if ((*ret & 0xffffffc0) == VDEC_SCE_DECODER_ERROR_BASE_MAP[std::to_underlying(decoder_type)])
{
return CELL_VDEC_ERROR_UNK;
}
if (*ret != CELL_OK || *mem_size > VDEC_SCE_DECODER_MAX_MEM_SIZE_MAP[std::to_underlying(decoder_type)][*profile_level_internal - 1])
{
return CELL_VDEC_ERROR_FATAL;
}
attr->decoderVerLower = decoderVerLower;
attr->decoderVerUpper = 0x4840010;
attr->memSize = !spec_addr ? ensure(memSize) : 4 * 1024 * 1024;
attr->cmdDepth = 4;
return CELL_OK;
}
error_code cellVdecQueryAttr(vm::cptr<CellVdecType> type, vm::ptr<CellVdecAttr> attr)
template <VdecSceDecoderType decoder_type, typename specific_info_t>
static error_code query_attr_mpeg_vc1_jvt(ppu_thread& ppu, VdecDecoderAttr& attr, u32 profile_level, const void* codec_specific_info)
{
cellVdec.warning("cellVdecQueryAttr(type=*0x%x, attr=*0x%x)", type, attr);
attr.mem_size = 0;
attr.unk2 = 0x20;
if (!type || !attr)
const vm::var<u32> mem_size;
if (const error_code ret = get_memory_size<decoder_type, specific_info_t>(ppu, profile_level, mem_size, static_cast<const specific_info_t*>(codec_specific_info)); ret != CELL_OK)
{
return { CELL_VDEC_ERROR_ARG, "type=%d, attr=%d", !!type, !!attr };
return ret;
}
return vdecQueryAttr(type->codecType, type->profileLevel, 0, attr.get_ptr());
attr.mem_size = *mem_size + (5 * 0x50) + 0xa8 + 0x41580 + 0x3493;
attr.unk1 = VDEC_SCE_DECODER_UNK_MAP[std::to_underlying(decoder_type)].unk4;
attr.cmd_depth = 4;
attr.decoder_version = get_version<decoder_type>(ppu);
return CELL_OK;
}
error_code cellVdecQueryAttrEx(vm::cptr<CellVdecTypeEx> type, vm::ptr<CellVdecAttr> attr)
template <bool is_mvc>
static error_code query_memory_avc_mvc(ppu_thread& ppu, u32 profile_level, vm::ptr<AvcDecAttr> attr, const CellVdecAvcSpecificInfo* avc_specific_info)
{
cellVdec.warning("cellVdecQueryAttrEx(type=*0x%x, attr=*0x%x)", type, attr);
const vm::var<AvcDecParams> avc_params{ AvcDecParams{} };
switch (profile_level)
{
case CELL_VDEC_AVC_LEVEL_1P0:
case CELL_VDEC_AVC_LEVEL_1P1:
case CELL_VDEC_AVC_LEVEL_1P2:
case CELL_VDEC_AVC_LEVEL_1P3:
case CELL_VDEC_AVC_LEVEL_2P0:
case CELL_VDEC_AVC_LEVEL_2P1:
case CELL_VDEC_AVC_LEVEL_2P2:
case CELL_VDEC_AVC_LEVEL_3P0:
case CELL_VDEC_AVC_LEVEL_3P1:
case CELL_VDEC_AVC_LEVEL_3P2:
case CELL_VDEC_AVC_LEVEL_4P0:
case CELL_VDEC_AVC_LEVEL_4P1:
case CELL_VDEC_AVC_LEVEL_4P2:
avc_params->profile_level = profile_level;
break;
case CELL_VDEC_AVC_LEVEL_UNK:
avc_params->profile_level = CELL_VDEC_AVC_LEVEL_4P2;
avc_params->disable_deblocking_filter = true;
break;
default:
return CELL_VDEC_ERROR_ARG;
}
if (avc_specific_info)
{
if (avc_specific_info->thisSize != sizeof(CellVdecAvcSpecificInfo))
{
return CELL_VDEC_ERROR_ARG;
}
avc_params->disable_deblocking_filter = avc_specific_info->disableDeblockingFilter;
avc_params->number_of_decoded_frame_buffer = avc_specific_info->numberOfDecodedFrameBuffer;
if (avc_specific_info->maxDecodedFrameWidth != 0 && avc_specific_info->maxDecodedFrameHeight != 0)
{
avc_params->max_decoded_frame_width = utils::aligned_div<u16>(avc_specific_info->maxDecodedFrameWidth, 0x10);
avc_params->max_decoded_frame_height = utils::aligned_div<u16>(avc_specific_info->maxDecodedFrameHeight, 0x10);
}
}
if (ppu_execute<is_mvc ? mvcDecQueryMemory : avcDecQueryMemory>(ppu, +avc_params, attr) != CELL_OK)
{
return CELL_VDEC_ERROR_FATAL;
}
const u32 max_mem_size = [&]
{
switch (profile_level)
{
case CELL_VDEC_AVC_LEVEL_1P0: return is_mvc ? 0x9e2980 : 0x6b6d80;
case CELL_VDEC_AVC_LEVEL_1P1: return is_mvc ? 0xbec300 : 0x822280;
case CELL_VDEC_AVC_LEVEL_1P2: return is_mvc ? 0xe44d00 : 0x998600;
case CELL_VDEC_AVC_LEVEL_1P3: // Same as below
case CELL_VDEC_AVC_LEVEL_2P0: return is_mvc ? 0xe72d00 : 0x9bad80;
case CELL_VDEC_AVC_LEVEL_2P1: return is_mvc ? 0x13f1380 : 0xe46380;
case CELL_VDEC_AVC_LEVEL_2P2: return is_mvc ? 0x1aba180 : 0x1499f80;
case CELL_VDEC_AVC_LEVEL_3P0: return is_mvc ? 0x1b58780 : 0x1510a00;
case CELL_VDEC_AVC_LEVEL_3P1: return is_mvc ? 0x28c6700 : 0x1c88380;
case CELL_VDEC_AVC_LEVEL_3P2: return is_mvc ? 0x3208700 : 0x234c800;
case CELL_VDEC_AVC_LEVEL_4P0: // Same as below
case CELL_VDEC_AVC_LEVEL_4P1:
case CELL_VDEC_AVC_LEVEL_4P2:
case CELL_VDEC_AVC_LEVEL_UNK: return is_mvc ? 0x487ed00 : 0x335ab00;
default: std::unreachable(); // Already checked above
}
}();
return attr->mem_size > max_mem_size ? static_cast<error_code>(CELL_VDEC_ERROR_FATAL) : CELL_OK;
}
template <bool is_mvc>
static error_code query_attr_avc_mvc(ppu_thread& ppu, VdecDecoderAttr& attr, u32 profile_level, const void* avc_specific_info)
{
const vm::var<AvcDecAttr> codec_attr;
if (const error_code ret = query_memory_avc_mvc<is_mvc>(ppu, profile_level, codec_attr, static_cast<const CellVdecAvcSpecificInfo*>(avc_specific_info)); ret != CELL_OK)
{
return ret;
}
attr.mem_size = 0x41580 + codec_attr->mem_size + (is_mvc ? 0x12fd : 0x89fd);
attr.unk1 = is_mvc ? 0x214 : 0x244;
attr.unk2 = 0x30;
const vm::var<u32> version;
ppu_execute<is_mvc ? mvcDecGetVersion : avcDecGetVersion>(ppu, +version);
attr.decoder_version = *version;
const vm::var<u32[]> unk{ 2 };
ppu_execute<is_mvc ? mvcDecQueryCharacteristics : avcDecQueryCharacteristics>(ppu, +unk);
const vm::var<s32> sdk_ver;
ensure(sys_process_get_sdk_version(sys_process_getpid(), sdk_ver) == CELL_OK); // Not checked on LLE
attr.cmd_depth = unk[0] - (*sdk_ver >= 0x130000);
return CELL_OK;
}
template <bool divx311>
static error_code query_attr_divx(ppu_thread& ppu, VdecDecoderAttr& attr, u32 profile_level, const void* divx_specific_info)
{
attr.mem_size = 0;
attr.unk2 = 0x20;
const vm::var<DivxDecParams> params;
if constexpr (!divx311)
{
switch (profile_level)
{
case CELL_VDEC_DIVX_QMOBILE: *params = { .profile_level = 0, .max_decoded_frame_width = 176, .max_decoded_frame_height = 144, .number_of_decoded_frame_buffer = 4 }; break;
case CELL_VDEC_DIVX_MOBILE: *params = { .profile_level = 0, .max_decoded_frame_width = 352, .max_decoded_frame_height = 288, .number_of_decoded_frame_buffer = 4 }; break;
case CELL_VDEC_DIVX_HOME_THEATER: *params = { .profile_level = 0, .max_decoded_frame_width = 720, .max_decoded_frame_height = 576, .number_of_decoded_frame_buffer = 4 }; break;
case CELL_VDEC_DIVX_HD_720: *params = { .profile_level = 0, .max_decoded_frame_width = 1280, .max_decoded_frame_height = 720, .number_of_decoded_frame_buffer = 4 }; break;
case CELL_VDEC_DIVX_HD_1080: *params = { .profile_level = 0, .max_decoded_frame_width = 1920, .max_decoded_frame_height = 1088, .number_of_decoded_frame_buffer = 4 }; break;
default: return CELL_VDEC_ERROR_ARG;
}
}
else
{
*params = { .profile_level = 0, .max_decoded_frame_width = 720, .max_decoded_frame_height = 576, .number_of_decoded_frame_buffer = 2 };
}
if (divx_specific_info)
{
const auto* const _divx_specific_info = static_cast<const CellVdecDivxSpecificInfo2*>(divx_specific_info);
if (((divx311 || _divx_specific_info->thisSize != sizeof(CellVdecDivxSpecificInfo)) && _divx_specific_info->thisSize != sizeof(CellVdecDivxSpecificInfo2))
|| _divx_specific_info->maxDecodedFrameWidth > (divx311 ? 1920 : +params->max_decoded_frame_width) || _divx_specific_info->maxDecodedFrameHeight > (divx311 ? 1088 : +params->max_decoded_frame_height)
|| (divx311 && _divx_specific_info->numberOfDecodedFrameBuffer != 0 && _divx_specific_info->numberOfDecodedFrameBuffer != 2))
{
return CELL_VDEC_ERROR_ARG;
}
if (_divx_specific_info->maxDecodedFrameWidth != 0 && _divx_specific_info->maxDecodedFrameHeight != 0)
{
params->max_decoded_frame_width = _divx_specific_info->maxDecodedFrameWidth;
params->max_decoded_frame_height = _divx_specific_info->maxDecodedFrameHeight;
}
if (_divx_specific_info->thisSize == sizeof(CellVdecDivxSpecificInfo2) && _divx_specific_info->numberOfDecodedFrameBuffer != 0)
{
params->number_of_decoded_frame_buffer = _divx_specific_info->numberOfDecodedFrameBuffer;
}
}
const vm::var<u32> mem_size;
const vm::var<u32> decoder_version;
if (ppu_execute<divx311 ? &divx311DecQueryAttr : &divxDecQueryAttr>(ppu, +params, +mem_size, +decoder_version) != CELL_OK)
{
return CELL_VDEC_ERROR_ARG;
}
if (divx311 && profile_level != CELL_VDEC_DIVX_UNK)
{
return CELL_VDEC_ERROR_ARG;
}
const u32 max_mem_size = [&]
{
switch (profile_level)
{
case CELL_VDEC_DIVX_QMOBILE: return 0x1577be;
case CELL_VDEC_DIVX_MOBILE: return 0x1e675e;
case CELL_VDEC_DIVX_HOME_THEATER: return 0x4108de;
case CELL_VDEC_DIVX_HD_720: return 0x77df0e;
case CELL_VDEC_DIVX_HD_1080: return 0xf420fe;
case CELL_VDEC_DIVX_UNK: return 0x1ce600;
default: std::unreachable(); // Already checked above
}
}();
if (*mem_size > max_mem_size)
{
return CELL_VDEC_ERROR_ARG;
}
attr.mem_size = 0x41580 + (5 * 0x50) + 0xa8 + *mem_size + (divx311 ? 0x3500 : 0x34f8);
attr.unk1 = divx311 ? 0x18 : 0x20;
attr.cmd_depth = 4;
attr.decoder_version = *decoder_version;
return CELL_OK;
}
static inline bool check_codec_type(u32 codec_type)
{
return codec_type <= CELL_VDEC_CODEC_TYPE_MPEG4 || (codec_type < CELL_VDEC_CODEC_TYPE_MAX && !(codec_type & 1));
}
static VdecDecoderSpecificOps get_decoder_specific_ops(u32 codec_type)
{
// TODO remaining functions
switch (codec_type)
{
case CELL_VDEC_CODEC_TYPE_MPEG2:
return
{
query_attr_mpeg_vc1_jvt<VdecSceDecoderType::mpeg2, CellVdecMpeg2SpecificInfo>
};
case CELL_VDEC_CODEC_TYPE_AVC:
return
{
query_attr_avc_mvc<false>
};
case CELL_VDEC_CODEC_TYPE_MPEG4:
return
{
query_attr_mpeg_vc1_jvt<VdecSceDecoderType::mpeg4, CellVdecMpeg4SpecificInfo>
};
case CELL_VDEC_CODEC_TYPE_VC1:
return
{
query_attr_mpeg_vc1_jvt<VdecSceDecoderType::vc1, CellVdecVc1SpecificInfo>
};
case CELL_VDEC_CODEC_TYPE_DIVX:
return
{
query_attr_divx<false>
};
case CELL_VDEC_CODEC_TYPE_JVT:
return
{
query_attr_mpeg_vc1_jvt<VdecSceDecoderType::jvt, CellVdecAvcSpecificInfo>
};
case CELL_VDEC_CODEC_TYPE_DIVX3_11:
return
{
query_attr_divx<true>
};
case CELL_VDEC_CODEC_TYPE_MVC:
case CELL_VDEC_CODEC_TYPE_MVC2:
return
{
query_attr_avc_mvc<true>
};
default:
fmt::throw_exception("Invalid codec type");
}
}
static inline u32 get_unk_size_2(u32 unk)
{
return (unk * 0x14) + 0x14;
}
static inline u32 get_unk_size(u32 unk1, u32 unk2)
{
return ((unk2 + 0x90) * unk1) + (get_unk_size_2(unk1) * 2) + 0x40;
}
error_code cellVdecQueryAttr(ppu_thread& ppu, vm::cptr<CellVdecType> type, vm::ptr<CellVdecAttr> attr)
{
cellVdec.notice("cellVdecQueryAttr(type=*0x%x, attr=*0x%x)", type, attr);
if (!type)
{
return CELL_VDEC_ERROR_ARG;
}
const vm::var<CellVdecTypeEx> type_ex{{ .codecType = type->codecType, .profileLevel = type->profileLevel, .codecSpecificInfo = vm::null }};
return cellVdecQueryAttrEx(ppu, type_ex, attr);
}
error_code cellVdecQueryAttrEx(ppu_thread& ppu, vm::cptr<CellVdecTypeEx> type, vm::ptr<CellVdecAttr> attr)
{
cellVdec.notice("cellVdecQueryAttrEx(type=*0x%x, attr=*0x%x)", type, attr);
if (!type || !attr)
{
return { CELL_VDEC_ERROR_ARG, "type=%d, attr=%d", !!type, !!attr };
return CELL_VDEC_ERROR_ARG;
}
return vdecQueryAttr(type->codecType, type->profileLevel, type->codecSpecificInfo_addr, attr.get_ptr());
attr->memSize = 0;
if (!check_codec_type(type->codecType))
{
return CELL_VDEC_ERROR_ARG;
}
VdecDecoderAttr decoder_attr;
if (get_decoder_specific_ops(type->codecType).query_attr(ppu, decoder_attr, type->profileLevel, type->codecSpecificInfo ? type->codecSpecificInfo.get_ptr() : nullptr) != CELL_OK)
{
return CELL_VDEC_ERROR_ARG;
}
const u32 unk_size = get_unk_size(decoder_attr.unk2, decoder_attr.unk1);
u32 mem_size = decoder_attr.mem_size + 0x858 + unk_size;
const vm::var<s32> sdk_ver;
ensure(sys_process_get_sdk_version(sys_process_getpid(), sdk_ver) == CELL_OK); // Not checked on LLE
const bool new_sdk = *sdk_ver >= 0x210000;
const u32 max_mem_size = [&]
{
switch (type->codecType)
{
case CELL_VDEC_CODEC_TYPE_MPEG2:
switch (type->profileLevel)
{
case CELL_VDEC_MPEG2_MP_LL: return new_sdk ? 0x11290b : 0x2a610b;
case CELL_VDEC_MPEG2_MP_ML: return new_sdk ? 0x2dfb8b : 0x47110b;
case CELL_VDEC_MPEG2_MP_H14: return new_sdk ? 0xa0270b : 0xb8f90b;
case CELL_VDEC_MPEG2_MP_HL: return new_sdk ? 0xd2f40b : 0xeb990b;
default: std::unreachable(); // Already checked in VdecDecoderSpecificOps::query_attr
}
case CELL_VDEC_CODEC_TYPE_AVC:
switch (type->profileLevel)
{
case CELL_VDEC_AVC_LEVEL_1P0: return new_sdk ? 0x7024fd : 0xa014fd;
case CELL_VDEC_AVC_LEVEL_1P1: return new_sdk ? 0x86d9fd : 0xb6c9fd;
case CELL_VDEC_AVC_LEVEL_1P2: return new_sdk ? 0x9e3d7d : 0xce2d7d;
case CELL_VDEC_AVC_LEVEL_1P3: // Same as below
case CELL_VDEC_AVC_LEVEL_2P0: return new_sdk ? 0xa064fd : 0xd054fd;
case CELL_VDEC_AVC_LEVEL_2P1: return new_sdk ? 0xe91afd : 0x1190afd;
case CELL_VDEC_AVC_LEVEL_2P2: return new_sdk ? 0x14e56fd : 0x17e46fd;
case CELL_VDEC_AVC_LEVEL_3P0: return new_sdk ? 0x155c17d : 0x185b17d;
case CELL_VDEC_AVC_LEVEL_3P1: return new_sdk ? 0x1cd3afd : 0x1fd2afd;
case CELL_VDEC_AVC_LEVEL_3P2: return new_sdk ? 0x2397f7d : 0x2696f7d;
case CELL_VDEC_AVC_LEVEL_4P0: // Same as below
case CELL_VDEC_AVC_LEVEL_4P1:
case CELL_VDEC_AVC_LEVEL_4P2:
case CELL_VDEC_AVC_LEVEL_UNK: return new_sdk ? 0x33a627d : 0x36a527d;
default: std::unreachable(); // Already checked in VdecDecoderSpecificOps::query_attr
}
case CELL_VDEC_CODEC_TYPE_MPEG4:
switch (type->profileLevel)
{
case CELL_VDEC_MPEG4_SP_L1: return new_sdk ? 0x8b78b : 0xbb70b;
case CELL_VDEC_MPEG4_SP_L2: // Same as below
case CELL_VDEC_MPEG4_SP_L3: return new_sdk ? 0xefe0b : 0x11fd8b;
case CELL_VDEC_MPEG4_SP_D1_NTSC: return new_sdk ? 0x22db0b : 0x25da8b;
case CELL_VDEC_MPEG4_SP_VGA: return new_sdk ? 0x1fc00b : 0x22bf8b;
case CELL_VDEC_MPEG4_SP_D1_PAL: return new_sdk ? 0x28570b : 0x2b568b;
default: std::unreachable(); // Already checked in VdecDecoderSpecificOps::query_attr
}
case CELL_VDEC_CODEC_TYPE_VC1:
switch (type->profileLevel)
{
case CELL_VDEC_VC1_SP_LL: return new_sdk ? 0x1fdefb : 0x291149;
case CELL_VDEC_VC1_SP_ML: return new_sdk ? 0x3298fb : 0x42a727;
case CELL_VDEC_VC1_MP_LL: return new_sdk ? 0x3d93fb : 0x42a727;
case CELL_VDEC_VC1_MP_ML: return new_sdk ? 0x9e383b : 0xa34efd;
case CELL_VDEC_VC1_MP_HL: return new_sdk ? 0x287197b : 0x28c4363;
case CELL_VDEC_VC1_AP_L0: return new_sdk ? 0x3298fb : 0x42a727;
case CELL_VDEC_VC1_AP_L1: return new_sdk ? 0x79db3b : 0xa34efd;
case CELL_VDEC_VC1_AP_L2: return new_sdk ? 0x12073fb : 0x184b857;
case CELL_VDEC_VC1_AP_L3: return new_sdk ? 0x202887b : 0x2b562fb;
case CELL_VDEC_VC1_AP_L4: return new_sdk ? 0x3949a7b : 0x4d0a77b;
default: std::unreachable(); // Already checked in VdecDecoderSpecificOps::query_attr
}
case CELL_VDEC_CODEC_TYPE_DIVX:
switch (type->profileLevel)
{
case CELL_VDEC_DIVX_QMOBILE: return new_sdk ? 0x19e82e : 0x1def30;
case CELL_VDEC_DIVX_MOBILE: return new_sdk ? 0x22d7ce : 0x26ded0;
case CELL_VDEC_DIVX_HOME_THEATER: return new_sdk ? 0x45794e : 0x498060;
case CELL_VDEC_DIVX_HD_720: return new_sdk ? 0x7c4f7e : 0x805690;
case CELL_VDEC_DIVX_HD_1080: return new_sdk ? 0xf8916e : 0xfc9870;
default: std::unreachable(); // Already checked in VdecDecoderSpecificOps::query_attr
}
case CELL_VDEC_CODEC_TYPE_JVT:
switch (type->profileLevel)
{
case CELL_VDEC_AVC_LEVEL_1P0: return new_sdk ? 0x3ca7db : 0x3c27db;
case CELL_VDEC_AVC_LEVEL_1P1: // Same as below
case CELL_VDEC_AVC_LEVEL_1P2:
case CELL_VDEC_AVC_LEVEL_1P3:
case CELL_VDEC_AVC_LEVEL_2P0: return new_sdk ? 0x7ca15b : 0x7c215b;
case CELL_VDEC_AVC_LEVEL_2P1: return new_sdk ? 0xd0055b : 0xcf855b;
case CELL_VDEC_AVC_LEVEL_2P2: // Same as below
case CELL_VDEC_AVC_LEVEL_3P0: return new_sdk ? 0x17ee15b : 0x17e615b;
case CELL_VDEC_AVC_LEVEL_3P1: return new_sdk ? 0x328f6db : 0x32876db;
case CELL_VDEC_AVC_LEVEL_3P2: return new_sdk ? 0x44e37db : 0x44db7db;
case CELL_VDEC_AVC_LEVEL_4P0: // Same as below
case CELL_VDEC_AVC_LEVEL_4P1:
case CELL_VDEC_AVC_LEVEL_4P2:
case CELL_VDEC_AVC_LEVEL_UNK: return new_sdk ? 0x6be11db : 0x6bd91db;
default: std::unreachable(); // Already checked in VdecDecoderSpecificOps::query_attr
}
case CELL_VDEC_CODEC_TYPE_DIVX3_11:
return 0x215578;
case CELL_VDEC_CODEC_TYPE_MVC:
case CELL_VDEC_CODEC_TYPE_MVC2:
switch (type->profileLevel)
{
case CELL_VDEC_AVC_LEVEL_1P0: return 0xa2e0fd;
case CELL_VDEC_AVC_LEVEL_1P1: return 0xc37a7d;
case CELL_VDEC_AVC_LEVEL_1P2: return 0xe9047d;
case CELL_VDEC_AVC_LEVEL_1P3: // Same as below
case CELL_VDEC_AVC_LEVEL_2P0: return 0xebe47d;
case CELL_VDEC_AVC_LEVEL_2P1: return 0x143cafd;
case CELL_VDEC_AVC_LEVEL_2P2: return 0x1b058fd;
case CELL_VDEC_AVC_LEVEL_3P0: return 0x1ba3efd;
case CELL_VDEC_AVC_LEVEL_3P1: return 0x2911e7d;
case CELL_VDEC_AVC_LEVEL_3P2: return 0x3253e7d;
case CELL_VDEC_AVC_LEVEL_4P0: // Same as below
case CELL_VDEC_AVC_LEVEL_4P1:
case CELL_VDEC_AVC_LEVEL_4P2:
case CELL_VDEC_AVC_LEVEL_UNK: return 0x48ca47d;
default: std::unreachable(); // Already checked in VdecDecoderSpecificOps::query_attr
}
default:
std::unreachable(); // Already checked above
}
}();
if (mem_size > max_mem_size)
{
cellVdec.warning("The required memory size is greater than expected"); // LLE prints an error to stdout
}
else if (!new_sdk)
{
mem_size = max_mem_size;
}
*attr = { .memSize = mem_size, .cmdDepth = decoder_attr.cmd_depth, .decoderVerUpper = 0x4890000, .decoderVerLower = decoder_attr.decoder_version };
return CELL_OK;
}
template <typename T, typename U>
@ -953,23 +1371,23 @@ static error_code vdecOpen(ppu_thread& ppu, T type, U res, vm::cptr<CellVdecCb>
res->memAddr, res->ppuThreadPriority, res->spuThreadPriority, res->ppuThreadStackSize, type->codecType };
}
u32 spec_addr = 0;
const void* spec = nullptr;
if constexpr (std::is_same_v<std::decay_t<typename T::type>, CellVdecTypeEx>)
{
spec_addr = type->codecSpecificInfo_addr;
spec = type->codecSpecificInfo.get_ptr();
}
CellVdecAttr attr{};
const error_code err = vdecQueryAttr(type->codecType, type->profileLevel, spec_addr, &attr);
VdecDecoderAttr attr;
const error_code err = get_decoder_specific_ops(type->codecType).query_attr(ppu, attr, type->profileLevel, spec);
if (err != CELL_OK)
{
return err;
}
if (attr.memSize > res->memSize)
if (attr.mem_size > res->memSize)
{
return { CELL_VDEC_ERROR_ARG, "attr.memSize=%d, res->memSize=%d", attr.memSize, res->memSize };
return { CELL_VDEC_ERROR_ARG, "attr.memSize=%d, res->memSize=%d", attr.mem_size, res->memSize };
}
// Create decoder context

View File

@ -9,10 +9,11 @@ enum CellVdecError : u32
CELL_VDEC_ERROR_EMPTY = 0x80610104,
CELL_VDEC_ERROR_AU = 0x80610105,
CELL_VDEC_ERROR_PIC = 0x80610106,
CELL_VDEC_ERROR_UNK = 0x80610110,
CELL_VDEC_ERROR_FATAL = 0x80610180,
};
enum CellVdecCodecType : s32
enum CellVdecCodecType : u32
{
CELL_VDEC_CODEC_TYPE_MPEG2 = 0,
CELL_VDEC_CODEC_TYPE_AVC = 1,
@ -96,7 +97,7 @@ struct CellVdecTypeEx
{
be_t<s32> codecType; // CellVdecCodecType
be_t<u32> profileLevel;
be_t<u32> codecSpecificInfo_addr;
vm::bcptr<void> codecSpecificInfo;
};
// Library Attributes
@ -220,7 +221,7 @@ enum
CELL_VDEC_AVC_CCD_MAX = 128,
};
enum AVC_level : u8
enum AVC_level : u32
{
CELL_VDEC_AVC_LEVEL_1P0 = 10,
CELL_VDEC_AVC_LEVEL_1P1 = 11,
@ -235,6 +236,7 @@ enum AVC_level : u8
CELL_VDEC_AVC_LEVEL_4P0 = 40,
CELL_VDEC_AVC_LEVEL_4P1 = 41,
CELL_VDEC_AVC_LEVEL_4P2 = 42,
CELL_VDEC_AVC_LEVEL_UNK = 1042 // Same as 4.2, but disables the deblocking filter
};
struct CellVdecAvcSpecificInfo
@ -399,6 +401,7 @@ enum DIVX_level : u8
CELL_VDEC_DIVX_HOME_THEATER = 12,
CELL_VDEC_DIVX_HD_720 = 13,
CELL_VDEC_DIVX_HD_1080 = 14,
CELL_VDEC_DIVX_UNK = 15 // Only used for DivX version 3.11
};
struct CellVdecDivxSpecificInfo
@ -695,3 +698,118 @@ struct CellVdecMpeg4SpecificInfo
be_t<u16> maxDecodedFrameWidth;
be_t<u16> maxDecodedFrameHeight;
};
enum VC1_level
{
CELL_VDEC_VC1_SP_LL,
CELL_VDEC_VC1_SP_ML,
CELL_VDEC_VC1_MP_LL,
CELL_VDEC_VC1_MP_ML,
CELL_VDEC_VC1_MP_HL,
CELL_VDEC_VC1_AP_L0,
CELL_VDEC_VC1_AP_L1,
CELL_VDEC_VC1_AP_L2,
CELL_VDEC_VC1_AP_L3,
CELL_VDEC_VC1_AP_L4
};
struct CellVdecVc1SpecificInfo
{
be_t<u32> thisSize;
be_t<u16> maxDecodedFrameWidth;
be_t<u16> maxDecodedFrameHeight;
};
struct VdecDecoderAttr
{
be_t<u32> mem_size;
be_t<u32> unk1;
u8 cmd_depth;
be_t<u32> unk2;
be_t<u32> decoder_version;
};
struct VdecDecoderSpecificOps
{
error_code (&query_attr)(ppu_thread& ppu, VdecDecoderAttr& attr, u32 profile_level, const void* codec_specific_info);
// TODO remaining functions
};
// Abstraction layer for decoders with "S...D" in their names
enum class VdecSceDecoderType : u8
{
mpeg2,
mpeg4,
vc1,
jvt
};
template <typename param_t>
struct VdecSceDecoderOps
{
error_code (&get_memory_size)(ppu_thread& ppu, vm::ptr<u32> memSize, u32 profileLevel);
error_code (&get_memory_size_2)(ppu_thread& ppu, vm::ptr<u32> memSize, u32 profileLevel, vm::cptr<param_t> params);
error_code (&get_version_number)(ppu_thread& ppu, vm::ptr<u32> version);
// TODO remaining functions
};
struct VdecSceDecoderUnk
{
u32 unk1;
u32 unk2;
u32 unk3;
u32 unk4;
};
constexpr VdecSceDecoderOps<Smvd2Params> VDEC_SCE_DECODER_OPS_MPEG2 =
{
.get_memory_size = ppu_execute<&smvd2GetMemorySize>,
.get_memory_size_2 = ppu_execute<&smvd2GetMemorySize2>,
.get_version_number = ppu_execute<&smvd2GetVersionNumber>,
// TODO remaining functions
};
constexpr VdecSceDecoderOps<Smvd4Params> VDEC_SCE_DECODER_OPS_MPEG4 =
{
.get_memory_size = ppu_execute<&smvd4GetMemorySize>,
.get_memory_size_2 = ppu_execute<&smvd4GetMemorySize2>,
.get_version_number = ppu_execute<&smvd4GetVersionNumber>,
// TODO remaining functions
};
constexpr VdecSceDecoderOps<Svc1dParams> VDEC_SCE_DECODER_OPS_VC1 =
{
.get_memory_size = ppu_execute<&svc1dGetMemorySize>,
.get_memory_size_2 = ppu_execute<&svc1dGetMemorySize2>,
.get_version_number = ppu_execute<&svc1dGetVersionNumber>,
// TODO remaining functions
};
constexpr VdecSceDecoderOps<SjvtdParams> VDEC_SCE_DECODER_OPS_JVT =
{
.get_memory_size = ppu_execute<&sjvtdGetMemorySize>,
.get_memory_size_2 = ppu_execute<&sjvtdGetMemorySize2>,
.get_version_number = ppu_execute<&sjvtdGetVersionNumber>,
// TODO remaining functions
};
constexpr std::array VDEC_SCE_DECODER_ERROR_BASE_MAP = { 0x80615000u, 0x80615100u, 0x80615300u, 0x80615800u };
constexpr std::array VDEC_SCE_DECODER_MAX_MEM_SIZE_MAP =
{
std::array<u32, 13>{ 0xc8400, 0x295680, 0x9b8200, 0xce4f00 },
std::array<u32, 13>{ 0x44480, 0x44480, 0xa8b00, 0x1e6800, 0x1b4d00, 0x23e400 },
std::array<u32, 13>{ 0x1b44f0, 0x2dfef0, 0x38f9f0, 0x999e30, 0x2827f70, 0x2dfef0, 0x754130, 0x11bd9f0, 0x1fdee70, 0x3900070 },
std::array<u32, 13>{ 0x380fd0, 0x380fd0, 0x380fd0, 0x380fd0, 0x780950, 0xcb6d50, 0xcb6d50, 0x17a4950, 0x3245ed0, 0x4499fd0, 0x4499fd0, 0x4499fd0, 0x6b979d0 }
};
constexpr std::array VDEC_SCE_DECODER_UNK_MAP =
{
VdecSceDecoderUnk{ .unk1 = 2, .unk2 = 0, .unk4 = 0x1c8 },
VdecSceDecoderUnk{ .unk1 = 1, .unk2 = 6, .unk4 = 0x38 },
VdecSceDecoderUnk{ .unk1 = 3, .unk2 = 6, .unk4 = 0x170 },
VdecSceDecoderUnk{ .unk1 = 4, .unk2 = 6, .unk4 = 0x160 }
};
error_code cellVdecQueryAttrEx(ppu_thread& ppu, vm::cptr<CellVdecTypeEx> type, vm::ptr<CellVdecAttr> attr);