// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include "common/assert.h" #include "common/logging/log.h" #include "core/libraries/libs.h" #include "core/libraries/videodec/videodec2.h" #include "core/libraries/videodec/videodec2_impl.h" #include "core/libraries/videodec/videodec_error.h" namespace Libraries::Videodec2 { static constexpr u64 kMinimumMemorySize = 16_MB; ///> Fake minimum memory size for querying s32 PS4_SYSV_ABI sceVideodec2QueryComputeMemoryInfo(OrbisVideodec2ComputeMemoryInfo* computeMemInfo) { LOG_INFO(Lib_Vdec2, "called"); if (!computeMemInfo) { LOG_ERROR(Lib_Vdec2, "Invalid arguments"); return ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER; } if (computeMemInfo->thisSize != sizeof(OrbisVideodec2ComputeMemoryInfo)) { LOG_ERROR(Lib_Vdec2, "Invalid struct size"); return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE; } computeMemInfo->cpuGpuMemory = nullptr; computeMemInfo->cpuGpuMemorySize = kMinimumMemorySize; return ORBIS_OK; } s32 PS4_SYSV_ABI sceVideodec2AllocateComputeQueue(const OrbisVideodec2ComputeConfigInfo* computeCfgInfo, const OrbisVideodec2ComputeMemoryInfo* computeMemInfo, OrbisVideodec2ComputeQueue* computeQueue) { LOG_WARNING(Lib_Vdec2, "called"); if (!computeCfgInfo || !computeMemInfo || !computeQueue) { LOG_ERROR(Lib_Vdec2, "Invalid arguments"); return ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER; } if (computeCfgInfo->thisSize != sizeof(OrbisVideodec2ComputeConfigInfo) || computeMemInfo->thisSize != sizeof(OrbisVideodec2ComputeMemoryInfo)) { LOG_ERROR(Lib_Vdec2, "Invalid struct size"); return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE; } if (computeCfgInfo->reserved0 != 0 || computeCfgInfo->reserved1 != 0) { LOG_ERROR(Lib_Vdec2, "Invalid compute config"); return ORBIS_VIDEODEC2_ERROR_CONFIG_INFO; } if (computeCfgInfo->computePipeId > 4) { LOG_ERROR(Lib_Vdec2, "Invalid compute pipe id"); return ORBIS_VIDEODEC2_ERROR_COMPUTE_PIPE_ID; } if (computeCfgInfo->computeQueueId > 7) { LOG_ERROR(Lib_Vdec2, "Invalid compute queue id"); return ORBIS_VIDEODEC2_ERROR_COMPUTE_QUEUE_ID; } if (!computeMemInfo->cpuGpuMemory) { LOG_ERROR(Lib_Vdec2, "Invalid memory pointer"); return ORBIS_VIDEODEC2_ERROR_MEMORY_POINTER; } // The real library returns a pointer to memory inside cpuGpuMemory *computeQueue = computeMemInfo->cpuGpuMemory; return ORBIS_OK; } s32 PS4_SYSV_ABI sceVideodec2ReleaseComputeQueue(OrbisVideodec2ComputeQueue computeQueue) { LOG_INFO(Lib_Vdec2, "called"); return ORBIS_OK; } s32 PS4_SYSV_ABI sceVideodec2QueryDecoderMemoryInfo(const OrbisVideodec2DecoderConfigInfo* decoderCfgInfo, OrbisVideodec2DecoderMemoryInfo* decoderMemInfo) { LOG_INFO(Lib_Vdec2, "called"); if (!decoderCfgInfo || !decoderMemInfo) { LOG_ERROR(Lib_Vdec2, "Invalid arguments"); return ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER; } if (decoderCfgInfo->thisSize != sizeof(OrbisVideodec2DecoderConfigInfo) || decoderMemInfo->thisSize != sizeof(OrbisVideodec2DecoderMemoryInfo)) { LOG_ERROR(Lib_Vdec2, "Invalid struct size"); return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE; } decoderMemInfo->cpuMemory = nullptr; decoderMemInfo->gpuMemory = nullptr; decoderMemInfo->cpuGpuMemory = nullptr; decoderMemInfo->cpuGpuMemorySize = kMinimumMemorySize; decoderMemInfo->cpuMemorySize = kMinimumMemorySize; decoderMemInfo->gpuMemorySize = kMinimumMemorySize; decoderMemInfo->maxFrameBufferSize = kMinimumMemorySize; decoderMemInfo->frameBufferAlignment = 0x100; return ORBIS_OK; } s32 PS4_SYSV_ABI sceVideodec2CreateDecoder(const OrbisVideodec2DecoderConfigInfo* decoderCfgInfo, const OrbisVideodec2DecoderMemoryInfo* decoderMemInfo, OrbisVideodec2Decoder* decoder) { LOG_INFO(Lib_Vdec2, "called"); if (!decoderCfgInfo || !decoderMemInfo || !decoder) { LOG_ERROR(Lib_Vdec2, "Invalid arguments"); return ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER; } if (decoderCfgInfo->thisSize != sizeof(OrbisVideodec2DecoderConfigInfo) || decoderMemInfo->thisSize != sizeof(OrbisVideodec2DecoderMemoryInfo)) { LOG_ERROR(Lib_Vdec2, "Invalid struct size"); return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE; } *decoder = new VdecDecoder(*decoderCfgInfo, *decoderMemInfo); return ORBIS_OK; } s32 PS4_SYSV_ABI sceVideodec2DeleteDecoder(OrbisVideodec2Decoder decoder) { LOG_INFO(Lib_Vdec2, "called"); if (!decoder) { LOG_ERROR(Lib_Vdec2, "Invalid arguments"); return ORBIS_VIDEODEC2_ERROR_DECODER_INSTANCE; } delete decoder; return ORBIS_OK; } s32 PS4_SYSV_ABI sceVideodec2Decode(OrbisVideodec2Decoder decoder, const OrbisVideodec2InputData* inputData, OrbisVideodec2FrameBuffer* frameBuffer, OrbisVideodec2OutputInfo* outputInfo) { LOG_TRACE(Lib_Vdec2, "called"); if (!decoder) { LOG_ERROR(Lib_Vdec2, "Invalid decoder instance"); return ORBIS_VIDEODEC2_ERROR_DECODER_INSTANCE; } if (!inputData || !frameBuffer || !outputInfo) { LOG_ERROR(Lib_Vdec2, "Invalid arguments"); return ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER; } if (inputData->thisSize != sizeof(OrbisVideodec2InputData) || frameBuffer->thisSize != sizeof(OrbisVideodec2FrameBuffer)) { LOG_ERROR(Lib_Vdec2, "Invalid struct size"); return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE; } return decoder->Decode(*inputData, *frameBuffer, *outputInfo); } s32 PS4_SYSV_ABI sceVideodec2Flush(OrbisVideodec2Decoder decoder, OrbisVideodec2FrameBuffer* frameBuffer, OrbisVideodec2OutputInfo* outputInfo) { LOG_INFO(Lib_Vdec2, "called"); if (!decoder) { LOG_ERROR(Lib_Vdec2, "Invalid decoder instance"); return ORBIS_VIDEODEC2_ERROR_DECODER_INSTANCE; } if (!frameBuffer || !outputInfo) { LOG_ERROR(Lib_Vdec2, "Invalid arguments"); return ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER; } if (frameBuffer->thisSize != sizeof(OrbisVideodec2FrameBuffer) || (outputInfo->thisSize | 8) != sizeof(OrbisVideodec2OutputInfo)) { LOG_ERROR(Lib_Vdec2, "Invalid struct size"); return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE; } return decoder->Flush(*frameBuffer, *outputInfo); } s32 PS4_SYSV_ABI sceVideodec2Reset(OrbisVideodec2Decoder decoder) { LOG_INFO(Lib_Vdec2, "called"); if (!decoder) { LOG_ERROR(Lib_Vdec2, "Invalid decoder instance"); return ORBIS_VIDEODEC2_ERROR_DECODER_INSTANCE; } return decoder->Reset(); } s32 PS4_SYSV_ABI sceVideodec2GetPictureInfo(const OrbisVideodec2OutputInfo* outputInfo, void* p1stPictureInfoOut, void* p2ndPictureInfoOut) { LOG_TRACE(Lib_Vdec2, "called"); if (!outputInfo) { LOG_ERROR(Lib_Vdec2, "Invalid arguments"); return ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER; } if ((outputInfo->thisSize | 8) != sizeof(OrbisVideodec2OutputInfo)) { LOG_ERROR(Lib_Vdec2, "Invalid struct size"); return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE; } if (outputInfo->pictureCount == 0) { LOG_ERROR(Lib_Vdec2, "No picture info available"); return ORBIS_OK; } // If the game uses the older Videodec2 structs, we need to accomodate that. if (outputInfo->thisSize != sizeof(OrbisVideodec2OutputInfo)) { if (gLegacyPictureInfos.empty()) { LOG_ERROR(Lib_Vdec2, "No picture info available"); return ORBIS_OK; } if (p1stPictureInfoOut) { OrbisVideodec2LegacyAvcPictureInfo* picInfo = static_cast(p1stPictureInfoOut); if (picInfo->thisSize != sizeof(OrbisVideodec2LegacyAvcPictureInfo)) { LOG_ERROR(Lib_Vdec2, "Invalid struct size"); return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE; } *picInfo = gLegacyPictureInfos.back(); } } else { if (gPictureInfos.empty()) { LOG_ERROR(Lib_Vdec2, "No picture info available"); return ORBIS_OK; } if (p1stPictureInfoOut) { OrbisVideodec2AvcPictureInfo* picInfo = static_cast(p1stPictureInfoOut); if (picInfo->thisSize != sizeof(OrbisVideodec2AvcPictureInfo)) { LOG_ERROR(Lib_Vdec2, "Invalid struct size"); return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE; } *picInfo = gPictureInfos.back(); } } if (outputInfo->pictureCount > 1) { UNREACHABLE(); } return ORBIS_OK; } void RegisterLib(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("RnDibcGCPKw", "libSceVideodec2", 1, "libSceVideodec2", sceVideodec2QueryComputeMemoryInfo); LIB_FUNCTION("eD+X2SmxUt4", "libSceVideodec2", 1, "libSceVideodec2", sceVideodec2AllocateComputeQueue); LIB_FUNCTION("UvtA3FAiF4Y", "libSceVideodec2", 1, "libSceVideodec2", sceVideodec2ReleaseComputeQueue); LIB_FUNCTION("qqMCwlULR+E", "libSceVideodec2", 1, "libSceVideodec2", sceVideodec2QueryDecoderMemoryInfo); LIB_FUNCTION("CNNRoRYd8XI", "libSceVideodec2", 1, "libSceVideodec2", sceVideodec2CreateDecoder); LIB_FUNCTION("jwImxXRGSKA", "libSceVideodec2", 1, "libSceVideodec2", sceVideodec2DeleteDecoder); LIB_FUNCTION("852F5+q6+iM", "libSceVideodec2", 1, "libSceVideodec2", sceVideodec2Decode); LIB_FUNCTION("l1hXwscLuCY", "libSceVideodec2", 1, "libSceVideodec2", sceVideodec2Flush); LIB_FUNCTION("wJXikG6QFN8", "libSceVideodec2", 1, "libSceVideodec2", sceVideodec2Reset); LIB_FUNCTION("NtXRa3dRzU0", "libSceVideodec2", 1, "libSceVideodec2", sceVideodec2GetPictureInfo); } } // namespace Libraries::Videodec2