Compare commits

...

283 Commits

Author SHA1 Message Date
David Marcec
f9d8f20d7e fixed size calculation error for decode interleaved 2018-07-29 12:19:17 +10:00
David Marcec
f6e5fb17d7 removed externals/ from the submodule name 2018-07-29 10:57:30 +10:00
David Marcec
fd53a7b0a1 string->cstring and made WorkerBufferSize a static local function 2018-07-29 10:33:56 +10:00
David Marcec
d2e361533c const 2018-07-29 10:24:38 +10:00
David Marcec
afa72e1725 Use struct deleter instead 2018-07-29 10:19:23 +10:00
David Marcec
86c41aabd7 Fixed opus cmake, changed std::unique_ptr 2018-07-29 09:44:58 +10:00
David Marcec
a7c8064927 Used unique_ptr
unique_ptr doesn't seem to like c structs, so using a void * and casting it to an OpusDecoder
2018-07-29 01:38:39 +10:00
David Marcec
ade5096f84 Added new hwopus commands 2018-07-29 00:41:21 +10:00
bunnei
e1d66ea794 Merge pull request #842 from bunnei/audio-core
Initial implementation of Audio Core
2018-07-27 20:25:32 -07:00
bunnei
f1c519f2cb audout: Implement IAudioOut interface with AudioCore. 2018-07-27 22:55:39 -04:00
bunnei
2a742229ee core: Add AudioCore to global state. 2018-07-27 22:33:37 -04:00
bunnei
ab756fd068 audio_core: Add initial code for keeping track of audout state. 2018-07-27 22:33:31 -04:00
bunnei
abb4894188 Merge pull request #696 from DarkLordZach/romfs
RomFS Extraction
2018-07-27 19:10:05 -07:00
Zach Hilman
906d785c73 RomFS Extraction 2018-07-27 18:14:03 -04:00
bunnei
0191a1e526 Merge pull request #845 from lioncash/nfc
service: Add nfc services
2018-07-27 14:29:27 -07:00
bunnei
2128ab2d21 Merge pull request #839 from FearlessTobi/actually-port-3594
Port #3594 from Citra: "citra_qt: Add Continue/Pause & Toggle Speed Limit hotkeys"
2018-07-27 13:06:56 -07:00
bunnei
833ebbb626 Merge pull request #844 from lioncash/lbl
service: Add the lbl service
2018-07-27 13:06:11 -07:00
bunnei
d2749ef0ed Merge pull request #841 from lioncash/btdrv
service: Add the btdrv service
2018-07-27 13:05:36 -07:00
Lioncash
50dadc33e3 service/nfc: Implement Create[x]Interface functions
These simply return the respective interface.
2018-07-27 15:12:08 -04:00
Lioncash
04d144aa40 service: Add nfc services
Adds the skeleton of the nfc service based off the information provided
on Switch Brew.
2018-07-27 14:50:24 -04:00
Lioncash
ea8dd8b650 service/lbl: Implement EnableVrMode, DisableVrMode and GetVrMode
Implements these functions according to the information available on
Switch Brew.
2018-07-27 14:20:42 -04:00
Lioncash
c2c543e8f7 service: Add the lbl service
Adds the skeleton of the lbl service based off the information provided
by Switch Brew.
2018-07-27 14:20:07 -04:00
bunnei
dc4e5f9159 Merge pull request #837 from lioncash/priv
kernel/timer: Make data members private where applicable
2018-07-27 07:18:50 -07:00
bunnei
e247bd577c Merge pull request #833 from lioncash/irs
service/hid: Add missing services
2018-07-27 07:18:29 -07:00
Lioncash
f49248437e service: Add the btdrv service
Adds the skeleton for the btdrv service based off the information provided by Switch Brew
2018-07-26 18:06:17 -04:00
Lioncash
1121622dc1 service/hid: Add the hidbus, hid:dbg, hid:sys, and hid:tmp services 2018-07-26 15:05:42 -04:00
Lioncash
7550c2c866 service/hid: Add the xcd:sys service 2018-07-26 15:05:42 -04:00
Lioncash
82cb5f030d service/hid: Add irs services 2018-07-26 15:05:24 -04:00
bunnei
ec2b7cdb1d Merge pull request #836 from FearlessTobi/port-3594
Port #3665 from Citra: "frontend: Log Citra version"
2018-07-26 12:04:30 -07:00
bunnei
f1fbb476df Merge pull request #835 from FearlessTobi/port-minor-prs
Port #3641 and #3702 from Citra (Small changes to default_ini and gitignore)
2018-07-26 12:03:56 -07:00
bunnei
8440b814bc Merge pull request #834 from lioncash/grc
service: Add the grc:c service
2018-07-26 12:03:30 -07:00
bunnei
41d7b020a8 Merge pull request #832 from lioncash/nim
service: Add the nim services
2018-07-26 12:02:29 -07:00
bunnei
e4191b624c Merge pull request #831 from lioncash/ldn
service: Add ldn services
2018-07-26 11:52:42 -07:00
bunnei
40eb243f86 Merge pull request #830 from lioncash/socket
service/sockets: Add missing socket services
2018-07-26 11:51:11 -07:00
bunnei
e013fdc2b2 Merge pull request #808 from lioncash/mem-dedup
video_core/memory_manager: Avoid repeated unnecessary page slot lookups
2018-07-26 11:50:27 -07:00
bunnei
5a27f4e4cc Merge pull request #829 from Subv/r16f_rt
GPU: Allow using R16F as a render target format.
2018-07-26 11:44:37 -07:00
fearlessTobi
18c2c96927 Port #3594 from Citra 2018-07-26 16:09:52 +02:00
Lioncash
b28af1f6c9 service: Add the grc:c service
Adds the basic skeleton for the grc:c service based off the information
provided by Switch Brew.
2018-07-26 09:53:58 -04:00
Subv
f85cff0f48 GPU: Allow using R16F as a render target format. 2018-07-26 08:52:21 -05:00
Lioncash
0cd843151f kernel/timer: Make data members private where applicable
Instead, we can just expose functions that return the queryable state
instead of letting anything modify it.
2018-07-26 09:51:44 -04:00
fearlessTobi
6f05a57712 Port #3665 from Citra 2018-07-26 15:51:14 +02:00
fearlessTobi
9d2a1c6f12 Port #3702 from Citra 2018-07-26 15:35:24 +02:00
fearlessTobi
7a2acc86e6 Port #3641 from Citra 2018-07-26 15:35:10 +02:00
Lioncash
f916611e32 service: Add the nim services
Adds the skeleton for the nim services based off information from Switch
Brew.
2018-07-26 02:47:06 -04:00
Lioncash
8781beaf0d service: Add ldn services
Adds ldn services based off information provided by Switch Brew.
2018-07-26 01:48:06 -04:00
bunnei
57cd80c410 Merge pull request #827 from lioncash/log
service/lm: Minor changes
2018-07-25 22:30:43 -07:00
bunnei
31642ae2ee Merge pull request #825 from greggameplayer/R16_G16
Implement R16_G16
2018-07-25 22:30:25 -07:00
Lioncash
3f5dfc4d96 service/sockets: Add ethc:c and ethc:i services 2018-07-26 01:07:21 -04:00
Lioncash
165e7645e1 service/sockets: Add missing bsdcfg socket service 2018-07-26 01:00:15 -04:00
Sebastian Valle
1958d07d7d Merge pull request #828 from lioncash/ldr
service: Add ldr services
2018-07-25 23:44:13 -05:00
Sebastian Valle
8ac52c6b56 Merge pull request #826 from lioncash/erpt
service: Add erpt and eupld services
2018-07-25 23:43:47 -05:00
Sebastian Valle
0e05f98be2 Merge pull request #823 from lioncash/nifm
service/nifm: Deduplicate interface code
2018-07-25 23:43:03 -05:00
Lioncash
3fb124961e service: Add ldr services
Adds the skeleton for the ldr-related services based off the information
provided on Switch Brew.
2018-07-25 23:11:03 -04:00
Lioncash
91d86df920 lm: Move LM's class declaration into the cpp file
This isn't used directly outside of this translation unit, so we can
hide it from external use.
2018-07-25 22:40:16 -04:00
Lioncash
6f4d3d8163 lm: Amend names of Initialize() in Logger and Initialize() in LM
Amends these to match the information on Switch Brew.
2018-07-25 22:39:39 -04:00
Lioncash
8650be1020 lm: Add missing function entry to Logger's function table 2018-07-25 22:26:32 -04:00
Lioncash
7458e71f24 service: Add eupld services
Adds the skeleton for the eupld services based off information on Switch
Brew.
2018-07-25 22:13:43 -04:00
Lioncash
821f2c03cb service: Add the erpt services
Adds the basic skeleton of the erpt service based off information on
Switch Brew.
2018-07-25 22:13:39 -04:00
bunnei
b0adb9a3d9 Merge pull request #824 from lioncash/nvdrv
service/nvdrv: Minor changes
2018-07-25 19:02:08 -07:00
bunnei
851089b482 Merge pull request #822 from lioncash/pm
service: Add pm services
2018-07-25 19:01:28 -07:00
Unknown
4672a01cbf Implement R16_G16
correct trailing white spaces


Delete tabs


correct placement
Add RG16F & RG16UI & RG16I & RG16S PixelFormats
Return correct data according to changes done previously
correct PixelFormat declaration
correct coding style error
correct coding style error part 2
correct RG16S Declaration error
correct alignment
2018-07-26 02:01:29 +02:00
Mat M
d245610939 Merge pull request #820 from lioncash/es
service: Add the es service
2018-07-25 18:51:59 -04:00
Mat M
19b5ae5a25 Merge pull request #821 from lioncash/wait
wait_tree: Add missing switch case for WaitTreeThread::GetText()
2018-07-25 18:45:01 -04:00
bunnei
c88382517c Merge pull request #819 from Subv/srgb
GPU: Use the right texture format for sRGBA framebuffers.
2018-07-25 14:47:26 -07:00
Lioncash
f9951352f6 service/nvdrv: Take std::string in Open() by const reference
Avoids copies from being made, since the string is only ever used for
lookup, the data is never transfered anywhere.

Ideally, we'd use a std::string_view here, but devices is a
std::unordered_map, not a std::map, so we can't use heterogenous lookup
here.
2018-07-25 17:37:43 -04:00
Lioncash
a2cd07d094 service/nvdrv: Use std::move where applicable
Avoids unnecessary reference count increments and decrements.

In one case, we don't need to make a shared_ptr copy at all,
just to call a member function.
2018-07-25 17:31:08 -04:00
Lioncash
85ed42a1d2 service/nifm: Deduplicate interface code
Rather than having the same code for each nifm service variant, we can
centralize it on one class and get rid of a bit of extra code.
2018-07-25 17:18:41 -04:00
Lioncash
c664f8a257 service: Add pm services
Adds the skeleton for the process management services based off
information on Switch Brew.
2018-07-25 16:57:16 -04:00
Lioncash
2474340684 wait_tree: Add missing switch case for WaitTreeThread::GetText()
We were missing the enum entry for WaitIPC
2018-07-25 15:50:41 -04:00
Lioncash
4175712976 service: Add the es service
Adds the skeleton for the ETicket service based off the information on
Switch Brew
2018-07-25 15:36:55 -04:00
Mat M
1e4935c3a0 Merge pull request #801 from lioncash/time
time: Add the time:a service
2018-07-25 15:08:33 -04:00
Mat M
d3fd0351a7 Merge pull request #804 from lioncash/log
svc: Log parameters in SetMemoryAttribute()
2018-07-25 14:43:24 -04:00
Lioncash
3c1cb3b11e time: Add the time:a service
Given we already have time:s and time:u, we should also have time:a
2018-07-25 14:42:04 -04:00
bunnei
657bd113e3 Merge pull request #803 from MerryMage/core_timing_util
core_timing: Split off utility functions into core_timing_util
2018-07-25 11:09:31 -07:00
bunnei
52cb5dcf0f Merge pull request #802 from lioncash/unreach
wait_tree: Silence warning about all code paths not returning a value
2018-07-25 11:08:44 -07:00
bunnei
5c42cadbc9 Merge pull request #800 from lioncash/set
set_sys: Implement SetColorSetId()
2018-07-25 10:25:29 -07:00
bunnei
f445464baf Merge pull request #813 from Subv/z24_s8_tex
GPU: Allow the use of Z24S8 as a texture format.
2018-07-25 10:25:01 -07:00
Subv
c5b838aeef GPU: Use the right texture format for sRGBA framebuffers. 2018-07-25 09:52:39 -05:00
Subv
ee8123bf13 GPU: Allow the use of Z24S8 as a texture format. 2018-07-25 09:41:24 -05:00
bunnei
0686183c3e Merge pull request #816 from Subv/z32_s8
GPU: Implemented the Z32_S8_X24 depth buffer format.
2018-07-25 07:37:00 -07:00
bunnei
af787744ab Merge pull request #815 from Subv/z32f_tex
GPU: Allow using Z32 as a texture format.
2018-07-25 07:33:09 -07:00
bunnei
704824d50a Merge pull request #814 from Subv/rt_r8
GPU: Allow the usage of R8 as a render target format.
2018-07-25 07:32:18 -07:00
bunnei
184324c2f0 Merge pull request #818 from MerryMage/dynarmic
externals: Update dynarmic to 98e2380
2018-07-25 07:30:50 -07:00
MerryMage
abc5b3347d externals: Update dynarmic to 98e2380
98e2380 fuzz_with_unicorn: Disable testing of FDIV
041b7d5 block_of_code: Add ABI_PARAMS array
2a2371c A64: Implement MLA, MLS (by element), vector single/double variant
78c640a A64: Implement FMLS (vector), single/double variant
b6b6993 emit_x64_vector_floating_point: Specify NanHandler::function_type explicitly
4b9d12a emit_x64_vector_floating_point: ChooseOnFsize arguments maybe_unused
b1e3616 IR: Implement FPVectorNeg
4343612 A64: Implement FMLA (vector), single/double variant
93eeb25 IR: Implement FPVectorMulAdd
57e5c7e emit_x64_vector_floating_point: Standardize naming scheme
bcb9e41 emit_x64_floating_point: Simplify indexers
83aa585 emit_x64_vector_floating_point: Simplify EmitVectorOperation*
f4087c8 mp: rename mp.h to mp/function_info.h
1864090 emit_x64_vector: Slightly improve ArithmeticShiftRightByte
e048441 emit_x64_vector: Simplify VectorShuffleImpl
ff025e8 IR: Implement A64OrQC
6fac68d A64: Implement UQSHRN, UQRSHRN (vector)
5a8d9c3 emit_x64_vector: -0x80000000 isn't -0x80000000
759289e A64: Implement UQXTN (vector)
2a96281 emit_x64_vector: Fix non-SSE4.1 saturated narrowing reconstruction comparison
0682353 A64: Implement SQXTN (vector)
6c5229e emit_x64_vector: packusdw reqiures SSE4.1
158d9b1 A64: Implement SQSHRUN, SQRSHRUN (vector)
f886013 simd_shift_by_immediate: Simplify ShiftRight
d9b59c6 A64: Implement SQXTUN
50fe28b microinstruction: Reorganize FPSCR related instruction queries
d9d036a microinstruction: Add missing FP scalar opcodes to ReadsFromFPSCR() and WritesToFPSCR()
db96163 u128: Make Bit() a const-qualified member function
f7052ae A64: Implement FRSQRTS (vector), single/double variant
0925ef6 A64: Implement FRSQRTE (vector), single/double variant
f4cbbe3 A64: Implement FRSQRTS (scalar), single/double variant
4ef864e IR: Implement FPRSqrtStepFused
9dffeeb fp: Implement FPRSqrtStepFused
aa04556 fp: Implement FPNeg
cbde1c5 process_nan: Add two operand variant
1ec2663 A64: Implement FMAXP, FMINP, FMAXNMP and FMINNMP's scalar double/single-precision variant
027ddf9 emit_x64_floating_point: Fixup special NaN case in FMA FPMulAdd implementation
75a9f77 fp: Use a forward declaration in fused.h
1ee1630 u128: Implement comparison operators in terms of one another
3b77f48 tests: Print cpu info
bed3cc0 u128: StickyLogicalShiftRight requires special-casing for amount == 64
15d04f4 A64: Implement FMLA and FMLS (by element)'s double/single-precision scalar variant
7cfccdf A64: Implement FMUL (by element)'s scalar double/single-precision variant
7d2d62e (fpmuladd) emit_x64_floating_point: Implement accurate fallback for FPMulAdd{32,64}
a599eac fp: Implement FPMulAdd
d70b90e process_nan: Add FPProcessNaNs3
38ef0e0 block_of_code: Add SysV ABI fifth and sixth parameters
8e2ff56 u128: Add StickyLogicalShiftRight
3b337df u128: Add Multiply64To128
8219075 u128: Add u128::Bit
a574dcb u128: Add comparison operators
391d6d4 unpacked: Use ResidualErrorOnRightShift in FPRoundBase
5e0cf9c fp: Remove MantissaT
8c0a84c FPRSqrtEstimate: Improve documentation of RecipSqrtEstimate
c41d855 FPRSqrtEstimate: Deduplicate array bounds
4cf055b A64: Implement FMAXV, FMINV, FMAXNMV, and FMINNMV
bf24f0f FPRSqrtEstimate: Use forward declarations where applicable
206230e translate: Return by bool in helpers where applicable
346b725 Simplify fallback case for EmitVectorSetElement64()
2c34e1d emit_x64_floating_point: s/Esimate/Estimate/
5213fb6 simd_scalar_two_register_misc: Implement FRSQRTE, scalar variant
7ed089f IR: Implement FPRSqrtEstimate
cd2e286 simd_vector_x_indexed_element: Implement FMUL (by element), vector variant
2018-07-25 14:26:51 +01:00
bunnei
a6ea6febc9 Merge pull request #809 from lioncash/rasterizer
gl_rasterizer: Minor cleanup
2018-07-24 19:31:34 -07:00
bunnei
e0106a7d68 Merge pull request #811 from Subv/code_address_assert
GPU: Remove the assert that required the CODE_ADDRESS to be 0.
2018-07-24 19:31:09 -07:00
Subv
daf2504d31 GPU: Implemented the Z32_S8_X24 depth buffer format. 2018-07-24 20:41:40 -05:00
Subv
f747a7e35d GPU: Allow using Z32 as a texture format. 2018-07-24 19:54:23 -05:00
Subv
4f574201ea GPU: Allow the usage of R8 as a render target format. 2018-07-24 19:49:36 -05:00
bunnei
340771ccd7 Merge pull request #806 from lioncash/friend
friend: Deduplicate interfaces
2018-07-24 17:42:16 -07:00
bunnei
5d4ad999cf Merge pull request #810 from Subv/r16
GPU: Implemented the R16 and R16F texture formats.
2018-07-24 17:41:02 -07:00
bunnei
ea0bc8c002 Merge pull request #805 from lioncash/sign
svc: Resolve sign comparison warnings in WaitSynchronization()
2018-07-24 12:50:03 -07:00
bunnei
933c9ee853 Merge pull request #807 from lioncash/unused
deconstructed_rom_directory: Remove unused FindRomFS() function
2018-07-24 12:49:40 -07:00
Subv
8f2c4191ab GPU: Remove the assert that required the CODE_ADDRESS to be 0.
Games usually just leave it at 0 but nouveau sets it to something else. This already works fine, the assert is useless.
2018-07-24 13:54:12 -05:00
Subv
4cc1e180ec GPU: Implemented the R16 and R16F texture formats. 2018-07-24 13:39:16 -05:00
Lioncash
0162f8b3a7 gl_rasterizer: Replace magic number with GL_INVALID_INDEX in SetupConstBuffers()
This is just the named constant that OpenGL provides, so we can use that
instead of using a literal -1
2018-07-24 12:24:49 -04:00
Lioncash
16139ed53b gl_rasterizer: Use std::string_view instead of std::string when checking for extensions
We can avoid heap allocations here by just using a std::string_view
instead of performing unnecessary copying of the string data.
2018-07-24 12:10:37 -04:00
Lioncash
b5eb3905cd gl_rasterizer: Use in-class member initializers where applicable
We can just assign to the members directly in these cases.
2018-07-24 12:08:12 -04:00
Lioncash
bf608f125e video_core/memory_manager: Replace a loop with std::array's fill() function in PageSlot()
We already have a function that does what this code was doing, so let's
use that instead.
2018-07-24 11:56:30 -04:00
Lioncash
d71e19fd75 video_core/memory_manager: Avoid repeated unnecessary page slot lookups
We don't need to keep calling the same function over and over again in a
loop, especially when the behavior is slightly non-trivial. We can just
keep a reference to the looked up location and do all the checking and
assignments based off it instead.
2018-07-24 11:19:54 -04:00
Lioncash
4cf2185e81 deconstructed_rom_directory: Remove unused FindRomFS() function 2018-07-24 10:54:07 -04:00
Lioncash
07c2d057bd friend: Add friend:m, friend:s, and friend:v services
Given we already have friend:a and friend:u, we should add the remaining
services as well.
2018-07-24 10:26:01 -04:00
Lioncash
9539a1eadd friend/interface: Add missing CreateDaemonSuspendSessionService() to the function handler table 2018-07-24 10:24:16 -04:00
Lioncash
77daef44b0 friend: Deduplicate interfaces 2018-07-24 10:21:51 -04:00
Lioncash
c73410bf2c svc: Resolve sign comparison warnings in WaitSynchronization()
The loop's induction variable was signed, but we were comparing against
an unsigned variable.
2018-07-24 09:55:17 -04:00
Lioncash
8e8e906432 svc: Log parameters in SetMemoryAttribute()
Provides slightly more context than only logging out the address value.
2018-07-24 09:46:46 -04:00
bunnei
316c994f55 Merge pull request #798 from lioncash/const
arm_dynarmic: Make MakeJit() a const member function
2018-07-24 04:48:06 -07:00
bunnei
23e85b6b9f Merge pull request #797 from lioncash/explicit
core: Make converting constructors explicit where applicable
2018-07-24 04:47:26 -07:00
bunnei
1cbf7ac6ea Merge pull request #795 from lioncash/decl
apm/interface: Remove redundant declaration of InstallInterfaces()
2018-07-24 04:46:41 -07:00
bunnei
0f830d08f1 Merge pull request #799 from Subv/tex_r32f
GPU: Implement texture format R32F.
2018-07-24 04:46:07 -07:00
bunnei
d092ea0870 Merge pull request #794 from lioncash/ref
mutex: Pass SharedPtr to GetHighestPriorityMutexWaitingThread() by reference
2018-07-24 04:45:34 -07:00
bunnei
b70f757913 Merge pull request #796 from bunnei/gl-uint
maxwell_to_gl: Implement VertexAttribute::Type::UnsignedInt.
2018-07-24 04:44:56 -07:00
bunnei
54af9c206a Merge pull request #789 from bunnei/tex-wrap-border
maxwell_to_gl: Implement Texture::WrapMode::Border.
2018-07-24 04:44:37 -07:00
Lioncash
a61124a9e7 time: Simplify interface creation
We can use one instance of the interface instead of duplicating code.
2018-07-24 06:21:27 -04:00
MerryMage
44646e2ea0 core_timing: Split off utility functions into core_timing_util 2018-07-24 11:03:24 +01:00
MerryMage
b8459d2778 CMakeLists: Sort filenames 2018-07-24 11:02:53 +01:00
Lioncash
9d2cacdc9e wait_tree: Silence warning about all code paths not returning a value
If code execution hits this spot, something has gone very wrong, so mark
the path as unreachable. This silences a warning on MSVC.
2018-07-24 04:06:55 -04:00
Lioncash
63c605c04a set_sys: Implement SetColorSetId() 2018-07-24 00:48:16 -04:00
Lioncash
76f191ce36 ipc_helper: Add helper member function for popping enum values to RequestParser 2018-07-24 00:47:51 -04:00
bunnei
2f029577c7 Merge pull request #793 from lioncash/priv
ipc_helpers: Make member variables of ResponseBuilder private
2018-07-23 21:23:27 -07:00
bunnei
bc5b65a1b0 Merge pull request #785 from lioncash/fs
partition_filesystem: Use std::move where applicable
2018-07-23 20:36:59 -07:00
bunnei
69c45ce71c gl_rasterizer: Implement texture border color. 2018-07-23 23:34:42 -04:00
bunnei
6b3e54621f maxwell_to_gl: Implement Texture::WrapMode::Border. 2018-07-23 23:34:41 -04:00
Subv
ccc42702b5 GPU: Implement texture format R32F. 2018-07-23 22:21:31 -05:00
Lioncash
7d124ec82b arm_dynarmic: Make MakeJit() a const member function
This functions doesn't modify instance state, so it can be a made a
const member function.
2018-07-23 23:19:37 -04:00
Lioncash
1d755abce4 core: Make converting constructors explicit where applicable
Avoids unwanted implicit conversions. Thankfully, given the large amount
of cleanup in past PRs, only this tiny amount is left over to cover.
2018-07-23 23:13:22 -04:00
bunnei
1ff3bea6c7 Merge pull request #791 from bunnei/rg32f-rgba32f-bgra8
gl_rasterizer_cache: Implement formats BGRA8_UNORM/RGBA32_FLOAT/RG32_FLOAT
2018-07-23 20:13:19 -07:00
bunnei
2ff86f5765 maxwell_to_gl: Implement VertexAttribute::Type::UnsignedInt. 2018-07-23 23:12:14 -04:00
bunnei
92304181d5 Merge pull request #792 from lioncash/retval
gl_shader_decompiler: Correct return value of WriteTexsInstruction()
2018-07-23 20:06:48 -07:00
Lioncash
8b83adfed6 apm/interface: Remove redundant declaration of InstallInterfaces()
This is already declared in apm/apm.h
2018-07-23 23:01:04 -04:00
Lioncash
42b5158c96 mutex: Pass SharedPtr to GetHighestPriorityMutexWaitingThread() by reference
The pointed to thread's members are simply observed in this case, so we
don't need to copy it here.
2018-07-23 22:54:35 -04:00
bunnei
47ac369180 Merge pull request #790 from bunnei/shader-print-instr
gl_shader_decompiler: Print instruction value in shader comments.
2018-07-23 19:48:47 -07:00
bunnei
c2b4ff5d48 Merge pull request #788 from bunnei/shader-check-zero
gl_shader_decompiler: Check if SetRegister result is ZeroIndex.
2018-07-23 19:44:05 -07:00
Zach Hilman
59cb258409 VFS Regression and Accuracy Fixes (#776)
* Regression and Mode Fixes

* Review Fixes

* string_view correction

* Add operator& for FileSys::Mode

* Return std::string from SanitizePath

* Farming Simulator Fix

* Use != With mode operator&
2018-07-23 19:40:35 -07:00
Lioncash
22fd3f0026 hle_ipc: Make constructors explicit where applicable 2018-07-23 22:40:24 -04:00
Lioncash
33e2033af5 gl_shader_decompiler: Correct return value of WriteTexsInstruction()
This should be returning void, not a std::string
2018-07-23 22:31:58 -04:00
Lioncash
2a1daf8f83 ipc_helpers: Make member variables of ResponseBuilder private
These aren't used externally at all, so they can be made private.
2018-07-23 22:29:07 -04:00
bunnei
f6657bc8d7 Merge pull request #787 from bunnei/tlds
gl_shader_decompiler: Implement shader instruction TLDS.
2018-07-23 19:14:01 -07:00
bunnei
25635907a2 Merge pull request #786 from lioncash/exclusive
exclusive_monitor: Use consistent type alias for u64
2018-07-23 19:11:05 -07:00
bunnei
84cc5dd35d Merge pull request #784 from lioncash/loader
loader: Minor cleanup
2018-07-23 19:08:12 -07:00
bunnei
1ce5e04be8 Merge pull request #783 from lioncash/linker
linker: Remove unused parameter from WriteRelocations()
2018-07-23 19:07:22 -07:00
bunnei
10dd03dec5 Merge pull request #782 from lioncash/file
loader/nro: Minor changes
2018-07-23 19:06:30 -07:00
bunnei
9505283989 gl_shader_decompiler: Implement shader instruction TLDS. 2018-07-23 22:02:51 -04:00
bunnei
f6f6f3811e Merge pull request #781 from lioncash/decl
gl_shader_decompiler: Simplify GetCommonDeclarations()
2018-07-23 18:30:23 -07:00
bunnei
52ec1840f5 Merge pull request #780 from lioncash/move
vi: Minor changes
2018-07-23 18:29:11 -07:00
bunnei
e0b6771e25 Merge pull request #779 from lioncash/shared
hle: Remove unused config_mem and shared_page source files
2018-07-23 18:28:45 -07:00
bunnei
a27c0099ed gl_rasterizer_cache: Implement RenderTargetFormat RG32_FLOAT. 2018-07-23 21:22:54 -04:00
bunnei
3a19c1098d gl_rasterizer_cache: Implement RenderTargetFormat RGBA32_FLOAT. 2018-07-23 21:22:53 -04:00
bunnei
bcc184acfa gl_rasterizer_cache: Implement RenderTargetFormat BGRA8_UNORM. 2018-07-23 21:22:44 -04:00
bunnei
89db8c2171 gl_rasterizer_cache: Add missing log statements. 2018-07-23 21:20:09 -04:00
bunnei
c4322ce87e gl_shader_decompiler: Print instruction value in shader comments. 2018-07-23 21:11:05 -04:00
bunnei
81aa02424b gl_shader_decompiler: Check if SetRegister result is ZeroIndex. 2018-07-23 21:08:40 -04:00
Lioncash
e12c84d5c5 exclusive_monitor: Use consistent type alias for u64
Uses the same type aliases we use for virtual addresses, and converts
one lingering usage of std::array<uint64_t, 2> to u128 for consistency.
2018-07-23 20:54:57 -04:00
Lioncash
db48ebb9c9 partition_filesystem: Use std::move where applicable
Avoids copying a std::string instance and avoids unnecessary atomic
reference count incrementing and decrementing.
2018-07-23 20:27:11 -04:00
Lioncash
a147fa5825 loader: Remove unnecessary constructor call in IdentifyFile()
RealVfsFile inherits from VfsFile, the instance from std::make_shared is
already compatible with the function argument type, making the copy
constructor call unnecessary.
2018-07-23 17:44:58 -04:00
Lioncash
184c516182 linker: Remove unused parameter from WriteRelocations()
is_jump_relocation is never used within the function, so we can just
remove it.
2018-07-23 17:40:12 -04:00
Lioncash
1b4d0ac20e nro: Replace inclusion with a forward declaration
It's sufficient to use a forward declaration instead of a direct
inclusion here.
2018-07-23 17:29:02 -04:00
Lioncash
2b497e5830 nro: Make bracing consistent
Makes the code more uniform, and also braces cases where the body of an
unbraced conditional travels more than one line.
2018-07-23 17:24:29 -04:00
Lioncash
ac8133b9ee nro: Make constructor explicit
Makes it consistent with the other Apploader constructors, and prevents
implicit conversions.
2018-07-23 17:20:33 -04:00
Lioncash
1c16700372 nro: Remove unused forward declaration
This isn't used anywhere in the header.
2018-07-23 17:19:42 -04:00
bunnei
07e5319d55 Merge pull request #695 from DarkLordZach/nro-asset
NRO Assets and NACP File Format
2018-07-23 14:14:11 -07:00
bunnei
d787873b3b Merge pull request #778 from lioncash/log
set: Add missing log call in GetAvailableLanguageCodeCount()
2018-07-23 14:13:09 -07:00
Lioncash
3b88ce3dcb gl_shader_decompiler: Simplify GetCommonDeclarations() 2018-07-23 17:11:18 -04:00
Lioncash
1432912ae8 vi: Add std::is_trivially_copyable checks to Read and Write functions
It's undefined behavior to memcpy an object that isn't considered
trivially copyable, so put a compile-time check in to make sure this
doesn't occur.
2018-07-23 14:53:54 -04:00
Lioncash
344a0c91f2 vi: std::move std::vector in constructors where applicable
Allows avoiding unnecessary copies of the vector depending on the
calling code.

While we're at it, remove a redundant no-parameter base constructor call
2018-07-23 14:49:54 -04:00
Lioncash
cbe841c9c9 hle: Remove config_mem.h/.cpp
This is just an unused hold-over from citra, so we can get rid of this
to trim off an exposed global, among other things.
2018-07-23 12:57:34 -04:00
Lioncash
1f3889a290 hle: Remove shared_page.h/.cpp
This is a holdover from citra that's essentially unused.
2018-07-23 12:53:07 -04:00
Lioncash
e85308cd90 set: Add missing log call in GetAvailableLanguageCodeCount()
Forgot to include this in 22f448b632
2018-07-23 12:37:42 -04:00
bunnei
7138b99f21 Merge pull request #775 from lioncash/str
string_util: Minor changes
2018-07-23 09:34:41 -07:00
Zach Hilman
e8f641a52d NRO Assets and NACP file format
Cleanup

Review fixes
2018-07-23 12:34:26 -04:00
bunnei
a85366a40c Merge pull request #777 from lioncash/lang
set: Amend return value of GetAvailableLanguageCodes()
2018-07-23 09:34:08 -07:00
Lioncash
22f448b632 set: Implement GetAvailableLanguageCodeCount()
This just returns the size of the language code buffer.
2018-07-23 00:29:40 -04:00
Lioncash
37aeecd29f set: Correct return code size of value in GetAvailableLanguageCodes()
The return code should be 32-bit in size.
2018-07-23 00:29:22 -04:00
bunnei
e85a528bb9 Merge pull request #769 from bunnei/shader-mask-fixes
shader_bytecode: Implement other TEXS masks.
2018-07-22 18:03:31 -07:00
Lioncash
9d33122197 string_util: Get rid of separate resize() in CPToUTF16(), UTF16ToUTF8(), CodeToUTF8() and UTF8ToUTF16()
There's no need to perform the resize separately here, since the
constructor allows presizing the buffer.

Also move the empty string check before the construction of the string
to make the early out more straightforward.
2018-07-22 16:39:21 -04:00
Lioncash
26a157cd31 string_util: Use emplace_back() in SplitString() instead of push_back()
This is equivalent to doing:

push_back(std::string(""));

which is likely not to cause issues, assuming a decent std::string
implementation with small-string optimizations implemented in its
design, however it's still a little unnecessary to copy that buffer
regardless. Instead, we can use emplace_back() to directly construct the
empty string within the std::vector instance, eliminating any possible
overhead from the copy.
2018-07-22 15:36:32 -04:00
Lioncash
cd46b267f5 string_util: Remove unnecessary std::string instance in TabsToSpaces()
We can just use the variant of std::string's replace() function that can
replace an occurrence with N copies of the same character, eliminating
the need to allocate a std::string containing a buffer of spaces.
2018-07-22 15:35:48 -04:00
bunnei
a4b2af7382 Merge pull request #774 from Subv/atomic_signal
Kernel/SVC: Perform atomic accesses in SignalProcessWideKey as per the real kernel.
2018-07-22 12:26:03 -07:00
bunnei
c994cdc532 Merge pull request #773 from Subv/gl_ext_check
Frontend: Check for more required OpenGL extensions during startup.
2018-07-22 11:47:07 -07:00
bunnei
5ee4c49c30 Merge pull request #768 from lioncash/string-view
file_util, vfs: Use std::string_view where applicable
2018-07-22 11:32:28 -07:00
Subv
7841447cf0 Kernel/SVC: Perform atomic accesses in SignalProcessWideKey as per the real kernel. 2018-07-22 12:27:24 -05:00
bunnei
3618a68f93 Merge pull request #770 from lioncash/construct
gl_shader_decompiler: Remove redundant Subroutine construction in AddSubroutine()
2018-07-22 10:24:20 -07:00
Subv
ba2fb83d60 Frontend: Check for more required OpenGL extensions during startup. 2018-07-22 12:05:38 -05:00
Mat M
5fc99553d2 Merge pull request #638 from MerryMage/mp
Implement exclusive monitor
2018-07-22 12:48:44 -04:00
Sebastian Valle
3e2b32a3ee Merge pull request #772 from MerryMage/dynarmic
externals: Update dynarmic to fc6b73bd
2018-07-22 11:45:47 -05:00
MerryMage
7dde4b7a77 externals: Update dynarmic to fc6b73bd
Resolves issues:
* 128-bit exclusive writes on Windows
* Non-updating CNTPCT_EL0

fc6b73 a64_emit_x64: Ensure host has updated ticks in EmitA64GetCNTPCT
888c67 a64_emit_x64: Fix stack misalignment on Windows for 128-bit exclusive writes
352d53 emit_x64_aes: Eliminate extraneous usage of a scratch register in EmitAESInverseMixColumns()
ab7fe7 A64: Implement SADDLV
09bd2b A64: Implement UADDLV
62e86d fp: Use forward declarations where applicable
b3edb7 emit_x64_vector: Append 'v' prefix onto movq in AVX path
2018-07-22 16:26:47 +01:00
MerryMage
0b1c2e5505 Implement exclusive monitor 2018-07-22 15:55:17 +01:00
Lioncash
0797657bc0 gl_shader_decompiler: Remove redundant Subroutine construction in AddSubroutine()
We don't need to toss away the Subroutine instance after the find() call
and reconstruct another instance with the same data right after it.
Particularly give Subroutine contains a std::set.
2018-07-22 03:30:35 -04:00
bunnei
148a5bef7e shader_bytecode: Implement other TEXS masks. 2018-07-22 03:23:15 -04:00
Lioncash
0081252d31 vfs: Correct file_p variable usage within InterpretAsDirectory()
ReplaceFileWithSubdirectory() takes a VirtualFile and a VirtualDir, but
it was being passed a string as one of its arguments. The only reason
this never caused issues is because this template isn't instantiated
anywhere yet.

This corrects an issue before it occurs.
2018-07-22 03:22:28 -04:00
Lioncash
398444e676 file_util, vfs: Use std::string_view where applicable
Avoids unnecessary construction of std::string instances where
applicable.
2018-07-22 03:22:21 -04:00
bunnei
258a5cee84 Merge pull request #765 from lioncash/file
file_util: Remove goto usages from Copy()
2018-07-22 00:03:35 -07:00
bunnei
af4bde8cd1 Merge pull request #767 from bunnei/shader-cleanup
gl_shader_decompiler: Remove unused state tracking and minor cleanup.
2018-07-22 00:03:17 -07:00
bunnei
2d563ec8d5 Merge pull request #766 from bunnei/shader-sel
gl_shader_decompiler: Implement SEL instruction.
2018-07-21 23:13:27 -07:00
bunnei
ef163c1a15 Merge pull request #764 from lioncash/move
file_util: Minor changes to ScanDirectoryTree() and ForeachDirectoryEntry()
2018-07-21 22:05:30 -07:00
bunnei
f5a2944ab6 gl_shader_decompiler: Remove unused state tracking and minor cleanup. 2018-07-22 01:00:44 -04:00
bunnei
c43eaa94f3 gl_shader_decompiler: Implement SEL instruction. 2018-07-22 00:37:12 -04:00
bunnei
4cd5df95d6 Merge pull request #761 from bunnei/improve-raster-cache
Improvements to rasterizer cache
2018-07-21 20:28:53 -07:00
Lioncash
c5de0a67a8 file_util: Remove goto usages from Copy()
We can just leverage std::unique_ptr to automatically close these for us
in error cases instead of jumping to the end of the function to call
fclose on them.
2018-07-21 23:08:55 -04:00
Lioncash
0ba7fe4ab1 file_util: Use a u64 to represent number of entries
This avoids a truncating cast on size. I doubt we'd ever traverse a
directory this large, however we also shouldn't truncate sizes away.
2018-07-21 22:42:08 -04:00
Lioncash
964154ce44 file_util: std::move FST entries in ScanDirectoryTree()
Avoids unnecessary copies when building up the FST entries.
2018-07-21 22:31:44 -04:00
bunnei
63fbf9a7d3 gl_rasterizer_cache: Blit surfaces on recreation instead of flush and load. 2018-07-21 21:51:06 -04:00
bunnei
4301f0b539 gl_rasterizer_cache: Use GPUVAddr as cache key, not parameter set. 2018-07-21 21:51:06 -04:00
bunnei
cd47391c2d gl_rasterizer_cache: Use zeta_width and zeta_height registers for depth buffer. 2018-07-21 21:51:06 -04:00
bunnei
d8c60029d6 gl_rasterizer: Use zeta_enable register to enable depth buffer. 2018-07-21 21:51:06 -04:00
bunnei
5287991a36 maxwell_3d: Add depth buffer enable, width, and height registers. 2018-07-21 21:51:05 -04:00
bunnei
53a219f163 Merge pull request #759 from lioncash/redundant
file_util: Remove redundant duplicate return in GetPathWithoutTop()
2018-07-21 18:50:38 -07:00
bunnei
3ac736c003 Merge pull request #748 from lioncash/namespace
video_core: Use nested namespaces where applicable
2018-07-21 18:50:14 -07:00
bunnei
f5e87f4ce1 Merge pull request #758 from lioncash/sync
common: Remove synchronized_wrapper.h
2018-07-21 18:30:31 -07:00
bunnei
9533875eeb Merge pull request #760 from lioncash/path
file_util: Use an enum class for GetUserPath()
2018-07-21 18:30:04 -07:00
bunnei
d95a1a3742 Merge pull request #762 from Subv/ioctl2
GPU: Implement the NVGPU_IOCTL_CHANNEL_KICKOFF_PB ioctl2 command.
2018-07-21 18:28:55 -07:00
Subv
5c49e56d41 GPU: Implement the NVGPU_IOCTL_CHANNEL_KICKOFF_PB ioctl2 command.
This behaves quite similarly to the SubmitGPFIFO command. Referenced from Ryujinx.
Many thanks to @gdkchan for investigating this!
2018-07-21 15:50:02 -05:00
Lioncash
d66b43dadf file_util: Use an enum class for GetUserPath()
Instead of using an unsigned int as a parameter and expecting a user to
always pass in the correct values, we can just convert the enum into an
enum class and use that type as the parameter type instead, which makes
the interface more type safe.

We also get rid of the bookkeeping "NUM_" element in the enum by just
using an unordered map. This function is generally low-frequency in
terms of calls (and I'd hope so, considering otherwise would mean we're
slamming the disk with IO all the time) so I'd consider this acceptable
in this case.
2018-07-21 16:21:19 -04:00
Lioncash
34d6a1349c file_util: Remove explicit type from std::min() in GetPathWithoutTop()
Given both operands are the same type, there won't be an issue with
overload selection that requires making this explicit.
2018-07-21 15:19:32 -04:00
Lioncash
41660c8923 file_util: Remove redundant duplicate return in GetPathWithoutTop() 2018-07-21 15:18:23 -04:00
Lioncash
973fdce79b common: Remove synchronized_wrapper.h
This is entirely unused in the codebase.
2018-07-21 14:51:44 -04:00
bunnei
0f20fa5a1e Merge pull request #754 from lioncash/part
partition_filesystem, vfs_real: Minor changes
2018-07-21 11:38:52 -07:00
bunnei
de7cb91995 Merge pull request #750 from lioncash/ctx
arm_interface: Remove unused tls_address member of ThreadContext
2018-07-21 11:38:16 -07:00
bunnei
867ba1ceee Merge pull request #756 from lioncash/dynarmic
externals: Update dynarmic to 7ea1241
2018-07-21 11:37:38 -07:00
bunnei
1c7c1347d8 Merge pull request #746 from lioncash/tests
tests/arm_test_common: Minor changes
2018-07-21 10:55:29 -07:00
bunnei
ff8754f921 Merge pull request #747 from lioncash/unimplemented
gl_shader_manager: Remove unimplemented function prototype
2018-07-21 10:54:58 -07:00
bunnei
89cc8c1617 Merge pull request #755 from lioncash/ctor
file_sys/errors: Remove redundant object constructor calls
2018-07-21 10:53:53 -07:00
bunnei
552aac7e6c Merge pull request #749 from lioncash/consistency
gpu: Rename Get3DEngine() to Maxwell3D()
2018-07-21 10:51:00 -07:00
bunnei
fe2498a650 Merge pull request #751 from Subv/tpidr_el0
CPU: Save and restore the TPIDR_EL0 system register on every context switch
2018-07-21 10:48:30 -07:00
bunnei
3d938b8c60 Merge pull request #753 from lioncash/const
vfs: Minor changes
2018-07-21 10:44:08 -07:00
Lioncash
1519ce7eab externals: Update dynarmic to 7ea1241
Resolves an issue with TPIDR setting being erroneously removed in the
dead code pass.
2018-07-21 13:25:14 -04:00
bunnei
d85cfc94e2 Merge pull request #752 from Subv/vfs_load
Loader: Only print the module names and addresses if they actually exist.
2018-07-20 22:57:18 -07:00
Lioncash
459e158340 file_sys/errors: Remove redundant object constructor calls
Given we're already constructing the error code, we don't need to call
the constructor inside of it.
2018-07-20 22:37:54 -04:00
Lioncash
b46c0ed1fa vfs_real: Remove redundant copying of std::vector instances in GetFiles() and GetSubdirectories()
We already return by value, so we don't explicitly need to make the
copy.
2018-07-20 22:30:22 -04:00
Lioncash
ec71915ede partition_filesystem, vfs_real: Add missing standard includes 2018-07-20 22:28:35 -04:00
Lioncash
d36e327ba6 partition_filesystem, vfs_real: Use std::move in ReplaceFileWithSubdirectory() where applicable
Avoids unnecessary atomic increment and decrement operations.
2018-07-20 22:23:58 -04:00
Lioncash
2b91386e15 partition_filesystem, vfs_real: Use std::distance() instead of subtraction
This is a little bit more self-documenting on what is being done here.
2018-07-20 22:19:17 -04:00
Lioncash
3e0727df1b vfs_offset: Simplify TrimToFit()
We can simply use std::clamp() here, instead of using an equivalent
with std::max() and std::min().
2018-07-20 22:04:37 -04:00
Lioncash
894b0de0f2 vfs: Make WriteBytes() overload taking a std::vector pass the std::vector by const reference
Given the data is intended to be directly written, there's no need to
take the std::vector by value and copy the data.
2018-07-20 21:51:30 -04:00
Lioncash
dd09439fee vfs: Use variable template variants of std::is_trivially_copyable
Provides the same behavior, but with less writing
2018-07-20 21:47:19 -04:00
Lioncash
05231d8b08 vfs: Amend constness on pointers in WriteBytes() and WriteArrays() member functions to be const qualified
These functions don't modify the data being pointed to, so these can be
pointers to const data
2018-07-20 21:40:15 -04:00
Subv
966874e357 Loader: Only print the module names and addresses if they actually exist. 2018-07-20 19:59:15 -05:00
Subv
d84eb9dac6 CPU: Save and restore the TPIDR_EL0 system register on every context switch.
Note that there's currently a dynarmic bug preventing this register from being written.
2018-07-20 19:57:45 -05:00
bunnei
8afc21f175 Merge pull request #743 from lioncash/view
logging: Use std::string_view where applicable
2018-07-20 17:17:04 -07:00
bunnei
d4104c72aa Merge pull request #745 from lioncash/package
param_package: Minor changes
2018-07-20 17:16:54 -07:00
Lioncash
ae09adfcb3 arm_interface: Remove unused tls_address member of ThreadContext
Currently, the TLS address is set within the scheduler, making this
member unused.
2018-07-20 18:57:40 -04:00
Lioncash
d5bc9aef4e gl_shader_manager: Replace unimplemented function prototype
This was just a linker error waiting to happen.
2018-07-20 18:39:54 -04:00
Lioncash
863579736c gpu: Rename Get3DEngine() to Maxwell3D()
This makes it match its const qualified equivalent.
2018-07-20 18:34:49 -04:00
Lioncash
bb960c8cb4 video_core: Use nested namespaces where applicable
Compresses a few namespace specifiers to be more compact.
2018-07-20 18:23:54 -04:00
bunnei
2b7d862366 Merge pull request #742 from bunnei/misc-apm
apm: Improve stub for GetPerformanceConfiguration.
2018-07-20 15:01:19 -07:00
bunnei
3acd6fa086 Merge pull request #741 from lioncash/enum
ipc_helpers: Add PushEnum() member function to ResponseBuilder
2018-07-20 15:00:44 -07:00
Lioncash
48733744bb arm_test_common: Get rid of truncation warnings
Explicitly cast the value to a u8 to show that this is intentional.
2018-07-20 17:53:53 -04:00
Lioncash
a8bb1eb39f arm_test_common: Make file static variable a member variable of the testing environment
Gets rid of file-static behavior.
2018-07-20 17:52:37 -04:00
Lioncash
a44475207c arm_test_common: Add missing header guard 2018-07-20 17:48:43 -04:00
Lioncash
3268321f4b param_package: Take std::string by value in string-based Set() function
Allows avoiding string copies by letting the strings be moved into the
function calls.
2018-07-20 17:24:06 -04:00
Lioncash
6279c2dcf4 param_package: Use std::unordered_map's insert_or_assign instead of map indexing
This avoids a redundant std::string construction if a key doesn't exist
in the map already.

e.g.

data[key] requires constructing a new default instance of the value in
the map (but this is wasteful, since we're already setting something
into the map over top of it).
2018-07-20 17:24:06 -04:00
Lioncash
474ec2ee5f param_package: Get rid of file-static std::string construction
Avoids potential dynamic allocation occuring during program launch
2018-07-20 17:24:02 -04:00
Lioncash
f63ccbd936 logging/filter: Use std::string_view in ParseFilterString()
Allows avoiding constructing std::string instances, since this only
reads an arbitrary sequence of characters.

We can also make ParseFilterRule() internal, since it doesn't depend on
any private instance state of Filter
2018-07-20 15:58:46 -04:00
Lioncash
7a1a860abe logging/backend: Add missing standard includes
A few inclusions were being satisfied indirectly. To prevent breakages
in the future, include these directly.
2018-07-20 15:31:27 -04:00
Lioncash
457d1b4490 logging/backend: Use std::string_view in RemoveBackend() and GetBackend()
These can just use a view to a string since its only comparing against
two names in both cases for matches. This avoids constructing
std::string instances where they aren't necessary.
2018-07-20 15:27:20 -04:00
bunnei
dffd154d6d apm: Improve stub for GetPerformanceConfiguration. 2018-07-20 15:20:01 -04:00
Lioncash
0a0b3c4b9f ipc_helpers: Add PushEnum() member function to ResponseBuilder
Allows pushing strongly-typed enum members without the need to always
cast them at the call sites.

Note that we *only* allow strongly-typed enums in this case. The reason
for this is that strongly typed enums have a guaranteed defined size, so
the size of the data being pushed is always deterministic. With regular
enums this can be a little more error-prone, so we disallow them.

This function simply uses the underlying type of the enum to determine
the size of the data. For example, if an enum is defined as:

enum class SomeEnum : u16 {
  SomeEntry
};

if PushEnum(SomeEnum::SomeEntry); is called, then it will push a
u16-size amount of data.
2018-07-20 15:00:58 -04:00
bunnei
c1c9ab31e8 Merge pull request #740 from Subv/acc_crash
HLE/ACC: Stub IManagerForApplication::GetAccountId to return an error.
2018-07-20 09:47:47 -07:00
bunnei
8346541901 Merge pull request #739 from lioncash/glad
externals: Update glad to version 0.1.25
2018-07-20 09:22:26 -07:00
bunnei
29f49bd3c1 Merge pull request #738 from lioncash/sign
gl_state: Get rid of mismatched sign conversions in Apply()
2018-07-20 09:21:57 -07:00
bunnei
ffbd51e207 Merge pull request #737 from lioncash/move
filesys/loader: std::move VirtualFile instances in constructors where applicable
2018-07-20 09:21:15 -07:00
bunnei
701c7cb85c Merge pull request #736 from lioncash/null
audout_u/audren_u: Ensure null terminators are written out in ListAudioOutsImpl(), ListAudioDeviceName(), and GetActiveAudioDeviceName()
2018-07-20 09:17:07 -07:00
bunnei
fbc2bcd4a9 Merge pull request #735 from lioncash/video-unused
maxwell_3d: Remove unused variable within GetStageTextures()
2018-07-20 09:16:15 -07:00
bunnei
741cae1e1d Merge pull request #734 from lioncash/thread
thread: Convert ThreadStatus into an enum class
2018-07-20 09:15:52 -07:00
bunnei
a1805ceb0b Merge pull request #733 from lioncash/dirs
partition_filesystem: Return pfs_dirs member variable within GetSubdirectories()
2018-07-20 09:15:16 -07:00
bunnei
86d1649b32 Merge pull request #732 from lioncash/unused
nso: Minor changes
2018-07-20 09:14:10 -07:00
bunnei
204d707ce7 Merge pull request #731 from lioncash/shadow
gl_shader_decompiler: Eliminate variable and declaration shadowing
2018-07-20 09:13:36 -07:00
Subv
9c7321fe6d HLE/ACC: Stub IManagerForApplication::GetAccountId to return an error.
And make IManagerForApplication::CheckAvailability always return false.
Returning a bogus id from GetAccountId causes games to crash on boot.
We should investigate this with a hwtest and either stub it properly or implement it.
2018-07-20 11:02:25 -05:00
Lioncash
6a9cd17227 externals: Update glad to version 0.1.25
Keeps the OpenGL loader library up to date. Previously we were at
version 0.1.16
2018-07-20 02:00:05 -04:00
Lioncash
0faa13baeb gl_state: Make references const where applicable in Apply() 2018-07-20 01:12:29 -04:00
Lioncash
e6b3d3a9ea gl_state: Get rid of mismatched sign conversions
While we're at it, amend the loop variable type to be the same width as
that returned by the .size() call.
2018-07-20 01:11:20 -04:00
Lioncash
8874d0e657 loader/{nca, nro}: std::move VirtualFile in the constructors where applicable
This avoids unnecessary atomic reference count increments and decrements
2018-07-20 00:10:24 -04:00
Lioncash
0e9d58e82a vfs_offset: std::move file and name parameters of OffsetVfsFile
Avoids potentially unnecessary atomic reference count incrementing and
decrementing, as well as string copying.
2018-07-20 00:04:54 -04:00
bunnei
f36affdbe3 Merge pull request #730 from lioncash/string
gl_shader_decompiler: Remove unnecessary const from return values
2018-07-19 20:46:43 -07:00
bunnei
ce66a188d0 Merge pull request #729 from lioncash/simplify
pl_u: Simplify WriteBuffer() calls in GetSharedFontInOrderOfPriority()
2018-07-19 20:46:11 -07:00
Lioncash
8b08f82dc7 maxwell_3d: Remove unused variable within GetStageTextures() 2018-07-19 22:38:28 -04:00
Lioncash
dbfe82773d thread: Convert ThreadStatus into an enum class
Makes the thread status strongly typed, so implicit conversions can't
happen. It also makes it easier to catch mistakes at compile time.
2018-07-19 22:08:56 -04:00
Lioncash
bbd6429ecb partition_filesystem: Return pfs_dirs member variable within GetSubdirectories()
This should be returned here, otherwise pfs_dirs is effectively only
ever added to, but never read.
2018-07-19 21:08:50 -04:00
Lioncash
364b950515 nso: Silence implicit sign conversion warnings 2018-07-19 20:51:15 -04:00
Lioncash
a25c5b982a nso: Remove unused function ReadSegment() 2018-07-19 20:49:27 -04:00
Lioncash
f26866ff6a gl_shader_decompiler: Eliminate variable and declaration shadowing
Ensures that no identifiers are being hidden, which also reduces
compiler warnings.
2018-07-19 20:32:49 -04:00
Lioncash
c2121cb059 gl_shader_decompiler: Remove unnecessary const from return values
This adds nothing from a behavioral point of view, and can inhibit the
move constructor/RVO
2018-07-19 20:11:04 -04:00
Lioncash
1bdb67440b pl_u: Simplify WriteBuffer() calls in GetSharedFontInOrderOfPriority()
With the new overload, we can simply pass the container directly.
2018-07-19 19:50:30 -04:00
202 changed files with 5935 additions and 2917 deletions

2
.gitignore vendored
View File

@@ -11,7 +11,7 @@ src/common/scm_rev.cpp
.idea/
.vs/
.vscode/
CMakeLists.txt.user
CMakeLists.txt.user*
# *nix related
# Common convention for backup or temporary files

3
.gitmodules vendored
View File

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

View File

@@ -50,3 +50,7 @@ if (ARCHITECTURE_x86_64)
target_include_directories(xbyak INTERFACE ./xbyak/xbyak)
target_compile_definitions(xbyak INTERFACE XBYAK_NO_OP_NAMES)
endif()
# Opus
add_subdirectory(opus)
target_include_directories(opus INTERFACE ./opus/include)

View File

@@ -2,7 +2,7 @@
#define __khrplatform_h_
/*
** Copyright (c) 2008-2009 The Khronos Group Inc.
** Copyright (c) 2008-2018 The Khronos Group Inc.
**
** Permission is hereby granted, free of charge, to any person obtaining a
** copy of this software and/or associated documentation files (the
@@ -26,18 +26,16 @@
/* Khronos platform-specific types and definitions.
*
* $Revision: 32517 $ on $Date: 2016-03-11 02:41:19 -0800 (Fri, 11 Mar 2016) $
* The master copy of khrplatform.h is maintained in the Khronos EGL
* Registry repository at https://github.com/KhronosGroup/EGL-Registry
* The last semantic modification to khrplatform.h was at commit ID:
* 67a3e0864c2d75ea5287b9f3d2eb74a745936692
*
* Adopters may modify this file to suit their platform. Adopters are
* encouraged to submit platform specific modifications to the Khronos
* group so that they can be included in future versions of this file.
* Please submit changes by sending them to the public Khronos Bugzilla
* (http://khronos.org/bugzilla) by filing a bug against product
* "Khronos (general)" component "Registry".
*
* A predefined template which fills in some of the bug fields can be
* reached using http://tinyurl.com/khrplatform-h-bugreport, but you
* must create a Bugzilla login first.
* Please submit changes by filing pull requests or issues on
* the EGL Registry repository linked above.
*
*
* See the Implementer's Guidelines for information about where this file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1
externals/opus vendored Submodule

Submodule externals/opus added at b2871922a1

View File

@@ -3,6 +3,7 @@ include_directories(.)
add_subdirectory(common)
add_subdirectory(core)
add_subdirectory(audio_core)
add_subdirectory(video_core)
add_subdirectory(input_common)
add_subdirectory(tests)

View File

@@ -0,0 +1,11 @@
add_library(audio_core STATIC
audio_out.cpp
audio_out.h
buffer.h
stream.cpp
stream.h
)
create_target_directory_groups(audio_core)
target_link_libraries(audio_core PUBLIC common core)

View File

@@ -0,0 +1,50 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "audio_core/audio_out.h"
#include "common/assert.h"
#include "common/logging/log.h"
namespace AudioCore {
/// Returns the stream format from the specified number of channels
static Stream::Format ChannelsToStreamFormat(int num_channels) {
switch (num_channels) {
case 1:
return Stream::Format::Mono16;
case 2:
return Stream::Format::Stereo16;
case 6:
return Stream::Format::Multi51Channel16;
}
LOG_CRITICAL(Audio, "Unimplemented num_channels={}", num_channels);
UNREACHABLE();
return {};
}
StreamPtr AudioOut::OpenStream(int sample_rate, int num_channels,
Stream::ReleaseCallback&& release_callback) {
streams.push_back(std::make_shared<Stream>(sample_rate, ChannelsToStreamFormat(num_channels),
std::move(release_callback)));
return streams.back();
}
std::vector<u64> AudioOut::GetTagsAndReleaseBuffers(StreamPtr stream, size_t max_count) {
return stream->GetTagsAndReleaseBuffers(max_count);
}
void AudioOut::StartStream(StreamPtr stream) {
stream->Play();
}
void AudioOut::StopStream(StreamPtr stream) {
stream->Stop();
}
bool AudioOut::QueueBuffer(StreamPtr stream, Buffer::Tag tag, std::vector<u8>&& data) {
return stream->QueueBuffer(std::make_shared<Buffer>(tag, std::move(data)));
}
} // namespace AudioCore

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 <memory>
#include <vector>
#include "audio_core/buffer.h"
#include "audio_core/stream.h"
#include "common/common_types.h"
namespace AudioCore {
using StreamPtr = std::shared_ptr<Stream>;
/**
* Represents an audio playback interface, used to open and play audio streams
*/
class AudioOut {
public:
/// Opens a new audio stream
StreamPtr OpenStream(int sample_rate, int num_channels,
Stream::ReleaseCallback&& release_callback);
/// Returns a vector of recently released buffers specified by tag for the specified stream
std::vector<u64> GetTagsAndReleaseBuffers(StreamPtr stream, size_t max_count);
/// Starts an audio stream for playback
void StartStream(StreamPtr stream);
/// Stops an audio stream that is currently playing
void StopStream(StreamPtr stream);
/// Queues a buffer into the specified audio stream, returns true on success
bool QueueBuffer(StreamPtr stream, Buffer::Tag tag, std::vector<u8>&& data);
private:
/// Active audio streams on the interface
std::vector<StreamPtr> streams;
};
} // namespace AudioCore

37
src/audio_core/buffer.h Normal file
View File

@@ -0,0 +1,37 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <vector>
#include "common/common_types.h"
namespace AudioCore {
/**
* Represents a buffer of audio samples to be played in an audio stream
*/
class Buffer {
public:
using Tag = u64;
Buffer(Tag tag, std::vector<u8>&& data) : tag{tag}, data{std::move(data)} {}
/// Returns the raw audio data for the buffer
const std::vector<u8>& GetData() const {
return data;
}
/// Returns the buffer tag, this is provided by the game to the audout service
Tag GetTag() const {
return tag;
}
private:
Tag tag;
std::vector<u8> data;
};
} // namespace AudioCore

103
src/audio_core/stream.cpp Normal file
View File

@@ -0,0 +1,103 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/core_timing.h"
#include "core/core_timing_util.h"
#include "audio_core/stream.h"
namespace AudioCore {
constexpr size_t MaxAudioBufferCount{32};
/// Returns the sample size for the specified audio stream format
static size_t SampleSizeFromFormat(Stream::Format format) {
switch (format) {
case Stream::Format::Mono16:
return 2;
case Stream::Format::Stereo16:
return 4;
case Stream::Format::Multi51Channel16:
return 12;
};
LOG_CRITICAL(Audio, "Unimplemented format={}", static_cast<u32>(format));
UNREACHABLE();
return {};
}
Stream::Stream(int sample_rate, Format format, ReleaseCallback&& release_callback)
: sample_rate{sample_rate}, format{format}, release_callback{std::move(release_callback)} {
release_event = CoreTiming::RegisterEvent(
"Stream::Release", [this](u64 userdata, int cycles_late) { ReleaseActiveBuffer(); });
}
void Stream::Play() {
state = State::Playing;
PlayNextBuffer();
}
void Stream::Stop() {
ASSERT_MSG(false, "Unimplemented");
}
s64 Stream::GetBufferReleaseCycles(const Buffer& buffer) const {
const size_t num_samples{buffer.GetData().size() / SampleSizeFromFormat(format)};
return CoreTiming::usToCycles((static_cast<u64>(num_samples) * 1000000) / sample_rate);
}
void Stream::PlayNextBuffer() {
if (!IsPlaying()) {
// Ensure we are in playing state before playing the next buffer
return;
}
if (active_buffer) {
// Do not queue a new buffer if we are already playing a buffer
return;
}
if (queued_buffers.empty()) {
// No queued buffers - we are effectively paused
return;
}
active_buffer = queued_buffers.front();
queued_buffers.pop();
CoreTiming::ScheduleEventThreadsafe(GetBufferReleaseCycles(*active_buffer), release_event, {});
}
void Stream::ReleaseActiveBuffer() {
released_buffers.push(std::move(active_buffer));
release_callback();
PlayNextBuffer();
}
bool Stream::QueueBuffer(BufferPtr&& buffer) {
if (queued_buffers.size() < MaxAudioBufferCount) {
queued_buffers.push(std::move(buffer));
PlayNextBuffer();
return true;
}
return false;
}
bool Stream::ContainsBuffer(Buffer::Tag tag) const {
ASSERT_MSG(false, "Unimplemented");
return {};
}
std::vector<Buffer::Tag> Stream::GetTagsAndReleaseBuffers(size_t max_count) {
std::vector<Buffer::Tag> tags;
for (size_t count = 0; count < max_count && !released_buffers.empty(); ++count) {
tags.push_back(released_buffers.front()->GetTag());
released_buffers.pop();
}
return tags;
}
} // namespace AudioCore

89
src/audio_core/stream.h Normal file
View File

@@ -0,0 +1,89 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <functional>
#include <memory>
#include <vector>
#include <queue>
#include "audio_core/buffer.h"
#include "common/assert.h"
#include "common/common_types.h"
#include "core/core_timing.h"
namespace AudioCore {
using BufferPtr = std::shared_ptr<Buffer>;
/**
* Represents an audio stream, which is a sequence of queued buffers, to be outputed by AudioOut
*/
class Stream {
public:
/// Audio format of the stream
enum class Format {
Mono16,
Stereo16,
Multi51Channel16,
};
/// Callback function type, used to change guest state on a buffer being released
using ReleaseCallback = std::function<void()>;
Stream(int sample_rate, Format format, ReleaseCallback&& release_callback);
/// Plays the audio stream
void Play();
/// Stops the audio stream
void Stop();
/// Queues a buffer into the audio stream, returns true on success
bool QueueBuffer(BufferPtr&& buffer);
/// Returns true if the audio stream contains a buffer with the specified tag
bool ContainsBuffer(Buffer::Tag tag) const;
/// Returns a vector of recently released buffers specified by tag
std::vector<Buffer::Tag> GetTagsAndReleaseBuffers(size_t max_count);
/// Returns true if the stream is currently playing
bool IsPlaying() const {
return state == State::Playing;
}
/// Returns the number of queued buffers
size_t GetQueueSize() const {
return queued_buffers.size();
}
private:
/// Current state of the stream
enum class State {
Stopped,
Playing,
};
/// Plays the next queued buffer in the audio stream, starting playback if necessary
void PlayNextBuffer();
/// Releases the actively playing buffer, signalling that it has been completed
void ReleaseActiveBuffer();
/// Gets the number of core cycles when the specified buffer will be released
s64 GetBufferReleaseCycles(const Buffer& buffer) const;
int sample_rate; ///< Sample rate of the stream
Format format; ///< Format of the stream
ReleaseCallback release_callback; ///< Buffer release callback for the stream
State state{State::Stopped}; ///< Playback state of the stream
CoreTiming::EventType* release_event{}; ///< Core timing release event for the stream
BufferPtr active_buffer; ///< Actively playing buffer in the stream
std::queue<BufferPtr> queued_buffers; ///< Buffers queued to be played in the stream
std::queue<BufferPtr> released_buffers; ///< Buffers recently released from the stream
};
} // namespace AudioCore

View File

@@ -63,7 +63,6 @@ add_library(common STATIC
string_util.cpp
string_util.h
swap.h
synchronized_wrapper.h
telemetry.cpp
telemetry.h
thread.cpp

View File

@@ -26,7 +26,7 @@
#define USA_DIR "USA"
#define JAP_DIR "JAP"
// Subdirs in the User dir returned by GetUserPath(D_USER_IDX)
// Subdirs in the User dir returned by GetUserPath(UserPath::UserDir)
#define CONFIG_DIR "config"
#define CACHE_DIR "cache"
#define SDMC_DIR "sdmc"
@@ -35,11 +35,11 @@
#define LOG_DIR "log"
// Filenames
// Files in the directory returned by GetUserPath(D_CONFIG_IDX)
// Files in the directory returned by GetUserPath(UserPath::ConfigDir)
#define EMU_CONFIG "emu.ini"
#define DEBUGGER_CONFIG "debugger.ini"
#define LOGGER_CONFIG "logger.ini"
// Files in the directory returned by GetUserPath(D_LOGS_IDX)
// Files in the directory returned by GetUserPath(UserPath::LogDir)
#define LOG_FILE "yuzu_log.txt"
// Sys files

View File

@@ -2,7 +2,10 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <array>
#include <memory>
#include <sstream>
#include <unordered_map>
#include "common/assert.h"
#include "common/common_funcs.h"
#include "common/common_paths.h"
@@ -274,14 +277,10 @@ bool Copy(const std::string& srcFilename, const std::string& destFilename) {
GetLastErrorMsg());
return false;
#else
// buffer size
#define BSIZE 1024
char buffer[BSIZE];
using CFilePointer = std::unique_ptr<FILE, decltype(&std::fclose)>;
// Open input file
FILE* input = fopen(srcFilename.c_str(), "rb");
CFilePointer input{fopen(srcFilename.c_str(), "rb"), std::fclose};
if (!input) {
LOG_ERROR(Common_Filesystem, "opening input failed {} --> {}: {}", srcFilename,
destFilename, GetLastErrorMsg());
@@ -289,44 +288,36 @@ bool Copy(const std::string& srcFilename, const std::string& destFilename) {
}
// open output file
FILE* output = fopen(destFilename.c_str(), "wb");
CFilePointer output{fopen(destFilename.c_str(), "wb"), std::fclose};
if (!output) {
fclose(input);
LOG_ERROR(Common_Filesystem, "opening output failed {} --> {}: {}", srcFilename,
destFilename, GetLastErrorMsg());
return false;
}
// copy loop
while (!feof(input)) {
std::array<char, 1024> buffer;
while (!feof(input.get())) {
// read input
size_t rnum = fread(buffer, sizeof(char), BSIZE, input);
if (rnum != BSIZE) {
if (ferror(input) != 0) {
size_t rnum = fread(buffer.data(), sizeof(char), buffer.size(), input.get());
if (rnum != buffer.size()) {
if (ferror(input.get()) != 0) {
LOG_ERROR(Common_Filesystem, "failed reading from source, {} --> {}: {}",
srcFilename, destFilename, GetLastErrorMsg());
goto bail;
return false;
}
}
// write output
size_t wnum = fwrite(buffer, sizeof(char), rnum, output);
size_t wnum = fwrite(buffer.data(), sizeof(char), rnum, output.get());
if (wnum != rnum) {
LOG_ERROR(Common_Filesystem, "failed writing to output, {} --> {}: {}", srcFilename,
destFilename, GetLastErrorMsg());
goto bail;
return false;
}
}
// close files
fclose(input);
fclose(output);
return true;
bail:
if (input)
fclose(input);
if (output)
fclose(output);
return false;
#endif
}
@@ -395,12 +386,12 @@ bool CreateEmptyFile(const std::string& filename) {
return true;
}
bool ForeachDirectoryEntry(unsigned* num_entries_out, const std::string& directory,
bool ForeachDirectoryEntry(u64* num_entries_out, const std::string& directory,
DirectoryEntryCallable callback) {
LOG_TRACE(Common_Filesystem, "directory {}", directory);
// How many files + directories we found
unsigned found_entries = 0;
u64 found_entries = 0;
// Save the status of callback function
bool callback_error = false;
@@ -430,7 +421,7 @@ bool ForeachDirectoryEntry(unsigned* num_entries_out, const std::string& directo
if (virtual_name == "." || virtual_name == "..")
continue;
unsigned ret_entries = 0;
u64 ret_entries = 0;
if (!callback(&ret_entries, directory, virtual_name)) {
callback_error = true;
break;
@@ -454,9 +445,9 @@ bool ForeachDirectoryEntry(unsigned* num_entries_out, const std::string& directo
return true;
}
unsigned ScanDirectoryTree(const std::string& directory, FSTEntry& parent_entry,
unsigned int recursion) {
const auto callback = [recursion, &parent_entry](unsigned* num_entries_out,
u64 ScanDirectoryTree(const std::string& directory, FSTEntry& parent_entry,
unsigned int recursion) {
const auto callback = [recursion, &parent_entry](u64* num_entries_out,
const std::string& directory,
const std::string& virtual_name) -> bool {
FSTEntry entry;
@@ -468,7 +459,7 @@ unsigned ScanDirectoryTree(const std::string& directory, FSTEntry& parent_entry,
// is a directory, lets go inside if we didn't recurse to often
if (recursion > 0) {
entry.size = ScanDirectoryTree(entry.physicalName, entry, recursion - 1);
*num_entries_out += (int)entry.size;
*num_entries_out += entry.size;
} else {
entry.size = 0;
}
@@ -479,16 +470,16 @@ unsigned ScanDirectoryTree(const std::string& directory, FSTEntry& parent_entry,
(*num_entries_out)++;
// Push into the tree
parent_entry.children.push_back(entry);
parent_entry.children.push_back(std::move(entry));
return true;
};
unsigned num_entries;
u64 num_entries;
return ForeachDirectoryEntry(&num_entries, directory, callback) ? num_entries : 0;
}
bool DeleteDirRecursively(const std::string& directory, unsigned int recursion) {
const auto callback = [recursion](unsigned* num_entries_out, const std::string& directory,
const auto callback = [recursion](u64* num_entries_out, const std::string& directory,
const std::string& virtual_name) -> bool {
std::string new_path = directory + DIR_SEP_CHR + virtual_name;
@@ -681,67 +672,68 @@ std::string GetSysDirectory() {
// Returns a string with a yuzu data dir or file in the user's home
// directory. To be used in "multi-user" mode (that is, installed).
const std::string& GetUserPath(const unsigned int DirIDX, const std::string& newPath) {
static std::string paths[NUM_PATH_INDICES];
const std::string& GetUserPath(UserPath path, const std::string& new_path) {
static std::unordered_map<UserPath, std::string> paths;
auto& user_path = paths[UserPath::UserDir];
// Set up all paths and files on the first run
if (paths[D_USER_IDX].empty()) {
if (user_path.empty()) {
#ifdef _WIN32
paths[D_USER_IDX] = GetExeDirectory() + DIR_SEP USERDATA_DIR DIR_SEP;
if (!FileUtil::IsDirectory(paths[D_USER_IDX])) {
paths[D_USER_IDX] = AppDataRoamingDirectory() + DIR_SEP EMU_DATA_DIR DIR_SEP;
user_path = GetExeDirectory() + DIR_SEP USERDATA_DIR DIR_SEP;
if (!FileUtil::IsDirectory(user_path)) {
user_path = AppDataRoamingDirectory() + DIR_SEP EMU_DATA_DIR DIR_SEP;
} else {
LOG_INFO(Common_Filesystem, "Using the local user directory");
}
paths[D_CONFIG_IDX] = paths[D_USER_IDX] + CONFIG_DIR DIR_SEP;
paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP;
paths.emplace(UserPath::ConfigDir, user_path + CONFIG_DIR DIR_SEP);
paths.emplace(UserPath::CacheDir, user_path + CACHE_DIR DIR_SEP);
#else
if (FileUtil::Exists(ROOT_DIR DIR_SEP USERDATA_DIR)) {
paths[D_USER_IDX] = ROOT_DIR DIR_SEP USERDATA_DIR DIR_SEP;
paths[D_CONFIG_IDX] = paths[D_USER_IDX] + CONFIG_DIR DIR_SEP;
paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP;
user_path = ROOT_DIR DIR_SEP USERDATA_DIR DIR_SEP;
paths.emplace(UserPath::ConfigDir, user_path + CONFIG_DIR DIR_SEP);
paths.emplace(UserPath::CacheDir, user_path + CACHE_DIR DIR_SEP);
} else {
std::string data_dir = GetUserDirectory("XDG_DATA_HOME");
std::string config_dir = GetUserDirectory("XDG_CONFIG_HOME");
std::string cache_dir = GetUserDirectory("XDG_CACHE_HOME");
paths[D_USER_IDX] = data_dir + DIR_SEP EMU_DATA_DIR DIR_SEP;
paths[D_CONFIG_IDX] = config_dir + DIR_SEP EMU_DATA_DIR DIR_SEP;
paths[D_CACHE_IDX] = cache_dir + DIR_SEP EMU_DATA_DIR DIR_SEP;
user_path = data_dir + DIR_SEP EMU_DATA_DIR DIR_SEP;
paths.emplace(UserPath::ConfigDir, config_dir + DIR_SEP EMU_DATA_DIR DIR_SEP);
paths.emplace(UserPath::CacheDir, cache_dir + DIR_SEP EMU_DATA_DIR DIR_SEP);
}
#endif
paths[D_SDMC_IDX] = paths[D_USER_IDX] + SDMC_DIR DIR_SEP;
paths[D_NAND_IDX] = paths[D_USER_IDX] + NAND_DIR DIR_SEP;
paths[D_SYSDATA_IDX] = paths[D_USER_IDX] + SYSDATA_DIR DIR_SEP;
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);
// TODO: Put the logs in a better location for each OS
paths[D_LOGS_IDX] = paths[D_USER_IDX] + LOG_DIR DIR_SEP;
paths.emplace(UserPath::LogDir, user_path + LOG_DIR DIR_SEP);
}
if (!newPath.empty()) {
if (!FileUtil::IsDirectory(newPath)) {
LOG_ERROR(Common_Filesystem, "Invalid path specified {}", newPath);
return paths[DirIDX];
if (!new_path.empty()) {
if (!FileUtil::IsDirectory(new_path)) {
LOG_ERROR(Common_Filesystem, "Invalid path specified {}", new_path);
return paths[path];
} else {
paths[DirIDX] = newPath;
paths[path] = new_path;
}
switch (DirIDX) {
case D_ROOT_IDX:
paths[D_USER_IDX] = paths[D_ROOT_IDX] + DIR_SEP;
switch (path) {
case UserPath::RootDir:
user_path = paths[UserPath::RootDir] + DIR_SEP;
break;
case D_USER_IDX:
paths[D_USER_IDX] = paths[D_ROOT_IDX] + DIR_SEP;
paths[D_CONFIG_IDX] = paths[D_USER_IDX] + CONFIG_DIR DIR_SEP;
paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP;
paths[D_SDMC_IDX] = paths[D_USER_IDX] + SDMC_DIR DIR_SEP;
paths[D_NAND_IDX] = paths[D_USER_IDX] + NAND_DIR DIR_SEP;
case UserPath::UserDir:
user_path = paths[UserPath::RootDir] + DIR_SEP;
paths[UserPath::ConfigDir] = user_path + CONFIG_DIR DIR_SEP;
paths[UserPath::CacheDir] = user_path + CACHE_DIR DIR_SEP;
paths[UserPath::SDMCDir] = user_path + SDMC_DIR DIR_SEP;
paths[UserPath::NANDDir] = user_path + NAND_DIR DIR_SEP;
break;
}
}
return paths[DirIDX];
return paths[path];
}
size_t WriteStringToFile(bool text_file, const std::string& str, const char* filename) {
@@ -800,71 +792,93 @@ void SplitFilename83(const std::string& filename, std::array<char, 9>& short_nam
}
}
std::vector<std::string> SplitPathComponents(const std::string& filename) {
auto copy(filename);
std::vector<std::string> SplitPathComponents(std::string_view filename) {
std::string copy(filename);
std::replace(copy.begin(), copy.end(), '\\', '/');
std::vector<std::string> out;
std::stringstream stream(filename);
std::stringstream stream(copy);
std::string item;
while (std::getline(stream, item, '/'))
while (std::getline(stream, item, '/')) {
out.push_back(std::move(item));
}
return out;
}
std::string GetParentPath(const std::string& path) {
auto out = path;
const auto name_bck_index = out.find_last_of('\\');
const auto name_fwd_index = out.find_last_of('/');
std::string_view GetParentPath(std::string_view path) {
const auto name_bck_index = path.rfind('\\');
const auto name_fwd_index = path.rfind('/');
size_t name_index;
if (name_bck_index == std::string::npos || name_fwd_index == std::string::npos)
name_index = std::min<size_t>(name_bck_index, name_fwd_index);
else
name_index = std::max<size_t>(name_bck_index, name_fwd_index);
return out.erase(name_index);
}
std::string GetPathWithoutTop(std::string path) {
if (path.empty())
return "";
while (path[0] == '\\' || path[0] == '/') {
path = path.substr(1);
if (path.empty())
return "";
if (name_bck_index == std::string_view::npos || name_fwd_index == std::string_view::npos) {
name_index = std::min(name_bck_index, name_fwd_index);
} else {
name_index = std::max(name_bck_index, name_fwd_index);
}
const auto name_bck_index = path.find_first_of('\\');
const auto name_fwd_index = path.find_first_of('/');
return path.substr(std::min<size_t>(name_bck_index, name_fwd_index) + 1);
return path.substr(std::min<size_t>(name_bck_index, name_fwd_index) + 1);
return path.substr(0, name_index);
}
std::string GetFilename(std::string path) {
std::replace(path.begin(), path.end(), '\\', '/');
auto name_index = path.find_last_of('/');
if (name_index == std::string::npos)
return "";
std::string_view GetPathWithoutTop(std::string_view path) {
if (path.empty()) {
return path;
}
while (path[0] == '\\' || path[0] == '/') {
path.remove_prefix(1);
if (path.empty()) {
return path;
}
}
const auto name_bck_index = path.find('\\');
const auto name_fwd_index = path.find('/');
return path.substr(std::min(name_bck_index, name_fwd_index) + 1);
}
std::string_view GetFilename(std::string_view path) {
const auto name_index = path.find_last_of("\\/");
if (name_index == std::string_view::npos) {
return {};
}
return path.substr(name_index + 1);
}
std::string GetExtensionFromFilename(const std::string& name) {
size_t index = name.find_last_of('.');
if (index == std::string::npos)
return "";
std::string_view GetExtensionFromFilename(std::string_view name) {
const size_t index = name.rfind('.');
if (index == std::string_view::npos) {
return {};
}
return name.substr(index + 1);
}
std::string RemoveTrailingSlash(const std::string& path) {
if (path.empty())
std::string_view RemoveTrailingSlash(std::string_view path) {
if (path.empty()) {
return path;
if (path.back() == '\\' || path.back() == '/')
return path.substr(0, path.size() - 1);
}
if (path.back() == '\\' || path.back() == '/') {
path.remove_suffix(1);
return path;
}
return path;
}
std::string SanitizePath(std::string_view path_) {
std::string path(path_);
std::replace(path.begin(), path.end(), '\\', '/');
path.erase(std::unique(path.begin(), path.end(),
[](char c1, char c2) { return c1 == '/' && c2 == '/'; }),
path.end());
return std::string(RemoveTrailingSlash(path));
}
IOFile::IOFile() {}
IOFile::IOFile(const std::string& filename, const char openmode[], int flags) {

View File

@@ -9,6 +9,7 @@
#include <fstream>
#include <functional>
#include <string>
#include <string_view>
#include <type_traits>
#include <vector>
#include "common/common_types.h"
@@ -16,21 +17,20 @@
#include "common/string_util.h"
#endif
// User directory indices for GetUserPath
enum {
D_USER_IDX,
D_ROOT_IDX,
D_CONFIG_IDX,
D_CACHE_IDX,
D_SDMC_IDX,
D_NAND_IDX,
D_SYSDATA_IDX,
D_LOGS_IDX,
NUM_PATH_INDICES
};
namespace FileUtil {
// User paths for GetUserPath
enum class UserPath {
CacheDir,
ConfigDir,
LogDir,
NANDDir,
RootDir,
SDMCDir,
SysDataDir,
UserDir,
};
// FileSystem tree node/
struct FSTEntry {
bool isDirectory;
@@ -85,7 +85,7 @@ bool CreateEmptyFile(const std::string& filename);
* @return whether handling the entry succeeded
*/
using DirectoryEntryCallable = std::function<bool(
unsigned* num_entries_out, const std::string& directory, const std::string& virtual_name)>;
u64* num_entries_out, const std::string& directory, const std::string& virtual_name)>;
/**
* Scans a directory, calling the callback for each file/directory contained within.
@@ -96,7 +96,7 @@ using DirectoryEntryCallable = std::function<bool(
* @param callback The callback which will be called for each entry
* @return whether scanning the directory succeeded
*/
bool ForeachDirectoryEntry(unsigned* num_entries_out, const std::string& directory,
bool ForeachDirectoryEntry(u64* num_entries_out, const std::string& directory,
DirectoryEntryCallable callback);
/**
@@ -106,8 +106,8 @@ bool ForeachDirectoryEntry(unsigned* num_entries_out, const std::string& directo
* @param recursion Number of children directories to read before giving up.
* @return the total number of files/directories found
*/
unsigned ScanDirectoryTree(const std::string& directory, FSTEntry& parent_entry,
unsigned int recursion = 0);
u64 ScanDirectoryTree(const std::string& directory, FSTEntry& parent_entry,
unsigned int recursion = 0);
// deletes the given directory and anything under it. Returns true on success.
bool DeleteDirRecursively(const std::string& directory, unsigned int recursion = 256);
@@ -123,7 +123,7 @@ bool SetCurrentDir(const std::string& directory);
// Returns a pointer to a string with a yuzu data dir in the user's home
// directory. To be used in "multi-user" mode (that is, installed).
const std::string& GetUserPath(const unsigned int DirIDX, const std::string& newPath = "");
const std::string& GetUserPath(UserPath path, const std::string& new_path = "");
// Returns the path to where the sys file are
std::string GetSysDirectory();
@@ -152,22 +152,22 @@ void SplitFilename83(const std::string& filename, std::array<char, 9>& short_nam
// Splits the path on '/' or '\' and put the components into a vector
// i.e. "C:\Users\Yuzu\Documents\save.bin" becomes {"C:", "Users", "Yuzu", "Documents", "save.bin" }
std::vector<std::string> SplitPathComponents(const std::string& filename);
std::vector<std::string> SplitPathComponents(std::string_view filename);
// Gets all of the text up to the last '/' or '\' in the path.
std::string GetParentPath(const std::string& path);
std::string_view GetParentPath(std::string_view path);
// Gets all of the text after the first '/' or '\' in the path.
std::string GetPathWithoutTop(std::string path);
std::string_view GetPathWithoutTop(std::string_view path);
// Gets the filename of the path
std::string GetFilename(std::string path);
std::string_view GetFilename(std::string_view path);
// Gets the extension of the filename
std::string GetExtensionFromFilename(const std::string& name);
std::string_view GetExtensionFromFilename(std::string_view name);
// Removes the final '/' or '\' if one exists
std::string RemoveTrailingSlash(const std::string& path);
std::string_view RemoveTrailingSlash(std::string_view path);
// Creates a new vector containing indices [first, last) from the original.
template <typename T>
@@ -178,6 +178,9 @@ std::vector<T> SliceVector(const std::vector<T>& vector, size_t first, size_t la
return std::vector<T>(vector.begin() + first, vector.begin() + first + last);
}
// Removes trailing slash, makes all '\\' into '/', and removes duplicate '/'.
std::string SanitizePath(std::string_view path);
// simple wrapper for cstdlib file functions to
// hopefully will make error checking easier
// and make forgetting an fclose() harder

View File

@@ -3,19 +3,20 @@
// Refer to the license.txt file included.
#include <algorithm>
#include <array>
#include <atomic>
#include <chrono>
#include <climits>
#include <condition_variable>
#include <memory>
#include <mutex>
#include <thread>
#include <vector>
#ifdef _WIN32
#include <share.h> // For _SH_DENYWR
#else
#define _SH_DENYWR 0
#endif
#include "common/assert.h"
#include "common/common_funcs.h" // snprintf compatibility define
#include "common/logging/backend.h"
#include "common/logging/log.h"
#include "common/logging/text_formatter.h"
@@ -48,11 +49,11 @@ public:
backends.push_back(std::move(backend));
}
void RemoveBackend(const std::string& backend_name) {
void RemoveBackend(std::string_view backend_name) {
std::lock_guard<std::mutex> lock(writing_mutex);
auto it = std::remove_if(backends.begin(), backends.end(), [&backend_name](const auto& i) {
return !strcmp(i->GetName(), backend_name.c_str());
});
const auto it =
std::remove_if(backends.begin(), backends.end(),
[&backend_name](const auto& i) { return backend_name == i->GetName(); });
backends.erase(it, backends.end());
}
@@ -64,10 +65,10 @@ public:
filter = f;
}
Backend* GetBackend(const std::string& backend_name) {
auto it = std::find_if(backends.begin(), backends.end(), [&backend_name](const auto& i) {
return !strcmp(i->GetName(), backend_name.c_str());
});
Backend* GetBackend(std::string_view backend_name) {
const auto it =
std::find_if(backends.begin(), backends.end(),
[&backend_name](const auto& i) { return backend_name == i->GetName(); });
if (it == backends.end())
return nullptr;
return it->get();
@@ -172,8 +173,11 @@ void FileBackend::Write(const Entry& entry) {
SUB(Service, Friend) \
SUB(Service, FS) \
SUB(Service, HID) \
SUB(Service, LBL) \
SUB(Service, LDN) \
SUB(Service, LM) \
SUB(Service, MM) \
SUB(Service, NFC) \
SUB(Service, NFP) \
SUB(Service, NIFM) \
SUB(Service, NS) \
@@ -265,11 +269,11 @@ void AddBackend(std::unique_ptr<Backend> backend) {
Impl::Instance().AddBackend(std::move(backend));
}
void RemoveBackend(const std::string& backend_name) {
void RemoveBackend(std::string_view backend_name) {
Impl::Instance().RemoveBackend(backend_name);
}
Backend* GetBackend(const std::string& backend_name) {
Backend* GetBackend(std::string_view backend_name) {
return Impl::Instance().GetBackend(backend_name);
}

View File

@@ -4,10 +4,9 @@
#pragma once
#include <chrono>
#include <cstdarg>
#include <memory>
#include <string>
#include <utility>
#include <string_view>
#include "common/file_util.h"
#include "common/logging/filter.h"
#include "common/logging/log.h"
@@ -106,9 +105,9 @@ private:
void AddBackend(std::unique_ptr<Backend> backend);
void RemoveBackend(const std::string& backend_name);
void RemoveBackend(std::string_view backend_name);
Backend* GetBackend(const std::string& backend_name);
Backend* GetBackend(std::string_view backend_name);
/**
* Returns the name of the passed log class as a C-string. Subclasses are separated by periods

View File

@@ -8,39 +8,9 @@
#include "common/string_util.h"
namespace Log {
Filter::Filter(Level default_level) {
ResetAll(default_level);
}
void Filter::ResetAll(Level level) {
class_levels.fill(level);
}
void Filter::SetClassLevel(Class log_class, Level level) {
class_levels[static_cast<size_t>(log_class)] = level;
}
void Filter::ParseFilterString(const std::string& filter_str) {
auto clause_begin = filter_str.cbegin();
while (clause_begin != filter_str.cend()) {
auto clause_end = std::find(clause_begin, filter_str.cend(), ' ');
// If clause isn't empty
if (clause_end != clause_begin) {
ParseFilterRule(clause_begin, clause_end);
}
if (clause_end != filter_str.cend()) {
// Skip over the whitespace
++clause_end;
}
clause_begin = clause_end;
}
}
namespace {
template <typename It>
static Level GetLevelByName(const It begin, const It end) {
Level GetLevelByName(const It begin, const It end) {
for (u8 i = 0; i < static_cast<u8>(Level::Count); ++i) {
const char* level_name = GetLevelName(static_cast<Level>(i));
if (Common::ComparePartialString(begin, end, level_name)) {
@@ -51,7 +21,7 @@ static Level GetLevelByName(const It begin, const It end) {
}
template <typename It>
static Class GetClassByName(const It begin, const It end) {
Class GetClassByName(const It begin, const It end) {
for (ClassType i = 0; i < static_cast<ClassType>(Class::Count); ++i) {
const char* level_name = GetLogClassName(static_cast<Class>(i));
if (Common::ComparePartialString(begin, end, level_name)) {
@@ -61,8 +31,8 @@ static Class GetClassByName(const It begin, const It end) {
return Class::Count;
}
bool Filter::ParseFilterRule(const std::string::const_iterator begin,
const std::string::const_iterator end) {
template <typename Iterator>
bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
auto level_separator = std::find(begin, end, ':');
if (level_separator == end) {
LOG_ERROR(Log, "Invalid log filter. Must specify a log level after `:`: {}",
@@ -77,7 +47,7 @@ bool Filter::ParseFilterRule(const std::string::const_iterator begin,
}
if (Common::ComparePartialString(begin, level_separator, "*")) {
ResetAll(level);
instance.ResetAll(level);
return true;
}
@@ -87,9 +57,40 @@ bool Filter::ParseFilterRule(const std::string::const_iterator begin,
return false;
}
SetClassLevel(log_class, level);
instance.SetClassLevel(log_class, level);
return true;
}
} // Anonymous namespace
Filter::Filter(Level default_level) {
ResetAll(default_level);
}
void Filter::ResetAll(Level level) {
class_levels.fill(level);
}
void Filter::SetClassLevel(Class log_class, Level level) {
class_levels[static_cast<size_t>(log_class)] = level;
}
void Filter::ParseFilterString(std::string_view filter_view) {
auto clause_begin = filter_view.cbegin();
while (clause_begin != filter_view.cend()) {
auto clause_end = std::find(clause_begin, filter_view.cend(), ' ');
// If clause isn't empty
if (clause_end != clause_begin) {
ParseFilterRule(*this, clause_begin, clause_end);
}
if (clause_end != filter_view.cend()) {
// Skip over the whitespace
++clause_end;
}
clause_begin = clause_end;
}
}
bool Filter::CheckMessage(Class log_class, Level level) const {
return static_cast<u8>(level) >= static_cast<u8>(class_levels[static_cast<size_t>(log_class)]);

View File

@@ -6,7 +6,7 @@
#include <array>
#include <cstddef>
#include <string>
#include <string_view>
#include "common/logging/log.h"
namespace Log {
@@ -40,9 +40,7 @@ public:
* - `Service:Info` -- Sets the level of Service to Info.
* - `Service.FS:Trace` -- Sets the level of the Service.FS class to Trace.
*/
void ParseFilterString(const std::string& filter_str);
bool ParseFilterRule(const std::string::const_iterator start,
const std::string::const_iterator end);
void ParseFilterString(std::string_view filter_view);
/// Matches class/level combination against the filter, returning true if it passed.
bool CheckMessage(Class log_class, Level level) const;

View File

@@ -60,8 +60,11 @@ enum class Class : ClassType {
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_MM, ///< The MM (Multimedia) service
Service_NFC, ///< The NFC (Near-field communication) service
Service_NFP, ///< The NFP service
Service_NIFM, ///< The NIFM (Network interface) service
Service_NS, ///< The NS services

View File

@@ -3,7 +3,9 @@
// Refer to the license.txt file included.
#include <array>
#include <utility>
#include <vector>
#include "common/logging/log.h"
#include "common/param_package.h"
#include "common/string_util.h"
@@ -12,10 +14,11 @@ namespace Common {
constexpr char KEY_VALUE_SEPARATOR = ':';
constexpr char PARAM_SEPARATOR = ',';
constexpr char ESCAPE_CHARACTER = '$';
const std::string KEY_VALUE_SEPARATOR_ESCAPE{ESCAPE_CHARACTER, '0'};
const std::string PARAM_SEPARATOR_ESCAPE{ESCAPE_CHARACTER, '1'};
const std::string ESCAPE_CHARACTER_ESCAPE{ESCAPE_CHARACTER, '2'};
constexpr char KEY_VALUE_SEPARATOR_ESCAPE[] = "$0";
constexpr char PARAM_SEPARATOR_ESCAPE[] = "$1";
constexpr char ESCAPE_CHARACTER_ESCAPE[] = "$2";
ParamPackage::ParamPackage(const std::string& serialized) {
std::vector<std::string> pairs;
@@ -35,7 +38,7 @@ ParamPackage::ParamPackage(const std::string& serialized) {
part = Common::ReplaceAll(part, ESCAPE_CHARACTER_ESCAPE, {ESCAPE_CHARACTER});
}
Set(key_value[0], key_value[1]);
Set(key_value[0], std::move(key_value[1]));
}
}
@@ -101,16 +104,16 @@ float ParamPackage::Get(const std::string& key, float default_value) const {
}
}
void ParamPackage::Set(const std::string& key, const std::string& value) {
data[key] = value;
void ParamPackage::Set(const std::string& key, std::string value) {
data.insert_or_assign(key, std::move(value));
}
void ParamPackage::Set(const std::string& key, int value) {
data[key] = std::to_string(value);
data.insert_or_assign(key, std::to_string(value));
}
void ParamPackage::Set(const std::string& key, float value) {
data[key] = std::to_string(value);
data.insert_or_assign(key, std::to_string(value));
}
bool ParamPackage::Has(const std::string& key) const {

View File

@@ -28,7 +28,7 @@ public:
std::string Get(const std::string& key, const std::string& default_value) const;
int Get(const std::string& key, int default_value) const;
float Get(const std::string& key, float default_value) const;
void Set(const std::string& key, const std::string& value);
void Set(const std::string& key, std::string value);
void Set(const std::string& key, int value);
void Set(const std::string& key, float value);
bool Has(const std::string& key) const;

View File

@@ -162,21 +162,21 @@ void SplitString(const std::string& str, const char delim, std::vector<std::stri
std::istringstream iss(str);
output.resize(1);
while (std::getline(iss, *output.rbegin(), delim))
output.push_back("");
while (std::getline(iss, *output.rbegin(), delim)) {
output.emplace_back();
}
output.pop_back();
}
std::string TabsToSpaces(int tab_size, const std::string& in) {
const std::string spaces(tab_size, ' ');
std::string out(in);
std::string TabsToSpaces(int tab_size, std::string in) {
size_t i = 0;
while (out.npos != (i = out.find('\t')))
out.replace(i, 1, spaces);
return out;
while ((i = in.find('\t')) != std::string::npos) {
in.replace(i, 1, tab_size, ' ');
}
return in;
}
std::string ReplaceAll(std::string result, const std::string& src, const std::string& dest) {
@@ -220,31 +220,37 @@ std::u16string UTF8ToUTF16(const std::string& input) {
}
static std::wstring CPToUTF16(u32 code_page, const std::string& input) {
auto const size =
const auto size =
MultiByteToWideChar(code_page, 0, input.data(), static_cast<int>(input.size()), nullptr, 0);
std::wstring output;
output.resize(size);
if (size == 0) {
return L"";
}
if (size == 0 ||
size != MultiByteToWideChar(code_page, 0, input.data(), static_cast<int>(input.size()),
&output[0], static_cast<int>(output.size())))
std::wstring output(size, L'\0');
if (size != MultiByteToWideChar(code_page, 0, input.data(), static_cast<int>(input.size()),
&output[0], static_cast<int>(output.size()))) {
output.clear();
}
return output;
}
std::string UTF16ToUTF8(const std::wstring& input) {
auto const size = WideCharToMultiByte(CP_UTF8, 0, input.data(), static_cast<int>(input.size()),
const auto size = WideCharToMultiByte(CP_UTF8, 0, input.data(), static_cast<int>(input.size()),
nullptr, 0, nullptr, nullptr);
if (size == 0) {
return "";
}
std::string output;
output.resize(size);
std::string output(size, '\0');
if (size == 0 ||
size != WideCharToMultiByte(CP_UTF8, 0, input.data(), static_cast<int>(input.size()),
&output[0], static_cast<int>(output.size()), nullptr, nullptr))
if (size != WideCharToMultiByte(CP_UTF8, 0, input.data(), static_cast<int>(input.size()),
&output[0], static_cast<int>(output.size()), nullptr,
nullptr)) {
output.clear();
}
return output;
}
@@ -265,8 +271,6 @@ std::string CP1252ToUTF8(const std::string& input) {
template <typename T>
static std::string CodeToUTF8(const char* fromcode, const std::basic_string<T>& input) {
std::string result;
iconv_t const conv_desc = iconv_open("UTF-8", fromcode);
if ((iconv_t)(-1) == conv_desc) {
LOG_ERROR(Common, "Iconv initialization failure [{}]: {}", fromcode, strerror(errno));
@@ -278,8 +282,7 @@ static std::string CodeToUTF8(const char* fromcode, const std::basic_string<T>&
// Multiply by 4, which is the max number of bytes to encode a codepoint
const size_t out_buffer_size = 4 * in_bytes;
std::string out_buffer;
out_buffer.resize(out_buffer_size);
std::string out_buffer(out_buffer_size, '\0');
auto src_buffer = &input[0];
size_t src_bytes = in_bytes;
@@ -304,6 +307,7 @@ static std::string CodeToUTF8(const char* fromcode, const std::basic_string<T>&
}
}
std::string result;
out_buffer.resize(out_buffer_size - dst_bytes);
out_buffer.swap(result);
@@ -313,8 +317,6 @@ static std::string CodeToUTF8(const char* fromcode, const std::basic_string<T>&
}
std::u16string UTF8ToUTF16(const std::string& input) {
std::u16string result;
iconv_t const conv_desc = iconv_open("UTF-16LE", "UTF-8");
if ((iconv_t)(-1) == conv_desc) {
LOG_ERROR(Common, "Iconv initialization failure [UTF-8]: {}", strerror(errno));
@@ -326,8 +328,7 @@ std::u16string UTF8ToUTF16(const std::string& input) {
// Multiply by 4, which is the max number of bytes to encode a codepoint
const size_t out_buffer_size = 4 * sizeof(char16_t) * in_bytes;
std::u16string out_buffer;
out_buffer.resize(out_buffer_size);
std::u16string out_buffer(out_buffer_size, char16_t{});
char* src_buffer = const_cast<char*>(&input[0]);
size_t src_bytes = in_bytes;
@@ -352,6 +353,7 @@ std::u16string UTF8ToUTF16(const std::string& input) {
}
}
std::u16string result;
out_buffer.resize(out_buffer_size - dst_bytes);
out_buffer.swap(result);

View File

@@ -57,7 +57,7 @@ static bool TryParse(const std::string& str, N* const output) {
return false;
}
std::string TabsToSpaces(int tab_size, const std::string& in);
std::string TabsToSpaces(int tab_size, std::string in);
void SplitString(const std::string& str, char delim, std::vector<std::string>& output);

View File

@@ -1,85 +0,0 @@
// Copyright 2015 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <algorithm>
#include <mutex>
namespace Common {
template <typename T>
class SynchronizedWrapper;
/**
* Synchronized reference, that keeps a SynchronizedWrapper's mutex locked during its lifetime. This
* greatly reduces the chance that someone will access the wrapped resource without locking the
* mutex.
*/
template <typename T>
class SynchronizedRef {
public:
SynchronizedRef(SynchronizedWrapper<T>& wrapper) : wrapper(&wrapper) {
wrapper.mutex.lock();
}
SynchronizedRef(SynchronizedRef&) = delete;
SynchronizedRef(SynchronizedRef&& o) : wrapper(o.wrapper) {
o.wrapper = nullptr;
}
~SynchronizedRef() {
if (wrapper)
wrapper->mutex.unlock();
}
SynchronizedRef& operator=(SynchronizedRef&) = delete;
SynchronizedRef& operator=(SynchronizedRef&& o) {
std::swap(wrapper, o.wrapper);
return *this;
}
T& operator*() {
return wrapper->data;
}
const T& operator*() const {
return wrapper->data;
}
T* operator->() {
return &wrapper->data;
}
const T* operator->() const {
return &wrapper->data;
}
private:
SynchronizedWrapper<T>* wrapper;
};
/**
* Wraps an object, only allowing access to it via a locking reference wrapper. Good to ensure no
* one forgets to lock a mutex before acessing an object. To access the wrapped object construct a
* SyncronizedRef on this wrapper. Inspired by Rust's Mutex type
* (http://doc.rust-lang.org/std/sync/struct.Mutex.html).
*/
template <typename T>
class SynchronizedWrapper {
public:
template <typename... Args>
SynchronizedWrapper(Args&&... args) : data(std::forward<Args>(args)...) {}
SynchronizedRef<T> Lock() {
return {*this};
}
private:
template <typename U>
friend class SynchronizedRef;
std::mutex mutex;
T data;
};
} // namespace Common

View File

@@ -1,5 +1,7 @@
add_library(core STATIC
arm/arm_interface.h
arm/exclusive_monitor.cpp
arm/exclusive_monitor.h
arm/unicorn/arm_unicorn.cpp
arm/unicorn/arm_unicorn.h
core.cpp
@@ -8,8 +10,12 @@ add_library(core STATIC
core_cpu.h
core_timing.cpp
core_timing.h
core_timing_util.cpp
core_timing_util.h
file_sys/content_archive.cpp
file_sys/content_archive.h
file_sys/control_metadata.cpp
file_sys/control_metadata.h
file_sys/directory.h
file_sys/errors.h
file_sys/mode.h
@@ -17,6 +23,8 @@ add_library(core STATIC
file_sys/partition_filesystem.h
file_sys/program_metadata.cpp
file_sys/program_metadata.h
file_sys/romfs.cpp
file_sys/romfs.h
file_sys/romfs_factory.cpp
file_sys/romfs_factory.h
file_sys/savedata_factory.cpp
@@ -29,6 +37,8 @@ add_library(core STATIC
file_sys/vfs_offset.h
file_sys/vfs_real.cpp
file_sys/vfs_real.h
file_sys/vfs_vector.cpp
file_sys/vfs_vector.h
frontend/emu_window.cpp
frontend/emu_window.h
frontend/framebuffer_layout.cpp
@@ -36,8 +46,6 @@ add_library(core STATIC
frontend/input.h
gdbstub/gdbstub.cpp
gdbstub/gdbstub.h
hle/config_mem.cpp
hle/config_mem.h
hle/ipc.h
hle/ipc_helpers.h
hle/kernel/address_arbiter.cpp
@@ -112,26 +120,34 @@ add_library(core STATIC
hle/service/apm/apm.h
hle/service/apm/interface.cpp
hle/service/apm/interface.h
hle/service/audio/audio.cpp
hle/service/audio/audio.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_u.cpp
hle/service/audio/audout_u.h
hle/service/audio/audrec_u.cpp
hle/service/audio/audrec_u.h
hle/service/audio/audren_u.cpp
hle/service/audio/audren_u.h
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
hle/service/audio/hwopus.cpp
hle/service/audio/hwopus.h
hle/service/bcat/module.cpp
hle/service/bcat/module.h
hle/service/bcat/bcat.cpp
hle/service/bcat/bcat.h
hle/service/bcat/module.cpp
hle/service/bcat/module.h
hle/service/btdrv/btdrv.cpp
hle/service/btdrv/btdrv.h
hle/service/erpt/erpt.cpp
hle/service/erpt/erpt.h
hle/service/es/es.cpp
hle/service/es/es.h
hle/service/eupld/eupld.cpp
hle/service/eupld/eupld.h
hle/service/fatal/fatal.cpp
hle/service/fatal/fatal.h
hle/service/fatal/fatal_p.cpp
@@ -144,28 +160,36 @@ add_library(core STATIC
hle/service/filesystem/fsp_srv.h
hle/service/friend/friend.cpp
hle/service/friend/friend.h
hle/service/friend/friend_a.cpp
hle/service/friend/friend_a.h
hle/service/friend/friend_u.cpp
hle/service/friend/friend_u.h
hle/service/friend/interface.cpp
hle/service/friend/interface.h
hle/service/grc/grc.cpp
hle/service/grc/grc.h
hle/service/hid/hid.cpp
hle/service/hid/hid.h
hle/service/hid/irs.cpp
hle/service/hid/irs.h
hle/service/hid/xcd.cpp
hle/service/hid/xcd.h
hle/service/lbl/lbl.cpp
hle/service/lbl/lbl.h
hle/service/ldn/ldn.cpp
hle/service/ldn/ldn.h
hle/service/ldr/ldr.cpp
hle/service/ldr/ldr.h
hle/service/lm/lm.cpp
hle/service/lm/lm.h
hle/service/mm/mm_u.cpp
hle/service/mm/mm_u.h
hle/service/nifm/nifm.cpp
hle/service/nifm/nifm.h
hle/service/nifm/nifm_a.cpp
hle/service/nifm/nifm_a.h
hle/service/nifm/nifm_s.cpp
hle/service/nifm/nifm_s.h
hle/service/nifm/nifm_u.cpp
hle/service/nifm/nifm_u.h
hle/service/nfc/nfc.cpp
hle/service/nfc/nfc.h
hle/service/nfp/nfp.cpp
hle/service/nfp/nfp.h
hle/service/nfp/nfp_user.cpp
hle/service/nfp/nfp_user.h
hle/service/nifm/nifm.cpp
hle/service/nifm/nifm.h
hle/service/nim/nim.cpp
hle/service/nim/nim.h
hle/service/ns/ns.cpp
hle/service/ns/ns.h
hle/service/ns/pl_u.cpp
@@ -199,6 +223,8 @@ add_library(core STATIC
hle/service/pctl/module.h
hle/service/pctl/pctl.cpp
hle/service/pctl/pctl.h
hle/service/pm/pm.cpp
hle/service/pm/pm.h
hle/service/prepo/prepo.cpp
hle/service/prepo/prepo.h
hle/service/service.cpp
@@ -219,6 +245,8 @@ add_library(core STATIC
hle/service/sm/sm.h
hle/service/sockets/bsd.cpp
hle/service/sockets/bsd.h
hle/service/sockets/ethc.cpp
hle/service/sockets/ethc.h
hle/service/sockets/nsd.cpp
hle/service/sockets/nsd.h
hle/service/sockets/sfdnsres.cpp
@@ -233,12 +261,10 @@ add_library(core STATIC
hle/service/spl/spl.h
hle/service/ssl/ssl.cpp
hle/service/ssl/ssl.h
hle/service/time/interface.cpp
hle/service/time/interface.h
hle/service/time/time.cpp
hle/service/time/time.h
hle/service/time/time_s.cpp
hle/service/time/time_s.h
hle/service/time/time_u.cpp
hle/service/time/time_u.h
hle/service/vi/vi.cpp
hle/service/vi/vi.h
hle/service/vi/vi_m.cpp
@@ -247,8 +273,6 @@ add_library(core STATIC
hle/service/vi/vi_s.h
hle/service/vi/vi_u.cpp
hle/service/vi/vi_u.h
hle/shared_page.cpp
hle/shared_page.h
hw/hw.cpp
hw/hw.h
hw/lcd.cpp
@@ -285,8 +309,8 @@ add_library(core STATIC
create_target_directory_groups(core)
target_link_libraries(core PUBLIC common PRIVATE video_core)
target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt lz4_static unicorn)
target_link_libraries(core PUBLIC common PRIVATE audio_core video_core)
target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt lz4_static opus unicorn)
if (ARCHITECTURE_x86_64)
target_sources(core PRIVATE

View File

@@ -20,9 +20,6 @@ public:
u64 cpsr;
std::array<u128, 32> fpu_registers;
u64 fpscr;
// TODO(bunnei): Fix once we have proper support for tpidrro_el0, etc. in the JIT
VAddr tls_address;
};
/// Runs the CPU until an event happens
@@ -104,6 +101,10 @@ public:
virtual void SetTlsAddress(VAddr address) = 0;
virtual u64 GetTPIDR_EL0() const = 0;
virtual void SetTPIDR_EL0(u64 value) = 0;
/**
* Saves the current CPU context
* @param ctx Thread context to save

View File

@@ -102,18 +102,28 @@ public:
u64 tpidr_el0 = 0;
};
std::unique_ptr<Dynarmic::A64::Jit> MakeJit(const std::unique_ptr<ARM_Dynarmic_Callbacks>& cb) {
const auto page_table = Core::CurrentProcess()->vm_manager.page_table.pointers.data();
std::unique_ptr<Dynarmic::A64::Jit> ARM_Dynarmic::MakeJit() const {
auto** const page_table = Core::CurrentProcess()->vm_manager.page_table.pointers.data();
Dynarmic::A64::UserConfig config;
// Callbacks
config.callbacks = cb.get();
// Memory
config.page_table = reinterpret_cast<void**>(page_table);
config.page_table_address_space_bits = Memory::ADDRESS_SPACE_BITS;
config.silently_mirror_page_table = false;
// Multi-process state
config.processor_id = core_index;
config.global_monitor = &exclusive_monitor->monitor;
// System registers
config.tpidrro_el0 = &cb->tpidrro_el0;
config.tpidr_el0 = &cb->tpidr_el0;
config.dczid_el0 = 4;
config.ctr_el0 = 0x8444c004;
config.page_table = reinterpret_cast<void**>(page_table);
config.page_table_address_space_bits = Memory::ADDRESS_SPACE_BITS;
config.silently_mirror_page_table = false;
return std::make_unique<Dynarmic::A64::Jit>(config);
}
@@ -128,8 +138,11 @@ void ARM_Dynarmic::Step() {
cb->InterpreterFallback(jit->GetPC(), 1);
}
ARM_Dynarmic::ARM_Dynarmic()
: cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), jit(MakeJit(cb)) {
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;
inner_unicorn.SaveContext(ctx);
LoadContext(ctx);
@@ -196,6 +209,14 @@ void ARM_Dynarmic::SetTlsAddress(u64 address) {
cb->tpidrro_el0 = address;
}
u64 ARM_Dynarmic::GetTPIDR_EL0() const {
return cb->tpidr_el0;
}
void ARM_Dynarmic::SetTPIDR_EL0(u64 value) {
cb->tpidr_el0 = value;
}
void ARM_Dynarmic::SaveContext(ARM_Interface::ThreadContext& ctx) {
ctx.cpu_registers = jit->GetRegisters();
ctx.sp = jit->GetSP();
@@ -203,7 +224,6 @@ void ARM_Dynarmic::SaveContext(ARM_Interface::ThreadContext& ctx) {
ctx.cpsr = jit->GetPstate();
ctx.fpu_registers = jit->GetVectors();
ctx.fpscr = jit->GetFpcr();
ctx.tls_address = cb->tpidrro_el0;
}
void ARM_Dynarmic::LoadContext(const ARM_Interface::ThreadContext& ctx) {
@@ -213,7 +233,6 @@ void ARM_Dynarmic::LoadContext(const ARM_Interface::ThreadContext& ctx) {
jit->SetPstate(static_cast<u32>(ctx.cpsr));
jit->SetVectors(ctx.fpu_registers);
jit->SetFpcr(static_cast<u32>(ctx.fpscr));
cb->tpidrro_el0 = ctx.tls_address;
}
void ARM_Dynarmic::PrepareReschedule() {
@@ -231,6 +250,45 @@ void ARM_Dynarmic::ClearExclusiveState() {
}
void ARM_Dynarmic::PageTableChanged() {
jit = MakeJit(cb);
jit = MakeJit();
current_page_table = Memory::GetCurrentPageTable();
}
DynarmicExclusiveMonitor::DynarmicExclusiveMonitor(size_t core_count) : monitor(core_count) {}
DynarmicExclusiveMonitor::~DynarmicExclusiveMonitor() = default;
void DynarmicExclusiveMonitor::SetExclusive(size_t core_index, VAddr addr) {
// Size doesn't actually matter.
monitor.Mark(core_index, addr, 16);
}
void DynarmicExclusiveMonitor::ClearExclusive() {
monitor.Clear();
}
bool DynarmicExclusiveMonitor::ExclusiveWrite8(size_t core_index, VAddr vaddr, u8 value) {
return monitor.DoExclusiveOperation(core_index, vaddr, 1,
[&] { Memory::Write8(vaddr, value); });
}
bool DynarmicExclusiveMonitor::ExclusiveWrite16(size_t core_index, VAddr vaddr, u16 value) {
return monitor.DoExclusiveOperation(core_index, vaddr, 2,
[&] { Memory::Write16(vaddr, value); });
}
bool DynarmicExclusiveMonitor::ExclusiveWrite32(size_t core_index, VAddr vaddr, u32 value) {
return monitor.DoExclusiveOperation(core_index, vaddr, 4,
[&] { Memory::Write32(vaddr, value); });
}
bool DynarmicExclusiveMonitor::ExclusiveWrite64(size_t core_index, VAddr vaddr, u64 value) {
return monitor.DoExclusiveOperation(core_index, vaddr, 8,
[&] { Memory::Write64(vaddr, value); });
}
bool DynarmicExclusiveMonitor::ExclusiveWrite128(size_t core_index, VAddr vaddr, u128 value) {
return monitor.DoExclusiveOperation(core_index, vaddr, 16, [&] {
Memory::Write64(vaddr, value[0]);
Memory::Write64(vaddr, value[1]);
});
}

View File

@@ -6,15 +6,18 @@
#include <memory>
#include <dynarmic/A64/a64.h>
#include <dynarmic/A64/exclusive_monitor.h>
#include "common/common_types.h"
#include "core/arm/arm_interface.h"
#include "core/arm/exclusive_monitor.h"
#include "core/arm/unicorn/arm_unicorn.h"
class ARM_Dynarmic_Callbacks;
class DynarmicExclusiveMonitor;
class ARM_Dynarmic final : public ARM_Interface {
public:
ARM_Dynarmic();
ARM_Dynarmic(std::shared_ptr<ExclusiveMonitor> exclusive_monitor, size_t core_index);
~ARM_Dynarmic();
void MapBackingMemory(VAddr address, size_t size, u8* memory,
@@ -34,6 +37,8 @@ public:
void SetCPSR(u32 cpsr) override;
VAddr GetTlsAddress() const override;
void SetTlsAddress(VAddr address) override;
void SetTPIDR_EL0(u64 value) override;
u64 GetTPIDR_EL0() const override;
void SaveContext(ThreadContext& ctx) override;
void LoadContext(const ThreadContext& ctx) override;
@@ -45,10 +50,34 @@ public:
void PageTableChanged() override;
private:
std::unique_ptr<Dynarmic::A64::Jit> MakeJit() const;
friend class ARM_Dynarmic_Callbacks;
std::unique_ptr<ARM_Dynarmic_Callbacks> cb;
std::unique_ptr<Dynarmic::A64::Jit> jit;
ARM_Unicorn inner_unicorn;
size_t core_index;
std::shared_ptr<DynarmicExclusiveMonitor> exclusive_monitor;
Memory::PageTable* current_page_table = nullptr;
};
class DynarmicExclusiveMonitor final : public ExclusiveMonitor {
public:
explicit DynarmicExclusiveMonitor(size_t core_count);
~DynarmicExclusiveMonitor();
void SetExclusive(size_t core_index, VAddr addr) override;
void ClearExclusive() override;
bool ExclusiveWrite8(size_t core_index, VAddr vaddr, u8 value) override;
bool ExclusiveWrite16(size_t core_index, VAddr vaddr, u16 value) override;
bool ExclusiveWrite32(size_t core_index, VAddr vaddr, u32 value) override;
bool ExclusiveWrite64(size_t core_index, VAddr vaddr, u64 value) override;
bool ExclusiveWrite128(size_t core_index, VAddr vaddr, u128 value) override;
private:
friend class ARM_Dynarmic;
Dynarmic::A64::ExclusiveMonitor monitor;
};

View File

@@ -0,0 +1,7 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/arm/exclusive_monitor.h"
ExclusiveMonitor::~ExclusiveMonitor() = default;

View File

@@ -0,0 +1,21 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "common/common_types.h"
class ExclusiveMonitor {
public:
virtual ~ExclusiveMonitor();
virtual void SetExclusive(size_t core_index, VAddr addr) = 0;
virtual void ClearExclusive() = 0;
virtual bool ExclusiveWrite8(size_t core_index, VAddr vaddr, u8 value) = 0;
virtual bool ExclusiveWrite16(size_t core_index, VAddr vaddr, u16 value) = 0;
virtual bool ExclusiveWrite32(size_t core_index, VAddr vaddr, u32 value) = 0;
virtual bool ExclusiveWrite64(size_t core_index, VAddr vaddr, u64 value) = 0;
virtual bool ExclusiveWrite128(size_t core_index, VAddr vaddr, u128 value) = 0;
};

View File

@@ -169,6 +169,16 @@ void ARM_Unicorn::SetTlsAddress(VAddr base) {
CHECKED(uc_reg_write(uc, UC_ARM64_REG_TPIDRRO_EL0, &base));
}
u64 ARM_Unicorn::GetTPIDR_EL0() const {
u64 value{};
CHECKED(uc_reg_read(uc, UC_ARM64_REG_TPIDR_EL0, &value));
return value;
}
void ARM_Unicorn::SetTPIDR_EL0(u64 value) {
CHECKED(uc_reg_write(uc, UC_ARM64_REG_TPIDR_EL0, &value));
}
void ARM_Unicorn::Run() {
if (GDBStub::IsServerEnabled()) {
ExecuteInstructions(std::max(4000000, 0));
@@ -220,8 +230,6 @@ void ARM_Unicorn::SaveContext(ARM_Interface::ThreadContext& ctx) {
CHECKED(uc_reg_read_batch(uc, uregs, tregs, 31));
ctx.tls_address = GetTlsAddress();
for (int i = 0; i < 32; ++i) {
uregs[i] = UC_ARM64_REG_Q0 + i;
tregs[i] = &ctx.fpu_registers[i];
@@ -249,8 +257,6 @@ void ARM_Unicorn::LoadContext(const ARM_Interface::ThreadContext& ctx) {
CHECKED(uc_reg_write_batch(uc, uregs, tregs, 31));
SetTlsAddress(ctx.tls_address);
for (auto i = 0; i < 32; ++i) {
uregs[i] = UC_ARM64_REG_Q0 + i;
tregs[i] = (void*)&ctx.fpu_registers[i];

View File

@@ -28,6 +28,8 @@ public:
void SetCPSR(u32 cpsr) override;
VAddr GetTlsAddress() const override;
void SetTlsAddress(VAddr address) override;
void SetTPIDR_EL0(u64 value) override;
u64 GetTPIDR_EL0() const override;
void SaveContext(ThreadContext& ctx) override;
void LoadContext(const ThreadContext& ctx) override;
void PrepareReschedule() override;

View File

@@ -171,11 +171,13 @@ System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) {
current_process = Kernel::Process::Create("main");
cpu_barrier = std::make_shared<CpuBarrier>();
cpu_exclusive_monitor = Cpu::MakeExclusiveMonitor(cpu_cores.size());
for (size_t index = 0; index < cpu_cores.size(); ++index) {
cpu_cores[index] = std::make_shared<Cpu>(cpu_barrier, index);
cpu_cores[index] = std::make_shared<Cpu>(cpu_exclusive_monitor, cpu_barrier, index);
}
gpu_core = std::make_unique<Tegra::GPU>();
audio_core = std::make_unique<AudioCore::AudioOut>();
telemetry_session = std::make_unique<Core::TelemetrySession>();
service_manager = std::make_shared<Service::SM::ServiceManager>();
@@ -227,6 +229,7 @@ void System::Shutdown() {
service_manager.reset();
telemetry_session.reset();
gpu_core.reset();
audio_core.reset();
// Close all CPU/threading state
cpu_barrier->NotifyEnd();

View File

@@ -8,7 +8,9 @@
#include <memory>
#include <string>
#include <thread>
#include "audio_core/audio_out.h"
#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/scheduler.h"
@@ -114,6 +116,11 @@ public:
return CurrentCpuCore().ArmInterface();
}
/// Gets the index of the currently running CPU core
size_t CurrentCoreIndex() {
return CurrentCpuCore().CoreIndex();
}
/// Gets an ARM interface to the CPU core with the specified index
ARM_Interface& ArmInterface(size_t core_index);
@@ -125,11 +132,21 @@ public:
return *gpu_core;
}
/// Gets the AudioCore interface
AudioCore::AudioOut& AudioCore() {
return *audio_core;
}
/// Gets the scheduler for the CPU core that is currently running
Kernel::Scheduler& CurrentScheduler() {
return *CurrentCpuCore().Scheduler();
}
/// Gets the exclusive monitor
ExclusiveMonitor& Monitor() {
return *cpu_exclusive_monitor;
}
/// Gets the scheduler for the CPU core with the specified index
const std::shared_ptr<Kernel::Scheduler>& Scheduler(size_t core_index);
@@ -184,8 +201,10 @@ private:
/// AppLoader used to load the current executing application
std::unique_ptr<Loader::AppLoader> app_loader;
std::unique_ptr<Tegra::GPU> gpu_core;
std::unique_ptr<AudioCore::AudioOut> audio_core;
std::shared_ptr<Tegra::DebugContext> debug_context;
Kernel::SharedPtr<Kernel::Process> current_process;
std::shared_ptr<ExclusiveMonitor> cpu_exclusive_monitor;
std::shared_ptr<CpuBarrier> cpu_barrier;
std::array<std::shared_ptr<Cpu>, NUM_CPU_CORES> cpu_cores;
std::array<std::unique_ptr<std::thread>, NUM_CPU_CORES - 1> cpu_core_threads;

View File

@@ -48,14 +48,15 @@ bool CpuBarrier::Rendezvous() {
return false;
}
Cpu::Cpu(std::shared_ptr<CpuBarrier> cpu_barrier, size_t core_index)
Cpu::Cpu(std::shared_ptr<ExclusiveMonitor> exclusive_monitor,
std::shared_ptr<CpuBarrier> cpu_barrier, size_t core_index)
: cpu_barrier{std::move(cpu_barrier)}, core_index{core_index} {
if (Settings::values.use_cpu_jit) {
#ifdef ARCHITECTURE_x86_64
arm_interface = std::make_shared<ARM_Dynarmic>();
arm_interface = std::make_shared<ARM_Dynarmic>(exclusive_monitor, core_index);
#else
cpu_core = std::make_shared<ARM_Unicorn>();
arm_interface = std::make_shared<ARM_Unicorn>();
LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available");
#endif
} else {
@@ -65,6 +66,18 @@ Cpu::Cpu(std::shared_ptr<CpuBarrier> cpu_barrier, size_t core_index)
scheduler = std::make_shared<Kernel::Scheduler>(arm_interface.get());
}
std::shared_ptr<ExclusiveMonitor> Cpu::MakeExclusiveMonitor(size_t num_cores) {
if (Settings::values.use_cpu_jit) {
#ifdef ARCHITECTURE_x86_64
return std::make_shared<DynarmicExclusiveMonitor>(num_cores);
#else
return nullptr; // TODO(merry): Passthrough exclusive monitor
#endif
} else {
return nullptr; // TODO(merry): Passthrough exclusive monitor
}
}
void Cpu::RunLoop(bool tight_loop) {
// Wait for all other CPU cores to complete the previous slice, such that they run in lock-step
if (!cpu_barrier->Rendezvous()) {

View File

@@ -10,6 +10,7 @@
#include <mutex>
#include <string>
#include "common/common_types.h"
#include "core/arm/exclusive_monitor.h"
class ARM_Interface;
@@ -40,7 +41,8 @@ private:
class Cpu {
public:
Cpu(std::shared_ptr<CpuBarrier> cpu_barrier, size_t core_index);
Cpu(std::shared_ptr<ExclusiveMonitor> exclusive_monitor,
std::shared_ptr<CpuBarrier> cpu_barrier, size_t core_index);
void RunLoop(bool tight_loop = true);
@@ -64,6 +66,12 @@ public:
return core_index == 0;
}
size_t CoreIndex() const {
return core_index;
}
static std::shared_ptr<ExclusiveMonitor> MakeExclusiveMonitor(size_t num_cores);
private:
void Reschedule();

View File

@@ -5,17 +5,15 @@
#include "core/core_timing.h"
#include <algorithm>
#include <cinttypes>
#include <limits>
#include <mutex>
#include <string>
#include <tuple>
#include <unordered_map>
#include <vector>
#include "common/assert.h"
#include "common/logging/log.h"
#include "common/thread.h"
#include "common/threadsafe_queue.h"
#include "core/core_timing_util.h"
namespace CoreTiming {
@@ -59,7 +57,6 @@ static u64 event_fifo_id;
static Common::MPSCQueue<Event, false> ts_queue;
constexpr int MAX_SLICE_LENGTH = 20000;
constexpr u64 MAX_VALUE_TO_MULTIPLY = std::numeric_limits<s64>::max() / BASE_CLOCK_RATE;
static s64 idled_cycles;
@@ -72,54 +69,6 @@ static EventType* ev_lost = nullptr;
static void EmptyTimedCallback(u64 userdata, s64 cyclesLate) {}
s64 usToCycles(s64 us) {
if (us / 1000000 > MAX_VALUE_TO_MULTIPLY) {
LOG_ERROR(Core_Timing, "Integer overflow, use max value");
return std::numeric_limits<s64>::max();
}
if (us > MAX_VALUE_TO_MULTIPLY) {
LOG_DEBUG(Core_Timing, "Time very big, do rounding");
return BASE_CLOCK_RATE * (us / 1000000);
}
return (BASE_CLOCK_RATE * us) / 1000000;
}
s64 usToCycles(u64 us) {
if (us / 1000000 > MAX_VALUE_TO_MULTIPLY) {
LOG_ERROR(Core_Timing, "Integer overflow, use max value");
return std::numeric_limits<s64>::max();
}
if (us > MAX_VALUE_TO_MULTIPLY) {
LOG_DEBUG(Core_Timing, "Time very big, do rounding");
return BASE_CLOCK_RATE * static_cast<s64>(us / 1000000);
}
return (BASE_CLOCK_RATE * static_cast<s64>(us)) / 1000000;
}
s64 nsToCycles(s64 ns) {
if (ns / 1000000000 > MAX_VALUE_TO_MULTIPLY) {
LOG_ERROR(Core_Timing, "Integer overflow, use max value");
return std::numeric_limits<s64>::max();
}
if (ns > MAX_VALUE_TO_MULTIPLY) {
LOG_DEBUG(Core_Timing, "Time very big, do rounding");
return BASE_CLOCK_RATE * (ns / 1000000000);
}
return (BASE_CLOCK_RATE * ns) / 1000000000;
}
s64 nsToCycles(u64 ns) {
if (ns / 1000000000 > MAX_VALUE_TO_MULTIPLY) {
LOG_ERROR(Core_Timing, "Integer overflow, use max value");
return std::numeric_limits<s64>::max();
}
if (ns > MAX_VALUE_TO_MULTIPLY) {
LOG_DEBUG(Core_Timing, "Time very big, do rounding");
return BASE_CLOCK_RATE * (static_cast<s64>(ns) / 1000000000);
}
return (BASE_CLOCK_RATE * static_cast<s64>(ns)) / 1000000000;
}
EventType* RegisterEvent(const std::string& name, TimedCallback callback) {
// check for existing type with same name.
// we want event type names to remain unique so that we can use them for serialization.

View File

@@ -23,59 +23,6 @@
namespace CoreTiming {
// The below clock rate is based on Switch's clockspeed being widely known as 1.020GHz
// The exact value used is of course unverified.
constexpr u64 BASE_CLOCK_RATE = 1019215872; // Switch clock speed is 1020MHz un/docked
inline s64 msToCycles(int ms) {
// since ms is int there is no way to overflow
return BASE_CLOCK_RATE * static_cast<s64>(ms) / 1000;
}
inline s64 msToCycles(float ms) {
return static_cast<s64>(BASE_CLOCK_RATE * (0.001f) * ms);
}
inline s64 msToCycles(double ms) {
return static_cast<s64>(BASE_CLOCK_RATE * (0.001) * ms);
}
inline s64 usToCycles(float us) {
return static_cast<s64>(BASE_CLOCK_RATE * (0.000001f) * us);
}
inline s64 usToCycles(int us) {
return (BASE_CLOCK_RATE * static_cast<s64>(us) / 1000000);
}
s64 usToCycles(s64 us);
s64 usToCycles(u64 us);
inline s64 nsToCycles(float ns) {
return static_cast<s64>(BASE_CLOCK_RATE * (0.000000001f) * ns);
}
inline s64 nsToCycles(int ns) {
return BASE_CLOCK_RATE * static_cast<s64>(ns) / 1000000000;
}
s64 nsToCycles(s64 ns);
s64 nsToCycles(u64 ns);
inline u64 cyclesToNs(s64 cycles) {
return cycles * 1000000000 / BASE_CLOCK_RATE;
}
inline s64 cyclesToUs(s64 cycles) {
return cycles * 1000000 / BASE_CLOCK_RATE;
}
inline u64 cyclesToMs(s64 cycles) {
return cycles * 1000 / BASE_CLOCK_RATE;
}
/**
* CoreTiming begins at the boundary of timing slice -1. An initial call to Advance() is
* required to end slice -1 and start slice 0 before the first cycle of code is executed.

View File

@@ -0,0 +1,63 @@
// Copyright 2008 Dolphin Emulator Project / 2017 Citra Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include "core/core_timing_util.h"
#include <cinttypes>
#include <limits>
#include "common/logging/log.h"
namespace CoreTiming {
constexpr u64 MAX_VALUE_TO_MULTIPLY = std::numeric_limits<s64>::max() / BASE_CLOCK_RATE;
s64 usToCycles(s64 us) {
if (us / 1000000 > MAX_VALUE_TO_MULTIPLY) {
LOG_ERROR(Core_Timing, "Integer overflow, use max value");
return std::numeric_limits<s64>::max();
}
if (us > MAX_VALUE_TO_MULTIPLY) {
LOG_DEBUG(Core_Timing, "Time very big, do rounding");
return BASE_CLOCK_RATE * (us / 1000000);
}
return (BASE_CLOCK_RATE * us) / 1000000;
}
s64 usToCycles(u64 us) {
if (us / 1000000 > MAX_VALUE_TO_MULTIPLY) {
LOG_ERROR(Core_Timing, "Integer overflow, use max value");
return std::numeric_limits<s64>::max();
}
if (us > MAX_VALUE_TO_MULTIPLY) {
LOG_DEBUG(Core_Timing, "Time very big, do rounding");
return BASE_CLOCK_RATE * static_cast<s64>(us / 1000000);
}
return (BASE_CLOCK_RATE * static_cast<s64>(us)) / 1000000;
}
s64 nsToCycles(s64 ns) {
if (ns / 1000000000 > MAX_VALUE_TO_MULTIPLY) {
LOG_ERROR(Core_Timing, "Integer overflow, use max value");
return std::numeric_limits<s64>::max();
}
if (ns > MAX_VALUE_TO_MULTIPLY) {
LOG_DEBUG(Core_Timing, "Time very big, do rounding");
return BASE_CLOCK_RATE * (ns / 1000000000);
}
return (BASE_CLOCK_RATE * ns) / 1000000000;
}
s64 nsToCycles(u64 ns) {
if (ns / 1000000000 > MAX_VALUE_TO_MULTIPLY) {
LOG_ERROR(Core_Timing, "Integer overflow, use max value");
return std::numeric_limits<s64>::max();
}
if (ns > MAX_VALUE_TO_MULTIPLY) {
LOG_DEBUG(Core_Timing, "Time very big, do rounding");
return BASE_CLOCK_RATE * (static_cast<s64>(ns) / 1000000000);
}
return (BASE_CLOCK_RATE * static_cast<s64>(ns)) / 1000000000;
}
} // namespace CoreTiming

View File

@@ -0,0 +1,64 @@
// Copyright 2008 Dolphin Emulator Project / 2017 Citra Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include "common/common_types.h"
namespace CoreTiming {
// The below clock rate is based on Switch's clockspeed being widely known as 1.020GHz
// The exact value used is of course unverified.
constexpr u64 BASE_CLOCK_RATE = 1019215872; // Switch clock speed is 1020MHz un/docked
inline s64 msToCycles(int ms) {
// since ms is int there is no way to overflow
return BASE_CLOCK_RATE * static_cast<s64>(ms) / 1000;
}
inline s64 msToCycles(float ms) {
return static_cast<s64>(BASE_CLOCK_RATE * (0.001f) * ms);
}
inline s64 msToCycles(double ms) {
return static_cast<s64>(BASE_CLOCK_RATE * (0.001) * ms);
}
inline s64 usToCycles(float us) {
return static_cast<s64>(BASE_CLOCK_RATE * (0.000001f) * us);
}
inline s64 usToCycles(int us) {
return (BASE_CLOCK_RATE * static_cast<s64>(us) / 1000000);
}
s64 usToCycles(s64 us);
s64 usToCycles(u64 us);
inline s64 nsToCycles(float ns) {
return static_cast<s64>(BASE_CLOCK_RATE * (0.000000001f) * ns);
}
inline s64 nsToCycles(int ns) {
return BASE_CLOCK_RATE * static_cast<s64>(ns) / 1000000000;
}
s64 nsToCycles(s64 ns);
s64 nsToCycles(u64 ns);
inline u64 cyclesToNs(s64 cycles) {
return cycles * 1000000000 / BASE_CLOCK_RATE;
}
inline s64 cyclesToUs(s64 cycles) {
return cycles * 1000000 / BASE_CLOCK_RATE;
}
inline u64 cyclesToMs(s64 cycles) {
return cycles * 1000 / BASE_CLOCK_RATE;
}
} // namespace CoreTiming

View File

@@ -9,6 +9,7 @@
#include "core/file_sys/content_archive.h"
#include "core/file_sys/vfs_offset.h"
#include "core/loader/loader.h"
#include "romfs.h"
namespace FileSys {
@@ -46,21 +47,9 @@ struct PFS0Superblock {
};
static_assert(sizeof(PFS0Superblock) == 0x200, "PFS0Superblock has incorrect size.");
struct IVFCLevel {
u64_le offset;
u64_le size;
u32_le block_size;
u32_le reserved;
};
static_assert(sizeof(IVFCLevel) == 0x18, "IVFCLevel has incorrect size.");
struct RomFSSuperblock {
NCASectionHeaderBlock header_block;
u32_le magic;
u32_le magic_number;
INSERT_PADDING_BYTES(8);
std::array<IVFCLevel, 6> levels;
INSERT_PADDING_BYTES(64);
IVFCHeader ivfc;
};
static_assert(sizeof(RomFSSuperblock) == 0xE8, "RomFSSuperblock has incorrect size.");
@@ -92,8 +81,8 @@ NCA::NCA(VirtualFile file_) : file(std::move(file_)) {
const size_t romfs_offset =
header.section_tables[i].media_offset * MEDIA_OFFSET_MULTIPLIER +
sb.levels[IVFC_MAX_LEVEL - 1].offset;
const size_t romfs_size = sb.levels[IVFC_MAX_LEVEL - 1].size;
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) {

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 "common/string_util.h"
#include "common/swap.h"
#include "core/file_sys/control_metadata.h"
namespace FileSys {
std::string LanguageEntry::GetApplicationName() const {
return Common::StringFromFixedZeroTerminatedBuffer(application_name.data(), 0x200);
}
std::string LanguageEntry::GetDeveloperName() const {
return Common::StringFromFixedZeroTerminatedBuffer(developer_name.data(), 0x100);
}
NACP::NACP(VirtualFile file_) : file(std::move(file_)), raw(std::make_unique<RawNACP>()) {
file->ReadObject(raw.get());
}
const LanguageEntry& NACP::GetLanguageEntry(Language language) const {
return raw->language_entries.at(static_cast<u8>(language));
}
std::string NACP::GetApplicationName(Language language) const {
return GetLanguageEntry(language).GetApplicationName();
}
std::string NACP::GetDeveloperName(Language language) const {
return GetLanguageEntry(language).GetDeveloperName();
}
u64 NACP::GetTitleId() const {
return raw->title_id;
}
std::string NACP::GetVersionString() const {
return Common::StringFromFixedZeroTerminatedBuffer(raw->version_string.data(), 0x10);
}
} // namespace FileSys

View File

@@ -0,0 +1,81 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include <memory>
#include <string>
#include "common/common_funcs.h"
#include "core/file_sys/vfs.h"
namespace FileSys {
// A localized entry containing strings within the NACP.
// One for each language of type Language.
struct LanguageEntry {
std::array<char, 0x200> application_name;
std::array<char, 0x100> developer_name;
std::string GetApplicationName() const;
std::string GetDeveloperName() const;
};
static_assert(sizeof(LanguageEntry) == 0x300, "LanguageEntry has incorrect size.");
// The raw file format of a NACP file.
struct RawNACP {
std::array<LanguageEntry, 16> language_entries;
INSERT_PADDING_BYTES(0x38);
u64_le title_id;
INSERT_PADDING_BYTES(0x20);
std::array<char, 0x10> version_string;
u64_le dlc_base_title_id;
u64_le title_id_2;
INSERT_PADDING_BYTES(0x28);
u64_le product_code;
u64_le title_id_3;
std::array<u64_le, 0x7> title_id_array;
INSERT_PADDING_BYTES(0x8);
u64_le title_id_update;
std::array<u8, 0x40> bcat_passphrase;
INSERT_PADDING_BYTES(0xEC0);
};
static_assert(sizeof(RawNACP) == 0x4000, "RawNACP has incorrect size.");
// A language on the NX. These are for names and icons.
enum class Language : u8 {
AmericanEnglish = 0,
BritishEnglish = 1,
Japanese = 2,
French = 3,
German = 4,
LatinAmericanSpanish = 5,
Spanish = 6,
Italian = 7,
Dutch = 8,
CanadianFrench = 9,
Portugese = 10,
Russian = 11,
Korean = 12,
Taiwanese = 13,
Chinese = 14,
};
// A class representing the format used by NX metadata files, typically named Control.nacp.
// These store application name, dev name, title id, and other miscellaneous data.
class NACP {
public:
explicit NACP(VirtualFile file);
const LanguageEntry& GetLanguageEntry(Language language = Language::AmericanEnglish) const;
std::string GetApplicationName(Language language = Language::AmericanEnglish) const;
std::string GetDeveloperName(Language language = Language::AmericanEnglish) const;
u64 GetTitleId() const;
std::string GetVersionString() const;
private:
VirtualFile file;
std::unique_ptr<RawNACP> raw;
};
} // namespace FileSys

View File

@@ -20,13 +20,13 @@ enum {
constexpr ResultCode ERROR_PATH_NOT_FOUND(ErrorModule::FS, ErrCodes::NotFound);
// TODO(bunnei): Replace these with correct errors for Switch OS
constexpr ResultCode ERROR_INVALID_PATH(ResultCode(-1));
constexpr ResultCode ERROR_UNSUPPORTED_OPEN_FLAGS(ResultCode(-1));
constexpr ResultCode ERROR_INVALID_OPEN_FLAGS(ResultCode(-1));
constexpr ResultCode ERROR_FILE_NOT_FOUND(ResultCode(-1));
constexpr ResultCode ERROR_UNEXPECTED_FILE_OR_DIRECTORY(ResultCode(-1));
constexpr ResultCode ERROR_DIRECTORY_ALREADY_EXISTS(ResultCode(-1));
constexpr ResultCode ERROR_FILE_ALREADY_EXISTS(ResultCode(-1));
constexpr ResultCode ERROR_DIRECTORY_NOT_EMPTY(ResultCode(-1));
constexpr ResultCode ERROR_INVALID_PATH(-1);
constexpr ResultCode ERROR_UNSUPPORTED_OPEN_FLAGS(-1);
constexpr ResultCode ERROR_INVALID_OPEN_FLAGS(-1);
constexpr ResultCode ERROR_FILE_NOT_FOUND(-1);
constexpr ResultCode ERROR_UNEXPECTED_FILE_OR_DIRECTORY(-1);
constexpr ResultCode ERROR_DIRECTORY_ALREADY_EXISTS(-1);
constexpr ResultCode ERROR_FILE_ALREADY_EXISTS(-1);
constexpr ResultCode ERROR_DIRECTORY_NOT_EMPTY(-1);
} // namespace FileSys

View File

@@ -11,7 +11,13 @@ namespace FileSys {
enum class Mode : u32 {
Read = 1,
Write = 2,
ReadWrite = 3,
Append = 4,
WriteAppend = 6,
};
inline u32 operator&(Mode lhs, Mode rhs) {
return static_cast<u32>(lhs) & static_cast<u32>(rhs);
}
} // namespace FileSys

View File

@@ -2,7 +2,12 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <cstddef>
#include <cstring>
#include <iterator>
#include <utility>
#include "common/file_util.h"
#include "common/logging/log.h"
#include "core/file_sys/partition_filesystem.h"
@@ -60,8 +65,8 @@ PartitionFilesystem::PartitionFilesystem(std::shared_ptr<VfsFile> file) {
std::string name(
reinterpret_cast<const char*>(&file_data[strtab_offset + entry.strtab_offset]));
pfs_files.emplace_back(
std::make_shared<OffsetVfsFile>(file, entry.size, content_offset + entry.offset, name));
pfs_files.emplace_back(std::make_shared<OffsetVfsFile>(
file, entry.size, content_offset + entry.offset, std::move(name)));
}
status = Loader::ResultStatus::Success;
@@ -76,7 +81,7 @@ std::vector<std::shared_ptr<VfsFile>> PartitionFilesystem::GetFiles() const {
}
std::vector<std::shared_ptr<VfsDirectory>> PartitionFilesystem::GetSubdirectories() const {
return {};
return pfs_dirs;
}
std::string PartitionFilesystem::GetName() const {
@@ -99,14 +104,15 @@ void PartitionFilesystem::PrintDebugInfo() const {
}
bool PartitionFilesystem::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
auto iter = std::find(pfs_files.begin(), pfs_files.end(), file);
const auto iter = std::find(pfs_files.begin(), pfs_files.end(), file);
if (iter == pfs_files.end())
return false;
pfs_files[iter - pfs_files.begin()] = pfs_files.back();
const std::ptrdiff_t offset = std::distance(pfs_files.begin(), iter);
pfs_files[offset] = std::move(pfs_files.back());
pfs_files.pop_back();
pfs_dirs.emplace_back(dir);
pfs_dirs.emplace_back(std::move(dir));
return true;
}

124
src/core/file_sys/romfs.cpp Normal file
View File

@@ -0,0 +1,124 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/common_types.h"
#include "common/swap.h"
#include "core/file_sys/romfs.h"
#include "core/file_sys/vfs.h"
#include "core/file_sys/vfs_offset.h"
#include "core/file_sys/vfs_vector.h"
namespace FileSys {
constexpr u32 ROMFS_ENTRY_EMPTY = 0xFFFFFFFF;
struct TableLocation {
u64_le offset;
u64_le size;
};
static_assert(sizeof(TableLocation) == 0x10, "TableLocation has incorrect size.");
struct RomFSHeader {
u64_le header_size;
TableLocation directory_hash;
TableLocation directory_meta;
TableLocation file_hash;
TableLocation file_meta;
u64_le data_offset;
};
static_assert(sizeof(RomFSHeader) == 0x50, "RomFSHeader has incorrect size.");
struct DirectoryEntry {
u32_le sibling;
u32_le child_dir;
u32_le child_file;
u32_le hash;
u32_le name_length;
};
static_assert(sizeof(DirectoryEntry) == 0x14, "DirectoryEntry has incorrect size.");
struct FileEntry {
u32_le parent;
u32_le sibling;
u64_le offset;
u64_le size;
u32_le hash;
u32_le name_length;
};
static_assert(sizeof(FileEntry) == 0x20, "FileEntry has incorrect size.");
template <typename Entry>
static std::pair<Entry, std::string> GetEntry(const VirtualFile& file, size_t offset) {
Entry entry{};
if (file->ReadObject(&entry, offset) != sizeof(Entry))
return {};
std::string string(entry.name_length, '\0');
if (file->ReadArray(&string[0], string.size(), offset + sizeof(Entry)) != string.size())
return {};
return {entry, string};
}
void ProcessFile(VirtualFile file, size_t file_offset, size_t data_offset, u32 this_file_offset,
std::shared_ptr<VectorVfsDirectory> parent) {
while (true) {
auto entry = GetEntry<FileEntry>(file, file_offset + this_file_offset);
parent->AddFile(std::make_shared<OffsetVfsFile>(
file, entry.first.size, entry.first.offset + data_offset, entry.second, parent));
if (entry.first.sibling == ROMFS_ENTRY_EMPTY)
break;
this_file_offset = entry.first.sibling;
}
}
void ProcessDirectory(VirtualFile file, size_t dir_offset, size_t file_offset, size_t data_offset,
u32 this_dir_offset, std::shared_ptr<VectorVfsDirectory> parent) {
while (true) {
auto entry = GetEntry<DirectoryEntry>(file, dir_offset + this_dir_offset);
auto current = std::make_shared<VectorVfsDirectory>(
std::vector<VirtualFile>{}, std::vector<VirtualDir>{}, parent, entry.second);
if (entry.first.child_file != ROMFS_ENTRY_EMPTY) {
ProcessFile(file, file_offset, data_offset, entry.first.child_file, current);
}
if (entry.first.child_dir != ROMFS_ENTRY_EMPTY) {
ProcessDirectory(file, dir_offset, file_offset, data_offset, entry.first.child_dir,
current);
}
parent->AddDirectory(current);
if (entry.first.sibling == ROMFS_ENTRY_EMPTY)
break;
this_dir_offset = entry.first.sibling;
}
}
VirtualDir ExtractRomFS(VirtualFile file) {
RomFSHeader header{};
if (file->ReadObject(&header) != sizeof(RomFSHeader))
return nullptr;
if (header.header_size != sizeof(RomFSHeader))
return nullptr;
const u64 file_offset = header.file_meta.offset;
const u64 dir_offset = header.directory_meta.offset + 4;
const auto root =
std::make_shared<VectorVfsDirectory>(std::vector<VirtualFile>{}, std::vector<VirtualDir>{},
file->GetContainingDirectory(), file->GetName());
ProcessDirectory(file, dir_offset, file_offset, header.data_offset, 0, root);
VirtualDir out = std::move(root);
while (out->GetSubdirectory("") != nullptr)
out = out->GetSubdirectory("");
return out;
}
} // namespace FileSys

35
src/core/file_sys/romfs.h Normal file
View File

@@ -0,0 +1,35 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include "common/common_funcs.h"
#include "common/swap.h"
#include "core/file_sys/vfs.h"
namespace FileSys {
struct IVFCLevel {
u64_le offset;
u64_le size;
u32_le block_size;
u32_le reserved;
};
static_assert(sizeof(IVFCLevel) == 0x18, "IVFCLevel has incorrect size.");
struct IVFCHeader {
u32_le magic;
u32_le magic_number;
INSERT_PADDING_BYTES(8);
std::array<IVFCLevel, 6> levels;
INSERT_PADDING_BYTES(64);
};
static_assert(sizeof(IVFCHeader) == 0xE0, "IVFCHeader has incorrect size.");
// Converts a RomFS binary blob to VFS Filesystem
// Returns nullptr on failure
VirtualDir ExtractRomFS(VirtualFile file);
} // namespace FileSys

View File

@@ -13,7 +13,7 @@ namespace FileSys {
VfsFile::~VfsFile() = default;
std::string VfsFile::GetExtension() const {
return FileUtil::GetExtensionFromFilename(GetName());
return std::string(FileUtil::GetExtensionFromFilename(GetName()));
}
VfsDirectory::~VfsDirectory() = default;
@@ -42,68 +42,91 @@ bool VfsFile::WriteByte(u8 data, size_t offset) {
return Write(&data, 1, offset) == 1;
}
size_t VfsFile::WriteBytes(std::vector<u8> data, size_t offset) {
size_t VfsFile::WriteBytes(const std::vector<u8>& data, size_t offset) {
return Write(data.data(), data.size(), offset);
}
std::shared_ptr<VfsFile> VfsDirectory::GetFileRelative(const std::string& path) const {
std::string VfsFile::GetFullPath() const {
if (GetContainingDirectory() == nullptr)
return "/" + GetName();
return GetContainingDirectory()->GetFullPath() + "/" + GetName();
}
std::shared_ptr<VfsFile> VfsDirectory::GetFileRelative(std::string_view path) const {
auto vec = FileUtil::SplitPathComponents(path);
vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }),
vec.end());
if (vec.empty())
if (vec.empty()) {
return nullptr;
if (vec.size() == 1)
}
if (vec.size() == 1) {
return GetFile(vec[0]);
}
auto dir = GetSubdirectory(vec[0]);
for (size_t component = 1; component < vec.size() - 1; ++component) {
if (dir == nullptr)
if (dir == nullptr) {
return nullptr;
}
dir = dir->GetSubdirectory(vec[component]);
}
if (dir == nullptr)
if (dir == nullptr) {
return nullptr;
}
return dir->GetFile(vec.back());
}
std::shared_ptr<VfsFile> VfsDirectory::GetFileAbsolute(const std::string& path) const {
if (IsRoot())
std::shared_ptr<VfsFile> VfsDirectory::GetFileAbsolute(std::string_view path) const {
if (IsRoot()) {
return GetFileRelative(path);
}
return GetParentDirectory()->GetFileAbsolute(path);
}
std::shared_ptr<VfsDirectory> VfsDirectory::GetDirectoryRelative(const std::string& path) const {
std::shared_ptr<VfsDirectory> VfsDirectory::GetDirectoryRelative(std::string_view path) const {
auto vec = FileUtil::SplitPathComponents(path);
vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }),
vec.end());
if (vec.empty())
if (vec.empty()) {
// TODO(DarkLordZach): Return this directory if path is '/' or similar. Can't currently
// because of const-ness
return nullptr;
}
auto dir = GetSubdirectory(vec[0]);
for (size_t component = 1; component < vec.size(); ++component) {
if (dir == nullptr)
if (dir == nullptr) {
return nullptr;
}
dir = dir->GetSubdirectory(vec[component]);
}
return dir;
}
std::shared_ptr<VfsDirectory> VfsDirectory::GetDirectoryAbsolute(const std::string& path) const {
if (IsRoot())
std::shared_ptr<VfsDirectory> VfsDirectory::GetDirectoryAbsolute(std::string_view path) const {
if (IsRoot()) {
return GetDirectoryRelative(path);
}
return GetParentDirectory()->GetDirectoryAbsolute(path);
}
std::shared_ptr<VfsFile> VfsDirectory::GetFile(const std::string& name) const {
std::shared_ptr<VfsFile> VfsDirectory::GetFile(std::string_view name) const {
const auto& files = GetFiles();
const auto iter = std::find_if(files.begin(), files.end(),
[&name](const auto& file1) { return name == file1->GetName(); });
return iter == files.end() ? nullptr : *iter;
}
std::shared_ptr<VfsDirectory> VfsDirectory::GetSubdirectory(const std::string& name) const {
std::shared_ptr<VfsDirectory> VfsDirectory::GetSubdirectory(std::string_view name) const {
const auto& subs = GetSubdirectories();
const auto iter = std::find_if(subs.begin(), subs.end(),
[&name](const auto& file1) { return name == file1->GetName(); });
@@ -128,77 +151,96 @@ size_t VfsDirectory::GetSize() const {
return file_total + subdir_total;
}
std::shared_ptr<VfsFile> VfsDirectory::CreateFileRelative(const std::string& path) {
std::shared_ptr<VfsFile> VfsDirectory::CreateFileRelative(std::string_view path) {
auto vec = FileUtil::SplitPathComponents(path);
vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }),
vec.end());
if (vec.empty())
if (vec.empty()) {
return nullptr;
if (vec.size() == 1)
}
if (vec.size() == 1) {
return CreateFile(vec[0]);
}
auto dir = GetSubdirectory(vec[0]);
if (dir == nullptr) {
dir = CreateSubdirectory(vec[0]);
if (dir == nullptr)
if (dir == nullptr) {
return nullptr;
}
}
return dir->CreateFileRelative(FileUtil::GetPathWithoutTop(path));
}
std::shared_ptr<VfsFile> VfsDirectory::CreateFileAbsolute(const std::string& path) {
if (IsRoot())
std::shared_ptr<VfsFile> VfsDirectory::CreateFileAbsolute(std::string_view path) {
if (IsRoot()) {
return CreateFileRelative(path);
}
return GetParentDirectory()->CreateFileAbsolute(path);
}
std::shared_ptr<VfsDirectory> VfsDirectory::CreateDirectoryRelative(const std::string& path) {
std::shared_ptr<VfsDirectory> VfsDirectory::CreateDirectoryRelative(std::string_view path) {
auto vec = FileUtil::SplitPathComponents(path);
vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }),
vec.end());
if (vec.empty())
if (vec.empty()) {
return nullptr;
if (vec.size() == 1)
}
if (vec.size() == 1) {
return CreateSubdirectory(vec[0]);
}
auto dir = GetSubdirectory(vec[0]);
if (dir == nullptr) {
dir = CreateSubdirectory(vec[0]);
if (dir == nullptr)
if (dir == nullptr) {
return nullptr;
}
}
return dir->CreateDirectoryRelative(FileUtil::GetPathWithoutTop(path));
}
std::shared_ptr<VfsDirectory> VfsDirectory::CreateDirectoryAbsolute(const std::string& path) {
if (IsRoot())
std::shared_ptr<VfsDirectory> VfsDirectory::CreateDirectoryAbsolute(std::string_view path) {
if (IsRoot()) {
return CreateDirectoryRelative(path);
}
return GetParentDirectory()->CreateDirectoryAbsolute(path);
}
bool VfsDirectory::DeleteSubdirectoryRecursive(const std::string& name) {
bool VfsDirectory::DeleteSubdirectoryRecursive(std::string_view name) {
auto dir = GetSubdirectory(name);
if (dir == nullptr)
if (dir == nullptr) {
return false;
}
bool success = true;
for (const auto& file : dir->GetFiles()) {
if (!DeleteFile(file->GetName()))
if (!DeleteFile(file->GetName())) {
success = false;
}
}
for (const auto& sdir : dir->GetSubdirectories()) {
if (!dir->DeleteSubdirectoryRecursive(sdir->GetName()))
if (!dir->DeleteSubdirectoryRecursive(sdir->GetName())) {
success = false;
}
}
return success;
}
bool VfsDirectory::Copy(const std::string& src, const std::string& dest) {
bool VfsDirectory::Copy(std::string_view src, std::string_view dest) {
const auto f1 = GetFile(src);
auto f2 = CreateFile(dest);
if (f1 == nullptr || f2 == nullptr)
if (f1 == nullptr || f2 == nullptr) {
return false;
}
if (!f2->Resize(f1->GetSize())) {
DeleteFile(dest);
@@ -208,6 +250,13 @@ bool VfsDirectory::Copy(const std::string& src, const std::string& dest) {
return f2->WriteBytes(f1->ReadAllBytes()) == f1->GetSize();
}
std::string VfsDirectory::GetFullPath() const {
if (IsRoot())
return GetName();
return GetParentDirectory()->GetFullPath() + "/" + GetName();
}
bool ReadOnlyVfsDirectory::IsWritable() const {
return false;
}
@@ -216,23 +265,32 @@ bool ReadOnlyVfsDirectory::IsReadable() const {
return true;
}
std::shared_ptr<VfsDirectory> ReadOnlyVfsDirectory::CreateSubdirectory(const std::string& name) {
std::shared_ptr<VfsDirectory> ReadOnlyVfsDirectory::CreateSubdirectory(std::string_view name) {
return nullptr;
}
std::shared_ptr<VfsFile> ReadOnlyVfsDirectory::CreateFile(const std::string& name) {
std::shared_ptr<VfsFile> ReadOnlyVfsDirectory::CreateFile(std::string_view name) {
return nullptr;
}
bool ReadOnlyVfsDirectory::DeleteSubdirectory(const std::string& name) {
bool ReadOnlyVfsDirectory::DeleteSubdirectory(std::string_view name) {
return false;
}
bool ReadOnlyVfsDirectory::DeleteFile(const std::string& name) {
bool ReadOnlyVfsDirectory::DeleteFile(std::string_view name) {
return false;
}
bool ReadOnlyVfsDirectory::Rename(const std::string& name) {
bool ReadOnlyVfsDirectory::Rename(std::string_view name) {
return false;
}
bool VfsRawCopy(VirtualFile src, VirtualFile dest) {
if (src == nullptr || dest == nullptr)
return false;
if (!dest->Resize(src->GetSize()))
return false;
std::vector<u8> data = src->ReadAllBytes();
return dest->WriteBytes(data, 0) == data.size();
}
} // namespace FileSys

View File

@@ -6,11 +6,11 @@
#include <memory>
#include <string>
#include <string_view>
#include <type_traits>
#include <vector>
#include "boost/optional.hpp"
#include "common/common_types.h"
#include "common/file_util.h"
namespace FileSys {
struct VfsFile;
@@ -59,8 +59,7 @@ struct VfsFile : NonCopyable {
// Returns the number of bytes (sizeof(T)*number_elements) read successfully.
template <typename T>
size_t ReadArray(T* data, size_t number_elements, size_t offset = 0) const {
static_assert(std::is_trivially_copyable<T>::value,
"Data type must be trivially copyable.");
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
return Read(reinterpret_cast<u8*>(data), number_elements * sizeof(T), offset);
}
@@ -69,8 +68,7 @@ struct VfsFile : NonCopyable {
// Returns the number of bytes read successfully.
template <typename T>
size_t ReadBytes(T* data, size_t size, size_t offset = 0) const {
static_assert(std::is_trivially_copyable<T>::value,
"Data type must be trivially copyable.");
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
return Read(reinterpret_cast<u8*>(data), size, offset);
}
@@ -78,8 +76,7 @@ struct VfsFile : NonCopyable {
// Returns the number of bytes read successfully (sizeof(T)).
template <typename T>
size_t ReadObject(T* data, size_t offset = 0) const {
static_assert(std::is_trivially_copyable<T>::value,
"Data type must be trivially copyable.");
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
return Read(reinterpret_cast<u8*>(data), sizeof(T), offset);
}
@@ -88,38 +85,37 @@ struct VfsFile : NonCopyable {
virtual bool WriteByte(u8 data, size_t offset = 0);
// Writes a vector of bytes to offset in file and returns the number of bytes successfully
// written.
virtual size_t WriteBytes(std::vector<u8> data, size_t offset = 0);
virtual size_t WriteBytes(const std::vector<u8>& data, size_t offset = 0);
// Writes an array of type T, size number_elements to offset in file.
// Returns the number of bytes (sizeof(T)*number_elements) written successfully.
template <typename T>
size_t WriteArray(T* data, size_t number_elements, size_t offset = 0) {
static_assert(std::is_trivially_copyable<T>::value,
"Data type must be trivially copyable.");
size_t WriteArray(const T* data, size_t number_elements, size_t offset = 0) {
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
return Write(data, number_elements * sizeof(T), offset);
}
// Writes size bytes starting at memory location data to offset in file.
// Returns the number of bytes written successfully.
template <typename T>
size_t WriteBytes(T* data, size_t size, size_t offset = 0) {
static_assert(std::is_trivially_copyable<T>::value,
"Data type must be trivially copyable.");
return Write(reinterpret_cast<u8*>(data), size, offset);
size_t WriteBytes(const T* data, size_t size, size_t offset = 0) {
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
return Write(reinterpret_cast<const u8*>(data), size, offset);
}
// Writes one object of type T to offset in file.
// Returns the number of bytes written successfully (sizeof(T)).
template <typename T>
size_t WriteObject(const T& data, size_t offset = 0) {
static_assert(std::is_trivially_copyable<T>::value,
"Data type must be trivially copyable.");
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
return Write(&data, sizeof(T), offset);
}
// Renames the file to name. Returns whether or not the operation was successsful.
virtual bool Rename(const std::string& name) = 0;
virtual bool Rename(std::string_view name) = 0;
// Returns the full path of this file as a string, recursively
virtual std::string GetFullPath() const;
};
// A class representing a directory in an abstract filesystem.
@@ -128,27 +124,27 @@ struct VfsDirectory : NonCopyable {
// Retrives the file located at path as if the current directory was root. Returns nullptr if
// not found.
virtual std::shared_ptr<VfsFile> GetFileRelative(const std::string& path) const;
virtual std::shared_ptr<VfsFile> GetFileRelative(std::string_view path) const;
// Calls GetFileRelative(path) on the root of the current directory.
virtual std::shared_ptr<VfsFile> GetFileAbsolute(const std::string& path) const;
virtual std::shared_ptr<VfsFile> GetFileAbsolute(std::string_view path) const;
// Retrives the directory located at path as if the current directory was root. Returns nullptr
// if not found.
virtual std::shared_ptr<VfsDirectory> GetDirectoryRelative(const std::string& path) const;
virtual std::shared_ptr<VfsDirectory> GetDirectoryRelative(std::string_view path) const;
// Calls GetDirectoryRelative(path) on the root of the current directory.
virtual std::shared_ptr<VfsDirectory> GetDirectoryAbsolute(const std::string& path) const;
virtual std::shared_ptr<VfsDirectory> GetDirectoryAbsolute(std::string_view path) const;
// Returns a vector containing all of the files in this directory.
virtual std::vector<std::shared_ptr<VfsFile>> GetFiles() const = 0;
// Returns the file with filename matching name. Returns nullptr if directory dosen't have a
// file with name.
virtual std::shared_ptr<VfsFile> GetFile(const std::string& name) const;
virtual std::shared_ptr<VfsFile> GetFile(std::string_view name) const;
// Returns a vector containing all of the subdirectories in this directory.
virtual std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const = 0;
// Returns the directory with name matching name. Returns nullptr if directory dosen't have a
// directory with name.
virtual std::shared_ptr<VfsDirectory> GetSubdirectory(const std::string& name) const;
virtual std::shared_ptr<VfsDirectory> GetSubdirectory(std::string_view name) const;
// Returns whether or not the directory can be written to.
virtual bool IsWritable() const = 0;
@@ -168,55 +164,69 @@ struct VfsDirectory : NonCopyable {
// Creates a new subdirectory with name name. Returns a pointer to the new directory or nullptr
// if the operation failed.
virtual std::shared_ptr<VfsDirectory> CreateSubdirectory(const std::string& name) = 0;
virtual std::shared_ptr<VfsDirectory> CreateSubdirectory(std::string_view name) = 0;
// Creates a new file with name name. Returns a pointer to the new file or nullptr if the
// operation failed.
virtual std::shared_ptr<VfsFile> CreateFile(const std::string& name) = 0;
virtual std::shared_ptr<VfsFile> CreateFile(std::string_view name) = 0;
// Creates a new file at the path relative to this directory. Also creates directories if
// they do not exist and is supported by this implementation. Returns nullptr on any failure.
virtual std::shared_ptr<VfsFile> CreateFileRelative(const std::string& path);
virtual std::shared_ptr<VfsFile> CreateFileRelative(std::string_view path);
// Creates a new file at the path relative to root of this directory. Also creates directories
// if they do not exist and is supported by this implementation. Returns nullptr on any failure.
virtual std::shared_ptr<VfsFile> CreateFileAbsolute(const std::string& path);
virtual std::shared_ptr<VfsFile> CreateFileAbsolute(std::string_view path);
// Creates a new directory at the path relative to this directory. Also creates directories if
// they do not exist and is supported by this implementation. Returns nullptr on any failure.
virtual std::shared_ptr<VfsDirectory> CreateDirectoryRelative(const std::string& path);
virtual std::shared_ptr<VfsDirectory> CreateDirectoryRelative(std::string_view path);
// Creates a new directory at the path relative to root of this directory. Also creates
// directories if they do not exist and is supported by this implementation. Returns nullptr on
// any failure.
virtual std::shared_ptr<VfsDirectory> CreateDirectoryAbsolute(const std::string& path);
virtual std::shared_ptr<VfsDirectory> CreateDirectoryAbsolute(std::string_view path);
// Deletes the subdirectory with name and returns true on success.
virtual bool DeleteSubdirectory(const std::string& name) = 0;
virtual bool DeleteSubdirectory(std::string_view name) = 0;
// Deletes all subdirectories and files of subdirectory with name recirsively and then deletes
// the subdirectory. Returns true on success.
virtual bool DeleteSubdirectoryRecursive(const std::string& name);
virtual bool DeleteSubdirectoryRecursive(std::string_view name);
// Returnes whether or not the file with name name was deleted successfully.
virtual bool DeleteFile(const std::string& name) = 0;
virtual bool DeleteFile(std::string_view name) = 0;
// Returns whether or not this directory was renamed to name.
virtual bool Rename(const std::string& name) = 0;
virtual bool Rename(std::string_view name) = 0;
// Returns whether or not the file with name src was successfully copied to a new file with name
// dest.
virtual bool Copy(const std::string& src, const std::string& dest);
virtual bool Copy(std::string_view src, std::string_view dest);
// Interprets the file with name file instead as a directory of type directory.
// The directory must have a constructor that takes a single argument of type
// std::shared_ptr<VfsFile>. Allows to reinterpret container files (i.e NCA, zip, XCI, etc) as a
// subdirectory in one call.
template <typename Directory>
bool InterpretAsDirectory(const std::string& file) {
bool InterpretAsDirectory(std::string_view file) {
auto file_p = GetFile(file);
if (file_p == nullptr) {
return false;
}
return ReplaceFileWithSubdirectory(file_p, std::make_shared<Directory>(file_p));
}
bool InterpretAsDirectory(const std::function<VirtualDir(VirtualFile)>& function,
const std::string& file) {
auto file_p = GetFile(file);
if (file_p == nullptr)
return false;
return ReplaceFileWithSubdirectory(file, std::make_shared<Directory>(file_p));
return ReplaceFileWithSubdirectory(file_p, function(file_p));
}
// Returns the full path of this directory as a string, recursively
virtual std::string GetFullPath() const;
protected:
// Backend for InterpretAsDirectory.
// Removes all references to file and adds a reference to dir in the directory's implementation.
@@ -228,10 +238,16 @@ protected:
struct ReadOnlyVfsDirectory : public VfsDirectory {
bool IsWritable() const override;
bool IsReadable() const override;
std::shared_ptr<VfsDirectory> CreateSubdirectory(const std::string& name) override;
std::shared_ptr<VfsFile> CreateFile(const std::string& name) override;
bool DeleteSubdirectory(const std::string& name) override;
bool DeleteFile(const std::string& name) override;
bool Rename(const std::string& name) override;
std::shared_ptr<VfsDirectory> CreateSubdirectory(std::string_view name) override;
std::shared_ptr<VfsFile> CreateFile(std::string_view name) override;
bool DeleteSubdirectory(std::string_view name) override;
bool DeleteFile(std::string_view name) override;
bool Rename(std::string_view name) override;
};
// A method that copies the raw data between two different implementations of VirtualFile. If you
// are using the same implementation, it is probably better to use the Copy method in the parent
// directory of src/dest.
bool VfsRawCopy(VirtualFile src, VirtualFile dest);
} // namespace FileSys

View File

@@ -2,13 +2,17 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <utility>
#include "core/file_sys/vfs_offset.h"
namespace FileSys {
OffsetVfsFile::OffsetVfsFile(std::shared_ptr<VfsFile> file_, size_t size_, size_t offset_,
const std::string& name_)
: file(file_), offset(offset_), size(size_), name(name_) {}
std::string name_, VirtualDir parent_)
: file(file_), offset(offset_), size(size_), name(std::move(name_)),
parent(parent_ == nullptr ? file->GetContainingDirectory() : std::move(parent_)) {}
std::string OffsetVfsFile::GetName() const {
return name.empty() ? file->GetName() : name;
@@ -32,7 +36,7 @@ bool OffsetVfsFile::Resize(size_t new_size) {
}
std::shared_ptr<VfsDirectory> OffsetVfsFile::GetContainingDirectory() const {
return file->GetContainingDirectory();
return parent;
}
bool OffsetVfsFile::IsWritable() const {
@@ -73,11 +77,11 @@ bool OffsetVfsFile::WriteByte(u8 data, size_t r_offset) {
return false;
}
size_t OffsetVfsFile::WriteBytes(std::vector<u8> data, size_t r_offset) {
size_t OffsetVfsFile::WriteBytes(const std::vector<u8>& data, size_t r_offset) {
return file->Write(data.data(), TrimToFit(data.size(), r_offset), offset + r_offset);
}
bool OffsetVfsFile::Rename(const std::string& name) {
bool OffsetVfsFile::Rename(std::string_view name) {
return file->Rename(name);
}
@@ -86,7 +90,7 @@ size_t OffsetVfsFile::GetOffset() const {
}
size_t OffsetVfsFile::TrimToFit(size_t r_size, size_t r_offset) const {
return std::max<size_t>(std::min<size_t>(size - r_offset, r_size), 0);
return std::clamp(r_size, size_t{0}, size - r_offset);
}
} // namespace FileSys

View File

@@ -4,6 +4,9 @@
#pragma once
#include <memory>
#include <string_view>
#include "core/file_sys/vfs.h"
namespace FileSys {
@@ -14,7 +17,7 @@ namespace FileSys {
// the size of this wrapper.
struct OffsetVfsFile : public VfsFile {
OffsetVfsFile(std::shared_ptr<VfsFile> file, size_t size, size_t offset = 0,
const std::string& new_name = "");
std::string new_name = "", VirtualDir new_parent = nullptr);
std::string GetName() const override;
size_t GetSize() const override;
@@ -28,9 +31,9 @@ struct OffsetVfsFile : public VfsFile {
std::vector<u8> ReadBytes(size_t size, size_t offset) const override;
std::vector<u8> ReadAllBytes() const override;
bool WriteByte(u8 data, size_t offset) override;
size_t WriteBytes(std::vector<u8> data, size_t offset) override;
size_t WriteBytes(const std::vector<u8>& data, size_t offset) override;
bool Rename(const std::string& name) override;
bool Rename(std::string_view name) override;
size_t GetOffset() const;
@@ -41,6 +44,7 @@ private:
size_t offset;
size_t size;
std::string name;
VirtualDir parent;
};
} // namespace FileSys

View File

@@ -2,30 +2,42 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <cstddef>
#include <iterator>
#include <utility>
#include "common/common_paths.h"
#include "common/logging/log.h"
#include "core/file_sys/vfs_real.h"
namespace FileSys {
static std::string PermissionsToCharArray(Mode perms) {
std::string out;
switch (perms) {
case Mode::Read:
out += "r";
break;
case Mode::Write:
out += "r+";
break;
case Mode::Append:
out += "a";
break;
static std::string ModeFlagsToString(Mode mode) {
std::string mode_str;
// Calculate the correct open mode for the file.
if (mode & Mode::Read && mode & Mode::Write) {
if (mode & Mode::Append)
mode_str = "a+";
else
mode_str = "r+";
} else {
if (mode & Mode::Read)
mode_str = "r";
else if (mode & Mode::Append)
mode_str = "a";
else if (mode & Mode::Write)
mode_str = "w";
}
return out + "b";
mode_str += "b";
return mode_str;
}
RealVfsFile::RealVfsFile(const std::string& path_, Mode perms_)
: backing(path_, PermissionsToCharArray(perms_).c_str()), path(path_),
: backing(path_, ModeFlagsToString(perms_).c_str()), path(path_),
parent_path(FileUtil::GetParentPath(path_)),
path_components(FileUtil::SplitPathComponents(path_)),
parent_components(FileUtil::SliceVector(path_components, 0, path_components.size() - 1)),
@@ -48,11 +60,11 @@ std::shared_ptr<VfsDirectory> RealVfsFile::GetContainingDirectory() const {
}
bool RealVfsFile::IsWritable() const {
return perms == Mode::Append || perms == Mode::Write;
return (perms & Mode::WriteAppend) != 0;
}
bool RealVfsFile::IsReadable() const {
return perms == Mode::Read || perms == Mode::Write;
return (perms & Mode::ReadWrite) != 0;
}
size_t RealVfsFile::Read(u8* data, size_t length, size_t offset) const {
@@ -67,12 +79,15 @@ size_t RealVfsFile::Write(const u8* data, size_t length, size_t offset) {
return backing.WriteBytes(data, length);
}
bool RealVfsFile::Rename(const std::string& name) {
const auto out = FileUtil::Rename(GetName(), name);
path = parent_path + DIR_SEP + name;
bool RealVfsFile::Rename(std::string_view name) {
std::string name_str(name.begin(), name.end());
const auto out = FileUtil::Rename(GetName(), name_str);
path = (parent_path + DIR_SEP).append(name);
path_components = parent_components;
path_components.push_back(name);
backing = FileUtil::IOFile(path, PermissionsToCharArray(perms).c_str());
path_components.push_back(std::move(name_str));
backing = FileUtil::IOFile(path, ModeFlagsToString(perms).c_str());
return out;
}
@@ -85,15 +100,15 @@ RealVfsDirectory::RealVfsDirectory(const std::string& path_, Mode perms_)
path_components(FileUtil::SplitPathComponents(path)),
parent_components(FileUtil::SliceVector(path_components, 0, path_components.size() - 1)),
perms(perms_) {
if (!FileUtil::Exists(path) && (perms == Mode::Write || perms == Mode::Append))
if (!FileUtil::Exists(path) && perms & Mode::WriteAppend)
FileUtil::CreateDir(path);
unsigned size;
if (perms == Mode::Append)
return;
FileUtil::ForeachDirectoryEntry(
&size, path,
[this](unsigned* entries_out, const std::string& directory, const std::string& filename) {
nullptr, path,
[this](u64* entries_out, const std::string& directory, const std::string& filename) {
std::string full_path = directory + DIR_SEP + filename;
if (FileUtil::IsDirectory(full_path))
subdirectories.emplace_back(std::make_shared<RealVfsDirectory>(full_path, perms));
@@ -104,19 +119,19 @@ RealVfsDirectory::RealVfsDirectory(const std::string& path_, Mode perms_)
}
std::vector<std::shared_ptr<VfsFile>> RealVfsDirectory::GetFiles() const {
return std::vector<std::shared_ptr<VfsFile>>(files);
return files;
}
std::vector<std::shared_ptr<VfsDirectory>> RealVfsDirectory::GetSubdirectories() const {
return std::vector<std::shared_ptr<VfsDirectory>>(subdirectories);
return subdirectories;
}
bool RealVfsDirectory::IsWritable() const {
return perms == Mode::Write || perms == Mode::Append;
return (perms & Mode::WriteAppend) != 0;
}
bool RealVfsDirectory::IsReadable() const {
return perms == Mode::Read || perms == Mode::Write;
return (perms & Mode::ReadWrite) != 0;
}
std::string RealVfsDirectory::GetName() const {
@@ -130,47 +145,72 @@ std::shared_ptr<VfsDirectory> RealVfsDirectory::GetParentDirectory() const {
return std::make_shared<RealVfsDirectory>(parent_path, perms);
}
std::shared_ptr<VfsDirectory> RealVfsDirectory::CreateSubdirectory(const std::string& name) {
if (!FileUtil::CreateDir(path + DIR_SEP + name))
std::shared_ptr<VfsDirectory> RealVfsDirectory::CreateSubdirectory(std::string_view name) {
const std::string subdir_path = (path + DIR_SEP).append(name);
if (!FileUtil::CreateDir(subdir_path)) {
return nullptr;
subdirectories.emplace_back(std::make_shared<RealVfsDirectory>(path + DIR_SEP + name, perms));
}
subdirectories.emplace_back(std::make_shared<RealVfsDirectory>(subdir_path, perms));
return subdirectories.back();
}
std::shared_ptr<VfsFile> RealVfsDirectory::CreateFile(const std::string& name) {
if (!FileUtil::CreateEmptyFile(path + DIR_SEP + name))
std::shared_ptr<VfsFile> RealVfsDirectory::CreateFile(std::string_view name) {
const std::string file_path = (path + DIR_SEP).append(name);
if (!FileUtil::CreateEmptyFile(file_path)) {
return nullptr;
files.emplace_back(std::make_shared<RealVfsFile>(path + DIR_SEP + name, perms));
}
files.emplace_back(std::make_shared<RealVfsFile>(file_path, perms));
return files.back();
}
bool RealVfsDirectory::DeleteSubdirectory(const std::string& name) {
return FileUtil::DeleteDirRecursively(path + DIR_SEP + name);
bool RealVfsDirectory::DeleteSubdirectory(std::string_view name) {
const std::string subdir_path = (path + DIR_SEP).append(name);
return FileUtil::DeleteDirRecursively(subdir_path);
}
bool RealVfsDirectory::DeleteFile(const std::string& name) {
auto file = GetFile(name);
if (file == nullptr)
bool RealVfsDirectory::DeleteFile(std::string_view name) {
const auto file = GetFile(name);
if (file == nullptr) {
return false;
}
files.erase(std::find(files.begin(), files.end(), file));
auto real_file = std::static_pointer_cast<RealVfsFile>(file);
real_file->Close();
return FileUtil::Delete(path + DIR_SEP + name);
const std::string file_path = (path + DIR_SEP).append(name);
return FileUtil::Delete(file_path);
}
bool RealVfsDirectory::Rename(const std::string& name) {
return FileUtil::Rename(path, parent_path + DIR_SEP + name);
bool RealVfsDirectory::Rename(std::string_view name) {
const std::string new_name = (parent_path + DIR_SEP).append(name);
return FileUtil::Rename(path, new_name);
}
std::string RealVfsDirectory::GetFullPath() const {
auto out = path;
std::replace(out.begin(), out.end(), '\\', '/');
return out;
}
bool RealVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
auto iter = std::find(files.begin(), files.end(), file);
const auto iter = std::find(files.begin(), files.end(), file);
if (iter == files.end())
return false;
files[iter - files.begin()] = files.back();
const std::ptrdiff_t offset = std::distance(files.begin(), iter);
files[offset] = files.back();
files.pop_back();
subdirectories.emplace_back(dir);
subdirectories.emplace_back(std::move(dir));
return true;
}

View File

@@ -4,6 +4,8 @@
#pragma once
#include <string_view>
#include "common/file_util.h"
#include "core/file_sys/mode.h"
#include "core/file_sys/vfs.h"
@@ -24,7 +26,7 @@ struct RealVfsFile : public VfsFile {
bool IsReadable() const override;
size_t Read(u8* data, size_t length, size_t offset) const override;
size_t Write(const u8* data, size_t length, size_t offset) override;
bool Rename(const std::string& name) override;
bool Rename(std::string_view name) override;
private:
bool Close();
@@ -39,7 +41,7 @@ private:
// An implementation of VfsDirectory that represents a directory on the user's computer.
struct RealVfsDirectory : public VfsDirectory {
RealVfsDirectory(const std::string& path, Mode perms);
RealVfsDirectory(const std::string& path, Mode perms = Mode::Read);
std::vector<std::shared_ptr<VfsFile>> GetFiles() const override;
std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override;
@@ -47,11 +49,12 @@ struct RealVfsDirectory : public VfsDirectory {
bool IsReadable() const override;
std::string GetName() const override;
std::shared_ptr<VfsDirectory> GetParentDirectory() const override;
std::shared_ptr<VfsDirectory> CreateSubdirectory(const std::string& name) override;
std::shared_ptr<VfsFile> CreateFile(const std::string& name) override;
bool DeleteSubdirectory(const std::string& name) override;
bool DeleteFile(const std::string& name) override;
bool Rename(const std::string& name) override;
std::shared_ptr<VfsDirectory> CreateSubdirectory(std::string_view name) override;
std::shared_ptr<VfsFile> CreateFile(std::string_view name) override;
bool DeleteSubdirectory(std::string_view name) override;
bool DeleteFile(std::string_view name) override;
bool Rename(std::string_view name) override;
std::string GetFullPath() const override;
protected:
bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;

View File

@@ -0,0 +1,83 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include "core/file_sys/vfs_vector.h"
namespace FileSys {
VectorVfsDirectory::VectorVfsDirectory(std::vector<VirtualFile> files_,
std::vector<VirtualDir> dirs_, VirtualDir parent_,
std::string name_)
: files(std::move(files_)), dirs(std::move(dirs_)), parent(std::move(parent_)),
name(std::move(name_)) {}
std::vector<std::shared_ptr<VfsFile>> VectorVfsDirectory::GetFiles() const {
return files;
}
std::vector<std::shared_ptr<VfsDirectory>> VectorVfsDirectory::GetSubdirectories() const {
return dirs;
}
bool VectorVfsDirectory::IsWritable() const {
return false;
}
bool VectorVfsDirectory::IsReadable() const {
return true;
}
std::string VectorVfsDirectory::GetName() const {
return name;
}
std::shared_ptr<VfsDirectory> VectorVfsDirectory::GetParentDirectory() const {
return parent;
}
template <typename T>
static bool FindAndRemoveVectorElement(std::vector<T>& vec, std::string_view name) {
auto iter = std::find_if(vec.begin(), vec.end(), [name](T e) { return e->GetName() == name; });
if (iter == vec.end())
return false;
auto old_size = vec.size();
vec.erase(iter);
return true;
}
bool VectorVfsDirectory::DeleteSubdirectory(std::string_view name) {
return FindAndRemoveVectorElement(dirs, name);
}
bool VectorVfsDirectory::DeleteFile(std::string_view name) {
return FindAndRemoveVectorElement(files, name);
}
bool VectorVfsDirectory::Rename(std::string_view name_) {
name = name_;
return true;
}
std::shared_ptr<VfsDirectory> VectorVfsDirectory::CreateSubdirectory(std::string_view name) {
return nullptr;
}
std::shared_ptr<VfsFile> VectorVfsDirectory::CreateFile(std::string_view name) {
return nullptr;
}
void VectorVfsDirectory::AddFile(VirtualFile file) {
files.push_back(std::move(file));
}
void VectorVfsDirectory::AddDirectory(VirtualDir dir) {
dirs.push_back(std::move(dir));
}
bool VectorVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
if (!DeleteFile(file->GetName()))
return false;
dirs.emplace_back(dir);
return true;
}
} // namespace FileSys

View File

@@ -0,0 +1,44 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/file_sys/vfs.h"
namespace FileSys {
// An implementation of VfsDirectory that maintains two vectors for subdirectories and files.
// Vector data is supplied upon construction.
struct VectorVfsDirectory : public VfsDirectory {
explicit VectorVfsDirectory(std::vector<VirtualFile> files = {},
std::vector<VirtualDir> dirs = {}, VirtualDir parent = nullptr,
std::string name = "");
std::vector<std::shared_ptr<VfsFile>> GetFiles() const override;
std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override;
bool IsWritable() const override;
bool IsReadable() const override;
std::string GetName() const override;
std::shared_ptr<VfsDirectory> GetParentDirectory() const override;
bool DeleteSubdirectory(std::string_view name) override;
bool DeleteFile(std::string_view name) override;
bool Rename(std::string_view name) override;
std::shared_ptr<VfsDirectory> CreateSubdirectory(std::string_view name) override;
std::shared_ptr<VfsFile> CreateFile(std::string_view name) override;
virtual void AddFile(VirtualFile file);
virtual void AddDirectory(VirtualDir dir);
protected:
bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
private:
std::vector<VirtualFile> files;
std::vector<VirtualDir> dirs;
VirtualDir parent;
std::string name;
};
} // namespace FileSys

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.
#include <cstring>
#include "core/hle/config_mem.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace ConfigMem {
ConfigMemDef config_mem;
void Init() {
std::memset(&config_mem, 0, sizeof(config_mem));
// Values extracted from firmware 11.2.0-35E
config_mem.kernel_version_min = 0x34;
config_mem.kernel_version_maj = 0x2;
config_mem.ns_tid = 0x0004013000008002;
config_mem.sys_core_ver = 0x2;
config_mem.unit_info = 0x1; // Bit 0 set for Retail
config_mem.prev_firm = 0x1;
config_mem.ctr_sdk_ver = 0x0000F297;
config_mem.firm_version_min = 0x34;
config_mem.firm_version_maj = 0x2;
config_mem.firm_sys_core_ver = 0x2;
config_mem.firm_ctr_sdk_ver = 0x0000F297;
}
} // namespace ConfigMem

View File

@@ -1,56 +0,0 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
// Configuration memory stores various hardware/kernel configuration settings. This memory page is
// read-only for ARM11 processes. I'm guessing this would normally be written to by the firmware/
// bootrom. Because we're not emulating this, and essentially just "stubbing" the functionality, I'm
// putting this as a subset of HLE for now.
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
#include "core/memory.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace ConfigMem {
struct ConfigMemDef {
u8 kernel_unk; // 0
u8 kernel_version_rev; // 1
u8 kernel_version_min; // 2
u8 kernel_version_maj; // 3
u32_le update_flag; // 4
u64_le ns_tid; // 8
u32_le sys_core_ver; // 10
u8 unit_info; // 14
u8 boot_firm; // 15
u8 prev_firm; // 16
INSERT_PADDING_BYTES(0x1); // 17
u32_le ctr_sdk_ver; // 18
INSERT_PADDING_BYTES(0x30 - 0x1C); // 1C
u32_le app_mem_type; // 30
INSERT_PADDING_BYTES(0x40 - 0x34); // 34
u32_le app_mem_alloc; // 40
u32_le sys_mem_alloc; // 44
u32_le base_mem_alloc; // 48
INSERT_PADDING_BYTES(0x60 - 0x4C); // 4C
u8 firm_unk; // 60
u8 firm_version_rev; // 61
u8 firm_version_min; // 62
u8 firm_version_maj; // 63
u32_le firm_sys_core_ver; // 64
u32_le firm_ctr_sdk_ver; // 68
INSERT_PADDING_BYTES(0x1000 - 0x6C); // 6C
};
static_assert(sizeof(ConfigMemDef) == Memory::CONFIG_MEMORY_SIZE,
"Config Memory structure size is wrong");
extern ConfigMemDef config_mem;
void Init();
} // namespace ConfigMem

View File

@@ -25,9 +25,9 @@ protected:
ptrdiff_t index = 0;
public:
RequestHelperBase(u32* command_buffer) : cmdbuf(command_buffer) {}
explicit RequestHelperBase(u32* command_buffer) : cmdbuf(command_buffer) {}
RequestHelperBase(Kernel::HLERequestContext& context)
explicit RequestHelperBase(Kernel::HLERequestContext& context)
: context(&context), cmdbuf(context.CommandBuffer()) {}
void Skip(unsigned size_in_words, bool set_to_null) {
@@ -56,13 +56,6 @@ public:
class ResponseBuilder : public RequestHelperBase {
public:
ResponseBuilder(u32* command_buffer) : RequestHelperBase(command_buffer) {}
u32 normal_params_size{};
u32 num_handles_to_copy{};
u32 num_objects_to_move{}; ///< Domain objects or move handles, context dependent
std::ptrdiff_t datapayload_index{};
/// Flags used for customizing the behavior of ResponseBuilder
enum class Flags : u32 {
None = 0,
@@ -71,9 +64,11 @@ public:
AlwaysMoveHandles = 1,
};
ResponseBuilder(Kernel::HLERequestContext& context, u32 normal_params_size,
u32 num_handles_to_copy = 0, u32 num_objects_to_move = 0,
Flags flags = Flags::None)
explicit ResponseBuilder(u32* command_buffer) : RequestHelperBase(command_buffer) {}
explicit ResponseBuilder(Kernel::HLERequestContext& context, u32 normal_params_size,
u32 num_handles_to_copy = 0, u32 num_objects_to_move = 0,
Flags flags = Flags::None)
: RequestHelperBase(context), normal_params_size(normal_params_size),
num_handles_to_copy(num_handles_to_copy), num_objects_to_move(num_objects_to_move) {
@@ -174,6 +169,25 @@ public:
template <typename First, typename... Other>
void Push(const First& first_value, const Other&... other_values);
/**
* Helper function for pushing strongly-typed enumeration values.
*
* @tparam Enum The enumeration type to be pushed
*
* @param value The value to push.
*
* @note The underlying size of the enumeration type is the size of the
* data that gets pushed. e.g. "enum class SomeEnum : u16" will
* push a u16-sized amount of data.
*/
template <typename Enum>
void PushEnum(Enum value) {
static_assert(std::is_enum_v<Enum>, "T must be an enum type within a PushEnum call.");
static_assert(!std::is_convertible_v<Enum, int>,
"enum type in PushEnum must be a strongly typed enum.");
Push(static_cast<std::underlying_type_t<Enum>>(value));
}
/**
* @brief Copies the content of the given trivially copyable class to the buffer as a normal
* param
@@ -187,6 +201,12 @@ public:
template <typename... O>
void PushCopyObjects(Kernel::SharedPtr<O>... pointers);
private:
u32 normal_params_size{};
u32 num_handles_to_copy{};
u32 num_objects_to_move{}; ///< Domain objects or move handles, context dependent
std::ptrdiff_t datapayload_index{};
};
/// Push ///
@@ -254,9 +274,9 @@ inline void ResponseBuilder::PushMoveObjects(Kernel::SharedPtr<O>... pointers) {
class RequestParser : public RequestHelperBase {
public:
RequestParser(u32* command_buffer) : RequestHelperBase(command_buffer) {}
explicit RequestParser(u32* command_buffer) : RequestHelperBase(command_buffer) {}
RequestParser(Kernel::HLERequestContext& context) : RequestHelperBase(context) {
explicit RequestParser(Kernel::HLERequestContext& context) : RequestHelperBase(context) {
ASSERT_MSG(context.GetDataPayloadOffset(), "context is incomplete");
Skip(context.GetDataPayloadOffset(), false);
// Skip the u64 command id, it's already stored in the context
@@ -266,8 +286,9 @@ public:
ResponseBuilder MakeBuilder(u32 normal_params_size, u32 num_handles_to_copy,
u32 num_handles_to_move,
ResponseBuilder::Flags flags = ResponseBuilder::Flags::None) {
return {*context, normal_params_size, num_handles_to_copy, num_handles_to_move, flags};
ResponseBuilder::Flags flags = ResponseBuilder::Flags::None) const {
return ResponseBuilder{*context, normal_params_size, num_handles_to_copy,
num_handles_to_move, flags};
}
template <typename T>
@@ -279,6 +300,14 @@ public:
template <typename First, typename... Other>
void Pop(First& first_value, Other&... other_values);
template <typename T>
T PopEnum() {
static_assert(std::is_enum_v<T>, "T must be an enum type within a PopEnum call.");
static_assert(!std::is_convertible_v<T, int>,
"enum type in PopEnum must be a strongly typed enum.");
return static_cast<T>(Pop<std::underlying_type_t<T>>());
}
/**
* @brief Reads the next normal parameters as a struct, by copying it
* @note: The output class must be correctly packed/padded to fit hardware layout.

View File

@@ -20,7 +20,7 @@ namespace AddressArbiter {
static ResultCode WaitForAddress(VAddr address, s64 timeout) {
SharedPtr<Thread> current_thread = GetCurrentThread();
current_thread->arb_wait_address = address;
current_thread->status = THREADSTATUS_WAIT_ARB;
current_thread->status = ThreadStatus::WaitArb;
current_thread->wakeup_callback = nullptr;
current_thread->WakeAfterDelay(timeout);
@@ -65,7 +65,7 @@ static void WakeThreads(std::vector<SharedPtr<Thread>>& waiting_threads, s32 num
// Signal the waiting threads.
for (size_t i = 0; i < last; i++) {
ASSERT(waiting_threads[i]->status == THREADSTATUS_WAIT_ARB);
ASSERT(waiting_threads[i]->status == ThreadStatus::WaitArb);
waiting_threads[i]->SetWaitSynchronizationResult(RESULT_SUCCESS);
waiting_threads[i]->arb_wait_address = 0;
waiting_threads[i]->ResumeFromWait();

View File

@@ -38,7 +38,7 @@ SharedPtr<Event> HLERequestContext::SleepClientThread(SharedPtr<Thread> thread,
thread->wakeup_callback =
[context = *this, callback](ThreadWakeupReason reason, SharedPtr<Thread> thread,
SharedPtr<WaitObject> object, size_t index) mutable -> bool {
ASSERT(thread->status == THREADSTATUS_WAIT_HLE_EVENT);
ASSERT(thread->status == ThreadStatus::WaitHLEEvent);
callback(thread, context, reason);
context.WriteToOutgoingCommandBuffer(*thread);
return true;
@@ -50,7 +50,7 @@ SharedPtr<Event> HLERequestContext::SleepClientThread(SharedPtr<Thread> thread,
}
event->Clear();
thread->status = THREADSTATUS_WAIT_HLE_EVENT;
thread->status = ThreadStatus::WaitHLEEvent;
thread->wait_objects = {event};
event->AddWaitingThread(thread);

View File

@@ -91,7 +91,7 @@ protected:
*/
class HLERequestContext {
public:
HLERequestContext(SharedPtr<Kernel::ServerSession> session);
explicit HLERequestContext(SharedPtr<ServerSession> session);
~HLERequestContext();
/// Returns a pointer to the IPC command buffer for this request.

View File

@@ -2,7 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/hle/config_mem.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/memory.h"
@@ -11,7 +10,6 @@
#include "core/hle/kernel/resource_limit.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/kernel/timer.h"
#include "core/hle/shared_page.h"
namespace Kernel {
@@ -19,9 +17,6 @@ unsigned int Object::next_object_id;
/// Initialize the kernel
void Init(u32 system_mode) {
ConfigMem::Init();
SharedPage::Init();
Kernel::MemoryInit(system_mode);
Kernel::ResourceLimitsInit();

View File

@@ -11,11 +11,9 @@
#include "common/assert.h"
#include "common/common_types.h"
#include "common/logging/log.h"
#include "core/hle/config_mem.h"
#include "core/hle/kernel/memory.h"
#include "core/hle/kernel/vm_manager.h"
#include "core/hle/result.h"
#include "core/hle/shared_page.h"
#include "core/memory.h"
#include "core/memory_setup.h"
@@ -63,14 +61,6 @@ void MemoryInit(u32 mem_type) {
// We must've allocated the entire FCRAM by the end
ASSERT(base == Memory::FCRAM_SIZE);
using ConfigMem::config_mem;
config_mem.app_mem_type = mem_type;
// app_mem_malloc does not always match the configured size for memory_region[0]: in case the
// n3DS type override is in effect it reports the size the game expects, not the real one.
config_mem.app_mem_alloc = memory_region_sizes[mem_type][0];
config_mem.sys_mem_alloc = static_cast<u32_le>(memory_regions[1].size);
config_mem.base_mem_alloc = static_cast<u32_le>(memory_regions[2].size);
}
void MemoryShutdown() {

View File

@@ -19,7 +19,7 @@ namespace Kernel {
/// Returns the number of threads that are waiting for a mutex, and the highest priority one among
/// those.
static std::pair<SharedPtr<Thread>, u32> GetHighestPriorityMutexWaitingThread(
SharedPtr<Thread> current_thread, VAddr mutex_addr) {
const SharedPtr<Thread>& current_thread, VAddr mutex_addr) {
SharedPtr<Thread> highest_priority_thread;
u32 num_waiters = 0;
@@ -28,7 +28,7 @@ static std::pair<SharedPtr<Thread>, u32> GetHighestPriorityMutexWaitingThread(
if (thread->mutex_wait_address != mutex_addr)
continue;
ASSERT(thread->status == THREADSTATUS_WAIT_MUTEX);
ASSERT(thread->status == ThreadStatus::WaitMutex);
++num_waiters;
if (highest_priority_thread == nullptr ||
@@ -83,7 +83,7 @@ ResultCode Mutex::TryAcquire(VAddr address, Handle holding_thread_handle,
GetCurrentThread()->mutex_wait_address = address;
GetCurrentThread()->wait_handle = requesting_thread_handle;
GetCurrentThread()->status = THREADSTATUS_WAIT_MUTEX;
GetCurrentThread()->status = ThreadStatus::WaitMutex;
GetCurrentThread()->wakeup_callback = nullptr;
// Update the lock holder thread's priority to prevent priority inversion.
@@ -121,7 +121,7 @@ ResultCode Mutex::Release(VAddr address) {
// Grant the mutex to the next waiting thread and resume it.
Memory::Write32(address, mutex_value);
ASSERT(thread->status == THREADSTATUS_WAIT_MUTEX);
ASSERT(thread->status == ThreadStatus::WaitMutex);
thread->ResumeFromWait();
thread->lock_owner = nullptr;

View File

@@ -34,7 +34,7 @@ Thread* Scheduler::PopNextReadyThread() {
Thread* next = nullptr;
Thread* thread = GetCurrentThread();
if (thread && thread->status == THREADSTATUS_RUNNING) {
if (thread && thread->status == ThreadStatus::Running) {
// We have to do better than the current thread.
// This call returns null when that's not possible.
next = ready_queue.pop_first_better(thread->current_priority);
@@ -56,18 +56,20 @@ void Scheduler::SwitchContext(Thread* new_thread) {
if (previous_thread) {
previous_thread->last_running_ticks = CoreTiming::GetTicks();
cpu_core->SaveContext(previous_thread->context);
// Save the TPIDR_EL0 system register in case it was modified.
previous_thread->tpidr_el0 = cpu_core->GetTPIDR_EL0();
if (previous_thread->status == THREADSTATUS_RUNNING) {
if (previous_thread->status == ThreadStatus::Running) {
// This is only the case when a reschedule is triggered without the current thread
// yielding execution (i.e. an event triggered, system core time-sliced, etc)
ready_queue.push_front(previous_thread->current_priority, previous_thread);
previous_thread->status = THREADSTATUS_READY;
previous_thread->status = ThreadStatus::Ready;
}
}
// Load context of new thread
if (new_thread) {
ASSERT_MSG(new_thread->status == THREADSTATUS_READY,
ASSERT_MSG(new_thread->status == ThreadStatus::Ready,
"Thread must be ready to become running.");
// Cancel any outstanding wakeup events for this thread
@@ -78,7 +80,7 @@ void Scheduler::SwitchContext(Thread* new_thread) {
current_thread = new_thread;
ready_queue.remove(new_thread->current_priority, new_thread);
new_thread->status = THREADSTATUS_RUNNING;
new_thread->status = ThreadStatus::Running;
if (previous_process != current_thread->owner_process) {
Core::CurrentProcess() = current_thread->owner_process;
@@ -87,6 +89,7 @@ void Scheduler::SwitchContext(Thread* new_thread) {
cpu_core->LoadContext(new_thread->context);
cpu_core->SetTlsAddress(new_thread->GetTLSAddress());
cpu_core->SetTPIDR_EL0(new_thread->GetTPIDR_EL0());
cpu_core->ClearExclusiveState();
} else {
current_thread = nullptr;
@@ -129,14 +132,14 @@ void Scheduler::RemoveThread(Thread* thread) {
void Scheduler::ScheduleThread(Thread* thread, u32 priority) {
std::lock_guard<std::mutex> lock(scheduler_mutex);
ASSERT(thread->status == THREADSTATUS_READY);
ASSERT(thread->status == ThreadStatus::Ready);
ready_queue.push_back(priority, thread);
}
void Scheduler::UnscheduleThread(Thread* thread, u32 priority) {
std::lock_guard<std::mutex> lock(scheduler_mutex);
ASSERT(thread->status == THREADSTATUS_READY);
ASSERT(thread->status == ThreadStatus::Ready);
ready_queue.remove(priority, thread);
}
@@ -144,7 +147,7 @@ void Scheduler::SetThreadPriority(Thread* thread, u32 priority) {
std::lock_guard<std::mutex> lock(scheduler_mutex);
// If thread was ready, adjust queues
if (thread->status == THREADSTATUS_READY)
if (thread->status == ThreadStatus::Ready)
ready_queue.move(thread, thread->current_priority, priority);
else
ready_queue.prepare(priority);

View File

@@ -110,10 +110,10 @@ ResultCode ServerSession::HandleSyncRequest(SharedPtr<Thread> thread) {
result = hle_handler->HandleSyncRequest(context);
}
if (thread->status == THREADSTATUS_RUNNING) {
if (thread->status == ThreadStatus::Running) {
// Put the thread to sleep until the server replies, it will be awoken in
// svcReplyAndReceive for LLE servers.
thread->status = THREADSTATUS_WAIT_IPC;
thread->status = ThreadStatus::WaitIPC;
if (hle_handler != nullptr) {
// For HLE services, we put the request threads to sleep for a short duration to

View File

@@ -40,7 +40,9 @@ static ResultCode SetHeapSize(VAddr* heap_addr, u64 heap_size) {
}
static ResultCode SetMemoryAttribute(VAddr addr, u64 size, u32 state0, u32 state1) {
LOG_WARNING(Kernel_SVC, "(STUBBED) called, addr=0x{:X}", addr);
LOG_WARNING(Kernel_SVC,
"(STUBBED) called, addr=0x{:X}, size=0x{:X}, state0=0x{:X}, state1=0x{:X}", addr,
size, state0, state1);
return RESULT_SUCCESS;
}
@@ -133,7 +135,7 @@ static ResultCode GetProcessId(u32* process_id, Handle process_handle) {
/// Default thread wakeup callback for WaitSynchronization
static bool DefaultThreadWakeupCallback(ThreadWakeupReason reason, SharedPtr<Thread> thread,
SharedPtr<WaitObject> object, size_t index) {
ASSERT(thread->status == THREADSTATUS_WAIT_SYNCH_ANY);
ASSERT(thread->status == ThreadStatus::WaitSynchAny);
if (reason == ThreadWakeupReason::Timeout) {
thread->SetWaitSynchronizationResult(RESULT_TIMEOUT);
@@ -165,11 +167,14 @@ static ResultCode WaitSynchronization(Handle* index, VAddr handles_address, u64
using ObjectPtr = SharedPtr<WaitObject>;
std::vector<ObjectPtr> objects(handle_count);
for (int i = 0; i < handle_count; ++i) {
Handle handle = Memory::Read32(handles_address + i * sizeof(Handle));
auto object = g_handle_table.Get<WaitObject>(handle);
if (object == nullptr)
for (u64 i = 0; i < handle_count; ++i) {
const Handle handle = Memory::Read32(handles_address + i * sizeof(Handle));
const auto object = g_handle_table.Get<WaitObject>(handle);
if (object == nullptr) {
return ERR_INVALID_HANDLE;
}
objects[i] = object;
}
@@ -197,7 +202,7 @@ static ResultCode WaitSynchronization(Handle* index, VAddr handles_address, u64
object->AddWaitingThread(thread);
thread->wait_objects = std::move(objects);
thread->status = THREADSTATUS_WAIT_SYNCH_ANY;
thread->status = ThreadStatus::WaitSynchAny;
// Create an event to wake the thread up after the specified nanosecond delay has passed
thread->WakeAfterDelay(nano_seconds);
@@ -217,7 +222,7 @@ static ResultCode CancelSynchronization(Handle thread_handle) {
return ERR_INVALID_HANDLE;
}
ASSERT(thread->status == THREADSTATUS_WAIT_SYNCH_ANY);
ASSERT(thread->status == ThreadStatus::WaitSynchAny);
thread->SetWaitSynchronizationResult(
ResultCode(ErrorModule::Kernel, ErrCodes::SynchronizationCanceled));
thread->ResumeFromWait();
@@ -468,8 +473,8 @@ static void ExitProcess() {
continue;
// TODO(Subv): When are the other running/ready threads terminated?
ASSERT_MSG(thread->status == THREADSTATUS_WAIT_SYNCH_ANY ||
thread->status == THREADSTATUS_WAIT_SYNCH_ALL,
ASSERT_MSG(thread->status == ThreadStatus::WaitSynchAny ||
thread->status == ThreadStatus::WaitSynchAll,
"Exiting processes with non-waiting threads is currently unimplemented");
thread->Stop();
@@ -545,7 +550,7 @@ static ResultCode StartThread(Handle thread_handle) {
return ERR_INVALID_HANDLE;
}
ASSERT(thread->status == THREADSTATUS_DORMANT);
ASSERT(thread->status == ThreadStatus::Dormant);
thread->ResumeFromWait();
Core::System::GetInstance().CpuCore(thread->processor_id).PrepareReschedule();
@@ -596,7 +601,7 @@ static ResultCode WaitProcessWideKeyAtomic(VAddr mutex_addr, VAddr condition_var
current_thread->condvar_wait_address = condition_variable_addr;
current_thread->mutex_wait_address = mutex_addr;
current_thread->wait_handle = thread_handle;
current_thread->status = THREADSTATUS_WAIT_MUTEX;
current_thread->status = ThreadStatus::WaitMutex;
current_thread->wakeup_callback = nullptr;
current_thread->WakeAfterDelay(nano_seconds);
@@ -650,13 +655,28 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
ASSERT(thread->condvar_wait_address == condition_variable_addr);
// If the mutex is not yet acquired, acquire it.
u32 mutex_val = Memory::Read32(thread->mutex_wait_address);
size_t current_core = Core::System::GetInstance().CurrentCoreIndex();
auto& monitor = Core::System::GetInstance().Monitor();
// Atomically read the value of the mutex.
u32 mutex_val = 0;
do {
monitor.SetExclusive(current_core, thread->mutex_wait_address);
// If the mutex is not yet acquired, acquire it.
mutex_val = Memory::Read32(thread->mutex_wait_address);
if (mutex_val != 0) {
monitor.ClearExclusive();
break;
}
} while (!monitor.ExclusiveWrite32(current_core, thread->mutex_wait_address,
thread->wait_handle));
if (mutex_val == 0) {
// We were able to acquire the mutex, resume this thread.
Memory::Write32(thread->mutex_wait_address, thread->wait_handle);
ASSERT(thread->status == THREADSTATUS_WAIT_MUTEX);
ASSERT(thread->status == ThreadStatus::WaitMutex);
thread->ResumeFromWait();
auto lock_owner = thread->lock_owner;
@@ -668,17 +688,26 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
thread->condvar_wait_address = 0;
thread->wait_handle = 0;
} else {
// Couldn't acquire the mutex, block the thread.
// Atomically signal that the mutex now has a waiting thread.
do {
monitor.SetExclusive(current_core, thread->mutex_wait_address);
// Ensure that the mutex value is still what we expect.
u32 value = Memory::Read32(thread->mutex_wait_address);
// TODO(Subv): When this happens, the kernel just clears the exclusive state and
// retries the initial read for this thread.
ASSERT_MSG(mutex_val == value, "Unhandled synchronization primitive case");
} while (!monitor.ExclusiveWrite32(current_core, thread->mutex_wait_address,
mutex_val | Mutex::MutexHasWaitersFlag));
// The mutex is already owned by some other thread, make this thread wait on it.
Handle owner_handle = static_cast<Handle>(mutex_val & Mutex::MutexOwnerMask);
auto owner = g_handle_table.Get<Thread>(owner_handle);
ASSERT(owner);
ASSERT(thread->status != THREADSTATUS_RUNNING);
thread->status = THREADSTATUS_WAIT_MUTEX;
ASSERT(thread->status != ThreadStatus::Running);
thread->status = ThreadStatus::WaitMutex;
thread->wakeup_callback = nullptr;
// Signal that the mutex now has a waiting thread.
Memory::Write32(thread->mutex_wait_address, mutex_val | Mutex::MutexHasWaitersFlag);
owner->AddMutexWaiter(thread);
Core::System::GetInstance().CpuCore(thread->processor_id).PrepareReschedule();

View File

@@ -14,6 +14,7 @@
#include "core/arm/arm_interface.h"
#include "core/core.h"
#include "core/core_timing.h"
#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"
@@ -30,7 +31,7 @@ namespace Kernel {
static CoreTiming::EventType* ThreadWakeupEventType = nullptr;
bool Thread::ShouldWait(Thread* thread) const {
return status != THREADSTATUS_DEAD;
return status != ThreadStatus::Dead;
}
void Thread::Acquire(Thread* thread) {
@@ -63,11 +64,11 @@ void Thread::Stop() {
// Clean up thread from ready queue
// This is only needed when the thread is termintated forcefully (SVC TerminateProcess)
if (status == THREADSTATUS_READY) {
if (status == ThreadStatus::Ready) {
scheduler->UnscheduleThread(this, current_priority);
}
status = THREADSTATUS_DEAD;
status = ThreadStatus::Dead;
WakeupAllWaitingThreads();
@@ -86,7 +87,7 @@ void Thread::Stop() {
void WaitCurrentThread_Sleep() {
Thread* thread = GetCurrentThread();
thread->status = THREADSTATUS_WAIT_SLEEP;
thread->status = ThreadStatus::WaitSleep;
}
void ExitCurrentThread() {
@@ -110,10 +111,9 @@ static void ThreadWakeupCallback(u64 thread_handle, int cycles_late) {
bool resume = true;
if (thread->status == THREADSTATUS_WAIT_SYNCH_ANY ||
thread->status == THREADSTATUS_WAIT_SYNCH_ALL ||
thread->status == THREADSTATUS_WAIT_HLE_EVENT) {
if (thread->status == ThreadStatus::WaitSynchAny ||
thread->status == ThreadStatus::WaitSynchAll ||
thread->status == ThreadStatus::WaitHLEEvent) {
// Remove the thread from each of its waiting objects' waitlists
for (auto& object : thread->wait_objects)
object->RemoveWaitingThread(thread.get());
@@ -126,7 +126,7 @@ static void ThreadWakeupCallback(u64 thread_handle, int cycles_late) {
if (thread->mutex_wait_address != 0 || thread->condvar_wait_address != 0 ||
thread->wait_handle) {
ASSERT(thread->status == THREADSTATUS_WAIT_MUTEX);
ASSERT(thread->status == ThreadStatus::WaitMutex);
thread->mutex_wait_address = 0;
thread->condvar_wait_address = 0;
thread->wait_handle = 0;
@@ -141,7 +141,7 @@ static void ThreadWakeupCallback(u64 thread_handle, int cycles_late) {
}
if (thread->arb_wait_address != 0) {
ASSERT(thread->status == THREADSTATUS_WAIT_ARB);
ASSERT(thread->status == ThreadStatus::WaitArb);
thread->arb_wait_address = 0;
}
@@ -178,28 +178,28 @@ void Thread::ResumeFromWait() {
ASSERT_MSG(wait_objects.empty(), "Thread is waking up while waiting for objects");
switch (status) {
case THREADSTATUS_WAIT_SYNCH_ALL:
case THREADSTATUS_WAIT_SYNCH_ANY:
case THREADSTATUS_WAIT_HLE_EVENT:
case THREADSTATUS_WAIT_SLEEP:
case THREADSTATUS_WAIT_IPC:
case THREADSTATUS_WAIT_MUTEX:
case THREADSTATUS_WAIT_ARB:
case ThreadStatus::WaitSynchAll:
case ThreadStatus::WaitSynchAny:
case ThreadStatus::WaitHLEEvent:
case ThreadStatus::WaitSleep:
case ThreadStatus::WaitIPC:
case ThreadStatus::WaitMutex:
case ThreadStatus::WaitArb:
break;
case THREADSTATUS_READY:
case ThreadStatus::Ready:
// The thread's wakeup callback must have already been cleared when the thread was first
// awoken.
ASSERT(wakeup_callback == nullptr);
// If the thread is waiting on multiple wait objects, it might be awoken more than once
// before actually resuming. We can ignore subsequent wakeups if the thread status has
// already been set to THREADSTATUS_READY.
// already been set to ThreadStatus::Ready.
return;
case THREADSTATUS_RUNNING:
case ThreadStatus::Running:
DEBUG_ASSERT_MSG(false, "Thread with object id {} has already resumed.", GetObjectId());
return;
case THREADSTATUS_DEAD:
case ThreadStatus::Dead:
// This should never happen, as threads must complete before being stopped.
DEBUG_ASSERT_MSG(false, "Thread with object id {} cannot be resumed because it's DEAD.",
GetObjectId());
@@ -208,7 +208,7 @@ void Thread::ResumeFromWait() {
wakeup_callback = nullptr;
status = THREADSTATUS_READY;
status = ThreadStatus::Ready;
boost::optional<s32> new_processor_id = GetNextProcessorId(affinity_mask);
if (!new_processor_id) {
@@ -310,9 +310,10 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
SharedPtr<Thread> thread(new Thread);
thread->thread_id = NewThreadId();
thread->status = THREADSTATUS_DORMANT;
thread->status = ThreadStatus::Dormant;
thread->entry_point = entry_point;
thread->stack_top = stack_top;
thread->tpidr_el0 = 0;
thread->nominal_priority = thread->current_priority = priority;
thread->last_running_ticks = CoreTiming::GetTicks();
thread->processor_id = processor_id;
@@ -471,7 +472,7 @@ void Thread::ChangeCore(u32 core, u64 mask) {
ideal_core = core;
affinity_mask = mask;
if (status != THREADSTATUS_READY) {
if (status != ThreadStatus::Ready) {
return;
}

View File

@@ -36,18 +36,18 @@ enum ThreadProcessorId : s32 {
(1 << THREADPROCESSORID_2) | (1 << THREADPROCESSORID_3)
};
enum ThreadStatus {
THREADSTATUS_RUNNING, ///< Currently running
THREADSTATUS_READY, ///< Ready to run
THREADSTATUS_WAIT_HLE_EVENT, ///< Waiting for hle event to finish
THREADSTATUS_WAIT_SLEEP, ///< Waiting due to a SleepThread SVC
THREADSTATUS_WAIT_IPC, ///< Waiting for the reply from an IPC request
THREADSTATUS_WAIT_SYNCH_ANY, ///< Waiting due to WaitSynch1 or WaitSynchN with wait_all = false
THREADSTATUS_WAIT_SYNCH_ALL, ///< Waiting due to WaitSynchronizationN with wait_all = true
THREADSTATUS_WAIT_MUTEX, ///< Waiting due to an ArbitrateLock/WaitProcessWideKey svc
THREADSTATUS_WAIT_ARB, ///< Waiting due to a SignalToAddress/WaitForAddress svc
THREADSTATUS_DORMANT, ///< Created but not yet made ready
THREADSTATUS_DEAD ///< Run to completion, or forcefully terminated
enum class ThreadStatus {
Running, ///< Currently running
Ready, ///< Ready to run
WaitHLEEvent, ///< Waiting for hle event to finish
WaitSleep, ///< Waiting due to a SleepThread SVC
WaitIPC, ///< Waiting for the reply from an IPC request
WaitSynchAny, ///< Waiting due to WaitSynch1 or WaitSynchN with wait_all = false
WaitSynchAll, ///< Waiting due to WaitSynchronizationN with wait_all = true
WaitMutex, ///< Waiting due to an ArbitrateLock/WaitProcessWideKey svc
WaitArb, ///< Waiting due to a SignalToAddress/WaitForAddress svc
Dormant, ///< Created but not yet made ready
Dead ///< Run to completion, or forcefully terminated
};
enum class ThreadWakeupReason {
@@ -182,6 +182,14 @@ public:
return tls_address;
}
/*
* Returns the value of the TPIDR_EL0 Read/Write system register for this thread.
* @returns The value of the TPIDR_EL0 register.
*/
u64 GetTPIDR_EL0() const {
return tpidr_el0;
}
/*
* Returns the address of the current thread's command buffer, located in the TLS.
* @returns VAddr of the thread's command buffer.
@@ -194,14 +202,14 @@ public:
* with wait_all = true.
*/
bool IsSleepingOnWaitAll() const {
return status == THREADSTATUS_WAIT_SYNCH_ALL;
return status == ThreadStatus::WaitSynchAll;
}
ARM_Interface::ThreadContext context;
u32 thread_id;
u32 status;
ThreadStatus status;
VAddr entry_point;
VAddr stack_top;
@@ -213,6 +221,7 @@ public:
s32 processor_id;
VAddr tls_address; ///< Virtual address of the Thread Local Storage of the thread
u64 tpidr_el0; ///< TPIDR_EL0 read/write system register.
SharedPtr<Process> owner_process; ///< Process that owns this thread

View File

@@ -6,6 +6,7 @@
#include "common/assert.h"
#include "common/logging/log.h"
#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/thread.h"

View File

@@ -32,13 +32,17 @@ public:
return HANDLE_TYPE;
}
ResetType reset_type; ///< The ResetType of this timer
ResetType GetResetType() const {
return reset_type;
}
bool signaled; ///< Whether the timer has been signaled or not
std::string name; ///< Name of timer (optional)
u64 GetInitialDelay() const {
return initial_delay;
}
u64 initial_delay; ///< The delay until the timer fires for the first time
u64 interval_delay; ///< The delay until the timer fires after the first time
u64 GetIntervalDelay() const {
return interval_delay;
}
bool ShouldWait(Thread* thread) const override;
void Acquire(Thread* thread) override;
@@ -67,6 +71,14 @@ private:
Timer();
~Timer() override;
ResetType reset_type; ///< The ResetType of this timer
u64 initial_delay; ///< The delay until the timer fires for the first time
u64 interval_delay; ///< The delay until the timer fires after the first time
bool signaled; ///< Whether the timer has been signaled or not
std::string name; ///< Name of timer (optional)
/// Handle used as userdata to reference this object when inserting into the CoreTiming queue.
Handle callback_handle;
};

View File

@@ -5,7 +5,6 @@
#include <algorithm>
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/hle/config_mem.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/memory.h"
@@ -13,7 +12,6 @@
#include "core/hle/kernel/resource_limit.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/kernel/timer.h"
#include "core/hle/shared_page.h"
namespace Kernel {
@@ -38,9 +36,9 @@ SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() {
for (const auto& thread : waiting_threads) {
// The list of waiting threads must not contain threads that are not waiting to be awakened.
ASSERT_MSG(thread->status == THREADSTATUS_WAIT_SYNCH_ANY ||
thread->status == THREADSTATUS_WAIT_SYNCH_ALL ||
thread->status == THREADSTATUS_WAIT_HLE_EVENT,
ASSERT_MSG(thread->status == ThreadStatus::WaitSynchAny ||
thread->status == ThreadStatus::WaitSynchAll ||
thread->status == ThreadStatus::WaitHLEEvent,
"Inconsistent thread statuses in waiting_threads");
if (thread->current_priority >= candidate_priority)
@@ -49,10 +47,10 @@ SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() {
if (ShouldWait(thread.get()))
continue;
// A thread is ready to run if it's either in THREADSTATUS_WAIT_SYNCH_ANY or
// in THREADSTATUS_WAIT_SYNCH_ALL and the rest of the objects it is waiting on are ready.
// A thread is ready to run if it's either in ThreadStatus::WaitSynchAny or
// in ThreadStatus::WaitSynchAll and the rest of the objects it is waiting on are ready.
bool ready_to_run = true;
if (thread->status == THREADSTATUS_WAIT_SYNCH_ALL) {
if (thread->status == ThreadStatus::WaitSynchAll) {
ready_to_run = std::none_of(thread->wait_objects.begin(), thread->wait_objects.end(),
[&thread](const SharedPtr<WaitObject>& object) {
return object->ShouldWait(thread.get());

View File

@@ -35,7 +35,7 @@ static constexpr u128 DEFAULT_USER_ID{1ull, 0ull};
class IProfile final : public ServiceFramework<IProfile> {
public:
IProfile(u128 user_id) : ServiceFramework("IProfile"), user_id(user_id) {
explicit IProfile(u128 user_id) : ServiceFramework("IProfile"), user_id(user_id) {
static const FunctionInfo functions[] = {
{0, nullptr, "Get"},
{1, &IProfile::GetBase, "GetBase"},
@@ -82,14 +82,16 @@ private:
LOG_WARNING(Service_ACC, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push(true); // TODO: Check when this is supposed to return true and when not
rb.Push(false); // TODO: Check when this is supposed to return true and when not
}
void GetAccountId(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_ACC, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push<u64>(0x12345678ABCDEF);
// TODO(Subv): Find out what this actually does and implement it. Stub it as an error for
// now since we do not implement NNID. Returning a bogus id here will cause games to send
// invalid IPC requests after ListOpenUsers is called.
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultCode(-1));
}
};
@@ -104,7 +106,7 @@ void Module::Interface::ListAllUsers(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_ACC, "(STUBBED) called");
// TODO(Subv): There is only one user for now.
const std::vector<u128> user_ids = {DEFAULT_USER_ID};
ctx.WriteBuffer(user_ids.data(), user_ids.size() * sizeof(u128));
ctx.WriteBuffer(user_ids);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
@@ -113,7 +115,7 @@ void Module::Interface::ListOpenUsers(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_ACC, "(STUBBED) called");
// TODO(Subv): There is only one user for now.
const std::vector<u128> user_ids = {DEFAULT_USER_ID};
ctx.WriteBuffer(user_ids.data(), user_ids.size() * sizeof(u128));
ctx.WriteBuffer(user_ids);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}

View File

@@ -20,6 +20,21 @@ public:
}
private:
enum class PerformanceConfiguration : u32 {
Config1 = 0x00010000,
Config2 = 0x00010001,
Config3 = 0x00010002,
Config4 = 0x00020000,
Config5 = 0x00020001,
Config6 = 0x00020002,
Config7 = 0x00020003,
Config8 = 0x00020004,
Config9 = 0x00020005,
Config10 = 0x00020006,
Config11 = 0x92220007,
Config12 = 0x92220008,
};
void SetPerformanceConfiguration(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
@@ -40,7 +55,7 @@ private:
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(0); // Performance configuration
rb.Push<u32>(static_cast<u32>(PerformanceConfiguration::Config1));
LOG_WARNING(Service_APM, "(STUBBED) called mode={}", static_cast<u32>(mode));
}

View File

@@ -19,7 +19,4 @@ private:
std::shared_ptr<Module> apm;
};
/// Registers all AM services with the specified service manager.
void InstallInterfaces(SM::ServiceManager& service_manager);
} // namespace Service::APM

View File

@@ -5,7 +5,7 @@
#include <array>
#include <vector>
#include "common/logging/log.h"
#include "core/core_timing.h"
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/event.h"
#include "core/hle/kernel/hle_ipc.h"
@@ -13,17 +13,22 @@
namespace Service::Audio {
/// Switch sample rate frequency
constexpr u32 sample_rate{48000};
/// TODO(st4rk): dynamic number of channels, as I think Switch has support
/// to more audio channels (probably when Docked I guess)
constexpr u32 audio_channels{2};
/// TODO(st4rk): find a proper value for the audio_ticks
constexpr u64 audio_ticks{static_cast<u64>(CoreTiming::BASE_CLOCK_RATE / 500)};
namespace ErrCodes {
enum {
ErrorUnknown = 2,
BufferCountExceeded = 8,
};
}
constexpr std::array<char, 10> DefaultDevice{{"DeviceOut"}};
constexpr int DefaultSampleRate{48000};
class IAudioOut final : public ServiceFramework<IAudioOut> {
public:
IAudioOut() : ServiceFramework("IAudioOut"), audio_out_state(AudioState::Stopped) {
IAudioOut(AudoutParams audio_params)
: ServiceFramework("IAudioOut"), audio_params(audio_params),
audio_core(Core::System::GetInstance().AudioCore()) {
static const FunctionInfo functions[] = {
{0, &IAudioOut::GetAudioOutState, "GetAudioOutState"},
{1, &IAudioOut::StartAudioOut, "StartAudioOut"},
@@ -31,66 +36,65 @@ public:
{3, &IAudioOut::AppendAudioOutBufferImpl, "AppendAudioOutBuffer"},
{4, &IAudioOut::RegisterBufferEvent, "RegisterBufferEvent"},
{5, &IAudioOut::GetReleasedAudioOutBufferImpl, "GetReleasedAudioOutBuffer"},
{6, nullptr, "ContainsAudioOutBuffer"},
{6, &IAudioOut::ContainsAudioOutBuffer, "ContainsAudioOutBuffer"},
{7, &IAudioOut::AppendAudioOutBufferImpl, "AppendAudioOutBufferAuto"},
{8, &IAudioOut::GetReleasedAudioOutBufferImpl, "GetReleasedAudioOutBufferAuto"},
{9, nullptr, "GetAudioOutBufferCount"},
{9, &IAudioOut::GetAudioOutBufferCount, "GetAudioOutBufferCount"},
{10, nullptr, "GetAudioOutPlayedSampleCount"},
{11, nullptr, "FlushAudioOutBuffers"},
};
RegisterHandlers(functions);
// This is the event handle used to check if the audio buffer was released
buffer_event =
Kernel::Event::Create(Kernel::ResetType::OneShot, "IAudioOutBufferReleasedEvent");
buffer_event = Kernel::Event::Create(Kernel::ResetType::Sticky, "IAudioOutBufferReleased");
// Register event callback to update the Audio Buffer
audio_event = CoreTiming::RegisterEvent(
"IAudioOut::UpdateAudioBuffersCallback", [this](u64 userdata, int cycles_late) {
UpdateAudioBuffersCallback();
CoreTiming::ScheduleEvent(audio_ticks - cycles_late, audio_event);
});
// Start the audio event
CoreTiming::ScheduleEvent(audio_ticks, audio_event);
}
~IAudioOut() {
CoreTiming::UnscheduleEvent(audio_event, 0);
stream = audio_core.OpenStream(audio_params.sample_rate, audio_params.channel_count,
[=]() { buffer_event->Signal(); });
}
private:
struct AudioBuffer {
u64_le next;
u64_le buffer;
u64_le buffer_capacity;
u64_le buffer_size;
u64_le offset;
};
static_assert(sizeof(AudioBuffer) == 0x28, "AudioBuffer is an invalid size");
void GetAudioOutState(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Audio, "called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push(static_cast<u32>(audio_out_state));
rb.Push(static_cast<u32>(stream->IsPlaying() ? AudioState::Started : AudioState::Stopped));
}
void StartAudioOut(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_Audio, "(STUBBED) called");
LOG_DEBUG(Service_Audio, "called");
// Start audio
audio_out_state = AudioState::Started;
if (stream->IsPlaying()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultCode(ErrorModule::Audio, ErrCodes::ErrorUnknown));
return;
}
audio_core.StartStream(stream);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void StopAudioOut(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_Audio, "(STUBBED) called");
LOG_DEBUG(Service_Audio, "called");
// Stop audio
audio_out_state = AudioState::Stopped;
queue_keys.clear();
audio_core.StopStream(stream);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void RegisterBufferEvent(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_Audio, "(STUBBED) called");
LOG_DEBUG(Service_Audio, "called");
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
@@ -98,101 +102,107 @@ private:
}
void AppendAudioOutBufferImpl(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_Audio, "(STUBBED) called");
LOG_DEBUG(Service_Audio, "(STUBBED) called {}", ctx.Description());
IPC::RequestParser rp{ctx};
const u64 key{rp.Pop<u64>()};
queue_keys.insert(queue_keys.begin(), key);
const auto& input_buffer{ctx.ReadBuffer()};
ASSERT_MSG(input_buffer.size() == sizeof(AudioBuffer),
"AudioBuffer input is an invalid size!");
AudioBuffer audio_buffer{};
std::memcpy(&audio_buffer, input_buffer.data(), sizeof(AudioBuffer));
const u64 tag{rp.Pop<u64>()};
std::vector<u8> data(audio_buffer.buffer_size);
Memory::ReadBlock(audio_buffer.buffer, data.data(), data.size());
if (!audio_core.QueueBuffer(stream, tag, std::move(data))) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultCode(ErrorModule::Audio, ErrCodes::BufferCountExceeded));
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void GetReleasedAudioOutBufferImpl(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_Audio, "(STUBBED) called");
LOG_DEBUG(Service_Audio, "called {}", ctx.Description());
IPC::RequestParser rp{ctx};
const u64 max_count{ctx.GetWriteBufferSize() / sizeof(u64)};
const auto released_buffers{audio_core.GetTagsAndReleaseBuffers(stream, max_count)};
// TODO(st4rk): This is how libtransistor currently implements the
// GetReleasedAudioOutBuffer, it should return the key (a VAddr) to the app and this address
// is used to know which buffer should be filled with data and send again to the service
// through AppendAudioOutBuffer. Check if this is the proper way to do it.
u64 key{0};
if (queue_keys.size()) {
key = queue_keys.back();
queue_keys.pop_back();
}
ctx.WriteBuffer(&key, sizeof(u64));
std::vector<u64> tags{released_buffers};
tags.resize(max_count);
ctx.WriteBuffer(tags);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
// TODO(st4rk): This might be the total of released buffers, needs to be verified on
// hardware
rb.Push<u32>(static_cast<u32>(queue_keys.size()));
rb.Push<u32>(static_cast<u32>(released_buffers.size()));
}
void UpdateAudioBuffersCallback() {
if (audio_out_state != AudioState::Started) {
return;
}
if (queue_keys.empty()) {
return;
}
buffer_event->Signal();
void ContainsAudioOutBuffer(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Audio, "called");
IPC::RequestParser rp{ctx};
const u64 tag{rp.Pop<u64>()};
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push(stream->ContainsBuffer(tag));
}
enum class AudioState : u32 {
Started,
Stopped,
};
void GetAudioOutBufferCount(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Audio, "called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push(static_cast<u32>(stream->GetQueueSize()));
}
/// This is used to trigger the audio event callback that is going to read the samples from the
/// audio_buffer list and enqueue the samples using the sink (audio_core).
CoreTiming::EventType* audio_event;
AudioCore::AudioOut& audio_core;
AudioCore::StreamPtr stream;
AudoutParams audio_params{};
/// This is the evend handle used to check if the audio buffer was released
Kernel::SharedPtr<Kernel::Event> buffer_event;
/// (st4rk): This is just a temporary workaround for the future implementation. Libtransistor
/// uses the key as an address in the App, so we need to return when the
/// GetReleasedAudioOutBuffer_1 is called, otherwise we'll run in problems, because
/// libtransistor uses the key returned as an pointer.
std::vector<u64> queue_keys;
AudioState audio_out_state;
};
void AudOutU::ListAudioOutsImpl(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_Audio, "(STUBBED) called");
LOG_DEBUG(Service_Audio, "called");
IPC::RequestParser rp{ctx};
constexpr std::array<char, 15> audio_interface{{"AudioInterface"}};
ctx.WriteBuffer(audio_interface);
ctx.WriteBuffer(DefaultDevice);
IPC::ResponseBuilder rb = rp.MakeBuilder(3, 0, 0);
rb.Push(RESULT_SUCCESS);
// TODO(st4rk): We're currently returning only one audio interface (stringlist size). However,
// it's highly possible to have more than one interface (despite that libtransistor requires
// only one).
rb.Push<u32>(1);
rb.Push<u32>(1); // Amount of audio devices
}
void AudOutU::OpenAudioOutImpl(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_Audio, "(STUBBED) called");
LOG_DEBUG(Service_Audio, "called");
if (!audio_out_interface) {
audio_out_interface = std::make_shared<IAudioOut>();
ctx.WriteBuffer(DefaultDevice);
IPC::RequestParser rp{ctx};
auto params{rp.PopRaw<AudoutParams>()};
if (params.channel_count <= 2) {
// Mono does not exist for audout
params.channel_count = 2;
} else {
params.channel_count = 6;
}
if (!params.sample_rate) {
params.sample_rate = DefaultSampleRate;
}
// TODO(bunnei): Support more than one IAudioOut interface. When we add this, ListAudioOutsImpl
// will likely need to be updated as well.
ASSERT_MSG(!audio_out_interface, "Unimplemented");
audio_out_interface = std::make_shared<IAudioOut>(std::move(params));
IPC::ResponseBuilder rb{ctx, 6, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(sample_rate);
rb.Push<u32>(audio_channels);
rb.Push<u32>(DefaultSampleRate);
rb.Push<u32>(params.channel_count);
rb.Push<u32>(static_cast<u32>(PcmFormat::Int16));
rb.Push<u32>(0); // This field is unknown
rb.Push<u32>(static_cast<u32>(AudioState::Stopped));
rb.PushIpcInterface<Audio::IAudioOut>(audio_out_interface);
}

View File

@@ -12,6 +12,18 @@ class HLERequestContext;
namespace Service::Audio {
struct AudoutParams {
s32_le sample_rate;
u16_le channel_count;
INSERT_PADDING_BYTES(2);
};
static_assert(sizeof(AudoutParams) == 0x8, "AudoutParams is an invalid size");
enum class AudioState : u32 {
Started,
Stopped,
};
class IAudioOut;
class AudOutU final : public ServiceFramework<AudOutU> {

View File

@@ -7,6 +7,7 @@
#include "common/alignment.h"
#include "common/logging/log.h"
#include "core/core_timing.h"
#include "core/core_timing_util.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/event.h"
#include "core/hle/kernel/hle_ipc.h"

View File

@@ -2,6 +2,8 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <cstring>
#include <opus.h>
#include "common/logging/log.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/hle_ipc.h"
@@ -9,19 +11,142 @@
namespace Service::Audio {
struct OpusDeleter {
void operator()(void* ptr) const {
operator delete(ptr);
}
};
class IHardwareOpusDecoderManager final : public ServiceFramework<IHardwareOpusDecoderManager> {
public:
IHardwareOpusDecoderManager(std::unique_ptr<OpusDecoder, OpusDeleter> decoder, u32 sample_rate,
u32 channel_count)
: ServiceFramework("IHardwareOpusDecoderManager"), decoder(std::move(decoder)),
sample_rate(sample_rate), channel_count(channel_count) {
static const FunctionInfo functions[] = {
{0, &IHardwareOpusDecoderManager::DecodeInterleaved, "DecodeInterleaved"},
{1, nullptr, "SetContext"},
{2, nullptr, "DecodeInterleavedForMultiStream"},
{3, nullptr, "SetContextForMultiStream"},
{4, nullptr, "Unknown4"},
{5, nullptr, "Unknown5"},
{6, nullptr, "Unknown6"},
{7, nullptr, "Unknown7"},
};
RegisterHandlers(functions);
}
private:
void DecodeInterleaved(Kernel::HLERequestContext& ctx) {
u32 consumed = 0;
u32 sample_count = 0;
std::vector<opus_int16> samples(ctx.GetWriteBufferSize() / sizeof(opus_int16));
if (!Decoder_DecodeInterleaved(consumed, sample_count, ctx.ReadBuffer(), samples)) {
IPC::ResponseBuilder rb{ctx, 2};
// TODO(ogniK): Use correct error code
rb.Push(ResultCode(-1));
return;
}
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(consumed);
rb.Push<u32>(sample_count);
ctx.WriteBuffer(samples.data(), samples.size() * sizeof(s16));
}
bool Decoder_DecodeInterleaved(u32& consumed, u32& sample_count, const std::vector<u8>& input,
std::vector<opus_int16>& output) {
size_t raw_output_sz = output.size() * sizeof(opus_int16);
if (sizeof(OpusHeader) > input.size())
return false;
OpusHeader hdr{};
std::memcpy(&hdr, input.data(), sizeof(OpusHeader));
if (sizeof(OpusHeader) + static_cast<u32>(hdr.sz) > input.size()) {
return false;
}
auto frame = input.data() + sizeof(OpusHeader);
auto decoded_sample_count = opus_packet_get_nb_samples(
frame, static_cast<opus_int32>(input.size() - sizeof(OpusHeader)),
static_cast<opus_int32>(sample_rate));
if (decoded_sample_count * channel_count * sizeof(u16) > raw_output_sz)
return false;
auto out_sample_count =
opus_decode(decoder.get(), frame, hdr.sz, output.data(),
(static_cast<int>(raw_output_sz / sizeof(s16) / channel_count)), 0);
if (out_sample_count < 0)
return false;
sample_count = out_sample_count;
consumed = static_cast<u32>(sizeof(OpusHeader) + hdr.sz);
return true;
}
struct OpusHeader {
u32_be sz; // Needs to be BE for some odd reason
INSERT_PADDING_WORDS(1);
};
static_assert(sizeof(OpusHeader) == 0x8, "OpusHeader is an invalid size");
std::unique_ptr<OpusDecoder, OpusDeleter> decoder;
u32 sample_rate;
u32 channel_count;
};
static size_t WorkerBufferSize(u32 channel_count) {
ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count");
return opus_decoder_get_size(static_cast<int>(channel_count));
}
void HwOpus::GetWorkBufferSize(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_Audio, "(STUBBED) called");
IPC::RequestParser rp{ctx};
auto sample_rate = rp.Pop<u32>();
auto channel_count = rp.Pop<u32>();
ASSERT_MSG(sample_rate == 48000 || sample_rate == 24000 || sample_rate == 16000 ||
sample_rate == 12000 || sample_rate == 8000,
"Invalid sample rate");
ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count");
u32 worker_buffer_sz = static_cast<u32>(WorkerBufferSize(channel_count));
LOG_DEBUG(Audio, "called worker_buffer_sz={}", worker_buffer_sz);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(0x4000);
rb.Push<u32>(worker_buffer_sz);
}
void HwOpus::OpenOpusDecoder(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
auto sample_rate = rp.Pop<u32>();
auto channel_count = rp.Pop<u32>();
auto buffer_sz = rp.Pop<u32>();
LOG_DEBUG(Audio, "called sample_rate={}, channel_count={}, buffer_size={}", sample_rate,
channel_count, buffer_sz);
ASSERT_MSG(sample_rate == 48000 || sample_rate == 24000 || sample_rate == 16000 ||
sample_rate == 12000 || sample_rate == 8000,
"Invalid sample rate");
ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count");
size_t worker_sz = WorkerBufferSize(channel_count);
ASSERT_MSG(buffer_sz < worker_sz, "Worker buffer too large");
std::unique_ptr<OpusDecoder, OpusDeleter> decoder{
static_cast<OpusDecoder*>(operator new(worker_sz))};
if (opus_decoder_init(decoder.get(), sample_rate, channel_count)) {
IPC::ResponseBuilder rb{ctx, 2};
// TODO(ogniK): Use correct error code
rb.Push(ResultCode(-1));
return;
}
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IHardwareOpusDecoderManager>(std::move(decoder), sample_rate,
channel_count);
}
HwOpus::HwOpus() : ServiceFramework("hwopus") {
static const FunctionInfo functions[] = {
{0, nullptr, "Initialize"},
{0, &HwOpus::OpenOpusDecoder, "OpenOpusDecoder"},
{1, &HwOpus::GetWorkBufferSize, "GetWorkBufferSize"},
{2, nullptr, "InitializeMultiStream"},
{3, nullptr, "GetWorkBufferSizeMultiStream"},
{2, nullptr, "OpenOpusDecoderForMultiStream"},
{3, nullptr, "GetWorkBufferSizeForMultiStream"},
};
RegisterHandlers(functions);
}

View File

@@ -14,6 +14,7 @@ public:
~HwOpus() = default;
private:
void OpenOpusDecoder(Kernel::HLERequestContext& ctx);
void GetWorkBufferSize(Kernel::HLERequestContext& ctx);
};

View File

@@ -0,0 +1,72 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/hle/service/btdrv/btdrv.h"
#include "core/hle/service/service.h"
#include "core/hle/service/sm/sm.h"
namespace Service::BtDrv {
class BtDrv final : public ServiceFramework<BtDrv> {
public:
explicit BtDrv() : ServiceFramework{"btdrv"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "Unknown"},
{1, nullptr, "Init"},
{2, nullptr, "Enable"},
{3, nullptr, "Disable"},
{4, nullptr, "CleanupAndShutdown"},
{5, nullptr, "GetAdapterProperties"},
{6, nullptr, "GetAdapterProperty"},
{7, nullptr, "SetAdapterProperty"},
{8, nullptr, "StartDiscovery"},
{9, nullptr, "CancelDiscovery"},
{10, nullptr, "CreateBond"},
{11, nullptr, "RemoveBond"},
{12, nullptr, "CancelBond"},
{13, nullptr, "PinReply"},
{14, nullptr, "SspReply"},
{15, nullptr, "Unknown2"},
{16, nullptr, "InitInterfaces"},
{17, nullptr, "HidHostInterface_Connect"},
{18, nullptr, "HidHostInterface_Disconnect"},
{19, nullptr, "HidHostInterface_SendData"},
{20, nullptr, "HidHostInterface_SendData2"},
{21, nullptr, "HidHostInterface_SetReport"},
{22, nullptr, "HidHostInterface_GetReport"},
{23, nullptr, "HidHostInterface_WakeController"},
{24, nullptr, "HidHostInterface_AddPairedDevice"},
{25, nullptr, "HidHostInterface_GetPairedDevice"},
{26, nullptr, "HidHostInterface_CleanupAndShutdown"},
{27, nullptr, "Unknown3"},
{28, nullptr, "ExtInterface_SetTSI"},
{29, nullptr, "ExtInterface_SetBurstMode"},
{30, nullptr, "ExtInterface_SetZeroRetran"},
{31, nullptr, "ExtInterface_SetMcMode"},
{32, nullptr, "ExtInterface_StartLlrMode"},
{33, nullptr, "ExtInterface_ExitLlrMode"},
{34, nullptr, "ExtInterface_SetRadio"},
{35, nullptr, "ExtInterface_SetVisibility"},
{36, nullptr, "Unknown4"},
{37, nullptr, "Unknown5"},
{38, nullptr, "HidHostInterface_GetLatestPlr"},
{39, nullptr, "ExtInterface_GetPendingConnections"},
{40, nullptr, "HidHostInterface_GetChannelMap"},
{41, nullptr, "SetIsBluetoothBoostEnabled"},
{42, nullptr, "GetIsBluetoothBoostEnabled"},
{43, nullptr, "SetIsBluetoothAfhEnabled"},
{44, nullptr, "GetIsBluetoothAfhEnabled"},
};
// clang-format on
RegisterHandlers(functions);
}
};
void InstallInterfaces(SM::ServiceManager& sm) {
std::make_shared<BtDrv>()->InstallAsService(sm);
}
} // namespace Service::BtDrv

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
namespace Service::SM {
class ServiceManager;
}
namespace Service::BtDrv {
/// Registers all BtDrv services with the specified service manager.
void InstallInterfaces(SM::ServiceManager& sm);
} // namespace Service::BtDrv

View File

@@ -0,0 +1,51 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <memory>
#include "core/hle/service/erpt/erpt.h"
#include "core/hle/service/service.h"
#include "core/hle/service/sm/sm.h"
namespace Service::ERPT {
class ErrorReportContext final : public ServiceFramework<ErrorReportContext> {
public:
explicit ErrorReportContext() : ServiceFramework{"erpt:c"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "SubmitContext"},
{1, nullptr, "CreateReport"},
{2, nullptr, "Unknown1"},
{3, nullptr, "Unknown2"},
{4, nullptr, "Unknown3"},
{5, nullptr, "Unknown4"},
{6, nullptr, "Unknown5"},
};
// clang-format on
RegisterHandlers(functions);
}
};
class ErrorReportSession final : public ServiceFramework<ErrorReportSession> {
public:
explicit ErrorReportSession() : ServiceFramework{"erpt:r"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "OpenReport"},
{1, nullptr, "OpenManager"},
};
// clang-format on
RegisterHandlers(functions);
}
};
void InstallInterfaces(SM::ServiceManager& sm) {
std::make_shared<ErrorReportContext>()->InstallAsService(sm);
std::make_shared<ErrorReportSession>()->InstallAsService(sm);
}
} // namespace Service::ERPT

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
namespace Service::SM {
class ServiceManager;
}
namespace Service::ERPT {
/// Registers all ERPT services with the specified service manager.
void InstallInterfaces(SM::ServiceManager& sm);
} // namespace Service::ERPT

View File

@@ -0,0 +1,57 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/hle/service/service.h"
namespace Service::ES {
class ETicket final : public ServiceFramework<ETicket> {
public:
explicit ETicket() : ServiceFramework{"es"} {
static const FunctionInfo functions[] = {
{1, nullptr, "ImportTicket"},
{2, nullptr, "ImportTicketCertificateSet"},
{3, nullptr, "DeleteTicket"},
{4, nullptr, "DeletePersonalizedTicket"},
{5, nullptr, "DeleteAllCommonTicket"},
{6, nullptr, "DeleteAllPersonalizedTicket"},
{7, nullptr, "DeleteAllPersonalizedTicketEx"},
{8, nullptr, "GetTitleKey"},
{9, nullptr, "CountCommonTicket"},
{10, nullptr, "CountPersonalizedTicket"},
{11, nullptr, "ListCommonTicket"},
{12, nullptr, "ListPersonalizedTicket"},
{13, nullptr, "ListMissingPersonalizedTicket"},
{14, nullptr, "GetCommonTicketSize"},
{15, nullptr, "GetPersonalizedTicketSize"},
{16, nullptr, "GetCommonTicketData"},
{17, nullptr, "GetPersonalizedTicketData"},
{18, nullptr, "OwnTicket"},
{19, nullptr, "GetTicketInfo"},
{20, nullptr, "ListLightTicketInfo"},
{21, nullptr, "SignData"},
{22, nullptr, "GetCommonTicketAndCertificateSize"},
{23, nullptr, "GetCommonTicketAndCertificateData"},
{24, nullptr, "ImportPrepurchaseRecord"},
{25, nullptr, "DeletePrepurchaseRecord"},
{26, nullptr, "DeleteAllPrepurchaseRecord"},
{27, nullptr, "CountPrepurchaseRecord"},
{28, nullptr, "ListPrepurchaseRecord"},
{29, nullptr, "ListPrepurchaseRecordInfo"},
{30, nullptr, "Unknown1"},
{31, nullptr, "Unknown2"},
{32, nullptr, "Unknown3"},
{33, nullptr, "Unknown4"},
{34, nullptr, "Unknown5"},
{35, nullptr, "Unknown6"},
};
RegisterHandlers(functions);
}
};
void InstallInterfaces(SM::ServiceManager& service_manager) {
std::make_shared<ETicket>()->InstallAsService(service_manager);
}
} // namespace Service::ES

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
namespace Service::SM {
class ServiceManager;
}
namespace Service::ES {
/// Registers all ES services with the specified service manager.
void InstallInterfaces(SM::ServiceManager& service_manager);
} // namespace Service::ES

View File

@@ -0,0 +1,52 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <memory>
#include "core/hle/service/eupld/eupld.h"
#include "core/hle/service/service.h"
#include "core/hle/service/sm/sm.h"
namespace Service::EUPLD {
class ErrorUploadContext final : public ServiceFramework<ErrorUploadContext> {
public:
explicit ErrorUploadContext() : ServiceFramework{"eupld:c"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "SetUrl"},
{1, nullptr, "ImportCrt"},
{2, nullptr, "ImportPki"},
{3, nullptr, "SetAutoUpload"},
};
// clang-format on
RegisterHandlers(functions);
}
};
class ErrorUploadRequest final : public ServiceFramework<ErrorUploadRequest> {
public:
explicit ErrorUploadRequest() : ServiceFramework{"eupld:r"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "Initialize"},
{1, nullptr, "UploadAll"},
{2, nullptr, "UploadSelected"},
{3, nullptr, "GetUploadStatus"},
{4, nullptr, "CancelUpload"},
{5, nullptr, "GetResult"},
};
// clang-format on
RegisterHandlers(functions);
}
};
void InstallInterfaces(SM::ServiceManager& sm) {
std::make_shared<ErrorUploadContext>()->InstallAsService(sm);
std::make_shared<ErrorUploadRequest>()->InstallAsService(sm);
}
} // namespace Service::EUPLD

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
namespace Service::SM {
class ServiceManager;
}
namespace Service::EUPLD {
/// Registers all EUPLD services with the specified service manager.
void InstallInterfaces(SM::ServiceManager& sm);
} // namespace Service::EUPLD

View File

@@ -24,7 +24,8 @@ namespace Service::FileSystem {
constexpr u64 EMULATED_SD_REPORTED_SIZE = 32000000000;
static FileSys::VirtualDir GetDirectoryRelativeWrapped(FileSys::VirtualDir base,
const std::string& dir_name) {
std::string_view dir_name_) {
std::string dir_name(FileUtil::SanitizePath(dir_name_));
if (dir_name.empty() || dir_name == "." || dir_name == "/" || dir_name == "\\")
return base;
@@ -38,7 +39,8 @@ std::string VfsDirectoryServiceWrapper::GetName() const {
return backing->GetName();
}
ResultCode VfsDirectoryServiceWrapper::CreateFile(const std::string& path, u64 size) const {
ResultCode VfsDirectoryServiceWrapper::CreateFile(const std::string& path_, u64 size) const {
std::string path(FileUtil::SanitizePath(path_));
auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
auto file = dir->CreateFile(FileUtil::GetFilename(path));
if (file == nullptr) {
@@ -52,7 +54,8 @@ ResultCode VfsDirectoryServiceWrapper::CreateFile(const std::string& path, u64 s
return RESULT_SUCCESS;
}
ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path) const {
ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path_) const {
std::string path(FileUtil::SanitizePath(path_));
auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
if (path == "/" || path == "\\") {
// TODO(DarkLordZach): Why do games call this and what should it do? Works as is but...
@@ -60,14 +63,15 @@ ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path) const
}
if (dir->GetFile(FileUtil::GetFilename(path)) == nullptr)
return FileSys::ERROR_PATH_NOT_FOUND;
if (!backing->DeleteFile(FileUtil::GetFilename(path))) {
if (!dir->DeleteFile(FileUtil::GetFilename(path))) {
// TODO(DarkLordZach): Find a better error code for this
return ResultCode(-1);
}
return RESULT_SUCCESS;
}
ResultCode VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path) const {
ResultCode VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path_) const {
std::string path(FileUtil::SanitizePath(path_));
auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
if (dir == nullptr && FileUtil::GetFilename(FileUtil::GetParentPath(path)).empty())
dir = backing;
@@ -79,7 +83,8 @@ ResultCode VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path)
return RESULT_SUCCESS;
}
ResultCode VfsDirectoryServiceWrapper::DeleteDirectory(const std::string& path) const {
ResultCode VfsDirectoryServiceWrapper::DeleteDirectory(const std::string& path_) const {
std::string path(FileUtil::SanitizePath(path_));
auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
if (!dir->DeleteSubdirectory(FileUtil::GetFilename(path))) {
// TODO(DarkLordZach): Find a better error code for this
@@ -88,7 +93,8 @@ ResultCode VfsDirectoryServiceWrapper::DeleteDirectory(const std::string& path)
return RESULT_SUCCESS;
}
ResultCode VfsDirectoryServiceWrapper::DeleteDirectoryRecursively(const std::string& path) const {
ResultCode VfsDirectoryServiceWrapper::DeleteDirectoryRecursively(const std::string& path_) const {
std::string path(FileUtil::SanitizePath(path_));
auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
if (!dir->DeleteSubdirectoryRecursive(FileUtil::GetFilename(path))) {
// TODO(DarkLordZach): Find a better error code for this
@@ -97,8 +103,10 @@ ResultCode VfsDirectoryServiceWrapper::DeleteDirectoryRecursively(const std::str
return RESULT_SUCCESS;
}
ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path,
const std::string& dest_path) const {
ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path_,
const std::string& dest_path_) const {
std::string src_path(FileUtil::SanitizePath(src_path_));
std::string dest_path(FileUtil::SanitizePath(dest_path_));
auto src = backing->GetFileRelative(src_path);
if (FileUtil::GetParentPath(src_path) == FileUtil::GetParentPath(dest_path)) {
// Use more-optimized vfs implementation rename.
@@ -130,8 +138,10 @@ ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path,
return RESULT_SUCCESS;
}
ResultCode VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_path,
const std::string& dest_path) const {
ResultCode VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_path_,
const std::string& dest_path_) const {
std::string src_path(FileUtil::SanitizePath(src_path_));
std::string dest_path(FileUtil::SanitizePath(dest_path_));
auto src = GetDirectoryRelativeWrapped(backing, src_path);
if (FileUtil::GetParentPath(src_path) == FileUtil::GetParentPath(dest_path)) {
// Use more-optimized vfs implementation rename.
@@ -154,8 +164,9 @@ ResultCode VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_pa
return ResultCode(-1);
}
ResultVal<FileSys::VirtualFile> VfsDirectoryServiceWrapper::OpenFile(const std::string& path,
ResultVal<FileSys::VirtualFile> VfsDirectoryServiceWrapper::OpenFile(const std::string& path_,
FileSys::Mode mode) const {
std::string path(FileUtil::SanitizePath(path_));
auto npath = path;
while (npath.size() > 0 && (npath[0] == '/' || npath[0] == '\\'))
npath = npath.substr(1);
@@ -171,7 +182,8 @@ ResultVal<FileSys::VirtualFile> VfsDirectoryServiceWrapper::OpenFile(const std::
return MakeResult<FileSys::VirtualFile>(file);
}
ResultVal<FileSys::VirtualDir> VfsDirectoryServiceWrapper::OpenDirectory(const std::string& path) {
ResultVal<FileSys::VirtualDir> VfsDirectoryServiceWrapper::OpenDirectory(const std::string& path_) {
std::string path(FileUtil::SanitizePath(path_));
auto dir = GetDirectoryRelativeWrapped(backing, path);
if (dir == nullptr) {
// TODO(DarkLordZach): Find a better error code for this
@@ -188,7 +200,8 @@ u64 VfsDirectoryServiceWrapper::GetFreeSpaceSize() const {
}
ResultVal<FileSys::EntryType> VfsDirectoryServiceWrapper::GetEntryType(
const std::string& path) const {
const std::string& path_) const {
std::string path(FileUtil::SanitizePath(path_));
auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
if (dir == nullptr)
return FileSys::ERROR_PATH_NOT_FOUND;
@@ -272,9 +285,9 @@ void RegisterFileSystems() {
sdmc_factory = nullptr;
auto nand_directory = std::make_shared<FileSys::RealVfsDirectory>(
FileUtil::GetUserPath(D_NAND_IDX), FileSys::Mode::Write);
FileUtil::GetUserPath(FileUtil::UserPath::NANDDir), FileSys::Mode::ReadWrite);
auto sd_directory = std::make_shared<FileSys::RealVfsDirectory>(
FileUtil::GetUserPath(D_SDMC_IDX), FileSys::Mode::Write);
FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir), FileSys::Mode::ReadWrite);
auto savedata = std::make_unique<FileSys::SaveDataFactory>(std::move(nand_directory));
save_data_factory = std::move(savedata);

View File

@@ -5,8 +5,7 @@
#include "common/logging/log.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/service/friend/friend.h"
#include "core/hle/service/friend/friend_a.h"
#include "core/hle/service/friend/friend_u.h"
#include "core/hle/service/friend/interface.h"
namespace Service::Friend {
@@ -21,8 +20,11 @@ Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
void InstallInterfaces(SM::ServiceManager& service_manager) {
auto module = std::make_shared<Module>();
std::make_shared<Friend_A>(module)->InstallAsService(service_manager);
std::make_shared<Friend_U>(module)->InstallAsService(service_manager);
std::make_shared<Friend>(module, "friend:a")->InstallAsService(service_manager);
std::make_shared<Friend>(module, "friend:m")->InstallAsService(service_manager);
std::make_shared<Friend>(module, "friend:s")->InstallAsService(service_manager);
std::make_shared<Friend>(module, "friend:u")->InstallAsService(service_manager);
std::make_shared<Friend>(module, "friend:v")->InstallAsService(service_manager);
}
} // namespace Service::Friend

View File

@@ -1,18 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/hle/service/friend/friend_a.h"
namespace Service::Friend {
Friend_A::Friend_A(std::shared_ptr<Module> module)
: Module::Interface(std::move(module), "friend:a") {
static const FunctionInfo functions[] = {
{0, &Friend_A::CreateFriendService, "CreateFriendService"},
{1, nullptr, "CreateNotificationService"},
};
RegisterHandlers(functions);
}
} // namespace Service::Friend

View File

@@ -1,16 +0,0 @@
// 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/friend/friend.h"
namespace Service::Friend {
class Friend_U final : public Module::Interface {
public:
explicit Friend_U(std::shared_ptr<Module> module);
};
} // namespace Service::Friend

View File

@@ -2,15 +2,16 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/hle/service/friend/friend_u.h"
#include "core/hle/service/friend/interface.h"
namespace Service::Friend {
Friend_U::Friend_U(std::shared_ptr<Module> module)
: Module::Interface(std::move(module), "friend:u") {
Friend::Friend(std::shared_ptr<Module> module, const char* name)
: Interface(std::move(module), name) {
static const FunctionInfo functions[] = {
{0, &Friend_U::CreateFriendService, "CreateFriendService"},
{0, &Friend::CreateFriendService, "CreateFriendService"},
{1, nullptr, "CreateNotificationService"},
{2, nullptr, "CreateDaemonSuspendSessionService"},
};
RegisterHandlers(functions);
}

View File

@@ -8,9 +8,9 @@
namespace Service::Friend {
class Friend_A final : public Module::Interface {
class Friend final : public Module::Interface {
public:
explicit Friend_A(std::shared_ptr<Module> module);
explicit Friend(std::shared_ptr<Module> module, const char* name);
};
} // namespace Service::Friend

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