Merge branch 'master' of https://github.com/yuzu-emu/yuzu into GDBWork
This commit is contained in:
@@ -3,6 +3,7 @@ include_directories(.)
|
|||||||
|
|
||||||
add_subdirectory(common)
|
add_subdirectory(common)
|
||||||
add_subdirectory(core)
|
add_subdirectory(core)
|
||||||
|
add_subdirectory(audio_core)
|
||||||
add_subdirectory(video_core)
|
add_subdirectory(video_core)
|
||||||
add_subdirectory(input_common)
|
add_subdirectory(input_common)
|
||||||
add_subdirectory(tests)
|
add_subdirectory(tests)
|
||||||
|
|||||||
11
src/audio_core/CMakeLists.txt
Normal file
11
src/audio_core/CMakeLists.txt
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
add_library(audio_core STATIC
|
||||||
|
audio_out.cpp
|
||||||
|
audio_out.h
|
||||||
|
buffer.h
|
||||||
|
stream.cpp
|
||||||
|
stream.h
|
||||||
|
)
|
||||||
|
|
||||||
|
create_target_directory_groups(audio_core)
|
||||||
|
|
||||||
|
target_link_libraries(audio_core PUBLIC common core)
|
||||||
50
src/audio_core/audio_out.cpp
Normal file
50
src/audio_core/audio_out.cpp
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
// Copyright 2018 yuzu Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include "audio_core/audio_out.h"
|
||||||
|
#include "common/assert.h"
|
||||||
|
#include "common/logging/log.h"
|
||||||
|
|
||||||
|
namespace AudioCore {
|
||||||
|
|
||||||
|
/// Returns the stream format from the specified number of channels
|
||||||
|
static Stream::Format ChannelsToStreamFormat(int num_channels) {
|
||||||
|
switch (num_channels) {
|
||||||
|
case 1:
|
||||||
|
return Stream::Format::Mono16;
|
||||||
|
case 2:
|
||||||
|
return Stream::Format::Stereo16;
|
||||||
|
case 6:
|
||||||
|
return Stream::Format::Multi51Channel16;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_CRITICAL(Audio, "Unimplemented num_channels={}", num_channels);
|
||||||
|
UNREACHABLE();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
StreamPtr AudioOut::OpenStream(int sample_rate, int num_channels,
|
||||||
|
Stream::ReleaseCallback&& release_callback) {
|
||||||
|
streams.push_back(std::make_shared<Stream>(sample_rate, ChannelsToStreamFormat(num_channels),
|
||||||
|
std::move(release_callback)));
|
||||||
|
return streams.back();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<u64> AudioOut::GetTagsAndReleaseBuffers(StreamPtr stream, size_t max_count) {
|
||||||
|
return stream->GetTagsAndReleaseBuffers(max_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioOut::StartStream(StreamPtr stream) {
|
||||||
|
stream->Play();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioOut::StopStream(StreamPtr stream) {
|
||||||
|
stream->Stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AudioOut::QueueBuffer(StreamPtr stream, Buffer::Tag tag, std::vector<u8>&& data) {
|
||||||
|
return stream->QueueBuffer(std::make_shared<Buffer>(tag, std::move(data)));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace AudioCore
|
||||||
44
src/audio_core/audio_out.h
Normal file
44
src/audio_core/audio_out.h
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
// Copyright 2018 yuzu Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "audio_core/buffer.h"
|
||||||
|
#include "audio_core/stream.h"
|
||||||
|
#include "common/common_types.h"
|
||||||
|
|
||||||
|
namespace AudioCore {
|
||||||
|
|
||||||
|
using StreamPtr = std::shared_ptr<Stream>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents an audio playback interface, used to open and play audio streams
|
||||||
|
*/
|
||||||
|
class AudioOut {
|
||||||
|
public:
|
||||||
|
/// Opens a new audio stream
|
||||||
|
StreamPtr OpenStream(int sample_rate, int num_channels,
|
||||||
|
Stream::ReleaseCallback&& release_callback);
|
||||||
|
|
||||||
|
/// Returns a vector of recently released buffers specified by tag for the specified stream
|
||||||
|
std::vector<u64> GetTagsAndReleaseBuffers(StreamPtr stream, size_t max_count);
|
||||||
|
|
||||||
|
/// Starts an audio stream for playback
|
||||||
|
void StartStream(StreamPtr stream);
|
||||||
|
|
||||||
|
/// Stops an audio stream that is currently playing
|
||||||
|
void StopStream(StreamPtr stream);
|
||||||
|
|
||||||
|
/// Queues a buffer into the specified audio stream, returns true on success
|
||||||
|
bool QueueBuffer(StreamPtr stream, Buffer::Tag tag, std::vector<u8>&& data);
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// Active audio streams on the interface
|
||||||
|
std::vector<StreamPtr> streams;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace AudioCore
|
||||||
37
src/audio_core/buffer.h
Normal file
37
src/audio_core/buffer.h
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
// Copyright 2018 yuzu Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "common/common_types.h"
|
||||||
|
|
||||||
|
namespace AudioCore {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a buffer of audio samples to be played in an audio stream
|
||||||
|
*/
|
||||||
|
class Buffer {
|
||||||
|
public:
|
||||||
|
using Tag = u64;
|
||||||
|
|
||||||
|
Buffer(Tag tag, std::vector<u8>&& data) : tag{tag}, data{std::move(data)} {}
|
||||||
|
|
||||||
|
/// Returns the raw audio data for the buffer
|
||||||
|
const std::vector<u8>& GetData() const {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the buffer tag, this is provided by the game to the audout service
|
||||||
|
Tag GetTag() const {
|
||||||
|
return tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Tag tag;
|
||||||
|
std::vector<u8> data;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace AudioCore
|
||||||
103
src/audio_core/stream.cpp
Normal file
103
src/audio_core/stream.cpp
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
// Copyright 2018 yuzu Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include "common/assert.h"
|
||||||
|
#include "common/logging/log.h"
|
||||||
|
#include "core/core_timing.h"
|
||||||
|
#include "core/core_timing_util.h"
|
||||||
|
|
||||||
|
#include "audio_core/stream.h"
|
||||||
|
|
||||||
|
namespace AudioCore {
|
||||||
|
|
||||||
|
constexpr size_t MaxAudioBufferCount{32};
|
||||||
|
|
||||||
|
/// Returns the sample size for the specified audio stream format
|
||||||
|
static size_t SampleSizeFromFormat(Stream::Format format) {
|
||||||
|
switch (format) {
|
||||||
|
case Stream::Format::Mono16:
|
||||||
|
return 2;
|
||||||
|
case Stream::Format::Stereo16:
|
||||||
|
return 4;
|
||||||
|
case Stream::Format::Multi51Channel16:
|
||||||
|
return 12;
|
||||||
|
};
|
||||||
|
|
||||||
|
LOG_CRITICAL(Audio, "Unimplemented format={}", static_cast<u32>(format));
|
||||||
|
UNREACHABLE();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
Stream::Stream(int sample_rate, Format format, ReleaseCallback&& release_callback)
|
||||||
|
: sample_rate{sample_rate}, format{format}, release_callback{std::move(release_callback)} {
|
||||||
|
release_event = CoreTiming::RegisterEvent(
|
||||||
|
"Stream::Release", [this](u64 userdata, int cycles_late) { ReleaseActiveBuffer(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
void Stream::Play() {
|
||||||
|
state = State::Playing;
|
||||||
|
PlayNextBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Stream::Stop() {
|
||||||
|
ASSERT_MSG(false, "Unimplemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
s64 Stream::GetBufferReleaseCycles(const Buffer& buffer) const {
|
||||||
|
const size_t num_samples{buffer.GetData().size() / SampleSizeFromFormat(format)};
|
||||||
|
return CoreTiming::usToCycles((static_cast<u64>(num_samples) * 1000000) / sample_rate);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Stream::PlayNextBuffer() {
|
||||||
|
if (!IsPlaying()) {
|
||||||
|
// Ensure we are in playing state before playing the next buffer
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (active_buffer) {
|
||||||
|
// Do not queue a new buffer if we are already playing a buffer
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (queued_buffers.empty()) {
|
||||||
|
// No queued buffers - we are effectively paused
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
active_buffer = queued_buffers.front();
|
||||||
|
queued_buffers.pop();
|
||||||
|
|
||||||
|
CoreTiming::ScheduleEventThreadsafe(GetBufferReleaseCycles(*active_buffer), release_event, {});
|
||||||
|
}
|
||||||
|
|
||||||
|
void Stream::ReleaseActiveBuffer() {
|
||||||
|
released_buffers.push(std::move(active_buffer));
|
||||||
|
release_callback();
|
||||||
|
PlayNextBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Stream::QueueBuffer(BufferPtr&& buffer) {
|
||||||
|
if (queued_buffers.size() < MaxAudioBufferCount) {
|
||||||
|
queued_buffers.push(std::move(buffer));
|
||||||
|
PlayNextBuffer();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Stream::ContainsBuffer(Buffer::Tag tag) const {
|
||||||
|
ASSERT_MSG(false, "Unimplemented");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Buffer::Tag> Stream::GetTagsAndReleaseBuffers(size_t max_count) {
|
||||||
|
std::vector<Buffer::Tag> tags;
|
||||||
|
for (size_t count = 0; count < max_count && !released_buffers.empty(); ++count) {
|
||||||
|
tags.push_back(released_buffers.front()->GetTag());
|
||||||
|
released_buffers.pop();
|
||||||
|
}
|
||||||
|
return tags;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace AudioCore
|
||||||
89
src/audio_core/stream.h
Normal file
89
src/audio_core/stream.h
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
// Copyright 2018 yuzu Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
#include <queue>
|
||||||
|
|
||||||
|
#include "audio_core/buffer.h"
|
||||||
|
#include "common/assert.h"
|
||||||
|
#include "common/common_types.h"
|
||||||
|
#include "core/core_timing.h"
|
||||||
|
|
||||||
|
namespace AudioCore {
|
||||||
|
|
||||||
|
using BufferPtr = std::shared_ptr<Buffer>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents an audio stream, which is a sequence of queued buffers, to be outputed by AudioOut
|
||||||
|
*/
|
||||||
|
class Stream {
|
||||||
|
public:
|
||||||
|
/// Audio format of the stream
|
||||||
|
enum class Format {
|
||||||
|
Mono16,
|
||||||
|
Stereo16,
|
||||||
|
Multi51Channel16,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Callback function type, used to change guest state on a buffer being released
|
||||||
|
using ReleaseCallback = std::function<void()>;
|
||||||
|
|
||||||
|
Stream(int sample_rate, Format format, ReleaseCallback&& release_callback);
|
||||||
|
|
||||||
|
/// Plays the audio stream
|
||||||
|
void Play();
|
||||||
|
|
||||||
|
/// Stops the audio stream
|
||||||
|
void Stop();
|
||||||
|
|
||||||
|
/// Queues a buffer into the audio stream, returns true on success
|
||||||
|
bool QueueBuffer(BufferPtr&& buffer);
|
||||||
|
|
||||||
|
/// Returns true if the audio stream contains a buffer with the specified tag
|
||||||
|
bool ContainsBuffer(Buffer::Tag tag) const;
|
||||||
|
|
||||||
|
/// Returns a vector of recently released buffers specified by tag
|
||||||
|
std::vector<Buffer::Tag> GetTagsAndReleaseBuffers(size_t max_count);
|
||||||
|
|
||||||
|
/// Returns true if the stream is currently playing
|
||||||
|
bool IsPlaying() const {
|
||||||
|
return state == State::Playing;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the number of queued buffers
|
||||||
|
size_t GetQueueSize() const {
|
||||||
|
return queued_buffers.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// Current state of the stream
|
||||||
|
enum class State {
|
||||||
|
Stopped,
|
||||||
|
Playing,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Plays the next queued buffer in the audio stream, starting playback if necessary
|
||||||
|
void PlayNextBuffer();
|
||||||
|
|
||||||
|
/// Releases the actively playing buffer, signalling that it has been completed
|
||||||
|
void ReleaseActiveBuffer();
|
||||||
|
|
||||||
|
/// Gets the number of core cycles when the specified buffer will be released
|
||||||
|
s64 GetBufferReleaseCycles(const Buffer& buffer) const;
|
||||||
|
|
||||||
|
int sample_rate; ///< Sample rate of the stream
|
||||||
|
Format format; ///< Format of the stream
|
||||||
|
ReleaseCallback release_callback; ///< Buffer release callback for the stream
|
||||||
|
State state{State::Stopped}; ///< Playback state of the stream
|
||||||
|
CoreTiming::EventType* release_event{}; ///< Core timing release event for the stream
|
||||||
|
BufferPtr active_buffer; ///< Actively playing buffer in the stream
|
||||||
|
std::queue<BufferPtr> queued_buffers; ///< Buffers queued to be played in the stream
|
||||||
|
std::queue<BufferPtr> released_buffers; ///< Buffers recently released from the stream
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace AudioCore
|
||||||
@@ -173,9 +173,11 @@ void FileBackend::Write(const Entry& entry) {
|
|||||||
SUB(Service, Friend) \
|
SUB(Service, Friend) \
|
||||||
SUB(Service, FS) \
|
SUB(Service, FS) \
|
||||||
SUB(Service, HID) \
|
SUB(Service, HID) \
|
||||||
|
SUB(Service, LBL) \
|
||||||
SUB(Service, LDN) \
|
SUB(Service, LDN) \
|
||||||
SUB(Service, LM) \
|
SUB(Service, LM) \
|
||||||
SUB(Service, MM) \
|
SUB(Service, MM) \
|
||||||
|
SUB(Service, NFC) \
|
||||||
SUB(Service, NFP) \
|
SUB(Service, NFP) \
|
||||||
SUB(Service, NIFM) \
|
SUB(Service, NIFM) \
|
||||||
SUB(Service, NS) \
|
SUB(Service, NS) \
|
||||||
|
|||||||
@@ -60,9 +60,11 @@ enum class Class : ClassType {
|
|||||||
Service_Friend, ///< The friend service
|
Service_Friend, ///< The friend service
|
||||||
Service_FS, ///< The FS (Filesystem) service
|
Service_FS, ///< The FS (Filesystem) service
|
||||||
Service_HID, ///< The HID (Human interface device) service
|
Service_HID, ///< The HID (Human interface device) service
|
||||||
|
Service_LBL, ///< The LBL (LCD backlight) service
|
||||||
Service_LDN, ///< The LDN (Local domain network) service
|
Service_LDN, ///< The LDN (Local domain network) service
|
||||||
Service_LM, ///< The LM (Logger) service
|
Service_LM, ///< The LM (Logger) service
|
||||||
Service_MM, ///< The MM (Multimedia) service
|
Service_MM, ///< The MM (Multimedia) service
|
||||||
|
Service_NFC, ///< The NFC (Near-field communication) service
|
||||||
Service_NFP, ///< The NFP service
|
Service_NFP, ///< The NFP service
|
||||||
Service_NIFM, ///< The NIFM (Network interface) service
|
Service_NIFM, ///< The NIFM (Network interface) service
|
||||||
Service_NS, ///< The NS services
|
Service_NS, ///< The NS services
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ add_library(core STATIC
|
|||||||
file_sys/partition_filesystem.h
|
file_sys/partition_filesystem.h
|
||||||
file_sys/program_metadata.cpp
|
file_sys/program_metadata.cpp
|
||||||
file_sys/program_metadata.h
|
file_sys/program_metadata.h
|
||||||
|
file_sys/romfs.cpp
|
||||||
|
file_sys/romfs.h
|
||||||
file_sys/romfs_factory.cpp
|
file_sys/romfs_factory.cpp
|
||||||
file_sys/romfs_factory.h
|
file_sys/romfs_factory.h
|
||||||
file_sys/savedata_factory.cpp
|
file_sys/savedata_factory.cpp
|
||||||
@@ -35,6 +37,8 @@ add_library(core STATIC
|
|||||||
file_sys/vfs_offset.h
|
file_sys/vfs_offset.h
|
||||||
file_sys/vfs_real.cpp
|
file_sys/vfs_real.cpp
|
||||||
file_sys/vfs_real.h
|
file_sys/vfs_real.h
|
||||||
|
file_sys/vfs_vector.cpp
|
||||||
|
file_sys/vfs_vector.h
|
||||||
frontend/emu_window.cpp
|
frontend/emu_window.cpp
|
||||||
frontend/emu_window.h
|
frontend/emu_window.h
|
||||||
frontend/framebuffer_layout.cpp
|
frontend/framebuffer_layout.cpp
|
||||||
@@ -136,6 +140,8 @@ add_library(core STATIC
|
|||||||
hle/service/bcat/bcat.h
|
hle/service/bcat/bcat.h
|
||||||
hle/service/bcat/module.cpp
|
hle/service/bcat/module.cpp
|
||||||
hle/service/bcat/module.h
|
hle/service/bcat/module.h
|
||||||
|
hle/service/btdrv/btdrv.cpp
|
||||||
|
hle/service/btdrv/btdrv.h
|
||||||
hle/service/erpt/erpt.cpp
|
hle/service/erpt/erpt.cpp
|
||||||
hle/service/erpt/erpt.h
|
hle/service/erpt/erpt.h
|
||||||
hle/service/es/es.cpp
|
hle/service/es/es.cpp
|
||||||
@@ -164,6 +170,8 @@ add_library(core STATIC
|
|||||||
hle/service/hid/irs.h
|
hle/service/hid/irs.h
|
||||||
hle/service/hid/xcd.cpp
|
hle/service/hid/xcd.cpp
|
||||||
hle/service/hid/xcd.h
|
hle/service/hid/xcd.h
|
||||||
|
hle/service/lbl/lbl.cpp
|
||||||
|
hle/service/lbl/lbl.h
|
||||||
hle/service/ldn/ldn.cpp
|
hle/service/ldn/ldn.cpp
|
||||||
hle/service/ldn/ldn.h
|
hle/service/ldn/ldn.h
|
||||||
hle/service/ldr/ldr.cpp
|
hle/service/ldr/ldr.cpp
|
||||||
@@ -172,6 +180,8 @@ add_library(core STATIC
|
|||||||
hle/service/lm/lm.h
|
hle/service/lm/lm.h
|
||||||
hle/service/mm/mm_u.cpp
|
hle/service/mm/mm_u.cpp
|
||||||
hle/service/mm/mm_u.h
|
hle/service/mm/mm_u.h
|
||||||
|
hle/service/nfc/nfc.cpp
|
||||||
|
hle/service/nfc/nfc.h
|
||||||
hle/service/nfp/nfp.cpp
|
hle/service/nfp/nfp.cpp
|
||||||
hle/service/nfp/nfp.h
|
hle/service/nfp/nfp.h
|
||||||
hle/service/nfp/nfp_user.cpp
|
hle/service/nfp/nfp_user.cpp
|
||||||
@@ -299,7 +309,7 @@ add_library(core STATIC
|
|||||||
|
|
||||||
create_target_directory_groups(core)
|
create_target_directory_groups(core)
|
||||||
|
|
||||||
target_link_libraries(core PUBLIC common PRIVATE video_core)
|
target_link_libraries(core PUBLIC common PRIVATE audio_core video_core)
|
||||||
target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt lz4_static unicorn)
|
target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt lz4_static unicorn)
|
||||||
|
|
||||||
if (ARCHITECTURE_x86_64)
|
if (ARCHITECTURE_x86_64)
|
||||||
|
|||||||
@@ -182,6 +182,7 @@ System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
gpu_core = std::make_unique<Tegra::GPU>();
|
gpu_core = std::make_unique<Tegra::GPU>();
|
||||||
|
audio_core = std::make_unique<AudioCore::AudioOut>();
|
||||||
telemetry_session = std::make_unique<Core::TelemetrySession>();
|
telemetry_session = std::make_unique<Core::TelemetrySession>();
|
||||||
service_manager = std::make_shared<Service::SM::ServiceManager>();
|
service_manager = std::make_shared<Service::SM::ServiceManager>();
|
||||||
|
|
||||||
@@ -233,6 +234,7 @@ void System::Shutdown() {
|
|||||||
service_manager.reset();
|
service_manager.reset();
|
||||||
telemetry_session.reset();
|
telemetry_session.reset();
|
||||||
gpu_core.reset();
|
gpu_core.reset();
|
||||||
|
audio_core.reset();
|
||||||
|
|
||||||
// Close all CPU/threading state
|
// Close all CPU/threading state
|
||||||
cpu_barrier->NotifyEnd();
|
cpu_barrier->NotifyEnd();
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
#include "audio_core/audio_out.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "core/arm/exclusive_monitor.h"
|
#include "core/arm/exclusive_monitor.h"
|
||||||
#include "core/core_cpu.h"
|
#include "core/core_cpu.h"
|
||||||
@@ -131,6 +132,11 @@ public:
|
|||||||
return *gpu_core;
|
return *gpu_core;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gets the AudioCore interface
|
||||||
|
AudioCore::AudioOut& AudioCore() {
|
||||||
|
return *audio_core;
|
||||||
|
}
|
||||||
|
|
||||||
/// Gets the scheduler for the CPU core that is currently running
|
/// Gets the scheduler for the CPU core that is currently running
|
||||||
Kernel::Scheduler& CurrentScheduler() {
|
Kernel::Scheduler& CurrentScheduler() {
|
||||||
return *CurrentCpuCore().Scheduler();
|
return *CurrentCpuCore().Scheduler();
|
||||||
@@ -195,6 +201,7 @@ private:
|
|||||||
/// AppLoader used to load the current executing application
|
/// AppLoader used to load the current executing application
|
||||||
std::unique_ptr<Loader::AppLoader> app_loader;
|
std::unique_ptr<Loader::AppLoader> app_loader;
|
||||||
std::unique_ptr<Tegra::GPU> gpu_core;
|
std::unique_ptr<Tegra::GPU> gpu_core;
|
||||||
|
std::unique_ptr<AudioCore::AudioOut> audio_core;
|
||||||
std::shared_ptr<Tegra::DebugContext> debug_context;
|
std::shared_ptr<Tegra::DebugContext> debug_context;
|
||||||
Kernel::SharedPtr<Kernel::Process> current_process;
|
Kernel::SharedPtr<Kernel::Process> current_process;
|
||||||
std::shared_ptr<ExclusiveMonitor> cpu_exclusive_monitor;
|
std::shared_ptr<ExclusiveMonitor> cpu_exclusive_monitor;
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#include "core/file_sys/content_archive.h"
|
#include "core/file_sys/content_archive.h"
|
||||||
#include "core/file_sys/vfs_offset.h"
|
#include "core/file_sys/vfs_offset.h"
|
||||||
#include "core/loader/loader.h"
|
#include "core/loader/loader.h"
|
||||||
|
#include "romfs.h"
|
||||||
|
|
||||||
namespace FileSys {
|
namespace FileSys {
|
||||||
|
|
||||||
@@ -46,21 +47,9 @@ struct PFS0Superblock {
|
|||||||
};
|
};
|
||||||
static_assert(sizeof(PFS0Superblock) == 0x200, "PFS0Superblock has incorrect size.");
|
static_assert(sizeof(PFS0Superblock) == 0x200, "PFS0Superblock has incorrect size.");
|
||||||
|
|
||||||
struct IVFCLevel {
|
|
||||||
u64_le offset;
|
|
||||||
u64_le size;
|
|
||||||
u32_le block_size;
|
|
||||||
u32_le reserved;
|
|
||||||
};
|
|
||||||
static_assert(sizeof(IVFCLevel) == 0x18, "IVFCLevel has incorrect size.");
|
|
||||||
|
|
||||||
struct RomFSSuperblock {
|
struct RomFSSuperblock {
|
||||||
NCASectionHeaderBlock header_block;
|
NCASectionHeaderBlock header_block;
|
||||||
u32_le magic;
|
IVFCHeader ivfc;
|
||||||
u32_le magic_number;
|
|
||||||
INSERT_PADDING_BYTES(8);
|
|
||||||
std::array<IVFCLevel, 6> levels;
|
|
||||||
INSERT_PADDING_BYTES(64);
|
|
||||||
};
|
};
|
||||||
static_assert(sizeof(RomFSSuperblock) == 0xE8, "RomFSSuperblock has incorrect size.");
|
static_assert(sizeof(RomFSSuperblock) == 0xE8, "RomFSSuperblock has incorrect size.");
|
||||||
|
|
||||||
@@ -92,8 +81,8 @@ NCA::NCA(VirtualFile file_) : file(std::move(file_)) {
|
|||||||
|
|
||||||
const size_t romfs_offset =
|
const size_t romfs_offset =
|
||||||
header.section_tables[i].media_offset * MEDIA_OFFSET_MULTIPLIER +
|
header.section_tables[i].media_offset * MEDIA_OFFSET_MULTIPLIER +
|
||||||
sb.levels[IVFC_MAX_LEVEL - 1].offset;
|
sb.ivfc.levels[IVFC_MAX_LEVEL - 1].offset;
|
||||||
const size_t romfs_size = sb.levels[IVFC_MAX_LEVEL - 1].size;
|
const size_t romfs_size = sb.ivfc.levels[IVFC_MAX_LEVEL - 1].size;
|
||||||
files.emplace_back(std::make_shared<OffsetVfsFile>(file, romfs_size, romfs_offset));
|
files.emplace_back(std::make_shared<OffsetVfsFile>(file, romfs_size, romfs_offset));
|
||||||
romfs = files.back();
|
romfs = files.back();
|
||||||
} else if (block.filesystem_type == NCASectionFilesystemType::PFS0) {
|
} else if (block.filesystem_type == NCASectionFilesystemType::PFS0) {
|
||||||
|
|||||||
124
src/core/file_sys/romfs.cpp
Normal file
124
src/core/file_sys/romfs.cpp
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
// Copyright 2018 yuzu emulator team
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include "common/common_types.h"
|
||||||
|
#include "common/swap.h"
|
||||||
|
#include "core/file_sys/romfs.h"
|
||||||
|
#include "core/file_sys/vfs.h"
|
||||||
|
#include "core/file_sys/vfs_offset.h"
|
||||||
|
#include "core/file_sys/vfs_vector.h"
|
||||||
|
|
||||||
|
namespace FileSys {
|
||||||
|
|
||||||
|
constexpr u32 ROMFS_ENTRY_EMPTY = 0xFFFFFFFF;
|
||||||
|
|
||||||
|
struct TableLocation {
|
||||||
|
u64_le offset;
|
||||||
|
u64_le size;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(TableLocation) == 0x10, "TableLocation has incorrect size.");
|
||||||
|
|
||||||
|
struct RomFSHeader {
|
||||||
|
u64_le header_size;
|
||||||
|
TableLocation directory_hash;
|
||||||
|
TableLocation directory_meta;
|
||||||
|
TableLocation file_hash;
|
||||||
|
TableLocation file_meta;
|
||||||
|
u64_le data_offset;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(RomFSHeader) == 0x50, "RomFSHeader has incorrect size.");
|
||||||
|
|
||||||
|
struct DirectoryEntry {
|
||||||
|
u32_le sibling;
|
||||||
|
u32_le child_dir;
|
||||||
|
u32_le child_file;
|
||||||
|
u32_le hash;
|
||||||
|
u32_le name_length;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(DirectoryEntry) == 0x14, "DirectoryEntry has incorrect size.");
|
||||||
|
|
||||||
|
struct FileEntry {
|
||||||
|
u32_le parent;
|
||||||
|
u32_le sibling;
|
||||||
|
u64_le offset;
|
||||||
|
u64_le size;
|
||||||
|
u32_le hash;
|
||||||
|
u32_le name_length;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(FileEntry) == 0x20, "FileEntry has incorrect size.");
|
||||||
|
|
||||||
|
template <typename Entry>
|
||||||
|
static std::pair<Entry, std::string> GetEntry(const VirtualFile& file, size_t offset) {
|
||||||
|
Entry entry{};
|
||||||
|
if (file->ReadObject(&entry, offset) != sizeof(Entry))
|
||||||
|
return {};
|
||||||
|
std::string string(entry.name_length, '\0');
|
||||||
|
if (file->ReadArray(&string[0], string.size(), offset + sizeof(Entry)) != string.size())
|
||||||
|
return {};
|
||||||
|
return {entry, string};
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProcessFile(VirtualFile file, size_t file_offset, size_t data_offset, u32 this_file_offset,
|
||||||
|
std::shared_ptr<VectorVfsDirectory> parent) {
|
||||||
|
while (true) {
|
||||||
|
auto entry = GetEntry<FileEntry>(file, file_offset + this_file_offset);
|
||||||
|
|
||||||
|
parent->AddFile(std::make_shared<OffsetVfsFile>(
|
||||||
|
file, entry.first.size, entry.first.offset + data_offset, entry.second, parent));
|
||||||
|
|
||||||
|
if (entry.first.sibling == ROMFS_ENTRY_EMPTY)
|
||||||
|
break;
|
||||||
|
|
||||||
|
this_file_offset = entry.first.sibling;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProcessDirectory(VirtualFile file, size_t dir_offset, size_t file_offset, size_t data_offset,
|
||||||
|
u32 this_dir_offset, std::shared_ptr<VectorVfsDirectory> parent) {
|
||||||
|
while (true) {
|
||||||
|
auto entry = GetEntry<DirectoryEntry>(file, dir_offset + this_dir_offset);
|
||||||
|
auto current = std::make_shared<VectorVfsDirectory>(
|
||||||
|
std::vector<VirtualFile>{}, std::vector<VirtualDir>{}, parent, entry.second);
|
||||||
|
|
||||||
|
if (entry.first.child_file != ROMFS_ENTRY_EMPTY) {
|
||||||
|
ProcessFile(file, file_offset, data_offset, entry.first.child_file, current);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry.first.child_dir != ROMFS_ENTRY_EMPTY) {
|
||||||
|
ProcessDirectory(file, dir_offset, file_offset, data_offset, entry.first.child_dir,
|
||||||
|
current);
|
||||||
|
}
|
||||||
|
|
||||||
|
parent->AddDirectory(current);
|
||||||
|
if (entry.first.sibling == ROMFS_ENTRY_EMPTY)
|
||||||
|
break;
|
||||||
|
this_dir_offset = entry.first.sibling;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualDir ExtractRomFS(VirtualFile file) {
|
||||||
|
RomFSHeader header{};
|
||||||
|
if (file->ReadObject(&header) != sizeof(RomFSHeader))
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
if (header.header_size != sizeof(RomFSHeader))
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
const u64 file_offset = header.file_meta.offset;
|
||||||
|
const u64 dir_offset = header.directory_meta.offset + 4;
|
||||||
|
|
||||||
|
const auto root =
|
||||||
|
std::make_shared<VectorVfsDirectory>(std::vector<VirtualFile>{}, std::vector<VirtualDir>{},
|
||||||
|
file->GetContainingDirectory(), file->GetName());
|
||||||
|
|
||||||
|
ProcessDirectory(file, dir_offset, file_offset, header.data_offset, 0, root);
|
||||||
|
|
||||||
|
VirtualDir out = std::move(root);
|
||||||
|
|
||||||
|
while (out->GetSubdirectory("") != nullptr)
|
||||||
|
out = out->GetSubdirectory("");
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
} // namespace FileSys
|
||||||
35
src/core/file_sys/romfs.h
Normal file
35
src/core/file_sys/romfs.h
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
// Copyright 2018 yuzu emulator team
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include "common/common_funcs.h"
|
||||||
|
#include "common/swap.h"
|
||||||
|
#include "core/file_sys/vfs.h"
|
||||||
|
|
||||||
|
namespace FileSys {
|
||||||
|
|
||||||
|
struct IVFCLevel {
|
||||||
|
u64_le offset;
|
||||||
|
u64_le size;
|
||||||
|
u32_le block_size;
|
||||||
|
u32_le reserved;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(IVFCLevel) == 0x18, "IVFCLevel has incorrect size.");
|
||||||
|
|
||||||
|
struct IVFCHeader {
|
||||||
|
u32_le magic;
|
||||||
|
u32_le magic_number;
|
||||||
|
INSERT_PADDING_BYTES(8);
|
||||||
|
std::array<IVFCLevel, 6> levels;
|
||||||
|
INSERT_PADDING_BYTES(64);
|
||||||
|
};
|
||||||
|
static_assert(sizeof(IVFCHeader) == 0xE0, "IVFCHeader has incorrect size.");
|
||||||
|
|
||||||
|
// Converts a RomFS binary blob to VFS Filesystem
|
||||||
|
// Returns nullptr on failure
|
||||||
|
VirtualDir ExtractRomFS(VirtualFile file);
|
||||||
|
|
||||||
|
} // namespace FileSys
|
||||||
@@ -46,6 +46,13 @@ size_t VfsFile::WriteBytes(const std::vector<u8>& data, size_t offset) {
|
|||||||
return Write(data.data(), data.size(), offset);
|
return Write(data.data(), data.size(), offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string VfsFile::GetFullPath() const {
|
||||||
|
if (GetContainingDirectory() == nullptr)
|
||||||
|
return "/" + GetName();
|
||||||
|
|
||||||
|
return GetContainingDirectory()->GetFullPath() + "/" + GetName();
|
||||||
|
}
|
||||||
|
|
||||||
std::shared_ptr<VfsFile> VfsDirectory::GetFileRelative(std::string_view path) const {
|
std::shared_ptr<VfsFile> VfsDirectory::GetFileRelative(std::string_view path) const {
|
||||||
auto vec = FileUtil::SplitPathComponents(path);
|
auto vec = FileUtil::SplitPathComponents(path);
|
||||||
vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }),
|
vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }),
|
||||||
@@ -243,6 +250,13 @@ bool VfsDirectory::Copy(std::string_view src, std::string_view dest) {
|
|||||||
return f2->WriteBytes(f1->ReadAllBytes()) == f1->GetSize();
|
return f2->WriteBytes(f1->ReadAllBytes()) == f1->GetSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string VfsDirectory::GetFullPath() const {
|
||||||
|
if (IsRoot())
|
||||||
|
return GetName();
|
||||||
|
|
||||||
|
return GetParentDirectory()->GetFullPath() + "/" + GetName();
|
||||||
|
}
|
||||||
|
|
||||||
bool ReadOnlyVfsDirectory::IsWritable() const {
|
bool ReadOnlyVfsDirectory::IsWritable() const {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -270,4 +284,13 @@ bool ReadOnlyVfsDirectory::DeleteFile(std::string_view name) {
|
|||||||
bool ReadOnlyVfsDirectory::Rename(std::string_view name) {
|
bool ReadOnlyVfsDirectory::Rename(std::string_view name) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool VfsRawCopy(VirtualFile src, VirtualFile dest) {
|
||||||
|
if (src == nullptr || dest == nullptr)
|
||||||
|
return false;
|
||||||
|
if (!dest->Resize(src->GetSize()))
|
||||||
|
return false;
|
||||||
|
std::vector<u8> data = src->ReadAllBytes();
|
||||||
|
return dest->WriteBytes(data, 0) == data.size();
|
||||||
|
}
|
||||||
} // namespace FileSys
|
} // namespace FileSys
|
||||||
|
|||||||
@@ -113,6 +113,9 @@ struct VfsFile : NonCopyable {
|
|||||||
|
|
||||||
// Renames the file to name. Returns whether or not the operation was successsful.
|
// Renames the file to name. Returns whether or not the operation was successsful.
|
||||||
virtual bool Rename(std::string_view name) = 0;
|
virtual bool Rename(std::string_view name) = 0;
|
||||||
|
|
||||||
|
// Returns the full path of this file as a string, recursively
|
||||||
|
virtual std::string GetFullPath() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
// A class representing a directory in an abstract filesystem.
|
// A class representing a directory in an abstract filesystem.
|
||||||
@@ -213,6 +216,17 @@ struct VfsDirectory : NonCopyable {
|
|||||||
return ReplaceFileWithSubdirectory(file_p, std::make_shared<Directory>(file_p));
|
return ReplaceFileWithSubdirectory(file_p, std::make_shared<Directory>(file_p));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool InterpretAsDirectory(const std::function<VirtualDir(VirtualFile)>& function,
|
||||||
|
const std::string& file) {
|
||||||
|
auto file_p = GetFile(file);
|
||||||
|
if (file_p == nullptr)
|
||||||
|
return false;
|
||||||
|
return ReplaceFileWithSubdirectory(file_p, function(file_p));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the full path of this directory as a string, recursively
|
||||||
|
virtual std::string GetFullPath() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// Backend for InterpretAsDirectory.
|
// Backend for InterpretAsDirectory.
|
||||||
// Removes all references to file and adds a reference to dir in the directory's implementation.
|
// Removes all references to file and adds a reference to dir in the directory's implementation.
|
||||||
@@ -230,4 +244,10 @@ struct ReadOnlyVfsDirectory : public VfsDirectory {
|
|||||||
bool DeleteFile(std::string_view name) override;
|
bool DeleteFile(std::string_view name) override;
|
||||||
bool Rename(std::string_view name) override;
|
bool Rename(std::string_view name) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// A method that copies the raw data between two different implementations of VirtualFile. If you
|
||||||
|
// are using the same implementation, it is probably better to use the Copy method in the parent
|
||||||
|
// directory of src/dest.
|
||||||
|
bool VfsRawCopy(VirtualFile src, VirtualFile dest);
|
||||||
|
|
||||||
} // namespace FileSys
|
} // namespace FileSys
|
||||||
|
|||||||
@@ -10,8 +10,9 @@
|
|||||||
namespace FileSys {
|
namespace FileSys {
|
||||||
|
|
||||||
OffsetVfsFile::OffsetVfsFile(std::shared_ptr<VfsFile> file_, size_t size_, size_t offset_,
|
OffsetVfsFile::OffsetVfsFile(std::shared_ptr<VfsFile> file_, size_t size_, size_t offset_,
|
||||||
std::string name_)
|
std::string name_, VirtualDir parent_)
|
||||||
: file(std::move(file_)), offset(offset_), size(size_), name(std::move(name_)) {}
|
: file(file_), offset(offset_), size(size_), name(std::move(name_)),
|
||||||
|
parent(parent_ == nullptr ? file->GetContainingDirectory() : std::move(parent_)) {}
|
||||||
|
|
||||||
std::string OffsetVfsFile::GetName() const {
|
std::string OffsetVfsFile::GetName() const {
|
||||||
return name.empty() ? file->GetName() : name;
|
return name.empty() ? file->GetName() : name;
|
||||||
@@ -35,7 +36,7 @@ bool OffsetVfsFile::Resize(size_t new_size) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<VfsDirectory> OffsetVfsFile::GetContainingDirectory() const {
|
std::shared_ptr<VfsDirectory> OffsetVfsFile::GetContainingDirectory() const {
|
||||||
return file->GetContainingDirectory();
|
return parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OffsetVfsFile::IsWritable() const {
|
bool OffsetVfsFile::IsWritable() const {
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ namespace FileSys {
|
|||||||
// the size of this wrapper.
|
// the size of this wrapper.
|
||||||
struct OffsetVfsFile : public VfsFile {
|
struct OffsetVfsFile : public VfsFile {
|
||||||
OffsetVfsFile(std::shared_ptr<VfsFile> file, size_t size, size_t offset = 0,
|
OffsetVfsFile(std::shared_ptr<VfsFile> file, size_t size, size_t offset = 0,
|
||||||
std::string new_name = "");
|
std::string new_name = "", VirtualDir new_parent = nullptr);
|
||||||
|
|
||||||
std::string GetName() const override;
|
std::string GetName() const override;
|
||||||
size_t GetSize() const override;
|
size_t GetSize() const override;
|
||||||
@@ -44,6 +44,7 @@ private:
|
|||||||
size_t offset;
|
size_t offset;
|
||||||
size_t size;
|
size_t size;
|
||||||
std::string name;
|
std::string name;
|
||||||
|
VirtualDir parent;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace FileSys
|
} // namespace FileSys
|
||||||
|
|||||||
@@ -195,6 +195,12 @@ bool RealVfsDirectory::Rename(std::string_view name) {
|
|||||||
return FileUtil::Rename(path, new_name);
|
return FileUtil::Rename(path, new_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string RealVfsDirectory::GetFullPath() const {
|
||||||
|
auto out = path;
|
||||||
|
std::replace(out.begin(), out.end(), '\\', '/');
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
bool RealVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
|
bool RealVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
|
||||||
const auto iter = std::find(files.begin(), files.end(), file);
|
const auto iter = std::find(files.begin(), files.end(), file);
|
||||||
if (iter == files.end())
|
if (iter == files.end())
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ private:
|
|||||||
|
|
||||||
// An implementation of VfsDirectory that represents a directory on the user's computer.
|
// An implementation of VfsDirectory that represents a directory on the user's computer.
|
||||||
struct RealVfsDirectory : public VfsDirectory {
|
struct RealVfsDirectory : public VfsDirectory {
|
||||||
RealVfsDirectory(const std::string& path, Mode perms);
|
RealVfsDirectory(const std::string& path, Mode perms = Mode::Read);
|
||||||
|
|
||||||
std::vector<std::shared_ptr<VfsFile>> GetFiles() const override;
|
std::vector<std::shared_ptr<VfsFile>> GetFiles() const override;
|
||||||
std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override;
|
std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override;
|
||||||
@@ -54,6 +54,7 @@ struct RealVfsDirectory : public VfsDirectory {
|
|||||||
bool DeleteSubdirectory(std::string_view name) override;
|
bool DeleteSubdirectory(std::string_view name) override;
|
||||||
bool DeleteFile(std::string_view name) override;
|
bool DeleteFile(std::string_view name) override;
|
||||||
bool Rename(std::string_view name) override;
|
bool Rename(std::string_view name) override;
|
||||||
|
std::string GetFullPath() const override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
|
bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
|
||||||
|
|||||||
83
src/core/file_sys/vfs_vector.cpp
Normal file
83
src/core/file_sys/vfs_vector.cpp
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
// Copyright 2018 yuzu emulator team
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include "core/file_sys/vfs_vector.h"
|
||||||
|
|
||||||
|
namespace FileSys {
|
||||||
|
VectorVfsDirectory::VectorVfsDirectory(std::vector<VirtualFile> files_,
|
||||||
|
std::vector<VirtualDir> dirs_, VirtualDir parent_,
|
||||||
|
std::string name_)
|
||||||
|
: files(std::move(files_)), dirs(std::move(dirs_)), parent(std::move(parent_)),
|
||||||
|
name(std::move(name_)) {}
|
||||||
|
|
||||||
|
std::vector<std::shared_ptr<VfsFile>> VectorVfsDirectory::GetFiles() const {
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::shared_ptr<VfsDirectory>> VectorVfsDirectory::GetSubdirectories() const {
|
||||||
|
return dirs;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VectorVfsDirectory::IsWritable() const {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VectorVfsDirectory::IsReadable() const {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string VectorVfsDirectory::GetName() const {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
std::shared_ptr<VfsDirectory> VectorVfsDirectory::GetParentDirectory() const {
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static bool FindAndRemoveVectorElement(std::vector<T>& vec, std::string_view name) {
|
||||||
|
auto iter = std::find_if(vec.begin(), vec.end(), [name](T e) { return e->GetName() == name; });
|
||||||
|
if (iter == vec.end())
|
||||||
|
return false;
|
||||||
|
auto old_size = vec.size();
|
||||||
|
vec.erase(iter);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VectorVfsDirectory::DeleteSubdirectory(std::string_view name) {
|
||||||
|
return FindAndRemoveVectorElement(dirs, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VectorVfsDirectory::DeleteFile(std::string_view name) {
|
||||||
|
return FindAndRemoveVectorElement(files, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VectorVfsDirectory::Rename(std::string_view name_) {
|
||||||
|
name = name_;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<VfsDirectory> VectorVfsDirectory::CreateSubdirectory(std::string_view name) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<VfsFile> VectorVfsDirectory::CreateFile(std::string_view name) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VectorVfsDirectory::AddFile(VirtualFile file) {
|
||||||
|
files.push_back(std::move(file));
|
||||||
|
}
|
||||||
|
|
||||||
|
void VectorVfsDirectory::AddDirectory(VirtualDir dir) {
|
||||||
|
dirs.push_back(std::move(dir));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VectorVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
|
||||||
|
if (!DeleteFile(file->GetName()))
|
||||||
|
return false;
|
||||||
|
dirs.emplace_back(dir);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} // namespace FileSys
|
||||||
44
src/core/file_sys/vfs_vector.h
Normal file
44
src/core/file_sys/vfs_vector.h
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
// Copyright 2018 yuzu emulator team
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "core/file_sys/vfs.h"
|
||||||
|
|
||||||
|
namespace FileSys {
|
||||||
|
|
||||||
|
// An implementation of VfsDirectory that maintains two vectors for subdirectories and files.
|
||||||
|
// Vector data is supplied upon construction.
|
||||||
|
struct VectorVfsDirectory : public VfsDirectory {
|
||||||
|
explicit VectorVfsDirectory(std::vector<VirtualFile> files = {},
|
||||||
|
std::vector<VirtualDir> dirs = {}, VirtualDir parent = nullptr,
|
||||||
|
std::string name = "");
|
||||||
|
|
||||||
|
std::vector<std::shared_ptr<VfsFile>> GetFiles() const override;
|
||||||
|
std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override;
|
||||||
|
bool IsWritable() const override;
|
||||||
|
bool IsReadable() const override;
|
||||||
|
std::string GetName() const override;
|
||||||
|
std::shared_ptr<VfsDirectory> GetParentDirectory() const override;
|
||||||
|
bool DeleteSubdirectory(std::string_view name) override;
|
||||||
|
bool DeleteFile(std::string_view name) override;
|
||||||
|
bool Rename(std::string_view name) override;
|
||||||
|
std::shared_ptr<VfsDirectory> CreateSubdirectory(std::string_view name) override;
|
||||||
|
std::shared_ptr<VfsFile> CreateFile(std::string_view name) override;
|
||||||
|
|
||||||
|
virtual void AddFile(VirtualFile file);
|
||||||
|
virtual void AddDirectory(VirtualDir dir);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<VirtualFile> files;
|
||||||
|
std::vector<VirtualDir> dirs;
|
||||||
|
|
||||||
|
VirtualDir parent;
|
||||||
|
std::string name;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace FileSys
|
||||||
@@ -5,8 +5,7 @@
|
|||||||
#include <array>
|
#include <array>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "core/core_timing.h"
|
#include "core/core.h"
|
||||||
#include "core/core_timing_util.h"
|
|
||||||
#include "core/hle/ipc_helpers.h"
|
#include "core/hle/ipc_helpers.h"
|
||||||
#include "core/hle/kernel/event.h"
|
#include "core/hle/kernel/event.h"
|
||||||
#include "core/hle/kernel/hle_ipc.h"
|
#include "core/hle/kernel/hle_ipc.h"
|
||||||
@@ -14,17 +13,22 @@
|
|||||||
|
|
||||||
namespace Service::Audio {
|
namespace Service::Audio {
|
||||||
|
|
||||||
/// Switch sample rate frequency
|
namespace ErrCodes {
|
||||||
constexpr u32 sample_rate{48000};
|
enum {
|
||||||
/// TODO(st4rk): dynamic number of channels, as I think Switch has support
|
ErrorUnknown = 2,
|
||||||
/// to more audio channels (probably when Docked I guess)
|
BufferCountExceeded = 8,
|
||||||
constexpr u32 audio_channels{2};
|
};
|
||||||
/// TODO(st4rk): find a proper value for the audio_ticks
|
}
|
||||||
constexpr u64 audio_ticks{static_cast<u64>(CoreTiming::BASE_CLOCK_RATE / 500)};
|
|
||||||
|
constexpr std::array<char, 10> DefaultDevice{{"DeviceOut"}};
|
||||||
|
constexpr int DefaultSampleRate{48000};
|
||||||
|
|
||||||
class IAudioOut final : public ServiceFramework<IAudioOut> {
|
class IAudioOut final : public ServiceFramework<IAudioOut> {
|
||||||
public:
|
public:
|
||||||
IAudioOut() : ServiceFramework("IAudioOut"), audio_out_state(AudioState::Stopped) {
|
IAudioOut(AudoutParams audio_params)
|
||||||
|
: ServiceFramework("IAudioOut"), audio_params(audio_params),
|
||||||
|
audio_core(Core::System::GetInstance().AudioCore()) {
|
||||||
|
|
||||||
static const FunctionInfo functions[] = {
|
static const FunctionInfo functions[] = {
|
||||||
{0, &IAudioOut::GetAudioOutState, "GetAudioOutState"},
|
{0, &IAudioOut::GetAudioOutState, "GetAudioOutState"},
|
||||||
{1, &IAudioOut::StartAudioOut, "StartAudioOut"},
|
{1, &IAudioOut::StartAudioOut, "StartAudioOut"},
|
||||||
@@ -32,66 +36,65 @@ public:
|
|||||||
{3, &IAudioOut::AppendAudioOutBufferImpl, "AppendAudioOutBuffer"},
|
{3, &IAudioOut::AppendAudioOutBufferImpl, "AppendAudioOutBuffer"},
|
||||||
{4, &IAudioOut::RegisterBufferEvent, "RegisterBufferEvent"},
|
{4, &IAudioOut::RegisterBufferEvent, "RegisterBufferEvent"},
|
||||||
{5, &IAudioOut::GetReleasedAudioOutBufferImpl, "GetReleasedAudioOutBuffer"},
|
{5, &IAudioOut::GetReleasedAudioOutBufferImpl, "GetReleasedAudioOutBuffer"},
|
||||||
{6, nullptr, "ContainsAudioOutBuffer"},
|
{6, &IAudioOut::ContainsAudioOutBuffer, "ContainsAudioOutBuffer"},
|
||||||
{7, &IAudioOut::AppendAudioOutBufferImpl, "AppendAudioOutBufferAuto"},
|
{7, &IAudioOut::AppendAudioOutBufferImpl, "AppendAudioOutBufferAuto"},
|
||||||
{8, &IAudioOut::GetReleasedAudioOutBufferImpl, "GetReleasedAudioOutBufferAuto"},
|
{8, &IAudioOut::GetReleasedAudioOutBufferImpl, "GetReleasedAudioOutBufferAuto"},
|
||||||
{9, nullptr, "GetAudioOutBufferCount"},
|
{9, &IAudioOut::GetAudioOutBufferCount, "GetAudioOutBufferCount"},
|
||||||
{10, nullptr, "GetAudioOutPlayedSampleCount"},
|
{10, nullptr, "GetAudioOutPlayedSampleCount"},
|
||||||
{11, nullptr, "FlushAudioOutBuffers"},
|
{11, nullptr, "FlushAudioOutBuffers"},
|
||||||
};
|
};
|
||||||
RegisterHandlers(functions);
|
RegisterHandlers(functions);
|
||||||
|
|
||||||
// This is the event handle used to check if the audio buffer was released
|
// This is the event handle used to check if the audio buffer was released
|
||||||
buffer_event =
|
buffer_event = Kernel::Event::Create(Kernel::ResetType::Sticky, "IAudioOutBufferReleased");
|
||||||
Kernel::Event::Create(Kernel::ResetType::OneShot, "IAudioOutBufferReleasedEvent");
|
|
||||||
|
|
||||||
// Register event callback to update the Audio Buffer
|
stream = audio_core.OpenStream(audio_params.sample_rate, audio_params.channel_count,
|
||||||
audio_event = CoreTiming::RegisterEvent(
|
[=]() { buffer_event->Signal(); });
|
||||||
"IAudioOut::UpdateAudioBuffersCallback", [this](u64 userdata, int cycles_late) {
|
|
||||||
UpdateAudioBuffersCallback();
|
|
||||||
CoreTiming::ScheduleEvent(audio_ticks - cycles_late, audio_event);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Start the audio event
|
|
||||||
CoreTiming::ScheduleEvent(audio_ticks, audio_event);
|
|
||||||
}
|
|
||||||
|
|
||||||
~IAudioOut() {
|
|
||||||
CoreTiming::UnscheduleEvent(audio_event, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
struct AudioBuffer {
|
||||||
|
u64_le next;
|
||||||
|
u64_le buffer;
|
||||||
|
u64_le buffer_capacity;
|
||||||
|
u64_le buffer_size;
|
||||||
|
u64_le offset;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(AudioBuffer) == 0x28, "AudioBuffer is an invalid size");
|
||||||
|
|
||||||
void GetAudioOutState(Kernel::HLERequestContext& ctx) {
|
void GetAudioOutState(Kernel::HLERequestContext& ctx) {
|
||||||
LOG_DEBUG(Service_Audio, "called");
|
LOG_DEBUG(Service_Audio, "called");
|
||||||
IPC::ResponseBuilder rb{ctx, 3};
|
IPC::ResponseBuilder rb{ctx, 3};
|
||||||
rb.Push(RESULT_SUCCESS);
|
rb.Push(RESULT_SUCCESS);
|
||||||
rb.Push(static_cast<u32>(audio_out_state));
|
rb.Push(static_cast<u32>(stream->IsPlaying() ? AudioState::Started : AudioState::Stopped));
|
||||||
}
|
}
|
||||||
|
|
||||||
void StartAudioOut(Kernel::HLERequestContext& ctx) {
|
void StartAudioOut(Kernel::HLERequestContext& ctx) {
|
||||||
LOG_WARNING(Service_Audio, "(STUBBED) called");
|
LOG_DEBUG(Service_Audio, "called");
|
||||||
|
|
||||||
// Start audio
|
if (stream->IsPlaying()) {
|
||||||
audio_out_state = AudioState::Started;
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
|
rb.Push(ResultCode(ErrorModule::Audio, ErrCodes::ErrorUnknown));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
audio_core.StartStream(stream);
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
rb.Push(RESULT_SUCCESS);
|
rb.Push(RESULT_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
void StopAudioOut(Kernel::HLERequestContext& ctx) {
|
void StopAudioOut(Kernel::HLERequestContext& ctx) {
|
||||||
LOG_WARNING(Service_Audio, "(STUBBED) called");
|
LOG_DEBUG(Service_Audio, "called");
|
||||||
|
|
||||||
// Stop audio
|
audio_core.StopStream(stream);
|
||||||
audio_out_state = AudioState::Stopped;
|
|
||||||
|
|
||||||
queue_keys.clear();
|
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
rb.Push(RESULT_SUCCESS);
|
rb.Push(RESULT_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RegisterBufferEvent(Kernel::HLERequestContext& ctx) {
|
void RegisterBufferEvent(Kernel::HLERequestContext& ctx) {
|
||||||
LOG_WARNING(Service_Audio, "(STUBBED) called");
|
LOG_DEBUG(Service_Audio, "called");
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||||
rb.Push(RESULT_SUCCESS);
|
rb.Push(RESULT_SUCCESS);
|
||||||
@@ -99,101 +102,107 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void AppendAudioOutBufferImpl(Kernel::HLERequestContext& ctx) {
|
void AppendAudioOutBufferImpl(Kernel::HLERequestContext& ctx) {
|
||||||
LOG_WARNING(Service_Audio, "(STUBBED) called");
|
LOG_DEBUG(Service_Audio, "(STUBBED) called {}", ctx.Description());
|
||||||
IPC::RequestParser rp{ctx};
|
IPC::RequestParser rp{ctx};
|
||||||
|
|
||||||
const u64 key{rp.Pop<u64>()};
|
const auto& input_buffer{ctx.ReadBuffer()};
|
||||||
queue_keys.insert(queue_keys.begin(), key);
|
ASSERT_MSG(input_buffer.size() == sizeof(AudioBuffer),
|
||||||
|
"AudioBuffer input is an invalid size!");
|
||||||
|
AudioBuffer audio_buffer{};
|
||||||
|
std::memcpy(&audio_buffer, input_buffer.data(), sizeof(AudioBuffer));
|
||||||
|
const u64 tag{rp.Pop<u64>()};
|
||||||
|
|
||||||
|
std::vector<u8> data(audio_buffer.buffer_size);
|
||||||
|
Memory::ReadBlock(audio_buffer.buffer, data.data(), data.size());
|
||||||
|
|
||||||
|
if (!audio_core.QueueBuffer(stream, tag, std::move(data))) {
|
||||||
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
|
rb.Push(ResultCode(ErrorModule::Audio, ErrCodes::BufferCountExceeded));
|
||||||
|
}
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
rb.Push(RESULT_SUCCESS);
|
rb.Push(RESULT_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GetReleasedAudioOutBufferImpl(Kernel::HLERequestContext& ctx) {
|
void GetReleasedAudioOutBufferImpl(Kernel::HLERequestContext& ctx) {
|
||||||
LOG_WARNING(Service_Audio, "(STUBBED) called");
|
LOG_DEBUG(Service_Audio, "called {}", ctx.Description());
|
||||||
|
IPC::RequestParser rp{ctx};
|
||||||
|
const u64 max_count{ctx.GetWriteBufferSize() / sizeof(u64)};
|
||||||
|
const auto released_buffers{audio_core.GetTagsAndReleaseBuffers(stream, max_count)};
|
||||||
|
|
||||||
// TODO(st4rk): This is how libtransistor currently implements the
|
std::vector<u64> tags{released_buffers};
|
||||||
// GetReleasedAudioOutBuffer, it should return the key (a VAddr) to the app and this address
|
tags.resize(max_count);
|
||||||
// is used to know which buffer should be filled with data and send again to the service
|
ctx.WriteBuffer(tags);
|
||||||
// through AppendAudioOutBuffer. Check if this is the proper way to do it.
|
|
||||||
u64 key{0};
|
|
||||||
|
|
||||||
if (queue_keys.size()) {
|
|
||||||
key = queue_keys.back();
|
|
||||||
queue_keys.pop_back();
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.WriteBuffer(&key, sizeof(u64));
|
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 3};
|
IPC::ResponseBuilder rb{ctx, 3};
|
||||||
rb.Push(RESULT_SUCCESS);
|
rb.Push(RESULT_SUCCESS);
|
||||||
// TODO(st4rk): This might be the total of released buffers, needs to be verified on
|
rb.Push<u32>(static_cast<u32>(released_buffers.size()));
|
||||||
// hardware
|
|
||||||
rb.Push<u32>(static_cast<u32>(queue_keys.size()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void UpdateAudioBuffersCallback() {
|
void ContainsAudioOutBuffer(Kernel::HLERequestContext& ctx) {
|
||||||
if (audio_out_state != AudioState::Started) {
|
LOG_DEBUG(Service_Audio, "called");
|
||||||
return;
|
IPC::RequestParser rp{ctx};
|
||||||
}
|
const u64 tag{rp.Pop<u64>()};
|
||||||
|
IPC::ResponseBuilder rb{ctx, 3};
|
||||||
if (queue_keys.empty()) {
|
rb.Push(RESULT_SUCCESS);
|
||||||
return;
|
rb.Push(stream->ContainsBuffer(tag));
|
||||||
}
|
|
||||||
|
|
||||||
buffer_event->Signal();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class AudioState : u32 {
|
void GetAudioOutBufferCount(Kernel::HLERequestContext& ctx) {
|
||||||
Started,
|
LOG_DEBUG(Service_Audio, "called");
|
||||||
Stopped,
|
IPC::ResponseBuilder rb{ctx, 3};
|
||||||
};
|
rb.Push(RESULT_SUCCESS);
|
||||||
|
rb.Push(static_cast<u32>(stream->GetQueueSize()));
|
||||||
|
}
|
||||||
|
|
||||||
/// This is used to trigger the audio event callback that is going to read the samples from the
|
AudioCore::AudioOut& audio_core;
|
||||||
/// audio_buffer list and enqueue the samples using the sink (audio_core).
|
AudioCore::StreamPtr stream;
|
||||||
CoreTiming::EventType* audio_event;
|
|
||||||
|
AudoutParams audio_params{};
|
||||||
|
|
||||||
/// This is the evend handle used to check if the audio buffer was released
|
/// This is the evend handle used to check if the audio buffer was released
|
||||||
Kernel::SharedPtr<Kernel::Event> buffer_event;
|
Kernel::SharedPtr<Kernel::Event> buffer_event;
|
||||||
|
|
||||||
/// (st4rk): This is just a temporary workaround for the future implementation. Libtransistor
|
|
||||||
/// uses the key as an address in the App, so we need to return when the
|
|
||||||
/// GetReleasedAudioOutBuffer_1 is called, otherwise we'll run in problems, because
|
|
||||||
/// libtransistor uses the key returned as an pointer.
|
|
||||||
std::vector<u64> queue_keys;
|
|
||||||
|
|
||||||
AudioState audio_out_state;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
void AudOutU::ListAudioOutsImpl(Kernel::HLERequestContext& ctx) {
|
void AudOutU::ListAudioOutsImpl(Kernel::HLERequestContext& ctx) {
|
||||||
LOG_WARNING(Service_Audio, "(STUBBED) called");
|
LOG_DEBUG(Service_Audio, "called");
|
||||||
IPC::RequestParser rp{ctx};
|
IPC::RequestParser rp{ctx};
|
||||||
|
|
||||||
constexpr std::array<char, 15> audio_interface{{"AudioInterface"}};
|
ctx.WriteBuffer(DefaultDevice);
|
||||||
ctx.WriteBuffer(audio_interface);
|
|
||||||
|
|
||||||
IPC::ResponseBuilder rb = rp.MakeBuilder(3, 0, 0);
|
IPC::ResponseBuilder rb = rp.MakeBuilder(3, 0, 0);
|
||||||
|
|
||||||
rb.Push(RESULT_SUCCESS);
|
rb.Push(RESULT_SUCCESS);
|
||||||
// TODO(st4rk): We're currently returning only one audio interface (stringlist size). However,
|
rb.Push<u32>(1); // Amount of audio devices
|
||||||
// it's highly possible to have more than one interface (despite that libtransistor requires
|
|
||||||
// only one).
|
|
||||||
rb.Push<u32>(1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudOutU::OpenAudioOutImpl(Kernel::HLERequestContext& ctx) {
|
void AudOutU::OpenAudioOutImpl(Kernel::HLERequestContext& ctx) {
|
||||||
LOG_WARNING(Service_Audio, "(STUBBED) called");
|
LOG_DEBUG(Service_Audio, "called");
|
||||||
|
|
||||||
if (!audio_out_interface) {
|
ctx.WriteBuffer(DefaultDevice);
|
||||||
audio_out_interface = std::make_shared<IAudioOut>();
|
IPC::RequestParser rp{ctx};
|
||||||
|
auto params{rp.PopRaw<AudoutParams>()};
|
||||||
|
if (params.channel_count <= 2) {
|
||||||
|
// Mono does not exist for audout
|
||||||
|
params.channel_count = 2;
|
||||||
|
} else {
|
||||||
|
params.channel_count = 6;
|
||||||
}
|
}
|
||||||
|
if (!params.sample_rate) {
|
||||||
|
params.sample_rate = DefaultSampleRate;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(bunnei): Support more than one IAudioOut interface. When we add this, ListAudioOutsImpl
|
||||||
|
// will likely need to be updated as well.
|
||||||
|
ASSERT_MSG(!audio_out_interface, "Unimplemented");
|
||||||
|
audio_out_interface = std::make_shared<IAudioOut>(std::move(params));
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 6, 0, 1};
|
IPC::ResponseBuilder rb{ctx, 6, 0, 1};
|
||||||
rb.Push(RESULT_SUCCESS);
|
rb.Push(RESULT_SUCCESS);
|
||||||
rb.Push<u32>(sample_rate);
|
rb.Push<u32>(DefaultSampleRate);
|
||||||
rb.Push<u32>(audio_channels);
|
rb.Push<u32>(params.channel_count);
|
||||||
rb.Push<u32>(static_cast<u32>(PcmFormat::Int16));
|
rb.Push<u32>(static_cast<u32>(PcmFormat::Int16));
|
||||||
rb.Push<u32>(0); // This field is unknown
|
rb.Push<u32>(static_cast<u32>(AudioState::Stopped));
|
||||||
rb.PushIpcInterface<Audio::IAudioOut>(audio_out_interface);
|
rb.PushIpcInterface<Audio::IAudioOut>(audio_out_interface);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,18 @@ class HLERequestContext;
|
|||||||
|
|
||||||
namespace Service::Audio {
|
namespace Service::Audio {
|
||||||
|
|
||||||
|
struct AudoutParams {
|
||||||
|
s32_le sample_rate;
|
||||||
|
u16_le channel_count;
|
||||||
|
INSERT_PADDING_BYTES(2);
|
||||||
|
};
|
||||||
|
static_assert(sizeof(AudoutParams) == 0x8, "AudoutParams is an invalid size");
|
||||||
|
|
||||||
|
enum class AudioState : u32 {
|
||||||
|
Started,
|
||||||
|
Stopped,
|
||||||
|
};
|
||||||
|
|
||||||
class IAudioOut;
|
class IAudioOut;
|
||||||
|
|
||||||
class AudOutU final : public ServiceFramework<AudOutU> {
|
class AudOutU final : public ServiceFramework<AudOutU> {
|
||||||
|
|||||||
72
src/core/hle/service/btdrv/btdrv.cpp
Normal file
72
src/core/hle/service/btdrv/btdrv.cpp
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
// Copyright 2018 yuzu emulator team
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include "core/hle/service/btdrv/btdrv.h"
|
||||||
|
#include "core/hle/service/service.h"
|
||||||
|
#include "core/hle/service/sm/sm.h"
|
||||||
|
|
||||||
|
namespace Service::BtDrv {
|
||||||
|
|
||||||
|
class BtDrv final : public ServiceFramework<BtDrv> {
|
||||||
|
public:
|
||||||
|
explicit BtDrv() : ServiceFramework{"btdrv"} {
|
||||||
|
// clang-format off
|
||||||
|
static const FunctionInfo functions[] = {
|
||||||
|
{0, nullptr, "Unknown"},
|
||||||
|
{1, nullptr, "Init"},
|
||||||
|
{2, nullptr, "Enable"},
|
||||||
|
{3, nullptr, "Disable"},
|
||||||
|
{4, nullptr, "CleanupAndShutdown"},
|
||||||
|
{5, nullptr, "GetAdapterProperties"},
|
||||||
|
{6, nullptr, "GetAdapterProperty"},
|
||||||
|
{7, nullptr, "SetAdapterProperty"},
|
||||||
|
{8, nullptr, "StartDiscovery"},
|
||||||
|
{9, nullptr, "CancelDiscovery"},
|
||||||
|
{10, nullptr, "CreateBond"},
|
||||||
|
{11, nullptr, "RemoveBond"},
|
||||||
|
{12, nullptr, "CancelBond"},
|
||||||
|
{13, nullptr, "PinReply"},
|
||||||
|
{14, nullptr, "SspReply"},
|
||||||
|
{15, nullptr, "Unknown2"},
|
||||||
|
{16, nullptr, "InitInterfaces"},
|
||||||
|
{17, nullptr, "HidHostInterface_Connect"},
|
||||||
|
{18, nullptr, "HidHostInterface_Disconnect"},
|
||||||
|
{19, nullptr, "HidHostInterface_SendData"},
|
||||||
|
{20, nullptr, "HidHostInterface_SendData2"},
|
||||||
|
{21, nullptr, "HidHostInterface_SetReport"},
|
||||||
|
{22, nullptr, "HidHostInterface_GetReport"},
|
||||||
|
{23, nullptr, "HidHostInterface_WakeController"},
|
||||||
|
{24, nullptr, "HidHostInterface_AddPairedDevice"},
|
||||||
|
{25, nullptr, "HidHostInterface_GetPairedDevice"},
|
||||||
|
{26, nullptr, "HidHostInterface_CleanupAndShutdown"},
|
||||||
|
{27, nullptr, "Unknown3"},
|
||||||
|
{28, nullptr, "ExtInterface_SetTSI"},
|
||||||
|
{29, nullptr, "ExtInterface_SetBurstMode"},
|
||||||
|
{30, nullptr, "ExtInterface_SetZeroRetran"},
|
||||||
|
{31, nullptr, "ExtInterface_SetMcMode"},
|
||||||
|
{32, nullptr, "ExtInterface_StartLlrMode"},
|
||||||
|
{33, nullptr, "ExtInterface_ExitLlrMode"},
|
||||||
|
{34, nullptr, "ExtInterface_SetRadio"},
|
||||||
|
{35, nullptr, "ExtInterface_SetVisibility"},
|
||||||
|
{36, nullptr, "Unknown4"},
|
||||||
|
{37, nullptr, "Unknown5"},
|
||||||
|
{38, nullptr, "HidHostInterface_GetLatestPlr"},
|
||||||
|
{39, nullptr, "ExtInterface_GetPendingConnections"},
|
||||||
|
{40, nullptr, "HidHostInterface_GetChannelMap"},
|
||||||
|
{41, nullptr, "SetIsBluetoothBoostEnabled"},
|
||||||
|
{42, nullptr, "GetIsBluetoothBoostEnabled"},
|
||||||
|
{43, nullptr, "SetIsBluetoothAfhEnabled"},
|
||||||
|
{44, nullptr, "GetIsBluetoothAfhEnabled"},
|
||||||
|
};
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
RegisterHandlers(functions);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void InstallInterfaces(SM::ServiceManager& sm) {
|
||||||
|
std::make_shared<BtDrv>()->InstallAsService(sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Service::BtDrv
|
||||||
16
src/core/hle/service/btdrv/btdrv.h
Normal file
16
src/core/hle/service/btdrv/btdrv.h
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
// Copyright 2018 yuzu emulator team
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace Service::SM {
|
||||||
|
class ServiceManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Service::BtDrv {
|
||||||
|
|
||||||
|
/// Registers all BtDrv services with the specified service manager.
|
||||||
|
void InstallInterfaces(SM::ServiceManager& sm);
|
||||||
|
|
||||||
|
} // namespace Service::BtDrv
|
||||||
90
src/core/hle/service/lbl/lbl.cpp
Normal file
90
src/core/hle/service/lbl/lbl.cpp
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
// Copyright 2018 yuzu emulator team
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "common/logging/log.h"
|
||||||
|
#include "core/hle/ipc_helpers.h"
|
||||||
|
#include "core/hle/kernel/hle_ipc.h"
|
||||||
|
#include "core/hle/service/lbl/lbl.h"
|
||||||
|
#include "core/hle/service/service.h"
|
||||||
|
#include "core/hle/service/sm/sm.h"
|
||||||
|
|
||||||
|
namespace Service::LBL {
|
||||||
|
|
||||||
|
class LBL final : public ServiceFramework<LBL> {
|
||||||
|
public:
|
||||||
|
explicit LBL() : ServiceFramework{"lbl"} {
|
||||||
|
// clang-format off
|
||||||
|
static const FunctionInfo functions[] = {
|
||||||
|
{0, nullptr, "Unknown1"},
|
||||||
|
{1, nullptr, "Unknown2"},
|
||||||
|
{2, nullptr, "Unknown3"},
|
||||||
|
{3, nullptr, "Unknown4"},
|
||||||
|
{4, nullptr, "Unknown5"},
|
||||||
|
{5, nullptr, "Unknown6"},
|
||||||
|
{6, nullptr, "TurnOffBacklight"},
|
||||||
|
{7, nullptr, "TurnOnBacklight"},
|
||||||
|
{8, nullptr, "GetBacklightStatus"},
|
||||||
|
{9, nullptr, "Unknown7"},
|
||||||
|
{10, nullptr, "Unknown8"},
|
||||||
|
{11, nullptr, "Unknown9"},
|
||||||
|
{12, nullptr, "Unknown10"},
|
||||||
|
{13, nullptr, "Unknown11"},
|
||||||
|
{14, nullptr, "Unknown12"},
|
||||||
|
{15, nullptr, "Unknown13"},
|
||||||
|
{16, nullptr, "ReadRawLightSensor"},
|
||||||
|
{17, nullptr, "Unknown14"},
|
||||||
|
{18, nullptr, "Unknown15"},
|
||||||
|
{19, nullptr, "Unknown16"},
|
||||||
|
{20, nullptr, "Unknown17"},
|
||||||
|
{21, nullptr, "Unknown18"},
|
||||||
|
{22, nullptr, "Unknown19"},
|
||||||
|
{23, nullptr, "Unknown20"},
|
||||||
|
{24, nullptr, "Unknown21"},
|
||||||
|
{25, nullptr, "Unknown22"},
|
||||||
|
{26, &LBL::EnableVrMode, "EnableVrMode"},
|
||||||
|
{27, &LBL::DisableVrMode, "DisableVrMode"},
|
||||||
|
{28, &LBL::GetVrMode, "GetVrMode"},
|
||||||
|
};
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
RegisterHandlers(functions);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void EnableVrMode(Kernel::HLERequestContext& ctx) {
|
||||||
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
|
rb.Push(RESULT_SUCCESS);
|
||||||
|
|
||||||
|
vr_mode_enabled = true;
|
||||||
|
|
||||||
|
LOG_DEBUG(Service_LBL, "called");
|
||||||
|
}
|
||||||
|
|
||||||
|
void DisableVrMode(Kernel::HLERequestContext& ctx) {
|
||||||
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
|
rb.Push(RESULT_SUCCESS);
|
||||||
|
|
||||||
|
vr_mode_enabled = false;
|
||||||
|
|
||||||
|
LOG_DEBUG(Service_LBL, "called");
|
||||||
|
}
|
||||||
|
|
||||||
|
void GetVrMode(Kernel::HLERequestContext& ctx) {
|
||||||
|
IPC::ResponseBuilder rb{ctx, 3};
|
||||||
|
rb.Push(RESULT_SUCCESS);
|
||||||
|
rb.Push(vr_mode_enabled);
|
||||||
|
|
||||||
|
LOG_DEBUG(Service_LBL, "called");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool vr_mode_enabled = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
void InstallInterfaces(SM::ServiceManager& sm) {
|
||||||
|
std::make_shared<LBL>()->InstallAsService(sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Service::LBL
|
||||||
15
src/core/hle/service/lbl/lbl.h
Normal file
15
src/core/hle/service/lbl/lbl.h
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
// Copyright 2018 yuzu emulator team
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace Service::SM {
|
||||||
|
class ServiceManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Service::LBL {
|
||||||
|
|
||||||
|
void InstallInterfaces(SM::ServiceManager& sm);
|
||||||
|
|
||||||
|
} // namespace Service::LBL
|
||||||
222
src/core/hle/service/nfc/nfc.cpp
Normal file
222
src/core/hle/service/nfc/nfc.cpp
Normal file
@@ -0,0 +1,222 @@
|
|||||||
|
// Copyright 2018 yuzu emulator team
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "common/logging/log.h"
|
||||||
|
#include "core/hle/ipc_helpers.h"
|
||||||
|
#include "core/hle/kernel/hle_ipc.h"
|
||||||
|
#include "core/hle/service/nfc/nfc.h"
|
||||||
|
#include "core/hle/service/service.h"
|
||||||
|
#include "core/hle/service/sm/sm.h"
|
||||||
|
|
||||||
|
namespace Service::NFC {
|
||||||
|
|
||||||
|
class IAm final : public ServiceFramework<IAm> {
|
||||||
|
public:
|
||||||
|
explicit IAm() : ServiceFramework{"IAm"} {
|
||||||
|
// clang-format off
|
||||||
|
static const FunctionInfo functions[] = {
|
||||||
|
{0, nullptr, "Initialize"},
|
||||||
|
{1, nullptr, "Finalize"},
|
||||||
|
{2, nullptr, "NotifyForegroundApplet"},
|
||||||
|
};
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
RegisterHandlers(functions);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class NFC_AM final : public ServiceFramework<NFC_AM> {
|
||||||
|
public:
|
||||||
|
explicit NFC_AM() : ServiceFramework{"nfc:am"} {
|
||||||
|
// clang-format off
|
||||||
|
static const FunctionInfo functions[] = {
|
||||||
|
{0, &NFC_AM::CreateAmInterface, "CreateAmInterface"},
|
||||||
|
};
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
RegisterHandlers(functions);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void CreateAmInterface(Kernel::HLERequestContext& ctx) {
|
||||||
|
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||||
|
rb.Push(RESULT_SUCCESS);
|
||||||
|
rb.PushIpcInterface<IAm>();
|
||||||
|
|
||||||
|
LOG_DEBUG(Service_NFC, "called");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class MFIUser final : public ServiceFramework<MFIUser> {
|
||||||
|
public:
|
||||||
|
explicit MFIUser() : ServiceFramework{"IUser"} {
|
||||||
|
// clang-format off
|
||||||
|
static const FunctionInfo functions[] = {
|
||||||
|
{0, nullptr, "Initialize"},
|
||||||
|
{1, nullptr, "Finalize"},
|
||||||
|
{2, nullptr, "ListDevices"},
|
||||||
|
{3, nullptr, "StartDetection"},
|
||||||
|
{4, nullptr, "StopDetection"},
|
||||||
|
{5, nullptr, "Read"},
|
||||||
|
{6, nullptr, "Write"},
|
||||||
|
{7, nullptr, "GetTagInfo"},
|
||||||
|
{8, nullptr, "GetActivateEventHandle"},
|
||||||
|
{9, nullptr, "GetDeactivateEventHandle"},
|
||||||
|
{10, nullptr, "GetState"},
|
||||||
|
{11, nullptr, "GetDeviceState"},
|
||||||
|
{12, nullptr, "GetNpadId"},
|
||||||
|
{13, nullptr, "GetAvailabilityChangeEventHandle"},
|
||||||
|
};
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
RegisterHandlers(functions);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class NFC_MF_U final : public ServiceFramework<NFC_MF_U> {
|
||||||
|
public:
|
||||||
|
explicit NFC_MF_U() : ServiceFramework{"nfc:mf:u"} {
|
||||||
|
// clang-format off
|
||||||
|
static const FunctionInfo functions[] = {
|
||||||
|
{0, &NFC_MF_U::CreateUserInterface, "CreateUserInterface"},
|
||||||
|
};
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
RegisterHandlers(functions);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void CreateUserInterface(Kernel::HLERequestContext& ctx) {
|
||||||
|
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||||
|
rb.Push(RESULT_SUCCESS);
|
||||||
|
rb.PushIpcInterface<MFIUser>();
|
||||||
|
|
||||||
|
LOG_DEBUG(Service_NFC, "called");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class IUser final : public ServiceFramework<IUser> {
|
||||||
|
public:
|
||||||
|
explicit IUser() : ServiceFramework{"IUser"} {
|
||||||
|
// clang-format off
|
||||||
|
static const FunctionInfo functions[] = {
|
||||||
|
{0, nullptr, "Initialize"},
|
||||||
|
{1, nullptr, "Finalize"},
|
||||||
|
{2, nullptr, "GetState"},
|
||||||
|
{3, nullptr, "IsNfcEnabled"},
|
||||||
|
{400, nullptr, "Initialize"},
|
||||||
|
{401, nullptr, "Finalize"},
|
||||||
|
{402, nullptr, "GetState"},
|
||||||
|
{403, nullptr, "IsNfcEnabled"},
|
||||||
|
{404, nullptr, "ListDevices"},
|
||||||
|
{405, nullptr, "GetDeviceState"},
|
||||||
|
{406, nullptr, "GetNpadId"},
|
||||||
|
{407, nullptr, "AttachAvailabilityChangeEvent"},
|
||||||
|
{408, nullptr, "StartDetection"},
|
||||||
|
{409, nullptr, "StopDetection"},
|
||||||
|
{410, nullptr, "GetTagInfo"},
|
||||||
|
{411, nullptr, "AttachActivateEvent"},
|
||||||
|
{412, nullptr, "AttachDeactivateEvent"},
|
||||||
|
{1000, nullptr, "ReadMifare"},
|
||||||
|
{1001, nullptr, "WriteMifare"},
|
||||||
|
{1300, nullptr, "SendCommandByPassThrough"},
|
||||||
|
{1301, nullptr, "KeepPassThroughSession"},
|
||||||
|
{1302, nullptr, "ReleasePassThroughSession"},
|
||||||
|
};
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
RegisterHandlers(functions);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class NFC_U final : public ServiceFramework<NFC_U> {
|
||||||
|
public:
|
||||||
|
explicit NFC_U() : ServiceFramework{"nfc:u"} {
|
||||||
|
// clang-format off
|
||||||
|
static const FunctionInfo functions[] = {
|
||||||
|
{0, &NFC_U::CreateUserInterface, "CreateUserInterface"},
|
||||||
|
};
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
RegisterHandlers(functions);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void CreateUserInterface(Kernel::HLERequestContext& ctx) {
|
||||||
|
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||||
|
rb.Push(RESULT_SUCCESS);
|
||||||
|
rb.PushIpcInterface<IUser>();
|
||||||
|
|
||||||
|
LOG_DEBUG(Service_NFC, "called");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class ISystem final : public ServiceFramework<ISystem> {
|
||||||
|
public:
|
||||||
|
explicit ISystem() : ServiceFramework{"ISystem"} {
|
||||||
|
// clang-format off
|
||||||
|
static const FunctionInfo functions[] = {
|
||||||
|
{0, nullptr, "Initialize"},
|
||||||
|
{1, nullptr, "Finalize"},
|
||||||
|
{2, nullptr, "GetState"},
|
||||||
|
{3, nullptr, "IsNfcEnabled"},
|
||||||
|
{100, nullptr, "SetNfcEnabled"},
|
||||||
|
{400, nullptr, "InitializeSystem"},
|
||||||
|
{401, nullptr, "FinalizeSystem"},
|
||||||
|
{402, nullptr, "GetState"},
|
||||||
|
{403, nullptr, "IsNfcEnabled"},
|
||||||
|
{404, nullptr, "ListDevices"},
|
||||||
|
{405, nullptr, "GetDeviceState"},
|
||||||
|
{406, nullptr, "GetNpadId"},
|
||||||
|
{407, nullptr, "AttachAvailabilityChangeEvent"},
|
||||||
|
{408, nullptr, "StartDetection"},
|
||||||
|
{409, nullptr, "StopDetection"},
|
||||||
|
{410, nullptr, "GetTagInfo"},
|
||||||
|
{411, nullptr, "AttachActivateEvent"},
|
||||||
|
{412, nullptr, "AttachDeactivateEvent"},
|
||||||
|
{500, nullptr, "SetNfcEnabled"},
|
||||||
|
{1000, nullptr, "ReadMifare"},
|
||||||
|
{1001, nullptr, "WriteMifare"},
|
||||||
|
{1300, nullptr, "SendCommandByPassThrough"},
|
||||||
|
{1301, nullptr, "KeepPassThroughSession"},
|
||||||
|
{1302, nullptr, "ReleasePassThroughSession"},
|
||||||
|
};
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
RegisterHandlers(functions);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class NFC_SYS final : public ServiceFramework<NFC_SYS> {
|
||||||
|
public:
|
||||||
|
explicit NFC_SYS() : ServiceFramework{"nfc:sys"} {
|
||||||
|
// clang-format off
|
||||||
|
static const FunctionInfo functions[] = {
|
||||||
|
{0, &NFC_SYS::CreateSystemInterface, "CreateSystemInterface"},
|
||||||
|
};
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
RegisterHandlers(functions);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void CreateSystemInterface(Kernel::HLERequestContext& ctx) {
|
||||||
|
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||||
|
rb.Push(RESULT_SUCCESS);
|
||||||
|
rb.PushIpcInterface<ISystem>();
|
||||||
|
|
||||||
|
LOG_DEBUG(Service_NFC, "called");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void InstallInterfaces(SM::ServiceManager& sm) {
|
||||||
|
std::make_shared<NFC_AM>()->InstallAsService(sm);
|
||||||
|
std::make_shared<NFC_MF_U>()->InstallAsService(sm);
|
||||||
|
std::make_shared<NFC_U>()->InstallAsService(sm);
|
||||||
|
std::make_shared<NFC_SYS>()->InstallAsService(sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Service::NFC
|
||||||
15
src/core/hle/service/nfc/nfc.h
Normal file
15
src/core/hle/service/nfc/nfc.h
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
// Copyright 2018 yuzu emulator team
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace Service::SM {
|
||||||
|
class ServiceManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Service::NFC {
|
||||||
|
|
||||||
|
void InstallInterfaces(SM::ServiceManager& sm);
|
||||||
|
|
||||||
|
} // namespace Service::NFC
|
||||||
@@ -21,6 +21,7 @@
|
|||||||
#include "core/hle/service/apm/apm.h"
|
#include "core/hle/service/apm/apm.h"
|
||||||
#include "core/hle/service/audio/audio.h"
|
#include "core/hle/service/audio/audio.h"
|
||||||
#include "core/hle/service/bcat/bcat.h"
|
#include "core/hle/service/bcat/bcat.h"
|
||||||
|
#include "core/hle/service/btdrv/btdrv.h"
|
||||||
#include "core/hle/service/erpt/erpt.h"
|
#include "core/hle/service/erpt/erpt.h"
|
||||||
#include "core/hle/service/es/es.h"
|
#include "core/hle/service/es/es.h"
|
||||||
#include "core/hle/service/eupld/eupld.h"
|
#include "core/hle/service/eupld/eupld.h"
|
||||||
@@ -29,10 +30,12 @@
|
|||||||
#include "core/hle/service/friend/friend.h"
|
#include "core/hle/service/friend/friend.h"
|
||||||
#include "core/hle/service/grc/grc.h"
|
#include "core/hle/service/grc/grc.h"
|
||||||
#include "core/hle/service/hid/hid.h"
|
#include "core/hle/service/hid/hid.h"
|
||||||
|
#include "core/hle/service/lbl/lbl.h"
|
||||||
#include "core/hle/service/ldn/ldn.h"
|
#include "core/hle/service/ldn/ldn.h"
|
||||||
#include "core/hle/service/ldr/ldr.h"
|
#include "core/hle/service/ldr/ldr.h"
|
||||||
#include "core/hle/service/lm/lm.h"
|
#include "core/hle/service/lm/lm.h"
|
||||||
#include "core/hle/service/mm/mm_u.h"
|
#include "core/hle/service/mm/mm_u.h"
|
||||||
|
#include "core/hle/service/nfc/nfc.h"
|
||||||
#include "core/hle/service/nfp/nfp.h"
|
#include "core/hle/service/nfp/nfp.h"
|
||||||
#include "core/hle/service/nifm/nifm.h"
|
#include "core/hle/service/nifm/nifm.h"
|
||||||
#include "core/hle/service/nim/nim.h"
|
#include "core/hle/service/nim/nim.h"
|
||||||
@@ -193,8 +196,9 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm) {
|
|||||||
AM::InstallInterfaces(*sm, nv_flinger);
|
AM::InstallInterfaces(*sm, nv_flinger);
|
||||||
AOC::InstallInterfaces(*sm);
|
AOC::InstallInterfaces(*sm);
|
||||||
APM::InstallInterfaces(*sm);
|
APM::InstallInterfaces(*sm);
|
||||||
BCAT::InstallInterfaces(*sm);
|
|
||||||
Audio::InstallInterfaces(*sm);
|
Audio::InstallInterfaces(*sm);
|
||||||
|
BCAT::InstallInterfaces(*sm);
|
||||||
|
BtDrv::InstallInterfaces(*sm);
|
||||||
ERPT::InstallInterfaces(*sm);
|
ERPT::InstallInterfaces(*sm);
|
||||||
ES::InstallInterfaces(*sm);
|
ES::InstallInterfaces(*sm);
|
||||||
EUPLD::InstallInterfaces(*sm);
|
EUPLD::InstallInterfaces(*sm);
|
||||||
@@ -203,10 +207,12 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm) {
|
|||||||
Friend::InstallInterfaces(*sm);
|
Friend::InstallInterfaces(*sm);
|
||||||
GRC::InstallInterfaces(*sm);
|
GRC::InstallInterfaces(*sm);
|
||||||
HID::InstallInterfaces(*sm);
|
HID::InstallInterfaces(*sm);
|
||||||
|
LBL::InstallInterfaces(*sm);
|
||||||
LDN::InstallInterfaces(*sm);
|
LDN::InstallInterfaces(*sm);
|
||||||
LDR::InstallInterfaces(*sm);
|
LDR::InstallInterfaces(*sm);
|
||||||
LM::InstallInterfaces(*sm);
|
LM::InstallInterfaces(*sm);
|
||||||
MM::InstallInterfaces(*sm);
|
MM::InstallInterfaces(*sm);
|
||||||
|
NFC::InstallInterfaces(*sm);
|
||||||
NFP::InstallInterfaces(*sm);
|
NFP::InstallInterfaces(*sm);
|
||||||
NIFM::InstallInterfaces(*sm);
|
NIFM::InstallInterfaces(*sm);
|
||||||
NIM::InstallInterfaces(*sm);
|
NIM::InstallInterfaces(*sm);
|
||||||
|
|||||||
@@ -207,15 +207,27 @@ void GMainWindow::InitializeRecentFileMenuActions() {
|
|||||||
void GMainWindow::InitializeHotkeys() {
|
void GMainWindow::InitializeHotkeys() {
|
||||||
RegisterHotkey("Main Window", "Load File", QKeySequence::Open);
|
RegisterHotkey("Main Window", "Load File", QKeySequence::Open);
|
||||||
RegisterHotkey("Main Window", "Start Emulation");
|
RegisterHotkey("Main Window", "Start Emulation");
|
||||||
|
RegisterHotkey("Main Window", "Continue/Pause", QKeySequence(Qt::Key_F4));
|
||||||
RegisterHotkey("Main Window", "Fullscreen", QKeySequence::FullScreen);
|
RegisterHotkey("Main Window", "Fullscreen", QKeySequence::FullScreen);
|
||||||
RegisterHotkey("Main Window", "Exit Fullscreen", QKeySequence(Qt::Key_Escape),
|
RegisterHotkey("Main Window", "Exit Fullscreen", QKeySequence(Qt::Key_Escape),
|
||||||
Qt::ApplicationShortcut);
|
Qt::ApplicationShortcut);
|
||||||
|
RegisterHotkey("Main Window", "Toggle Speed Limit", QKeySequence("CTRL+Z"),
|
||||||
|
Qt::ApplicationShortcut);
|
||||||
LoadHotkeys();
|
LoadHotkeys();
|
||||||
|
|
||||||
connect(GetHotkey("Main Window", "Load File", this), &QShortcut::activated, this,
|
connect(GetHotkey("Main Window", "Load File", this), &QShortcut::activated, this,
|
||||||
&GMainWindow::OnMenuLoadFile);
|
&GMainWindow::OnMenuLoadFile);
|
||||||
connect(GetHotkey("Main Window", "Start Emulation", this), &QShortcut::activated, this,
|
connect(GetHotkey("Main Window", "Start Emulation", this), &QShortcut::activated, this,
|
||||||
&GMainWindow::OnStartGame);
|
&GMainWindow::OnStartGame);
|
||||||
|
connect(GetHotkey("Main Window", "Continue/Pause", this), &QShortcut::activated, this, [&] {
|
||||||
|
if (emulation_running) {
|
||||||
|
if (emu_thread->IsRunning()) {
|
||||||
|
OnPauseGame();
|
||||||
|
} else {
|
||||||
|
OnStartGame();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
connect(GetHotkey("Main Window", "Fullscreen", render_window), &QShortcut::activated,
|
connect(GetHotkey("Main Window", "Fullscreen", render_window), &QShortcut::activated,
|
||||||
ui.action_Fullscreen, &QAction::trigger);
|
ui.action_Fullscreen, &QAction::trigger);
|
||||||
connect(GetHotkey("Main Window", "Fullscreen", render_window), &QShortcut::activatedAmbiguously,
|
connect(GetHotkey("Main Window", "Fullscreen", render_window), &QShortcut::activatedAmbiguously,
|
||||||
@@ -226,6 +238,10 @@ void GMainWindow::InitializeHotkeys() {
|
|||||||
ToggleFullscreen();
|
ToggleFullscreen();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
connect(GetHotkey("Main Window", "Toggle Speed Limit", this), &QShortcut::activated, this, [&] {
|
||||||
|
Settings::values.toggle_framelimit = !Settings::values.toggle_framelimit;
|
||||||
|
UpdateStatusBar();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void GMainWindow::SetDefaultUIGeometry() {
|
void GMainWindow::SetDefaultUIGeometry() {
|
||||||
|
|||||||
Reference in New Issue
Block a user