summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/CMakeLists.txt1
-rw-r--r--src/audio_core/CMakeLists.txt11
-rw-r--r--src/audio_core/audio_out.cpp50
-rw-r--r--src/audio_core/audio_out.h44
-rw-r--r--src/audio_core/buffer.h37
-rw-r--r--src/audio_core/stream.cpp103
-rw-r--r--src/audio_core/stream.h89
-rw-r--r--src/common/logging/backend.cpp2
-rw-r--r--src/common/logging/log.h2
-rw-r--r--src/core/CMakeLists.txt12
-rw-r--r--src/core/core.cpp2
-rw-r--r--src/core/core.h7
-rw-r--r--src/core/file_sys/content_archive.cpp19
-rw-r--r--src/core/file_sys/romfs.cpp124
-rw-r--r--src/core/file_sys/romfs.h35
-rw-r--r--src/core/file_sys/vfs.cpp23
-rw-r--r--src/core/file_sys/vfs.h20
-rw-r--r--src/core/file_sys/vfs_offset.cpp7
-rw-r--r--src/core/file_sys/vfs_offset.h3
-rw-r--r--src/core/file_sys/vfs_real.cpp6
-rw-r--r--src/core/file_sys/vfs_real.h3
-rw-r--r--src/core/file_sys/vfs_vector.cpp83
-rw-r--r--src/core/file_sys/vfs_vector.h44
-rw-r--r--src/core/hle/service/audio/audout_u.cpp195
-rw-r--r--src/core/hle/service/audio/audout_u.h12
-rw-r--r--src/core/hle/service/btdrv/btdrv.cpp72
-rw-r--r--src/core/hle/service/btdrv/btdrv.h16
-rw-r--r--src/core/hle/service/lbl/lbl.cpp90
-rw-r--r--src/core/hle/service/lbl/lbl.h15
-rw-r--r--src/core/hle/service/nfc/nfc.cpp222
-rw-r--r--src/core/hle/service/nfc/nfc.h15
-rw-r--r--src/core/hle/service/service.cpp8
-rw-r--r--src/yuzu/main.cpp16
33 files changed, 1273 insertions, 115 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 85354f43e..a88551fbc 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -3,6 +3,7 @@ include_directories(.)
3 3
4add_subdirectory(common) 4add_subdirectory(common)
5add_subdirectory(core) 5add_subdirectory(core)
6add_subdirectory(audio_core)
6add_subdirectory(video_core) 7add_subdirectory(video_core)
7add_subdirectory(input_common) 8add_subdirectory(input_common)
8add_subdirectory(tests) 9add_subdirectory(tests)
diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt
new file mode 100644
index 000000000..f00a55994
--- /dev/null
+++ b/src/audio_core/CMakeLists.txt
@@ -0,0 +1,11 @@
1add_library(audio_core STATIC
2 audio_out.cpp
3 audio_out.h
4 buffer.h
5 stream.cpp
6 stream.h
7)
8
9create_target_directory_groups(audio_core)
10
11target_link_libraries(audio_core PUBLIC common core)
diff --git a/src/audio_core/audio_out.cpp b/src/audio_core/audio_out.cpp
new file mode 100644
index 000000000..6d418a05b
--- /dev/null
+++ b/src/audio_core/audio_out.cpp
@@ -0,0 +1,50 @@
1// Copyright 2018 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "audio_core/audio_out.h"
6#include "common/assert.h"
7#include "common/logging/log.h"
8
9namespace AudioCore {
10
11/// Returns the stream format from the specified number of channels
12static Stream::Format ChannelsToStreamFormat(int num_channels) {
13 switch (num_channels) {
14 case 1:
15 return Stream::Format::Mono16;
16 case 2:
17 return Stream::Format::Stereo16;
18 case 6:
19 return Stream::Format::Multi51Channel16;
20 }
21
22 LOG_CRITICAL(Audio, "Unimplemented num_channels={}", num_channels);
23 UNREACHABLE();
24 return {};
25}
26
27StreamPtr AudioOut::OpenStream(int sample_rate, int num_channels,
28 Stream::ReleaseCallback&& release_callback) {
29 streams.push_back(std::make_shared<Stream>(sample_rate, ChannelsToStreamFormat(num_channels),
30 std::move(release_callback)));
31 return streams.back();
32}
33
34std::vector<u64> AudioOut::GetTagsAndReleaseBuffers(StreamPtr stream, size_t max_count) {
35 return stream->GetTagsAndReleaseBuffers(max_count);
36}
37
38void AudioOut::StartStream(StreamPtr stream) {
39 stream->Play();
40}
41
42void AudioOut::StopStream(StreamPtr stream) {
43 stream->Stop();
44}
45
46bool AudioOut::QueueBuffer(StreamPtr stream, Buffer::Tag tag, std::vector<u8>&& data) {
47 return stream->QueueBuffer(std::make_shared<Buffer>(tag, std::move(data)));
48}
49
50} // namespace AudioCore
diff --git a/src/audio_core/audio_out.h b/src/audio_core/audio_out.h
new file mode 100644
index 000000000..a86499d10
--- /dev/null
+++ b/src/audio_core/audio_out.h
@@ -0,0 +1,44 @@
1// Copyright 2018 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8#include <vector>
9
10#include "audio_core/buffer.h"
11#include "audio_core/stream.h"
12#include "common/common_types.h"
13
14namespace AudioCore {
15
16using StreamPtr = std::shared_ptr<Stream>;
17
18/**
19 * Represents an audio playback interface, used to open and play audio streams
20 */
21class AudioOut {
22public:
23 /// Opens a new audio stream
24 StreamPtr OpenStream(int sample_rate, int num_channels,
25 Stream::ReleaseCallback&& release_callback);
26
27 /// Returns a vector of recently released buffers specified by tag for the specified stream
28 std::vector<u64> GetTagsAndReleaseBuffers(StreamPtr stream, size_t max_count);
29
30 /// Starts an audio stream for playback
31 void StartStream(StreamPtr stream);
32
33 /// Stops an audio stream that is currently playing
34 void StopStream(StreamPtr stream);
35
36 /// Queues a buffer into the specified audio stream, returns true on success
37 bool QueueBuffer(StreamPtr stream, Buffer::Tag tag, std::vector<u8>&& data);
38
39private:
40 /// Active audio streams on the interface
41 std::vector<StreamPtr> streams;
42};
43
44} // namespace AudioCore
diff --git a/src/audio_core/buffer.h b/src/audio_core/buffer.h
new file mode 100644
index 000000000..874ec787e
--- /dev/null
+++ b/src/audio_core/buffer.h
@@ -0,0 +1,37 @@
1// Copyright 2018 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <vector>
8
9#include "common/common_types.h"
10
11namespace AudioCore {
12
13/**
14 * Represents a buffer of audio samples to be played in an audio stream
15 */
16class Buffer {
17public:
18 using Tag = u64;
19
20 Buffer(Tag tag, std::vector<u8>&& data) : tag{tag}, data{std::move(data)} {}
21
22 /// Returns the raw audio data for the buffer
23 const std::vector<u8>& GetData() const {
24 return data;
25 }
26
27 /// Returns the buffer tag, this is provided by the game to the audout service
28 Tag GetTag() const {
29 return tag;
30 }
31
32private:
33 Tag tag;
34 std::vector<u8> data;
35};
36
37} // namespace AudioCore
diff --git a/src/audio_core/stream.cpp b/src/audio_core/stream.cpp
new file mode 100644
index 000000000..82bff4b9e
--- /dev/null
+++ b/src/audio_core/stream.cpp
@@ -0,0 +1,103 @@
1// Copyright 2018 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/assert.h"
6#include "common/logging/log.h"
7#include "core/core_timing.h"
8#include "core/core_timing_util.h"
9
10#include "audio_core/stream.h"
11
12namespace AudioCore {
13
14constexpr size_t MaxAudioBufferCount{32};
15
16/// Returns the sample size for the specified audio stream format
17static size_t SampleSizeFromFormat(Stream::Format format) {
18 switch (format) {
19 case Stream::Format::Mono16:
20 return 2;
21 case Stream::Format::Stereo16:
22 return 4;
23 case Stream::Format::Multi51Channel16:
24 return 12;
25 };
26
27 LOG_CRITICAL(Audio, "Unimplemented format={}", static_cast<u32>(format));
28 UNREACHABLE();
29 return {};
30}
31
32Stream::Stream(int sample_rate, Format format, ReleaseCallback&& release_callback)
33 : sample_rate{sample_rate}, format{format}, release_callback{std::move(release_callback)} {
34 release_event = CoreTiming::RegisterEvent(
35 "Stream::Release", [this](u64 userdata, int cycles_late) { ReleaseActiveBuffer(); });
36}
37
38void Stream::Play() {
39 state = State::Playing;
40 PlayNextBuffer();
41}
42
43void Stream::Stop() {
44 ASSERT_MSG(false, "Unimplemented");
45}
46
47s64 Stream::GetBufferReleaseCycles(const Buffer& buffer) const {
48 const size_t num_samples{buffer.GetData().size() / SampleSizeFromFormat(format)};
49 return CoreTiming::usToCycles((static_cast<u64>(num_samples) * 1000000) / sample_rate);
50}
51
52void Stream::PlayNextBuffer() {
53 if (!IsPlaying()) {
54 // Ensure we are in playing state before playing the next buffer
55 return;
56 }
57
58 if (active_buffer) {
59 // Do not queue a new buffer if we are already playing a buffer
60 return;
61 }
62
63 if (queued_buffers.empty()) {
64 // No queued buffers - we are effectively paused
65 return;
66 }
67
68 active_buffer = queued_buffers.front();
69 queued_buffers.pop();
70
71 CoreTiming::ScheduleEventThreadsafe(GetBufferReleaseCycles(*active_buffer), release_event, {});
72}
73
74void Stream::ReleaseActiveBuffer() {
75 released_buffers.push(std::move(active_buffer));
76 release_callback();
77 PlayNextBuffer();
78}
79
80bool Stream::QueueBuffer(BufferPtr&& buffer) {
81 if (queued_buffers.size() < MaxAudioBufferCount) {
82 queued_buffers.push(std::move(buffer));
83 PlayNextBuffer();
84 return true;
85 }
86 return false;
87}
88
89bool Stream::ContainsBuffer(Buffer::Tag tag) const {
90 ASSERT_MSG(false, "Unimplemented");
91 return {};
92}
93
94std::vector<Buffer::Tag> Stream::GetTagsAndReleaseBuffers(size_t max_count) {
95 std::vector<Buffer::Tag> tags;
96 for (size_t count = 0; count < max_count && !released_buffers.empty(); ++count) {
97 tags.push_back(released_buffers.front()->GetTag());
98 released_buffers.pop();
99 }
100 return tags;
101}
102
103} // namespace AudioCore
diff --git a/src/audio_core/stream.h b/src/audio_core/stream.h
new file mode 100644
index 000000000..5f43b0798
--- /dev/null
+++ b/src/audio_core/stream.h
@@ -0,0 +1,89 @@
1// Copyright 2018 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <functional>
8#include <memory>
9#include <vector>
10#include <queue>
11
12#include "audio_core/buffer.h"
13#include "common/assert.h"
14#include "common/common_types.h"
15#include "core/core_timing.h"
16
17namespace AudioCore {
18
19using BufferPtr = std::shared_ptr<Buffer>;
20
21/**
22 * Represents an audio stream, which is a sequence of queued buffers, to be outputed by AudioOut
23 */
24class Stream {
25public:
26 /// Audio format of the stream
27 enum class Format {
28 Mono16,
29 Stereo16,
30 Multi51Channel16,
31 };
32
33 /// Callback function type, used to change guest state on a buffer being released
34 using ReleaseCallback = std::function<void()>;
35
36 Stream(int sample_rate, Format format, ReleaseCallback&& release_callback);
37
38 /// Plays the audio stream
39 void Play();
40
41 /// Stops the audio stream
42 void Stop();
43
44 /// Queues a buffer into the audio stream, returns true on success
45 bool QueueBuffer(BufferPtr&& buffer);
46
47 /// Returns true if the audio stream contains a buffer with the specified tag
48 bool ContainsBuffer(Buffer::Tag tag) const;
49
50 /// Returns a vector of recently released buffers specified by tag
51 std::vector<Buffer::Tag> GetTagsAndReleaseBuffers(size_t max_count);
52
53 /// Returns true if the stream is currently playing
54 bool IsPlaying() const {
55 return state == State::Playing;
56 }
57
58 /// Returns the number of queued buffers
59 size_t GetQueueSize() const {
60 return queued_buffers.size();
61 }
62
63private:
64 /// Current state of the stream
65 enum class State {
66 Stopped,
67 Playing,
68 };
69
70 /// Plays the next queued buffer in the audio stream, starting playback if necessary
71 void PlayNextBuffer();
72
73 /// Releases the actively playing buffer, signalling that it has been completed
74 void ReleaseActiveBuffer();
75
76 /// Gets the number of core cycles when the specified buffer will be released
77 s64 GetBufferReleaseCycles(const Buffer& buffer) const;
78
79 int sample_rate; ///< Sample rate of the stream
80 Format format; ///< Format of the stream
81 ReleaseCallback release_callback; ///< Buffer release callback for the stream
82 State state{State::Stopped}; ///< Playback state of the stream
83 CoreTiming::EventType* release_event{}; ///< Core timing release event for the stream
84 BufferPtr active_buffer; ///< Actively playing buffer in the stream
85 std::queue<BufferPtr> queued_buffers; ///< Buffers queued to be played in the stream
86 std::queue<BufferPtr> released_buffers; ///< Buffers recently released from the stream
87};
88
89} // namespace AudioCore
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
index 281336830..ca30fa6ff 100644
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -173,10 +173,12 @@ void FileBackend::Write(const Entry& entry) {
173 SUB(Service, Friend) \ 173 SUB(Service, Friend) \
174 SUB(Service, FS) \ 174 SUB(Service, FS) \
175 SUB(Service, HID) \ 175 SUB(Service, HID) \
176 SUB(Service, LBL) \
176 SUB(Service, LDN) \ 177 SUB(Service, LDN) \
177 SUB(Service, LM) \ 178 SUB(Service, LM) \
178 SUB(Service, Mii) \ 179 SUB(Service, Mii) \
179 SUB(Service, MM) \ 180 SUB(Service, MM) \
181 SUB(Service, NFC) \
180 SUB(Service, NFP) \ 182 SUB(Service, NFP) \
181 SUB(Service, NIFM) \ 183 SUB(Service, NIFM) \
182 SUB(Service, NS) \ 184 SUB(Service, NS) \
diff --git a/src/common/logging/log.h b/src/common/logging/log.h
index 7f0788801..2515cb40f 100644
--- a/src/common/logging/log.h
+++ b/src/common/logging/log.h
@@ -60,10 +60,12 @@ enum class Class : ClassType {
60 Service_Friend, ///< The friend service 60 Service_Friend, ///< The friend service
61 Service_FS, ///< The FS (Filesystem) service 61 Service_FS, ///< The FS (Filesystem) service
62 Service_HID, ///< The HID (Human interface device) service 62 Service_HID, ///< The HID (Human interface device) service
63 Service_LBL, ///< The LBL (LCD backlight) service
63 Service_LDN, ///< The LDN (Local domain network) service 64 Service_LDN, ///< The LDN (Local domain network) service
64 Service_LM, ///< The LM (Logger) service 65 Service_LM, ///< The LM (Logger) service
65 Service_Mii, ///< The Mii service 66 Service_Mii, ///< The Mii service
66 Service_MM, ///< The MM (Multimedia) service 67 Service_MM, ///< The MM (Multimedia) service
68 Service_NFC, ///< The NFC (Near-field communication) service
67 Service_NFP, ///< The NFP service 69 Service_NFP, ///< The NFP service
68 Service_NIFM, ///< The NIFM (Network interface) service 70 Service_NIFM, ///< The NIFM (Network interface) service
69 Service_NS, ///< The NS services 71 Service_NS, ///< The NS services
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 568c0ae0f..18faf08f8 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -23,6 +23,8 @@ add_library(core STATIC
23 file_sys/partition_filesystem.h 23 file_sys/partition_filesystem.h
24 file_sys/program_metadata.cpp 24 file_sys/program_metadata.cpp
25 file_sys/program_metadata.h 25 file_sys/program_metadata.h
26 file_sys/romfs.cpp
27 file_sys/romfs.h
26 file_sys/romfs_factory.cpp 28 file_sys/romfs_factory.cpp
27 file_sys/romfs_factory.h 29 file_sys/romfs_factory.h
28 file_sys/savedata_factory.cpp 30 file_sys/savedata_factory.cpp
@@ -35,6 +37,8 @@ add_library(core STATIC
35 file_sys/vfs_offset.h 37 file_sys/vfs_offset.h
36 file_sys/vfs_real.cpp 38 file_sys/vfs_real.cpp
37 file_sys/vfs_real.h 39 file_sys/vfs_real.h
40 file_sys/vfs_vector.cpp
41 file_sys/vfs_vector.h
38 frontend/emu_window.cpp 42 frontend/emu_window.cpp
39 frontend/emu_window.h 43 frontend/emu_window.h
40 frontend/framebuffer_layout.cpp 44 frontend/framebuffer_layout.cpp
@@ -136,6 +140,8 @@ add_library(core STATIC
136 hle/service/bcat/bcat.h 140 hle/service/bcat/bcat.h
137 hle/service/bcat/module.cpp 141 hle/service/bcat/module.cpp
138 hle/service/bcat/module.h 142 hle/service/bcat/module.h
143 hle/service/btdrv/btdrv.cpp
144 hle/service/btdrv/btdrv.h
139 hle/service/erpt/erpt.cpp 145 hle/service/erpt/erpt.cpp
140 hle/service/erpt/erpt.h 146 hle/service/erpt/erpt.h
141 hle/service/es/es.cpp 147 hle/service/es/es.cpp
@@ -164,6 +170,8 @@ add_library(core STATIC
164 hle/service/hid/irs.h 170 hle/service/hid/irs.h
165 hle/service/hid/xcd.cpp 171 hle/service/hid/xcd.cpp
166 hle/service/hid/xcd.h 172 hle/service/hid/xcd.h
173 hle/service/lbl/lbl.cpp
174 hle/service/lbl/lbl.h
167 hle/service/ldn/ldn.cpp 175 hle/service/ldn/ldn.cpp
168 hle/service/ldn/ldn.h 176 hle/service/ldn/ldn.h
169 hle/service/ldr/ldr.cpp 177 hle/service/ldr/ldr.cpp
@@ -174,6 +182,8 @@ add_library(core STATIC
174 hle/service/mii/mii.h 182 hle/service/mii/mii.h
175 hle/service/mm/mm_u.cpp 183 hle/service/mm/mm_u.cpp
176 hle/service/mm/mm_u.h 184 hle/service/mm/mm_u.h
185 hle/service/nfc/nfc.cpp
186 hle/service/nfc/nfc.h
177 hle/service/nfp/nfp.cpp 187 hle/service/nfp/nfp.cpp
178 hle/service/nfp/nfp.h 188 hle/service/nfp/nfp.h
179 hle/service/nfp/nfp_user.cpp 189 hle/service/nfp/nfp_user.cpp
@@ -301,7 +311,7 @@ add_library(core STATIC
301 311
302create_target_directory_groups(core) 312create_target_directory_groups(core)
303 313
304target_link_libraries(core PUBLIC common PRIVATE video_core) 314target_link_libraries(core PUBLIC common PRIVATE audio_core video_core)
305target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt lz4_static unicorn) 315target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt lz4_static unicorn)
306 316
307if (ARCHITECTURE_x86_64) 317if (ARCHITECTURE_x86_64)
diff --git a/src/core/core.cpp b/src/core/core.cpp
index b7f4b4532..186fa46df 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -177,6 +177,7 @@ System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) {
177 } 177 }
178 178
179 gpu_core = std::make_unique<Tegra::GPU>(); 179 gpu_core = std::make_unique<Tegra::GPU>();
180 audio_core = std::make_unique<AudioCore::AudioOut>();
180 telemetry_session = std::make_unique<Core::TelemetrySession>(); 181 telemetry_session = std::make_unique<Core::TelemetrySession>();
181 service_manager = std::make_shared<Service::SM::ServiceManager>(); 182 service_manager = std::make_shared<Service::SM::ServiceManager>();
182 183
@@ -228,6 +229,7 @@ void System::Shutdown() {
228 service_manager.reset(); 229 service_manager.reset();
229 telemetry_session.reset(); 230 telemetry_session.reset();
230 gpu_core.reset(); 231 gpu_core.reset();
232 audio_core.reset();
231 233
232 // Close all CPU/threading state 234 // Close all CPU/threading state
233 cpu_barrier->NotifyEnd(); 235 cpu_barrier->NotifyEnd();
diff --git a/src/core/core.h b/src/core/core.h
index c123fe401..6f4df775f 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -8,6 +8,7 @@
8#include <memory> 8#include <memory>
9#include <string> 9#include <string>
10#include <thread> 10#include <thread>
11#include "audio_core/audio_out.h"
11#include "common/common_types.h" 12#include "common/common_types.h"
12#include "core/arm/exclusive_monitor.h" 13#include "core/arm/exclusive_monitor.h"
13#include "core/core_cpu.h" 14#include "core/core_cpu.h"
@@ -131,6 +132,11 @@ public:
131 return *gpu_core; 132 return *gpu_core;
132 } 133 }
133 134
135 /// Gets the AudioCore interface
136 AudioCore::AudioOut& AudioCore() {
137 return *audio_core;
138 }
139
134 /// Gets the scheduler for the CPU core that is currently running 140 /// Gets the scheduler for the CPU core that is currently running
135 Kernel::Scheduler& CurrentScheduler() { 141 Kernel::Scheduler& CurrentScheduler() {
136 return *CurrentCpuCore().Scheduler(); 142 return *CurrentCpuCore().Scheduler();
@@ -195,6 +201,7 @@ private:
195 /// AppLoader used to load the current executing application 201 /// AppLoader used to load the current executing application
196 std::unique_ptr<Loader::AppLoader> app_loader; 202 std::unique_ptr<Loader::AppLoader> app_loader;
197 std::unique_ptr<Tegra::GPU> gpu_core; 203 std::unique_ptr<Tegra::GPU> gpu_core;
204 std::unique_ptr<AudioCore::AudioOut> audio_core;
198 std::shared_ptr<Tegra::DebugContext> debug_context; 205 std::shared_ptr<Tegra::DebugContext> debug_context;
199 Kernel::SharedPtr<Kernel::Process> current_process; 206 Kernel::SharedPtr<Kernel::Process> current_process;
200 std::shared_ptr<ExclusiveMonitor> cpu_exclusive_monitor; 207 std::shared_ptr<ExclusiveMonitor> cpu_exclusive_monitor;
diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp
index d6b20c047..61cb0bbe3 100644
--- a/src/core/file_sys/content_archive.cpp
+++ b/src/core/file_sys/content_archive.cpp
@@ -9,6 +9,7 @@
9#include "core/file_sys/content_archive.h" 9#include "core/file_sys/content_archive.h"
10#include "core/file_sys/vfs_offset.h" 10#include "core/file_sys/vfs_offset.h"
11#include "core/loader/loader.h" 11#include "core/loader/loader.h"
12#include "romfs.h"
12 13
13namespace FileSys { 14namespace FileSys {
14 15
@@ -46,21 +47,9 @@ struct PFS0Superblock {
46}; 47};
47static_assert(sizeof(PFS0Superblock) == 0x200, "PFS0Superblock has incorrect size."); 48static_assert(sizeof(PFS0Superblock) == 0x200, "PFS0Superblock has incorrect size.");
48 49
49struct IVFCLevel {
50 u64_le offset;
51 u64_le size;
52 u32_le block_size;
53 u32_le reserved;
54};
55static_assert(sizeof(IVFCLevel) == 0x18, "IVFCLevel has incorrect size.");
56
57struct RomFSSuperblock { 50struct RomFSSuperblock {
58 NCASectionHeaderBlock header_block; 51 NCASectionHeaderBlock header_block;
59 u32_le magic; 52 IVFCHeader ivfc;
60 u32_le magic_number;
61 INSERT_PADDING_BYTES(8);
62 std::array<IVFCLevel, 6> levels;
63 INSERT_PADDING_BYTES(64);
64}; 53};
65static_assert(sizeof(RomFSSuperblock) == 0xE8, "RomFSSuperblock has incorrect size."); 54static_assert(sizeof(RomFSSuperblock) == 0xE8, "RomFSSuperblock has incorrect size.");
66 55
@@ -92,8 +81,8 @@ NCA::NCA(VirtualFile file_) : file(std::move(file_)) {
92 81
93 const size_t romfs_offset = 82 const size_t romfs_offset =
94 header.section_tables[i].media_offset * MEDIA_OFFSET_MULTIPLIER + 83 header.section_tables[i].media_offset * MEDIA_OFFSET_MULTIPLIER +
95 sb.levels[IVFC_MAX_LEVEL - 1].offset; 84 sb.ivfc.levels[IVFC_MAX_LEVEL - 1].offset;
96 const size_t romfs_size = sb.levels[IVFC_MAX_LEVEL - 1].size; 85 const size_t romfs_size = sb.ivfc.levels[IVFC_MAX_LEVEL - 1].size;
97 files.emplace_back(std::make_shared<OffsetVfsFile>(file, romfs_size, romfs_offset)); 86 files.emplace_back(std::make_shared<OffsetVfsFile>(file, romfs_size, romfs_offset));
98 romfs = files.back(); 87 romfs = files.back();
99 } else if (block.filesystem_type == NCASectionFilesystemType::PFS0) { 88 } else if (block.filesystem_type == NCASectionFilesystemType::PFS0) {
diff --git a/src/core/file_sys/romfs.cpp b/src/core/file_sys/romfs.cpp
new file mode 100644
index 000000000..ff3ddb29c
--- /dev/null
+++ b/src/core/file_sys/romfs.cpp
@@ -0,0 +1,124 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/common_types.h"
6#include "common/swap.h"
7#include "core/file_sys/romfs.h"
8#include "core/file_sys/vfs.h"
9#include "core/file_sys/vfs_offset.h"
10#include "core/file_sys/vfs_vector.h"
11
12namespace FileSys {
13
14constexpr u32 ROMFS_ENTRY_EMPTY = 0xFFFFFFFF;
15
16struct TableLocation {
17 u64_le offset;
18 u64_le size;
19};
20static_assert(sizeof(TableLocation) == 0x10, "TableLocation has incorrect size.");
21
22struct RomFSHeader {
23 u64_le header_size;
24 TableLocation directory_hash;
25 TableLocation directory_meta;
26 TableLocation file_hash;
27 TableLocation file_meta;
28 u64_le data_offset;
29};
30static_assert(sizeof(RomFSHeader) == 0x50, "RomFSHeader has incorrect size.");
31
32struct DirectoryEntry {
33 u32_le sibling;
34 u32_le child_dir;
35 u32_le child_file;
36 u32_le hash;
37 u32_le name_length;
38};
39static_assert(sizeof(DirectoryEntry) == 0x14, "DirectoryEntry has incorrect size.");
40
41struct FileEntry {
42 u32_le parent;
43 u32_le sibling;
44 u64_le offset;
45 u64_le size;
46 u32_le hash;
47 u32_le name_length;
48};
49static_assert(sizeof(FileEntry) == 0x20, "FileEntry has incorrect size.");
50
51template <typename Entry>
52static std::pair<Entry, std::string> GetEntry(const VirtualFile& file, size_t offset) {
53 Entry entry{};
54 if (file->ReadObject(&entry, offset) != sizeof(Entry))
55 return {};
56 std::string string(entry.name_length, '\0');
57 if (file->ReadArray(&string[0], string.size(), offset + sizeof(Entry)) != string.size())
58 return {};
59 return {entry, string};
60}
61
62void ProcessFile(VirtualFile file, size_t file_offset, size_t data_offset, u32 this_file_offset,
63 std::shared_ptr<VectorVfsDirectory> parent) {
64 while (true) {
65 auto entry = GetEntry<FileEntry>(file, file_offset + this_file_offset);
66
67 parent->AddFile(std::make_shared<OffsetVfsFile>(
68 file, entry.first.size, entry.first.offset + data_offset, entry.second, parent));
69
70 if (entry.first.sibling == ROMFS_ENTRY_EMPTY)
71 break;
72
73 this_file_offset = entry.first.sibling;
74 }
75}
76
77void ProcessDirectory(VirtualFile file, size_t dir_offset, size_t file_offset, size_t data_offset,
78 u32 this_dir_offset, std::shared_ptr<VectorVfsDirectory> parent) {
79 while (true) {
80 auto entry = GetEntry<DirectoryEntry>(file, dir_offset + this_dir_offset);
81 auto current = std::make_shared<VectorVfsDirectory>(
82 std::vector<VirtualFile>{}, std::vector<VirtualDir>{}, parent, entry.second);
83
84 if (entry.first.child_file != ROMFS_ENTRY_EMPTY) {
85 ProcessFile(file, file_offset, data_offset, entry.first.child_file, current);
86 }
87
88 if (entry.first.child_dir != ROMFS_ENTRY_EMPTY) {
89 ProcessDirectory(file, dir_offset, file_offset, data_offset, entry.first.child_dir,
90 current);
91 }
92
93 parent->AddDirectory(current);
94 if (entry.first.sibling == ROMFS_ENTRY_EMPTY)
95 break;
96 this_dir_offset = entry.first.sibling;
97 }
98}
99
100VirtualDir ExtractRomFS(VirtualFile file) {
101 RomFSHeader header{};
102 if (file->ReadObject(&header) != sizeof(RomFSHeader))
103 return nullptr;
104
105 if (header.header_size != sizeof(RomFSHeader))
106 return nullptr;
107
108 const u64 file_offset = header.file_meta.offset;
109 const u64 dir_offset = header.directory_meta.offset + 4;
110
111 const auto root =
112 std::make_shared<VectorVfsDirectory>(std::vector<VirtualFile>{}, std::vector<VirtualDir>{},
113 file->GetContainingDirectory(), file->GetName());
114
115 ProcessDirectory(file, dir_offset, file_offset, header.data_offset, 0, root);
116
117 VirtualDir out = std::move(root);
118
119 while (out->GetSubdirectory("") != nullptr)
120 out = out->GetSubdirectory("");
121
122 return out;
123}
124} // namespace FileSys
diff --git a/src/core/file_sys/romfs.h b/src/core/file_sys/romfs.h
new file mode 100644
index 000000000..03a876d22
--- /dev/null
+++ b/src/core/file_sys/romfs.h
@@ -0,0 +1,35 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8#include "common/common_funcs.h"
9#include "common/swap.h"
10#include "core/file_sys/vfs.h"
11
12namespace FileSys {
13
14struct IVFCLevel {
15 u64_le offset;
16 u64_le size;
17 u32_le block_size;
18 u32_le reserved;
19};
20static_assert(sizeof(IVFCLevel) == 0x18, "IVFCLevel has incorrect size.");
21
22struct IVFCHeader {
23 u32_le magic;
24 u32_le magic_number;
25 INSERT_PADDING_BYTES(8);
26 std::array<IVFCLevel, 6> levels;
27 INSERT_PADDING_BYTES(64);
28};
29static_assert(sizeof(IVFCHeader) == 0xE0, "IVFCHeader has incorrect size.");
30
31// Converts a RomFS binary blob to VFS Filesystem
32// Returns nullptr on failure
33VirtualDir ExtractRomFS(VirtualFile file);
34
35} // namespace FileSys
diff --git a/src/core/file_sys/vfs.cpp b/src/core/file_sys/vfs.cpp
index b99a4fd5b..84a6a7397 100644
--- a/src/core/file_sys/vfs.cpp
+++ b/src/core/file_sys/vfs.cpp
@@ -46,6 +46,13 @@ size_t VfsFile::WriteBytes(const std::vector<u8>& data, size_t offset) {
46 return Write(data.data(), data.size(), offset); 46 return Write(data.data(), data.size(), offset);
47} 47}
48 48
49std::string VfsFile::GetFullPath() const {
50 if (GetContainingDirectory() == nullptr)
51 return "/" + GetName();
52
53 return GetContainingDirectory()->GetFullPath() + "/" + GetName();
54}
55
49std::shared_ptr<VfsFile> VfsDirectory::GetFileRelative(std::string_view path) const { 56std::shared_ptr<VfsFile> VfsDirectory::GetFileRelative(std::string_view path) const {
50 auto vec = FileUtil::SplitPathComponents(path); 57 auto vec = FileUtil::SplitPathComponents(path);
51 vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }), 58 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) {
243 return f2->WriteBytes(f1->ReadAllBytes()) == f1->GetSize(); 250 return f2->WriteBytes(f1->ReadAllBytes()) == f1->GetSize();
244} 251}
245 252
253std::string VfsDirectory::GetFullPath() const {
254 if (IsRoot())
255 return GetName();
256
257 return GetParentDirectory()->GetFullPath() + "/" + GetName();
258}
259
246bool ReadOnlyVfsDirectory::IsWritable() const { 260bool ReadOnlyVfsDirectory::IsWritable() const {
247 return false; 261 return false;
248} 262}
@@ -270,4 +284,13 @@ bool ReadOnlyVfsDirectory::DeleteFile(std::string_view name) {
270bool ReadOnlyVfsDirectory::Rename(std::string_view name) { 284bool ReadOnlyVfsDirectory::Rename(std::string_view name) {
271 return false; 285 return false;
272} 286}
287
288bool VfsRawCopy(VirtualFile src, VirtualFile dest) {
289 if (src == nullptr || dest == nullptr)
290 return false;
291 if (!dest->Resize(src->GetSize()))
292 return false;
293 std::vector<u8> data = src->ReadAllBytes();
294 return dest->WriteBytes(data, 0) == data.size();
295}
273} // namespace FileSys 296} // namespace FileSys
diff --git a/src/core/file_sys/vfs.h b/src/core/file_sys/vfs.h
index 4a13b8378..cf871edd6 100644
--- a/src/core/file_sys/vfs.h
+++ b/src/core/file_sys/vfs.h
@@ -113,6 +113,9 @@ struct VfsFile : NonCopyable {
113 113
114 // Renames the file to name. Returns whether or not the operation was successsful. 114 // Renames the file to name. Returns whether or not the operation was successsful.
115 virtual bool Rename(std::string_view name) = 0; 115 virtual bool Rename(std::string_view name) = 0;
116
117 // Returns the full path of this file as a string, recursively
118 virtual std::string GetFullPath() const;
116}; 119};
117 120
118// A class representing a directory in an abstract filesystem. 121// A class representing a directory in an abstract filesystem.
@@ -213,6 +216,17 @@ struct VfsDirectory : NonCopyable {
213 return ReplaceFileWithSubdirectory(file_p, std::make_shared<Directory>(file_p)); 216 return ReplaceFileWithSubdirectory(file_p, std::make_shared<Directory>(file_p));
214 } 217 }
215 218
219 bool InterpretAsDirectory(const std::function<VirtualDir(VirtualFile)>& function,
220 const std::string& file) {
221 auto file_p = GetFile(file);
222 if (file_p == nullptr)
223 return false;
224 return ReplaceFileWithSubdirectory(file_p, function(file_p));
225 }
226
227 // Returns the full path of this directory as a string, recursively
228 virtual std::string GetFullPath() const;
229
216protected: 230protected:
217 // Backend for InterpretAsDirectory. 231 // Backend for InterpretAsDirectory.
218 // Removes all references to file and adds a reference to dir in the directory's implementation. 232 // Removes all references to file and adds a reference to dir in the directory's implementation.
@@ -230,4 +244,10 @@ struct ReadOnlyVfsDirectory : public VfsDirectory {
230 bool DeleteFile(std::string_view name) override; 244 bool DeleteFile(std::string_view name) override;
231 bool Rename(std::string_view name) override; 245 bool Rename(std::string_view name) override;
232}; 246};
247
248// A method that copies the raw data between two different implementations of VirtualFile. If you
249// are using the same implementation, it is probably better to use the Copy method in the parent
250// directory of src/dest.
251bool VfsRawCopy(VirtualFile src, VirtualFile dest);
252
233} // namespace FileSys 253} // namespace FileSys
diff --git a/src/core/file_sys/vfs_offset.cpp b/src/core/file_sys/vfs_offset.cpp
index a40331cef..847cde2f5 100644
--- a/src/core/file_sys/vfs_offset.cpp
+++ b/src/core/file_sys/vfs_offset.cpp
@@ -10,8 +10,9 @@
10namespace FileSys { 10namespace FileSys {
11 11
12OffsetVfsFile::OffsetVfsFile(std::shared_ptr<VfsFile> file_, size_t size_, size_t offset_, 12OffsetVfsFile::OffsetVfsFile(std::shared_ptr<VfsFile> file_, size_t size_, size_t offset_,
13 std::string name_) 13 std::string name_, VirtualDir parent_)
14 : file(std::move(file_)), offset(offset_), size(size_), name(std::move(name_)) {} 14 : file(file_), offset(offset_), size(size_), name(std::move(name_)),
15 parent(parent_ == nullptr ? file->GetContainingDirectory() : std::move(parent_)) {}
15 16
16std::string OffsetVfsFile::GetName() const { 17std::string OffsetVfsFile::GetName() const {
17 return name.empty() ? file->GetName() : name; 18 return name.empty() ? file->GetName() : name;
@@ -35,7 +36,7 @@ bool OffsetVfsFile::Resize(size_t new_size) {
35} 36}
36 37
37std::shared_ptr<VfsDirectory> OffsetVfsFile::GetContainingDirectory() const { 38std::shared_ptr<VfsDirectory> OffsetVfsFile::GetContainingDirectory() const {
38 return file->GetContainingDirectory(); 39 return parent;
39} 40}
40 41
41bool OffsetVfsFile::IsWritable() const { 42bool OffsetVfsFile::IsWritable() const {
diff --git a/src/core/file_sys/vfs_offset.h b/src/core/file_sys/vfs_offset.h
index 4f471e3ba..235970dc5 100644
--- a/src/core/file_sys/vfs_offset.h
+++ b/src/core/file_sys/vfs_offset.h
@@ -17,7 +17,7 @@ namespace FileSys {
17// the size of this wrapper. 17// the size of this wrapper.
18struct OffsetVfsFile : public VfsFile { 18struct OffsetVfsFile : public VfsFile {
19 OffsetVfsFile(std::shared_ptr<VfsFile> file, size_t size, size_t offset = 0, 19 OffsetVfsFile(std::shared_ptr<VfsFile> file, size_t size, size_t offset = 0,
20 std::string new_name = ""); 20 std::string new_name = "", VirtualDir new_parent = nullptr);
21 21
22 std::string GetName() const override; 22 std::string GetName() const override;
23 size_t GetSize() const override; 23 size_t GetSize() const override;
@@ -44,6 +44,7 @@ private:
44 size_t offset; 44 size_t offset;
45 size_t size; 45 size_t size;
46 std::string name; 46 std::string name;
47 VirtualDir parent;
47}; 48};
48 49
49} // namespace FileSys 50} // namespace FileSys
diff --git a/src/core/file_sys/vfs_real.cpp b/src/core/file_sys/vfs_real.cpp
index 9ce2e1efa..82d54da4a 100644
--- a/src/core/file_sys/vfs_real.cpp
+++ b/src/core/file_sys/vfs_real.cpp
@@ -195,6 +195,12 @@ bool RealVfsDirectory::Rename(std::string_view name) {
195 return FileUtil::Rename(path, new_name); 195 return FileUtil::Rename(path, new_name);
196} 196}
197 197
198std::string RealVfsDirectory::GetFullPath() const {
199 auto out = path;
200 std::replace(out.begin(), out.end(), '\\', '/');
201 return out;
202}
203
198bool RealVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { 204bool RealVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
199 const auto iter = std::find(files.begin(), files.end(), file); 205 const auto iter = std::find(files.begin(), files.end(), file);
200 if (iter == files.end()) 206 if (iter == files.end())
diff --git a/src/core/file_sys/vfs_real.h b/src/core/file_sys/vfs_real.h
index 2151211c9..243d58576 100644
--- a/src/core/file_sys/vfs_real.h
+++ b/src/core/file_sys/vfs_real.h
@@ -41,7 +41,7 @@ private:
41 41
42// An implementation of VfsDirectory that represents a directory on the user's computer. 42// An implementation of VfsDirectory that represents a directory on the user's computer.
43struct RealVfsDirectory : public VfsDirectory { 43struct RealVfsDirectory : public VfsDirectory {
44 RealVfsDirectory(const std::string& path, Mode perms); 44 RealVfsDirectory(const std::string& path, Mode perms = Mode::Read);
45 45
46 std::vector<std::shared_ptr<VfsFile>> GetFiles() const override; 46 std::vector<std::shared_ptr<VfsFile>> GetFiles() const override;
47 std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override; 47 std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override;
@@ -54,6 +54,7 @@ struct RealVfsDirectory : public VfsDirectory {
54 bool DeleteSubdirectory(std::string_view name) override; 54 bool DeleteSubdirectory(std::string_view name) override;
55 bool DeleteFile(std::string_view name) override; 55 bool DeleteFile(std::string_view name) override;
56 bool Rename(std::string_view name) override; 56 bool Rename(std::string_view name) override;
57 std::string GetFullPath() const override;
57 58
58protected: 59protected:
59 bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; 60 bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
diff --git a/src/core/file_sys/vfs_vector.cpp b/src/core/file_sys/vfs_vector.cpp
new file mode 100644
index 000000000..4c6337e3a
--- /dev/null
+++ b/src/core/file_sys/vfs_vector.cpp
@@ -0,0 +1,83 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include "core/file_sys/vfs_vector.h"
7
8namespace FileSys {
9VectorVfsDirectory::VectorVfsDirectory(std::vector<VirtualFile> files_,
10 std::vector<VirtualDir> dirs_, VirtualDir parent_,
11 std::string name_)
12 : files(std::move(files_)), dirs(std::move(dirs_)), parent(std::move(parent_)),
13 name(std::move(name_)) {}
14
15std::vector<std::shared_ptr<VfsFile>> VectorVfsDirectory::GetFiles() const {
16 return files;
17}
18
19std::vector<std::shared_ptr<VfsDirectory>> VectorVfsDirectory::GetSubdirectories() const {
20 return dirs;
21}
22
23bool VectorVfsDirectory::IsWritable() const {
24 return false;
25}
26
27bool VectorVfsDirectory::IsReadable() const {
28 return true;
29}
30
31std::string VectorVfsDirectory::GetName() const {
32 return name;
33}
34std::shared_ptr<VfsDirectory> VectorVfsDirectory::GetParentDirectory() const {
35 return parent;
36}
37
38template <typename T>
39static bool FindAndRemoveVectorElement(std::vector<T>& vec, std::string_view name) {
40 auto iter = std::find_if(vec.begin(), vec.end(), [name](T e) { return e->GetName() == name; });
41 if (iter == vec.end())
42 return false;
43 auto old_size = vec.size();
44 vec.erase(iter);
45 return true;
46}
47
48bool VectorVfsDirectory::DeleteSubdirectory(std::string_view name) {
49 return FindAndRemoveVectorElement(dirs, name);
50}
51
52bool VectorVfsDirectory::DeleteFile(std::string_view name) {
53 return FindAndRemoveVectorElement(files, name);
54}
55
56bool VectorVfsDirectory::Rename(std::string_view name_) {
57 name = name_;
58 return true;
59}
60
61std::shared_ptr<VfsDirectory> VectorVfsDirectory::CreateSubdirectory(std::string_view name) {
62 return nullptr;
63}
64
65std::shared_ptr<VfsFile> VectorVfsDirectory::CreateFile(std::string_view name) {
66 return nullptr;
67}
68
69void VectorVfsDirectory::AddFile(VirtualFile file) {
70 files.push_back(std::move(file));
71}
72
73void VectorVfsDirectory::AddDirectory(VirtualDir dir) {
74 dirs.push_back(std::move(dir));
75}
76
77bool VectorVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
78 if (!DeleteFile(file->GetName()))
79 return false;
80 dirs.emplace_back(dir);
81 return true;
82}
83} // namespace FileSys
diff --git a/src/core/file_sys/vfs_vector.h b/src/core/file_sys/vfs_vector.h
new file mode 100644
index 000000000..ba469647b
--- /dev/null
+++ b/src/core/file_sys/vfs_vector.h
@@ -0,0 +1,44 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "core/file_sys/vfs.h"
8
9namespace FileSys {
10
11// An implementation of VfsDirectory that maintains two vectors for subdirectories and files.
12// Vector data is supplied upon construction.
13struct VectorVfsDirectory : public VfsDirectory {
14 explicit VectorVfsDirectory(std::vector<VirtualFile> files = {},
15 std::vector<VirtualDir> dirs = {}, VirtualDir parent = nullptr,
16 std::string name = "");
17
18 std::vector<std::shared_ptr<VfsFile>> GetFiles() const override;
19 std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override;
20 bool IsWritable() const override;
21 bool IsReadable() const override;
22 std::string GetName() const override;
23 std::shared_ptr<VfsDirectory> GetParentDirectory() const override;
24 bool DeleteSubdirectory(std::string_view name) override;
25 bool DeleteFile(std::string_view name) override;
26 bool Rename(std::string_view name) override;
27 std::shared_ptr<VfsDirectory> CreateSubdirectory(std::string_view name) override;
28 std::shared_ptr<VfsFile> CreateFile(std::string_view name) override;
29
30 virtual void AddFile(VirtualFile file);
31 virtual void AddDirectory(VirtualDir dir);
32
33protected:
34 bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
35
36private:
37 std::vector<VirtualFile> files;
38 std::vector<VirtualDir> dirs;
39
40 VirtualDir parent;
41 std::string name;
42};
43
44} // namespace FileSys
diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp
index 1dcd84d98..a15d53ff8 100644
--- a/src/core/hle/service/audio/audout_u.cpp
+++ b/src/core/hle/service/audio/audout_u.cpp
@@ -5,8 +5,7 @@
5#include <array> 5#include <array>
6#include <vector> 6#include <vector>
7#include "common/logging/log.h" 7#include "common/logging/log.h"
8#include "core/core_timing.h" 8#include "core/core.h"
9#include "core/core_timing_util.h"
10#include "core/hle/ipc_helpers.h" 9#include "core/hle/ipc_helpers.h"
11#include "core/hle/kernel/event.h" 10#include "core/hle/kernel/event.h"
12#include "core/hle/kernel/hle_ipc.h" 11#include "core/hle/kernel/hle_ipc.h"
@@ -14,17 +13,22 @@
14 13
15namespace Service::Audio { 14namespace Service::Audio {
16 15
17/// Switch sample rate frequency 16namespace ErrCodes {
18constexpr u32 sample_rate{48000}; 17enum {
19/// TODO(st4rk): dynamic number of channels, as I think Switch has support 18 ErrorUnknown = 2,
20/// to more audio channels (probably when Docked I guess) 19 BufferCountExceeded = 8,
21constexpr u32 audio_channels{2}; 20};
22/// TODO(st4rk): find a proper value for the audio_ticks 21}
23constexpr u64 audio_ticks{static_cast<u64>(CoreTiming::BASE_CLOCK_RATE / 500)}; 22
23constexpr std::array<char, 10> DefaultDevice{{"DeviceOut"}};
24constexpr int DefaultSampleRate{48000};
24 25
25class IAudioOut final : public ServiceFramework<IAudioOut> { 26class IAudioOut final : public ServiceFramework<IAudioOut> {
26public: 27public:
27 IAudioOut() : ServiceFramework("IAudioOut"), audio_out_state(AudioState::Stopped) { 28 IAudioOut(AudoutParams audio_params)
29 : ServiceFramework("IAudioOut"), audio_params(audio_params),
30 audio_core(Core::System::GetInstance().AudioCore()) {
31
28 static const FunctionInfo functions[] = { 32 static const FunctionInfo functions[] = {
29 {0, &IAudioOut::GetAudioOutState, "GetAudioOutState"}, 33 {0, &IAudioOut::GetAudioOutState, "GetAudioOutState"},
30 {1, &IAudioOut::StartAudioOut, "StartAudioOut"}, 34 {1, &IAudioOut::StartAudioOut, "StartAudioOut"},
@@ -32,66 +36,65 @@ public:
32 {3, &IAudioOut::AppendAudioOutBufferImpl, "AppendAudioOutBuffer"}, 36 {3, &IAudioOut::AppendAudioOutBufferImpl, "AppendAudioOutBuffer"},
33 {4, &IAudioOut::RegisterBufferEvent, "RegisterBufferEvent"}, 37 {4, &IAudioOut::RegisterBufferEvent, "RegisterBufferEvent"},
34 {5, &IAudioOut::GetReleasedAudioOutBufferImpl, "GetReleasedAudioOutBuffer"}, 38 {5, &IAudioOut::GetReleasedAudioOutBufferImpl, "GetReleasedAudioOutBuffer"},
35 {6, nullptr, "ContainsAudioOutBuffer"}, 39 {6, &IAudioOut::ContainsAudioOutBuffer, "ContainsAudioOutBuffer"},
36 {7, &IAudioOut::AppendAudioOutBufferImpl, "AppendAudioOutBufferAuto"}, 40 {7, &IAudioOut::AppendAudioOutBufferImpl, "AppendAudioOutBufferAuto"},
37 {8, &IAudioOut::GetReleasedAudioOutBufferImpl, "GetReleasedAudioOutBufferAuto"}, 41 {8, &IAudioOut::GetReleasedAudioOutBufferImpl, "GetReleasedAudioOutBufferAuto"},
38 {9, nullptr, "GetAudioOutBufferCount"}, 42 {9, &IAudioOut::GetAudioOutBufferCount, "GetAudioOutBufferCount"},
39 {10, nullptr, "GetAudioOutPlayedSampleCount"}, 43 {10, nullptr, "GetAudioOutPlayedSampleCount"},
40 {11, nullptr, "FlushAudioOutBuffers"}, 44 {11, nullptr, "FlushAudioOutBuffers"},
41 }; 45 };
42 RegisterHandlers(functions); 46 RegisterHandlers(functions);
43 47
44 // This is the event handle used to check if the audio buffer was released 48 // This is the event handle used to check if the audio buffer was released
45 buffer_event = 49 buffer_event = Kernel::Event::Create(Kernel::ResetType::Sticky, "IAudioOutBufferReleased");
46 Kernel::Event::Create(Kernel::ResetType::OneShot, "IAudioOutBufferReleasedEvent");
47
48 // Register event callback to update the Audio Buffer
49 audio_event = CoreTiming::RegisterEvent(
50 "IAudioOut::UpdateAudioBuffersCallback", [this](u64 userdata, int cycles_late) {
51 UpdateAudioBuffersCallback();
52 CoreTiming::ScheduleEvent(audio_ticks - cycles_late, audio_event);
53 });
54
55 // Start the audio event
56 CoreTiming::ScheduleEvent(audio_ticks, audio_event);
57 }
58 50
59 ~IAudioOut() { 51 stream = audio_core.OpenStream(audio_params.sample_rate, audio_params.channel_count,
60 CoreTiming::UnscheduleEvent(audio_event, 0); 52 [=]() { buffer_event->Signal(); });
61 } 53 }
62 54
63private: 55private:
56 struct AudioBuffer {
57 u64_le next;
58 u64_le buffer;
59 u64_le buffer_capacity;
60 u64_le buffer_size;
61 u64_le offset;
62 };
63 static_assert(sizeof(AudioBuffer) == 0x28, "AudioBuffer is an invalid size");
64
64 void GetAudioOutState(Kernel::HLERequestContext& ctx) { 65 void GetAudioOutState(Kernel::HLERequestContext& ctx) {
65 LOG_DEBUG(Service_Audio, "called"); 66 LOG_DEBUG(Service_Audio, "called");
66 IPC::ResponseBuilder rb{ctx, 3}; 67 IPC::ResponseBuilder rb{ctx, 3};
67 rb.Push(RESULT_SUCCESS); 68 rb.Push(RESULT_SUCCESS);
68 rb.Push(static_cast<u32>(audio_out_state)); 69 rb.Push(static_cast<u32>(stream->IsPlaying() ? AudioState::Started : AudioState::Stopped));
69 } 70 }
70 71
71 void StartAudioOut(Kernel::HLERequestContext& ctx) { 72 void StartAudioOut(Kernel::HLERequestContext& ctx) {
72 LOG_WARNING(Service_Audio, "(STUBBED) called"); 73 LOG_DEBUG(Service_Audio, "called");
74
75 if (stream->IsPlaying()) {
76 IPC::ResponseBuilder rb{ctx, 2};
77 rb.Push(ResultCode(ErrorModule::Audio, ErrCodes::ErrorUnknown));
78 return;
79 }
73 80
74 // Start audio 81 audio_core.StartStream(stream);
75 audio_out_state = AudioState::Started;
76 82
77 IPC::ResponseBuilder rb{ctx, 2}; 83 IPC::ResponseBuilder rb{ctx, 2};
78 rb.Push(RESULT_SUCCESS); 84 rb.Push(RESULT_SUCCESS);
79 } 85 }
80 86
81 void StopAudioOut(Kernel::HLERequestContext& ctx) { 87 void StopAudioOut(Kernel::HLERequestContext& ctx) {
82 LOG_WARNING(Service_Audio, "(STUBBED) called"); 88 LOG_DEBUG(Service_Audio, "called");
83
84 // Stop audio
85 audio_out_state = AudioState::Stopped;
86 89
87 queue_keys.clear(); 90 audio_core.StopStream(stream);
88 91
89 IPC::ResponseBuilder rb{ctx, 2}; 92 IPC::ResponseBuilder rb{ctx, 2};
90 rb.Push(RESULT_SUCCESS); 93 rb.Push(RESULT_SUCCESS);
91 } 94 }
92 95
93 void RegisterBufferEvent(Kernel::HLERequestContext& ctx) { 96 void RegisterBufferEvent(Kernel::HLERequestContext& ctx) {
94 LOG_WARNING(Service_Audio, "(STUBBED) called"); 97 LOG_DEBUG(Service_Audio, "called");
95 98
96 IPC::ResponseBuilder rb{ctx, 2, 1}; 99 IPC::ResponseBuilder rb{ctx, 2, 1};
97 rb.Push(RESULT_SUCCESS); 100 rb.Push(RESULT_SUCCESS);
@@ -99,101 +102,107 @@ private:
99 } 102 }
100 103
101 void AppendAudioOutBufferImpl(Kernel::HLERequestContext& ctx) { 104 void AppendAudioOutBufferImpl(Kernel::HLERequestContext& ctx) {
102 LOG_WARNING(Service_Audio, "(STUBBED) called"); 105 LOG_DEBUG(Service_Audio, "(STUBBED) called {}", ctx.Description());
103 IPC::RequestParser rp{ctx}; 106 IPC::RequestParser rp{ctx};
104 107
105 const u64 key{rp.Pop<u64>()}; 108 const auto& input_buffer{ctx.ReadBuffer()};
106 queue_keys.insert(queue_keys.begin(), key); 109 ASSERT_MSG(input_buffer.size() == sizeof(AudioBuffer),
110 "AudioBuffer input is an invalid size!");
111 AudioBuffer audio_buffer{};
112 std::memcpy(&audio_buffer, input_buffer.data(), sizeof(AudioBuffer));
113 const u64 tag{rp.Pop<u64>()};
114
115 std::vector<u8> data(audio_buffer.buffer_size);
116 Memory::ReadBlock(audio_buffer.buffer, data.data(), data.size());
117
118 if (!audio_core.QueueBuffer(stream, tag, std::move(data))) {
119 IPC::ResponseBuilder rb{ctx, 2};
120 rb.Push(ResultCode(ErrorModule::Audio, ErrCodes::BufferCountExceeded));
121 }
107 122
108 IPC::ResponseBuilder rb{ctx, 2}; 123 IPC::ResponseBuilder rb{ctx, 2};
109 rb.Push(RESULT_SUCCESS); 124 rb.Push(RESULT_SUCCESS);
110 } 125 }
111 126
112 void GetReleasedAudioOutBufferImpl(Kernel::HLERequestContext& ctx) { 127 void GetReleasedAudioOutBufferImpl(Kernel::HLERequestContext& ctx) {
113 LOG_WARNING(Service_Audio, "(STUBBED) called"); 128 LOG_DEBUG(Service_Audio, "called {}", ctx.Description());
114 129 IPC::RequestParser rp{ctx};
115 // TODO(st4rk): This is how libtransistor currently implements the 130 const u64 max_count{ctx.GetWriteBufferSize() / sizeof(u64)};
116 // GetReleasedAudioOutBuffer, it should return the key (a VAddr) to the app and this address 131 const auto released_buffers{audio_core.GetTagsAndReleaseBuffers(stream, max_count)};
117 // is used to know which buffer should be filled with data and send again to the service
118 // through AppendAudioOutBuffer. Check if this is the proper way to do it.
119 u64 key{0};
120
121 if (queue_keys.size()) {
122 key = queue_keys.back();
123 queue_keys.pop_back();
124 }
125 132
126 ctx.WriteBuffer(&key, sizeof(u64)); 133 std::vector<u64> tags{released_buffers};
134 tags.resize(max_count);
135 ctx.WriteBuffer(tags);
127 136
128 IPC::ResponseBuilder rb{ctx, 3}; 137 IPC::ResponseBuilder rb{ctx, 3};
129 rb.Push(RESULT_SUCCESS); 138 rb.Push(RESULT_SUCCESS);
130 // TODO(st4rk): This might be the total of released buffers, needs to be verified on 139 rb.Push<u32>(static_cast<u32>(released_buffers.size()));
131 // hardware
132 rb.Push<u32>(static_cast<u32>(queue_keys.size()));
133 } 140 }
134 141
135 void UpdateAudioBuffersCallback() { 142 void ContainsAudioOutBuffer(Kernel::HLERequestContext& ctx) {
136 if (audio_out_state != AudioState::Started) { 143 LOG_DEBUG(Service_Audio, "called");
137 return; 144 IPC::RequestParser rp{ctx};
138 } 145 const u64 tag{rp.Pop<u64>()};
139 146 IPC::ResponseBuilder rb{ctx, 3};
140 if (queue_keys.empty()) { 147 rb.Push(RESULT_SUCCESS);
141 return; 148 rb.Push(stream->ContainsBuffer(tag));
142 } 149 }
143 150
144 buffer_event->Signal(); 151 void GetAudioOutBufferCount(Kernel::HLERequestContext& ctx) {
152 LOG_DEBUG(Service_Audio, "called");
153 IPC::ResponseBuilder rb{ctx, 3};
154 rb.Push(RESULT_SUCCESS);
155 rb.Push(static_cast<u32>(stream->GetQueueSize()));
145 } 156 }
146 157
147 enum class AudioState : u32 { 158 AudioCore::AudioOut& audio_core;
148 Started, 159 AudioCore::StreamPtr stream;
149 Stopped,
150 };
151 160
152 /// This is used to trigger the audio event callback that is going to read the samples from the 161 AudoutParams audio_params{};
153 /// audio_buffer list and enqueue the samples using the sink (audio_core).
154 CoreTiming::EventType* audio_event;
155 162
156 /// This is the evend handle used to check if the audio buffer was released 163 /// This is the evend handle used to check if the audio buffer was released
157 Kernel::SharedPtr<Kernel::Event> buffer_event; 164 Kernel::SharedPtr<Kernel::Event> buffer_event;
158
159 /// (st4rk): This is just a temporary workaround for the future implementation. Libtransistor
160 /// uses the key as an address in the App, so we need to return when the
161 /// GetReleasedAudioOutBuffer_1 is called, otherwise we'll run in problems, because
162 /// libtransistor uses the key returned as an pointer.
163 std::vector<u64> queue_keys;
164
165 AudioState audio_out_state;
166}; 165};
167 166
168void AudOutU::ListAudioOutsImpl(Kernel::HLERequestContext& ctx) { 167void AudOutU::ListAudioOutsImpl(Kernel::HLERequestContext& ctx) {
169 LOG_WARNING(Service_Audio, "(STUBBED) called"); 168 LOG_DEBUG(Service_Audio, "called");
170 IPC::RequestParser rp{ctx}; 169 IPC::RequestParser rp{ctx};
171 170
172 constexpr std::array<char, 15> audio_interface{{"AudioInterface"}}; 171 ctx.WriteBuffer(DefaultDevice);
173 ctx.WriteBuffer(audio_interface);
174 172
175 IPC::ResponseBuilder rb = rp.MakeBuilder(3, 0, 0); 173 IPC::ResponseBuilder rb = rp.MakeBuilder(3, 0, 0);
176 174
177 rb.Push(RESULT_SUCCESS); 175 rb.Push(RESULT_SUCCESS);
178 // TODO(st4rk): We're currently returning only one audio interface (stringlist size). However, 176 rb.Push<u32>(1); // Amount of audio devices
179 // it's highly possible to have more than one interface (despite that libtransistor requires
180 // only one).
181 rb.Push<u32>(1);
182} 177}
183 178
184void AudOutU::OpenAudioOutImpl(Kernel::HLERequestContext& ctx) { 179void AudOutU::OpenAudioOutImpl(Kernel::HLERequestContext& ctx) {
185 LOG_WARNING(Service_Audio, "(STUBBED) called"); 180 LOG_DEBUG(Service_Audio, "called");
186 181
187 if (!audio_out_interface) { 182 ctx.WriteBuffer(DefaultDevice);
188 audio_out_interface = std::make_shared<IAudioOut>(); 183 IPC::RequestParser rp{ctx};
184 auto params{rp.PopRaw<AudoutParams>()};
185 if (params.channel_count <= 2) {
186 // Mono does not exist for audout
187 params.channel_count = 2;
188 } else {
189 params.channel_count = 6;
189 } 190 }
191 if (!params.sample_rate) {
192 params.sample_rate = DefaultSampleRate;
193 }
194
195 // TODO(bunnei): Support more than one IAudioOut interface. When we add this, ListAudioOutsImpl
196 // will likely need to be updated as well.
197 ASSERT_MSG(!audio_out_interface, "Unimplemented");
198 audio_out_interface = std::make_shared<IAudioOut>(std::move(params));
190 199
191 IPC::ResponseBuilder rb{ctx, 6, 0, 1}; 200 IPC::ResponseBuilder rb{ctx, 6, 0, 1};
192 rb.Push(RESULT_SUCCESS); 201 rb.Push(RESULT_SUCCESS);
193 rb.Push<u32>(sample_rate); 202 rb.Push<u32>(DefaultSampleRate);
194 rb.Push<u32>(audio_channels); 203 rb.Push<u32>(params.channel_count);
195 rb.Push<u32>(static_cast<u32>(PcmFormat::Int16)); 204 rb.Push<u32>(static_cast<u32>(PcmFormat::Int16));
196 rb.Push<u32>(0); // This field is unknown 205 rb.Push<u32>(static_cast<u32>(AudioState::Stopped));
197 rb.PushIpcInterface<Audio::IAudioOut>(audio_out_interface); 206 rb.PushIpcInterface<Audio::IAudioOut>(audio_out_interface);
198} 207}
199 208
diff --git a/src/core/hle/service/audio/audout_u.h b/src/core/hle/service/audio/audout_u.h
index 847d86aa6..bc43f1f44 100644
--- a/src/core/hle/service/audio/audout_u.h
+++ b/src/core/hle/service/audio/audout_u.h
@@ -12,6 +12,18 @@ class HLERequestContext;
12 12
13namespace Service::Audio { 13namespace Service::Audio {
14 14
15struct AudoutParams {
16 s32_le sample_rate;
17 u16_le channel_count;
18 INSERT_PADDING_BYTES(2);
19};
20static_assert(sizeof(AudoutParams) == 0x8, "AudoutParams is an invalid size");
21
22enum class AudioState : u32 {
23 Started,
24 Stopped,
25};
26
15class IAudioOut; 27class IAudioOut;
16 28
17class AudOutU final : public ServiceFramework<AudOutU> { 29class AudOutU final : public ServiceFramework<AudOutU> {
diff --git a/src/core/hle/service/btdrv/btdrv.cpp b/src/core/hle/service/btdrv/btdrv.cpp
new file mode 100644
index 000000000..d0a15cc4c
--- /dev/null
+++ b/src/core/hle/service/btdrv/btdrv.cpp
@@ -0,0 +1,72 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/service/btdrv/btdrv.h"
6#include "core/hle/service/service.h"
7#include "core/hle/service/sm/sm.h"
8
9namespace Service::BtDrv {
10
11class BtDrv final : public ServiceFramework<BtDrv> {
12public:
13 explicit BtDrv() : ServiceFramework{"btdrv"} {
14 // clang-format off
15 static const FunctionInfo functions[] = {
16 {0, nullptr, "Unknown"},
17 {1, nullptr, "Init"},
18 {2, nullptr, "Enable"},
19 {3, nullptr, "Disable"},
20 {4, nullptr, "CleanupAndShutdown"},
21 {5, nullptr, "GetAdapterProperties"},
22 {6, nullptr, "GetAdapterProperty"},
23 {7, nullptr, "SetAdapterProperty"},
24 {8, nullptr, "StartDiscovery"},
25 {9, nullptr, "CancelDiscovery"},
26 {10, nullptr, "CreateBond"},
27 {11, nullptr, "RemoveBond"},
28 {12, nullptr, "CancelBond"},
29 {13, nullptr, "PinReply"},
30 {14, nullptr, "SspReply"},
31 {15, nullptr, "Unknown2"},
32 {16, nullptr, "InitInterfaces"},
33 {17, nullptr, "HidHostInterface_Connect"},
34 {18, nullptr, "HidHostInterface_Disconnect"},
35 {19, nullptr, "HidHostInterface_SendData"},
36 {20, nullptr, "HidHostInterface_SendData2"},
37 {21, nullptr, "HidHostInterface_SetReport"},
38 {22, nullptr, "HidHostInterface_GetReport"},
39 {23, nullptr, "HidHostInterface_WakeController"},
40 {24, nullptr, "HidHostInterface_AddPairedDevice"},
41 {25, nullptr, "HidHostInterface_GetPairedDevice"},
42 {26, nullptr, "HidHostInterface_CleanupAndShutdown"},
43 {27, nullptr, "Unknown3"},
44 {28, nullptr, "ExtInterface_SetTSI"},
45 {29, nullptr, "ExtInterface_SetBurstMode"},
46 {30, nullptr, "ExtInterface_SetZeroRetran"},
47 {31, nullptr, "ExtInterface_SetMcMode"},
48 {32, nullptr, "ExtInterface_StartLlrMode"},
49 {33, nullptr, "ExtInterface_ExitLlrMode"},
50 {34, nullptr, "ExtInterface_SetRadio"},
51 {35, nullptr, "ExtInterface_SetVisibility"},
52 {36, nullptr, "Unknown4"},
53 {37, nullptr, "Unknown5"},
54 {38, nullptr, "HidHostInterface_GetLatestPlr"},
55 {39, nullptr, "ExtInterface_GetPendingConnections"},
56 {40, nullptr, "HidHostInterface_GetChannelMap"},
57 {41, nullptr, "SetIsBluetoothBoostEnabled"},
58 {42, nullptr, "GetIsBluetoothBoostEnabled"},
59 {43, nullptr, "SetIsBluetoothAfhEnabled"},
60 {44, nullptr, "GetIsBluetoothAfhEnabled"},
61 };
62 // clang-format on
63
64 RegisterHandlers(functions);
65 }
66};
67
68void InstallInterfaces(SM::ServiceManager& sm) {
69 std::make_shared<BtDrv>()->InstallAsService(sm);
70}
71
72} // namespace Service::BtDrv
diff --git a/src/core/hle/service/btdrv/btdrv.h b/src/core/hle/service/btdrv/btdrv.h
new file mode 100644
index 000000000..164e56f43
--- /dev/null
+++ b/src/core/hle/service/btdrv/btdrv.h
@@ -0,0 +1,16 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7namespace Service::SM {
8class ServiceManager;
9}
10
11namespace Service::BtDrv {
12
13/// Registers all BtDrv services with the specified service manager.
14void InstallInterfaces(SM::ServiceManager& sm);
15
16} // namespace Service::BtDrv
diff --git a/src/core/hle/service/lbl/lbl.cpp b/src/core/hle/service/lbl/lbl.cpp
new file mode 100644
index 000000000..8fc8b1057
--- /dev/null
+++ b/src/core/hle/service/lbl/lbl.cpp
@@ -0,0 +1,90 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <memory>
6
7#include "common/logging/log.h"
8#include "core/hle/ipc_helpers.h"
9#include "core/hle/kernel/hle_ipc.h"
10#include "core/hle/service/lbl/lbl.h"
11#include "core/hle/service/service.h"
12#include "core/hle/service/sm/sm.h"
13
14namespace Service::LBL {
15
16class LBL final : public ServiceFramework<LBL> {
17public:
18 explicit LBL() : ServiceFramework{"lbl"} {
19 // clang-format off
20 static const FunctionInfo functions[] = {
21 {0, nullptr, "Unknown1"},
22 {1, nullptr, "Unknown2"},
23 {2, nullptr, "Unknown3"},
24 {3, nullptr, "Unknown4"},
25 {4, nullptr, "Unknown5"},
26 {5, nullptr, "Unknown6"},
27 {6, nullptr, "TurnOffBacklight"},
28 {7, nullptr, "TurnOnBacklight"},
29 {8, nullptr, "GetBacklightStatus"},
30 {9, nullptr, "Unknown7"},
31 {10, nullptr, "Unknown8"},
32 {11, nullptr, "Unknown9"},
33 {12, nullptr, "Unknown10"},
34 {13, nullptr, "Unknown11"},
35 {14, nullptr, "Unknown12"},
36 {15, nullptr, "Unknown13"},
37 {16, nullptr, "ReadRawLightSensor"},
38 {17, nullptr, "Unknown14"},
39 {18, nullptr, "Unknown15"},
40 {19, nullptr, "Unknown16"},
41 {20, nullptr, "Unknown17"},
42 {21, nullptr, "Unknown18"},
43 {22, nullptr, "Unknown19"},
44 {23, nullptr, "Unknown20"},
45 {24, nullptr, "Unknown21"},
46 {25, nullptr, "Unknown22"},
47 {26, &LBL::EnableVrMode, "EnableVrMode"},
48 {27, &LBL::DisableVrMode, "DisableVrMode"},
49 {28, &LBL::GetVrMode, "GetVrMode"},
50 };
51 // clang-format on
52
53 RegisterHandlers(functions);
54 }
55
56private:
57 void EnableVrMode(Kernel::HLERequestContext& ctx) {
58 IPC::ResponseBuilder rb{ctx, 2};
59 rb.Push(RESULT_SUCCESS);
60
61 vr_mode_enabled = true;
62
63 LOG_DEBUG(Service_LBL, "called");
64 }
65
66 void DisableVrMode(Kernel::HLERequestContext& ctx) {
67 IPC::ResponseBuilder rb{ctx, 2};
68 rb.Push(RESULT_SUCCESS);
69
70 vr_mode_enabled = false;
71
72 LOG_DEBUG(Service_LBL, "called");
73 }
74
75 void GetVrMode(Kernel::HLERequestContext& ctx) {
76 IPC::ResponseBuilder rb{ctx, 3};
77 rb.Push(RESULT_SUCCESS);
78 rb.Push(vr_mode_enabled);
79
80 LOG_DEBUG(Service_LBL, "called");
81 }
82
83 bool vr_mode_enabled = false;
84};
85
86void InstallInterfaces(SM::ServiceManager& sm) {
87 std::make_shared<LBL>()->InstallAsService(sm);
88}
89
90} // namespace Service::LBL
diff --git a/src/core/hle/service/lbl/lbl.h b/src/core/hle/service/lbl/lbl.h
new file mode 100644
index 000000000..bf6f400f8
--- /dev/null
+++ b/src/core/hle/service/lbl/lbl.h
@@ -0,0 +1,15 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7namespace Service::SM {
8class ServiceManager;
9}
10
11namespace Service::LBL {
12
13void InstallInterfaces(SM::ServiceManager& sm);
14
15} // namespace Service::LBL
diff --git a/src/core/hle/service/nfc/nfc.cpp b/src/core/hle/service/nfc/nfc.cpp
new file mode 100644
index 000000000..8fec97db8
--- /dev/null
+++ b/src/core/hle/service/nfc/nfc.cpp
@@ -0,0 +1,222 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <memory>
6
7#include "common/logging/log.h"
8#include "core/hle/ipc_helpers.h"
9#include "core/hle/kernel/hle_ipc.h"
10#include "core/hle/service/nfc/nfc.h"
11#include "core/hle/service/service.h"
12#include "core/hle/service/sm/sm.h"
13
14namespace Service::NFC {
15
16class IAm final : public ServiceFramework<IAm> {
17public:
18 explicit IAm() : ServiceFramework{"IAm"} {
19 // clang-format off
20 static const FunctionInfo functions[] = {
21 {0, nullptr, "Initialize"},
22 {1, nullptr, "Finalize"},
23 {2, nullptr, "NotifyForegroundApplet"},
24 };
25 // clang-format on
26
27 RegisterHandlers(functions);
28 }
29};
30
31class NFC_AM final : public ServiceFramework<NFC_AM> {
32public:
33 explicit NFC_AM() : ServiceFramework{"nfc:am"} {
34 // clang-format off
35 static const FunctionInfo functions[] = {
36 {0, &NFC_AM::CreateAmInterface, "CreateAmInterface"},
37 };
38 // clang-format on
39
40 RegisterHandlers(functions);
41 }
42
43private:
44 void CreateAmInterface(Kernel::HLERequestContext& ctx) {
45 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
46 rb.Push(RESULT_SUCCESS);
47 rb.PushIpcInterface<IAm>();
48
49 LOG_DEBUG(Service_NFC, "called");
50 }
51};
52
53class MFIUser final : public ServiceFramework<MFIUser> {
54public:
55 explicit MFIUser() : ServiceFramework{"IUser"} {
56 // clang-format off
57 static const FunctionInfo functions[] = {
58 {0, nullptr, "Initialize"},
59 {1, nullptr, "Finalize"},
60 {2, nullptr, "ListDevices"},
61 {3, nullptr, "StartDetection"},
62 {4, nullptr, "StopDetection"},
63 {5, nullptr, "Read"},
64 {6, nullptr, "Write"},
65 {7, nullptr, "GetTagInfo"},
66 {8, nullptr, "GetActivateEventHandle"},
67 {9, nullptr, "GetDeactivateEventHandle"},
68 {10, nullptr, "GetState"},
69 {11, nullptr, "GetDeviceState"},
70 {12, nullptr, "GetNpadId"},
71 {13, nullptr, "GetAvailabilityChangeEventHandle"},
72 };
73 // clang-format on
74
75 RegisterHandlers(functions);
76 }
77};
78
79class NFC_MF_U final : public ServiceFramework<NFC_MF_U> {
80public:
81 explicit NFC_MF_U() : ServiceFramework{"nfc:mf:u"} {
82 // clang-format off
83 static const FunctionInfo functions[] = {
84 {0, &NFC_MF_U::CreateUserInterface, "CreateUserInterface"},
85 };
86 // clang-format on
87
88 RegisterHandlers(functions);
89 }
90
91private:
92 void CreateUserInterface(Kernel::HLERequestContext& ctx) {
93 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
94 rb.Push(RESULT_SUCCESS);
95 rb.PushIpcInterface<MFIUser>();
96
97 LOG_DEBUG(Service_NFC, "called");
98 }
99};
100
101class IUser final : public ServiceFramework<IUser> {
102public:
103 explicit IUser() : ServiceFramework{"IUser"} {
104 // clang-format off
105 static const FunctionInfo functions[] = {
106 {0, nullptr, "Initialize"},
107 {1, nullptr, "Finalize"},
108 {2, nullptr, "GetState"},
109 {3, nullptr, "IsNfcEnabled"},
110 {400, nullptr, "Initialize"},
111 {401, nullptr, "Finalize"},
112 {402, nullptr, "GetState"},
113 {403, nullptr, "IsNfcEnabled"},
114 {404, nullptr, "ListDevices"},
115 {405, nullptr, "GetDeviceState"},
116 {406, nullptr, "GetNpadId"},
117 {407, nullptr, "AttachAvailabilityChangeEvent"},
118 {408, nullptr, "StartDetection"},
119 {409, nullptr, "StopDetection"},
120 {410, nullptr, "GetTagInfo"},
121 {411, nullptr, "AttachActivateEvent"},
122 {412, nullptr, "AttachDeactivateEvent"},
123 {1000, nullptr, "ReadMifare"},
124 {1001, nullptr, "WriteMifare"},
125 {1300, nullptr, "SendCommandByPassThrough"},
126 {1301, nullptr, "KeepPassThroughSession"},
127 {1302, nullptr, "ReleasePassThroughSession"},
128 };
129 // clang-format on
130
131 RegisterHandlers(functions);
132 }
133};
134
135class NFC_U final : public ServiceFramework<NFC_U> {
136public:
137 explicit NFC_U() : ServiceFramework{"nfc:u"} {
138 // clang-format off
139 static const FunctionInfo functions[] = {
140 {0, &NFC_U::CreateUserInterface, "CreateUserInterface"},
141 };
142 // clang-format on
143
144 RegisterHandlers(functions);
145 }
146
147private:
148 void CreateUserInterface(Kernel::HLERequestContext& ctx) {
149 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
150 rb.Push(RESULT_SUCCESS);
151 rb.PushIpcInterface<IUser>();
152
153 LOG_DEBUG(Service_NFC, "called");
154 }
155};
156
157class ISystem final : public ServiceFramework<ISystem> {
158public:
159 explicit ISystem() : ServiceFramework{"ISystem"} {
160 // clang-format off
161 static const FunctionInfo functions[] = {
162 {0, nullptr, "Initialize"},
163 {1, nullptr, "Finalize"},
164 {2, nullptr, "GetState"},
165 {3, nullptr, "IsNfcEnabled"},
166 {100, nullptr, "SetNfcEnabled"},
167 {400, nullptr, "InitializeSystem"},
168 {401, nullptr, "FinalizeSystem"},
169 {402, nullptr, "GetState"},
170 {403, nullptr, "IsNfcEnabled"},
171 {404, nullptr, "ListDevices"},
172 {405, nullptr, "GetDeviceState"},
173 {406, nullptr, "GetNpadId"},
174 {407, nullptr, "AttachAvailabilityChangeEvent"},
175 {408, nullptr, "StartDetection"},
176 {409, nullptr, "StopDetection"},
177 {410, nullptr, "GetTagInfo"},
178 {411, nullptr, "AttachActivateEvent"},
179 {412, nullptr, "AttachDeactivateEvent"},
180 {500, nullptr, "SetNfcEnabled"},
181 {1000, nullptr, "ReadMifare"},
182 {1001, nullptr, "WriteMifare"},
183 {1300, nullptr, "SendCommandByPassThrough"},
184 {1301, nullptr, "KeepPassThroughSession"},
185 {1302, nullptr, "ReleasePassThroughSession"},
186 };
187 // clang-format on
188
189 RegisterHandlers(functions);
190 }
191};
192
193class NFC_SYS final : public ServiceFramework<NFC_SYS> {
194public:
195 explicit NFC_SYS() : ServiceFramework{"nfc:sys"} {
196 // clang-format off
197 static const FunctionInfo functions[] = {
198 {0, &NFC_SYS::CreateSystemInterface, "CreateSystemInterface"},
199 };
200 // clang-format on
201
202 RegisterHandlers(functions);
203 }
204
205private:
206 void CreateSystemInterface(Kernel::HLERequestContext& ctx) {
207 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
208 rb.Push(RESULT_SUCCESS);
209 rb.PushIpcInterface<ISystem>();
210
211 LOG_DEBUG(Service_NFC, "called");
212 }
213};
214
215void InstallInterfaces(SM::ServiceManager& sm) {
216 std::make_shared<NFC_AM>()->InstallAsService(sm);
217 std::make_shared<NFC_MF_U>()->InstallAsService(sm);
218 std::make_shared<NFC_U>()->InstallAsService(sm);
219 std::make_shared<NFC_SYS>()->InstallAsService(sm);
220}
221
222} // namespace Service::NFC
diff --git a/src/core/hle/service/nfc/nfc.h b/src/core/hle/service/nfc/nfc.h
new file mode 100644
index 000000000..4d2d815f9
--- /dev/null
+++ b/src/core/hle/service/nfc/nfc.h
@@ -0,0 +1,15 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7namespace Service::SM {
8class ServiceManager;
9}
10
11namespace Service::NFC {
12
13void InstallInterfaces(SM::ServiceManager& sm);
14
15} // namespace Service::NFC
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index 76a73f741..98c912eb3 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -21,6 +21,7 @@
21#include "core/hle/service/apm/apm.h" 21#include "core/hle/service/apm/apm.h"
22#include "core/hle/service/audio/audio.h" 22#include "core/hle/service/audio/audio.h"
23#include "core/hle/service/bcat/bcat.h" 23#include "core/hle/service/bcat/bcat.h"
24#include "core/hle/service/btdrv/btdrv.h"
24#include "core/hle/service/erpt/erpt.h" 25#include "core/hle/service/erpt/erpt.h"
25#include "core/hle/service/es/es.h" 26#include "core/hle/service/es/es.h"
26#include "core/hle/service/eupld/eupld.h" 27#include "core/hle/service/eupld/eupld.h"
@@ -29,11 +30,13 @@
29#include "core/hle/service/friend/friend.h" 30#include "core/hle/service/friend/friend.h"
30#include "core/hle/service/grc/grc.h" 31#include "core/hle/service/grc/grc.h"
31#include "core/hle/service/hid/hid.h" 32#include "core/hle/service/hid/hid.h"
33#include "core/hle/service/lbl/lbl.h"
32#include "core/hle/service/ldn/ldn.h" 34#include "core/hle/service/ldn/ldn.h"
33#include "core/hle/service/ldr/ldr.h" 35#include "core/hle/service/ldr/ldr.h"
34#include "core/hle/service/lm/lm.h" 36#include "core/hle/service/lm/lm.h"
35#include "core/hle/service/mii/mii.h" 37#include "core/hle/service/mii/mii.h"
36#include "core/hle/service/mm/mm_u.h" 38#include "core/hle/service/mm/mm_u.h"
39#include "core/hle/service/nfc/nfc.h"
37#include "core/hle/service/nfp/nfp.h" 40#include "core/hle/service/nfp/nfp.h"
38#include "core/hle/service/nifm/nifm.h" 41#include "core/hle/service/nifm/nifm.h"
39#include "core/hle/service/nim/nim.h" 42#include "core/hle/service/nim/nim.h"
@@ -194,8 +197,9 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm) {
194 AM::InstallInterfaces(*sm, nv_flinger); 197 AM::InstallInterfaces(*sm, nv_flinger);
195 AOC::InstallInterfaces(*sm); 198 AOC::InstallInterfaces(*sm);
196 APM::InstallInterfaces(*sm); 199 APM::InstallInterfaces(*sm);
197 BCAT::InstallInterfaces(*sm);
198 Audio::InstallInterfaces(*sm); 200 Audio::InstallInterfaces(*sm);
201 BCAT::InstallInterfaces(*sm);
202 BtDrv::InstallInterfaces(*sm);
199 ERPT::InstallInterfaces(*sm); 203 ERPT::InstallInterfaces(*sm);
200 ES::InstallInterfaces(*sm); 204 ES::InstallInterfaces(*sm);
201 EUPLD::InstallInterfaces(*sm); 205 EUPLD::InstallInterfaces(*sm);
@@ -204,11 +208,13 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm) {
204 Friend::InstallInterfaces(*sm); 208 Friend::InstallInterfaces(*sm);
205 GRC::InstallInterfaces(*sm); 209 GRC::InstallInterfaces(*sm);
206 HID::InstallInterfaces(*sm); 210 HID::InstallInterfaces(*sm);
211 LBL::InstallInterfaces(*sm);
207 LDN::InstallInterfaces(*sm); 212 LDN::InstallInterfaces(*sm);
208 LDR::InstallInterfaces(*sm); 213 LDR::InstallInterfaces(*sm);
209 LM::InstallInterfaces(*sm); 214 LM::InstallInterfaces(*sm);
210 Mii::InstallInterfaces(*sm); 215 Mii::InstallInterfaces(*sm);
211 MM::InstallInterfaces(*sm); 216 MM::InstallInterfaces(*sm);
217 NFC::InstallInterfaces(*sm);
212 NFP::InstallInterfaces(*sm); 218 NFP::InstallInterfaces(*sm);
213 NIFM::InstallInterfaces(*sm); 219 NIFM::InstallInterfaces(*sm);
214 NIM::InstallInterfaces(*sm); 220 NIM::InstallInterfaces(*sm);
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 97273f967..96998643e 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -207,15 +207,27 @@ void GMainWindow::InitializeRecentFileMenuActions() {
207void GMainWindow::InitializeHotkeys() { 207void GMainWindow::InitializeHotkeys() {
208 RegisterHotkey("Main Window", "Load File", QKeySequence::Open); 208 RegisterHotkey("Main Window", "Load File", QKeySequence::Open);
209 RegisterHotkey("Main Window", "Start Emulation"); 209 RegisterHotkey("Main Window", "Start Emulation");
210 RegisterHotkey("Main Window", "Continue/Pause", QKeySequence(Qt::Key_F4));
210 RegisterHotkey("Main Window", "Fullscreen", QKeySequence::FullScreen); 211 RegisterHotkey("Main Window", "Fullscreen", QKeySequence::FullScreen);
211 RegisterHotkey("Main Window", "Exit Fullscreen", QKeySequence(Qt::Key_Escape), 212 RegisterHotkey("Main Window", "Exit Fullscreen", QKeySequence(Qt::Key_Escape),
212 Qt::ApplicationShortcut); 213 Qt::ApplicationShortcut);
214 RegisterHotkey("Main Window", "Toggle Speed Limit", QKeySequence("CTRL+Z"),
215 Qt::ApplicationShortcut);
213 LoadHotkeys(); 216 LoadHotkeys();
214 217
215 connect(GetHotkey("Main Window", "Load File", this), &QShortcut::activated, this, 218 connect(GetHotkey("Main Window", "Load File", this), &QShortcut::activated, this,
216 &GMainWindow::OnMenuLoadFile); 219 &GMainWindow::OnMenuLoadFile);
217 connect(GetHotkey("Main Window", "Start Emulation", this), &QShortcut::activated, this, 220 connect(GetHotkey("Main Window", "Start Emulation", this), &QShortcut::activated, this,
218 &GMainWindow::OnStartGame); 221 &GMainWindow::OnStartGame);
222 connect(GetHotkey("Main Window", "Continue/Pause", this), &QShortcut::activated, this, [&] {
223 if (emulation_running) {
224 if (emu_thread->IsRunning()) {
225 OnPauseGame();
226 } else {
227 OnStartGame();
228 }
229 }
230 });
219 connect(GetHotkey("Main Window", "Fullscreen", render_window), &QShortcut::activated, 231 connect(GetHotkey("Main Window", "Fullscreen", render_window), &QShortcut::activated,
220 ui.action_Fullscreen, &QAction::trigger); 232 ui.action_Fullscreen, &QAction::trigger);
221 connect(GetHotkey("Main Window", "Fullscreen", render_window), &QShortcut::activatedAmbiguously, 233 connect(GetHotkey("Main Window", "Fullscreen", render_window), &QShortcut::activatedAmbiguously,
@@ -226,6 +238,10 @@ void GMainWindow::InitializeHotkeys() {
226 ToggleFullscreen(); 238 ToggleFullscreen();
227 } 239 }
228 }); 240 });
241 connect(GetHotkey("Main Window", "Toggle Speed Limit", this), &QShortcut::activated, this, [&] {
242 Settings::values.toggle_framelimit = !Settings::values.toggle_framelimit;
243 UpdateStatusBar();
244 });
229} 245}
230 246
231void GMainWindow::SetDefaultUIGeometry() { 247void GMainWindow::SetDefaultUIGeometry() {