mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2026-04-02 19:08:03 -06:00
Merge branch 'main' into user_and_settings
This commit is contained in:
commit
b62c79675f
4
.github/workflows/build.yml
vendored
4
.github/workflows/build.yml
vendored
@ -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
|
||||
|
||||
@ -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}")
|
||||
|
||||
|
||||
3
dist/net.shadps4.shadPS4.metainfo.xml
vendored
3
dist/net.shadps4.shadPS4.metainfo.xml
vendored
@ -37,6 +37,9 @@
|
||||
<category translate="no">Game</category>
|
||||
</categories>
|
||||
<releases>
|
||||
<release version="0.13.0" date="2025-12-24">
|
||||
<url>https://github.com/shadps4-emu/shadPS4/releases/tag/v.0.13.0</url>
|
||||
</release>
|
||||
<release version="0.12.5" date="2025-11-07">
|
||||
<url>https://github.com/shadps4-emu/shadPS4/releases/tag/v.0.12.5</url>
|
||||
</release>
|
||||
|
||||
2
externals/ffmpeg-core
vendored
2
externals/ffmpeg-core
vendored
@ -1 +1 @@
|
||||
Subproject commit b0de1dcca26c0ebfb8011b8e59dd17fc399db0ff
|
||||
Subproject commit 94dde08c8a9e4271a93a2a7e4159e9fb05d30c0a
|
||||
2
externals/fmt
vendored
2
externals/fmt
vendored
@ -1 +1 @@
|
||||
Subproject commit 64db979e38ec644b1798e41610b28c8d2c8a2739
|
||||
Subproject commit ec73fb72477d80926c758894a3ab2cb3994fd051
|
||||
2
externals/sdl3
vendored
2
externals/sdl3
vendored
@ -1 +1 @@
|
||||
Subproject commit e9c2e9bfc3a6e1e70596f743fa9e1fc5fadabef7
|
||||
Subproject commit bdb72bb3f051de32c91f5deb439a50bfd51499dc
|
||||
@ -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<u64>(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<u64>(code_address),
|
||||
ZYAN_SUCCESS(status) ? ZydisMnemonicGetString(instruction.mnemonic)
|
||||
: "Failed to decode");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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 "";
|
||||
|
||||
@ -133,7 +133,7 @@ struct AjmSidebandGaplessDecode {
|
||||
|
||||
struct AjmSidebandResampleParameters {
|
||||
float ratio;
|
||||
uint32_t flags;
|
||||
u32 flags;
|
||||
};
|
||||
|
||||
struct AjmDecAt9InitializeParameters {
|
||||
|
||||
@ -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<AjmSidebandDecAt9CodecInfo*>(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<Atrac9Handle*>(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<u8>& in_buf, AjmInstanceGapless& g
|
||||
}
|
||||
}
|
||||
|
||||
std::tuple<u32, u32, bool> AjmAt9Decoder::ProcessData(std::span<u8>& in_buf,
|
||||
SparseOutputBuffer& output,
|
||||
AjmInstanceGapless& gapless) {
|
||||
bool is_reset = false;
|
||||
u32 AjmAt9Decoder::GetMinimumInputSize() const {
|
||||
return m_superframe_bytes_remain;
|
||||
}
|
||||
|
||||
DecoderResult AjmAt9Decoder::ProcessData(std::span<u8>& in_buf, SparseOutputBuffer& output,
|
||||
AjmInstanceGapless& gapless) {
|
||||
DecoderResult result{};
|
||||
if (True(m_flags & AjmAt9CodecFlags::ParseRiffHeader) &&
|
||||
*reinterpret_cast<u32*>(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<u32, u32, bool> AjmAt9Decoder::ProcessData(std::span<u8>& 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<u32, u32, bool> AjmAt9Decoder::ProcessData(std::span<u8>& 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<u32, u32, bool> AjmAt9Decoder::ProcessData(std::span<u8>& in_buf,
|
||||
}
|
||||
m_superframe_bytes_remain = m_codec_info.superframeSize;
|
||||
m_num_frames = 0;
|
||||
} else if (gapless.IsEnd()) {
|
||||
// Drain the remaining superframe
|
||||
std::vector<s16> 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<u32>(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);
|
||||
}
|
||||
|
||||
|
||||
@ -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<u32, u32, bool> ProcessData(std::span<u8>& input, SparseOutputBuffer& output,
|
||||
AjmInstanceGapless& gapless) override;
|
||||
DecoderResult ProcessData(std::span<u8>& input, SparseOutputBuffer& output,
|
||||
AjmInstanceGapless& gapless) override;
|
||||
|
||||
private:
|
||||
template <class T>
|
||||
|
||||
@ -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<AjmJob, 16> jobs;
|
||||
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
@ -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 <magic_enum/magic_enum.hpp>
|
||||
|
||||
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<u8> in_buf(job.input.buffer);
|
||||
SparseOutputBuffer out_buf(job.output.buffers);
|
||||
std::span<u8> 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);
|
||||
}
|
||||
|
||||
|
||||
@ -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<u32, u32, bool> ProcessData(std::span<u8>& input, SparseOutputBuffer& output,
|
||||
AjmInstanceGapless& gapless) = 0;
|
||||
virtual DecoderResult ProcessData(std::span<u8>& input, SparseOutputBuffer& output,
|
||||
AjmInstanceGapless& gapless) = 0;
|
||||
};
|
||||
|
||||
class AjmInstance {
|
||||
@ -94,7 +103,6 @@ public:
|
||||
|
||||
private:
|
||||
bool HasEnoughSpace(const SparseOutputBuffer& output) const;
|
||||
std::optional<u32> GetNumRemainingSamples() const;
|
||||
void Reset();
|
||||
|
||||
AjmInstanceFlags m_flags{};
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -10,6 +10,7 @@ namespace Libraries::Ajm {
|
||||
class AjmInstanceStatistics {
|
||||
public:
|
||||
void ExecuteJob(AjmJob& job);
|
||||
void Reset();
|
||||
|
||||
static AjmInstanceStatistics& Getinstance();
|
||||
};
|
||||
|
||||
@ -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<u32, u32, bool> AjmMp3Decoder::ProcessData(std::span<u8>& in_buf,
|
||||
SparseOutputBuffer& output,
|
||||
AjmInstanceGapless& gapless) {
|
||||
u32 AjmMp3Decoder::GetMinimumInputSize() const {
|
||||
// 4 bytes is for mp3 header that contains frame_size
|
||||
return std::max<u32>(m_frame_size, 4);
|
||||
}
|
||||
|
||||
DecoderResult AjmMp3Decoder::ProcessData(std::span<u8>& 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<u32*>(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<u16>(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<u32, u32, bool> AjmMp3Decoder::ProcessData(std::span<u8>& 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<u32, u32, bool> AjmMp3Decoder::ProcessData(std::span<u8>& 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<u32, u32, bool> AjmMp3Decoder::ProcessData(std::span<u8>& 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<u32, u32, bool> AjmMp3Decoder::ProcessData(std::span<u8>& 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<u32>(gapless.current.skip_samples, m_frame_samples);
|
||||
const auto samples =
|
||||
gapless.init.total_samples != 0
|
||||
? std::min<u32>(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;
|
||||
|
||||
@ -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<u32, u32, bool> ProcessData(std::span<u8>& input, SparseOutputBuffer& output,
|
||||
AjmInstanceGapless& gapless) override;
|
||||
DecoderResult ProcessData(std::span<u8>& 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<u32> m_header;
|
||||
u32 m_frame_samples = 0;
|
||||
u32 m_frame_size = 0;
|
||||
};
|
||||
|
||||
} // namespace Libraries::Ajm
|
||||
|
||||
17
src/core/libraries/ajm/ajm_result.h
Normal file
17
src/core/libraries/ajm/ajm_result.h
Normal file
@ -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;
|
||||
@ -37,6 +37,7 @@
|
||||
#endif
|
||||
|
||||
namespace D = Core::Devices;
|
||||
namespace fs = std::filesystem;
|
||||
using FactoryDevice = std::function<std::shared_ptr<D::BaseDevice>(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<Core::FileSys::HandleTable>::Instance();
|
||||
auto* mnt = Common::Singleton<Core::FileSys::MntPoints>::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<Core::FileSys::MntPoints>::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<Core::FileSys::MntPoints>::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<s64>(std::filesystem::file_size(path_name));
|
||||
sb->st_size = static_cast<s64>(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<Core::FileSys::MntPoints>::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<Core::FileSys::MntPoints>::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<Core::FileSys::HandleTable>::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);
|
||||
|
||||
@ -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<Resolver>(name, poolid, flags);
|
||||
resolver->m_guest_name = name;
|
||||
resolver->resolver = std::make_shared<Resolver>(safe_name, poolid, flags);
|
||||
resolver->m_guest_name = safe_name;
|
||||
return fd;
|
||||
}
|
||||
|
||||
|
||||
@ -105,17 +105,6 @@ void Linker::Execute(const std::vector<std::string>& 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<void*>(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()) {
|
||||
|
||||
@ -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 =
|
||||
|
||||
@ -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{};
|
||||
|
||||
Loading…
Reference in New Issue
Block a user