Compare commits

...

117 Commits

Author SHA1 Message Date
bunnei
b46df98e93 audio_core: Implement audren_u audio playback. 2018-08-04 21:54:30 -04:00
bunnei
1dee8ceda1 audio_core: Use s16 where possible for audio samples. 2018-08-04 18:22:58 -04:00
bunnei
f1cb3903ac audio_core: Port codec code from Citra for ADPCM decoding. 2018-08-04 18:22:58 -04:00
bunnei
02fccc0940 cubeb_sink: Support variable sample_rate and num_channels. 2018-08-04 15:30:10 -04:00
bunnei
34b3f83498 audio_core: Sinks need unique names as well. 2018-08-04 14:34:12 -04:00
bunnei
9f846d3aa4 audio_core: Streams need unique names for CoreTiming. 2018-08-04 14:34:12 -04:00
bunnei
2b06301dbf Merge pull request #849 from DarkLordZach/xci
XCI and Encrypted NCA Support
2018-08-04 14:33:11 -04:00
bunnei
13d6593753 Merge pull request #919 from lioncash/sign
gl_shader_manager: Amend sign differences in an assertion comparison in SetShaderUniformBlockBinding()
2018-08-04 14:29:59 -04:00
Lioncash
3b678b9e8e gl_shader_manager: Invert conditional in SetShaderUniformBlockBinding()
This lets us indent the majority of the code and places the error case
first.
2018-08-04 02:57:11 -04:00
Lioncash
dde5dce736 gl_shader_manager: Amend sign differences in an assertion comparison in SetShaderUniformBlockBinding()
Ensures both operands have the same sign in the comparison.

While we're at it, we can get rid of the redundant casting of ub_size to
an int. This type will always be trivial and alias a built-in type (not
doing so would break backwards compatibility at a standard level).
2018-08-04 02:55:03 -04:00
bunnei
762fcaf5de Merge pull request #911 from lioncash/prototype
video_core: Remove unimplemented Start() function prototype
2018-08-04 02:18:38 -04:00
bunnei
b0129489ea Merge pull request #913 from lioncash/unused-func
memory: Remove unused GetSpecialHandlers() function
2018-08-04 02:17:44 -04:00
bunnei
206f2e3436 Merge pull request #914 from lioncash/codeset
kernel/process: Use accessors instead of class members for referencing segment array
2018-08-04 02:17:25 -04:00
bunnei
d43dad001e Merge pull request #917 from lioncash/crash
kernel/thread: Fix potential crashes introduced in 26de4bb5
2018-08-04 01:19:01 -04:00
Lioncash
e93fa7f2cc kernel/thread: Fix potential crashes introduced in 26de4bb521
This amends cases where crashes can occur that were missed due to the
odd way the previous code was set up (using 3DS memory regions that
don't exist).
2018-08-03 23:49:10 -04:00
bunnei
29f31356d8 Merge pull request #910 from lioncash/unused
gl_shader_decompiler: Remove unused variable in GenerateDeclarations()
2018-08-03 15:54:11 -04:00
Lioncash
e649db8c6b kernel/process: Use std::array where applicable 2018-08-03 14:46:30 -04:00
Lioncash
2beda7c2b3 kernel/process: Use accessors instead of class members for referencing segment array
Using member variables for referencing the segments array increases the
size of the class in memory for little benefit. The same behavior can be
achieved through the use of accessors that just return the relevant
segment.
2018-08-03 14:45:45 -04:00
Lioncash
59b04c0df6 memory: Remove unused GetSpecialHandlers() function
This is just unused code, so we may as well get rid of it.
2018-08-03 14:20:50 -04:00
bunnei
40e63ede6d Merge pull request #908 from lioncash/memory
core/memory: Get rid of 3DS leftovers
2018-08-03 14:07:49 -04:00
bunnei
64806a8397 Merge pull request #909 from lioncash/const
gl_shader_manager: Make ProgramManager's GetCurrentProgramStage() a const member function
2018-08-03 14:07:30 -04:00
Lioncash
b4e050e6c4 video_core: Remove unimplemented Start() function prototype
Given this has no definition, we can just remove it entirely.
2018-08-03 12:48:14 -04:00
Lioncash
b45e5c2399 gl_shader_decompiler: Remove unused variable in GenerateDeclarations()
This variable was being incremented, but we were never actually using
it.
2018-08-03 12:18:31 -04:00
Lioncash
555d76d065 gl_shader_manager: Make ProgramManager's GetCurrentProgramStage() a const member function
This function doesn't modify class state, so it can be made const.
2018-08-03 12:08:17 -04:00
Lioncash
26de4bb521 core/memory: Get rid of 3DS leftovers
Removes leftover code from citra that isn't needed.
2018-08-03 11:22:47 -04:00
David
c1d54f4aea Added ability to change username & language code in the settings ui. Added IProfile::Get and SET::GetLanguageCode for libnx tests (#851) 2018-08-03 11:02:55 -04:00
bunnei
b6d61abd78 Merge pull request #895 from lioncash/sink
sink_details: std::move std::function instances
2018-08-03 11:00:56 -04:00
bunnei
40e78b9a89 Merge pull request #898 from lioncash/mig
service: Add migration services
2018-08-03 11:00:27 -04:00
bunnei
ef9433411d Merge pull request #900 from lioncash/init
math_util: Always initialize members of Rectangle
2018-08-03 11:00:10 -04:00
bunnei
00ba704a7f Merge pull request #892 from lioncash/global
video_core: Make global EmuWindow instance part of the base renderer …
2018-08-03 00:31:32 -04:00
bunnei
4c3c608d59 Merge pull request #894 from lioncash/object
kernel: Move object class to its own source files
2018-08-03 00:28:43 -04:00
bunnei
4b84d5bcec Merge pull request #904 from lioncash/static
kernel/thread: Minor changes
2018-08-03 00:19:29 -04:00
bunnei
9e48ca23b2 Merge pull request #906 from lioncash/override
input_common: minor changes
2018-08-03 00:18:52 -04:00
bunnei
49d817134a Merge pull request #907 from lioncash/slot
yuzu: Use Qt 5 signal/slots where applicable
2018-08-03 00:17:55 -04:00
bunnei
61ed68f3d0 Merge pull request #905 from lioncash/vma
kernel/vm_manager: Minor changes
2018-08-02 23:18:03 -04:00
bunnei
291ccf7257 Merge pull request #903 from lioncash/copy
vfs_vector: Minor changes
2018-08-02 23:01:19 -04:00
bunnei
52da0ce399 Merge pull request #901 from lioncash/ref
gl_shader_manager: Take ShaderSetup instances by const reference in UseProgrammableVertexShader() and UseProgrammableFragmentShader()
2018-08-02 23:00:56 -04:00
Lioncash
db340f6402 yuzu: Use Qt 5 signal/slots where applicable
Makes the signal/slot connections type-safe instead of string-based.
2018-08-02 22:18:33 -04:00
Lioncash
684fc2c320 input_common: Use std::move where applicable
Avoids unnecessary atomic reference count increments and decrements
2018-08-02 21:51:11 -04:00
Lioncash
29b6afb82f input_common: Add missing override specifiers 2018-08-02 21:44:25 -04:00
bunnei
4de18e054b Merge pull request #899 from lioncash/unused
hw: Remove unused files
2018-08-02 14:58:01 -04:00
bunnei
bae1822aed Merge pull request #902 from lioncash/array
gl_state: Make texture_units a std::array
2018-08-02 14:57:42 -04:00
bunnei
e79e967151 Merge pull request #891 from lioncash/ns
service/ns: Add missing ns services
2018-08-02 14:57:24 -04:00
greggameplayer
fe64e1d38e Implement RGB32F PixelFormat (#886) (used by Go Vacation) 2018-08-02 14:56:38 -04:00
bunnei
d0bd01146e Merge pull request #893 from lioncash/psc
service: Add the psc services
2018-08-02 14:53:55 -04:00
Lioncash
d94a173877 kernel/vm_manager: Convert loop into std::any_of() 2018-08-02 12:46:15 -04:00
Lioncash
c4e0c3d76c kernel/vm_manager: Use const where applicable
Makes our immutable state explicit.
2018-08-02 12:21:46 -04:00
Lioncash
ce5ad45278 kernel/vm_manager: Use the VAddr type alias in CarveVMA()
These two variables correspond to address ranges.
2018-08-02 12:15:50 -04:00
Lioncash
6058c84b79 kernel/thread: Make GetFreeThreadLocalSlot()'s loop indices size_t
Avoids using a u32 to compare against a range of size_t, which can be a
source of warnings. While we're at it, compress a std::tie into a
structured binding.
2018-08-02 12:01:25 -04:00
Lioncash
fac0e42b2f kernel/thread: Make GetFreeThreadLocalSlot() reference parameter a const reference
This function only reads the data being referenced, it doesn't modify
it, so we can turn the reference into a const reference.
2018-08-02 11:56:11 -04:00
Lioncash
9a50a4f2cc kernel/thread: Make GetFreeThreadLocalSlot() internally linked
This function isn't used outside of this translation unit, so we can
make it internally linked.
2018-08-02 11:54:23 -04:00
Lioncash
42a4c6b79e vfs_vector: Remove unused variable in FindAndRemoveVectorElement()
This wasn't being used for anything, so it can be removed.
2018-08-02 11:40:24 -04:00
Lioncash
cec9e9b811 vfs_vector: Avoid unnecessary copies where applicable
The lambda elements should be taken by const reference here, and we can
move the virtual directory passed to ReplaceFileWithSubdirectory()
2018-08-02 11:37:39 -04:00
Lioncash
6b32e24161 gl_state: Make texture_units a std::array
Gets rid of the use of a raw C array.
2018-08-02 11:19:58 -04:00
Lioncash
d92e8ab062 gl_shader_manager: Take ShaderSetup instances by const reference in UseProgrammableVertexShader() and UseProgrammableFragmentShader()
Avoids performing unnecessary copies of 65560 byte sized ShaderSetup
instances, considering it's only used as part of lookup and not
modified.

Given the parameters were already const, it's likely taking these
parameters by reference was intended but the ampersand was forgotten.
2018-08-02 11:09:46 -04:00
Lioncash
f2a03468b1 math_util: Always initialize members of Rectangle
Prevents potentially using the members uninitialized.
2018-08-02 10:47:34 -04:00
Lioncash
c6db1c390b hw: Remove unused files
None of these files are used in any meaningful way. They're just
leftovers from citra. Also has the benefit of getting rid of an unused
global variable.
2018-08-02 10:23:10 -04:00
Lioncash
7469e26e5e service: Add migration services
Adds the basic skeleton for the mig:usr service based off information
provided by Switch Brew.
2018-08-02 10:09:45 -04:00
bunnei
a03c644aed Merge pull request #896 from lioncash/audio-out
audio_out: Use Buffer::Tag alias in GetTagsAndReleaseBuffers()'s prototype
2018-08-02 09:51:47 -04:00
Lioncash
c1c397d37c audio_out: Use Buffer::Tag alias in GetTagsAndReleaseBuffers()'s prototype
This makes the Buffer::Tag usage consistent with the Stream class's
prototype of GetTagsAndReleaseBuffers().
2018-08-02 05:18:32 -04:00
Lioncash
2bc4ab3958 sink_details: Deduplicate long std::function repetition
We can just use type aliases to avoid needing to write the same long
type twice
2018-08-01 23:56:02 -04:00
Lioncash
89ebef6571 sink_details: std::move std::function instances
Given std::function is allowed to potentially allocate, these should be
std::move'd to prevent potential reallocation (should that ever happen).
2018-08-01 23:50:48 -04:00
Lioncash
bf45092c61 kernel: Move object class to its own source files
General moving to keep kernel object types separate from the direct
kernel code. Also essentially a preliminary cleanup before eliminating
global kernel state in the kernel code.
2018-08-01 23:34:42 -04:00
Lioncash
42c5171322 logging/log: Remove incorrect description in PCV doc comment
PCV isn't the parental control service.
2018-08-01 23:31:31 -04:00
Lioncash
5233040ab4 service: Add psc services
Adds the basic skeleton for the psc services based off the information
provided by Switch Brew.
2018-08-01 23:31:27 -04:00
Lioncash
0f2ac928f2 video_core: Make global EmuWindow instance part of the base renderer class
Makes the global a member of the RendererBase class. We also change this
to be a reference. Passing any form of null pointer to these functions
is incorrect entirely, especially given the code itself assumes that the
pointer would always be in a valid state.

This also makes it easier to follow the lifecycle of instances being
used, as we explicitly interact the renderer with the rasterizer, rather
than it just operating on a global pointer.
2018-08-01 21:40:30 -04:00
bunnei
746d7d4d28 Merge pull request #888 from lioncash/caps
service: Add capture services
2018-08-01 21:34:28 -04:00
bunnei
9bb8720289 Merge pull request #890 from lioncash/logger
lm: Amend name of ILogger
2018-08-01 21:33:11 -04:00
bunnei
16b2fd9fc8 Merge pull request #889 from lioncash/fsp
service/filesystem: Add fsp:ldr and fsp:pr services
2018-08-01 21:32:54 -04:00
bunnei
200c95db8a Merge pull request #887 from lioncash/pcv
service: Add bpc and pcv services
2018-08-01 21:32:36 -04:00
Lioncash
48acb764a4 service/ns: Add missing ns services
Implements the basic skeleton of ns:am2, ns:ec, ns:rid, ns:rt, ns:su,
ns:vm, and ns:web based off the information provided by Switch Brew and
SwIPC.
2018-08-01 18:02:18 -04:00
Lioncash
f77cfab516 lm: Amend name of ILogger
Previously this was being registered with the name "Logger". While we're
at it, also change the name of the class to match it.
2018-08-01 17:08:44 -04:00
Lioncash
208a457909 service/filesystem: Add fsp:ldr and fsp:pr services
Adds the basic skeleton for the remaining fsp services based off
information provided by Switch Brew.
2018-08-01 17:01:29 -04:00
Lioncash
e39294c267 service: Add capture services
Adds the basic skeleton for the capture services based off information
provided by Switch Brew.
2018-08-01 16:45:51 -04:00
Lioncash
d109279543 service: Add bpc and pcv services
Adds the basic skeleton for the remaining pcv-related services based off
information on Switch Brew.
2018-08-01 16:13:04 -04:00
bunnei
99a1d7440d Merge pull request #885 from greggameplayer/R32-Float
Implement R32_FLOAT RenderTargetFormat
2018-08-01 11:51:59 -04:00
Unknown
0d8fcab136 Implement R32_FLOAT RenderTargetFormat 2018-08-01 15:31:42 +02:00
bunnei
703663d761 Merge pull request #882 from lioncash/unused
kernel/thread: Remove unimplemented function prototype
2018-07-31 22:25:49 -07:00
Zach Hilman
13cdf1f159 Add missing parameter to files.push_back() 2018-08-01 00:16:54 -04:00
Zach Hilman
0497bb5528 Fix merge conflicts with opus and update docs 2018-08-01 00:16:54 -04:00
Zach Hilman
187d8e215f Use more descriptive error codes and messages 2018-08-01 00:16:54 -04:00
Zach Hilman
9d59b96ef9 Use static const instead of const static 2018-08-01 00:16:54 -04:00
Zach Hilman
a9c921a41d Use ErrorEncrypted where applicable and fix no keys crash 2018-08-01 00:16:54 -04:00
Zach Hilman
03149d3e4a Add missing includes and use const where applicable 2018-08-01 00:16:54 -04:00
Zach Hilman
150527ec19 Allow key loading from %YUZU_DIR%/keys in addition to ~/.switch 2018-08-01 00:16:54 -04:00
Zach Hilman
cc8234fa89 Use SHGetKnownFolderPath instead of SHGetFolderPathA 2018-08-01 00:16:54 -04:00
Zach Hilman
239a3113e4 Make XCI comply to review and style guidelines 2018-08-01 00:16:54 -04:00
Zach Hilman
22342487e8 Extract mbedtls to cpp file 2018-08-01 00:16:54 -04:00
Zach Hilman
83c3ae8be8 Add missing string.h include 2018-08-01 00:16:54 -04:00
Zach Hilman
c54a10cb4f Update mbedtls and fix compile error 2018-08-01 00:16:54 -04:00
Zach Hilman
df5b75694f Remove files that are not used 2018-08-01 00:16:54 -04:00
bunnei
d2ad279a32 Merge pull request #871 from bunnei/audio-config
audio_core: Add configuration settings.
2018-07-31 21:04:26 -07:00
Lioncash
49e198b20d kernel/thread: Remove unimplemented function prototype
Given there's no implementation, we may as well remove the code
entirely.
2018-07-31 23:21:38 -04:00
bunnei
ff2c1b0a94 Merge pull request #877 from lioncash/remove
kernel: Remove unused object_address_table.cpp/.h
2018-07-31 20:11:39 -07:00
bunnei
98af269415 Merge pull request #880 from lioncash/audio
service/audio: Add missing services
2018-07-31 20:11:04 -07:00
bunnei
ca84b530a3 audio_core: Add configuration settings. 2018-07-31 22:38:42 -04:00
bunnei
f5efac3442 Merge pull request #876 from lioncash/include
kernel: Remove unnecessary includes
2018-07-31 19:16:05 -07:00
Lioncash
bba63b33a1 service/audio: Add missing services
Adds the missing audctl service, as well as the :a and :d services for
audin, audout, audrec, and audren.
2018-07-31 21:58:30 -04:00
bunnei
da07faebfe Merge pull request #879 from lioncash/audio
audout_u: Remove std::move in OpenAudioOutImpl()
2018-07-31 16:49:56 -07:00
bunnei
0a2b219a31 Merge pull request #864 from FearlessTobi/port-3973
Port #3973 from Citra: "Remove polymorphism issue"
2018-07-31 09:34:23 -07:00
bunnei
3575c076cb Merge pull request #869 from Subv/ubsan
Corrected a few error cases detected by asan/ubsan
2018-07-31 09:24:13 -07:00
bunnei
fd020ad52a Merge pull request #875 from lioncash/fgm
service: Add fgm services
2018-07-31 09:23:17 -07:00
bunnei
3a2581cc7d Merge pull request #874 from lioncash/am
service/am: Add missing am services
2018-07-31 09:22:56 -07:00
Lioncash
1ced7bbea5 audout_u: Remove std::move in OpenAudioOutImpl()
Previously the code was using the values from params further below after
it was std::moved. Thankfully, given AudoutParams is a trivially
copyable struct, the values would have simply been copied in this
instance and not invalidated to garbage values.
2018-07-31 10:24:38 -04:00
Lioncash
a2304fad16 kernel: Remove unnecessary includes
Removes unnecessary direct dependencies in some headers and also gets
rid of indirect dependencies that were being relied on to be included.
2018-07-31 10:15:17 -04:00
bunnei
b79362b9da Merge pull request #870 from lioncash/init
arm_dynarmic: Correct initializer list order
2018-07-31 07:12:54 -07:00
bunnei
86491da0d6 Merge pull request #872 from lioncash/pcie
service: Add the pcie service
2018-07-31 07:12:08 -07:00
Lioncash
7da8f15461 service/am: Add missing am services
Adds the basic skeleton for missing am services idle:sys, omm, and spsm
based off the information provided by Switch Brew.
2018-07-31 08:02:20 -04:00
Lioncash
268eeeb406 service: Add fgm services
Adds the basic skeleton for the fgm services based off the information
provided by Switch Brew.
2018-07-31 08:01:19 -04:00
Lioncash
f08c0520a4 arm_dynarmic: Make SetTlsAddress() prototype and definition consistent
Makes the definition use the same type aliases as in its prototype.
2018-07-31 07:58:26 -04:00
Lioncash
9d6aa7bff7 arm_dynarmic: Remove unnecessary qualifying of ThreadContext
Given the ARM_Dynarmic class inherits from ARM_Interface, we don't need
to qualify here.
2018-07-31 07:56:59 -04:00
Lioncash
444a01afa6 arm_dynarmic: Correct initializer list order
Amends the initializer list to be in the same order that each variable
would be initialized in. We also do this to ensure we don't use a bogus
uninitialized instance of the exclusive monitor within MakeJit()

We can also remove the jit member from the initializer list as this is
initialized by PageTableChanged()
2018-07-31 07:54:58 -04:00
Lioncash
e373027a73 service: Add the pcie service
Adds the basic skeleton of the pcie service based off information on
Switch Brew.
2018-07-31 06:40:21 -04:00
Subv
8191273a3d MacroInterpreter: Avoid left shifting negative values.
The branch target is signed, so multiply by 4 instead of left shifting by 2
2018-07-30 20:38:24 -05:00
Subv
e119e17d18 nvhost_gpu: Added checks to ensure we don't read past the end of the entries when handling a GPU command list. 2018-07-30 20:09:13 -05:00
Subv
2482aca7c3 nvhost_ctrl_gpu: Only read the input parameters if they are actually there.
Passing nullptr to memcpy is undefined behavior.
2018-07-30 20:08:36 -05:00
B3n30
6e5f83ee24 remove polymorphism issue 2018-07-29 15:24:41 +02:00
212 changed files with 4712 additions and 1712 deletions

7
.gitmodules vendored
View File

@@ -25,6 +25,9 @@
[submodule "unicorn"]
path = externals/unicorn
url = https://github.com/yuzu-emu/unicorn
[submodule "mbedtls"]
path = externals/mbedtls
url = https://github.com/DarkLordZach/mbedtls
[submodule "opus"]
path = externals/opus
url = https://github.com/ogniK5377/opus.git
path = externals/opus
url = https://github.com/ogniK5377/opus.git

View File

@@ -35,6 +35,10 @@ set(LZ4_BUNDLED_MODE ON)
add_subdirectory(lz4/contrib/cmake_unofficial)
target_include_directories(lz4_static INTERFACE ./lz4/lib)
# mbedtls
add_subdirectory(mbedtls)
target_include_directories(mbedtls PUBLIC ./mbedtls/include)
# MicroProfile
add_library(microprofile INTERFACE)
target_include_directories(microprofile INTERFACE ./microprofile)

1
externals/mbedtls vendored Submodule

Submodule externals/mbedtls added at 06442b840e

View File

@@ -1,9 +1,13 @@
add_library(audio_core STATIC
audio_out.cpp
audio_out.h
audio_renderer.cpp
audio_renderer.h
buffer.h
cubeb_sink.cpp
cubeb_sink.h
codec.cpp
codec.h
null_sink.h
stream.cpp
stream.h

View File

@@ -7,6 +7,7 @@
#include "audio_core/sink_details.h"
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/settings.h"
namespace AudioCore {
@@ -26,19 +27,19 @@ static Stream::Format ChannelsToStreamFormat(u32 num_channels) {
return {};
}
StreamPtr AudioOut::OpenStream(u32 sample_rate, u32 num_channels,
StreamPtr AudioOut::OpenStream(u32 sample_rate, u32 num_channels, std::string&& name,
Stream::ReleaseCallback&& release_callback) {
if (!sink) {
const SinkDetails& sink_details = GetSinkDetails("auto");
sink = sink_details.factory("");
const SinkDetails& sink_details = GetSinkDetails(Settings::values.sink_id);
sink = sink_details.factory(Settings::values.audio_device_id);
}
return std::make_shared<Stream>(sample_rate, ChannelsToStreamFormat(num_channels),
std::move(release_callback),
sink->AcquireSinkStream(sample_rate, num_channels));
return std::make_shared<Stream>(
sample_rate, ChannelsToStreamFormat(num_channels), std::move(release_callback),
sink->AcquireSinkStream(sample_rate, num_channels, name), std::move(name));
}
std::vector<u64> AudioOut::GetTagsAndReleaseBuffers(StreamPtr stream, size_t max_count) {
std::vector<Buffer::Tag> AudioOut::GetTagsAndReleaseBuffers(StreamPtr stream, size_t max_count) {
return stream->GetTagsAndReleaseBuffers(max_count);
}
@@ -50,7 +51,7 @@ void AudioOut::StopStream(StreamPtr stream) {
stream->Stop();
}
bool AudioOut::QueueBuffer(StreamPtr stream, Buffer::Tag tag, std::vector<u8>&& data) {
bool AudioOut::QueueBuffer(StreamPtr stream, Buffer::Tag tag, std::vector<s16>&& data) {
return stream->QueueBuffer(std::make_shared<Buffer>(tag, std::move(data)));
}

View File

@@ -5,6 +5,7 @@
#pragma once
#include <memory>
#include <string>
#include <vector>
#include "audio_core/buffer.h"
@@ -20,11 +21,11 @@ namespace AudioCore {
class AudioOut {
public:
/// Opens a new audio stream
StreamPtr OpenStream(u32 sample_rate, u32 num_channels,
StreamPtr OpenStream(u32 sample_rate, u32 num_channels, std::string&& name,
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);
std::vector<Buffer::Tag> GetTagsAndReleaseBuffers(StreamPtr stream, size_t max_count);
/// Starts an audio stream for playback
void StartStream(StreamPtr stream);
@@ -33,7 +34,7 @@ public:
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);
bool QueueBuffer(StreamPtr stream, Buffer::Tag tag, std::vector<s16>&& data);
private:
SinkPtr sink;

View File

@@ -0,0 +1,234 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "audio_core/audio_renderer.h"
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/memory.h"
namespace AudioCore {
constexpr u32 STREAM_SAMPLE_RATE{48000};
constexpr u32 STREAM_NUM_CHANNELS{2};
AudioRenderer::AudioRenderer(AudioRendererParameter params,
Kernel::SharedPtr<Kernel::Event> buffer_event)
: worker_params{params}, buffer_event{buffer_event}, voices(params.voice_count) {
audio_core = std::make_unique<AudioCore::AudioOut>();
stream = audio_core->OpenStream(STREAM_SAMPLE_RATE, STREAM_NUM_CHANNELS, "AudioRenderer",
[=]() { buffer_event->Signal(); });
audio_core->StartStream(stream);
QueueMixedBuffer(0);
QueueMixedBuffer(1);
QueueMixedBuffer(2);
}
std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_params) {
// Copy UpdateDataHeader struct
UpdateDataHeader config{};
std::memcpy(&config, input_params.data(), sizeof(UpdateDataHeader));
u32 memory_pool_count = worker_params.effect_count + (worker_params.voice_count * 4);
// Copy MemoryPoolInfo structs
std::vector<MemoryPoolInfo> mem_pool_info(memory_pool_count);
std::memcpy(mem_pool_info.data(),
input_params.data() + sizeof(UpdateDataHeader) + config.behavior_size,
memory_pool_count * sizeof(MemoryPoolInfo));
// Copy VoiceInfo structs
size_t offset{sizeof(UpdateDataHeader) + config.behavior_size + config.memory_pools_size +
config.voice_resource_size};
for (auto& voice : voices) {
std::memcpy(&voice.Info(), input_params.data() + offset, sizeof(VoiceInfo));
offset += sizeof(VoiceInfo);
}
// Update voices
for (auto& voice : voices) {
voice.UpdateState();
if (!voice.GetInfo().is_in_use) {
continue;
}
if (voice.GetInfo().is_new) {
voice.SetWaveIndex(voice.GetInfo().wave_buffer_head);
}
}
// Update memory pool state
std::vector<MemoryPoolEntry> memory_pool(memory_pool_count);
for (size_t index = 0; index < memory_pool.size(); ++index) {
if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestAttach) {
memory_pool[index].state = MemoryPoolStates::Attached;
} else if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestDetach) {
memory_pool[index].state = MemoryPoolStates::Detached;
}
}
// Release previous buffers and queue next ones for playback
ReleaseAndQueueBuffers();
// Copy output header
UpdateDataHeader response_data{worker_params};
std::vector<u8> output_params(response_data.total_size);
std::memcpy(output_params.data(), &response_data, sizeof(UpdateDataHeader));
// Copy output memory pool entries
std::memcpy(output_params.data() + sizeof(UpdateDataHeader), memory_pool.data(),
response_data.memory_pools_size);
// Copy output voice status
size_t voice_out_status_offset{sizeof(UpdateDataHeader) + response_data.memory_pools_size};
for (const auto& voice : voices) {
std::memcpy(output_params.data() + voice_out_status_offset, &voice.GetOutStatus(),
sizeof(VoiceOutStatus));
voice_out_status_offset += sizeof(VoiceOutStatus);
}
return output_params;
}
void AudioRenderer::VoiceState::SetWaveIndex(size_t index) {
wave_index = index & 3;
is_refresh_pending = true;
}
std::vector<s16> AudioRenderer::VoiceState::DequeueSamples(size_t sample_count) {
if (!IsPlaying()) {
return {};
}
if (is_refresh_pending) {
RefreshBuffer();
}
const size_t max_size{samples.size() - offset};
const size_t dequeue_offset{offset};
size_t size{sample_count * STREAM_NUM_CHANNELS};
if (size > max_size) {
size = max_size;
}
out_status.played_sample_count += size / STREAM_NUM_CHANNELS;
offset += size;
const auto& wave_buffer{info.wave_buffer[wave_index]};
if (offset == samples.size()) {
offset = 0;
if (!wave_buffer.is_looping) {
SetWaveIndex(wave_index + 1);
}
out_status.wave_buffer_consumed++;
if (wave_buffer.end_of_stream) {
info.play_state = PlayState::Paused;
}
}
return {samples.begin() + dequeue_offset, samples.begin() + dequeue_offset + size};
}
void AudioRenderer::VoiceState::UpdateState() {
if (is_in_use && !info.is_in_use) {
// No longer in use, reset state
is_refresh_pending = true;
wave_index = 0;
offset = 0;
out_status = {};
}
is_in_use = info.is_in_use;
}
void AudioRenderer::VoiceState::RefreshBuffer() {
std::vector<s16> new_samples(info.wave_buffer[wave_index].buffer_sz / sizeof(s16));
Memory::ReadBlock(info.wave_buffer[wave_index].buffer_addr, new_samples.data(),
info.wave_buffer[wave_index].buffer_sz);
switch (static_cast<Codec::PcmFormat>(info.sample_format)) {
case Codec::PcmFormat::Int16: {
// PCM16 is played as-is
break;
}
case Codec::PcmFormat::Adpcm: {
// Decode ADPCM to PCM16
Codec::ADPCM_Coeff coeffs;
Memory::ReadBlock(info.additional_params_addr, coeffs.data(), sizeof(Codec::ADPCM_Coeff));
new_samples = Codec::DecodeADPCM(reinterpret_cast<u8*>(new_samples.data()),
new_samples.size() * sizeof(s16), coeffs, adpcm_state);
break;
}
default:
LOG_CRITICAL(Audio, "Unimplemented sample_format={}", info.sample_format);
UNREACHABLE();
break;
}
switch (info.channel_count) {
case 1:
// 1 channel is upsampled to 2 channel
samples.resize(new_samples.size() * 2);
for (size_t index = 0; index < new_samples.size(); ++index) {
samples[index * 2] = new_samples[index];
samples[index * 2 + 1] = new_samples[index];
}
break;
case 2: {
// 2 channel is played as is
samples = std::move(new_samples);
break;
}
default:
LOG_CRITICAL(Audio, "Unimplemented channel_count={}", info.channel_count);
UNREACHABLE();
break;
}
is_refresh_pending = false;
}
static constexpr s16 ClampToS16(s32 value) {
return static_cast<s16>(std::clamp(value, -32768, 32767));
}
void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) {
constexpr size_t BUFFER_SIZE{512};
std::vector<s16> buffer(BUFFER_SIZE * stream->GetNumChannels());
for (auto& voice : voices) {
if (!voice.IsPlaying()) {
continue;
}
size_t offset{};
s64 samples_remaining{BUFFER_SIZE};
while (samples_remaining > 0) {
const std::vector<s16> samples{voice.DequeueSamples(samples_remaining)};
if (samples.empty()) {
break;
}
samples_remaining -= samples.size();
for (const auto& sample : samples) {
const s32 buffer_sample{buffer[offset]};
buffer[offset++] =
ClampToS16(buffer_sample + static_cast<s32>(sample * voice.GetInfo().volume));
}
}
}
audio_core->QueueBuffer(stream, tag, std::move(buffer));
}
void AudioRenderer::ReleaseAndQueueBuffers() {
const auto released_buffers{audio_core->GetTagsAndReleaseBuffers(stream, 2)};
for (const auto& tag : released_buffers) {
QueueMixedBuffer(tag);
}
}
} // namespace AudioCore

View File

@@ -0,0 +1,206 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include <memory>
#include <vector>
#include "audio_core/audio_out.h"
#include "audio_core/codec.h"
#include "audio_core/stream.h"
#include "common/common_types.h"
#include "common/swap.h"
#include "core/hle/kernel/event.h"
namespace AudioCore {
enum class PlayState : u8 {
Started = 0,
Stopped = 1,
Paused = 2,
};
struct AudioRendererParameter {
u32_le sample_rate;
u32_le sample_count;
u32_le unknown_8;
u32_le unknown_c;
u32_le voice_count;
u32_le sink_count;
u32_le effect_count;
u32_le unknown_1c;
u8 unknown_20;
INSERT_PADDING_BYTES(3);
u32_le splitter_count;
u32_le unknown_2c;
INSERT_PADDING_WORDS(1);
u32_le revision;
};
static_assert(sizeof(AudioRendererParameter) == 52, "AudioRendererParameter is an invalid size");
enum class MemoryPoolStates : u32 { // Should be LE
Invalid = 0x0,
Unknown = 0x1,
RequestDetach = 0x2,
Detached = 0x3,
RequestAttach = 0x4,
Attached = 0x5,
Released = 0x6,
};
struct MemoryPoolEntry {
MemoryPoolStates state;
u32_le unknown_4;
u32_le unknown_8;
u32_le unknown_c;
};
static_assert(sizeof(MemoryPoolEntry) == 0x10, "MemoryPoolEntry has wrong size");
struct MemoryPoolInfo {
u64_le pool_address;
u64_le pool_size;
MemoryPoolStates pool_state;
INSERT_PADDING_WORDS(3); // Unknown
};
static_assert(sizeof(MemoryPoolInfo) == 0x20, "MemoryPoolInfo has wrong size");
struct BiquadFilter {
u8 enable;
INSERT_PADDING_BYTES(1);
std::array<s16_le, 3> numerator;
std::array<s16_le, 2> denominator;
};
static_assert(sizeof(BiquadFilter) == 0xc, "BiquadFilter has wrong size");
struct WaveBuffer {
u64_le buffer_addr;
u64_le buffer_sz;
s32_le start_sample_offset;
s32_le end_sample_offset;
u8 is_looping;
u8 end_of_stream;
u8 sent_to_server;
INSERT_PADDING_BYTES(5);
u64 context_addr;
u64 context_sz;
INSERT_PADDING_BYTES(8);
};
static_assert(sizeof(WaveBuffer) == 0x38, "WaveBuffer has wrong size");
struct VoiceInfo {
u32_le id;
u32_le node_id;
u8 is_new;
u8 is_in_use;
PlayState play_state;
u8 sample_format;
u32_le sample_rate;
u32_le priority;
u32_le sorting_order;
u32_le channel_count;
float_le pitch;
float_le volume;
std::array<BiquadFilter, 2> biquad_filter;
u32_le wave_buffer_count;
u32_le wave_buffer_head;
INSERT_PADDING_WORDS(1);
u64_le additional_params_addr;
u64_le additional_params_sz;
u32_le mix_id;
u32_le splitter_info_id;
std::array<WaveBuffer, 4> wave_buffer;
std::array<u32_le, 6> voice_channel_resource_ids;
INSERT_PADDING_BYTES(24);
};
static_assert(sizeof(VoiceInfo) == 0x170, "VoiceInfo is wrong size");
struct VoiceOutStatus {
u64_le played_sample_count;
u32_le wave_buffer_consumed;
u32_le voice_drops_count;
};
static_assert(sizeof(VoiceOutStatus) == 0x10, "VoiceOutStatus has wrong size");
struct UpdateDataHeader {
UpdateDataHeader() {}
explicit UpdateDataHeader(const AudioRendererParameter& config) {
revision = Common::MakeMagic('R', 'E', 'V', '4'); // 5.1.0 Revision
behavior_size = 0xb0;
memory_pools_size = (config.effect_count + (config.voice_count * 4)) * 0x10;
voices_size = config.voice_count * 0x10;
voice_resource_size = 0x0;
effects_size = config.effect_count * 0x10;
mixes_size = 0x0;
sinks_size = config.sink_count * 0x20;
performance_manager_size = 0x10;
total_size = sizeof(UpdateDataHeader) + behavior_size + memory_pools_size + voices_size +
effects_size + sinks_size + performance_manager_size;
}
u32_le revision;
u32_le behavior_size;
u32_le memory_pools_size;
u32_le voices_size;
u32_le voice_resource_size;
u32_le effects_size;
u32_le mixes_size;
u32_le sinks_size;
u32_le performance_manager_size;
INSERT_PADDING_WORDS(6);
u32_le total_size;
};
static_assert(sizeof(UpdateDataHeader) == 0x40, "UpdateDataHeader has wrong size");
class AudioRenderer {
public:
AudioRenderer(AudioRendererParameter params, Kernel::SharedPtr<Kernel::Event> buffer_event);
std::vector<u8> UpdateAudioRenderer(const std::vector<u8>& input_params);
void QueueMixedBuffer(Buffer::Tag tag);
void ReleaseAndQueueBuffers();
private:
class VoiceState {
public:
bool IsPlaying() const {
return is_in_use && info.play_state == PlayState::Started;
}
const VoiceOutStatus& GetOutStatus() const {
return out_status;
}
const VoiceInfo& GetInfo() const {
return info;
}
VoiceInfo& Info() {
return info;
}
void SetWaveIndex(size_t index);
std::vector<s16> DequeueSamples(size_t sample_count);
void UpdateState();
void RefreshBuffer();
private:
bool is_in_use{};
bool is_refresh_pending{};
size_t wave_index{};
size_t offset{};
Codec::ADPCMState adpcm_state{};
std::vector<s16> samples;
VoiceOutStatus out_status{};
VoiceInfo info{};
};
AudioRendererParameter worker_params;
Kernel::SharedPtr<Kernel::Event> buffer_event;
std::vector<VoiceState> voices;
std::unique_ptr<AudioCore::AudioOut> audio_core;
AudioCore::StreamPtr stream;
};
} // namespace AudioCore

View File

@@ -18,11 +18,16 @@ class Buffer {
public:
using Tag = u64;
Buffer(Tag tag, std::vector<u8>&& data) : tag{tag}, data{std::move(data)} {}
Buffer(Tag tag, std::vector<s16>&& samples) : tag{tag}, samples{std::move(samples)} {}
/// Returns the raw audio data for the buffer
const std::vector<u8>& GetData() const {
return data;
std::vector<s16>& Samples() {
return samples;
}
/// Returns the raw audio data for the buffer
const std::vector<s16>& GetSamples() const {
return samples;
}
/// Returns the buffer tag, this is provided by the game to the audout service
@@ -32,7 +37,7 @@ public:
private:
Tag tag;
std::vector<u8> data;
std::vector<s16> samples;
};
using BufferPtr = std::shared_ptr<Buffer>;

77
src/audio_core/codec.cpp Normal file
View File

@@ -0,0 +1,77 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include "audio_core/codec.h"
namespace AudioCore::Codec {
std::vector<s16> DecodeADPCM(const u8* const data, size_t size, const ADPCM_Coeff& coeff,
ADPCMState& state) {
// GC-ADPCM with scale factor and variable coefficients.
// Frames are 8 bytes long containing 14 samples each.
// Samples are 4 bits (one nibble) long.
constexpr size_t FRAME_LEN = 8;
constexpr size_t SAMPLES_PER_FRAME = 14;
constexpr std::array<int, 16> SIGNED_NIBBLES = {
{0, 1, 2, 3, 4, 5, 6, 7, -8, -7, -6, -5, -4, -3, -2, -1}};
const size_t sample_count = (size / FRAME_LEN) * SAMPLES_PER_FRAME;
const size_t ret_size =
sample_count % 2 == 0 ? sample_count : sample_count + 1; // Ensure multiple of two.
std::vector<s16> ret(ret_size);
int yn1 = state.yn1, yn2 = state.yn2;
const size_t NUM_FRAMES =
(sample_count + (SAMPLES_PER_FRAME - 1)) / SAMPLES_PER_FRAME; // Round up.
for (size_t framei = 0; framei < NUM_FRAMES; framei++) {
const int frame_header = data[framei * FRAME_LEN];
const int scale = 1 << (frame_header & 0xF);
const int idx = (frame_header >> 4) & 0x7;
// Coefficients are fixed point with 11 bits fractional part.
const int coef1 = coeff[idx * 2 + 0];
const int coef2 = coeff[idx * 2 + 1];
// Decodes an audio sample. One nibble produces one sample.
const auto decode_sample = [&](const int nibble) -> s16 {
const int xn = nibble * scale;
// We first transform everything into 11 bit fixed point, perform the second order
// digital filter, then transform back.
// 0x400 == 0.5 in 11 bit fixed point.
// Filter: y[n] = x[n] + 0.5 + c1 * y[n-1] + c2 * y[n-2]
int val = ((xn << 11) + 0x400 + coef1 * yn1 + coef2 * yn2) >> 11;
// Clamp to output range.
val = std::clamp<s32>(val, -32768, 32767);
// Advance output feedback.
yn2 = yn1;
yn1 = val;
return static_cast<s16>(val);
};
size_t outputi = framei * SAMPLES_PER_FRAME;
size_t datai = framei * FRAME_LEN + 1;
for (size_t i = 0; i < SAMPLES_PER_FRAME && outputi < sample_count; i += 2) {
const s16 sample1 = decode_sample(SIGNED_NIBBLES[data[datai] >> 4]);
ret[outputi] = sample1;
outputi++;
const s16 sample2 = decode_sample(SIGNED_NIBBLES[data[datai] & 0xF]);
ret[outputi] = sample2;
outputi++;
datai++;
}
}
state.yn1 = yn1;
state.yn2 = yn2;
return ret;
}
} // namespace AudioCore::Codec

44
src/audio_core/codec.h Normal file
View 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 <array>
#include <vector>
#include "common/common_types.h"
namespace AudioCore::Codec {
enum class PcmFormat : u32 {
Invalid = 0,
Int8 = 1,
Int16 = 2,
Int24 = 3,
Int32 = 4,
PcmFloat = 5,
Adpcm = 6,
};
/// See: Codec::DecodeADPCM
struct ADPCMState {
// Two historical samples from previous processed buffer,
// required for ADPCM decoding
s16 yn1; ///< y[n-1]
s16 yn2; ///< y[n-2]
};
using ADPCM_Coeff = std::array<s16, 16>;
/**
* @param data Pointer to buffer that contains ADPCM data to decode
* @param size Size of buffer in bytes
* @param coeff ADPCM coefficients
* @param state ADPCM state, this is updated with new state
* @return Decoded stereo signed PCM16 data, sample_count in length
*/
std::vector<s16> DecodeADPCM(const u8* const data, size_t size, const ADPCM_Coeff& coeff,
ADPCMState& state);
}; // namespace AudioCore::Codec

View File

@@ -13,20 +13,30 @@ namespace AudioCore {
class SinkStreamImpl final : public SinkStream {
public:
SinkStreamImpl(cubeb* ctx, cubeb_devid output_device) : ctx{ctx} {
cubeb_stream_params params;
params.rate = 48000;
params.channels = GetNumChannels();
params.format = CUBEB_SAMPLE_S16NE;
params.layout = CUBEB_LAYOUT_STEREO;
SinkStreamImpl(cubeb* ctx, u32 sample_rate, u32 num_channels_, cubeb_devid output_device,
const std::string& name)
: ctx{ctx}, num_channels{num_channels_} {
u32 minimum_latency = 0;
if (num_channels == 6) {
// 6-channel audio does not seem to work with cubeb + SDL, so we downsample this to 2
// channel for now
is_6_channel = true;
num_channels = 2;
}
cubeb_stream_params params{};
params.rate = sample_rate;
params.channels = num_channels;
params.format = CUBEB_SAMPLE_S16NE;
params.layout = num_channels == 1 ? CUBEB_LAYOUT_MONO : CUBEB_LAYOUT_STEREO;
u32 minimum_latency{};
if (cubeb_get_min_latency(ctx, &params, &minimum_latency) != CUBEB_OK) {
LOG_CRITICAL(Audio_Sink, "Error getting minimum latency");
}
if (cubeb_stream_init(ctx, &stream_backend, "yuzu Audio Output", nullptr, nullptr,
output_device, &params, std::max(512u, minimum_latency),
if (cubeb_stream_init(ctx, &stream_backend, name.c_str(), nullptr, nullptr, output_device,
&params, std::max(512u, minimum_latency),
&SinkStreamImpl::DataCallback, &SinkStreamImpl::StateCallback,
this) != CUBEB_OK) {
LOG_CRITICAL(Audio_Sink, "Error initializing cubeb stream");
@@ -51,33 +61,29 @@ public:
cubeb_stream_destroy(stream_backend);
}
void EnqueueSamples(u32 num_channels, const s16* samples, size_t sample_count) override {
void EnqueueSamples(u32 num_channels, const std::vector<s16>& samples) override {
if (!ctx) {
return;
}
queue.reserve(queue.size() + sample_count * GetNumChannels());
queue.reserve(queue.size() + samples.size() * GetNumChannels());
if (num_channels == 2) {
// Copy as-is
std::copy(samples, samples + sample_count * GetNumChannels(),
std::back_inserter(queue));
} else if (num_channels == 6) {
if (is_6_channel) {
// Downsample 6 channels to 2
const size_t sample_count_copy_size = sample_count * num_channels * 2;
const size_t sample_count_copy_size = samples.size() * 2;
queue.reserve(sample_count_copy_size);
for (size_t i = 0; i < sample_count * num_channels; i += num_channels) {
for (size_t i = 0; i < samples.size(); i += num_channels) {
queue.push_back(samples[i]);
queue.push_back(samples[i + 1]);
}
} else {
ASSERT_MSG(false, "Unimplemented");
// Copy as-is
std::copy(samples.begin(), samples.end(), std::back_inserter(queue));
}
}
u32 GetNumChannels() const {
// Only support 2-channel stereo output for now
return 2;
return num_channels;
}
private:
@@ -85,6 +91,8 @@ private:
cubeb* ctx{};
cubeb_stream* stream_backend{};
u32 num_channels{};
bool is_6_channel{};
std::vector<s16> queue;
@@ -129,8 +137,10 @@ CubebSink::~CubebSink() {
cubeb_destroy(ctx);
}
SinkStream& CubebSink::AcquireSinkStream(u32 sample_rate, u32 num_channels) {
sink_streams.push_back(std::make_unique<SinkStreamImpl>(ctx, output_device));
SinkStream& CubebSink::AcquireSinkStream(u32 sample_rate, u32 num_channels,
const std::string& name) {
sink_streams.push_back(
std::make_unique<SinkStreamImpl>(ctx, sample_rate, num_channels, output_device, name));
return *sink_streams.back();
}

View File

@@ -18,7 +18,8 @@ public:
explicit CubebSink(std::string device_id);
~CubebSink() override;
SinkStream& AcquireSinkStream(u32 sample_rate, u32 num_channels) override;
SinkStream& AcquireSinkStream(u32 sample_rate, u32 num_channels,
const std::string& name) override;
private:
cubeb* ctx{};

View File

@@ -13,14 +13,14 @@ public:
explicit NullSink(std::string){};
~NullSink() override = default;
SinkStream& AcquireSinkStream(u32 /*sample_rate*/, u32 /*num_channels*/) override {
SinkStream& AcquireSinkStream(u32 /*sample_rate*/, u32 /*num_channels*/,
const std::string& /*name*/) override {
return null_sink_stream;
}
private:
struct NullSinkStreamImpl final : SinkStream {
void EnqueueSamples(u32 /*num_channels*/, const s16* /*samples*/,
size_t /*sample_count*/) override {}
void EnqueueSamples(u32 /*num_channels*/, const std::vector<s16>& /*samples*/) override {}
} null_sink_stream;
};

View File

@@ -5,6 +5,7 @@
#pragma once
#include <memory>
#include <string>
#include "audio_core/sink_stream.h"
#include "common/common_types.h"
@@ -21,7 +22,8 @@ constexpr char auto_device_name[] = "auto";
class Sink {
public:
virtual ~Sink() = default;
virtual SinkStream& AcquireSinkStream(u32 sample_rate, u32 num_channels) = 0;
virtual SinkStream& AcquireSinkStream(u32 sample_rate, u32 num_channels,
const std::string& name) = 0;
};
using SinkPtr = std::unique_ptr<Sink>;

View File

@@ -6,6 +6,7 @@
#include <functional>
#include <memory>
#include <utility>
#include <vector>
namespace AudioCore {
@@ -13,16 +14,18 @@ namespace AudioCore {
class Sink;
struct SinkDetails {
SinkDetails(const char* id_, std::function<std::unique_ptr<Sink>(std::string)> factory_,
std::function<std::vector<std::string>()> list_devices_)
: id(id_), factory(factory_), list_devices(list_devices_) {}
using FactoryFn = std::function<std::unique_ptr<Sink>(std::string)>;
using ListDevicesFn = std::function<std::vector<std::string>()>;
SinkDetails(const char* id_, FactoryFn factory_, ListDevicesFn list_devices_)
: id(id_), factory(std::move(factory_)), list_devices(std::move(list_devices_)) {}
/// Name for this sink.
const char* id;
/// A method to call to construct an instance of this type of sink.
std::function<std::unique_ptr<Sink>(std::string device_id)> factory;
FactoryFn factory;
/// A method to call to list available devices.
std::function<std::vector<std::string>()> list_devices;
ListDevicesFn list_devices;
};
extern const std::vector<SinkDetails> g_sink_details;

View File

@@ -5,6 +5,7 @@
#pragma once
#include <memory>
#include <vector>
#include "common/common_types.h"
@@ -22,9 +23,8 @@ public:
* Feed stereo samples to sink.
* @param num_channels Number of channels used.
* @param samples Samples in interleaved stereo PCM16 format.
* @param sample_count Number of samples.
*/
virtual void EnqueueSamples(u32 num_channels, const s16* samples, size_t sample_count) = 0;
virtual void EnqueueSamples(u32 num_channels, const std::vector<s16>& samples) = 0;
};
using SinkStreamPtr = std::unique_ptr<SinkStream>;

View File

@@ -2,14 +2,17 @@
// 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 <algorithm>
#include <cmath>
#include "audio_core/sink.h"
#include "audio_core/sink_details.h"
#include "audio_core/stream.h"
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/core_timing.h"
#include "core/core_timing_util.h"
#include "core/settings.h"
namespace AudioCore {
@@ -29,17 +32,13 @@ u32 Stream::GetNumChannels() const {
return {};
}
u32 Stream::GetSampleSize() const {
return GetNumChannels() * 2;
}
Stream::Stream(u32 sample_rate, Format format, ReleaseCallback&& release_callback,
SinkStream& sink_stream)
SinkStream& sink_stream, std::string&& name_)
: sample_rate{sample_rate}, format{format}, release_callback{std::move(release_callback)},
sink_stream{sink_stream} {
sink_stream{sink_stream}, name{std::move(name_)} {
release_event = CoreTiming::RegisterEvent(
"Stream::Release", [this](u64 userdata, int cycles_late) { ReleaseActiveBuffer(); });
name, [this](u64 userdata, int cycles_late) { ReleaseActiveBuffer(); });
}
void Stream::Play() {
@@ -52,10 +51,24 @@ void Stream::Stop() {
}
s64 Stream::GetBufferReleaseCycles(const Buffer& buffer) const {
const size_t num_samples{buffer.GetData().size() / GetSampleSize()};
const size_t num_samples{buffer.GetSamples().size() / GetNumChannels()};
return CoreTiming::usToCycles((static_cast<u64>(num_samples) * 1000000) / sample_rate);
}
static void VolumeAdjustSamples(std::vector<s16>& samples) {
const float volume{std::clamp(Settings::values.volume, 0.0f, 1.0f)};
if (volume == 1.0f) {
return;
}
// Implementation of a volume slider with a dynamic range of 60 dB
const float volume_scale_factor{std::exp(6.90775f * volume) * 0.001f};
for (auto& sample : samples) {
sample = static_cast<s16>(sample * volume_scale_factor);
}
}
void Stream::PlayNextBuffer() {
if (!IsPlaying()) {
// Ensure we are in playing state before playing the next buffer
@@ -75,14 +88,14 @@ void Stream::PlayNextBuffer() {
active_buffer = queued_buffers.front();
queued_buffers.pop();
sink_stream.EnqueueSamples(GetNumChannels(),
reinterpret_cast<const s16*>(active_buffer->GetData().data()),
active_buffer->GetData().size() / GetSampleSize());
VolumeAdjustSamples(active_buffer->Samples());
sink_stream.EnqueueSamples(GetNumChannels(), active_buffer->GetSamples());
CoreTiming::ScheduleEventThreadsafe(GetBufferReleaseCycles(*active_buffer), release_event, {});
}
void Stream::ReleaseActiveBuffer() {
ASSERT(active_buffer);
released_buffers.push(std::move(active_buffer));
release_callback();
PlayNextBuffer();

View File

@@ -6,6 +6,7 @@
#include <functional>
#include <memory>
#include <string>
#include <vector>
#include <queue>
@@ -33,7 +34,7 @@ public:
using ReleaseCallback = std::function<void()>;
Stream(u32 sample_rate, Format format, ReleaseCallback&& release_callback,
SinkStream& sink_stream);
SinkStream& sink_stream, std::string&& name_);
/// Plays the audio stream
void Play();
@@ -68,9 +69,6 @@ public:
/// Gets the number of channels
u32 GetNumChannels() const;
/// Gets the sample size in bytes
u32 GetSampleSize() const;
private:
/// Current state of the stream
enum class State {
@@ -96,6 +94,7 @@ private:
std::queue<BufferPtr> queued_buffers; ///< Buffers queued to be played in the stream
std::queue<BufferPtr> released_buffers; ///< Buffers recently released from the stream
SinkStream& sink_stream; ///< Output sink for the stream
std::string name; ///< Name of the stream, must be unique
};
using StreamPtr = std::shared_ptr<Stream>;

View File

@@ -32,6 +32,7 @@
#define SDMC_DIR "sdmc"
#define NAND_DIR "nand"
#define SYSDATA_DIR "sysdata"
#define KEYS_DIR "keys"
#define LOG_DIR "log"
// Filenames

View File

@@ -706,6 +706,7 @@ const std::string& GetUserPath(UserPath path, const std::string& new_path) {
paths.emplace(UserPath::SDMCDir, user_path + SDMC_DIR DIR_SEP);
paths.emplace(UserPath::NANDDir, user_path + NAND_DIR DIR_SEP);
paths.emplace(UserPath::SysDataDir, user_path + SYSDATA_DIR DIR_SEP);
paths.emplace(UserPath::KeysDir, user_path + KEYS_DIR DIR_SEP);
// TODO: Put the logs in a better location for each OS
paths.emplace(UserPath::LogDir, user_path + LOG_DIR DIR_SEP);
}
@@ -736,6 +737,19 @@ const std::string& GetUserPath(UserPath path, const std::string& new_path) {
return paths[path];
}
std::string GetHactoolConfigurationPath() {
#ifdef _WIN32
PWSTR pw_local_path = nullptr;
if (SHGetKnownFolderPath(FOLDERID_Profile, 0, nullptr, &pw_local_path) != S_OK)
return "";
std::string local_path = Common::UTF16ToUTF8(pw_local_path);
CoTaskMemFree(pw_local_path);
return local_path + "\\.switch";
#else
return GetHomeDirectory() + "/.switch";
#endif
}
size_t WriteStringToFile(bool text_file, const std::string& str, const char* filename) {
return FileUtil::IOFile(filename, text_file ? "w" : "wb").WriteBytes(str.data(), str.size());
}

View File

@@ -23,6 +23,7 @@ namespace FileUtil {
enum class UserPath {
CacheDir,
ConfigDir,
KeysDir,
LogDir,
NANDDir,
RootDir,
@@ -125,6 +126,8 @@ bool SetCurrentDir(const std::string& directory);
// directory. To be used in "multi-user" mode (that is, installed).
const std::string& GetUserPath(UserPath path, const std::string& new_path = "");
std::string GetHactoolConfigurationPath();
// Returns the path to where the sys file are
std::string GetSysDirectory();

View File

@@ -169,14 +169,18 @@ void FileBackend::Write(const Entry& entry) {
SUB(Service, AOC) \
SUB(Service, APM) \
SUB(Service, BCAT) \
SUB(Service, BPC) \
SUB(Service, BTM) \
SUB(Service, Capture) \
SUB(Service, Fatal) \
SUB(Service, FGM) \
SUB(Service, Friend) \
SUB(Service, FS) \
SUB(Service, HID) \
SUB(Service, LBL) \
SUB(Service, LDN) \
SUB(Service, LM) \
SUB(Service, Migration) \
SUB(Service, Mii) \
SUB(Service, MM) \
SUB(Service, NCM) \
@@ -185,8 +189,11 @@ void FileBackend::Write(const Entry& entry) {
SUB(Service, NIFM) \
SUB(Service, NS) \
SUB(Service, NVDRV) \
SUB(Service, PCIE) \
SUB(Service, PCTL) \
SUB(Service, PCV) \
SUB(Service, PREPO) \
SUB(Service, PSC) \
SUB(Service, SET) \
SUB(Service, SM) \
SUB(Service, SPL) \
@@ -210,6 +217,7 @@ void FileBackend::Write(const Entry& entry) {
CLS(Input) \
CLS(Network) \
CLS(Loader) \
CLS(Crypto) \
CLS(WebService)
// GetClassName is a macro defined by Windows.h, grrr...

View File

@@ -56,14 +56,18 @@ enum class Class : ClassType {
Service_APM, ///< The APM (Performance) service
Service_Audio, ///< The Audio (Audio control) service
Service_BCAT, ///< The BCAT service
Service_BPC, ///< The BPC service
Service_BTM, ///< The BTM service
Service_Capture, ///< The capture service
Service_Fatal, ///< The Fatal service
Service_FGM, ///< The FGM service
Service_Friend, ///< The friend service
Service_FS, ///< The FS (Filesystem) 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_LM, ///< The LM (Logger) service
Service_Migration, ///< The migration service
Service_Mii, ///< The Mii service
Service_MM, ///< The MM (Multimedia) service
Service_NCM, ///< The NCM service
@@ -72,8 +76,11 @@ enum class Class : ClassType {
Service_NIFM, ///< The NIFM (Network interface) service
Service_NS, ///< The NS services
Service_NVDRV, ///< The NVDRV (Nvidia driver) service
Service_PCIE, ///< The PCIe service
Service_PCTL, ///< The PCTL (Parental control) service
Service_PCV, ///< The PCV service
Service_PREPO, ///< The PREPO (Play report) service
Service_PSC, ///< The PSC service
Service_SET, ///< The SET (Settings) service
Service_SM, ///< The SM (Service manager) service
Service_SPL, ///< The SPL service
@@ -95,6 +102,7 @@ enum class Class : ClassType {
Audio_DSP, ///< The HLE implementation of the DSP
Audio_Sink, ///< Emulator audio output backend
Loader, ///< ROM loader
Crypto, ///< Cryptographic engine/functions
Input, ///< Input emulation
Network, ///< Network emulation
WebService, ///< Interface to yuzu Web Services

View File

@@ -19,12 +19,12 @@ inline bool IntervalsIntersect(unsigned start0, unsigned length0, unsigned start
template <class T>
struct Rectangle {
T left;
T top;
T right;
T bottom;
T left{};
T top{};
T right{};
T bottom{};
Rectangle() {}
Rectangle() = default;
Rectangle(T left, T top, T right, T bottom)
: left(left), top(top), right(right), bottom(bottom) {}

View File

@@ -33,9 +33,11 @@ public:
bool Empty() const {
return !read_ptr->next.load();
}
T& Front() const {
return read_ptr->current;
}
template <typename Arg>
void Push(Arg&& t) {
// create the element, add it to the queue
@@ -108,15 +110,41 @@ private:
// single reader, multiple writer queue
template <typename T, bool NeedSize = true>
class MPSCQueue : public SPSCQueue<T, NeedSize> {
class MPSCQueue {
public:
u32 Size() const {
return spsc_queue.Size();
}
bool Empty() const {
return spsc_queue.Empty();
}
T& Front() const {
return spsc_queue.Front();
}
template <typename Arg>
void Push(Arg&& t) {
std::lock_guard<std::mutex> lock(write_lock);
SPSCQueue<T, NeedSize>::Push(t);
spsc_queue.Push(t);
}
void Pop() {
return spsc_queue.Pop();
}
bool Pop(T& t) {
return spsc_queue.Pop(t);
}
// not thread-safe
void Clear() {
spsc_queue.Clear();
}
private:
SPSCQueue<T, NeedSize> spsc_queue;
std::mutex write_lock;
};
} // namespace Common

View File

@@ -12,6 +12,16 @@ add_library(core STATIC
core_timing.h
core_timing_util.cpp
core_timing_util.h
crypto/aes_util.cpp
crypto/aes_util.h
crypto/encryption_layer.cpp
crypto/encryption_layer.h
crypto/key_manager.cpp
crypto/key_manager.h
crypto/ctr_encryption_layer.cpp
crypto/ctr_encryption_layer.h
file_sys/card_image.cpp
file_sys/card_image.h
file_sys/content_archive.cpp
file_sys/content_archive.h
file_sys/control_metadata.cpp
@@ -63,10 +73,10 @@ add_library(core STATIC
hle/kernel/hle_ipc.h
hle/kernel/kernel.cpp
hle/kernel/kernel.h
hle/kernel/memory.cpp
hle/kernel/memory.h
hle/kernel/mutex.cpp
hle/kernel/mutex.h
hle/kernel/object.cpp
hle/kernel/object.h
hle/kernel/process.cpp
hle/kernel/process.h
hle/kernel/resource_limit.cpp
@@ -112,23 +122,39 @@ add_library(core STATIC
hle/service/am/applet_ae.h
hle/service/am/applet_oe.cpp
hle/service/am/applet_oe.h
hle/service/am/idle.cpp
hle/service/am/idle.h
hle/service/am/omm.cpp
hle/service/am/omm.h
hle/service/am/spsm.cpp
hle/service/am/spsm.h
hle/service/aoc/aoc_u.cpp
hle/service/aoc/aoc_u.h
hle/service/apm/apm.cpp
hle/service/apm/apm.h
hle/service/apm/interface.cpp
hle/service/apm/interface.h
hle/service/audio/audctl.cpp
hle/service/audio/audctl.h
hle/service/audio/auddbg.cpp
hle/service/audio/auddbg.h
hle/service/audio/audin_a.cpp
hle/service/audio/audin_a.h
hle/service/audio/audin_u.cpp
hle/service/audio/audin_u.h
hle/service/audio/audio.cpp
hle/service/audio/audio.h
hle/service/audio/audout_a.cpp
hle/service/audio/audout_a.h
hle/service/audio/audout_u.cpp
hle/service/audio/audout_u.h
hle/service/audio/audrec_a.cpp
hle/service/audio/audrec_a.h
hle/service/audio/audrec_u.cpp
hle/service/audio/audrec_u.h
hle/service/audio/audren_a.cpp
hle/service/audio/audren_a.h
hle/service/audio/audren_u.cpp
hle/service/audio/audren_u.cpp
hle/service/audio/audren_u.h
hle/service/audio/audren_u.h
hle/service/audio/codecctl.cpp
hle/service/audio/codecctl.h
@@ -138,10 +164,14 @@ add_library(core STATIC
hle/service/bcat/bcat.h
hle/service/bcat/module.cpp
hle/service/bcat/module.h
hle/service/bpc/bpc.cpp
hle/service/bpc/bpc.h
hle/service/btdrv/btdrv.cpp
hle/service/btdrv/btdrv.h
hle/service/btm/btm.cpp
hle/service/btm/btm.h
hle/service/caps/caps.cpp
hle/service/caps/caps.h
hle/service/erpt/erpt.cpp
hle/service/erpt/erpt.h
hle/service/es/es.cpp
@@ -156,8 +186,14 @@ add_library(core STATIC
hle/service/fatal/fatal_u.h
hle/service/filesystem/filesystem.cpp
hle/service/filesystem/filesystem.h
hle/service/filesystem/fsp_ldr.cpp
hle/service/filesystem/fsp_ldr.h
hle/service/filesystem/fsp_pr.cpp
hle/service/filesystem/fsp_pr.h
hle/service/filesystem/fsp_srv.cpp
hle/service/filesystem/fsp_srv.h
hle/service/fgm/fgm.cpp
hle/service/fgm/fgm.h
hle/service/friend/friend.cpp
hle/service/friend/friend.h
hle/service/friend/interface.cpp
@@ -178,6 +214,8 @@ add_library(core STATIC
hle/service/ldr/ldr.h
hle/service/lm/lm.cpp
hle/service/lm/lm.h
hle/service/mig/mig.cpp
hle/service/mig/mig.h
hle/service/mii/mii.cpp
hle/service/mii/mii.h
hle/service/mm/mm_u.cpp
@@ -223,14 +261,20 @@ add_library(core STATIC
hle/service/nvflinger/buffer_queue.h
hle/service/nvflinger/nvflinger.cpp
hle/service/nvflinger/nvflinger.h
hle/service/pcie/pcie.cpp
hle/service/pcie/pcie.h
hle/service/pctl/module.cpp
hle/service/pctl/module.h
hle/service/pctl/pctl.cpp
hle/service/pctl/pctl.h
hle/service/pcv/pcv.cpp
hle/service/pcv/pcv.h
hle/service/pm/pm.cpp
hle/service/pm/pm.h
hle/service/prepo/prepo.cpp
hle/service/prepo/prepo.h
hle/service/psc/psc.cpp
hle/service/psc/psc.h
hle/service/service.cpp
hle/service/service.h
hle/service/set/set.cpp
@@ -279,10 +323,6 @@ add_library(core STATIC
hle/service/vi/vi_u.h
hle/service/wlan/wlan.cpp
hle/service/wlan/wlan.h
hw/hw.cpp
hw/hw.h
hw/lcd.cpp
hw/lcd.h
loader/deconstructed_rom_directory.cpp
loader/deconstructed_rom_directory.h
loader/elf.cpp
@@ -297,6 +337,8 @@ add_library(core STATIC
loader/nro.h
loader/nso.cpp
loader/nso.h
loader/xci.cpp
loader/xci.h
memory.cpp
memory.h
memory_hook.cpp
@@ -316,7 +358,7 @@ add_library(core STATIC
create_target_directory_groups(core)
target_link_libraries(core PUBLIC common PRIVATE audio_core video_core)
target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt lz4_static opus unicorn)
target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt lz4_static mbedtls opus unicorn)
if (ARCHITECTURE_x86_64)
target_sources(core PRIVATE

View File

@@ -10,7 +10,7 @@
#include "core/arm/dynarmic/arm_dynarmic.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/hle/kernel/memory.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/svc.h"
#include "core/memory.h"
@@ -139,14 +139,12 @@ void ARM_Dynarmic::Step() {
}
ARM_Dynarmic::ARM_Dynarmic(std::shared_ptr<ExclusiveMonitor> exclusive_monitor, size_t core_index)
: cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)),
jit(MakeJit()), exclusive_monitor{std::dynamic_pointer_cast<DynarmicExclusiveMonitor>(
exclusive_monitor)},
core_index{core_index} {
ARM_Interface::ThreadContext ctx;
: cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), core_index{core_index},
exclusive_monitor{std::dynamic_pointer_cast<DynarmicExclusiveMonitor>(exclusive_monitor)} {
ThreadContext ctx;
inner_unicorn.SaveContext(ctx);
LoadContext(ctx);
PageTableChanged();
LoadContext(ctx);
}
ARM_Dynarmic::~ARM_Dynarmic() = default;
@@ -205,7 +203,7 @@ u64 ARM_Dynarmic::GetTlsAddress() const {
return cb->tpidrro_el0;
}
void ARM_Dynarmic::SetTlsAddress(u64 address) {
void ARM_Dynarmic::SetTlsAddress(VAddr address) {
cb->tpidrro_el0 = address;
}
@@ -217,7 +215,7 @@ void ARM_Dynarmic::SetTPIDR_EL0(u64 value) {
cb->tpidr_el0 = value;
}
void ARM_Dynarmic::SaveContext(ARM_Interface::ThreadContext& ctx) {
void ARM_Dynarmic::SaveContext(ThreadContext& ctx) {
ctx.cpu_registers = jit->GetRegisters();
ctx.sp = jit->GetSP();
ctx.pc = jit->GetPC();
@@ -226,7 +224,7 @@ void ARM_Dynarmic::SaveContext(ARM_Interface::ThreadContext& ctx) {
ctx.fpscr = jit->GetFpcr();
}
void ARM_Dynarmic::LoadContext(const ARM_Interface::ThreadContext& ctx) {
void ARM_Dynarmic::LoadContext(const ThreadContext& ctx) {
jit->SetRegisters(ctx.cpu_registers);
jit->SetSP(ctx.sp);
jit->SetPC(ctx.pc);

View File

@@ -15,9 +15,7 @@
#include "core/hle/service/service.h"
#include "core/hle/service/sm/controller.h"
#include "core/hle/service/sm/sm.h"
#include "core/hw/hw.h"
#include "core/loader/loader.h"
#include "core/memory_setup.h"
#include "core/settings.h"
#include "file_sys/vfs_real.h"
#include "video_core/video_core.h"
@@ -86,7 +84,7 @@ System::ResultStatus System::SingleStep() {
return RunLoop(false);
}
System::ResultStatus System::Load(EmuWindow* emu_window, const std::string& filepath) {
System::ResultStatus System::Load(EmuWindow& emu_window, const std::string& filepath) {
app_loader = Loader::GetLoader(std::make_shared<FileSys::RealVfsFile>(filepath));
if (!app_loader) {
@@ -101,8 +99,10 @@ System::ResultStatus System::Load(EmuWindow* emu_window, const std::string& file
static_cast<int>(system_mode.second));
switch (system_mode.second) {
case Loader::ResultStatus::ErrorEncrypted:
return ResultStatus::ErrorLoader_ErrorEncrypted;
case Loader::ResultStatus::ErrorMissingKeys:
return ResultStatus::ErrorLoader_ErrorMissingKeys;
case Loader::ResultStatus::ErrorDecrypting:
return ResultStatus::ErrorLoader_ErrorDecrypting;
case Loader::ResultStatus::ErrorInvalidFormat:
return ResultStatus::ErrorLoader_ErrorInvalidFormat;
case Loader::ResultStatus::ErrorUnsupportedArch:
@@ -112,7 +112,7 @@ System::ResultStatus System::Load(EmuWindow* emu_window, const std::string& file
}
}
ResultStatus init_result{Init(emu_window, system_mode.first.get())};
ResultStatus init_result{Init(emu_window)};
if (init_result != ResultStatus::Success) {
LOG_CRITICAL(Core, "Failed to initialize system (Error {})!",
static_cast<int>(init_result));
@@ -126,8 +126,10 @@ System::ResultStatus System::Load(EmuWindow* emu_window, const std::string& file
System::Shutdown();
switch (load_result) {
case Loader::ResultStatus::ErrorEncrypted:
return ResultStatus::ErrorLoader_ErrorEncrypted;
case Loader::ResultStatus::ErrorMissingKeys:
return ResultStatus::ErrorLoader_ErrorMissingKeys;
case Loader::ResultStatus::ErrorDecrypting:
return ResultStatus::ErrorLoader_ErrorDecrypting;
case Loader::ResultStatus::ErrorInvalidFormat:
return ResultStatus::ErrorLoader_ErrorInvalidFormat;
case Loader::ResultStatus::ErrorUnsupportedArch:
@@ -163,7 +165,7 @@ Cpu& System::CpuCore(size_t core_index) {
return *cpu_cores[core_index];
}
System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) {
System::ResultStatus System::Init(EmuWindow& emu_window) {
LOG_DEBUG(HW_Memory, "initialized OK");
CoreTiming::Init();
@@ -180,8 +182,7 @@ System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) {
telemetry_session = std::make_unique<Core::TelemetrySession>();
service_manager = std::make_shared<Service::SM::ServiceManager>();
HW::Init();
Kernel::Init(system_mode);
Kernel::Init();
Service::Init(service_manager);
GDBStub::Init();
@@ -224,7 +225,6 @@ void System::Shutdown() {
GDBStub::Shutdown();
Service::Shutdown();
Kernel::Shutdown();
HW::Shutdown();
service_manager.reset();
telemetry_session.reset();
gpu_core.reset();

View File

@@ -11,7 +11,7 @@
#include "common/common_types.h"
#include "core/arm/exclusive_monitor.h"
#include "core/core_cpu.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/scheduler.h"
#include "core/loader/loader.h"
#include "core/memory.h"
@@ -43,12 +43,14 @@ public:
/// Enumeration representing the return values of the System Initialize and Load process.
enum class ResultStatus : u32 {
Success, ///< Succeeded
ErrorNotInitialized, ///< Error trying to use core prior to initialization
ErrorGetLoader, ///< Error finding the correct application loader
ErrorSystemMode, ///< Error determining the system mode
ErrorLoader, ///< Error loading the specified application
ErrorLoader_ErrorEncrypted, ///< Error loading the specified application due to encryption
Success, ///< Succeeded
ErrorNotInitialized, ///< Error trying to use core prior to initialization
ErrorGetLoader, ///< Error finding the correct application loader
ErrorSystemMode, ///< Error determining the system mode
ErrorLoader, ///< Error loading the specified application
ErrorLoader_ErrorMissingKeys, ///< Error because the key/keys needed to run could not be
///< found.
ErrorLoader_ErrorDecrypting, ///< Error loading the specified application due to encryption
ErrorLoader_ErrorInvalidFormat, ///< Error loading the specified application due to an
/// invalid format
ErrorSystemFiles, ///< Error in finding system files
@@ -81,11 +83,12 @@ public:
/**
* Load an executable application.
* @param emu_window Pointer to the host-system window used for video output and keyboard input.
* @param emu_window Reference to the host-system window used for video output and keyboard
* input.
* @param filepath String path to the executable application to load on the host file system.
* @returns ResultStatus code, indicating if the operation succeeded.
*/
ResultStatus Load(EmuWindow* emu_window, const std::string& filepath);
ResultStatus Load(EmuWindow& emu_window, const std::string& filepath);
/**
* Indicates if the emulated system is powered on (all subsystems initialized and able to run an
@@ -186,11 +189,11 @@ private:
/**
* Initialize the emulated system.
* @param emu_window Pointer to the host-system window used for video output and keyboard input.
* @param system_mode The system mode.
* @param emu_window Reference to the host-system window used for video output and keyboard
* input.
* @return ResultStatus code, indicating if the operation succeeded.
*/
ResultStatus Init(EmuWindow* emu_window, u32 system_mode);
ResultStatus Init(EmuWindow& emu_window);
/// AppLoader used to load the current executing application
std::unique_ptr<Loader::AppLoader> app_loader;

View File

@@ -12,7 +12,6 @@
#include "core/arm/unicorn/arm_unicorn.h"
#include "core/core_cpu.h"
#include "core/core_timing.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/scheduler.h"
#include "core/hle/kernel/thread.h"
#include "core/settings.h"

View File

@@ -0,0 +1,112 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <mbedtls/cipher.h>
#include "core/crypto/aes_util.h"
#include "core/crypto/key_manager.h"
namespace Core::Crypto {
static_assert(static_cast<size_t>(Mode::CTR) == static_cast<size_t>(MBEDTLS_CIPHER_AES_128_CTR),
"CTR has incorrect value.");
static_assert(static_cast<size_t>(Mode::ECB) == static_cast<size_t>(MBEDTLS_CIPHER_AES_128_ECB),
"ECB has incorrect value.");
static_assert(static_cast<size_t>(Mode::XTS) == static_cast<size_t>(MBEDTLS_CIPHER_AES_128_XTS),
"XTS has incorrect value.");
// Structure to hide mbedtls types from header file
struct CipherContext {
mbedtls_cipher_context_t encryption_context;
mbedtls_cipher_context_t decryption_context;
};
template <typename Key, size_t KeySize>
Crypto::AESCipher<Key, KeySize>::AESCipher(Key key, Mode mode)
: ctx(std::make_unique<CipherContext>()) {
mbedtls_cipher_init(&ctx->encryption_context);
mbedtls_cipher_init(&ctx->decryption_context);
ASSERT_MSG((mbedtls_cipher_setup(
&ctx->encryption_context,
mbedtls_cipher_info_from_type(static_cast<mbedtls_cipher_type_t>(mode))) ||
mbedtls_cipher_setup(
&ctx->decryption_context,
mbedtls_cipher_info_from_type(static_cast<mbedtls_cipher_type_t>(mode)))) == 0,
"Failed to initialize mbedtls ciphers.");
ASSERT(
!mbedtls_cipher_setkey(&ctx->encryption_context, key.data(), KeySize * 8, MBEDTLS_ENCRYPT));
ASSERT(
!mbedtls_cipher_setkey(&ctx->decryption_context, key.data(), KeySize * 8, MBEDTLS_DECRYPT));
//"Failed to set key on mbedtls ciphers.");
}
template <typename Key, size_t KeySize>
AESCipher<Key, KeySize>::~AESCipher() {
mbedtls_cipher_free(&ctx->encryption_context);
mbedtls_cipher_free(&ctx->decryption_context);
}
template <typename Key, size_t KeySize>
void AESCipher<Key, KeySize>::SetIV(std::vector<u8> iv) {
ASSERT_MSG((mbedtls_cipher_set_iv(&ctx->encryption_context, iv.data(), iv.size()) ||
mbedtls_cipher_set_iv(&ctx->decryption_context, iv.data(), iv.size())) == 0,
"Failed to set IV on mbedtls ciphers.");
}
template <typename Key, size_t KeySize>
void AESCipher<Key, KeySize>::Transcode(const u8* src, size_t size, u8* dest, Op op) {
size_t written = 0;
const auto context = op == Op::Encrypt ? &ctx->encryption_context : &ctx->decryption_context;
mbedtls_cipher_reset(context);
if (mbedtls_cipher_get_cipher_mode(context) == MBEDTLS_MODE_XTS) {
mbedtls_cipher_update(context, src, size, dest, &written);
if (written != size)
LOG_WARNING(Crypto, "Not all data was decrypted requested={:016X}, actual={:016X}.",
size, written);
} else {
const auto block_size = mbedtls_cipher_get_block_size(context);
for (size_t offset = 0; offset < size; offset += block_size) {
auto length = std::min<size_t>(block_size, size - offset);
mbedtls_cipher_update(context, src + offset, length, dest + offset, &written);
if (written != length)
LOG_WARNING(Crypto, "Not all data was decrypted requested={:016X}, actual={:016X}.",
length, written);
}
}
mbedtls_cipher_finish(context, nullptr, nullptr);
}
template <typename Key, size_t KeySize>
void AESCipher<Key, KeySize>::XTSTranscode(const u8* src, size_t size, u8* dest, size_t sector_id,
size_t sector_size, Op op) {
if (size % sector_size > 0) {
LOG_CRITICAL(Crypto, "Data size must be a multiple of sector size.");
return;
}
for (size_t i = 0; i < size; i += sector_size) {
SetIV(CalculateNintendoTweak(sector_id++));
Transcode<u8, u8>(src + i, sector_size, dest + i, op);
}
}
template <typename Key, size_t KeySize>
std::vector<u8> AESCipher<Key, KeySize>::CalculateNintendoTweak(size_t sector_id) {
std::vector<u8> out(0x10);
for (size_t i = 0xF; i <= 0xF; --i) {
out[i] = sector_id & 0xFF;
sector_id >>= 8;
}
return out;
}
template class AESCipher<Key128>;
template class AESCipher<Key256>;
} // namespace Core::Crypto

View File

@@ -0,0 +1,62 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <memory>
#include <type_traits>
#include <vector>
#include "common/assert.h"
#include "core/file_sys/vfs.h"
namespace Core::Crypto {
struct CipherContext;
enum class Mode {
CTR = 11,
ECB = 2,
XTS = 70,
};
enum class Op {
Encrypt,
Decrypt,
};
template <typename Key, size_t KeySize = sizeof(Key)>
class AESCipher {
static_assert(std::is_same_v<Key, std::array<u8, KeySize>>, "Key must be std::array of u8.");
static_assert(KeySize == 0x10 || KeySize == 0x20, "KeySize must be 128 or 256.");
public:
AESCipher(Key key, Mode mode);
~AESCipher();
void SetIV(std::vector<u8> iv);
template <typename Source, typename Dest>
void Transcode(const Source* src, size_t size, Dest* dest, Op op) {
Transcode(reinterpret_cast<const u8*>(src), size, reinterpret_cast<u8*>(dest), op);
}
void Transcode(const u8* src, size_t size, u8* dest, Op op);
template <typename Source, typename Dest>
void XTSTranscode(const Source* src, size_t size, Dest* dest, size_t sector_id,
size_t sector_size, Op op) {
XTSTranscode(reinterpret_cast<const u8*>(src), size, reinterpret_cast<u8*>(dest), sector_id,
sector_size, op);
}
void XTSTranscode(const u8* src, size_t size, u8* dest, size_t sector_id, size_t sector_size,
Op op);
private:
std::unique_ptr<CipherContext> ctx;
static std::vector<u8> CalculateNintendoTweak(size_t sector_id);
};
} // namespace Core::Crypto

View File

@@ -0,0 +1,56 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <cstring>
#include "common/assert.h"
#include "core/crypto/ctr_encryption_layer.h"
namespace Core::Crypto {
CTREncryptionLayer::CTREncryptionLayer(FileSys::VirtualFile base_, Key128 key_, size_t base_offset)
: EncryptionLayer(std::move(base_)), base_offset(base_offset), cipher(key_, Mode::CTR),
iv(16, 0) {}
size_t CTREncryptionLayer::Read(u8* data, size_t length, size_t offset) const {
if (length == 0)
return 0;
const auto sector_offset = offset & 0xF;
if (sector_offset == 0) {
UpdateIV(base_offset + offset);
std::vector<u8> raw = base->ReadBytes(length, offset);
if (raw.size() != length)
return Read(data, raw.size(), offset);
cipher.Transcode(raw.data(), length, data, Op::Decrypt);
return length;
}
// offset does not fall on block boundary (0x10)
std::vector<u8> block = base->ReadBytes(0x10, offset - sector_offset);
UpdateIV(base_offset + offset - sector_offset);
cipher.Transcode(block.data(), block.size(), block.data(), Op::Decrypt);
size_t read = 0x10 - sector_offset;
if (length + sector_offset < 0x10) {
std::memcpy(data, block.data() + sector_offset, std::min<u64>(length, read));
return read;
}
std::memcpy(data, block.data() + sector_offset, read);
return read + Read(data + read, length - read, offset + read);
}
void CTREncryptionLayer::SetIV(const std::vector<u8>& iv_) {
const auto length = std::min(iv_.size(), iv.size());
iv.assign(iv_.cbegin(), iv_.cbegin() + length);
}
void CTREncryptionLayer::UpdateIV(size_t offset) const {
offset >>= 4;
for (size_t i = 0; i < 8; ++i) {
iv[16 - i - 1] = offset & 0xFF;
offset >>= 8;
}
cipher.SetIV(iv);
}
} // namespace Core::Crypto

View File

@@ -0,0 +1,33 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <vector>
#include "core/crypto/aes_util.h"
#include "core/crypto/encryption_layer.h"
#include "core/crypto/key_manager.h"
namespace Core::Crypto {
// Sits on top of a VirtualFile and provides CTR-mode AES decription.
class CTREncryptionLayer : public EncryptionLayer {
public:
CTREncryptionLayer(FileSys::VirtualFile base, Key128 key, size_t base_offset);
size_t Read(u8* data, size_t length, size_t offset) const override;
void SetIV(const std::vector<u8>& iv);
private:
size_t base_offset;
// Must be mutable as operations modify cipher contexts.
mutable AESCipher<Key128> cipher;
mutable std::vector<u8> iv;
void UpdateIV(size_t offset) const;
};
} // namespace Core::Crypto

View File

@@ -0,0 +1,42 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/crypto/encryption_layer.h"
namespace Core::Crypto {
EncryptionLayer::EncryptionLayer(FileSys::VirtualFile base_) : base(std::move(base_)) {}
std::string EncryptionLayer::GetName() const {
return base->GetName();
}
size_t EncryptionLayer::GetSize() const {
return base->GetSize();
}
bool EncryptionLayer::Resize(size_t new_size) {
return false;
}
std::shared_ptr<FileSys::VfsDirectory> EncryptionLayer::GetContainingDirectory() const {
return base->GetContainingDirectory();
}
bool EncryptionLayer::IsWritable() const {
return false;
}
bool EncryptionLayer::IsReadable() const {
return true;
}
size_t EncryptionLayer::Write(const u8* data, size_t length, size_t offset) {
return 0;
}
bool EncryptionLayer::Rename(std::string_view name) {
return base->Rename(name);
}
} // namespace Core::Crypto

View File

@@ -0,0 +1,32 @@
// 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 Core::Crypto {
// Basically non-functional class that implements all of the methods that are irrelevant to an
// EncryptionLayer. Reduces duplicate code.
class EncryptionLayer : public FileSys::VfsFile {
public:
explicit EncryptionLayer(FileSys::VirtualFile base);
size_t Read(u8* data, size_t length, size_t offset) const override = 0;
std::string GetName() const override;
size_t GetSize() const override;
bool Resize(size_t new_size) override;
std::shared_ptr<FileSys::VfsDirectory> GetContainingDirectory() const override;
bool IsWritable() const override;
bool IsReadable() const override;
size_t Write(const u8* data, size_t length, size_t offset) override;
bool Rename(std::string_view name) override;
protected:
FileSys::VirtualFile base;
};
} // namespace Core::Crypto

View File

@@ -0,0 +1,215 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <array>
#include <fstream>
#include <locale>
#include <sstream>
#include <string_view>
#include <mbedtls/sha256.h>
#include "common/assert.h"
#include "common/common_paths.h"
#include "common/file_util.h"
#include "common/logging/log.h"
#include "core/crypto/key_manager.h"
#include "core/settings.h"
#include "key_manager.h"
namespace Core::Crypto {
static u8 ToHexNibble(char c1) {
if (c1 >= 65 && c1 <= 70)
return c1 - 55;
if (c1 >= 97 && c1 <= 102)
return c1 - 87;
if (c1 >= 48 && c1 <= 57)
return c1 - 48;
throw std::logic_error("Invalid hex digit");
}
template <size_t Size>
static std::array<u8, Size> HexStringToArray(std::string_view str) {
std::array<u8, Size> out{};
for (size_t i = 0; i < 2 * Size; i += 2) {
auto d1 = str[i];
auto d2 = str[i + 1];
out[i / 2] = (ToHexNibble(d1) << 4) | ToHexNibble(d2);
}
return out;
}
std::array<u8, 16> operator""_array16(const char* str, size_t len) {
if (len != 32)
throw std::logic_error("Not of correct size.");
return HexStringToArray<16>(str);
}
std::array<u8, 32> operator""_array32(const char* str, size_t len) {
if (len != 64)
throw std::logic_error("Not of correct size.");
return HexStringToArray<32>(str);
}
KeyManager::KeyManager() {
// Initialize keys
const std::string hactool_keys_dir = FileUtil::GetHactoolConfigurationPath();
const std::string yuzu_keys_dir = FileUtil::GetUserPath(FileUtil::UserPath::KeysDir);
if (Settings::values.use_dev_keys) {
dev_mode = true;
AttemptLoadKeyFile(yuzu_keys_dir, hactool_keys_dir, "dev.keys", false);
} else {
dev_mode = false;
AttemptLoadKeyFile(yuzu_keys_dir, hactool_keys_dir, "prod.keys", false);
}
AttemptLoadKeyFile(yuzu_keys_dir, hactool_keys_dir, "title.keys", true);
}
void KeyManager::LoadFromFile(std::string_view filename_, bool is_title_keys) {
const auto filename = std::string(filename_);
std::ifstream file(filename);
if (!file.is_open())
return;
std::string line;
while (std::getline(file, line)) {
std::vector<std::string> out;
std::stringstream stream(line);
std::string item;
while (std::getline(stream, item, '='))
out.push_back(std::move(item));
if (out.size() != 2)
continue;
out[0].erase(std::remove(out[0].begin(), out[0].end(), ' '), out[0].end());
out[1].erase(std::remove(out[1].begin(), out[1].end(), ' '), out[1].end());
if (is_title_keys) {
auto rights_id_raw = HexStringToArray<16>(out[0]);
u128 rights_id{};
std::memcpy(rights_id.data(), rights_id_raw.data(), rights_id_raw.size());
Key128 key = HexStringToArray<16>(out[1]);
SetKey(S128KeyType::Titlekey, key, rights_id[1], rights_id[0]);
} else {
std::transform(out[0].begin(), out[0].end(), out[0].begin(), ::tolower);
if (s128_file_id.find(out[0]) != s128_file_id.end()) {
const auto index = s128_file_id.at(out[0]);
Key128 key = HexStringToArray<16>(out[1]);
SetKey(index.type, key, index.field1, index.field2);
} else if (s256_file_id.find(out[0]) != s256_file_id.end()) {
const auto index = s256_file_id.at(out[0]);
Key256 key = HexStringToArray<32>(out[1]);
SetKey(index.type, key, index.field1, index.field2);
}
}
}
}
void KeyManager::AttemptLoadKeyFile(std::string_view dir1_, std::string_view dir2_,
std::string_view filename_, bool title) {
const std::string dir1(dir1_);
const std::string dir2(dir2_);
const std::string filename(filename_);
if (FileUtil::Exists(dir1 + DIR_SEP + filename))
LoadFromFile(dir1 + DIR_SEP + filename, title);
else if (FileUtil::Exists(dir2 + DIR_SEP + filename))
LoadFromFile(dir2 + DIR_SEP + filename, title);
}
bool KeyManager::HasKey(S128KeyType id, u64 field1, u64 field2) const {
return s128_keys.find({id, field1, field2}) != s128_keys.end();
}
bool KeyManager::HasKey(S256KeyType id, u64 field1, u64 field2) const {
return s256_keys.find({id, field1, field2}) != s256_keys.end();
}
Key128 KeyManager::GetKey(S128KeyType id, u64 field1, u64 field2) const {
if (!HasKey(id, field1, field2))
return {};
return s128_keys.at({id, field1, field2});
}
Key256 KeyManager::GetKey(S256KeyType id, u64 field1, u64 field2) const {
if (!HasKey(id, field1, field2))
return {};
return s256_keys.at({id, field1, field2});
}
void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) {
s128_keys[{id, field1, field2}] = key;
}
void KeyManager::SetKey(S256KeyType id, Key256 key, u64 field1, u64 field2) {
s256_keys[{id, field1, field2}] = key;
}
bool KeyManager::KeyFileExists(bool title) {
const std::string hactool_keys_dir = FileUtil::GetHactoolConfigurationPath();
const std::string yuzu_keys_dir = FileUtil::GetUserPath(FileUtil::UserPath::KeysDir);
if (title) {
return FileUtil::Exists(hactool_keys_dir + DIR_SEP + "title.keys") ||
FileUtil::Exists(yuzu_keys_dir + DIR_SEP + "title.keys");
}
if (Settings::values.use_dev_keys) {
return FileUtil::Exists(hactool_keys_dir + DIR_SEP + "dev.keys") ||
FileUtil::Exists(yuzu_keys_dir + DIR_SEP + "dev.keys");
}
return FileUtil::Exists(hactool_keys_dir + DIR_SEP + "prod.keys") ||
FileUtil::Exists(yuzu_keys_dir + DIR_SEP + "prod.keys");
}
const std::unordered_map<std::string, KeyIndex<S128KeyType>> KeyManager::s128_file_id = {
{"master_key_00", {S128KeyType::Master, 0, 0}},
{"master_key_01", {S128KeyType::Master, 1, 0}},
{"master_key_02", {S128KeyType::Master, 2, 0}},
{"master_key_03", {S128KeyType::Master, 3, 0}},
{"master_key_04", {S128KeyType::Master, 4, 0}},
{"package1_key_00", {S128KeyType::Package1, 0, 0}},
{"package1_key_01", {S128KeyType::Package1, 1, 0}},
{"package1_key_02", {S128KeyType::Package1, 2, 0}},
{"package1_key_03", {S128KeyType::Package1, 3, 0}},
{"package1_key_04", {S128KeyType::Package1, 4, 0}},
{"package2_key_00", {S128KeyType::Package2, 0, 0}},
{"package2_key_01", {S128KeyType::Package2, 1, 0}},
{"package2_key_02", {S128KeyType::Package2, 2, 0}},
{"package2_key_03", {S128KeyType::Package2, 3, 0}},
{"package2_key_04", {S128KeyType::Package2, 4, 0}},
{"titlekek_00", {S128KeyType::Titlekek, 0, 0}},
{"titlekek_01", {S128KeyType::Titlekek, 1, 0}},
{"titlekek_02", {S128KeyType::Titlekek, 2, 0}},
{"titlekek_03", {S128KeyType::Titlekek, 3, 0}},
{"titlekek_04", {S128KeyType::Titlekek, 4, 0}},
{"eticket_rsa_kek", {S128KeyType::ETicketRSAKek, 0, 0}},
{"key_area_key_application_00",
{S128KeyType::KeyArea, 0, static_cast<u64>(KeyAreaKeyType::Application)}},
{"key_area_key_application_01",
{S128KeyType::KeyArea, 1, static_cast<u64>(KeyAreaKeyType::Application)}},
{"key_area_key_application_02",
{S128KeyType::KeyArea, 2, static_cast<u64>(KeyAreaKeyType::Application)}},
{"key_area_key_application_03",
{S128KeyType::KeyArea, 3, static_cast<u64>(KeyAreaKeyType::Application)}},
{"key_area_key_application_04",
{S128KeyType::KeyArea, 4, static_cast<u64>(KeyAreaKeyType::Application)}},
{"key_area_key_ocean_00", {S128KeyType::KeyArea, 0, static_cast<u64>(KeyAreaKeyType::Ocean)}},
{"key_area_key_ocean_01", {S128KeyType::KeyArea, 1, static_cast<u64>(KeyAreaKeyType::Ocean)}},
{"key_area_key_ocean_02", {S128KeyType::KeyArea, 2, static_cast<u64>(KeyAreaKeyType::Ocean)}},
{"key_area_key_ocean_03", {S128KeyType::KeyArea, 3, static_cast<u64>(KeyAreaKeyType::Ocean)}},
{"key_area_key_ocean_04", {S128KeyType::KeyArea, 4, static_cast<u64>(KeyAreaKeyType::Ocean)}},
{"key_area_key_system_00", {S128KeyType::KeyArea, 0, static_cast<u64>(KeyAreaKeyType::System)}},
{"key_area_key_system_01", {S128KeyType::KeyArea, 1, static_cast<u64>(KeyAreaKeyType::System)}},
{"key_area_key_system_02", {S128KeyType::KeyArea, 2, static_cast<u64>(KeyAreaKeyType::System)}},
{"key_area_key_system_03", {S128KeyType::KeyArea, 3, static_cast<u64>(KeyAreaKeyType::System)}},
{"key_area_key_system_04", {S128KeyType::KeyArea, 4, static_cast<u64>(KeyAreaKeyType::System)}},
};
const std::unordered_map<std::string, KeyIndex<S256KeyType>> KeyManager::s256_file_id = {
{"header_key", {S256KeyType::Header, 0, 0}},
{"sd_card_save_key", {S256KeyType::SDSave, 0, 0}},
{"sd_card_nca_key", {S256KeyType::SDNCA, 0, 0}},
};
} // namespace Core::Crypto

View File

@@ -0,0 +1,119 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include <type_traits>
#include <unordered_map>
#include <vector>
#include <fmt/format.h>
#include "common/common_types.h"
namespace Core::Crypto {
using Key128 = std::array<u8, 0x10>;
using Key256 = std::array<u8, 0x20>;
using SHA256Hash = std::array<u8, 0x20>;
static_assert(sizeof(Key128) == 16, "Key128 must be 128 bytes big.");
static_assert(sizeof(Key256) == 32, "Key128 must be 128 bytes big.");
enum class S256KeyType : u64 {
Header, //
SDSave, //
SDNCA, //
};
enum class S128KeyType : u64 {
Master, // f1=crypto revision
Package1, // f1=crypto revision
Package2, // f1=crypto revision
Titlekek, // f1=crypto revision
ETicketRSAKek, //
KeyArea, // f1=crypto revision f2=type {app, ocean, system}
SDSeed, //
Titlekey, // f1=rights id LSB f2=rights id MSB
};
enum class KeyAreaKeyType : u8 {
Application,
Ocean,
System,
};
template <typename KeyType>
struct KeyIndex {
KeyType type;
u64 field1;
u64 field2;
std::string DebugInfo() const {
u8 key_size = 16;
if constexpr (std::is_same_v<KeyType, S256KeyType>)
key_size = 32;
return fmt::format("key_size={:02X}, key={:02X}, field1={:016X}, field2={:016X}", key_size,
static_cast<u8>(type), field1, field2);
}
};
// The following two (== and hash) are so KeyIndex can be a key in unordered_map
template <typename KeyType>
bool operator==(const KeyIndex<KeyType>& lhs, const KeyIndex<KeyType>& rhs) {
return std::tie(lhs.type, lhs.field1, lhs.field2) == std::tie(rhs.type, rhs.field1, rhs.field2);
}
template <typename KeyType>
bool operator!=(const KeyIndex<KeyType>& lhs, const KeyIndex<KeyType>& rhs) {
return !operator==(lhs, rhs);
}
} // namespace Core::Crypto
namespace std {
template <typename KeyType>
struct hash<Core::Crypto::KeyIndex<KeyType>> {
size_t operator()(const Core::Crypto::KeyIndex<KeyType>& k) const {
using std::hash;
return ((hash<u64>()(static_cast<u64>(k.type)) ^ (hash<u64>()(k.field1) << 1)) >> 1) ^
(hash<u64>()(k.field2) << 1);
}
};
} // namespace std
namespace Core::Crypto {
std::array<u8, 0x10> operator"" _array16(const char* str, size_t len);
std::array<u8, 0x20> operator"" _array32(const char* str, size_t len);
class KeyManager {
public:
KeyManager();
bool HasKey(S128KeyType id, u64 field1 = 0, u64 field2 = 0) const;
bool HasKey(S256KeyType id, u64 field1 = 0, u64 field2 = 0) const;
Key128 GetKey(S128KeyType id, u64 field1 = 0, u64 field2 = 0) const;
Key256 GetKey(S256KeyType id, u64 field1 = 0, u64 field2 = 0) const;
void SetKey(S128KeyType id, Key128 key, u64 field1 = 0, u64 field2 = 0);
void SetKey(S256KeyType id, Key256 key, u64 field1 = 0, u64 field2 = 0);
static bool KeyFileExists(bool title);
private:
std::unordered_map<KeyIndex<S128KeyType>, Key128> s128_keys;
std::unordered_map<KeyIndex<S256KeyType>, Key256> s256_keys;
bool dev_mode;
void LoadFromFile(std::string_view filename, bool is_title_keys);
void AttemptLoadKeyFile(std::string_view dir1, std::string_view dir2, std::string_view filename,
bool title);
static const std::unordered_map<std::string, KeyIndex<S128KeyType>> s128_file_id;
static const std::unordered_map<std::string, KeyIndex<S256KeyType>> s256_file_id;
};
} // namespace Core::Crypto

View File

@@ -0,0 +1,5 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
namespace Crypto {} // namespace Crypto

View File

@@ -0,0 +1,20 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "common/assert.h"
#include "core/file_sys/vfs.h"
#include "key_manager.h"
#include "mbedtls/cipher.h"
namespace Crypto {
typedef std::array<u8, 0x20> SHA256Hash;
inline SHA256Hash operator"" _HASH(const char* data, size_t len) {
if (len != 0x40)
return {};
}
} // namespace Crypto

View File

@@ -0,0 +1,149 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <array>
#include <string>
#include <core/loader/loader.h>
#include "core/file_sys/card_image.h"
#include "core/file_sys/partition_filesystem.h"
#include "core/file_sys/vfs_offset.h"
namespace FileSys {
XCI::XCI(VirtualFile file_) : file(std::move(file_)), partitions(0x4) {
if (file->ReadObject(&header) != sizeof(GamecardHeader)) {
status = Loader::ResultStatus::ErrorInvalidFormat;
return;
}
if (header.magic != Common::MakeMagic('H', 'E', 'A', 'D')) {
status = Loader::ResultStatus::ErrorInvalidFormat;
return;
}
PartitionFilesystem main_hfs(
std::make_shared<OffsetVfsFile>(file, header.hfs_size, header.hfs_offset));
if (main_hfs.GetStatus() != Loader::ResultStatus::Success) {
status = main_hfs.GetStatus();
return;
}
static constexpr std::array<const char*, 0x4> partition_names = {"update", "normal", "secure",
"logo"};
for (XCIPartition partition :
{XCIPartition::Update, XCIPartition::Normal, XCIPartition::Secure, XCIPartition::Logo}) {
auto raw = main_hfs.GetFile(partition_names[static_cast<size_t>(partition)]);
if (raw != nullptr)
partitions[static_cast<size_t>(partition)] = std::make_shared<PartitionFilesystem>(raw);
}
auto result = AddNCAFromPartition(XCIPartition::Secure);
if (result != Loader::ResultStatus::Success) {
status = result;
return;
}
result = AddNCAFromPartition(XCIPartition::Update);
if (result != Loader::ResultStatus::Success) {
status = result;
return;
}
result = AddNCAFromPartition(XCIPartition::Normal);
if (result != Loader::ResultStatus::Success) {
status = result;
return;
}
if (GetFormatVersion() >= 0x2) {
result = AddNCAFromPartition(XCIPartition::Logo);
if (result != Loader::ResultStatus::Success) {
status = result;
return;
}
}
status = Loader::ResultStatus::Success;
}
Loader::ResultStatus XCI::GetStatus() const {
return status;
}
VirtualDir XCI::GetPartition(XCIPartition partition) const {
return partitions[static_cast<size_t>(partition)];
}
VirtualDir XCI::GetSecurePartition() const {
return GetPartition(XCIPartition::Secure);
}
VirtualDir XCI::GetNormalPartition() const {
return GetPartition(XCIPartition::Normal);
}
VirtualDir XCI::GetUpdatePartition() const {
return GetPartition(XCIPartition::Update);
}
VirtualDir XCI::GetLogoPartition() const {
return GetPartition(XCIPartition::Logo);
}
std::shared_ptr<NCA> XCI::GetNCAByType(NCAContentType type) const {
const auto iter =
std::find_if(ncas.begin(), ncas.end(),
[type](const std::shared_ptr<NCA>& nca) { return nca->GetType() == type; });
return iter == ncas.end() ? nullptr : *iter;
}
VirtualFile XCI::GetNCAFileByType(NCAContentType type) const {
auto nca = GetNCAByType(type);
if (nca != nullptr)
return nca->GetBaseFile();
return nullptr;
}
std::vector<std::shared_ptr<VfsFile>> XCI::GetFiles() const {
return {};
}
std::vector<std::shared_ptr<VfsDirectory>> XCI::GetSubdirectories() const {
return std::vector<std::shared_ptr<VfsDirectory>>();
}
std::string XCI::GetName() const {
return file->GetName();
}
std::shared_ptr<VfsDirectory> XCI::GetParentDirectory() const {
return file->GetContainingDirectory();
}
bool XCI::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
return false;
}
Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) {
if (partitions[static_cast<size_t>(part)] == nullptr) {
return Loader::ResultStatus::ErrorInvalidFormat;
}
for (const VirtualFile& file : partitions[static_cast<size_t>(part)]->GetFiles()) {
if (file->GetExtension() != "nca")
continue;
auto nca = std::make_shared<NCA>(file);
if (nca->GetStatus() == Loader::ResultStatus::Success)
ncas.push_back(std::move(nca));
}
return Loader::ResultStatus::Success;
}
u8 XCI::GetFormatVersion() const {
return GetLogoPartition() == nullptr ? 0x1 : 0x2;
}
} // namespace FileSys

View File

@@ -0,0 +1,96 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include <vector>
#include "common/common_types.h"
#include "common/swap.h"
#include "core/file_sys/content_archive.h"
#include "core/file_sys/vfs.h"
#include "core/loader/loader.h"
namespace FileSys {
enum class GamecardSize : u8 {
S_1GB = 0xFA,
S_2GB = 0xF8,
S_4GB = 0xF0,
S_8GB = 0xE0,
S_16GB = 0xE1,
S_32GB = 0xE2,
};
struct GamecardInfo {
std::array<u8, 0x70> data;
};
static_assert(sizeof(GamecardInfo) == 0x70, "GamecardInfo has incorrect size.");
struct GamecardHeader {
std::array<u8, 0x100> signature;
u32_le magic;
u32_le secure_area_start;
u32_le backup_area_start;
u8 kek_index;
GamecardSize size;
u8 header_version;
u8 flags;
u64_le package_id;
u64_le valid_data_end;
u128 info_iv;
u64_le hfs_offset;
u64_le hfs_size;
std::array<u8, 0x20> hfs_header_hash;
std::array<u8, 0x20> initial_data_hash;
u32_le secure_mode_flag;
u32_le title_key_flag;
u32_le key_flag;
u32_le normal_area_end;
GamecardInfo info;
};
static_assert(sizeof(GamecardHeader) == 0x200, "GamecardHeader has incorrect size.");
enum class XCIPartition : u8 { Update, Normal, Secure, Logo };
class XCI : public ReadOnlyVfsDirectory {
public:
explicit XCI(VirtualFile file);
Loader::ResultStatus GetStatus() const;
u8 GetFormatVersion() const;
VirtualDir GetPartition(XCIPartition partition) const;
VirtualDir GetSecurePartition() const;
VirtualDir GetNormalPartition() const;
VirtualDir GetUpdatePartition() const;
VirtualDir GetLogoPartition() const;
std::shared_ptr<NCA> GetNCAByType(NCAContentType type) const;
VirtualFile GetNCAFileByType(NCAContentType type) const;
std::vector<std::shared_ptr<VfsFile>> GetFiles() const override;
std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override;
std::string GetName() const override;
std::shared_ptr<VfsDirectory> GetParentDirectory() const override;
protected:
bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
private:
Loader::ResultStatus AddNCAFromPartition(XCIPartition part);
VirtualFile file;
GamecardHeader header{};
Loader::ResultStatus status;
std::vector<VirtualDir> partitions;
std::vector<std::shared_ptr<NCA>> ncas;
};
} // namespace FileSys

View File

@@ -4,12 +4,14 @@
#include <algorithm>
#include <utility>
#include <boost/optional.hpp>
#include "common/logging/log.h"
#include "core/crypto/aes_util.h"
#include "core/crypto/ctr_encryption_layer.h"
#include "core/file_sys/content_archive.h"
#include "core/file_sys/romfs.h"
#include "core/file_sys/vfs_offset.h"
#include "core/loader/loader.h"
#include "romfs.h"
namespace FileSys {
@@ -29,11 +31,19 @@ enum class NCASectionFilesystemType : u8 {
struct NCASectionHeaderBlock {
INSERT_PADDING_BYTES(3);
NCASectionFilesystemType filesystem_type;
u8 crypto_type;
NCASectionCryptoType crypto_type;
INSERT_PADDING_BYTES(3);
};
static_assert(sizeof(NCASectionHeaderBlock) == 0x8, "NCASectionHeaderBlock has incorrect size.");
struct NCASectionRaw {
NCASectionHeaderBlock header;
std::array<u8, 0x138> block_data;
std::array<u8, 0x8> section_ctr;
INSERT_PADDING_BYTES(0xB8);
};
static_assert(sizeof(NCASectionRaw) == 0x200, "NCASectionRaw has incorrect size.");
struct PFS0Superblock {
NCASectionHeaderBlock header_block;
std::array<u8, 0x20> hash;
@@ -43,67 +53,170 @@ struct PFS0Superblock {
u64_le hash_table_size;
u64_le pfs0_header_offset;
u64_le pfs0_size;
INSERT_PADDING_BYTES(432);
INSERT_PADDING_BYTES(0x1B0);
};
static_assert(sizeof(PFS0Superblock) == 0x200, "PFS0Superblock has incorrect size.");
struct RomFSSuperblock {
NCASectionHeaderBlock header_block;
IVFCHeader ivfc;
INSERT_PADDING_BYTES(0x118);
};
static_assert(sizeof(RomFSSuperblock) == 0xE8, "RomFSSuperblock has incorrect size.");
static_assert(sizeof(RomFSSuperblock) == 0x200, "RomFSSuperblock has incorrect size.");
union NCASectionHeader {
NCASectionRaw raw;
PFS0Superblock pfs0;
RomFSSuperblock romfs;
};
static_assert(sizeof(NCASectionHeader) == 0x200, "NCASectionHeader has incorrect size.");
bool IsValidNCA(const NCAHeader& header) {
// TODO(DarkLordZach): Add NCA2/NCA0 support.
return header.magic == Common::MakeMagic('N', 'C', 'A', '3');
}
boost::optional<Core::Crypto::Key128> NCA::GetKeyAreaKey(NCASectionCryptoType type) const {
u8 master_key_id = header.crypto_type;
if (header.crypto_type_2 > master_key_id)
master_key_id = header.crypto_type_2;
if (master_key_id > 0)
--master_key_id;
if (!keys.HasKey(Core::Crypto::S128KeyType::KeyArea, master_key_id, header.key_index))
return boost::none;
std::vector<u8> key_area(header.key_area.begin(), header.key_area.end());
Core::Crypto::AESCipher<Core::Crypto::Key128> cipher(
keys.GetKey(Core::Crypto::S128KeyType::KeyArea, master_key_id, header.key_index),
Core::Crypto::Mode::ECB);
cipher.Transcode(key_area.data(), key_area.size(), key_area.data(), Core::Crypto::Op::Decrypt);
Core::Crypto::Key128 out;
if (type == NCASectionCryptoType::XTS)
std::copy(key_area.begin(), key_area.begin() + 0x10, out.begin());
else if (type == NCASectionCryptoType::CTR)
std::copy(key_area.begin() + 0x20, key_area.begin() + 0x30, out.begin());
else
LOG_CRITICAL(Crypto, "Called GetKeyAreaKey on invalid NCASectionCryptoType type={:02X}",
static_cast<u8>(type));
u128 out_128{};
memcpy(out_128.data(), out.data(), 16);
LOG_DEBUG(Crypto, "called with crypto_rev={:02X}, kak_index={:02X}, key={:016X}{:016X}",
master_key_id, header.key_index, out_128[1], out_128[0]);
return out;
}
VirtualFile NCA::Decrypt(NCASectionHeader header, VirtualFile in, u64 starting_offset) const {
if (!encrypted)
return in;
switch (header.raw.header.crypto_type) {
case NCASectionCryptoType::NONE:
LOG_DEBUG(Crypto, "called with mode=NONE");
return in;
case NCASectionCryptoType::CTR:
LOG_DEBUG(Crypto, "called with mode=CTR, starting_offset={:016X}", starting_offset);
{
const auto key = GetKeyAreaKey(NCASectionCryptoType::CTR);
if (key == boost::none)
return nullptr;
auto out = std::make_shared<Core::Crypto::CTREncryptionLayer>(
std::move(in), key.value(), starting_offset);
std::vector<u8> iv(16);
for (u8 i = 0; i < 8; ++i)
iv[i] = header.raw.section_ctr[0x8 - i - 1];
out->SetIV(iv);
return std::static_pointer_cast<VfsFile>(out);
}
case NCASectionCryptoType::XTS:
// TODO(DarkLordZach): Implement XTSEncryptionLayer and title key encryption.
default:
LOG_ERROR(Crypto, "called with unhandled crypto type={:02X}",
static_cast<u8>(header.raw.header.crypto_type));
return nullptr;
}
}
NCA::NCA(VirtualFile file_) : file(std::move(file_)) {
if (sizeof(NCAHeader) != file->ReadObject(&header))
LOG_CRITICAL(Loader, "File reader errored out during header read.");
LOG_ERROR(Loader, "File reader errored out during header read.");
encrypted = false;
if (!IsValidNCA(header)) {
status = Loader::ResultStatus::ErrorInvalidFormat;
return;
NCAHeader dec_header{};
Core::Crypto::AESCipher<Core::Crypto::Key256> cipher(
keys.GetKey(Core::Crypto::S256KeyType::Header), Core::Crypto::Mode::XTS);
cipher.XTSTranscode(&header, sizeof(NCAHeader), &dec_header, 0, 0x200,
Core::Crypto::Op::Decrypt);
if (IsValidNCA(dec_header)) {
header = dec_header;
encrypted = true;
} else {
if (!keys.HasKey(Core::Crypto::S256KeyType::Header))
status = Loader::ResultStatus::ErrorMissingKeys;
else
status = Loader::ResultStatus::ErrorDecrypting;
return;
}
}
std::ptrdiff_t number_sections =
const std::ptrdiff_t number_sections =
std::count_if(std::begin(header.section_tables), std::end(header.section_tables),
[](NCASectionTableEntry entry) { return entry.media_offset > 0; });
std::vector<NCASectionHeader> sections(number_sections);
const auto length_sections = SECTION_HEADER_SIZE * number_sections;
if (encrypted) {
auto raw = file->ReadBytes(length_sections, SECTION_HEADER_OFFSET);
Core::Crypto::AESCipher<Core::Crypto::Key256> cipher(
keys.GetKey(Core::Crypto::S256KeyType::Header), Core::Crypto::Mode::XTS);
cipher.XTSTranscode(raw.data(), length_sections, sections.data(), 2, SECTION_HEADER_SIZE,
Core::Crypto::Op::Decrypt);
} else {
file->ReadBytes(sections.data(), length_sections, SECTION_HEADER_OFFSET);
}
for (std::ptrdiff_t i = 0; i < number_sections; ++i) {
// Seek to beginning of this section.
NCASectionHeaderBlock block{};
if (sizeof(NCASectionHeaderBlock) !=
file->ReadObject(&block, SECTION_HEADER_OFFSET + i * SECTION_HEADER_SIZE))
LOG_CRITICAL(Loader, "File reader errored out during header read.");
if (block.filesystem_type == NCASectionFilesystemType::ROMFS) {
RomFSSuperblock sb{};
if (sizeof(RomFSSuperblock) !=
file->ReadObject(&sb, SECTION_HEADER_OFFSET + i * SECTION_HEADER_SIZE))
LOG_CRITICAL(Loader, "File reader errored out during header read.");
auto section = sections[i];
if (section.raw.header.filesystem_type == NCASectionFilesystemType::ROMFS) {
const size_t romfs_offset =
header.section_tables[i].media_offset * MEDIA_OFFSET_MULTIPLIER +
sb.ivfc.levels[IVFC_MAX_LEVEL - 1].offset;
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));
romfs = files.back();
} else if (block.filesystem_type == NCASectionFilesystemType::PFS0) {
PFS0Superblock sb{};
// Seek back to beginning of this section.
if (sizeof(PFS0Superblock) !=
file->ReadObject(&sb, SECTION_HEADER_OFFSET + i * SECTION_HEADER_SIZE))
LOG_CRITICAL(Loader, "File reader errored out during header read.");
section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset;
const size_t romfs_size = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].size;
auto dec =
Decrypt(section, std::make_shared<OffsetVfsFile>(file, romfs_size, romfs_offset),
romfs_offset);
if (dec != nullptr) {
files.push_back(std::move(dec));
romfs = files.back();
} else {
status = Loader::ResultStatus::ErrorMissingKeys;
return;
}
} else if (section.raw.header.filesystem_type == NCASectionFilesystemType::PFS0) {
u64 offset = (static_cast<u64>(header.section_tables[i].media_offset) *
MEDIA_OFFSET_MULTIPLIER) +
sb.pfs0_header_offset;
section.pfs0.pfs0_header_offset;
u64 size = MEDIA_OFFSET_MULTIPLIER * (header.section_tables[i].media_end_offset -
header.section_tables[i].media_offset);
auto npfs = std::make_shared<PartitionFilesystem>(
std::make_shared<OffsetVfsFile>(file, size, offset));
auto dec =
Decrypt(section, std::make_shared<OffsetVfsFile>(file, size, offset), offset);
if (dec != nullptr) {
auto npfs = std::make_shared<PartitionFilesystem>(std::move(dec));
if (npfs->GetStatus() == Loader::ResultStatus::Success) {
dirs.emplace_back(npfs);
if (IsDirectoryExeFS(dirs.back()))
exefs = dirs.back();
if (npfs->GetStatus() == Loader::ResultStatus::Success) {
dirs.push_back(std::move(npfs));
if (IsDirectoryExeFS(dirs.back()))
exefs = dirs.back();
}
} else {
status = Loader::ResultStatus::ErrorMissingKeys;
return;
}
}
}
@@ -153,6 +266,10 @@ VirtualDir NCA::GetExeFS() const {
return exefs;
}
VirtualFile NCA::GetBaseFile() const {
return file;
}
bool NCA::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
return false;
}

View File

@@ -8,14 +8,18 @@
#include <memory>
#include <string>
#include <vector>
#include <boost/optional.hpp>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
#include "core/crypto/key_manager.h"
#include "core/file_sys/partition_filesystem.h"
#include "core/loader/loader.h"
namespace FileSys {
union NCASectionHeader;
enum class NCAContentType : u8 {
Program = 0,
Meta = 1,
@@ -24,6 +28,13 @@ enum class NCAContentType : u8 {
Data = 4,
};
enum class NCASectionCryptoType : u8 {
NONE = 1,
XTS = 2,
CTR = 3,
BKTR = 4,
};
struct NCASectionTableEntry {
u32_le media_offset;
u32_le media_end_offset;
@@ -48,7 +59,7 @@ struct NCAHeader {
std::array<u8, 0x10> rights_id;
std::array<NCASectionTableEntry, 0x4> section_tables;
std::array<std::array<u8, 0x20>, 0x4> hash_tables;
std::array<std::array<u8, 0x10>, 0x4> key_area;
std::array<u8, 0x40> key_area;
INSERT_PADDING_BYTES(0xC0);
};
static_assert(sizeof(NCAHeader) == 0x400, "NCAHeader has incorrect size.");
@@ -58,10 +69,7 @@ inline bool IsDirectoryExeFS(const std::shared_ptr<VfsDirectory>& pfs) {
return pfs->GetFile("main") != nullptr && pfs->GetFile("main.npdm") != nullptr;
}
inline bool IsValidNCA(const NCAHeader& header) {
return header.magic == Common::MakeMagic('N', 'C', 'A', '2') ||
header.magic == Common::MakeMagic('N', 'C', 'A', '3');
}
bool IsValidNCA(const NCAHeader& header);
// An implementation of VfsDirectory that represents a Nintendo Content Archive (NCA) conatiner.
// After construction, use GetStatus to determine if the file is valid and ready to be used.
@@ -81,10 +89,15 @@ public:
VirtualFile GetRomFS() const;
VirtualDir GetExeFS() const;
VirtualFile GetBaseFile() const;
protected:
bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
private:
boost::optional<Core::Crypto::Key128> GetKeyAreaKey(NCASectionCryptoType type) const;
VirtualFile Decrypt(NCASectionHeader header, VirtualFile in, u64 starting_offset) const;
std::vector<VirtualDir> dirs;
std::vector<VirtualFile> files;
@@ -95,6 +108,10 @@ private:
NCAHeader header{};
Loader::ResultStatus status{};
bool encrypted;
Core::Crypto::KeyManager keys;
};
} // namespace FileSys

View File

@@ -285,6 +285,26 @@ bool ReadOnlyVfsDirectory::Rename(std::string_view name) {
return false;
}
bool DeepEquals(const VirtualFile& file1, const VirtualFile& file2, size_t block_size) {
if (file1->GetSize() != file2->GetSize())
return false;
std::vector<u8> f1_v(block_size);
std::vector<u8> f2_v(block_size);
for (size_t i = 0; i < file1->GetSize(); i += block_size) {
auto f1_vs = file1->Read(f1_v.data(), block_size, i);
auto f2_vs = file2->Read(f2_v.data(), block_size, i);
if (f1_vs != f2_vs)
return false;
auto iters = std::mismatch(f1_v.begin(), f1_v.end(), f2_v.begin(), f2_v.end());
if (iters.first != f1_v.end() && iters.second != f2_v.end())
return false;
}
return true;
}
bool VfsRawCopy(VirtualFile src, VirtualFile dest) {
if (src == nullptr || dest == nullptr)
return false;

View File

@@ -245,6 +245,9 @@ struct ReadOnlyVfsDirectory : public VfsDirectory {
bool Rename(std::string_view name) override;
};
// Compare the two files, byte-for-byte, in increments specificed by block_size
bool DeepEquals(const VirtualFile& file1, const VirtualFile& file2, size_t block_size = 0x200);
// 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.

View File

@@ -3,6 +3,7 @@
// Refer to the license.txt file included.
#include <algorithm>
#include <utility>
#include "core/file_sys/vfs_vector.h"
namespace FileSys {
@@ -31,16 +32,18 @@ bool VectorVfsDirectory::IsReadable() const {
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; });
const auto iter =
std::find_if(vec.begin(), vec.end(), [name](const T& e) { return e->GetName() == name; });
if (iter == vec.end())
return false;
auto old_size = vec.size();
vec.erase(iter);
return true;
}
@@ -77,7 +80,7 @@ void VectorVfsDirectory::AddDirectory(VirtualDir dir) {
bool VectorVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
if (!DeleteFile(file->GetName()))
return false;
dirs.emplace_back(dir);
dirs.emplace_back(std::move(dir));
return true;
}
} // namespace FileSys

View File

@@ -37,7 +37,6 @@
#include "core/core.h"
#include "core/core_cpu.h"
#include "core/gdbstub/gdbstub.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/scheduler.h"
#include "core/loader/loader.h"
#include "core/memory.h"

View File

@@ -5,15 +5,18 @@
#pragma once
#include <array>
#include <cstring>
#include <memory>
#include <tuple>
#include <type_traits>
#include <utility>
#include "common/assert.h"
#include "common/common_types.h"
#include "core/hle/ipc.h"
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/client_session.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/server_port.h"
namespace IPC {

View File

@@ -2,15 +2,17 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <vector>
#include "common/assert.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/core.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/lock.h"
#include "core/hle/result.h"
#include "core/memory.h"
namespace Kernel {

View File

@@ -4,7 +4,9 @@
#pragma once
#include "core/hle/result.h"
#include "common/common_types.h"
union ResultCode;
namespace Kernel {

View File

@@ -2,12 +2,13 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/assert.h"
#include <tuple>
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/client_session.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/server_port.h"
#include "core/hle/kernel/server_session.h"

View File

@@ -6,7 +6,7 @@
#include <string>
#include "common/common_types.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/object.h"
#include "core/hle/result.h"
namespace Kernel {

View File

@@ -2,8 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/assert.h"
#include "core/hle/kernel/client_session.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/hle_ipc.h"

View File

@@ -7,7 +7,7 @@
#include <memory>
#include <string>
#include "common/common_types.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/object.h"
#include "core/hle/result.h"
namespace Kernel {

View File

@@ -3,11 +3,9 @@
// Refer to the license.txt file included.
#include <algorithm>
#include <map>
#include <vector>
#include "common/assert.h"
#include "core/hle/kernel/event.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/thread.h"
namespace Kernel {

View File

@@ -5,7 +5,7 @@
#pragma once
#include "common/common_types.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/wait_object.h"
namespace Kernel {

View File

@@ -8,7 +8,6 @@
#include "core/core.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/thread.h"

View File

@@ -7,7 +7,7 @@
#include <array>
#include <cstddef>
#include "common/common_types.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/object.h"
#include "core/hle/result.h"
namespace Kernel {

View File

@@ -2,17 +2,22 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <array>
#include <sstream>
#include <utility>
#include <boost/range/algorithm_ext/erase.hpp>
#include "common/assert.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/logging/log.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/event.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/server_session.h"
#include "core/memory.h"

View File

@@ -5,7 +5,6 @@
#pragma once
#include <array>
#include <iterator>
#include <memory>
#include <string>
#include <type_traits>
@@ -14,7 +13,7 @@
#include "common/common_types.h"
#include "common/swap.h"
#include "core/hle/ipc.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/server_session.h"
#include "core/hle/kernel/thread.h"

View File

@@ -4,7 +4,6 @@
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/memory.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/resource_limit.h"
#include "core/hle/kernel/thread.h"
@@ -15,9 +14,7 @@ namespace Kernel {
unsigned int Object::next_object_id;
/// Initialize the kernel
void Init(u32 system_mode) {
Kernel::MemoryInit(system_mode);
void Init() {
Kernel::ResourceLimitsInit();
Kernel::ThreadingInit();
Kernel::TimersInit();
@@ -37,7 +34,6 @@ void Shutdown() {
Kernel::TimersShutdown();
Kernel::ResourceLimitsShutdown();
Kernel::MemoryShutdown();
}
} // namespace Kernel

View File

@@ -4,122 +4,12 @@
#pragma once
#include <cstddef>
#include <string>
#include <utility>
#include <boost/smart_ptr/intrusive_ptr.hpp>
#include "common/assert.h"
#include "common/common_types.h"
namespace Kernel {
using Handle = u32;
enum class HandleType : u32 {
Unknown,
Event,
SharedMemory,
Thread,
Process,
AddressArbiter,
Timer,
ResourceLimit,
CodeSet,
ClientPort,
ServerPort,
ClientSession,
ServerSession,
};
enum class ResetType {
OneShot,
Sticky,
Pulse,
};
class Object : NonCopyable {
public:
virtual ~Object() {}
/// Returns a unique identifier for the object. For debugging purposes only.
unsigned int GetObjectId() const {
return object_id;
}
virtual std::string GetTypeName() const {
return "[BAD KERNEL OBJECT TYPE]";
}
virtual std::string GetName() const {
return "[UNKNOWN KERNEL OBJECT]";
}
virtual Kernel::HandleType GetHandleType() const = 0;
/**
* Check if a thread can wait on the object
* @return True if a thread can wait on the object, otherwise false
*/
bool IsWaitable() const {
switch (GetHandleType()) {
case HandleType::Event:
case HandleType::Thread:
case HandleType::Timer:
case HandleType::ServerPort:
case HandleType::ServerSession:
return true;
case HandleType::Unknown:
case HandleType::SharedMemory:
case HandleType::Process:
case HandleType::AddressArbiter:
case HandleType::ResourceLimit:
case HandleType::CodeSet:
case HandleType::ClientPort:
case HandleType::ClientSession:
return false;
}
UNREACHABLE();
}
public:
static unsigned int next_object_id;
private:
friend void intrusive_ptr_add_ref(Object*);
friend void intrusive_ptr_release(Object*);
unsigned int ref_count = 0;
unsigned int object_id = next_object_id++;
};
// Special functions used by boost::instrusive_ptr to do automatic ref-counting
inline void intrusive_ptr_add_ref(Object* object) {
++object->ref_count;
}
inline void intrusive_ptr_release(Object* object) {
if (--object->ref_count == 0) {
delete object;
}
}
template <typename T>
using SharedPtr = boost::intrusive_ptr<T>;
/**
* Attempts to downcast the given Object pointer to a pointer to T.
* @return Derived pointer to the object, or `nullptr` if `object` isn't of type T.
*/
template <typename T>
inline SharedPtr<T> DynamicObjectCast(SharedPtr<Object> object) {
if (object != nullptr && object->GetHandleType() == T::HANDLE_TYPE) {
return boost::static_pointer_cast<T>(std::move(object));
}
return nullptr;
}
/// Initialize the kernel with the specified system mode.
void Init(u32 system_mode);
void Init();
/// Shutdown the kernel
void Shutdown();

View File

@@ -1,92 +0,0 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <cinttypes>
#include <map>
#include <memory>
#include <utility>
#include <vector>
#include "common/assert.h"
#include "common/common_types.h"
#include "common/logging/log.h"
#include "core/hle/kernel/memory.h"
#include "core/hle/kernel/vm_manager.h"
#include "core/hle/result.h"
#include "core/memory.h"
#include "core/memory_setup.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace Kernel {
MemoryRegionInfo memory_regions[3];
/// Size of the APPLICATION, SYSTEM and BASE memory regions (respectively) for each system
/// memory configuration type.
static const u32 memory_region_sizes[8][3] = {
// Old 3DS layouts
{0x04000000, 0x02C00000, 0x01400000}, // 0
{/* This appears to be unused. */}, // 1
{0x06000000, 0x00C00000, 0x01400000}, // 2
{0x05000000, 0x01C00000, 0x01400000}, // 3
{0x04800000, 0x02400000, 0x01400000}, // 4
{0x02000000, 0x04C00000, 0x01400000}, // 5
// New 3DS layouts
{0x07C00000, 0x06400000, 0x02000000}, // 6
{0x0B200000, 0x02E00000, 0x02000000}, // 7
};
void MemoryInit(u32 mem_type) {
// TODO(yuriks): On the n3DS, all o3DS configurations (<=5) are forced to 6 instead.
ASSERT_MSG(mem_type <= 5, "New 3DS memory configuration aren't supported yet!");
ASSERT(mem_type != 1);
// The kernel allocation regions (APPLICATION, SYSTEM and BASE) are laid out in sequence, with
// the sizes specified in the memory_region_sizes table.
VAddr base = 0;
for (int i = 0; i < 3; ++i) {
memory_regions[i].base = base;
memory_regions[i].size = memory_region_sizes[mem_type][i];
memory_regions[i].used = 0;
memory_regions[i].linear_heap_memory = std::make_shared<std::vector<u8>>();
// Reserve enough space for this region of FCRAM.
// We do not want this block of memory to be relocated when allocating from it.
memory_regions[i].linear_heap_memory->reserve(memory_regions[i].size);
base += memory_regions[i].size;
}
// We must've allocated the entire FCRAM by the end
ASSERT(base == Memory::FCRAM_SIZE);
}
void MemoryShutdown() {
for (auto& region : memory_regions) {
region.base = 0;
region.size = 0;
region.used = 0;
region.linear_heap_memory = nullptr;
}
}
MemoryRegionInfo* GetMemoryRegion(MemoryRegion region) {
switch (region) {
case MemoryRegion::APPLICATION:
return &memory_regions[0];
case MemoryRegion::SYSTEM:
return &memory_regions[1];
case MemoryRegion::BASE:
return &memory_regions[2];
default:
UNREACHABLE();
}
}
void HandleSpecialMapping(VMManager& address_space, const AddressMapping& mapping) {}
void MapSharedPages(VMManager& address_space) {}
} // namespace Kernel

View File

@@ -1,31 +0,0 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <memory>
#include "common/common_types.h"
#include "core/hle/kernel/process.h"
namespace Kernel {
class VMManager;
struct MemoryRegionInfo {
u64 base; // Not an address, but offset from start of FCRAM
u64 size;
u64 used;
std::shared_ptr<std::vector<u8>> linear_heap_memory;
};
void MemoryInit(u32 mem_type);
void MemoryShutdown();
MemoryRegionInfo* GetMemoryRegion(MemoryRegion region);
void HandleSpecialMapping(VMManager& address_space, const AddressMapping& mapping);
void MapSharedPages(VMManager& address_space);
extern MemoryRegionInfo memory_regions[3];
} // namespace Kernel

View File

@@ -3,15 +3,19 @@
// Refer to the license.txt file included.
#include <map>
#include <utility>
#include <vector>
#include <boost/range/algorithm_ext/erase.hpp>
#include "common/assert.h"
#include "core/core.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/mutex.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/result.h"
namespace Kernel {

View File

@@ -4,12 +4,10 @@
#pragma once
#include <string>
#include "common/common_types.h"
#include "common/swap.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/wait_object.h"
#include "core/hle/result.h"
#include "core/hle/kernel/object.h"
union ResultCode;
namespace Kernel {

View File

@@ -0,0 +1,35 @@
// Copyright 2018 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/assert.h"
#include "core/hle/kernel/object.h"
namespace Kernel {
Object::~Object() = default;
bool Object::IsWaitable() const {
switch (GetHandleType()) {
case HandleType::Event:
case HandleType::Thread:
case HandleType::Timer:
case HandleType::ServerPort:
case HandleType::ServerSession:
return true;
case HandleType::Unknown:
case HandleType::SharedMemory:
case HandleType::Process:
case HandleType::AddressArbiter:
case HandleType::ResourceLimit:
case HandleType::CodeSet:
case HandleType::ClientPort:
case HandleType::ClientSession:
return false;
}
UNREACHABLE();
}
} // namespace Kernel

View File

@@ -0,0 +1,100 @@
// Copyright 2018 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <string>
#include <utility>
#include <boost/smart_ptr/intrusive_ptr.hpp>
#include "common/common_types.h"
namespace Kernel {
using Handle = u32;
enum class HandleType : u32 {
Unknown,
Event,
SharedMemory,
Thread,
Process,
AddressArbiter,
Timer,
ResourceLimit,
CodeSet,
ClientPort,
ServerPort,
ClientSession,
ServerSession,
};
enum class ResetType {
OneShot,
Sticky,
Pulse,
};
class Object : NonCopyable {
public:
virtual ~Object();
/// Returns a unique identifier for the object. For debugging purposes only.
unsigned int GetObjectId() const {
return object_id;
}
virtual std::string GetTypeName() const {
return "[BAD KERNEL OBJECT TYPE]";
}
virtual std::string GetName() const {
return "[UNKNOWN KERNEL OBJECT]";
}
virtual HandleType GetHandleType() const = 0;
/**
* Check if a thread can wait on the object
* @return True if a thread can wait on the object, otherwise false
*/
bool IsWaitable() const;
public:
static unsigned int next_object_id;
private:
friend void intrusive_ptr_add_ref(Object*);
friend void intrusive_ptr_release(Object*);
unsigned int ref_count = 0;
unsigned int object_id = next_object_id++;
};
// Special functions used by boost::instrusive_ptr to do automatic ref-counting
inline void intrusive_ptr_add_ref(Object* object) {
++object->ref_count;
}
inline void intrusive_ptr_release(Object* object) {
if (--object->ref_count == 0) {
delete object;
}
}
template <typename T>
using SharedPtr = boost::intrusive_ptr<T>;
/**
* Attempts to downcast the given Object pointer to a pointer to T.
* @return Derived pointer to the object, or `nullptr` if `object` isn't of type T.
*/
template <typename T>
inline SharedPtr<T> DynamicObjectCast(SharedPtr<Object> object) {
if (object != nullptr && object->GetHandleType() == T::HANDLE_TYPE) {
return boost::static_pointer_cast<T>(std::move(object));
}
return nullptr;
}
} // namespace Kernel

View File

@@ -8,7 +8,6 @@
#include "common/common_funcs.h"
#include "common/logging/log.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/memory.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/resource_limit.h"
#include "core/hle/kernel/thread.h"
@@ -125,14 +124,6 @@ void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) {
std::make_shared<std::vector<u8>>(stack_size, 0), 0, stack_size,
MemoryState::Mapped)
.Unwrap();
misc_memory_used += stack_size;
memory_region->used += stack_size;
// Map special address mappings
MapSharedPages(vm_manager);
for (const auto& mapping : address_mappings) {
HandleSpecialMapping(vm_manager, mapping);
}
vm_manager.LogLayout();
status = ProcessStatus::Running;
@@ -141,37 +132,19 @@ void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) {
}
void Process::LoadModule(SharedPtr<CodeSet> module_, VAddr base_addr) {
memory_region = GetMemoryRegion(flags.memory_region);
auto MapSegment = [&](CodeSet::Segment& segment, VMAPermission permissions,
MemoryState memory_state) {
const auto MapSegment = [&](CodeSet::Segment& segment, VMAPermission permissions,
MemoryState memory_state) {
auto vma = vm_manager
.MapMemoryBlock(segment.addr + base_addr, module_->memory, segment.offset,
segment.size, memory_state)
.Unwrap();
vm_manager.Reprotect(vma, permissions);
misc_memory_used += segment.size;
memory_region->used += segment.size;
};
// Map CodeSet segments
MapSegment(module_->code, VMAPermission::ReadExecute, MemoryState::CodeStatic);
MapSegment(module_->rodata, VMAPermission::Read, MemoryState::CodeMutable);
MapSegment(module_->data, VMAPermission::ReadWrite, MemoryState::CodeMutable);
}
VAddr Process::GetLinearHeapAreaAddress() const {
// Starting from system version 8.0.0 a new linear heap layout is supported to allow usage of
// the extra RAM in the n3DS.
return kernel_version < 0x22C ? Memory::LINEAR_HEAP_VADDR : Memory::NEW_LINEAR_HEAP_VADDR;
}
VAddr Process::GetLinearHeapBase() const {
return GetLinearHeapAreaAddress() + memory_region->base;
}
VAddr Process::GetLinearHeapLimit() const {
return GetLinearHeapBase() + memory_region->size;
MapSegment(module_->CodeSegment(), VMAPermission::ReadExecute, MemoryState::CodeStatic);
MapSegment(module_->RODataSegment(), VMAPermission::Read, MemoryState::CodeMutable);
MapSegment(module_->DataSegment(), VMAPermission::ReadWrite, MemoryState::CodeMutable);
}
ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission perms) {
@@ -206,7 +179,6 @@ ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission per
vm_manager.Reprotect(vma, perms);
heap_used = size;
memory_region->used += size;
return MakeResult<VAddr>(heap_end - size);
}
@@ -226,52 +198,6 @@ ResultCode Process::HeapFree(VAddr target, u32 size) {
return result;
heap_used -= size;
memory_region->used -= size;
return RESULT_SUCCESS;
}
ResultVal<VAddr> Process::LinearAllocate(VAddr target, u32 size, VMAPermission perms) {
UNIMPLEMENTED();
return {};
}
ResultCode Process::LinearFree(VAddr target, u32 size) {
auto& linheap_memory = memory_region->linear_heap_memory;
if (target < GetLinearHeapBase() || target + size > GetLinearHeapLimit() ||
target + size < target) {
return ERR_INVALID_ADDRESS;
}
if (size == 0) {
return RESULT_SUCCESS;
}
VAddr heap_end = GetLinearHeapBase() + (u32)linheap_memory->size();
if (target + size > heap_end) {
return ERR_INVALID_ADDRESS_STATE;
}
ResultCode result = vm_manager.UnmapRange(target, size);
if (result.IsError())
return result;
linear_heap_used -= size;
memory_region->used -= size;
if (target + size == heap_end) {
// End of linear heap has been freed, so check what's the last allocated block in it and
// reduce the size.
auto vma = vm_manager.FindVMA(target);
ASSERT(vma != vm_manager.vma_map.end());
ASSERT(vma->second.type == VMAType::Free);
VAddr new_end = vma->second.base;
if (new_end >= GetLinearHeapBase()) {
linheap_memory->resize(new_end - GetLinearHeapBase());
}
}
return RESULT_SUCCESS;
}

View File

@@ -4,6 +4,7 @@
#pragma once
#include <array>
#include <bitset>
#include <cstddef>
#include <memory>
@@ -12,7 +13,7 @@
#include <boost/container/static_vector.hpp>
#include "common/bit_field.h"
#include "common/common_types.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/kernel/vm_manager.h"
@@ -53,9 +54,14 @@ union ProcessFlags {
enum class ProcessStatus { Created, Running, Exited };
class ResourceLimit;
struct MemoryRegionInfo;
struct CodeSet final : public Object {
struct Segment {
size_t offset = 0;
VAddr addr = 0;
u32 size = 0;
};
static SharedPtr<CodeSet> Create(std::string name);
std::string GetTypeName() const override {
@@ -70,24 +76,38 @@ struct CodeSet final : public Object {
return HANDLE_TYPE;
}
/// Name of the process
std::string name;
Segment& CodeSegment() {
return segments[0];
}
const Segment& CodeSegment() const {
return segments[0];
}
Segment& RODataSegment() {
return segments[1];
}
const Segment& RODataSegment() const {
return segments[1];
}
Segment& DataSegment() {
return segments[2];
}
const Segment& DataSegment() const {
return segments[2];
}
std::shared_ptr<std::vector<u8>> memory;
struct Segment {
size_t offset = 0;
VAddr addr = 0;
u32 size = 0;
};
Segment segments[3];
Segment& code = segments[0];
Segment& rodata = segments[1];
Segment& data = segments[2];
std::array<Segment, 3> segments;
VAddr entrypoint;
/// Name of the process
std::string name;
private:
CodeSet();
~CodeSet() override;
@@ -163,12 +183,11 @@ public:
// This makes deallocation and reallocation of holes fast and keeps process memory contiguous
// in the emulator address space, allowing Memory::GetPointer to be reasonably safe.
std::shared_ptr<std::vector<u8>> heap_memory;
// The left/right bounds of the address space covered by heap_memory.
VAddr heap_start = 0, heap_end = 0;
u64 heap_used = 0, linear_heap_used = 0, misc_memory_used = 0;
MemoryRegionInfo* memory_region = nullptr;
VAddr heap_start = 0;
VAddr heap_end = 0;
u64 heap_used = 0;
/// The Thread Local Storage area is allocated as processes create threads,
/// each TLS area is 0x200 bytes, so one page (0x1000) is split up in 8 parts, and each part
@@ -179,16 +198,9 @@ public:
std::string name;
VAddr GetLinearHeapAreaAddress() const;
VAddr GetLinearHeapBase() const;
VAddr GetLinearHeapLimit() const;
ResultVal<VAddr> HeapAllocate(VAddr target, u64 size, VMAPermission perms);
ResultCode HeapFree(VAddr target, u32 size);
ResultVal<VAddr> LinearAllocate(VAddr target, u32 size, VMAPermission perms);
ResultCode LinearFree(VAddr target, u32 size);
ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size);
ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size);

View File

@@ -5,7 +5,7 @@
#pragma once
#include "common/common_types.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/object.h"
namespace Kernel {

View File

@@ -2,8 +2,12 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <utility>
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/arm/arm_interface.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/hle/kernel/process.h"

View File

@@ -8,9 +8,11 @@
#include <vector>
#include "common/common_types.h"
#include "common/thread_queue_list.h"
#include "core/arm/arm_interface.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/thread.h"
class ARM_Interface;
namespace Kernel {
class Scheduler final {

View File

@@ -6,7 +6,7 @@
#include "common/assert.h"
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/server_port.h"
#include "core/hle/kernel/server_session.h"
#include "core/hle/kernel/thread.h"

View File

@@ -7,8 +7,9 @@
#include <memory>
#include <string>
#include <tuple>
#include <vector>
#include "common/common_types.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/wait_object.h"
namespace Kernel {

View File

@@ -5,6 +5,8 @@
#include <tuple>
#include <utility>
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/client_port.h"

View File

@@ -6,12 +6,12 @@
#include <memory>
#include <string>
#include "common/assert.h"
#include <vector>
#include "common/common_types.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/wait_object.h"
#include "core/hle/result.h"
#include "core/memory.h"
namespace Kernel {

View File

@@ -4,7 +4,7 @@
#pragma once
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/object.h"
namespace Kernel {

View File

@@ -3,10 +3,11 @@
// Refer to the license.txt file included.
#include <utility>
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/memory.h"
#include "core/hle/kernel/shared_memory.h"
#include "core/memory.h"
@@ -28,35 +29,17 @@ SharedPtr<SharedMemory> SharedMemory::Create(SharedPtr<Process> owner_process, u
shared_memory->other_permissions = other_permissions;
if (address == 0) {
// We need to allocate a block from the Linear Heap ourselves.
// We'll manually allocate some memory from the linear heap in the specified region.
MemoryRegionInfo* memory_region = GetMemoryRegion(region);
auto& linheap_memory = memory_region->linear_heap_memory;
ASSERT_MSG(linheap_memory->size() + size <= memory_region->size,
"Not enough space in region to allocate shared memory!");
shared_memory->backing_block = linheap_memory;
shared_memory->backing_block_offset = linheap_memory->size();
// Allocate some memory from the end of the linear heap for this region.
linheap_memory->insert(linheap_memory->end(), size, 0);
memory_region->used += size;
shared_memory->linear_heap_phys_address =
Memory::FCRAM_PADDR + memory_region->base +
static_cast<PAddr>(shared_memory->backing_block_offset);
// Increase the amount of used linear heap memory for the owner process.
if (shared_memory->owner_process != nullptr) {
shared_memory->owner_process->linear_heap_used += size;
}
shared_memory->backing_block = std::make_shared<std::vector<u8>>(size);
shared_memory->backing_block_offset = 0;
// Refresh the address mappings for the current process.
if (Core::CurrentProcess() != nullptr) {
Core::CurrentProcess()->vm_manager.RefreshMemoryBlockMappings(linheap_memory.get());
Core::CurrentProcess()->vm_manager.RefreshMemoryBlockMappings(
shared_memory->backing_block.get());
}
} else {
auto& vm_manager = shared_memory->owner_process->vm_manager;
// The memory is already available and mapped in the owner process.
auto vma = vm_manager.FindVMA(address);
ASSERT_MSG(vma != vm_manager.vma_map.end(), "Invalid memory address");
@@ -72,6 +55,7 @@ SharedPtr<SharedMemory> SharedMemory::Create(SharedPtr<Process> owner_process, u
}
shared_memory->base_address = address;
return shared_memory;
}
@@ -122,11 +106,6 @@ ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermi
VAddr target_address = address;
if (base_address == 0 && target_address == 0) {
// Calculate the address at which to map the memory block.
target_address = Memory::PhysicalToVirtualAddress(linear_heap_phys_address).value();
}
// Map the memory block into the target process
auto result = target_process->vm_manager.MapMemoryBlock(
target_address, backing_block, backing_block_offset, size, MemoryState::Shared);

View File

@@ -4,9 +4,12 @@
#pragma once
#include <memory>
#include <string>
#include <vector>
#include "common/common_types.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/process.h"
#include "core/hle/result.h"
@@ -108,9 +111,6 @@ public:
SharedPtr<Process> owner_process;
/// Address of shared memory block in the owner process if specified.
VAddr base_address;
/// Physical address of the shared memory block in the linear heap if no address was specified
/// during creation.
PAddr linear_heap_phys_address;
/// Backing memory for this shared memory block.
std::shared_ptr<std::vector<u8>> backing_block;
/// Offset into the backing block for this shared memory.

View File

@@ -5,7 +5,10 @@
#include <algorithm>
#include <cinttypes>
#include <iterator>
#include <mutex>
#include <vector>
#include "common/assert.h"
#include "common/logging/log.h"
#include "common/microprofile.h"
#include "common/string_util.h"
@@ -264,7 +267,7 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
LOG_TRACE(Kernel_SVC, "called info_id=0x{:X}, info_sub_id=0x{:X}, handle=0x{:08X}", info_id,
info_sub_id, handle);
auto& vm_manager = Core::CurrentProcess()->vm_manager;
const auto& vm_manager = Core::CurrentProcess()->vm_manager;
switch (static_cast<GetInfoType>(info_id)) {
case GetInfoType::AllowedCpuIdBitmask:

View File

@@ -4,8 +4,11 @@
#include <algorithm>
#include <cinttypes>
#include <list>
#include <vector>
#include <boost/optional.hpp>
#include <boost/range/algorithm_ext/erase.hpp>
#include "common/assert.h"
#include "common/common_types.h"
#include "common/logging/log.h"
@@ -17,9 +20,7 @@
#include "core/core_timing_util.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/memory.h"
#include "core/hle/kernel/mutex.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/result.h"
@@ -79,8 +80,8 @@ void Thread::Stop() {
wait_objects.clear();
// Mark the TLS slot in the thread's page as free.
u64 tls_page = (tls_address - Memory::TLS_AREA_VADDR) / Memory::PAGE_SIZE;
u64 tls_slot =
const u64 tls_page = (tls_address - Memory::TLS_AREA_VADDR) / Memory::PAGE_SIZE;
const u64 tls_slot =
((tls_address - Memory::TLS_AREA_VADDR) % Memory::PAGE_SIZE) / Memory::TLS_ENTRY_SIZE;
Core::CurrentProcess()->tls_slots[tls_page].reset(tls_slot);
}
@@ -250,13 +251,14 @@ void Thread::ResumeFromWait() {
* slot: The index of the first free slot in the indicated page.
* alloc_needed: Whether there's a need to allocate a new TLS page (All pages are full).
*/
std::tuple<u32, u32, bool> GetFreeThreadLocalSlot(std::vector<std::bitset<8>>& tls_slots) {
static std::tuple<std::size_t, std::size_t, bool> GetFreeThreadLocalSlot(
const std::vector<std::bitset<8>>& tls_slots) {
// Iterate over all the allocated pages, and try to find one where not all slots are used.
for (unsigned page = 0; page < tls_slots.size(); ++page) {
for (std::size_t page = 0; page < tls_slots.size(); ++page) {
const auto& page_tls_slots = tls_slots[page];
if (!page_tls_slots.all()) {
// We found a page with at least one free slot, find which slot it is
for (unsigned slot = 0; slot < page_tls_slots.size(); ++slot) {
for (std::size_t slot = 0; slot < page_tls_slots.size(); ++slot) {
if (!page_tls_slots.test(slot)) {
return std::make_tuple(page, slot, false);
}
@@ -331,42 +333,22 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
// Find the next available TLS index, and mark it as used
auto& tls_slots = owner_process->tls_slots;
bool needs_allocation = true;
u32 available_page; // Which allocated page has free space
u32 available_slot; // Which slot within the page is free
std::tie(available_page, available_slot, needs_allocation) = GetFreeThreadLocalSlot(tls_slots);
auto [available_page, available_slot, needs_allocation] = GetFreeThreadLocalSlot(tls_slots);
if (needs_allocation) {
// There are no already-allocated pages with free slots, lets allocate a new one.
// TLS pages are allocated from the BASE region in the linear heap.
MemoryRegionInfo* memory_region = GetMemoryRegion(MemoryRegion::BASE);
auto& linheap_memory = memory_region->linear_heap_memory;
if (linheap_memory->size() + Memory::PAGE_SIZE > memory_region->size) {
LOG_ERROR(Kernel_SVC,
"Not enough space in region to allocate a new TLS page for thread");
return ERR_OUT_OF_MEMORY;
}
size_t offset = linheap_memory->size();
// Allocate some memory from the end of the linear heap for this region.
linheap_memory->insert(linheap_memory->end(), Memory::PAGE_SIZE, 0);
memory_region->used += Memory::PAGE_SIZE;
owner_process->linear_heap_used += Memory::PAGE_SIZE;
tls_slots.emplace_back(0); // The page is completely available at the start
available_page = static_cast<u32>(tls_slots.size() - 1);
available_page = tls_slots.size() - 1;
available_slot = 0; // Use the first slot in the new page
auto& vm_manager = owner_process->vm_manager;
vm_manager.RefreshMemoryBlockMappings(linheap_memory.get());
// Allocate some memory from the end of the linear heap for this region.
const size_t offset = thread->tls_memory->size();
thread->tls_memory->insert(thread->tls_memory->end(), Memory::PAGE_SIZE, 0);
auto& vm_manager = owner_process->vm_manager;
vm_manager.RefreshMemoryBlockMappings(thread->tls_memory.get());
// Map the page to the current process' address space.
// TODO(Subv): Find the correct MemoryState for this region.
vm_manager.MapMemoryBlock(Memory::TLS_AREA_VADDR + available_page * Memory::PAGE_SIZE,
linheap_memory, offset, Memory::PAGE_SIZE,
thread->tls_memory, 0, Memory::PAGE_SIZE,
MemoryState::ThreadLocal);
}

View File

@@ -4,15 +4,14 @@
#pragma once
#include <functional>
#include <memory>
#include <string>
#include <unordered_map>
#include <vector>
#include <boost/container/flat_map.hpp>
#include <boost/container/flat_set.hpp>
#include "common/common_types.h"
#include "core/arm/arm_interface.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/wait_object.h"
#include "core/hle/result.h"
@@ -266,6 +265,8 @@ public:
private:
Thread();
~Thread() override;
std::shared_ptr<std::vector<u8>> tls_memory = std::make_shared<std::vector<u8>>();
};
/**
@@ -288,12 +289,6 @@ Thread* GetCurrentThread();
*/
void WaitCurrentThread_Sleep();
/**
* Waits the current thread from an ArbitrateAddress call
* @param wait_address Arbitration address used to resume from wait
*/
void WaitCurrentThread_ArbitrateAddress(VAddr wait_address);
/**
* Stops the current thread and removes it from the thread_list
*/

View File

@@ -8,7 +8,7 @@
#include "core/core_timing.h"
#include "core/core_timing_util.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/kernel/timer.h"

View File

@@ -5,7 +5,7 @@
#pragma once
#include "common/common_types.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/wait_object.h"
namespace Kernel {

View File

@@ -2,6 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <iterator>
#include <utility>
#include "common/assert.h"
@@ -175,9 +176,9 @@ VMManager::VMAIter VMManager::Unmap(VMAIter vma_handle) {
ResultCode VMManager::UnmapRange(VAddr target, u64 size) {
CASCADE_RESULT(VMAIter vma, CarveVMARange(target, size));
VAddr target_end = target + size;
const VAddr target_end = target + size;
VMAIter end = vma_map.end();
const VMAIter end = vma_map.end();
// The comparison against the end of the range must be done using addresses since VMAs can be
// merged during this process, causing invalidation of the iterators.
while (vma != end && vma->second.base < target_end) {
@@ -207,9 +208,9 @@ VMManager::VMAHandle VMManager::Reprotect(VMAHandle vma_handle, VMAPermission ne
ResultCode VMManager::ReprotectRange(VAddr target, u64 size, VMAPermission new_perms) {
CASCADE_RESULT(VMAIter vma, CarveVMARange(target, size));
VAddr target_end = target + size;
const VAddr target_end = target + size;
VMAIter end = vma_map.end();
const VMAIter end = vma_map.end();
// The comparison against the end of the range must be done using addresses since VMAs can be
// merged during this process, causing invalidation of the iterators.
while (vma != end && vma->second.base < target_end) {
@@ -258,14 +259,14 @@ ResultVal<VMManager::VMAIter> VMManager::CarveVMA(VAddr base, u64 size) {
return ERR_INVALID_ADDRESS;
}
VirtualMemoryArea& vma = vma_handle->second;
const VirtualMemoryArea& vma = vma_handle->second;
if (vma.type != VMAType::Free) {
// Region is already allocated
return ERR_INVALID_ADDRESS_STATE;
}
u64 start_in_vma = base - vma.base;
u64 end_in_vma = start_in_vma + size;
const VAddr start_in_vma = base - vma.base;
const VAddr end_in_vma = start_in_vma + size;
if (end_in_vma > vma.size) {
// Requested allocation doesn't fit inside VMA
@@ -288,17 +289,16 @@ ResultVal<VMManager::VMAIter> VMManager::CarveVMARange(VAddr target, u64 size) {
ASSERT_MSG((size & Memory::PAGE_MASK) == 0, "non-page aligned size: 0x{:016X}", size);
ASSERT_MSG((target & Memory::PAGE_MASK) == 0, "non-page aligned base: 0x{:016X}", target);
VAddr target_end = target + size;
const VAddr target_end = target + size;
ASSERT(target_end >= target);
ASSERT(target_end <= MAX_ADDRESS);
ASSERT(size > 0);
VMAIter begin_vma = StripIterConstness(FindVMA(target));
VMAIter i_end = vma_map.lower_bound(target_end);
for (auto i = begin_vma; i != i_end; ++i) {
if (i->second.type == VMAType::Free) {
return ERR_INVALID_ADDRESS_STATE;
}
const VMAIter i_end = vma_map.lower_bound(target_end);
if (std::any_of(begin_vma, i_end,
[](const auto& entry) { return entry.second.type == VMAType::Free; })) {
return ERR_INVALID_ADDRESS_STATE;
}
if (target != begin_vma->second.base) {
@@ -346,7 +346,7 @@ VMManager::VMAIter VMManager::SplitVMA(VMAIter vma_handle, u64 offset_in_vma) {
}
VMManager::VMAIter VMManager::MergeAdjacent(VMAIter iter) {
VMAIter next_vma = std::next(iter);
const VMAIter next_vma = std::next(iter);
if (next_vma != vma_map.end() && iter->second.CanBeMergedWith(next_vma->second)) {
iter->second.size += next_vma->second.size;
vma_map.erase(next_vma);
@@ -382,22 +382,22 @@ void VMManager::UpdatePageTableForVMA(const VirtualMemoryArea& vma) {
}
}
u64 VMManager::GetTotalMemoryUsage() {
u64 VMManager::GetTotalMemoryUsage() const {
LOG_WARNING(Kernel, "(STUBBED) called");
return 0xF8000000;
}
u64 VMManager::GetTotalHeapUsage() {
u64 VMManager::GetTotalHeapUsage() const {
LOG_WARNING(Kernel, "(STUBBED) called");
return 0x0;
}
VAddr VMManager::GetAddressSpaceBaseAddr() {
VAddr VMManager::GetAddressSpaceBaseAddr() const {
LOG_WARNING(Kernel, "(STUBBED) called");
return 0x8000000;
}
u64 VMManager::GetAddressSpaceSize() {
u64 VMManager::GetAddressSpaceSize() const {
LOG_WARNING(Kernel, "(STUBBED) called");
return MAX_ADDRESS;
}

View File

@@ -190,16 +190,16 @@ public:
void LogLayout() const;
/// Gets the total memory usage, used by svcGetInfo
u64 GetTotalMemoryUsage();
u64 GetTotalMemoryUsage() const;
/// Gets the total heap usage, used by svcGetInfo
u64 GetTotalHeapUsage();
u64 GetTotalHeapUsage() const;
/// Gets the total address space base address, used by svcGetInfo
VAddr GetAddressSpaceBaseAddr();
VAddr GetAddressSpaceBaseAddr() const;
/// Gets the total address space address size, used by svcGetInfo
u64 GetAddressSpaceSize();
u64 GetAddressSpaceSize() const;
/// Each VMManager has its own page table, which is set as the main one when the owning process
/// is scheduled.

View File

@@ -5,11 +5,8 @@
#include <algorithm>
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/memory.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/resource_limit.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/kernel/timer.h"

View File

@@ -7,7 +7,7 @@
#include <vector>
#include <boost/smart_ptr/intrusive_ptr.hpp>
#include "common/common_types.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/object.h"
namespace Kernel {

View File

@@ -10,6 +10,7 @@
#include "core/hle/service/acc/acc_su.h"
#include "core/hle/service/acc/acc_u0.h"
#include "core/hle/service/acc/acc_u1.h"
#include "core/settings.h"
namespace Service::Account {
@@ -31,13 +32,14 @@ struct ProfileBase {
};
static_assert(sizeof(ProfileBase) == 0x38, "ProfileBase structure has incorrect size");
// TODO(ogniK): Generate a real user id based on username, md5(username) maybe?
static constexpr u128 DEFAULT_USER_ID{1ull, 0ull};
class IProfile final : public ServiceFramework<IProfile> {
public:
explicit IProfile(u128 user_id) : ServiceFramework("IProfile"), user_id(user_id) {
static const FunctionInfo functions[] = {
{0, nullptr, "Get"},
{0, &IProfile::Get, "Get"},
{1, &IProfile::GetBase, "GetBase"},
{10, nullptr, "GetImageSize"},
{11, nullptr, "LoadImage"},
@@ -46,14 +48,36 @@ public:
}
private:
void Get(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_ACC, "(STUBBED) called");
ProfileBase profile_base{};
profile_base.user_id = user_id;
if (Settings::values.username.size() > profile_base.username.size()) {
std::copy_n(Settings::values.username.begin(), profile_base.username.size(),
profile_base.username.begin());
} else {
std::copy(Settings::values.username.begin(), Settings::values.username.end(),
profile_base.username.begin());
}
IPC::ResponseBuilder rb{ctx, 16};
rb.Push(RESULT_SUCCESS);
rb.PushRaw(profile_base);
}
void GetBase(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_ACC, "(STUBBED) called");
// TODO(Subv): Retrieve this information from somewhere.
ProfileBase profile_base{};
profile_base.user_id = user_id;
profile_base.username = {'y', 'u', 'z', 'u'};
if (Settings::values.username.size() > profile_base.username.size()) {
std::copy_n(Settings::values.username.begin(), profile_base.username.size(),
profile_base.username.begin());
} else {
std::copy(Settings::values.username.begin(), Settings::values.username.end(),
profile_base.username.begin());
}
IPC::ResponseBuilder rb{ctx, 16};
rb.Push(RESULT_SUCCESS);
rb.PushRaw(profile_base);

View File

@@ -11,6 +11,9 @@
#include "core/hle/service/am/am.h"
#include "core/hle/service/am/applet_ae.h"
#include "core/hle/service/am/applet_oe.h"
#include "core/hle/service/am/idle.h"
#include "core/hle/service/am/omm.h"
#include "core/hle/service/am/spsm.h"
#include "core/hle/service/apm/apm.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/nvflinger/nvflinger.h"
@@ -649,7 +652,8 @@ void IApplicationFunctions::GetDesiredLanguage(Kernel::HLERequestContext& ctx) {
// TODO(bunnei): This should be configurable
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push(static_cast<u64>(Service::Set::LanguageCode::EN_US));
rb.Push(
static_cast<u64>(Service::Set::GetLanguageCodeFromIndex(Settings::values.language_index)));
LOG_DEBUG(Service_AM, "called");
}
@@ -689,6 +693,9 @@ void InstallInterfaces(SM::ServiceManager& service_manager,
std::shared_ptr<NVFlinger::NVFlinger> nvflinger) {
std::make_shared<AppletAE>(nvflinger)->InstallAsService(service_manager);
std::make_shared<AppletOE>(nvflinger)->InstallAsService(service_manager);
std::make_shared<IdleSys>()->InstallAsService(service_manager);
std::make_shared<OMM>()->InstallAsService(service_manager);
std::make_shared<SPSM>()->InstallAsService(service_manager);
}
IHomeMenuFunctions::IHomeMenuFunctions() : ServiceFramework("IHomeMenuFunctions") {

View File

@@ -0,0 +1,24 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/hle/service/am/idle.h"
namespace Service::AM {
IdleSys::IdleSys() : ServiceFramework{"idle:sys"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "GetAutoPowerDownEvent"},
{1, nullptr, "Unknown1"},
{2, nullptr, "Unknown2"},
{3, nullptr, "Unknown3"},
{4, nullptr, "Unknown4"},
{5, nullptr, "Unknown5"},
};
// clang-format on
RegisterHandlers(functions);
}
} // namespace Service::AM

View 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
#include "core/hle/service/service.h"
namespace Service::AM {
class IdleSys final : public ServiceFramework<IdleSys> {
public:
explicit IdleSys();
};
} // namespace Service::AM

View File

@@ -0,0 +1,42 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/hle/service/am/omm.h"
namespace Service::AM {
OMM::OMM() : ServiceFramework{"omm"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "GetOperationMode"},
{1, nullptr, "GetOperationModeChangeEvent"},
{2, nullptr, "EnableAudioVisual"},
{3, nullptr, "DisableAudioVisual"},
{4, nullptr, "EnterSleepAndWait"},
{5, nullptr, "GetCradleStatus"},
{6, nullptr, "FadeInDisplay"},
{7, nullptr, "FadeOutDisplay"},
{8, nullptr, "Unknown1"},
{9, nullptr, "Unknown2"},
{10, nullptr, "Unknown3"},
{11, nullptr, "Unknown4"},
{12, nullptr, "Unknown5"},
{13, nullptr, "Unknown6"},
{14, nullptr, "Unknown7"},
{15, nullptr, "Unknown8"},
{16, nullptr, "Unknown9"},
{17, nullptr, "Unknown10"},
{18, nullptr, "Unknown11"},
{19, nullptr, "Unknown12"},
{20, nullptr, "Unknown13"},
{21, nullptr, "Unknown14"},
{22, nullptr, "Unknown15"},
{23, nullptr, "Unknown16"},
};
// clang-format on
RegisterHandlers(functions);
}
} // namespace Service::AM

View 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
#include "core/hle/service/service.h"
namespace Service::AM {
class OMM final : public ServiceFramework<OMM> {
public:
explicit OMM();
};
} // namespace Service::AM

View File

@@ -0,0 +1,30 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/hle/service/am/spsm.h"
namespace Service::AM {
SPSM::SPSM() : ServiceFramework{"spsm"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "GetState"},
{1, nullptr, "SleepSystemAndWaitAwake"},
{2, nullptr, "Unknown1"},
{3, nullptr, "Unknown2"},
{4, nullptr, "GetNotificationMessageEventHandle"},
{5, nullptr, "Unknown3"},
{6, nullptr, "Unknown4"},
{7, nullptr, "Unknown5"},
{8, nullptr, "AnalyzePerformanceLogForLastSleepWakeSequence"},
{9, nullptr, "ChangeHomeButtonLongPressingTime"},
{10, nullptr, "Unknown6"},
{11, nullptr, "Unknown7"},
};
// clang-format on
RegisterHandlers(functions);
}
} // namespace Service::AM

View 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
#include "core/hle/service/service.h"
namespace Service::AM {
class SPSM final : public ServiceFramework<SPSM> {
public:
explicit SPSM();
};
} // namespace Service::AM

View File

@@ -0,0 +1,45 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/hle/service/audio/audctl.h"
namespace Service::Audio {
AudCtl::AudCtl() : ServiceFramework{"audctl"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "GetTargetVolume"},
{1, nullptr, "SetTargetVolume"},
{2, nullptr, "GetTargetVolumeMin"},
{3, nullptr, "GetTargetVolumeMax"},
{4, nullptr, "IsTargetMute"},
{5, nullptr, "SetTargetMute"},
{6, nullptr, "IsTargetConnected"},
{7, nullptr, "SetDefaultTarget"},
{8, nullptr, "GetDefaultTarget"},
{9, nullptr, "GetAudioOutputMode"},
{10, nullptr, "SetAudioOutputMode"},
{11, nullptr, "SetForceMutePolicy"},
{12, nullptr, "GetForceMutePolicy"},
{13, nullptr, "GetOutputModeSetting"},
{14, nullptr, "SetOutputModeSetting"},
{15, nullptr, "SetOutputTarget"},
{16, nullptr, "SetInputTargetForceEnabled"},
{17, nullptr, "SetHeadphoneOutputLevelMode"},
{18, nullptr, "GetHeadphoneOutputLevelMode"},
{19, nullptr, "AcquireAudioVolumeUpdateEventForPlayReport"},
{20, nullptr, "AcquireAudioOutputDeviceUpdateEventForPlayReport"},
{21, nullptr, "GetAudioOutputTargetForPlayReport"},
{22, nullptr, "NotifyHeadphoneVolumeWarningDisplayedEvent"},
{23, nullptr, "SetSystemOutputMasterVolume"},
{24, nullptr, "GetSystemOutputMasterVolume"},
{25, nullptr, "GetAudioVolumeDataForPlayReport"},
{26, nullptr, "UpdateHeadphoneSettings"},
};
// clang-format on
RegisterHandlers(functions);
}
} // namespace Service::Audio

Some files were not shown because too many files have changed in this diff Show More