#include #include #include #include #include #include "SceTypes.hpp" #include "prx/libc/include/General.hpp" #include "prx/libSceAudiodec/src/Codecs.hpp" namespace { constexpr std::int32_t ERROR_API_FAIL = static_cast(0x807F0000); constexpr std::int32_t ERROR_INVALID_TYPE = static_cast(0x807F0001); constexpr std::int32_t ERROR_ARG = static_cast(0x807F0002); constexpr std::int32_t ERROR_INVALID_PARAM_SIZE = static_cast(0x807F0004); constexpr std::int32_t ERROR_INVALID_BSI_INFO_SIZE = static_cast(0x807F0005); constexpr std::int32_t ERROR_INVALID_AU_INFO_SIZE = static_cast(0x807F0006); constexpr std::int32_t ERROR_INVALID_PCM_ITEM_SIZE = static_cast(0x807F0007); constexpr std::int32_t ERROR_INVALID_CTRL_POINTER = static_cast(0x807F0008); constexpr std::int32_t ERROR_INVALID_PARAM_POINTER = static_cast(0x807F0009); constexpr std::int32_t ERROR_INVALID_BSI_INFO_POINTER = static_cast(0x807F000A); constexpr std::int32_t ERROR_INVALID_AU_INFO_POINTER = static_cast(0x807F000B); constexpr std::int32_t ERROR_INVALID_PCM_ITEM_POINTER = static_cast(0x807F000C); constexpr std::int32_t ERROR_INVALID_AU_POINTER = static_cast(0x807F000D); constexpr std::int32_t ERROR_INVALID_PCM_POINTER = static_cast(0x807F000E); constexpr std::int32_t ERROR_INVALID_HANDLE = static_cast(0x807F000F); constexpr std::int32_t ERROR_INVALID_WORD_LENGTH = static_cast(0x807F0010); constexpr std::int32_t ERROR_INVALID_AU_SIZE = static_cast(0x807F0011); constexpr std::int32_t ERROR_INVALID_PCM_SIZE = static_cast(0x807F0012); constexpr std::int32_t ERROR_M4AAC_INVALID_SAMPLING_FREQ = static_cast(0x807F0300); constexpr std::int32_t ERROR_M4AAC_INVALID_ENABLE_HEAAC = static_cast(0x807F0302); constexpr std::int32_t ERROR_M4AAC_INVALID_CONFIG_NUMBER = static_cast(0x807F0303); constexpr std::int32_t ERROR_M4AAC_INVALID_MAX_CHANNELS = static_cast(0x807F0304); constexpr std::int32_t ERROR_AT9_INVALID_CONFIG_DATA = static_cast(0x807F1000); constexpr std::uint32_t TYPE_AT9 = 1; constexpr std::uint32_t TYPE_MP3 = 2; constexpr std::uint32_t TYPE_M4AAC = 3; constexpr std::uint32_t M4AAC_CONFIG_ADTS = 1; constexpr std::uint32_t M4AAC_CONFIG_RAW = 2; constexpr std::int32_t M4AAC_RESULT_DECODE_ERROR = -4; constexpr std::int32_t M4AAC_RESULT_INSUFFICIENT_DATA = -5; static_assert(sizeof(AudiodecAuInfo) == 24 && sizeof(AudiodecPcmItem) == 24 && sizeof(AudiodecCtrl) == 32); static_assert(sizeof(AudiodecParamAt9) == 12 && sizeof(AudiodecAt9Info) == 36); static_assert(sizeof(AudiodecParamMp3) == 8 && sizeof(AudiodecMp3Info) == 20); static_assert(sizeof(AudiodecParamM4aac) == 24 && sizeof(AudiodecM4aacInfo) == 20); struct Instance { std::uint32_t codecType = 0; std::unique_ptr decoder; Audiodec::At9Format at9{}; std::uint32_t channels = 0; std::uint32_t sampleRate = 0; }; std::mutex g_mutex; std::array g_initialized{}; std::array g_instances{}; bool validType(std::uint32_t codecType) { return codecType == TYPE_AT9 || codecType == TYPE_MP3 || codecType == TYPE_M4AAC; } std::int32_t wordSizeOf(const void* param) { return static_cast(param)->i_bw_pcm; } std::int32_t validateWordSize(std::int32_t wordSize) { if (wordSize == Audiodec::WORD_SIZE_16BIT || wordSize == Audiodec::WORD_SIZE_FLOAT) return 0; if (wordSize == 0) NotImplemented_nid_no_patch("libSceAudiodec 24-bit PCM output"); return ERROR_INVALID_WORD_LENGTH; } std::int32_t validateCtrl(const AudiodecCtrl* ctrl, std::uint32_t codecType) { if (!ctrl) return ERROR_INVALID_CTRL_POINTER; if (!ctrl->pParam) return ERROR_INVALID_PARAM_POINTER; if (!ctrl->pBsiInfo) return ERROR_INVALID_BSI_INFO_POINTER; const std::uint32_t paramSize = *static_cast(ctrl->pParam); const std::uint32_t infoSize = *static_cast(ctrl->pBsiInfo); switch (codecType) { case TYPE_AT9: if (paramSize != sizeof(AudiodecParamAt9)) return ERROR_INVALID_PARAM_SIZE; if (infoSize != sizeof(AudiodecAt9Info)) return ERROR_INVALID_BSI_INFO_SIZE; break; case TYPE_MP3: if (paramSize != sizeof(AudiodecParamMp3)) return ERROR_INVALID_PARAM_SIZE; if (infoSize != sizeof(AudiodecMp3Info)) return ERROR_INVALID_BSI_INFO_SIZE; break; default: if (paramSize < sizeof(AudiodecParamM4aac)) return ERROR_INVALID_PARAM_SIZE; if (infoSize != sizeof(AudiodecM4aacInfo)) return ERROR_INVALID_BSI_INFO_SIZE; break; } return validateWordSize(wordSizeOf(ctrl->pParam)); } std::int32_t validateBuffers(const AudiodecCtrl* ctrl) { if (!ctrl->pAuInfo) return ERROR_INVALID_AU_INFO_POINTER; if (!ctrl->pPcmItem) return ERROR_INVALID_PCM_ITEM_POINTER; if (ctrl->pAuInfo->ui_size != sizeof(AudiodecAuInfo)) return ERROR_INVALID_AU_INFO_SIZE; if (ctrl->pPcmItem->ui_size != sizeof(AudiodecPcmItem)) return ERROR_INVALID_PCM_ITEM_SIZE; if (!ctrl->pAuInfo->p_au_addr) return ERROR_INVALID_AU_POINTER; if (!ctrl->pPcmItem->p_pcm_addr) return ERROR_INVALID_PCM_POINTER; if (ctrl->pAuInfo->ui_au_size == 0) return ERROR_INVALID_AU_SIZE; if (ctrl->pPcmItem->ui_pcm_size == 0) return ERROR_INVALID_PCM_SIZE; return 0; } void fillAt9Info(AudiodecAt9Info* info, const Audiodec::At9Format& format) { const std::uint32_t superframeSamples = format.frameSamples * format.framesInSuperframe; info->ui_channel = format.channels; info->ui_bitrate = static_cast(static_cast(format.superframeSize) * 8 * format.sampleRate / superframeSamples); info->ui_sampling_rate = format.sampleRate; info->ui_super_frame_size = format.superframeSize; info->ui_frames_in_super_frame = format.framesInSuperframe; info->ui_next_frame_size = format.superframeSize; info->ui_frame_samples = format.frameSamples; info->i_result = 0; } void fillMp3Info(AudiodecMp3Info* info, const Audiodec::Mp3Header& header) { info->ui_header = header.word; info->uc_crc = header.crc; info->uc_mode = header.mode; info->uc_mode_extension = header.modeExtension; info->uc_copyright = header.copyright; info->uc_original = header.original; info->uc_emphasis = header.emphasis; info->uc_reserved[0] = 0; info->uc_reserved[1] = 0; info->i_result = 0; } std::int32_t createAac(const AudiodecParamM4aac* param, AudiodecM4aacInfo* info, Instance& instance) { if (param->ui_config_number != M4AAC_CONFIG_ADTS && param->ui_config_number != M4AAC_CONFIG_RAW) return ERROR_M4AAC_INVALID_CONFIG_NUMBER; const std::uint32_t sampleRate = Audiodec::AacSampleRate(param->ui_sampling_freq_index); if (param->ui_config_number == M4AAC_CONFIG_RAW && sampleRate == 0) return ERROR_M4AAC_INVALID_SAMPLING_FREQ; if (param->ui_max_channels > 8) return ERROR_M4AAC_INVALID_MAX_CHANNELS; if (param->ui_enable_heaac > 1) return ERROR_M4AAC_INVALID_ENABLE_HEAAC; const std::uint32_t channels = param->ui_max_channels == 0 ? 2 : param->ui_max_channels; instance.decoder = Audiodec::CreateAac(param->i_bw_pcm, param->ui_config_number == M4AAC_CONFIG_ADTS, param->ui_sampling_freq_index, channels); instance.channels = channels; instance.sampleRate = sampleRate; info->ui_sampling_freq = sampleRate; info->ui_number_of_channels = channels; info->ui_heaac = 0; info->i_result = 0; return 0; } Instance* find(std::int32_t handle) { if (handle <= 0 || static_cast(handle) > g_instances.size()) return nullptr; Instance& instance = g_instances[static_cast(handle - 1)]; return instance.decoder ? &instance : nullptr; } } extern "C" { int32_t APS5_VABI sceAudiodecInitLibrary(uint32_t codec_type) { if (!validType(codec_type)) return ERROR_INVALID_TYPE; std::lock_guard lock(g_mutex); ++g_initialized[codec_type]; return 0; } int32_t APS5_VABI sceAudiodecTermLibrary(uint32_t codec_type) { if (!validType(codec_type)) return ERROR_INVALID_TYPE; std::lock_guard lock(g_mutex); if (g_initialized[codec_type] > 0) --g_initialized[codec_type]; return 0; } int32_t APS5_VABI sceAudiodecCreateDecoder(AudiodecCtrl* ctrl, uint32_t codec_type) { if (!validType(codec_type)) return ERROR_INVALID_TYPE; if (const std::int32_t result = validateCtrl(ctrl, codec_type); result != 0) return result; std::lock_guard lock(g_mutex); if (g_initialized[codec_type] == 0) return ERROR_ARG; for (std::size_t i = 0; i < g_instances.size(); ++i) { Instance& instance = g_instances[i]; if (instance.decoder) continue; instance = {}; instance.codecType = codec_type; if (codec_type == TYPE_AT9) { const auto* param = static_cast(ctrl->pParam); instance.decoder = Audiodec::CreateAt9(param->i_bw_pcm, param->ui_config_data, instance.at9); if (!instance.decoder) return ERROR_AT9_INVALID_CONFIG_DATA; fillAt9Info(static_cast(ctrl->pBsiInfo), instance.at9); } else if (codec_type == TYPE_MP3) { instance.decoder = Audiodec::CreateMp3(static_cast(ctrl->pParam)->i_bw_pcm); } else if (const std::int32_t result = createAac(static_cast(ctrl->pParam), static_cast(ctrl->pBsiInfo), instance); result != 0) { instance = {}; return result; } return static_cast(i + 1); } return ERROR_API_FAIL; } int32_t APS5_VABI sceAudiodecDeleteDecoder(int32_t handle) { std::lock_guard lock(g_mutex); Instance* instance = find(handle); if (!instance) return ERROR_INVALID_HANDLE; *instance = {}; return 0; } int32_t APS5_VABI sceAudiodecClearContext(int32_t handle) { std::lock_guard lock(g_mutex); Instance* instance = find(handle); if (!instance) return ERROR_INVALID_HANDLE; instance->decoder->Reset(); return 0; } int32_t APS5_VABI sceAudiodecDecode(int32_t handle, AudiodecCtrl* ctrl) { std::lock_guard lock(g_mutex); Instance* instance = find(handle); if (!instance) return ERROR_INVALID_HANDLE; if (const std::int32_t result = validateCtrl(ctrl, instance->codecType); result != 0) return result; if (const std::int32_t result = validateBuffers(ctrl); result != 0) return result; const Audiodec::DecodeResult decoded = instance->decoder->Decode(static_cast(ctrl->pAuInfo->p_au_addr), ctrl->pAuInfo->ui_au_size, static_cast(ctrl->pPcmItem->p_pcm_addr), ctrl->pPcmItem->ui_pcm_size); if (instance->codecType == TYPE_M4AAC) { auto* info = static_cast(ctrl->pBsiInfo); info->i_result = decoded.status == Audiodec::DecodeStatus::InvalidData ? M4AAC_RESULT_DECODE_ERROR : decoded.status == Audiodec::DecodeStatus::PartialInput ? M4AAC_RESULT_INSUFFICIENT_DATA : 0; if (decoded.channels != 0) { info->ui_sampling_freq = decoded.sampleRate; info->ui_number_of_channels = decoded.channels; info->ui_heaac = decoded.highEfficiency; } } if (decoded.status == Audiodec::DecodeStatus::NotEnoughRoom) return ERROR_INVALID_PCM_SIZE; if (decoded.status != Audiodec::DecodeStatus::Ok) { ctrl->pAuInfo->ui_au_size = 0; ctrl->pPcmItem->ui_pcm_size = 0; return ERROR_API_FAIL; } if (instance->codecType == TYPE_MP3) fillMp3Info(static_cast(ctrl->pBsiInfo), decoded.mp3); if (instance->codecType == TYPE_AT9) fillAt9Info(static_cast(ctrl->pBsiInfo), instance->at9); ctrl->pAuInfo->ui_au_size = static_cast(decoded.consumed); ctrl->pPcmItem->ui_pcm_size = static_cast(decoded.produced); return 0; } }