diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d1fab6354..8cf5efbf0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -160,7 +160,7 @@ jobs: sudo add-apt-repository 'deb http://apt.llvm.org/noble/ llvm-toolchain-noble-19 main' - name: Install dependencies - run: sudo apt-get update && sudo apt install -y libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev libfuse2 clang-19 mold build-essential libasound2-dev libpulse-dev libopenal-dev libudev-dev + run: sudo apt-get update && sudo apt install -y libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev libfuse2 clang-19 mold build-essential libasound2-dev libpulse-dev libopenal-dev libudev-dev libxcursor-dev libxi-dev libxss-dev libxtst-dev - name: Cache CMake Configuration uses: actions/cache@v4 @@ -216,7 +216,7 @@ jobs: submodules: recursive - name: Install dependencies - run: sudo apt-get update && sudo apt install -y libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev libfuse2 gcc-14 mold build-essential libasound2-dev libpulse-dev libopenal-dev libudev-dev + run: sudo apt-get update && sudo apt install -y libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev libfuse2 gcc-14 mold build-essential libasound2-dev libpulse-dev libopenal-dev libudev-dev libxcursor-dev libxi-dev libxss-dev libxtst-dev - name: Cache CMake Configuration uses: actions/cache@v4 diff --git a/CMakeLists.txt b/CMakeLists.txt index ccd5a4175..d5b63d6fe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -202,8 +202,8 @@ execute_process( # Set Version set(EMULATOR_VERSION_MAJOR "0") -set(EMULATOR_VERSION_MINOR "12") -set(EMULATOR_VERSION_PATCH "6") +set(EMULATOR_VERSION_MINOR "13") +set(EMULATOR_VERSION_PATCH "1") set_source_files_properties(src/shadps4.rc PROPERTIES COMPILE_DEFINITIONS "EMULATOR_VERSION_MAJOR=${EMULATOR_VERSION_MAJOR};EMULATOR_VERSION_MINOR=${EMULATOR_VERSION_MINOR};EMULATOR_VERSION_PATCH=${EMULATOR_VERSION_PATCH}") diff --git a/dist/net.shadps4.shadPS4.metainfo.xml b/dist/net.shadps4.shadPS4.metainfo.xml index c85fcf003..f42840e9b 100644 --- a/dist/net.shadps4.shadPS4.metainfo.xml +++ b/dist/net.shadps4.shadPS4.metainfo.xml @@ -37,6 +37,9 @@ Game + + https://github.com/shadps4-emu/shadPS4/releases/tag/v.0.13.0 + https://github.com/shadps4-emu/shadPS4/releases/tag/v.0.12.5 diff --git a/externals/ffmpeg-core b/externals/ffmpeg-core index b0de1dcca..94dde08c8 160000 --- a/externals/ffmpeg-core +++ b/externals/ffmpeg-core @@ -1 +1 @@ -Subproject commit b0de1dcca26c0ebfb8011b8e59dd17fc399db0ff +Subproject commit 94dde08c8a9e4271a93a2a7e4159e9fb05d30c0a diff --git a/externals/fmt b/externals/fmt index 64db979e3..ec73fb724 160000 --- a/externals/fmt +++ b/externals/fmt @@ -1 +1 @@ -Subproject commit 64db979e38ec644b1798e41610b28c8d2c8a2739 +Subproject commit ec73fb72477d80926c758894a3ab2cb3994fd051 diff --git a/externals/sdl3 b/externals/sdl3 index e9c2e9bfc..bdb72bb3f 160000 --- a/externals/sdl3 +++ b/externals/sdl3 @@ -1 +1 @@ -Subproject commit e9c2e9bfc3a6e1e70596f743fa9e1fc5fadabef7 +Subproject commit bdb72bb3f051de32c91f5deb439a50bfd51499dc diff --git a/src/core/cpu_patches.cpp b/src/core/cpu_patches.cpp index 2788cfe58..e303417c3 100644 --- a/src/core/cpu_patches.cpp +++ b/src/core/cpu_patches.cpp @@ -790,11 +790,12 @@ static bool PatchesIllegalInstructionHandler(void* context) { Common::Decoder::Instance()->decodeInstruction(instruction, operands, code_address); if (ZYAN_SUCCESS(status) && instruction.mnemonic == ZydisMnemonic::ZYDIS_MNEMONIC_UD2) [[unlikely]] { - UNREACHABLE_MSG("ud2 at code address {:#x}", (u64)code_address); + UNREACHABLE_MSG("ud2 at code address {:#x}", reinterpret_cast(code_address)); } - LOG_ERROR(Core, "Failed to patch address {:x} -- mnemonic: {}", (u64)code_address, - ZYAN_SUCCESS(status) ? ZydisMnemonicGetString(instruction.mnemonic) - : "Failed to decode"); + UNREACHABLE_MSG("Failed to patch address {:x} -- mnemonic: {}", + reinterpret_cast(code_address), + ZYAN_SUCCESS(status) ? ZydisMnemonicGetString(instruction.mnemonic) + : "Failed to decode"); } } diff --git a/src/core/file_sys/fs.cpp b/src/core/file_sys/fs.cpp index 2a0fa43dd..f6c34ae94 100644 --- a/src/core/file_sys/fs.cpp +++ b/src/core/file_sys/fs.cpp @@ -52,6 +52,9 @@ std::filesystem::path MntPoints::GetHostPath(std::string_view path, bool* is_rea pos = corrected_path.find("//", pos + 1); } + if (path.length() > 255) + return ""; + const MntPair* mount = GetMount(corrected_path); if (!mount) { return ""; diff --git a/src/core/libraries/ajm/ajm.h b/src/core/libraries/ajm/ajm.h index 2c529cd4b..d68a4c0f4 100644 --- a/src/core/libraries/ajm/ajm.h +++ b/src/core/libraries/ajm/ajm.h @@ -133,7 +133,7 @@ struct AjmSidebandGaplessDecode { struct AjmSidebandResampleParameters { float ratio; - uint32_t flags; + u32 flags; }; struct AjmDecAt9InitializeParameters { diff --git a/src/core/libraries/ajm/ajm_at9.cpp b/src/core/libraries/ajm/ajm_at9.cpp index 014d1a4e5..ea7add4f3 100644 --- a/src/core/libraries/ajm/ajm_at9.cpp +++ b/src/core/libraries/ajm/ajm_at9.cpp @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include "ajm_result.h" #include "common/assert.h" #include "core/libraries/ajm/ajm_at9.h" #include "error_codes.h" @@ -85,8 +86,8 @@ void AjmAt9Decoder::GetInfo(void* out_info) const { auto* info = reinterpret_cast(out_info); info->super_frame_size = m_codec_info.superframeSize; info->frames_in_super_frame = m_codec_info.framesInSuperframe; + info->next_frame_size = m_superframe_bytes_remain; info->frame_samples = m_codec_info.frameSamples; - info->next_frame_size = static_cast(m_handle)->Config.FrameBytes; } u8 g_at9_guid[] = {0xD2, 0x42, 0xE1, 0x47, 0xBA, 0x36, 0x8D, 0x4D, @@ -133,18 +134,22 @@ void AjmAt9Decoder::ParseRIFFHeader(std::span& in_buf, AjmInstanceGapless& g } } -std::tuple AjmAt9Decoder::ProcessData(std::span& in_buf, - SparseOutputBuffer& output, - AjmInstanceGapless& gapless) { - bool is_reset = false; +u32 AjmAt9Decoder::GetMinimumInputSize() const { + return m_superframe_bytes_remain; +} + +DecoderResult AjmAt9Decoder::ProcessData(std::span& in_buf, SparseOutputBuffer& output, + AjmInstanceGapless& gapless) { + DecoderResult result{}; if (True(m_flags & AjmAt9CodecFlags::ParseRiffHeader) && *reinterpret_cast(in_buf.data()) == 'FFIR') { ParseRIFFHeader(in_buf, gapless); - is_reset = true; + result.is_reset = true; } if (!m_is_initialized) { - return {0, 0, is_reset}; + result.result = ORBIS_AJM_RESULT_NOT_INITIALIZED; + return result; } int ret = 0; @@ -166,7 +171,14 @@ std::tuple AjmAt9Decoder::ProcessData(std::span& in_buf, default: UNREACHABLE(); } - ASSERT_MSG(ret == At9Status::ERR_SUCCESS, "Atrac9Decode failed ret = {:#x}", ret); + if (ret != At9Status::ERR_SUCCESS) { + LOG_ERROR(Lib_Ajm, "Atrac9Decode failed ret = {:#x}", ret); + result.result = ORBIS_AJM_RESULT_CODEC_ERROR | ORBIS_AJM_RESULT_FATAL; + result.internal_result = ret; + return result; + } + + result.frames_decoded += 1; in_buf = in_buf.subspan(bytes_used); m_superframe_bytes_remain -= bytes_used; @@ -196,10 +208,10 @@ std::tuple AjmAt9Decoder::ProcessData(std::span& in_buf, UNREACHABLE(); } - const auto samples_written = pcm_written / m_codec_info.channels; - gapless.current.skipped_samples += m_codec_info.frameSamples - samples_written; + result.samples_written = pcm_written / m_codec_info.channels; + gapless.current.skipped_samples += m_codec_info.frameSamples - result.samples_written; if (gapless.init.total_samples != 0) { - gapless.current.total_samples -= samples_written; + gapless.current.total_samples -= result.samples_written; } m_num_frames += 1; @@ -209,9 +221,23 @@ std::tuple AjmAt9Decoder::ProcessData(std::span& in_buf, } m_superframe_bytes_remain = m_codec_info.superframeSize; m_num_frames = 0; + } else if (gapless.IsEnd()) { + // Drain the remaining superframe + std::vector buf(m_codec_info.frameSamples * m_codec_info.channels, 0); + while ((m_num_frames % m_codec_info.framesInSuperframe) != 0) { + ret = Atrac9Decode(m_handle, in_buf.data(), buf.data(), &bytes_used, + True(m_flags & AjmAt9CodecFlags::NonInterleavedOutput)); + in_buf = in_buf.subspan(bytes_used); + m_superframe_bytes_remain -= bytes_used; + result.frames_decoded += 1; + m_num_frames += 1; + } + in_buf = in_buf.subspan(m_superframe_bytes_remain); + m_superframe_bytes_remain = m_codec_info.superframeSize; + m_num_frames = 0; } - return {1, m_codec_info.frameSamples, is_reset}; + return result; } AjmSidebandFormat AjmAt9Decoder::GetFormat() const { @@ -232,7 +258,7 @@ u32 AjmAt9Decoder::GetNextFrameSize(const AjmInstanceGapless& gapless) const { const auto samples = gapless.init.total_samples != 0 ? std::min(gapless.current.total_samples, m_codec_info.frameSamples - skip_samples) - : m_codec_info.frameSamples; + : m_codec_info.frameSamples - skip_samples; return samples * m_codec_info.channels * GetPCMSize(m_format); } diff --git a/src/core/libraries/ajm/ajm_at9.h b/src/core/libraries/ajm/ajm_at9.h index 3262f1aa0..94a718824 100644 --- a/src/core/libraries/ajm/ajm_at9.h +++ b/src/core/libraries/ajm/ajm_at9.h @@ -36,9 +36,10 @@ struct AjmAt9Decoder final : AjmCodec { void Initialize(const void* buffer, u32 buffer_size) override; void GetInfo(void* out_info) const override; AjmSidebandFormat GetFormat() const override; + u32 GetMinimumInputSize() const override; u32 GetNextFrameSize(const AjmInstanceGapless& gapless) const override; - std::tuple ProcessData(std::span& input, SparseOutputBuffer& output, - AjmInstanceGapless& gapless) override; + DecoderResult ProcessData(std::span& input, SparseOutputBuffer& output, + AjmInstanceGapless& gapless) override; private: template diff --git a/src/core/libraries/ajm/ajm_batch.h b/src/core/libraries/ajm/ajm_batch.h index 09daa630d..7f8890898 100644 --- a/src/core/libraries/ajm/ajm_batch.h +++ b/src/core/libraries/ajm/ajm_batch.h @@ -52,6 +52,7 @@ struct AjmBatch { u32 id{}; std::atomic_bool waiting{}; std::atomic_bool canceled{}; + std::atomic_bool processed{}; std::binary_semaphore finished{0}; boost::container::small_vector jobs; diff --git a/src/core/libraries/ajm/ajm_context.cpp b/src/core/libraries/ajm/ajm_context.cpp index 0e2915f32..83d38c5b5 100644 --- a/src/core/libraries/ajm/ajm_context.cpp +++ b/src/core/libraries/ajm/ajm_context.cpp @@ -39,7 +39,12 @@ s32 AjmContext::BatchCancel(const u32 batch_id) { batch = *p_batch; } - batch->canceled = true; + if (batch->processed) { + return ORBIS_OK; + } + + bool expected = false; + batch->canceled.compare_exchange_strong(expected, true); return ORBIS_OK; } @@ -58,7 +63,9 @@ void AjmContext::WorkerThread(std::stop_token stop) { Common::SetCurrentThreadName("shadPS4:AjmWorker"); while (!stop.stop_requested()) { auto batch = batch_queue.PopWait(stop); - if (batch != nullptr) { + if (batch != nullptr && !batch->canceled) { + bool expected = false; + batch->processed.compare_exchange_strong(expected, true); ProcessBatch(batch->id, batch->jobs); batch->finished.release(); } diff --git a/src/core/libraries/ajm/ajm_instance.cpp b/src/core/libraries/ajm/ajm_instance.cpp index c4ea395b9..35685e6a4 100644 --- a/src/core/libraries/ajm/ajm_instance.cpp +++ b/src/core/libraries/ajm/ajm_instance.cpp @@ -1,27 +1,15 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "core/libraries/ajm/ajm_at9.h" -#include "core/libraries/ajm/ajm_instance.h" -#include "core/libraries/ajm/ajm_mp3.h" +#include "ajm_at9.h" +#include "ajm_instance.h" +#include "ajm_mp3.h" +#include "ajm_result.h" #include namespace Libraries::Ajm { -constexpr int ORBIS_AJM_RESULT_NOT_INITIALIZED = 0x00000001; -constexpr int ORBIS_AJM_RESULT_INVALID_DATA = 0x00000002; -constexpr int ORBIS_AJM_RESULT_INVALID_PARAMETER = 0x00000004; -constexpr int ORBIS_AJM_RESULT_PARTIAL_INPUT = 0x00000008; -constexpr int ORBIS_AJM_RESULT_NOT_ENOUGH_ROOM = 0x00000010; -constexpr int ORBIS_AJM_RESULT_STREAM_CHANGE = 0x00000020; -constexpr int ORBIS_AJM_RESULT_TOO_MANY_CHANNELS = 0x00000040; -constexpr int ORBIS_AJM_RESULT_UNSUPPORTED_FLAG = 0x00000080; -constexpr int ORBIS_AJM_RESULT_SIDEBAND_TRUNCATED = 0x00000100; -constexpr int ORBIS_AJM_RESULT_PRIORITY_PASSED = 0x00000200; -constexpr int ORBIS_AJM_RESULT_CODEC_ERROR = 0x40000000; -constexpr int ORBIS_AJM_RESULT_FATAL = 0x80000000; - u8 GetPCMSize(AjmFormatEncoding format) { switch (format) { case AjmFormatEncoding::S16: @@ -60,6 +48,7 @@ void AjmInstance::Reset() { void AjmInstance::ExecuteJob(AjmJob& job) { const auto control_flags = job.flags.control_flags; + job.output.p_result->result = 0; if (True(control_flags & AjmJobControlFlags::Reset)) { LOG_TRACE(Lib_Ajm, "Resetting instance {}", job.instance_id); Reset(); @@ -91,8 +80,7 @@ void AjmInstance::ExecuteJob(AjmJob& job) { m_gapless.current.total_samples -= sample_difference; } else { LOG_WARNING(Lib_Ajm, "ORBIS_AJM_RESULT_INVALID_PARAMETER"); - job.output.p_result->result = ORBIS_AJM_RESULT_INVALID_PARAMETER; - return; + job.output.p_result->result |= ORBIS_AJM_RESULT_INVALID_PARAMETER; } } @@ -106,61 +94,59 @@ void AjmInstance::ExecuteJob(AjmJob& job) { m_gapless.current.skip_samples -= sample_difference; } else { LOG_WARNING(Lib_Ajm, "ORBIS_AJM_RESULT_INVALID_PARAMETER"); - job.output.p_result->result = ORBIS_AJM_RESULT_INVALID_PARAMETER; - return; + job.output.p_result->result |= ORBIS_AJM_RESULT_INVALID_PARAMETER; } } } - if (!job.input.buffer.empty() && !job.output.buffers.empty()) { - std::span in_buf(job.input.buffer); - SparseOutputBuffer out_buf(job.output.buffers); + std::span in_buf(job.input.buffer); + SparseOutputBuffer out_buf(job.output.buffers); + auto in_size = in_buf.size(); + auto out_size = out_buf.Size(); + u32 frames_decoded = 0; - u32 frames_decoded = 0; - auto in_size = in_buf.size(); - auto out_size = out_buf.Size(); - while (!in_buf.empty() && !out_buf.IsEmpty() && !m_gapless.IsEnd()) { - if (!HasEnoughSpace(out_buf)) { - if (job.output.p_mframe == nullptr || frames_decoded == 0) { - LOG_WARNING(Lib_Ajm, "ORBIS_AJM_RESULT_NOT_ENOUGH_ROOM ({} < {})", - out_buf.Size(), m_codec->GetNextFrameSize(m_gapless)); - job.output.p_result->result = ORBIS_AJM_RESULT_NOT_ENOUGH_ROOM; - break; - } - } - - const auto [nframes, nsamples, reset] = - m_codec->ProcessData(in_buf, out_buf, m_gapless); - if (reset) { + if (!job.input.buffer.empty()) { + for (;;) { + if (m_flags.gapless_loop && m_gapless.IsEnd()) { + m_gapless.Reset(); m_total_samples = 0; } - if (!nframes) { - LOG_WARNING(Lib_Ajm, "ORBIS_AJM_RESULT_NOT_INITIALIZED"); - job.output.p_result->result = ORBIS_AJM_RESULT_NOT_INITIALIZED; + if (!HasEnoughSpace(out_buf)) { + LOG_TRACE(Lib_Ajm, "ORBIS_AJM_RESULT_NOT_ENOUGH_ROOM ({} < {})", out_buf.Size(), + m_codec->GetNextFrameSize(m_gapless)); + job.output.p_result->result |= ORBIS_AJM_RESULT_NOT_ENOUGH_ROOM; + } + if (in_buf.size() < m_codec->GetMinimumInputSize()) { + job.output.p_result->result |= ORBIS_AJM_RESULT_PARTIAL_INPUT; + } + if (job.output.p_result->result != 0) { + break; + } + const auto result = m_codec->ProcessData(in_buf, out_buf, m_gapless); + if (result.is_reset) { + m_total_samples = 0; + } else { + m_total_samples += result.samples_written; + } + frames_decoded += result.frames_decoded; + if (result.result != 0) { + job.output.p_result->result |= result.result; + job.output.p_result->internal_result = result.internal_result; break; } - frames_decoded += nframes; - m_total_samples += nsamples; - if (False(job.flags.run_flags & AjmJobRunFlags::MultipleFrames)) { break; } } + } - const auto total_decoded_samples = m_total_samples; - if (m_flags.gapless_loop && m_gapless.IsEnd()) { - in_buf = in_buf.subspan(in_buf.size()); - m_gapless.Reset(); - m_codec->Reset(); - } - if (job.output.p_mframe) { - job.output.p_mframe->num_frames = frames_decoded; - } - if (job.output.p_stream) { - job.output.p_stream->input_consumed = in_size - in_buf.size(); - job.output.p_stream->output_written = out_size - out_buf.Size(); - job.output.p_stream->total_decoded_samples = total_decoded_samples; - } + if (job.output.p_mframe) { + job.output.p_mframe->num_frames = frames_decoded; + } + if (job.output.p_stream) { + job.output.p_stream->input_consumed = in_size - in_buf.size(); + job.output.p_stream->output_written = out_size - out_buf.Size(); + job.output.p_stream->total_decoded_samples = m_total_samples; } if (job.output.p_format != nullptr) { @@ -175,6 +161,9 @@ void AjmInstance::ExecuteJob(AjmJob& job) { } bool AjmInstance::HasEnoughSpace(const SparseOutputBuffer& output) const { + if (m_gapless.IsEnd()) { + return true; + } return output.Size() >= m_codec->GetNextFrameSize(m_gapless); } diff --git a/src/core/libraries/ajm/ajm_instance.h b/src/core/libraries/ajm/ajm_instance.h index ad0a82f29..db53add4d 100644 --- a/src/core/libraries/ajm/ajm_instance.h +++ b/src/core/libraries/ajm/ajm_instance.h @@ -73,6 +73,14 @@ struct AjmInstanceGapless { } }; +struct DecoderResult { + s32 result = 0; + s32 internal_result = 0; + u32 frames_decoded = 0; + u32 samples_written = 0; + bool is_reset = false; +}; + class AjmCodec { public: virtual ~AjmCodec() = default; @@ -81,9 +89,10 @@ public: virtual void Reset() = 0; virtual void GetInfo(void* out_info) const = 0; virtual AjmSidebandFormat GetFormat() const = 0; + virtual u32 GetMinimumInputSize() const = 0; virtual u32 GetNextFrameSize(const AjmInstanceGapless& gapless) const = 0; - virtual std::tuple ProcessData(std::span& input, SparseOutputBuffer& output, - AjmInstanceGapless& gapless) = 0; + virtual DecoderResult ProcessData(std::span& input, SparseOutputBuffer& output, + AjmInstanceGapless& gapless) = 0; }; class AjmInstance { @@ -94,7 +103,6 @@ public: private: bool HasEnoughSpace(const SparseOutputBuffer& output) const; - std::optional GetNumRemainingSamples() const; void Reset(); AjmInstanceFlags m_flags{}; diff --git a/src/core/libraries/ajm/ajm_instance_statistics.cpp b/src/core/libraries/ajm/ajm_instance_statistics.cpp index c0c1af8bb..2e4a65914 100644 --- a/src/core/libraries/ajm/ajm_instance_statistics.cpp +++ b/src/core/libraries/ajm/ajm_instance_statistics.cpp @@ -8,7 +8,7 @@ namespace Libraries::Ajm { void AjmInstanceStatistics::ExecuteJob(AjmJob& job) { if (job.output.p_engine) { - job.output.p_engine->usage_batch = 0.01; + job.output.p_engine->usage_batch = 0.05; const auto ic = job.input.statistics_engine_parameters->interval_count; for (u32 idx = 0; idx < ic; ++idx) { job.output.p_engine->usage_interval[idx] = 0.01; @@ -25,10 +25,12 @@ void AjmInstanceStatistics::ExecuteJob(AjmJob& job) { job.output.p_memory->batch_size = 0x4200; job.output.p_memory->input_size = 0x2000; job.output.p_memory->output_size = 0x2000; - job.output.p_memory->small_size = 0x200; + job.output.p_memory->small_size = 0x400; } } +void AjmInstanceStatistics::Reset() {} + AjmInstanceStatistics& AjmInstanceStatistics::Getinstance() { static AjmInstanceStatistics instance; return instance; diff --git a/src/core/libraries/ajm/ajm_instance_statistics.h b/src/core/libraries/ajm/ajm_instance_statistics.h index ea70c9d56..0ec79aeac 100644 --- a/src/core/libraries/ajm/ajm_instance_statistics.h +++ b/src/core/libraries/ajm/ajm_instance_statistics.h @@ -10,6 +10,7 @@ namespace Libraries::Ajm { class AjmInstanceStatistics { public: void ExecuteJob(AjmJob& job); + void Reset(); static AjmInstanceStatistics& Getinstance(); }; diff --git a/src/core/libraries/ajm/ajm_mp3.cpp b/src/core/libraries/ajm/ajm_mp3.cpp index f17f53d51..f8d77f031 100644 --- a/src/core/libraries/ajm/ajm_mp3.cpp +++ b/src/core/libraries/ajm/ajm_mp3.cpp @@ -122,6 +122,7 @@ void AjmMp3Decoder::Reset() { avcodec_flush_buffers(m_codec_context); m_header.reset(); m_frame_samples = 0; + m_frame_size = 0; } void AjmMp3Decoder::GetInfo(void* out_info) const { @@ -138,16 +139,28 @@ void AjmMp3Decoder::GetInfo(void* out_info) const { } } -std::tuple AjmMp3Decoder::ProcessData(std::span& in_buf, - SparseOutputBuffer& output, - AjmInstanceGapless& gapless) { +u32 AjmMp3Decoder::GetMinimumInputSize() const { + // 4 bytes is for mp3 header that contains frame_size + return std::max(m_frame_size, 4); +} + +DecoderResult AjmMp3Decoder::ProcessData(std::span& in_buf, SparseOutputBuffer& output, + AjmInstanceGapless& gapless) { + DecoderResult result{}; AVPacket* pkt = av_packet_alloc(); if ((!m_header.has_value() || m_frame_samples == 0) && in_buf.size() >= 4) { m_header = std::byteswap(*reinterpret_cast(in_buf.data())); AjmDecMp3ParseFrame info{}; - ParseMp3Header(in_buf.data(), in_buf.size(), false, &info); + ParseMp3Header(in_buf.data(), in_buf.size(), true, &info); m_frame_samples = info.samples_per_channel; + m_frame_size = info.frame_size; + gapless.init = { + .total_samples = info.total_samples, + .skip_samples = static_cast(info.encoder_delay), + .skipped_samples = 0, + }; + gapless.current = gapless.init; } int ret = av_parser_parse2(m_parser, m_codec_context, &pkt->data, &pkt->size, in_buf.data(), @@ -155,9 +168,6 @@ std::tuple AjmMp3Decoder::ProcessData(std::span& in_buf, ASSERT_MSG(ret >= 0, "Error while parsing {}", ret); in_buf = in_buf.subspan(ret); - u32 frames_decoded = 0; - u32 samples_decoded = 0; - if (pkt->size) { // Send the packet with the compressed data to the decoder pkt->pts = m_parser->pts; @@ -177,9 +187,8 @@ std::tuple AjmMp3Decoder::ProcessData(std::span& in_buf, UNREACHABLE_MSG("Error during decoding"); } frame = ConvertAudioFrame(frame); - samples_decoded += u32(frame->nb_samples); - frames_decoded += 1; + result.frames_decoded += 1; u32 skip_samples = 0; if (gapless.current.skip_samples > 0) { skip_samples = std::min(u16(frame->nb_samples), gapless.current.skip_samples); @@ -211,6 +220,7 @@ std::tuple AjmMp3Decoder::ProcessData(std::span& in_buf, if (gapless.init.total_samples != 0) { gapless.current.total_samples -= samples; } + result.samples_written += samples; av_frame_free(&frame); } @@ -218,16 +228,16 @@ std::tuple AjmMp3Decoder::ProcessData(std::span& in_buf, av_packet_free(&pkt); - return {frames_decoded, samples_decoded, false}; + return result; } u32 AjmMp3Decoder::GetNextFrameSize(const AjmInstanceGapless& gapless) const { - const auto max_samples = gapless.init.total_samples != 0 - ? std::min(gapless.current.total_samples, m_frame_samples) - : m_frame_samples; - const auto skip_samples = std::min(u32(gapless.current.skip_samples), max_samples); - return (max_samples - skip_samples) * m_codec_context->ch_layout.nb_channels * - GetPCMSize(m_format); + const auto skip_samples = std::min(gapless.current.skip_samples, m_frame_samples); + const auto samples = + gapless.init.total_samples != 0 + ? std::min(gapless.current.total_samples, m_frame_samples - skip_samples) + : m_frame_samples - skip_samples; + return samples * m_codec_context->ch_layout.nb_channels * GetPCMSize(m_format); } class BitReader { @@ -264,7 +274,7 @@ private: int AjmMp3Decoder::ParseMp3Header(const u8* p_begin, u32 stream_size, int parse_ofl, AjmDecMp3ParseFrame* frame) { - LOG_INFO(Lib_Ajm, "called stream_size = {} parse_ofl = {}", stream_size, parse_ofl); + LOG_TRACE(Lib_Ajm, "called stream_size = {} parse_ofl = {}", stream_size, parse_ofl); if (p_begin == nullptr || stream_size < 4 || frame == nullptr) { return ORBIS_AJM_ERROR_INVALID_PARAMETER; diff --git a/src/core/libraries/ajm/ajm_mp3.h b/src/core/libraries/ajm/ajm_mp3.h index 7ac65fdba..c03d5ba15 100644 --- a/src/core/libraries/ajm/ajm_mp3.h +++ b/src/core/libraries/ajm/ajm_mp3.h @@ -70,9 +70,10 @@ public: void Initialize(const void* buffer, u32 buffer_size) override {} void GetInfo(void* out_info) const override; AjmSidebandFormat GetFormat() const override; + u32 GetMinimumInputSize() const override; u32 GetNextFrameSize(const AjmInstanceGapless& gapless) const override; - std::tuple ProcessData(std::span& input, SparseOutputBuffer& output, - AjmInstanceGapless& gapless) override; + DecoderResult ProcessData(std::span& input, SparseOutputBuffer& output, + AjmInstanceGapless& gapless) override; static int ParseMp3Header(const u8* buf, u32 stream_size, int parse_ofl, AjmDecMp3ParseFrame* frame); @@ -97,6 +98,7 @@ private: SwrContext* m_swr_context = nullptr; std::optional m_header; u32 m_frame_samples = 0; + u32 m_frame_size = 0; }; } // namespace Libraries::Ajm diff --git a/src/core/libraries/ajm/ajm_result.h b/src/core/libraries/ajm/ajm_result.h new file mode 100644 index 000000000..d4e6d1147 --- /dev/null +++ b/src/core/libraries/ajm/ajm_result.h @@ -0,0 +1,17 @@ +// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +constexpr int ORBIS_AJM_RESULT_NOT_INITIALIZED = 0x00000001; +constexpr int ORBIS_AJM_RESULT_INVALID_DATA = 0x00000002; +constexpr int ORBIS_AJM_RESULT_INVALID_PARAMETER = 0x00000004; +constexpr int ORBIS_AJM_RESULT_PARTIAL_INPUT = 0x00000008; +constexpr int ORBIS_AJM_RESULT_NOT_ENOUGH_ROOM = 0x00000010; +constexpr int ORBIS_AJM_RESULT_STREAM_CHANGE = 0x00000020; +constexpr int ORBIS_AJM_RESULT_TOO_MANY_CHANNELS = 0x00000040; +constexpr int ORBIS_AJM_RESULT_UNSUPPORTED_FLAG = 0x00000080; +constexpr int ORBIS_AJM_RESULT_SIDEBAND_TRUNCATED = 0x00000100; +constexpr int ORBIS_AJM_RESULT_PRIORITY_PASSED = 0x00000200; +constexpr int ORBIS_AJM_RESULT_CODEC_ERROR = 0x40000000; +constexpr int ORBIS_AJM_RESULT_FATAL = 0x80000000; diff --git a/src/core/libraries/kernel/file_system.cpp b/src/core/libraries/kernel/file_system.cpp index b4c342f18..5330b90fd 100644 --- a/src/core/libraries/kernel/file_system.cpp +++ b/src/core/libraries/kernel/file_system.cpp @@ -37,6 +37,7 @@ #endif namespace D = Core::Devices; +namespace fs = std::filesystem; using FactoryDevice = std::function(u32, const char*, int, u16)>; #define GET_DEVICE_FD(fd) \ @@ -74,6 +75,7 @@ namespace Libraries::Kernel { s32 PS4_SYSV_ABI open(const char* raw_path, s32 flags, u16 mode) { LOG_INFO(Kernel_Fs, "path = {} flags = {:#x} mode = {:#o}", raw_path, flags, mode); + auto* h = Common::Singleton::Instance(); auto* mnt = Common::Singleton::Instance(); @@ -87,6 +89,11 @@ s32 PS4_SYSV_ABI open(const char* raw_path, s32 flags, u16 mode) { return -1; } + if (strlen(raw_path) > 255) { + *__Error() = POSIX_ENAMETOOLONG; + return -1; + } + bool nonblock = (flags & ORBIS_KERNEL_O_NONBLOCK) != 0; bool append = (flags & ORBIS_KERNEL_O_APPEND) != 0; // Flags fsync and sync behave the same @@ -121,7 +128,7 @@ s32 PS4_SYSV_ABI open(const char* raw_path, s32 flags, u16 mode) { bool read_only = false; file->m_guest_name = path; file->m_host_name = mnt->GetHostPath(file->m_guest_name, &read_only); - bool exists = std::filesystem::exists(file->m_host_name); + bool exists = fs::exists(file->m_host_name); s32 e = 0; if (create) { @@ -149,14 +156,14 @@ s32 PS4_SYSV_ABI open(const char* raw_path, s32 flags, u16 mode) { return -1; } - if (std::filesystem::is_directory(file->m_host_name) || directory) { + if (fs::is_directory(file->m_host_name) || directory) { // Directories can be opened even if the directory flag isn't set. // In these cases, error behavior is identical to the directory code path. directory = true; } if (directory) { - if (!std::filesystem::is_directory(file->m_host_name)) { + if (!fs::is_directory(file->m_host_name)) { // If the opened file is not a directory, return ENOTDIR. // This will trigger when create & directory is specified, this is expected. h->DeleteHandle(handle); @@ -554,6 +561,10 @@ s64 PS4_SYSV_ABI sceKernelRead(s32 fd, void* buf, u64 nbytes) { s32 PS4_SYSV_ABI posix_mkdir(const char* path, u16 mode) { LOG_INFO(Kernel_Fs, "path = {} mode = {:#o}", path, mode); + if (strlen(path) > 255) { + *__Error() = POSIX_ENAMETOOLONG; + return -1; + } if (path == nullptr) { *__Error() = POSIX_ENOTDIR; return -1; @@ -563,7 +574,7 @@ s32 PS4_SYSV_ABI posix_mkdir(const char* path, u16 mode) { bool ro = false; const auto dir_name = mnt->GetHostPath(path, &ro); - if (std::filesystem::exists(dir_name)) { + if (fs::exists(dir_name)) { *__Error() = POSIX_EEXIST; return -1; } @@ -575,12 +586,12 @@ s32 PS4_SYSV_ABI posix_mkdir(const char* path, u16 mode) { // CUSA02456: path = /aotl after sceSaveDataMount(mode = 1) std::error_code ec; - if (dir_name.empty() || !std::filesystem::create_directory(dir_name, ec)) { + if (dir_name.empty() || !fs::create_directory(dir_name, ec)) { *__Error() = POSIX_EIO; return -1; } - if (!std::filesystem::exists(dir_name)) { + if (!fs::exists(dir_name)) { *__Error() = POSIX_ENOENT; return -1; } @@ -597,28 +608,32 @@ s32 PS4_SYSV_ABI sceKernelMkdir(const char* path, u16 mode) { } s32 PS4_SYSV_ABI posix_rmdir(const char* path) { + if (strlen(path) > 255) { + *__Error() = POSIX_ENAMETOOLONG; + return -1; + } auto* mnt = Common::Singleton::Instance(); bool ro = false; - const std::filesystem::path dir_name = mnt->GetHostPath(path, &ro); + const fs::path dir_name = mnt->GetHostPath(path, &ro); if (ro) { *__Error() = POSIX_EROFS; return -1; } - if (dir_name.empty() || !std::filesystem::is_directory(dir_name)) { + if (dir_name.empty() || !fs::is_directory(dir_name)) { *__Error() = POSIX_ENOTDIR; return -1; } - if (!std::filesystem::exists(dir_name)) { + if (!fs::exists(dir_name)) { *__Error() = POSIX_ENOENT; return -1; } std::error_code ec; - s32 result = std::filesystem::remove_all(dir_name, ec); + s32 result = fs::remove_all(dir_name, ec); if (ec) { *__Error() = POSIX_EIO; @@ -638,11 +653,15 @@ s32 PS4_SYSV_ABI sceKernelRmdir(const char* path) { s32 PS4_SYSV_ABI posix_stat(const char* path, OrbisKernelStat* sb) { LOG_DEBUG(Kernel_Fs, "(PARTIAL) path = {}", path); + if (strlen(path) > 255) { + *__Error() = POSIX_ENAMETOOLONG; + return -1; + } auto* mnt = Common::Singleton::Instance(); const auto path_name = mnt->GetHostPath(path); std::memset(sb, 0, sizeof(OrbisKernelStat)); - const bool is_dir = std::filesystem::is_directory(path_name); - const bool is_file = std::filesystem::is_regular_file(path_name); + const bool is_dir = fs::is_directory(path_name); + const bool is_file = fs::is_regular_file(path_name); if (!is_dir && !is_file) { *__Error() = POSIX_ENOENT; return -1; @@ -650,12 +669,12 @@ s32 PS4_SYSV_ABI posix_stat(const char* path, OrbisKernelStat* sb) { // get the difference between file clock and system clock const auto now_sys = std::chrono::system_clock::now(); - const auto now_file = std::filesystem::file_time_type::clock::now(); + const auto now_file = fs::file_time_type::clock::now(); // calculate the file modified time - const auto mtime = std::filesystem::last_write_time(path_name); + const auto mtime = fs::last_write_time(path_name); const auto mtimestamp = now_sys + (mtime - now_file); - if (std::filesystem::is_directory(path_name)) { + if (fs::is_directory(path_name)) { sb->st_mode = 0000777u | 0040000u; sb->st_size = 65536; sb->st_blksize = 65536; @@ -665,7 +684,7 @@ s32 PS4_SYSV_ABI posix_stat(const char* path, OrbisKernelStat* sb) { // TODO incomplete } else { sb->st_mode = 0000777u | 0100000u; - sb->st_size = static_cast(std::filesystem::file_size(path_name)); + sb->st_size = static_cast(fs::file_size(path_name)); sb->st_blksize = 512; sb->st_blocks = (sb->st_size + 511) / 512; sb->st_mtim.tv_sec = @@ -686,6 +705,10 @@ s32 PS4_SYSV_ABI sceKernelStat(const char* path, OrbisKernelStat* sb) { } s32 PS4_SYSV_ABI sceKernelCheckReachability(const char* path) { + if (strlen(path) > 255) { + return ORBIS_KERNEL_ERROR_ENAMETOOLONG; + } + auto* mnt = Common::Singleton::Instance(); std::string_view guest_path{path}; for (const auto& prefix : available_device | std::views::keys) { @@ -694,7 +717,7 @@ s32 PS4_SYSV_ABI sceKernelCheckReachability(const char* path) { } } const auto path_name = mnt->GetHostPath(guest_path); - if (!std::filesystem::exists(path_name)) { + if (!fs::exists(path_name)) { return ORBIS_KERNEL_ERROR_ENOENT; } return ORBIS_OK; @@ -807,7 +830,15 @@ s32 PS4_SYSV_ABI posix_rename(const char* from, const char* to) { auto* mnt = Common::Singleton::Instance(); bool ro = false; const auto src_path = mnt->GetHostPath(from, &ro); - if (!std::filesystem::exists(src_path)) { + if (strlen(from) > 255) { + *__Error() = POSIX_ENAMETOOLONG; + return -1; + } + if (strlen(to) > 255) { + *__Error() = POSIX_ENAMETOOLONG; + return -1; + } + if (!fs::exists(src_path)) { *__Error() = POSIX_ENOENT; return -1; } @@ -820,32 +851,36 @@ s32 PS4_SYSV_ABI posix_rename(const char* from, const char* to) { *__Error() = POSIX_EROFS; return -1; } - const bool src_is_dir = std::filesystem::is_directory(src_path); - const bool dst_is_dir = std::filesystem::is_directory(dst_path); - if (src_is_dir && !dst_is_dir) { - *__Error() = POSIX_ENOTDIR; - return -1; - } - if (!src_is_dir && dst_is_dir) { - *__Error() = POSIX_EISDIR; - return -1; - } - if (dst_is_dir && !std::filesystem::is_empty(dst_path)) { - *__Error() = POSIX_ENOTEMPTY; - return -1; + const bool src_is_dir = fs::is_directory(src_path); + const bool dst_is_dir = fs::is_directory(dst_path); + + if (fs::exists(dst_path)) { + if (src_is_dir && !dst_is_dir) { + *__Error() = POSIX_ENOTDIR; + return -1; + } + if (!src_is_dir && dst_is_dir) { + *__Error() = POSIX_EISDIR; + return -1; + } + if (dst_is_dir && !fs::is_empty(dst_path)) { + *__Error() = POSIX_ENOTEMPTY; + return -1; + } } - // On Windows, std::filesystem::rename will error if the file has been opened before. - std::filesystem::copy(src_path, dst_path, std::filesystem::copy_options::overwrite_existing); + // On Windows, fs::rename will error if the file has been opened before. + fs::copy(src_path, dst_path, + fs::copy_options::overwrite_existing | fs::copy_options::recursive); auto* h = Common::Singleton::Instance(); auto file = h->GetFile(src_path); if (file) { auto access_mode = file->f.GetAccessMode(); file->f.Close(); - std::filesystem::remove(src_path); + fs::remove(src_path); file->f.Open(dst_path, access_mode); } else { - std::filesystem::remove(src_path); + fs::remove_all(src_path); } return ORBIS_OK; @@ -1098,6 +1133,10 @@ s64 PS4_SYSV_ABI sceKernelPwritev(s32 fd, const OrbisKernelIovec* iov, s32 iovcn } s32 PS4_SYSV_ABI posix_unlink(const char* path) { + if (strlen(path) > 255) { + *__Error() = POSIX_ENAMETOOLONG; + return -1; + } if (path == nullptr) { *__Error() = POSIX_EINVAL; return -1; @@ -1118,7 +1157,7 @@ s32 PS4_SYSV_ABI posix_unlink(const char* path) { return -1; } - if (std::filesystem::is_directory(host_path)) { + if (fs::is_directory(host_path)) { *__Error() = POSIX_EPERM; return -1; } @@ -1491,6 +1530,7 @@ void RegisterFileSystem(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("FCcmRZhWtOk", "libkernel", 1, "libkernel", posix_pwritev); LIB_FUNCTION("nKWi-N2HBV4", "libkernel", 1, "libkernel", sceKernelPwrite); LIB_FUNCTION("mBd4AfLP+u8", "libkernel", 1, "libkernel", sceKernelPwritev); + LIB_FUNCTION("VAzswvTOCzI", "libkernel", 1, "libkernel", posix_unlink); LIB_FUNCTION("AUXVxWeJU-A", "libkernel", 1, "libkernel", sceKernelUnlink); LIB_FUNCTION("T8fER+tIGgk", "libScePosix", 1, "libkernel", posix_select); LIB_FUNCTION("T8fER+tIGgk", "libkernel", 1, "libkernel", posix_select); diff --git a/src/core/libraries/network/net.cpp b/src/core/libraries/network/net.cpp index 97005813b..a365d407b 100644 --- a/src/core/libraries/network/net.cpp +++ b/src/core/libraries/network/net.cpp @@ -1357,7 +1357,8 @@ int PS4_SYSV_ABI sceNetResolverConnectDestroy() { } int PS4_SYSV_ABI sceNetResolverCreate(const char* name, int poolid, int flags) { - LOG_INFO(Lib_Net, "name = {}, poolid = {}, flags = {}", name, poolid, flags); + const char* safe_name = name ? name : ""; + LOG_INFO(Lib_Net, "name = {}, poolid = {}, flags = {}", safe_name, poolid, flags); if (flags != 0) { *sceNetErrnoLoc() = ORBIS_NET_EINVAL; @@ -1368,8 +1369,8 @@ int PS4_SYSV_ABI sceNetResolverCreate(const char* name, int poolid, int flags) { auto* resolver = FDTable::Instance()->GetFile(fd); resolver->is_opened = true; resolver->type = Core::FileSys::FileType::Resolver; - resolver->resolver = std::make_shared(name, poolid, flags); - resolver->m_guest_name = name; + resolver->resolver = std::make_shared(safe_name, poolid, flags); + resolver->m_guest_name = safe_name; return fd; } diff --git a/src/core/linker.cpp b/src/core/linker.cpp index 5587b151c..644f9c374 100644 --- a/src/core/linker.cpp +++ b/src/core/linker.cpp @@ -105,17 +105,6 @@ void Linker::Execute(const std::vector& args) { memory->SetupMemoryRegions(fmem_size, use_extended_mem1, use_extended_mem2); - // Simulate sceKernelInternalMemory mapping, a mapping usually performed during libkernel init. - // Due to the large size of this mapping, failing to emulate it causes issues in some titles. - // This mapping belongs in the system reserved area, which starts at address 0x880000000. - static constexpr VAddr KernelAllocBase = 0x880000000ULL; - static constexpr s64 InternalMemorySize = 0x1000000; - void* addr_out{reinterpret_cast(KernelAllocBase)}; - - s32 ret = Libraries::Kernel::sceKernelMapNamedFlexibleMemory(&addr_out, InternalMemorySize, 3, - 0, "SceKernelInternalMemory"); - ASSERT_MSG(ret == 0, "Unable to perform sceKernelInternalMemory mapping"); - main_thread.Run([this, module, &args](std::stop_token) { Common::SetCurrentThreadName("GAME_MainThread"); if (auto& ipc = IPC::Instance()) { diff --git a/src/video_core/renderer_vulkan/vk_instance.cpp b/src/video_core/renderer_vulkan/vk_instance.cpp index ca7d09c52..44aa79d98 100644 --- a/src/video_core/renderer_vulkan/vk_instance.cpp +++ b/src/video_core/renderer_vulkan/vk_instance.cpp @@ -299,6 +299,7 @@ bool Instance::CreateDevice() { amd_shader_trinary_minmax = add_extension(VK_AMD_SHADER_TRINARY_MINMAX_EXTENSION_NAME); nv_framebuffer_mixed_samples = add_extension(VK_NV_FRAMEBUFFER_MIXED_SAMPLES_EXTENSION_NAME); amd_mixed_attachment_samples = add_extension(VK_AMD_MIXED_ATTACHMENT_SAMPLES_EXTENSION_NAME); + shader_atomic_float = add_extension(VK_EXT_SHADER_ATOMIC_FLOAT_EXTENSION_NAME); shader_atomic_float2 = add_extension(VK_EXT_SHADER_ATOMIC_FLOAT_2_EXTENSION_NAME); if (shader_atomic_float2) { shader_atomic_float2_features = diff --git a/src/video_core/renderer_vulkan/vk_instance.h b/src/video_core/renderer_vulkan/vk_instance.h index bbefdc1b3..8975669bb 100644 --- a/src/video_core/renderer_vulkan/vk_instance.h +++ b/src/video_core/renderer_vulkan/vk_instance.h @@ -493,6 +493,7 @@ private: bool amd_shader_trinary_minmax{}; bool nv_framebuffer_mixed_samples{}; bool amd_mixed_attachment_samples{}; + bool shader_atomic_float{}; bool shader_atomic_float2{}; bool workgroup_memory_explicit_layout{}; bool portability_subset{};