diff --git a/.gitmodules b/.gitmodules index 88635e645..5fc878df0 100644 --- a/.gitmodules +++ b/.gitmodules @@ -98,4 +98,7 @@ [submodule "externals/discord-rpc"] path = externals/discord-rpc url = https://github.com/shadps4-emu/ext-discord-rpc.git - shallow = true \ No newline at end of file + shallow = true +[submodule "externals/LibAtrac9"] + path = externals/LibAtrac9 + url = https://github.com/Vita3K/LibAtrac9 diff --git a/CMakeLists.txt b/CMakeLists.txt index 04bd6a331..18328f3cd 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -185,6 +185,12 @@ set(AUDIO_LIB src/core/libraries/audio/audioin.cpp src/core/libraries/audio/audioout.h src/core/libraries/ajm/ajm.cpp src/core/libraries/ajm/ajm.h + src/core/libraries/ajm/ajm_instance.h + src/core/libraries/ajm/ajm_error.h + src/core/libraries/ajm/ajm_mp3.cpp + src/core/libraries/ajm/ajm_mp3.h + src/core/libraries/ajm/ajm_at9.cpp + src/core/libraries/ajm/ajm_at9.h src/core/libraries/ngs2/ngs2.cpp src/core/libraries/ngs2/ngs2.h ) @@ -194,8 +200,7 @@ set(GNM_LIB src/core/libraries/gnmdriver/gnmdriver.cpp src/core/libraries/gnmdriver/gnm_error.h ) -set(KERNEL_LIB - src/core/libraries/kernel/event_flag/event_flag.cpp +set(KERNEL_LIB src/core/libraries/kernel/event_flag/event_flag.cpp src/core/libraries/kernel/event_flag/event_flag.h src/core/libraries/kernel/event_flag/event_flag_obj.cpp src/core/libraries/kernel/event_flag/event_flag_obj.h @@ -794,7 +799,7 @@ endif() create_target_directory_groups(shadps4) target_link_libraries(shadps4 PRIVATE magic_enum::magic_enum fmt::fmt toml11::toml11 tsl::robin_map xbyak::xbyak Tracy::TracyClient RenderDoc::API FFmpeg::ffmpeg Dear_ImGui gcn half::half) -target_link_libraries(shadps4 PRIVATE Boost::headers GPUOpen::VulkanMemoryAllocator sirit Vulkan::Headers xxHash::xxhash Zydis::Zydis glslang::SPIRV glslang::glslang SDL3::SDL3 pugixml::pugixml) +target_link_libraries(shadps4 PRIVATE Boost::headers GPUOpen::VulkanMemoryAllocator LibAtrac9 sirit Vulkan::Headers xxHash::xxhash Zydis::Zydis glslang::SPIRV glslang::glslang SDL3::SDL3 pugixml::pugixml) target_compile_definitions(shadps4 PRIVATE IMGUI_USER_CONFIG="imgui/imgui_config.h") target_compile_definitions(Dear_ImGui PRIVATE IMGUI_USER_CONFIG="${PROJECT_SOURCE_DIR}/src/imgui/imgui_config.h") diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index 8fefee0f8..17d710878 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -47,6 +47,11 @@ if (NOT TARGET FFmpeg::ffmpeg) add_library(FFmpeg::ffmpeg ALIAS ffmpeg) endif() +# LibAtrac9 +file(GLOB LIBATRAC9_SOURCES LibAtrac9/C/src/*.c) +add_library(LibAtrac9 STATIC ${LIBATRAC9_SOURCES}) +target_include_directories(LibAtrac9 INTERFACE LibAtrac9/C/src) + # Zlib-Ng if (NOT TARGET zlib-ng::zlib) set(ZLIB_ENABLE_TESTS OFF) diff --git a/externals/LibAtrac9 b/externals/LibAtrac9 new file mode 160000 index 000000000..82767fe38 --- /dev/null +++ b/externals/LibAtrac9 @@ -0,0 +1 @@ +Subproject commit 82767fe38823c32536726ea798f392b0b49e66b9 diff --git a/src/core/libraries/ajm/ajm.cpp b/src/core/libraries/ajm/ajm.cpp index 441a07f63..fb55f6873 100644 --- a/src/core/libraries/ajm/ajm.cpp +++ b/src/core/libraries/ajm/ajm.cpp @@ -1,15 +1,76 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "ajm.h" -#include "ajm_error.h" +#include +#include +#include +#include +#include +#include "common/assert.h" #include "common/logging/log.h" +#include "core/libraries/ajm/ajm.h" +#include "core/libraries/ajm/ajm_at9.h" +#include "core/libraries/ajm/ajm_error.h" +#include "core/libraries/ajm/ajm_instance.h" +#include "core/libraries/ajm/ajm_mp3.h" #include "core/libraries/error_codes.h" #include "core/libraries/libs.h" +extern "C" { +#include +#include +#include +#include +#include +} + namespace Libraries::Ajm { +static constexpr u32 AJM_INSTANCE_STATISTICS = 0x80000; + +static constexpr u32 SCE_AJM_WAIT_INFINITE = -1; + +static constexpr u32 MaxInstances = 0x2fff; + +static constexpr u32 MaxBatches = 1000; + +struct BatchInfo { + u16 instance{}; + u16 offset_in_qwords{}; // Needed for AjmBatchError? + bool waiting{}; + bool finished{}; + std::mutex mtx; + std::condition_variable cv; + int result{}; +}; + +struct AjmDevice { + u32 max_prio{}; + u32 min_prio{}; + u32 curr_cursor{}; + u32 release_cursor{MaxInstances - 1}; + std::array is_registered{}; + std::array free_instances{}; + std::array, MaxInstances> instances; + std::vector> batches{}; + std::mutex batches_mutex; + + [[nodiscard]] bool IsRegistered(AjmCodecType type) const { + return is_registered[static_cast(type)]; + } + + void Register(AjmCodecType type) { + is_registered[static_cast(type)] = true; + } + + AjmDevice() { + std::iota(free_instances.begin(), free_instances.end(), 1); + } +}; + +static std::unique_ptr dev{}; + int PS4_SYSV_ABI sceAjmBatchCancel() { LOG_ERROR(Lib_Ajm, "(STUBBED) called"); return ORBIS_OK; @@ -20,34 +81,322 @@ int PS4_SYSV_ABI sceAjmBatchErrorDump() { return ORBIS_OK; } -int PS4_SYSV_ABI sceAjmBatchJobControlBufferRa() { - LOG_ERROR(Lib_Ajm, "(STUBBED) called"); +void* PS4_SYSV_ABI sceAjmBatchJobControlBufferRa(AjmControlJob* batch_pos, u32 instance, + AjmFlags flags, u8* in_buffer, u32 in_size, + u8* out_buffer, u32 out_size, void* ret_addr) { + LOG_INFO(Lib_Ajm, + "called instance = {:#x}, flags = {:#x}, cmd = {}, in_size = {:#x}, out_size = {:#x}, " + "ret_addr = {}", + instance, flags.raw, magic_enum::enum_name(flags.control_flags), in_size, out_size, + fmt::ptr(ret_addr)); + + const u64 mask = instance == AJM_INSTANCE_STATISTICS ? 0xc0018007ULL : 0x60000000e7ffULL; + flags.raw &= mask; + + batch_pos->header.instance = instance; + + AjmControlJobInner* job; + if (ret_addr == nullptr) { + batch_pos->header.job_size = sizeof(AjmControlJobInner); + job = &batch_pos->job; + } else { + batch_pos->header.job_size = sizeof(AjmControlJobInner) + sizeof(AjmJobBuffer); + batch_pos->ret.ret_buf.ident = Identifier::ReturnAddrBuf; + batch_pos->ret.ret_buf.buf_size = 0; + batch_pos->ret.ret_buf.buffer = (u8*)ret_addr; + job = &batch_pos->ret.job; + } + + job->input.ident = Identifier::InputControlBuf; + job->input.buf_size = in_size; + job->input.buffer = in_buffer; + job->flags.raw1 = (job->flags.raw1 & 0xfc000000) + ((flags.raw >> 0x1a) & 0x180000) + 3; + job->flags.raw2 = u32(flags.raw); + job->output.ident = Identifier::OutputRunControlBuf; + job->output.buf_size = out_size; + job->output.buffer = out_buffer; + return ++job; +} + +void* PS4_SYSV_ABI sceAjmBatchJobInlineBuffer(u8* batch_pos, const void* in_buffer, size_t in_size, + const void** batch_address) { + // TODO + return nullptr; +} + +void* PS4_SYSV_ABI sceAjmBatchJobRunBufferRa(AjmRunJob* batch_pos, u32 instance, AjmFlags flags, + u8* in_buffer, u32 in_size, u8* out_buffer, + const u32 out_size, u8* sideband_output, + const u32 sideband_output_size, void* ret_addr) { + LOG_INFO(Lib_Ajm, + "called instance = {:#x}, flags = {:#x}, cmd = {}, in_size = {:#x}, out_size = {:#x}, " + "ret_addr = {}", + instance, flags.raw, magic_enum::enum_name(flags.run_flags), in_size, out_size, + fmt::ptr(ret_addr)); + + const u64 mask = 0xE00000001FFFLL; + flags.raw &= mask; + + batch_pos->header.instance = instance; + + AjmRunJobInner* job; + if (ret_addr == nullptr) { + batch_pos->header.job_size = sizeof(AjmRunJobInner); + job = &batch_pos->job; + } else { + batch_pos->header.job_size = sizeof(AjmRunJobInner) + sizeof(AjmJobBuffer); + batch_pos->ret.ret_buf.ident = Identifier::ReturnAddrBuf; + batch_pos->ret.ret_buf.buf_size = 0; + batch_pos->ret.ret_buf.buffer = (u8*)ret_addr; + job = &batch_pos->ret.job; + } + + job->input.ident = Identifier::InputRunBuf; + job->input.buf_size = in_size; + job->input.buffer = in_buffer; + job->flags.raw1 = (job->flags.raw1 & 0xfc000000) + (flags.raw >> 0x1a) + 4; + job->flags.raw2 = u32(flags.raw); + job->output.ident = Identifier::OutputRunControlBuf; + job->output.buf_size = out_size; + job->output.buffer = out_buffer; + job->sideband.ident = Identifier::OutputRunControlBuf; + job->sideband.buf_size = sideband_output_size; + job->sideband.buffer = sideband_output; + return ++job; +} + +void* PS4_SYSV_ABI sceAjmBatchJobRunSplitBufferRa(AjmMultiJob* batch_pos, u32 instance, + AjmFlags flags, const AjmBuffer* in_buffers, + u64 num_in_buffers, const AjmBuffer* out_buffers, + u64 num_out_buffers, void* sideband_output, + u64 sideband_output_size, void* ret_addr) { + LOG_INFO(Lib_Ajm, + "called instance = {}, flags = {:#x}, cmd = {}, sideband_cmd = {} num_input_buffers " + "= {}, num_output_buffers = {}, " + "ret_addr = {}", + instance, flags.raw, magic_enum::enum_name(flags.run_flags), + magic_enum::enum_name(flags.sideband_flags), num_in_buffers, num_out_buffers, + fmt::ptr(ret_addr)); + + const u32 job_size = (num_in_buffers * 2 + 1 + num_out_buffers * 2) * 8; + batch_pos->header.instance = instance; + + u32* job; + if (ret_addr == nullptr) { + batch_pos->header.job_size = job_size + 16; + job = batch_pos->job; + } else { + batch_pos->header.job_size = job_size + 32; + batch_pos->ret.ret_buf.ident = Identifier::ReturnAddrBuf; + batch_pos->ret.ret_buf.buf_size = 0; + batch_pos->ret.ret_buf.buffer = (u8*)ret_addr; + job = batch_pos->ret.job; + } + + for (s32 i = 0; i < num_in_buffers; i++) { + auto* in_buf = reinterpret_cast(job); + in_buf->ident = Identifier::InputRunBuf; + in_buf->buf_size = in_buffers[i].size; + in_buf->buffer = in_buffers[i].addr; + job += 4; + } + job[1] = u32(flags.raw & 0xe00000001fffULL); + job[0] &= 0xfc000030; + job[0] = s32((flags.raw & 0xe00000001fffULL) >> 0x1a) + 4; + job += 2; + + for (s32 i = 0; i < num_out_buffers; i++) { + auto* out_buf = reinterpret_cast(job); + out_buf->ident = Identifier::OutputMultijobBuf; + out_buf->buf_size = out_buffers[i].size; + out_buf->buffer = out_buffers[i].addr; + job += 4; + } + job[0] = job[0] & 0xffffffe0 | 0x12; // output.ident + job[1] = sideband_output_size; // output.buf_size + memcpy(&job[2], &sideband_output, sizeof(void*)); // output.buffer + return job + 4; +} + +int PS4_SYSV_ABI sceAjmBatchStartBuffer(u32 context, const u8* batch, u32 batch_size, + const int priority, AjmBatchError* batch_error, + u32* out_batch_id) { + LOG_INFO(Lib_Ajm, "called context = {}, batch_size = {:#x}, priority = {}", context, batch_size, + priority); + + if ((batch_size & 7) != 0) { + return ORBIS_AJM_ERROR_MALFORMED_BATCH; + } + + const auto batch_info = std::make_shared(); + if (dev->batches.size() >= MaxBatches) { + LOG_ERROR(Lib_Ajm, "Too many batches in job!"); + return ORBIS_AJM_ERROR_OUT_OF_MEMORY; + } + + *out_batch_id = static_cast(dev->batches.size()); + dev->batches.push_back(batch_info); + + const u8* batch_ptr = batch; + const u8* batch_end = batch + batch_size; + AjmJobHeader header{}; + + while (batch_ptr < batch_end) { + std::memcpy(&header, batch_ptr, sizeof(u64)); + const u32 instance = header.instance; + + const u8* curr_ptr = batch_ptr + sizeof(AjmJobHeader); + const u8* job_end = curr_ptr + header.job_size; + + boost::container::small_vector input_buffers; + while (true) { + Identifier ident{}; + std::memcpy(&ident, curr_ptr, sizeof(u8)); + + // Ignore return address buffers. + if (ident == Identifier::ReturnAddrBuf) { + curr_ptr += sizeof(AjmJobBuffer); + continue; + } + + // Add input buffer to the list of inputs. + if (ident == Identifier::InputRunBuf || ident == Identifier::InputControlBuf) { + auto& buffer = input_buffers.emplace_back(); + std::memcpy(&buffer, curr_ptr, sizeof(buffer)); + curr_ptr += sizeof(AjmJobBuffer); + continue; + } + + // A control or run flags identifier stops input buffer collection + // and provides necessary flags for the operation requested. + if (ident == Identifier::ControlFlags) { + AjmFlagsIdentifier flags; + std::memcpy(&flags, curr_ptr, sizeof(flags)); + + ASSERT_MSG(input_buffers.size() == 1, + "Only 1 input buffer is allowed for control commands"); + const auto& in_buffer = input_buffers.back(); + + const auto command = AjmJobControlFlags(flags.control_flags.Value()); + if (True(command & AjmJobControlFlags::Reset)) { + LOG_INFO(Lib_Ajm, "Resetting instance {}", instance); + dev->instances[instance]->Reset(); + } + if (True(command & AjmJobControlFlags::Initialize)) { + LOG_INFO(Lib_Ajm, "Initializing instance {}", instance); + dev->instances[instance]->Initialize(in_buffer.buffer, in_buffer.buf_size); + } + if (True(command & AjmJobControlFlags::Resample)) { + LOG_WARNING(Lib_Ajm, "Set resample params of instance {}", instance); + } + + curr_ptr += sizeof(flags); + AjmJobBuffer out_buffer; + std::memcpy(&out_buffer, curr_ptr, sizeof(out_buffer)); + + // Write sideband structures. + auto* result = reinterpret_cast(out_buffer.buffer); + result->result = 0; + result->internal_result = 0; + break; + } + if (ident == Identifier::RunFlags) { + AjmFlagsIdentifier flags; + std::memcpy(&flags, curr_ptr, sizeof(flags)); + + const auto command = AjmJobRunFlags(flags.run_flags.Value()); + const auto sideband = AjmJobSidebandFlags(flags.sideband_flags.Value()); + curr_ptr += sizeof(flags); + + // Collect output buffers. + boost::container::small_vector output_buffers; + while (curr_ptr < job_end) { + auto& buffer = output_buffers.emplace_back(); + std::memcpy(&buffer, curr_ptr, sizeof(buffer)); + curr_ptr += sizeof(buffer); + } + + ASSERT_MSG(input_buffers.size() == 1 && output_buffers.size() == 2, + "Run operation with multiple buffers untested in = {}, out = {}", + input_buffers.size(), output_buffers.size()); + AjmJobBuffer in_buffer = input_buffers.back(); + AjmJobBuffer out_buffer = output_buffers.front(); + AjmJobBuffer sideband_buffer = output_buffers.back(); + + // Write sideband structures. + auto* sideband_ptr = sideband_buffer.buffer; + auto* result = reinterpret_cast(sideband_ptr); + result->result = 0; + result->internal_result = 0; + sideband_ptr += sizeof(AjmSidebandResult); + + // Perform operation requested by run flags. + AjmInstance* decoder_instance = dev->instances[instance].get(); + if (True(command & AjmJobRunFlags::GetCodecInfo)) { + decoder_instance->GetCodecInfo(sideband_ptr); + } else { + LOG_INFO(Lib_Ajm, + "Decode job cmd = {}, sideband = {}, in_addr = {}, in_size = {}", + magic_enum::enum_name(command), magic_enum::enum_name(sideband), + fmt::ptr(in_buffer.buffer), in_buffer.buf_size); + + // Decode as much of the input bitstream as possible. + const auto [in_remain, out_remain, num_frames] = + decoder_instance->Decode(in_buffer.buffer, in_buffer.buf_size, + out_buffer.buffer, out_buffer.buf_size); + + // Check sideband flags for decoding + if (True(sideband & AjmJobSidebandFlags::Stream)) { + auto* stream = reinterpret_cast(sideband_ptr); + stream->input_consumed = in_buffer.buf_size - in_remain; + stream->output_written = out_buffer.buf_size - out_remain; + stream->total_decoded_samples = decoder_instance->decoded_samples; + sideband_ptr += sizeof(AjmSidebandStream); + } + if (True(command & AjmJobRunFlags::MultipleFrames)) { + auto* mframe = reinterpret_cast(sideband_ptr); + mframe->num_frames = num_frames; + sideband_ptr += sizeof(AjmSidebandMFrame); + } + } + break; + } + UNREACHABLE_MSG("Unknown ident = {}", u32(ident)); + } + batch_ptr += sizeof(AjmJobHeader) + header.job_size; + } + + batch_info->finished = true; + return ORBIS_OK; } -int PS4_SYSV_ABI sceAjmBatchJobInlineBuffer() { - LOG_ERROR(Lib_Ajm, "(STUBBED) called"); - return ORBIS_OK; -} +int PS4_SYSV_ABI sceAjmBatchWait(const u32 context, const u32 batch_id, const u32 timeout, + AjmBatchError* const batch_error) { + LOG_INFO(Lib_Ajm, "called context = {}, batch_id = {}, timeout = {}", context, batch_id, + timeout); -int PS4_SYSV_ABI sceAjmBatchJobRunBufferRa() { - LOG_ERROR(Lib_Ajm, "(STUBBED) called"); - return ORBIS_OK; -} + if (batch_id > 0xFF || batch_id >= dev->batches.size()) { + return ORBIS_AJM_ERROR_INVALID_BATCH; + } -int PS4_SYSV_ABI sceAjmBatchJobRunSplitBufferRa() { - LOG_ERROR(Lib_Ajm, "(STUBBED) called"); - return ORBIS_OK; -} + const auto& batch = dev->batches[batch_id]; -int PS4_SYSV_ABI sceAjmBatchStartBuffer() { - LOG_ERROR(Lib_Ajm, "(STUBBED) called"); - return ORBIS_OK; -} + if (batch->waiting) { + return ORBIS_AJM_ERROR_BUSY; + } + batch->waiting = true; -int PS4_SYSV_ABI sceAjmBatchWait() { - LOG_ERROR(Lib_Ajm, "(STUBBED) called"); - return ORBIS_OK; + { + std::unique_lock lk{batch->mtx}; + if (!batch->cv.wait_for(lk, std::chrono::milliseconds(timeout), + [&] { return batch->finished; })) { + return ORBIS_AJM_ERROR_IN_PROGRESS; + } + } + + dev->batches.erase(dev->batches.begin() + batch_id); + return 0; } int PS4_SYSV_ABI sceAjmDecAt9ParseConfigData() { @@ -55,9 +404,16 @@ int PS4_SYSV_ABI sceAjmDecAt9ParseConfigData() { return ORBIS_OK; } -int PS4_SYSV_ABI sceAjmDecMp3ParseFrame() { - LOG_ERROR(Lib_Ajm, "(STUBBED) called"); - return ORBIS_OK; +int PS4_SYSV_ABI sceAjmDecMp3ParseFrame(const u8* buf, u32 stream_size, int parse_ofl, + AjmDecMp3ParseFrame* frame) { + LOG_INFO(Lib_Ajm, "called parse_ofl = {}", parse_ofl); + if (buf == nullptr || stream_size < 4 || frame == nullptr) { + return ORBIS_AJM_ERROR_INVALID_PARAMETER; + } + if ((buf[0] & SYNCWORDH) != SYNCWORDH || (buf[1] & SYNCWORDL) != SYNCWORDL) { + return ORBIS_AJM_ERROR_INVALID_PARAMETER; + } + return AjmMp3Decoder::ParseMp3Header(buf, stream_size, parse_ofl, frame); } int PS4_SYSV_ABI sceAjmFinalize() { @@ -65,8 +421,13 @@ int PS4_SYSV_ABI sceAjmFinalize() { return ORBIS_OK; } -int PS4_SYSV_ABI sceAjmInitialize() { - LOG_ERROR(Lib_Ajm, "(STUBBED) called"); +int PS4_SYSV_ABI sceAjmInitialize(s64 reserved, u32* out_context) { + LOG_INFO(Lib_Ajm, "called reserved = {}", reserved); + if (out_context == nullptr || reserved != 0) { + return ORBIS_AJM_ERROR_INVALID_PARAMETER; + } + *out_context = 1; + dev = std::make_unique(); return ORBIS_OK; } @@ -75,13 +436,56 @@ int PS4_SYSV_ABI sceAjmInstanceCodecType() { return ORBIS_OK; } -int PS4_SYSV_ABI sceAjmInstanceCreate() { - LOG_ERROR(Lib_Ajm, "(STUBBED) called"); +int PS4_SYSV_ABI sceAjmInstanceCreate(u32 context, AjmCodecType codec_type, AjmInstanceFlags flags, + u32* out_instance) { + if (codec_type >= AjmCodecType::Max) { + return ORBIS_AJM_ERROR_INVALID_PARAMETER; + } + if (flags.version == 0) { + return ORBIS_AJM_ERROR_WRONG_REVISION_FLAG; + } + if (!dev->IsRegistered(codec_type)) { + return ORBIS_AJM_ERROR_CODEC_NOT_REGISTERED; + } + if (dev->curr_cursor == dev->release_cursor) { + return ORBIS_AJM_ERROR_OUT_OF_RESOURCES; + } + ASSERT_MSG(flags.format == 0, "Only signed 16-bit PCM output is supported currently!"); + const u32 index = dev->free_instances[dev->curr_cursor++]; + dev->curr_cursor %= MaxInstances; + std::unique_ptr instance; + switch (codec_type) { + case AjmCodecType::Mp3Dec: + instance = std::make_unique(); + break; + case AjmCodecType::At9Dec: + instance = std::make_unique(); + break; + default: + UNREACHABLE_MSG("Codec #{} not implemented", u32(codec_type)); + } + instance->index = index; + instance->codec_type = codec_type; + instance->num_channels = flags.channels; + dev->instances[index] = std::move(instance); + *out_instance = index; + LOG_INFO(Lib_Ajm, "called codec_type = {}, flags = {:#x}, instance = {}", + magic_enum::enum_name(codec_type), flags.raw, index); + return ORBIS_OK; } -int PS4_SYSV_ABI sceAjmInstanceDestroy() { - LOG_ERROR(Lib_Ajm, "(STUBBED) called"); +int PS4_SYSV_ABI sceAjmInstanceDestroy(u32 context, u32 instance) { + LOG_INFO(Lib_Ajm, "called context = {}, instance = {}", context, instance); + if ((instance & 0x3fff) > MaxInstances) { + return ORBIS_AJM_ERROR_INVALID_INSTANCE; + } + const u32 next_slot = (dev->release_cursor + 1) % MaxInstances; + if (next_slot != dev->curr_cursor) { + dev->free_instances[dev->release_cursor] = instance; + dev->release_cursor = next_slot; + } + dev->instances[instance].reset(); return ORBIS_OK; } @@ -105,8 +509,16 @@ int PS4_SYSV_ABI sceAjmMemoryUnregister() { return ORBIS_OK; } -int PS4_SYSV_ABI sceAjmModuleRegister() { - LOG_ERROR(Lib_Ajm, "(STUBBED) called"); +int PS4_SYSV_ABI sceAjmModuleRegister(u32 context, AjmCodecType codec_type, s64 reserved) { + LOG_INFO(Lib_Ajm, "called context = {}, codec_type = {}, reserved = {}", context, + u32(codec_type), reserved); + if (codec_type >= AjmCodecType::Max || reserved != 0) { + return ORBIS_AJM_ERROR_INVALID_PARAMETER; + } + if (dev->IsRegistered(codec_type)) { + return ORBIS_AJM_ERROR_CODEC_ALREADY_REGISTERED; + } + dev->Register(codec_type); return ORBIS_OK; } @@ -145,4 +557,4 @@ void RegisterlibSceAjm(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("AxhcqVv5AYU", "libSceAjm", 1, "libSceAjm", 1, 1, sceAjmStrError); }; -} // namespace Libraries::Ajm \ No newline at end of file +} // namespace Libraries::Ajm diff --git a/src/core/libraries/ajm/ajm.h b/src/core/libraries/ajm/ajm.h index 8491e519c..af71d20cd 100644 --- a/src/core/libraries/ajm/ajm.h +++ b/src/core/libraries/ajm/ajm.h @@ -3,6 +3,8 @@ #pragma once +#include "common/bit_field.h" +#include "common/enum.h" #include "common/types.h" namespace Core::Loader { @@ -11,28 +13,193 @@ class SymbolsResolver; namespace Libraries::Ajm { +struct AjmBatchInfo { + void* pBuffer; + u64 offset; + u64 size; +}; + +struct AjmBatchError { + int error_code; + const void* job_addr; + u32 cmd_offset; + const void* job_ra; +}; + +struct AjmBuffer { + u8* addr; + u64 size; +}; + +enum class Identifier : u32 { + InputRunBuf = 1, + InputControlBuf = 2, + ControlFlags = 3, + RunFlags = 4, + ReturnAddrBuf = 6, + OutputMultijobBuf = 17, + OutputRunControlBuf = 18, + IdentMask = 0xff, +}; + +struct AjmJobBuffer { + Identifier ident; + u32 buf_size; + u8* buffer; +}; + +enum class AjmJobControlFlags : u64 { + Reset = 1 << 0, + Initialize = 1 << 1, + Resample = 1 << 2, +}; +DECLARE_ENUM_FLAG_OPERATORS(AjmJobControlFlags) + +enum class AjmJobRunFlags : u64 { + GetCodecInfo = 1 << 0, + MultipleFrames = 1 << 1, +}; +DECLARE_ENUM_FLAG_OPERATORS(AjmJobRunFlags) + +enum class AjmJobSidebandFlags : u64 { + GaplessDecode = 1 << 0, + GetInfo = 1 << 1, + Stream = 1 << 2, +}; +DECLARE_ENUM_FLAG_OPERATORS(AjmJobSidebandFlags) + +union AjmFlags { + u64 raw; + struct { + u64 version : 3; + u64 codec : 8; + AjmJobRunFlags run_flags : 2; + AjmJobControlFlags control_flags : 3; + u64 reserved : 29; + AjmJobSidebandFlags sideband_flags : 3; + }; +}; + +struct AjmFlagsIdentifier { + union { + u32 raw1; + BitField<0, 4, Identifier> identifier; + BitField<19, 3, u32> sideband_flags; + }; + union { + u32 raw2; + BitField<0, 3, u32> version; + BitField<3, 8, u32> codec; + BitField<11, 2, u32> run_flags; + BitField<13, 3, u32> control_flags; + }; +}; + +struct AjmControlJobInner { + AjmJobBuffer input; + AjmFlagsIdentifier flags; + AjmJobBuffer output; +}; + +struct AjmRunJobInner : public AjmControlJobInner { + AjmJobBuffer sideband; +}; + +struct AjmJobHeader { + struct { + u32 : 6; + u32 instance : 16; + }; + u32 job_size; +}; + +struct AjmControlJob { + AjmJobHeader header; + union { + AjmControlJobInner job; + struct { + AjmJobBuffer ret_buf; + AjmControlJobInner job; + } ret; + }; +}; +static_assert(sizeof(AjmControlJob) == 64); + +struct AjmRunJob { + AjmJobHeader header; + union { + AjmRunJobInner job; + struct { + AjmJobBuffer ret_buf; + AjmRunJobInner job; + } ret; + }; +}; +static_assert(sizeof(AjmRunJob) == 80); + +struct AjmMultiJob { + AjmJobHeader header; + union { + u32 job[]; + struct { + AjmJobBuffer ret_buf; + u32 job[]; + } ret; + }; +}; + +union AjmInstanceFlags { + u64 raw; + struct { + u64 version : 3; + u64 channels : 4; + u64 format : 3; + u64 pad : 22; + u64 codec : 28; + }; +}; + +struct AjmDecMp3ParseFrame; +enum class AjmCodecType : u32; + int PS4_SYSV_ABI sceAjmBatchCancel(); int PS4_SYSV_ABI sceAjmBatchErrorDump(); -int PS4_SYSV_ABI sceAjmBatchJobControlBufferRa(); -int PS4_SYSV_ABI sceAjmBatchJobInlineBuffer(); -int PS4_SYSV_ABI sceAjmBatchJobRunBufferRa(); -int PS4_SYSV_ABI sceAjmBatchJobRunSplitBufferRa(); -int PS4_SYSV_ABI sceAjmBatchStartBuffer(); -int PS4_SYSV_ABI sceAjmBatchWait(); +void* PS4_SYSV_ABI sceAjmBatchJobControlBufferRa(AjmControlJob* batch_pos, u32 instance, + AjmFlags flags, u8* in_buffer, u32 in_size, + u8* out_buffer, u32 out_size, void* ret_addr); +void* PS4_SYSV_ABI sceAjmBatchJobInlineBuffer(u8* batch_pos, const void* in_buffer, u64 in_size, + const void** batch_address); +void* PS4_SYSV_ABI sceAjmBatchJobRunBufferRa(AjmRunJob* batch_pos, u32 instance, AjmFlags flags, + u8* in_buffer, u32 in_size, u8* out_buffer, + const u32 out_size, u8* sideband_output, + const u32 sideband_output_size, void* ret_addr); +void* PS4_SYSV_ABI sceAjmBatchJobRunSplitBufferRa(AjmMultiJob* batch_pos, u32 instance, + AjmFlags flags, const AjmBuffer* input_buffers, + u64 num_input_buffers, + const AjmBuffer* output_buffers, + u64 num_output_buffers, void* sideband_output, + u64 sideband_output_size, void* ret_addr); +int PS4_SYSV_ABI sceAjmBatchStartBuffer(u32 context, const u8* batch, u32 batch_size, + const int priority, AjmBatchError* batch_error, + u32* out_batch_id); +int PS4_SYSV_ABI sceAjmBatchWait(const u32 context, const u32 batch_id, const u32 timeout, + AjmBatchError* const batch_error); int PS4_SYSV_ABI sceAjmDecAt9ParseConfigData(); -int PS4_SYSV_ABI sceAjmDecMp3ParseFrame(); +int PS4_SYSV_ABI sceAjmDecMp3ParseFrame(const u8* stream, u32 stream_size, int parse_ofl, + AjmDecMp3ParseFrame* frame); int PS4_SYSV_ABI sceAjmFinalize(); -int PS4_SYSV_ABI sceAjmInitialize(); +int PS4_SYSV_ABI sceAjmInitialize(s64 reserved, u32* out_context); int PS4_SYSV_ABI sceAjmInstanceCodecType(); -int PS4_SYSV_ABI sceAjmInstanceCreate(); -int PS4_SYSV_ABI sceAjmInstanceDestroy(); +int PS4_SYSV_ABI sceAjmInstanceCreate(u32 context, AjmCodecType codec_type, AjmInstanceFlags flags, + u32* instance); +int PS4_SYSV_ABI sceAjmInstanceDestroy(u32 context, u32 instance); int PS4_SYSV_ABI sceAjmInstanceExtend(); int PS4_SYSV_ABI sceAjmInstanceSwitch(); int PS4_SYSV_ABI sceAjmMemoryRegister(); int PS4_SYSV_ABI sceAjmMemoryUnregister(); -int PS4_SYSV_ABI sceAjmModuleRegister(); +int PS4_SYSV_ABI sceAjmModuleRegister(u32 context, AjmCodecType codec_type, s64 reserved); int PS4_SYSV_ABI sceAjmModuleUnregister(); int PS4_SYSV_ABI sceAjmStrError(); void RegisterlibSceAjm(Core::Loader::SymbolsResolver* sym); -} // namespace Libraries::Ajm \ No newline at end of file +} // namespace Libraries::Ajm diff --git a/src/core/libraries/ajm/ajm_at9.cpp b/src/core/libraries/ajm/ajm_at9.cpp new file mode 100644 index 000000000..609129eaa --- /dev/null +++ b/src/core/libraries/ajm/ajm_at9.cpp @@ -0,0 +1,86 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include "common/assert.h" +#include "core/libraries/ajm/ajm_at9.h" +#include "error_codes.h" + +extern "C" { +#include +#include +#include +} + +namespace Libraries::Ajm { + +AjmAt9Decoder::AjmAt9Decoder() { + handle = Atrac9GetHandle(); + ASSERT_MSG(handle, "Atrac9GetHandle failed"); + AjmAt9Decoder::Reset(); +} + +AjmAt9Decoder::~AjmAt9Decoder() { + Atrac9ReleaseHandle(handle); +} + +void AjmAt9Decoder::Reset() { + decoded_samples = 0; +} + +void AjmAt9Decoder::Initialize(const void* buffer, u32 buffer_size) { + Atrac9ReleaseHandle(handle); + handle = Atrac9GetHandle(); + ASSERT_MSG(buffer_size == sizeof(SceAjmDecAt9InitializeParameters), + "Incorrect At9 initialization buffer size {}", buffer_size); + const auto params = reinterpret_cast(buffer); + std::memcpy(config_data, params->config_data, SCE_AT9_CONFIG_DATA_SIZE); + Atrac9InitDecoder(handle, config_data); +} + +void AjmAt9Decoder::GetCodecInfo(void* out_info) { + Atrac9CodecInfo decoder_codec_info; + Atrac9GetCodecInfo(handle, &decoder_codec_info); + + auto* codec_info = reinterpret_cast(out_info); + codec_info->uiFrameSamples = decoder_codec_info.frameSamples; + codec_info->uiFramesInSuperFrame = decoder_codec_info.framesInSuperframe; + codec_info->uiNextFrameSize = static_cast(handle)->Config.FrameBytes; + codec_info->uiSuperFrameSize = decoder_codec_info.superframeSize; +} + +std::tuple AjmAt9Decoder::Decode(const u8* in_buf, u32 in_size, u8* out_buf, + u32 out_size) { + if (in_size <= 0 || out_size <= 0) { + return std::tuple(in_size, out_size, 0); + } + + const auto decoder_handle = static_cast(handle); + Atrac9CodecInfo codec_info; + Atrac9GetCodecInfo(handle, &codec_info); + + int bytes_used = 0; + int frame_count = 0; + int bytes_remain = codec_info.superframeSize; + + const auto written_size = codec_info.channels * codec_info.frameSamples * sizeof(u16); + for (frame_count = 0; + frame_count < decoder_handle->Config.FramesPerSuperframe && in_size > 0 && out_size > 0; + frame_count++) { + u32 ret = Atrac9Decode(decoder_handle, in_buf, (short*)out_buf, &bytes_used); + ASSERT_MSG(ret == At9Status::ERR_SUCCESS, "Atrac9Decode failed ret = {:#x}", ret); + in_buf += bytes_used; + in_size -= bytes_used; + bytes_remain -= bytes_used; + out_buf += written_size; + out_size -= written_size; + decoded_samples += decoder_handle->Config.FrameSamples; + } + + in_size -= bytes_remain; + + LOG_TRACE(Lib_Ajm, "Decoded {} samples, frame count: {}", decoded_samples, frame_count); + return std::tuple(in_size, out_size, frame_count); +} + +} // namespace Libraries::Ajm diff --git a/src/core/libraries/ajm/ajm_at9.h b/src/core/libraries/ajm/ajm_at9.h new file mode 100644 index 000000000..d252c5c15 --- /dev/null +++ b/src/core/libraries/ajm/ajm_at9.h @@ -0,0 +1,53 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include "common/assert.h" +#include "common/types.h" +#include "core/libraries/ajm/ajm_instance.h" + +extern "C" { +#include +} + +namespace Libraries::Ajm { + +constexpr u32 SCE_AT9_CONFIG_DATA_SIZE = 4; +constexpr s32 SCE_AJM_DEC_AT9_MAX_CHANNELS = 8; + +struct SceAjmDecAt9InitializeParameters { + u8 config_data[SCE_AT9_CONFIG_DATA_SIZE]; + u32 reserved; +}; + +struct SceAjmSidebandDecAt9CodecInfo { + u32 uiSuperFrameSize; + u32 uiFramesInSuperFrame; + u32 uiNextFrameSize; + u32 uiFrameSamples; +}; + +struct AjmAt9Decoder final : AjmInstance { + void* handle; + bool decoder_initialized = false; + std::fstream file; + int length; + u8 config_data[SCE_AT9_CONFIG_DATA_SIZE]; + + explicit AjmAt9Decoder(); + ~AjmAt9Decoder() override; + + void Reset() override; + + void Initialize(const void* buffer, u32 buffer_size) override; + + void GetCodecInfo(void* out_info) override; + + std::tuple Decode(const u8* in_buf, u32 in_size, u8* out_buf, + u32 out_size) override; +}; + +} // namespace Libraries::Ajm diff --git a/src/core/libraries/ajm/ajm_instance.h b/src/core/libraries/ajm/ajm_instance.h new file mode 100644 index 000000000..2f5e8b441 --- /dev/null +++ b/src/core/libraries/ajm/ajm_instance.h @@ -0,0 +1,77 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/enum.h" +#include "common/types.h" + +extern "C" { +struct AVCodec; +struct AVCodecContext; +struct AVCodecParserContext; +} + +namespace Libraries::Ajm { + +enum class AjmCodecType : u32 { + Mp3Dec = 0, + At9Dec = 1, + M4aacDec = 2, + Max = 23, +}; +DECLARE_ENUM_FLAG_OPERATORS(AjmCodecType); +static constexpr u32 NumAjmCodecs = u32(AjmCodecType::Max); + +enum class AjmFormatEncoding : u32 { + S16 = 0, + S32 = 1, + Float = 2, +}; + +struct AjmSidebandResult { + s32 result; + s32 internal_result; +}; + +struct AjmSidebandMFrame { + u32 num_frames; + u32 reserved; +}; + +struct AjmSidebandStream { + s32 input_consumed; + s32 output_written; + u64 total_decoded_samples; +}; + +struct AjmSidebandFormat { + u32 num_channels; + u32 channel_mask; + u32 sampl_freq; + AjmFormatEncoding sample_encoding; + u32 bitrate; + u32 reserved; +}; + +struct AjmInstance { + AjmCodecType codec_type; + u32 decoded_samples{}; + AjmFormatEncoding fmt{}; + u32 num_channels{}; + u32 index{}; + + explicit AjmInstance() = default; + virtual ~AjmInstance() = default; + + virtual void Reset() = 0; + + virtual void Initialize(const void* buffer, u32 buffer_size) = 0; + + virtual void GetCodecInfo(void* out_info) = 0; + + virtual std::tuple Decode(const u8* in_buf, u32 in_size, u8* out_buf, + u32 out_size) = 0; +}; + +} // namespace Libraries::Ajm diff --git a/src/core/libraries/ajm/ajm_mp3.cpp b/src/core/libraries/ajm/ajm_mp3.cpp new file mode 100644 index 000000000..42d09a600 --- /dev/null +++ b/src/core/libraries/ajm/ajm_mp3.cpp @@ -0,0 +1,146 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/assert.h" +#include "core/libraries/ajm/ajm_mp3.h" + +extern "C" { +#include +#include +#include +} + +namespace Libraries::Ajm { + +// Following tables have been reversed from AJM library +static constexpr std::array, 3> SamplerateTable = {{ + {0x5622, 0x5DC0, 0x3E80}, + {0xAC44, 0xBB80, 0x7D00}, + {0x2B11, 0x2EE0, 0x1F40}, +}}; + +static constexpr std::array, 2> BitrateTable = {{ + {0, 0x20, 0x28, 0x30, 0x38, 0x40, 0x50, 0x60, 0x70, 0x80, 0xA0, 0xC0, 0xE0, 0x100, 0x140}, + {0, 0x8, 0x10, 0x18, 0x20, 0x28, 0x30, 0x38, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, 0xA0}, +}}; + +static constexpr std::array UnkTable = {0x48, 0x90}; + +SwrContext* swr_context{}; + +AVFrame* ConvertAudioFrame(AVFrame* frame) { + auto pcm16_frame = av_frame_clone(frame); + pcm16_frame->format = AV_SAMPLE_FMT_S16; + + if (swr_context) { + swr_free(&swr_context); + swr_context = nullptr; + } + AVChannelLayout in_ch_layout = frame->ch_layout; + AVChannelLayout out_ch_layout = pcm16_frame->ch_layout; + swr_alloc_set_opts2(&swr_context, &out_ch_layout, AV_SAMPLE_FMT_S16, frame->sample_rate, + &in_ch_layout, AVSampleFormat(frame->format), frame->sample_rate, 0, + nullptr); + swr_init(swr_context); + const auto res = swr_convert_frame(swr_context, pcm16_frame, frame); + if (res < 0) { + LOG_ERROR(Lib_AvPlayer, "Could not convert to S16: {}", av_err2str(res)); + return nullptr; + } + av_frame_free(&frame); + return pcm16_frame; +} + +AjmMp3Decoder::AjmMp3Decoder() { + codec = avcodec_find_decoder(AV_CODEC_ID_MP3); + ASSERT_MSG(codec, "MP3 codec not found"); + parser = av_parser_init(codec->id); + ASSERT_MSG(parser, "Parser not found"); + AjmMp3Decoder::Reset(); +} + +AjmMp3Decoder::~AjmMp3Decoder() { + avcodec_free_context(&c); + av_free(c); +} + +void AjmMp3Decoder::Reset() { + if (c) { + avcodec_free_context(&c); + av_free(c); + } + c = avcodec_alloc_context3(codec); + ASSERT_MSG(c, "Could not allocate audio codec context"); + int ret = avcodec_open2(c, codec, nullptr); + ASSERT_MSG(ret >= 0, "Could not open codec"); + decoded_samples = 0; +} + +std::tuple AjmMp3Decoder::Decode(const u8* buf, u32 in_size, u8* out_buf, + u32 out_size) { + u32 num_frames = 0; + AVPacket* pkt = av_packet_alloc(); + while (in_size > 0 && out_size > 0) { + int ret = av_parser_parse2(parser, c, &pkt->data, &pkt->size, buf, in_size, AV_NOPTS_VALUE, + AV_NOPTS_VALUE, 0); + ASSERT_MSG(ret >= 0, "Error while parsing {}", ret); + buf += ret; + in_size -= ret; + + if (pkt->size) { + // Send the packet with the compressed data to the decoder + pkt->pts = parser->pts; + pkt->dts = parser->dts; + pkt->flags = (parser->key_frame == 1) ? AV_PKT_FLAG_KEY : 0; + ret = avcodec_send_packet(c, pkt); + ASSERT_MSG(ret >= 0, "Error submitting the packet to the decoder {}", ret); + + // Read all the output frames (in general there may be any number of them + while (ret >= 0) { + AVFrame* frame = av_frame_alloc(); + ret = avcodec_receive_frame(c, frame); + if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { + break; + } else if (ret < 0) { + UNREACHABLE_MSG("Error during decoding"); + } + if (frame->format != AV_SAMPLE_FMT_S16) { + frame = ConvertAudioFrame(frame); + } + const auto size = frame->ch_layout.nb_channels * frame->nb_samples * sizeof(u16); + std::memcpy(out_buf, frame->data[0], size); + file.write((const char*)frame->data[0], size); + out_buf += size; + out_size -= size; + decoded_samples += frame->nb_samples; + num_frames++; + av_frame_free(&frame); + } + } + } + av_packet_free(&pkt); + return std::make_tuple(in_size, out_size, num_frames); +} + +int AjmMp3Decoder::ParseMp3Header(const u8* buf, u32 stream_size, int parse_ofl, + AjmDecMp3ParseFrame* frame) { + const u32 unk_idx = buf[1] >> 3 & 1; + const s32 version_idx = (buf[1] >> 3 & 3) ^ 2; + const s32 sr_idx = buf[2] >> 2 & 3; + const s32 br_idx = (buf[2] >> 4) & 0xf; + const s32 padding_bit = (buf[2] >> 1) & 0x1; + + frame->sample_rate = SamplerateTable[version_idx][sr_idx]; + frame->bitrate = BitrateTable[version_idx != 1][br_idx] * 1000; + frame->num_channels = (buf[3] < 0xc0) + 1; + frame->frame_size = (UnkTable[unk_idx] * frame->bitrate) / frame->sample_rate + padding_bit; + frame->samples_per_channel = UnkTable[unk_idx] * 8; + frame->encoder_delay = 0; + if (parse_ofl == 0) { + return 0; + } + + return 0; +} + +} // namespace Libraries::Ajm diff --git a/src/core/libraries/ajm/ajm_mp3.h b/src/core/libraries/ajm/ajm_mp3.h new file mode 100644 index 000000000..ebd040602 --- /dev/null +++ b/src/core/libraries/ajm/ajm_mp3.h @@ -0,0 +1,81 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include "common/types.h" +#include "core/libraries/ajm/ajm_instance.h" + +extern "C" { +struct AVCodec; +struct AVCodecContext; +struct AVCodecParserContext; +} + +namespace Libraries::Ajm { + +enum class AjmDecMp3OflType : u32 { None = 0, Lame = 1, Vbri = 2, Fgh = 3, VbriAndFgh = 4 }; + +// 11-bit syncword if MPEG 2.5 extensions are enabled +static constexpr u8 SYNCWORDH = 0xff; +static constexpr u8 SYNCWORDL = 0xe0; + +struct AjmDecMp3ParseFrame { + u64 frame_size; + u32 num_channels; + u32 samples_per_channel; + u32 bitrate; + u32 sample_rate; + u32 encoder_delay; + u32 num_frames; + u32 total_samples; + AjmDecMp3OflType ofl_type; +}; + +enum class ChannelMode : u8 { + Stereo = 0, + JointStero = 1, + Dual = 2, + Mono = 3, +}; + +struct AjmSidebandDecMp3CodecInfo { + u32 header; + bool has_crc; + ChannelMode channel_mode; + u8 mode_extension; + u8 copyright; + u8 original; + u8 emphasis; + u16 reserved[3]; +}; + +struct AjmDecMp3GetCodecInfoResult { + AjmSidebandResult result; + AjmSidebandDecMp3CodecInfo codec_info; +}; + +struct AjmMp3Decoder : public AjmInstance { + const AVCodec* codec = nullptr; + AVCodecContext* c = nullptr; + AVCodecParserContext* parser = nullptr; + std::ofstream file; + + explicit AjmMp3Decoder(); + ~AjmMp3Decoder() override; + + void Reset() override; + + void Initialize(const void* buffer, u32 buffer_size) override {} + + void GetCodecInfo(void* out_info) override {} + + std::tuple Decode(const u8* in_buf, u32 in_size, u8* out_buf, + u32 out_size) override; + + static int ParseMp3Header(const u8* buf, u32 stream_size, int parse_ofl, + AjmDecMp3ParseFrame* frame); +}; + +} // namespace Libraries::Ajm diff --git a/src/core/libraries/kernel/thread_management.cpp b/src/core/libraries/kernel/thread_management.cpp index 39c0eaf80..a8cee07c7 100644 --- a/src/core/libraries/kernel/thread_management.cpp +++ b/src/core/libraries/kernel/thread_management.cpp @@ -1075,7 +1075,16 @@ ScePthread PThreadPool::Create(const char* name) { } } +#ifdef _WIN64 auto* ret = new PthreadInternal{}; +#else + // TODO: Linux specific hack + static u8* hint_address = reinterpret_cast(0x7FFFFC000ULL); + auto* ret = reinterpret_cast( + mmap(hint_address, sizeof(PthreadInternal), PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0)); + hint_address += Common::AlignUp(sizeof(PthreadInternal), 4_KB); +#endif ret->is_free = false; ret->is_detached = false; ret->is_almost_done = false;