Compare commits

..

197 Commits

Author SHA1 Message Date
Morph
fa2aac1bf5 yuzu: main: Ensure enough space is available for RomFS dumping
This warns the user if there isn't enough free space to dump the entire RomFS to disk. It requires at least the size of the extracted RomFS + 1 GiB as a buffer of free space.
2021-06-11 14:04:11 -04:00
Morph
fbb170857f Merge pull request #6450 from lat9nq/update-sdl
externals: Update SDL to 2f248a2a
2021-06-11 06:33:50 -04:00
lat9nq
f738c6b231 externals: Update SDL to 2f248a2a 2021-06-11 04:40:16 -04:00
bunnei
c1b8e59ea0 Merge pull request #6407 from lat9nq/fix-libusb-2
cmake: Use autotools for libusb linking generally on GNU, and cleanup
2021-06-10 23:35:30 -07:00
bunnei
46ec0ee55b Merge pull request #6445 from degasus/fix_ubsn
Fix GCC undefined behavior sanitizer.
2021-06-10 22:17:33 -07:00
bunnei
4547b2735a Merge pull request #6444 from bunnei/fix-sm-sessions
hle: service: sm: Remove redundant session reservation, etc.
2021-06-10 12:17:13 -07:00
Markus Wick
6755025310 Fix GCC undefined behavior sanitizer.
* Wrong alignment in u64 LOG_DEBUG -> memcpy.
* Huge shift exponent in stride calculation for linear buffer, unused result -> skipped.
* Large shift in buffer cache if word = 0, skip checking for set bits.

Non of those were critical, so this should not change any behavior.
At least with the assumption, that the last one used masking behavior, which always yield continuous_bits = 0.
2021-06-10 21:07:27 +02:00
bunnei
781c85b951 hle: service: sm: Remove redundant session reservation, etc.
- We were double-reserving, causing us to run out of sessions in Pokemon Sword & Shield.
2021-06-10 11:34:41 -07:00
bunnei
fa8a0065ca hle: service: Increase arbitrary max sessions limit.
- Pokemon Sword/Shield are still hitting this for some reason, causing an svcBreak.
2021-06-10 00:08:09 -07:00
bunnei
74f0087bfa Merge pull request #6441 from bunnei/fix-session-handler
hle: kernel: KServerSession: Fix client disconnected.
2021-06-09 22:53:25 -07:00
bunnei
b259e95c09 hle: kernel: KClientPort: Add an assert for session count.
- Prevents us from over decrementing num_sessions.
2021-06-09 22:36:42 -07:00
bunnei
ec5674a6ad hle: service: sm: Fix GetService setup of session & port. 2021-06-09 22:29:18 -07:00
bunnei
2aa6a8d889 hle: service: Use correct size for ServerSessionCountMax. 2021-06-09 22:04:36 -07:00
bunnei
b2971b48ed hle: kernel: KServerSession: Fix client disconnected.
- Prevents a cloned session's handler from being overwritten by another disconnected session.
- Fixes session handler nullptr asserts with Pokemon Sword & Shield.
2021-06-09 21:37:11 -07:00
Ameer J
86d832ab9a Merge pull request #6439 from lat9nq/ci-no-7z
ci: common: Remove 7z packaging
2021-06-09 19:47:08 -04:00
Mai M
61c7a81ec8 Merge pull request #6440 from bunnei/cancel-synch
kernel: svc: Add missing error check to CancelSynchronization.
2021-06-09 19:08:36 -04:00
lat9nq
fbad68de0f ci: windows: Compress using xz
Use XZ instead of gzip for packing. Should save about 10 MB.
2021-06-09 18:54:23 -04:00
bunnei
c63ea608aa kernel: svc: Add missing error check to CancelSynchronization.
- Avoids a potential crash if the handle is invalid, and also makes this code accurate to real kernel behavior.
2021-06-09 15:24:46 -07:00
lat9nq
6eeb532c96 ci: common: Remove 7z packaging
Removes the 7z from being package during CI, as only .tar.xz preserves
information needed on Linux, and otherwise is just extremely redundant
to package in addition to the .tar.xz.  This affects Linux releases and
PR-verify artifacts only. MSVC releases do not use this script to my
knowledge.
2021-06-09 17:16:29 -04:00
Mai M
5857067a18 Merge pull request #6436 from liushuyu/master
src/common/CMakeLists.txt: fix variable escaping
2021-06-09 15:38:56 -04:00
bunnei
2d32fc2318 hle: service: Increase arbitrary max sessions limit.
- Pokemon Sword/Shield are still hitting this for some reason, causing an svcBreak.
2021-06-09 11:59:34 -07:00
bunnei
75a4ac12c6 Merge pull request #6413 from Kewlan/limitable_input_dialog_limit
limitable_input_dialog: Implement character limiter
2021-06-09 11:55:36 -07:00
liushuyu
eb9deffab6 src/common/CMakeLists.txt: fix variable escaping 2021-06-09 02:20:55 -06:00
Morph
15483c07c6 Merge pull request #6435 from lioncash/nodisc2
common/fs/path_util: Remove [[nodiscard]] from function with void return
2021-06-09 02:44:41 -04:00
bunnei
f9c3e2e872 Merge pull request #6434 from lioncash/tcontext
configure_ui: Add translation context for file-scope strings
2021-06-08 19:36:44 -07:00
bunnei
3c621d37f0 Merge pull request #6428 from bunnei/service-thread-crash-fix
hle: kernel: Remove service thread manager and use weak_ptr.
2021-06-08 16:43:55 -07:00
Lioncash
dd8577e91d common/fs/path_util: Remove [[nodiscard]] from function with void return
We can't make use of the return value here, since we don't a return
value to work with.
2021-06-08 19:36:09 -04:00
Lioncash
b3eb08254b configure_ui: Add translation context for file-scope strings
Allows for these strings to show up in the translation files.
2021-06-08 19:33:23 -04:00
Mai M
f09c9b5fcc Merge pull request #6426 from lat9nq/context-menu-start
yuzu qt: Start games from context menu
2021-06-08 17:09:25 -04:00
bunnei
b8fb9b3f11 hle: kernel: KServerSession: Work-around scenario where session is closed too early. 2021-06-08 13:39:20 -07:00
bunnei
08d798b6fe hle: kernel: hle_ipc: Ensure SessionRequestHandler is valid. 2021-06-07 21:55:37 -07:00
bunnei
3b5673daca Merge pull request #6412 from clementgallet/yuzu-cmd-window-gl
yuzu-cmd: Fix OpenGL rendering
2021-06-07 21:12:17 -07:00
bunnei
a493ab2678 hle: kernel: Remove service thread manager and use weak_ptr.
- We no longer need to queue up service threads to be destroyed.
- Fixes a race condition where a thread could be destroyed too early, which caused a crash in Pokemon Sword/Shield.
2021-06-07 21:10:51 -07:00
lat9nq
5ac018d1df yuzu qt: Start games from context menu
This connects the BootGame function to the context menu. In addition,
there is an option to boot without using the custom configuration.
2021-06-07 20:27:51 -04:00
bunnei
df91c9f5e6 Merge pull request #6410 from lat9nq/avoid-oob
decoders: Avoid out-of-bounds access
2021-06-07 10:51:17 -07:00
bunnei
28eb8c83d4 Merge pull request #6414 from bunnei/fix-service-threads
hle: kernel: Refactor to allocate a ServiceThread per service handler.
2021-06-06 22:52:07 -07:00
bunnei
9db569b2d9 hle: kernel: KServerSession: Use ASSERT_MSG where appropriate. 2021-06-06 22:09:25 -07:00
bunnei
ada4242c01 hle: kernel: k_server_session: Return service thread by strong pointer. 2021-06-06 17:54:06 -07:00
bunnei
93f93cb8bc hle: kernel: k_server_session: Ensure service thread is valid before dereference. 2021-06-06 17:03:36 -07:00
bunnei
afd0e2eb0b Merge pull request #6400 from ameerj/disable-uniform-simplify
buffer_cache: Simplify uniform disabling logic
2021-06-06 15:42:20 -07:00
bunnei
384cbe3829 hle: kernel: hle_ipc: Use default destructor for SessionRequestManager. 2021-06-06 15:41:16 -07:00
bunnei
6119836795 hle: kernel: KAutoObjectWithListContainer: Use boost::instrusive::rbtree.
- Fixes some crashes introduced by our common intrusive red/black tree impl.
2021-06-06 15:39:11 -07:00
Kewlan
058196a089 limitable_input_dialog: Implement character limiter
When using GetText() you can now choose what set of characters the user can't enter.
2021-06-06 09:07:55 +02:00
Morph
31dac5d95f Merge pull request #6415 from lioncash/res-nodisc
result: Add [[nodiscard]] specifiers where applicable
2021-06-06 00:52:36 -04:00
Rodrigo Locatti
1bccbc424c Merge pull request #6416 from ReinUsesLisp/update-dynarmic
externals: Update dynarmic
2021-06-05 17:35:54 -03:00
ReinUsesLisp
71a3c60d95 externals: Update dynarmic 2021-06-05 15:24:12 -03:00
Clément Gallet
2e1c58b905 Avoid -Wshadow warning
Co-authored-by: Mai M. <mathew1800@gmail.com>
2021-06-05 18:43:10 +02:00
Lioncash
25b73e135f result: Add [[nodiscard]] specifiers where applicable
The result code classes are used quite extensively throughout both the
kernel and service HLE code. We can mark these member functions as
[[nodiscard]] to prevent a few logic bugs from slipping through.
2021-06-05 06:09:07 -04:00
bunnei
fefc76e5da Merge pull request #6362 from lat9nq/reset-to-defaults
yuzu qt: Add settings reset button to general configuration
2021-06-04 21:07:39 -07:00
Mai M
07f6646f7f Merge pull request #6411 from clementgallet/yuzu-cmd-touch-button
yuzu-cmd: Add touch_from_button in config file
2021-06-04 23:21:29 -04:00
bunnei
27ce97fd42 hle: kernel: Refactor to allocate a ServiceThread per service handler.
- Previously, we would allocate a thread per session, which adds new threads on CloneCurrentObject.
- This results in race conditions with N sessions queuing requests to the same service interface.
- Fixes Pokken Tournament DX crashes/softlocks, which were regressed by #6347.
2021-06-04 19:26:48 -07:00
Clément Gallet
9ff8504452 yuzu-cmd: Fix OpenGL rendering 2021-06-04 11:39:04 +02:00
lat9nq
287a0f72a5 decoders: Break instead of continue
continue causes a memory leak in A Hat in Time.
2021-06-04 05:12:14 -04:00
lat9nq
1feefabeba decoders: Avoid out-of-bounds access
This is not a real fix, so assert here and continue before crashing.
2021-06-04 05:03:54 -04:00
bunnei
c8b3d92836 Merge pull request #6392 from german77/controller-widget
settings: Disable controller preview if controller is not active
2021-06-04 00:40:04 -07:00
bunnei
1d1f616063 Merge pull request #6389 from german77/Analog_button_fix
input_common: Analog button, use time based position
2021-06-03 21:06:38 -07:00
Maide
cb5fe12ee1 [game_list] Correct light theme loading (#6408)
Correct light theme loading

The setLayout call in game list instantiation will call resizing signals with default values in light theme, which was then being erroneously saved. setLayout doesn't seem to call resizing for any other theme, so I'm not sure why that happens.
2021-06-03 19:07:38 -04:00
Clément Gallet
166f5d1612 yuzu-cmd: Add touch_from_button in config file 2021-06-04 00:58:35 +02:00
lat9nq
7395cd3124 externals: libusb: Call program names not full paths 2021-06-03 04:53:01 -04:00
lat9nq
890acfa2c0 externals: libusb: Link libusb statically on Linux
Turns out that this is possible. Also addresses my own review comment.
2021-06-03 04:38:29 -04:00
lat9nq
ddc47e6df8 cmake: General improvements to libusb linking
Delegates libusb external communication to externals/CMakeLists.txt
Ensures an interface library `usb` for every pathway
input_common just links to the `usb` library now
externals/libusb/CMakeLists.txt sets variables to override SDL2's libusb
finding
Other minor cleanup
2021-06-03 03:49:35 -04:00
bunnei
e4fed17f59 Merge pull request #6402 from Kelebek1/UI
game_list: Stop the columns resizing on NAND install
2021-06-03 00:24:45 -07:00
lat9nq
55dd027115 cmake: Use autotools to build libusb generally for GNU
Building libusb was also broken on GCC (and maybe Clang) on our
CMakeLists after upgrading to 1.0.24, but it was not being checked
because our 18.04 container had libusb installed on it.
This builds on the MinGW work from earlier and extends it to the rest of
the GNU toolchains. In addition we make use of pkg-config when present
to find libusb. pkg-config is preferrable because we can specify a
minimum required version.
2021-06-03 02:49:53 -04:00
bunnei
5a6d002bf0 Merge pull request #6404 from lat9nq/revert_views
yuzu qt: Revert some usages of string_view
2021-06-02 22:11:35 -07:00
bunnei
395cc0c32f Merge pull request #6405 from Morph1984/result-success
fsp-srv: Replace one last instance of RESULT_SUCCESS
2021-06-02 19:20:24 -07:00
Morph
b840dd9af8 fsp-srv: Replace one last instance of RESULT_SUCCESS 2021-06-02 21:40:14 -04:00
Chloe
c4c256f56a fspsrv: Implement DisableAutoSaveDataCreation (#6355)
- Used by Mii Edit
2021-06-02 17:46:29 -07:00
lat9nq
c41451af75 yuzu qt: Revert some usages of string_view
Causes a heap-use-after free reported by AddressSanitizer. This makes
use of std::filesystem::path, but due to that we have to use their
string() function which may not work for all characters.
2021-06-02 19:50:20 -04:00
bunnei
4ea171fa5e Merge pull request #6308 from Morph1984/result
general: Replace RESULT_NAME with ResultName
2021-06-02 15:29:09 -07:00
bunnei
d6006e9a3f Merge pull request #6403 from Kewlan/game-list-for-loop-optimization
game_list: Minor for loop optimizations
2021-06-02 15:27:54 -07:00
Kewlan
65d42a428f game_list: Minor for loop optimizations
There's no need to check the first and last rows since they'll always be the Favorites and AddDir rows.
Also change the name of the clear_all variable for consistency.
2021-06-02 16:19:55 +02:00
Kelebek1
04e52ffed0 Stop the columns resizing on NAND install 2021-06-02 06:27:08 +01:00
Morph
a0e4c2e1fc general: Replace RESULT_UNKNOWN with ResultUnknown
Transition to PascalCase for result names.
2021-06-02 00:39:27 -04:00
Morph
12c1766997 general: Replace RESULT_SUCCESS with ResultSuccess
Transition to PascalCase for result names.
2021-06-02 00:39:27 -04:00
Morph
377cd301b3 Merge pull request #6395 from lioncash/result-move
common_funcs: Move R_ macros to result.h
2021-06-02 00:34:48 -04:00
Mai M
50866199a4 Merge pull request #6397 from Morph1984/fs_util
common: fs: fs_util: Add more string conversion functions
2021-06-02 00:28:34 -04:00
Morph
dba7bcd489 common: fs: fs_util: Move PathToUTF8String to fs_util 2021-06-02 00:26:26 -04:00
Morph
a1eeb9908d common: fs: fs_util: Add more string conversion functions 2021-06-02 00:26:26 -04:00
bunnei
91f559a71f Merge pull request #6361 from lat9nq/per-hb-cfg
yuzu qt: Handle per-game configs for title id 0
2021-06-01 17:24:08 -07:00
lat9nq
c17e1bd7a8 yuzu qt: Use lambda and std::function for reset callback
Also makes use of std::move, and performs a clang-format cleanup.

This addresses review comments.

Co-authored-by: LC <mathew1800@gmail.com>
2021-06-01 17:22:06 -04:00
lat9nq
4a3d57e469 yuzu: Add settings reset button to general configuration
Builds on german77's work to reset all settings back to their defaults.
This include UISettings and Settings values structs, but does not affect
save profiles, input profiles, and game directories.

This works from a button input in configure_general. When activated, it
calls a callback to close the whole configure dialog, then GMainWindow
deletes the old configuration, both on disk and in memory, and
reinitalizes a new one. It also resets a portion of the UI and calls the
telemetry window prompt.
2021-06-01 17:22:04 -04:00
fearlessTobi
8aeb425669 configuration: Initial work to reset all settings
This commit does not compile.

Initial work to add and connect a Reset to Defaults button to the
configure_general tab.

Co-authored-by: german77 <juangerman-13@hotmail.com>
2021-06-01 17:09:33 -04:00
ameerj
859ba21f6d buffer_cache: Simplify uniform disabling logic 2021-06-01 13:26:58 -04:00
Morph
65b389da70 Merge pull request #6396 from lat9nq/mingw-sdl-fix
externals: Use defaults for building SDL2 on WIN32
2021-06-01 04:14:08 -04:00
bunnei
348ca07e0d Merge pull request #6318 from german77/dualJoycon
input_common: Add dual joycon support
2021-06-01 00:51:00 -07:00
bunnei
0a6f685ad0 Merge pull request #6367 from ReinUsesLisp/vma-host
vulkan_memory_allocator: Allow textures to be allocated in host memory
2021-05-31 23:35:11 -07:00
Lioncash
3aed797466 common_funcs: Move R_ macros to result.h
These macros all interact with the result code type, so they should
ideally be within this file as well, so all the common_funcs machinery
doesn't need to be pulled in just to use them.
2021-05-31 16:41:00 -04:00
lat9nq
185e405bc1 externals: Use defaults for building SDL2 on WIN32
Whatever those settings do breaks controller detection on Windows, at
least with the MinGW container. If-guard it against WIN32 and just let
SDL2 configure using its defaults, aside from static linking.
2021-05-31 15:26:25 -04:00
Ameer J
519ddfae04 Merge pull request #6394 from lat9nq/mingw-fix
externals: libusb: Use autotools for MinGW
2021-05-31 14:28:56 -04:00
lat9nq
1914a1d21c externals: libusb: Use autotools for MinGW
After updating to 1.0.24, MinGW fails to build libusb as a result of
numerous errors. So we build libusb their way and let them update the
nontrivial stuff.

This only applies to MinGW: the old path is still in use for Linux
toolchains as well as MSVC.

This will dynamically link libusb, since I hit build errors with the old
way we used to resolve the conflict with SDL2.
2021-05-31 13:57:06 -04:00
bunnei
f34176996e Merge pull request #6385 from degasus/save_memory_access
core/memory: Check our memory fallbacks for out-of-bound behavior.
2021-05-30 23:21:39 -07:00
german77
ac48e059bc settings: Disable controller preview if controller is not active 2021-05-30 10:57:20 -05:00
bunnei
95f45112e9 Merge pull request #6344 from german77/update-libusb
Update libusb to 1.0.24
2021-05-30 01:36:46 -07:00
bunnei
a5ebba7e36 Merge pull request #6377 from lioncash/point
common: Extract Point struct into common
2021-05-30 01:35:26 -07:00
bunnei
a6cfc73cb2 Merge pull request #6387 from lioncash/class-token
k_class_token: Use variable templates where applicable
2021-05-29 23:55:17 -07:00
bunnei
90c3dd300f Merge pull request #6386 from bunnei/shutdown-fix
video_core: gpu: WaitFence: Do not block threads during shutdown.
2021-05-29 23:52:52 -07:00
bunnei
f072f48806 ci: build-msvc: Remove CMake install step.
- This is breaking our build pipelines with Zydis (dynarmic dependency).
2021-05-29 22:49:02 -07:00
german77
a323bc5af8 input_common: Analog button, use time based position instead of frequent updates 2021-05-30 00:13:51 -05:00
Mai M
2069430baa Merge pull request #6374 from Morph1984/swkbd-textcheck-encoding
applets/swkbd: Only read the text check message on Failure/Confirm
2021-05-29 23:34:40 -04:00
Mai M
fc708b396b Merge pull request #6364 from german77/stub-lp2p
ldn: Add and stub lp2p:sys lp2p:app INetworkServiceMonitor INetworkService

Mario Kart Live: Home Circuit needs lp2p:sys lp2p:app INetworkServiceMonitor INetworkService to be able to progress.

Note: The game still fails to boot from unimplemented LDN and BSD services.
2021-05-29 23:33:57 -04:00
bunnei
e41c8b6780 Merge pull request #6379 from degasus/update_dynarmic
externals: Update dynarmic.
2021-05-29 03:15:13 -07:00
Lioncash
646622ccd4 k_class_token: Use variable templates where applicable
Same behavior, less code.
2021-05-29 05:25:34 -04:00
bunnei
c7c4ef9d43 Merge pull request #6384 from lioncash/virtual
kernel: Add missing override specifiers
2021-05-29 02:11:40 -07:00
bunnei
cdabc9064b Merge pull request #6382 from lioncash/null
k_thread: Move dereference after null check in Initialize()
2021-05-29 01:17:28 -07:00
bunnei
8592f8a2b4 video_core: gpu: WaitFence: Do not block threads during shutdown.
- Fixes a hang on shutdown when NVFlinger thread is waiting on a syncpoint that will never occur.
- Commonly observed when stopping emulation in Super Mario Odyssey.
2021-05-29 01:06:04 -07:00
Markus Wick
42a7c5d017 core/memory: Check our memory fallbacks for out-of-bound behavior.
This makes it by far harder to crash yuzu.

Also implement the 48bit masking of AARCH64 while touching this code.
2021-05-29 09:28:26 +02:00
bunnei
5388e6db84 Merge pull request #6373 from bunnei/use-slabheap-tls
hle: kernel: KSlabHeap: Allow host or guest allocations.
2021-05-29 00:17:24 -07:00
Mai M
38dbe57797 Merge pull request #6383 from degasus/fix_gcc_warnings
Fix two GCC 11 warnings: Unneeded copies.
2021-05-29 03:08:11 -04:00
Markus Wick
ddb186e61d core/arm_interface: Improve the performance of memory fallbacks.
We just create one memory subsystem. This is a constant all the time.
So there is no need to call the non-inlined parent.Memory() helper on every callback.
2021-05-29 09:02:19 +02:00
Lioncash
7b2917b4e1 kernel: Add missing override specifiers
Over the course of the kernel refactoring a tiny bit of missing
overrides slipped through review, so we can add these.

While we're at it, we can remove redundant virtual keywords where
applicable as well.
2021-05-29 02:58:32 -04:00
Markus Wick
5a8cd1b118 Fix two GCC 11 warnings: Unneeded copies.
std::move created an unneeded copy.
iterating without reference also created copies.
2021-05-29 08:57:44 +02:00
Markus Wick
d2d7a5060f externals: Update dynarmic.
The new version supports fastmem on a64.
2021-05-29 08:53:01 +02:00
Lioncash
16ff0161b3 k_thread: Move dereference after null check in Initialize()
Prevents a -Wnonnull warning on GCC.
2021-05-29 00:31:38 -04:00
bunnei
420b1f89d3 Merge pull request #6371 from degasus/drop_ExceptionalExit
core/arm_interface: Call SVC after end of dynarmic block.
2021-05-28 17:45:05 -07:00
bunnei
ee099b2697 hle: kernel: KSlabHeap: Allow host or guest allocations.
- Use host allocations for kernel memory, as this is not properly emulated yet.
- Use guest allocations for TLS, as this needs to be backed by DeviceMemory.
2021-05-28 17:42:41 -07:00
bunnei
945effcc75 Merge pull request #6356 from ogniK5377/ApplyNpadSystemCommonPolicy
hid: ApplyNpadSystemCommonPolicy
2021-05-28 10:05:20 -07:00
Lioncash
5554de3933 touchscreen: Make use of common point struct 2021-05-28 08:15:22 -04:00
Lioncash
8171ad65cd common: Extract point into a common struct
This is generic enough that it can be moved into the Common class for
reuse.
2021-05-28 08:12:49 -04:00
Morph
d25648cb6c Merge pull request #6375 from lioncash/iofs
common/fs/file: Default initialize IOFile members
2021-05-28 02:43:33 -04:00
Lioncash
210c2c9a56 common/fs/file: Explicitly delete copy constructors
Relocates them to the same place the move equivalents are at for
consistent viewing.
2021-05-28 01:49:40 -04:00
Lioncash
6806a893bd common/fs/file: Devirtualize destructor
IOFile is a final class, so there's no need for a virtual destructor.
2021-05-28 01:47:52 -04:00
Lioncash
019bc9f6b2 common/fs/file: Default initialize IOFile members
Prevents a potential uninitialized read vector in the move constructor.
2021-05-28 01:46:30 -04:00
Morph
c68255f70f applets/swkbd: Make use of std::move where applicable
Avoids redundant string copies
2021-05-27 23:45:56 -04:00
Morph
247cd92216 applets/swkbd: Only read the text check message on Failure/Confirm
Applications may leave this region of memory uninitialized when the text check result is not either Failure or Confirm.
Attempting to read uninitialized memory may cause an exception within the UTF16 to UTF8 string converter.
Fix this by only reading the text check message on Failure or Confirm.
2021-05-27 23:45:56 -04:00
bunnei
3289abe1cc Merge pull request #6372 from bunnei/raster-cache-fix
video_core: rasterizer_cache: Use u16 for cached page count.
2021-05-27 15:48:21 -07:00
bunnei
4b95b0df97 video_core: rasterizer_cache: Use u16 for cached page count.
- Greatly reduces the risk of overflow, at the cost of doubling the size of this array.
2021-05-27 14:47:24 -07:00
Markus Wick
3d2e80daed core/arm_interface: Call SVC after end of dynarmic block.
So we can modify all of dynarmic states within SVC without ExceptionalExit.

Especially as the ExceptionalExit hack is dropped on upstream dynarmic.
2021-05-27 23:23:23 +02:00
Mai M
d825dff259 Merge pull request #6369 from lat9nq/cmake-fix-dep-opt
cmake: Fix usage of CMAKE_DEPENDENT_OPTION
2021-05-27 17:14:18 -04:00
Ameer J
9110cfdefb Merge pull request #6346 from lat9nq/apply-config-pgc
yuzu qt: Add an Apply button to configuration dialogs
2021-05-27 13:07:53 -04:00
lat9nq
f94cb0bdb2 cmake: Fix usage of CMAKE_DEPENDENT_OPTION
CMAKE_DEPENDENT_OPTION takes a value argument, but as a macro function
it will read a variable name as the name and not the value. For
YUZU_USE_BUNDLED_QT, ensure that we are reading the value of MSVC. For
YUZU_ALLOW_SYSTEM_SDL2, CMAKE_DEPENDENT_OPTION is redundant here anyway
as we don't use that path on any toolchain by default.
2021-05-27 12:51:52 -04:00
ReinUsesLisp
19454e71d8 vulkan_memory_allocator: Allow textures to be allocated in host memory
Allow Vulkan's allocator to use host memory when there's no more device
local memory. This delays OOM, but it will eventually still happen.
2021-05-27 05:50:48 -03:00
Mai M
116989be8e Merge pull request #6366 from lat9nq/bundled-qt-linux
cmake: Download Qt binaries on Linux if needed
2021-05-26 18:19:02 -04:00
Mai M
2c791b38e6 Merge pull request #6365 from degasus/drop_ChangeProcessorID
core/arm: Drop ChangeProcessorID.
2021-05-26 18:16:54 -04:00
lat9nq
52cc25ccbf cmake: Download Qt binaries on Linux if needed
If the local version of Qt is older than the minimum version required by
yuzu, download a pre-built binary package from yuzu-emu/ext-linux-bin
and build yuzu with it, instead.

This also requires linking yuzu to the correct libraries after building
it, and copying over the required binaries when building yuzu.

This sets the Qt requirement to 5.12, which is intentionally behind the
versions used by our toolchains since they are not all updated yet to
5.15.
2021-05-26 15:29:45 -04:00
Markus Wick
993dbe49fc core/arm: Drop ChangeProcessorID.
This code was used to switch the CPU ID on thread switches.
However since "hle: kernel: multicore: Replace n-JITs impl. with 4 JITs.", the CPU ID is not a constant.
This has been dead code since this rewrite, and dropped in dynarmic as well. So there is no need to keep it.
2021-05-26 19:48:24 +02:00
german77
65e20f424a ldn: Add and stub lp2p:sys lp2p:app INetworkServiceMonitor INetworkService 2021-05-26 10:06:00 -05:00
Morph
02169406be Merge pull request #6331 from lioncash/gesture
hid/gesture: Simplify point related code
2021-05-26 09:19:00 -04:00
lat9nq
bc38d4c81b yuzu qt: Restore const qualifiers
This addresses review comments.

Co-authored-by: LC <mathew1800@gmail.com>
2021-05-25 21:39:57 -04:00
bunnei
3394f24728 Merge pull request #6339 from Morph1984/swkbd-queuedconnection
applets/swkbd: Make use of QueuedConnection in returnPressed signal
2021-05-25 18:23:08 -07:00
lat9nq
cfb9bcbe42 yuzu qt: Handle per-game configs for title id 0
Currently with programs that have a 0 title id, yuzu loads the custom
configuration 0000000000000000.ini for per-game configs. This is not
ideal since many homebrews share this id. Instead for these programs, we
load a config that is simply the file name and `.ini` appended to it.
2021-05-25 20:10:41 -04:00
Morph
065867e2c2 common: fs: Rework the Common Filesystem interface to make use of std::filesystem (#6270)
* common: fs: fs_types: Create filesystem types

Contains various filesystem types used by the Common::FS library

* common: fs: fs_util: Add std::string to std::u8string conversion utility

* common: fs: path_util: Add utlity functions for paths

Contains various utility functions for getting or manipulating filesystem paths used by the Common::FS library

* common: fs: file: Rewrite the IOFile implementation

* common: fs: Reimplement Common::FS library using std::filesystem

* common: fs: fs_paths: Add fs_paths to replace common_paths

* common: fs: path_util: Add the rest of the path functions

* common: Remove the previous Common::FS implementation

* general: Remove unused fs includes

* string_util: Remove unused function and include

* nvidia_flags: Migrate to the new Common::FS library

* settings: Migrate to the new Common::FS library

* logging: backend: Migrate to the new Common::FS library

* core: Migrate to the new Common::FS library

* perf_stats: Migrate to the new Common::FS library

* reporter: Migrate to the new Common::FS library

* telemetry_session: Migrate to the new Common::FS library

* key_manager: Migrate to the new Common::FS library

* bis_factory: Migrate to the new Common::FS library

* registered_cache: Migrate to the new Common::FS library

* xts_archive: Migrate to the new Common::FS library

* service: acc: Migrate to the new Common::FS library

* applets/profile: Migrate to the new Common::FS library

* applets/web: Migrate to the new Common::FS library

* service: filesystem: Migrate to the new Common::FS library

* loader: Migrate to the new Common::FS library

* gl_shader_disk_cache: Migrate to the new Common::FS library

* nsight_aftermath_tracker: Migrate to the new Common::FS library

* vulkan_library: Migrate to the new Common::FS library

* configure_debug: Migrate to the new Common::FS library

* game_list_worker: Migrate to the new Common::FS library

* config: Migrate to the new Common::FS library

* configure_filesystem: Migrate to the new Common::FS library

* configure_per_game_addons: Migrate to the new Common::FS library

* configure_profile_manager: Migrate to the new Common::FS library

* configure_ui: Migrate to the new Common::FS library

* input_profiles: Migrate to the new Common::FS library

* yuzu_cmd: config: Migrate to the new Common::FS library

* yuzu_cmd: Migrate to the new Common::FS library

* vfs_real: Migrate to the new Common::FS library

* vfs: Migrate to the new Common::FS library

* vfs_libzip: Migrate to the new Common::FS library

* service: bcat: Migrate to the new Common::FS library

* yuzu: main: Migrate to the new Common::FS library

* vfs_real: Delete the contents of an existing file in CreateFile

Current usages of CreateFile expect to delete the contents of an existing file, retain this behavior for now.

* input_profiles: Don't iterate the input profile dir if it does not exist

Silences an error produced in the log if the directory does not exist.

* game_list_worker: Skip parsing file if the returned VfsFile is nullptr

Prevents crashes in GetLoader when the virtual file is nullptr

* common: fs: Validate paths for path length

* service: filesystem: Open the mod load directory as read only
2021-05-25 19:32:56 -04:00
bunnei
08a5cf0b5b Merge pull request #6349 from german77/suppress_config_warning
settings: Suppress duplicate label name warning
2021-05-25 15:51:51 -07:00
bunnei
8094743f63 Merge pull request #6348 from lioncash/zstd
CMakeLists: Update zstd to 1.5.0
2021-05-25 14:41:40 -07:00
bunnei
4bf53eb935 Merge pull request #6353 from german77/handheld_docked
settings: Forbid docked mode on handheld
2021-05-25 13:02:20 -07:00
Mai M
b6b73d9a5a Merge pull request #6358 from Morph1984/k_map_region
kernel: process_capability: Add MapRegion capability
2021-05-25 03:26:22 -04:00
lat9nq
c1bad4357a yuzu qt: Add an Apply button to configuration dialogs
Most of the code already exists to do this, but the Apply button itself
was never added. This adds a button and boolean that tells yuzu to save
the configuration after applying settings, even if close/Cancel is
pressed on the dialog. Changes after applying will not be saved when
Cancel is pressed, though.
2021-05-25 02:25:39 -04:00
Morph
ec28d3c439 kernel: process_capability: Add MapRegion capability
- Used by nx-hbloader
2021-05-25 01:44:46 -04:00
bunnei
d84a93c987 Merge pull request #6357 from lioncash/compression
common/compression: Make use of std::span
2021-05-24 18:50:26 -07:00
bunnei
aee3b57c44 Merge pull request #6312 from german77/analogMapping
input_common: Rewrite sdl analog mapping and fix controller disconnection crash
2021-05-24 12:02:37 -07:00
Lioncash
49bfd0c461 zstd_compression: Make use of std::span
Allows for the incoming data stream to be non-allocating.
2021-05-24 15:01:04 -04:00
Lioncash
00213377b1 lz4_compression: Make use of std::span
Allows making the incoming data stream non-allocating.
2021-05-24 15:00:59 -04:00
Chloe Marcec
db7abfecdd hid: ApplyNpadSystemCommonPolicy
We already do this specifically for homebrew, so we can keep it stubbed out for the time being
2021-05-24 15:07:47 +10:00
german77
7f445a59fa settings: Forbid docked mode on handheld 2021-05-23 20:34:46 -05:00
bunnei
3ead4a3494 Merge pull request #6347 from bunnei/ipc-improvements-next-2
Various improvements to IPC and session management (Part 2)
2021-05-23 16:28:40 -07:00
german77
ea4b7226a6 input_common: Add dual joycon support 2021-05-22 18:40:53 -05:00
german77
bb22d6d8f7 settings: Suppress duplicate label name warning 2021-05-22 18:32:35 -05:00
Morph
ecacb002be applets/swkbd: Make use of QueuedConnection in returnPressed signal
Some users have reported rare crashes when pressing the Enter key on the keyboard to confirm input in the normal software keyboard, particularly in Super Smash Bros. Ultimate while entering the name of a ruleset or controller layout.

It is suspected that the QLineEdit::returnPressed signal is causing a race condition as confirming input through other means does not produce the crash. Since Qt::QueuedConnection posts an event to the event queue of the callee's thread instead of executing it directly on the caller's thread, this eliminates any potential race conditions from occurring in this scenario.
2021-05-22 03:28:54 -04:00
Lioncash
922d5187c4 CMakeLists: Update zstd to 1.5.0
zstd 1.5.0 brings numerous performance improvements to the library, as
can be seen here: https://github.com/facebook/zstd/releases/tag/v1.5.0
2021-05-21 13:24:11 -04:00
bunnei
5068279f23 Merge pull request #6248 from A-w-x/intelmesa
gl_device: Intel: Disable texture view formats workaround on mesa
2021-05-20 23:47:14 -07:00
bunnei
136e8e829f Merge pull request #6333 from Morph1984/swkbd-confirm-text
applets/swkbd: Send the correct text string on TextCheck::Confirm
2021-05-20 22:42:54 -07:00
bunnei
6418a42884 hle: kernel: service_thread: Take reference to KServerSession on service request. 2021-05-20 22:39:44 -07:00
bunnei
e33ffdc555 hle: kernel: k_port: Use AcceptSession to ensure SessionList state is correct.
- Fixes a use-after-free, work-around until we fixup session/port management.
2021-05-20 21:41:52 -07:00
bunnei
b4fc2e52a2 hle: kernel: Use host memory allocations for KSlabMemory.
- There are some issues with the current workaround, we will just use host memory until we have a complete kernel memory implementation.
2021-05-20 21:41:52 -07:00
bunnei
7331bb9d8d Revert "WORKAROUND: Do not use slab heap while we track down issues with resource management."
This reverts commit f2c26443f8.
2021-05-20 21:41:52 -07:00
bunnei
f4fe71c1c9 hle: kernel: hle_ipc: Simplify incoming/outgoing move/copy/domain objects. 2021-05-20 21:41:52 -07:00
bunnei
342170fcd3 common: tree: Avoid a crash on nullptr dereference. 2021-05-20 21:41:51 -07:00
bunnei
7361eac10f hle: kernel: Implement CloneCurrentObject and improve session management. 2021-05-20 21:41:49 -07:00
bunnei
c40e7593f5 Revert "WORKAROUND: temp. disable session resource limits while we work out issues"
This reverts commit fc086f93b2.
2021-05-20 21:40:30 -07:00
bunnei
ea4e4b05e4 Merge pull request #6320 from Morph1984/get-pid
hle_ipc: Add a getter for PID
2021-05-20 21:40:03 -07:00
bunnei
7626ca3343 Merge pull request #6321 from lat9nq/per-game-cpu
configuration: Add CPU tab to game properties and slight per-game settings rework
2021-05-20 20:10:56 -07:00
german77
3a6e2922a2 Update libusb to 1.0.24 2021-05-20 22:10:48 -05:00
lat9nq
5153d5387a configure_cpu: Simplify UpdateGroup
Co-authored-by: Ameer J <52414509+ameerj@users.noreply.github.com>
2021-05-20 01:11:56 -04:00
bunnei
b5d21cc1b1 Merge pull request #6297 from lioncash/common-conv
parent_of_member: Make sign conversion explicit in OffsetOfImpl()
2021-05-19 18:43:47 -07:00
bunnei
41b1f8d616 Merge pull request #6310 from german77/nanMotion
input_common: Sanitize motion data
2021-05-19 15:47:48 -07:00
lat9nq
12ef74456c configuration_shared: Drop unused function and template another
Drops an unused variant of ApplyPerGameSetting, and turns the QComboBox
variants of SetPerGameSetting into a template.

Co-authored-by: Ameer J <52414509+ameerj@users.noreply.github.com>
2021-05-19 16:00:48 -04:00
Morph
5396593b55 applets/swkbd: Send the correct text string on TextCheck::Confirm
Previously the text string for the inline software keyboard was being sent instead of the normal software keyboard, leading to empty text being sent all the time.
2021-05-19 00:26:32 -04:00
bunnei
7d86a6ff02 Merge pull request #6317 from ameerj/fps-fix
perf_stats: Rework FPS counter to be more accurate
2021-05-18 19:56:29 -07:00
bunnei
61f293e5c9 Merge pull request #6337 from Morph1984/transfer-mem-size
KTransferMemory: Return size instead of size * PageSize in GetSize()
2021-05-18 11:00:34 -07:00
Lioncash
44556dc21a hid/gesture: Factor out last gesture retrieval into its own function
Deduplicates a commonly repeated expression.
2021-05-18 03:59:44 -04:00
Lioncash
a9d8e24e47 hid/gesture: Ensure all ID arrays are initialized
Makes for deterministic initial state.
2021-05-18 03:39:21 -04:00
Lioncash
74f30c0223 hid/gesture: Make Point a template
We can now use this in a generic context to reuse it with the finger
position.
2021-05-18 03:39:18 -04:00
Lioncash
20699e90fa hid/gesture: Replace x,y members of GestureState with a Point
Simplifies assignments.
2021-05-18 03:32:42 -04:00
Lioncash
2f1ef3910b hid/gesture: Add default comparators to Point
Simplifies some comparisons.
2021-05-18 03:32:42 -04:00
Lioncash
60831eabd9 hid/gesture: Rename Points to Point
This only represents a single point
2021-05-18 03:32:38 -04:00
lat9nq
339dc4f806 general: Demote custom_rtc to regular setting 2021-05-17 15:54:30 -04:00
Morph
049769a0c9 hle_ipc: unsigned -> u32
This is more concise and consistent with the rest of the codebase.
2021-05-16 04:11:00 -04:00
Morph
81a5ecdb18 hle_ipc: Add a getter for PID 2021-05-16 04:10:42 -04:00
lat9nq
ab2677f0a1 configuration: Add CPU tab to game properties
Allows setting CPU accuracy to Accurate or Unsafe per-game, as well as
the accuracy options for Unsafe. Debug is not allowed here as a per-game
CPU accuracy.
2021-05-16 01:31:42 -04:00
lat9nq
4aac1ae4b1 configuration: Simplify applying per-game settings
Originally, every time we add a per-game setting, we'd have to guard for
it when setting it on the global config, and use a specific function to
do it for the per-game config.

This moves the global check into the ApplyPerGameSetting function so
that we can use it for changing both the global and per-game states.
Less work for the programmer.
2021-05-15 22:59:38 -04:00
lat9nq
59236b7d0f configuration_shared: Add some comments
Monke brain can't remember what all of these does a year later.
2021-05-15 22:07:20 -04:00
lat9nq
e169fdad4f general: Make CPU accuracy and related a Settings::Setting
Required to make CPU accuracy and unsafe settings available to use as a
per-game setting.
2021-05-15 20:46:48 -04:00
ameerj
5bef54618a perf_stats: Rework FPS counter to be more accurate
The FPS counter was based on metrics in the nvdisp swapbuffers call. This metric would be accurate if the gpu thread/renderer were synchronous with the nvdisp service, but that's no longer the case.

This commit moves the frame counting responsibility onto the concrete renderers after their frame draw calls. Resulting in more meaningful metrics.
The displayed FPS is now made up of the average framerate between the previous and most recent update, in order to avoid distracting FPS counter updates when framerate is oscillating between close values.

The status bar update frequency was also changed from 2 seconds to 500ms.
2021-05-15 20:34:20 -04:00
german77
edd498f6e0 input_common: Fix crash when controller disconnects 2021-05-15 08:27:19 -05:00
german77
85eeae7aad input_common: Rewrite sdl analog mapping 2021-05-14 21:17:08 -05:00
german77
fd7c273fab input_common: Sanitize motion data 2021-05-13 13:41:32 -05:00
Lioncash
0aff3ba2ff parent_of_member: Make sign conversion explicit in OffsetOfImpl()
Previously these conversions were implicit and causing quite a few
warnings on clang.
2021-05-10 08:07:33 -04:00
A-w-x
6a2084a204 gl_device: Intel: Disable texture view formats workaround on mesa 2021-04-26 18:14:10 +02:00
311 changed files with 7619 additions and 4698 deletions

View File

@@ -9,11 +9,5 @@ cp "${REV_NAME}-source.tar.xz" "$DIR_NAME"
tar $COMPRESSION_FLAGS "$ARCHIVE_NAME" "$DIR_NAME"
mv "$DIR_NAME" $RELEASE_NAME
mv "${REV_NAME}-source.tar.xz" $RELEASE_NAME
7z a "$REV_NAME.7z" $RELEASE_NAME
# move the compiled archive into the artifacts directory to be uploaded by travis releases
mv "$ARCHIVE_NAME" "${ARTIFACTS_DIR}/"
mv "$REV_NAME.7z" "${ARTIFACTS_DIR}/"

View File

@@ -47,3 +47,6 @@ python3 .ci/scripts/windows/scan_dll.py package/imageformats/*.dll "package/"
EXTERNALS_PATH="$(pwd)/build/externals"
FFMPEG_DLL_PATH="$(find ${EXTERNALS_PATH} -maxdepth 1 -type d | grep ffmpeg)/bin"
find ${FFMPEG_DLL_PATH} -type f -regex ".*\.dll" -exec cp -v {} package/ ';'
# copy libraries from yuzu.exe path
find "$(pwd)/build/bin/" -type f -regex ".*\.dll" -exec cp -v {} package/ ';'

View File

@@ -3,8 +3,8 @@
. .ci/scripts/common/pre-upload.sh
REV_NAME="yuzu-windows-mingw-${GITDATE}-${GITREV}"
ARCHIVE_NAME="${REV_NAME}.tar.gz"
COMPRESSION_FLAGS="-czvf"
ARCHIVE_NAME="${REV_NAME}.tar.xz"
COMPRESSION_FLAGS="-cJvf"
if [ "${RELEASE_NAME}" = "mainline" ]; then
DIR_NAME="${REV_NAME}"

View File

@@ -8,7 +8,7 @@ steps:
displayName: 'Install vulkan-sdk'
- script: python -m pip install --upgrade pip conan
displayName: 'Install conan'
- script: refreshenv && mkdir build && cd build && cmake -G "Visual Studio 16 2019" -A x64 -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_BUNDLED_SDL2=1 -DYUZU_USE_QT_WEB_ENGINE=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${COMPAT} -DUSE_DISCORD_PRESENCE=ON -DENABLE_QT_TRANSLATION=ON -DDISPLAY_VERSION=${{ parameters['version'] }} .. && cmake --install . --config Release && cd ..
- script: refreshenv && mkdir build && cd build && cmake -G "Visual Studio 16 2019" -A x64 -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_BUNDLED_SDL2=1 -DYUZU_USE_QT_WEB_ENGINE=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${COMPAT} -DUSE_DISCORD_PRESENCE=ON -DENABLE_QT_TRANSLATION=ON -DDISPLAY_VERSION=${{ parameters['version'] }} -DCMAKE_BUILD_TYPE=Release .. && cd ..
displayName: 'Configure CMake'
- task: MSBuild@1
displayName: 'Build'

View File

@@ -13,16 +13,18 @@ project(yuzu)
option(ENABLE_SDL2 "Enable the SDL2 frontend" ON)
CMAKE_DEPENDENT_OPTION(YUZU_USE_BUNDLED_SDL2 "Download bundled SDL2 binaries" ON "ENABLE_SDL2;MSVC" OFF)
# On Linux system SDL2 is likely to be lacking HIDAPI support which have drawbacks but is needed for SDL motion
CMAKE_DEPENDENT_OPTION(YUZU_ALLOW_SYSTEM_SDL2 "Try using system SDL2 before fallling back to one from externals" NOT UNIX "ENABLE_SDL2" OFF)
option(YUZU_ALLOW_SYSTEM_SDL2 "Try using system SDL2 before fallling back to one from externals" OFF)
option(ENABLE_QT "Enable the Qt frontend" ON)
option(ENABLE_QT_TRANSLATION "Enable translations for the Qt frontend" OFF)
CMAKE_DEPENDENT_OPTION(YUZU_USE_BUNDLED_QT "Download bundled Qt binaries" ON "ENABLE_QT;MSVC" OFF)
CMAKE_DEPENDENT_OPTION(YUZU_USE_BUNDLED_QT "Download bundled Qt binaries" "${MSVC}" "ENABLE_QT" OFF)
option(ENABLE_WEB_SERVICE "Enable web services (telemetry, etc.)" ON)
option(YUZU_USE_BUNDLED_BOOST "Download bundled Boost" OFF)
option(YUZU_USE_BUNDLED_LIBUSB "Compile bundled libusb" OFF)
CMAKE_DEPENDENT_OPTION(YUZU_USE_BUNDLED_FFMPEG "Download/Build bundled FFmpeg" ON "WIN32" OFF)
option(YUZU_USE_QT_WEB_ENGINE "Use QtWebEngine for web applet implementation" OFF)
@@ -174,7 +176,7 @@ macro(yuzu_find_packages)
"lz4 1.8 lz4/1.9.2"
"nlohmann_json 3.8 nlohmann_json/3.8.0"
"ZLIB 1.2 zlib/1.2.11"
"zstd 1.4 zstd/1.4.8"
"zstd 1.5 zstd/1.5.0"
# can't use opus until AVX check is fixed: https://github.com/yuzu-emu/yuzu/pull/4068
#"opus 1.3 opus/1.3.1"
)
@@ -240,6 +242,7 @@ yuzu_find_packages()
# Qt5 requires that we find components, so it doesn't fit our pretty little find package function
if(ENABLE_QT)
set(QT_VERSION 5.12)
# We want to load the generated conan qt config so that we get the QT_ROOT var so that we can use the official
# Qt5Config inside the root folder instead of the conan generated one.
if(EXISTS ${CMAKE_BINARY_DIR}/qtConfig.cmake)
@@ -247,22 +250,40 @@ if(ENABLE_QT)
list(APPEND CMAKE_MODULE_PATH "${CONAN_QT_ROOT_RELEASE}")
list(APPEND CMAKE_PREFIX_PATH "${CONAN_QT_ROOT_RELEASE}")
endif()
# Check for system Qt on Linux, fallback to bundled Qt
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
if (NOT YUZU_USE_BUNDLED_QT)
find_package(Qt5 ${QT_VERSION} COMPONENTS Widgets QUIET)
if (NOT Qt5_FOUND)
set(YUZU_USE_BUNDLED_QT ON CACHE BOOL "Download bundled Qt" FORCE)
endif()
endif()
if (YUZU_USE_BUNDLED_QT)
# Binary package currently does not support Qt webengine, so make sure it's disabled
set(YUZU_USE_QT_WEB_ENGINE OFF CACHE BOOL "Use Qt Webengine" FORCE)
endif()
endif()
# Workaround for an issue where conan tries to build Qt from scratch instead of download prebuilt binaries
set(QT_PREFIX_HINT)
if(YUZU_USE_BUNDLED_QT)
if ((MSVC_VERSION GREATER_EQUAL 1910 AND MSVC_VERSION LESS 1930) AND ARCHITECTURE_x86_64)
set(QT_VER qt-5.12.8-msvc2017_64)
set(QT_BUILD qt-5.12.8-msvc2017_64)
elseif ((${CMAKE_SYSTEM_NAME} STREQUAL "Linux") AND NOT MINGW AND ARCHITECTURE_x86_64)
set(QT_BUILD qt5_5_15_2)
else()
message(FATAL_ERROR "No bundled Qt binaries for your toolchain. Disable YUZU_USE_BUNDLED_QT and provide your own.")
endif()
if (DEFINED QT_VER)
download_bundled_external("qt/" ${QT_VER} QT_PREFIX)
if (DEFINED QT_BUILD)
download_bundled_external("qt/" ${QT_BUILD} QT_PREFIX)
endif()
set(QT_PREFIX_HINT HINTS "${QT_PREFIX}")
endif()
find_package(Qt5 5.9 COMPONENTS Widgets ${QT_PREFIX_HINT})
find_package(Qt5 ${QT_VERSION} REQUIRED COMPONENTS Widgets ${QT_PREFIX_HINT} NO_CMAKE_SYSTEM_PATH)
if (YUZU_USE_QT_WEB_ENGINE)
find_package(Qt5 COMPONENTS WebEngineCore WebEngineWidgets)
endif()
@@ -271,6 +292,7 @@ if(ENABLE_QT)
find_package(Qt5 REQUIRED COMPONENTS LinguistTools ${QT_PREFIX_HINT})
endif()
endif()
# find SDL2 exports a bunch of variables that are needed, so its easier to do this outside of the yuzu_find_package
if (ENABLE_SDL2)
if (YUZU_USE_BUNDLED_SDL2)
@@ -379,7 +401,7 @@ if (CONAN_REQUIRED_LIBS)
if(ENABLE_QT)
list(APPEND CMAKE_MODULE_PATH "${CONAN_QT_ROOT_RELEASE}")
list(APPEND CMAKE_PREFIX_PATH "${CONAN_QT_ROOT_RELEASE}")
find_package(Qt5 5.9 REQUIRED COMPONENTS Widgets)
find_package(Qt5 5.12 REQUIRED COMPONENTS Widgets)
if (YUZU_USE_QT_WEB_ENGINE)
find_package(Qt5 REQUIRED COMPONENTS WebEngineCore WebEngineWidgets)
endif()
@@ -400,14 +422,22 @@ elseif (TARGET Boost::boost)
endif()
# Ensure libusb is properly configured (based on dolphin libusb include)
if(NOT APPLE)
if(NOT APPLE AND NOT YUZU_USE_BUNDLED_LIBUSB)
include(FindPkgConfig)
find_package(LibUSB)
endif()
if (NOT LIBUSB_FOUND)
add_subdirectory(externals/libusb)
set(LIBUSB_INCLUDE_DIR "")
set(LIBUSB_LIBRARIES usb)
if (PKG_CONFIG_FOUND)
pkg_check_modules(LIBUSB QUIET libusb-1.0>=1.0.24)
else()
find_package(LibUSB)
endif()
if (LIBUSB_FOUND)
add_library(usb INTERFACE)
target_include_directories(usb INTERFACE "${LIBUSB_INCLUDE_DIRS}")
target_link_libraries(usb INTERFACE "${LIBUSB_LIBRARIES}")
else()
message(WARNING "libusb not found, falling back to externals")
set(YUZU_USE_BUNDLED_LIBUSB ON)
endif()
endif()
# List of all FFmpeg components required

View File

@@ -1,52 +1,111 @@
function(copy_yuzu_Qt5_deps target_dir)
include(WindowsCopyFiles)
set(DLL_DEST "${CMAKE_BINARY_DIR}/bin/$<CONFIG>/")
set(Qt5_DLL_DIR "${Qt5_DIR}/../../../bin")
if (MSVC)
set(DLL_DEST "${CMAKE_BINARY_DIR}/bin/$<CONFIG>/")
set(Qt5_DLL_DIR "${Qt5_DIR}/../../../bin")
else()
set(DLL_DEST "${CMAKE_BINARY_DIR}/bin/")
set(Qt5_DLL_DIR "${Qt5_DIR}/../../../lib/")
endif()
set(Qt5_PLATFORMS_DIR "${Qt5_DIR}/../../../plugins/platforms/")
set(Qt5_PLATFORMTHEMES_DIR "${Qt5_DIR}/../../../plugins/platformthemes/")
set(Qt5_PLATFORMINPUTCONTEXTS_DIR "${Qt5_DIR}/../../../plugins/platforminputcontexts/")
set(Qt5_XCBGLINTEGRATIONS_DIR "${Qt5_DIR}/../../../plugins/xcbglintegrations/")
set(Qt5_STYLES_DIR "${Qt5_DIR}/../../../plugins/styles/")
set(Qt5_IMAGEFORMATS_DIR "${Qt5_DIR}/../../../plugins/imageformats/")
set(Qt5_RESOURCES_DIR "${Qt5_DIR}/../../../resources/")
set(PLATFORMS ${DLL_DEST}plugins/platforms/)
set(STYLES ${DLL_DEST}plugins/styles/)
set(IMAGEFORMATS ${DLL_DEST}plugins/imageformats/)
windows_copy_files(${target_dir} ${Qt5_DLL_DIR} ${DLL_DEST}
icudt*.dll
icuin*.dll
icuuc*.dll
Qt5Core$<$<CONFIG:Debug>:d>.*
Qt5Gui$<$<CONFIG:Debug>:d>.*
Qt5Widgets$<$<CONFIG:Debug>:d>.*
)
if (YUZU_USE_QT_WEB_ENGINE)
windows_copy_files(${target_dir} ${Qt5_DLL_DIR} ${DLL_DEST}
Qt5Network$<$<CONFIG:Debug>:d>.*
Qt5Positioning$<$<CONFIG:Debug>:d>.*
Qt5PrintSupport$<$<CONFIG:Debug>:d>.*
Qt5Qml$<$<CONFIG:Debug>:d>.*
Qt5Quick$<$<CONFIG:Debug>:d>.*
Qt5QuickWidgets$<$<CONFIG:Debug>:d>.*
Qt5WebChannel$<$<CONFIG:Debug>:d>.*
Qt5WebEngine$<$<CONFIG:Debug>:d>.*
Qt5WebEngineCore$<$<CONFIG:Debug>:d>.*
Qt5WebEngineWidgets$<$<CONFIG:Debug>:d>.*
QtWebEngineProcess$<$<CONFIG:Debug>:d>.*
if (MSVC)
windows_copy_files(${target_dir} ${Qt5_DLL_DIR} ${DLL_DEST}
icudt*.dll
icuin*.dll
icuuc*.dll
Qt5Core$<$<CONFIG:Debug>:d>.*
Qt5Gui$<$<CONFIG:Debug>:d>.*
Qt5Widgets$<$<CONFIG:Debug>:d>.*
)
windows_copy_files(${target_dir} ${Qt5_RESOURCES_DIR} ${DLL_DEST}
qtwebengine_resources.pak
qtwebengine_devtools_resources.pak
qtwebengine_resources_100p.pak
qtwebengine_resources_200p.pak
icudtl.dat
)
endif ()
windows_copy_files(yuzu ${Qt5_PLATFORMS_DIR} ${PLATFORMS} qwindows$<$<CONFIG:Debug>:d>.*)
windows_copy_files(yuzu ${Qt5_STYLES_DIR} ${STYLES} qwindowsvistastyle$<$<CONFIG:Debug>:d>.*)
windows_copy_files(yuzu ${Qt5_IMAGEFORMATS_DIR} ${IMAGEFORMATS}
qjpeg$<$<CONFIG:Debug>:d>.*
qgif$<$<CONFIG:Debug>:d>.*
)
if (YUZU_USE_QT_WEB_ENGINE)
windows_copy_files(${target_dir} ${Qt5_DLL_DIR} ${DLL_DEST}
Qt5Network$<$<CONFIG:Debug>:d>.*
Qt5Positioning$<$<CONFIG:Debug>:d>.*
Qt5PrintSupport$<$<CONFIG:Debug>:d>.*
Qt5Qml$<$<CONFIG:Debug>:d>.*
Qt5Quick$<$<CONFIG:Debug>:d>.*
Qt5QuickWidgets$<$<CONFIG:Debug>:d>.*
Qt5WebChannel$<$<CONFIG:Debug>:d>.*
Qt5WebEngine$<$<CONFIG:Debug>:d>.*
Qt5WebEngineCore$<$<CONFIG:Debug>:d>.*
Qt5WebEngineWidgets$<$<CONFIG:Debug>:d>.*
QtWebEngineProcess$<$<CONFIG:Debug>:d>.*
)
windows_copy_files(${target_dir} ${Qt5_RESOURCES_DIR} ${DLL_DEST}
qtwebengine_resources.pak
qtwebengine_devtools_resources.pak
qtwebengine_resources_100p.pak
qtwebengine_resources_200p.pak
icudtl.dat
)
endif ()
windows_copy_files(yuzu ${Qt5_PLATFORMS_DIR} ${PLATFORMS} qwindows$<$<CONFIG:Debug>:d>.*)
windows_copy_files(yuzu ${Qt5_STYLES_DIR} ${STYLES} qwindowsvistastyle$<$<CONFIG:Debug>:d>.*)
windows_copy_files(yuzu ${Qt5_IMAGEFORMATS_DIR} ${IMAGEFORMATS}
qjpeg$<$<CONFIG:Debug>:d>.*
qgif$<$<CONFIG:Debug>:d>.*
)
else()
set(Qt5_DLLS
"${Qt5_DLL_DIR}libQt5Core.so.5"
"${Qt5_DLL_DIR}libQt5DBus.so.5"
"${Qt5_DLL_DIR}libQt5Gui.so.5"
"${Qt5_DLL_DIR}libQt5Widgets.so.5"
"${Qt5_DLL_DIR}libQt5XcbQpa.so.5"
"${Qt5_DLL_DIR}libicudata.so.60"
"${Qt5_DLL_DIR}libicui18n.so.60"
"${Qt5_DLL_DIR}libicuuc.so.60"
)
set(Qt5_IMAGEFORMAT_DLLS
"${Qt5_IMAGEFORMATS_DIR}libqjpeg.so"
"${Qt5_IMAGEFORMATS_DIR}libqgif.so"
"${Qt5_IMAGEFORMATS_DIR}libqico.so"
)
set(Qt5_PLATFORMTHEME_DLLS
"${Qt5_PLATFORMTHEMES_DIR}libqgtk3.so"
"${Qt5_PLATFORMTHEMES_DIR}libqxdgdesktopportal.so"
)
set(Qt5_PLATFORM_DLLS
"${Qt5_PLATFORMS_DIR}libqxcb.so"
)
set(Qt5_PLATFORMINPUTCONTEXT_DLLS
"${Qt5_PLATFORMINPUTCONTEXTS_DIR}libcomposeplatforminputcontextplugin.so"
"${Qt5_PLATFORMINPUTCONTEXTS_DIR}libibusplatforminputcontextplugin.so"
)
set(Qt5_XCBGLINTEGRATION_DLLS
"${Qt5_XCBGLINTEGRATIONS_DIR}libqxcb-glx-integration.so"
)
foreach(LIB ${Qt5_DLLS})
file(COPY ${LIB} DESTINATION "${DLL_DEST}/lib" FOLLOW_SYMLINK_CHAIN)
endforeach()
foreach(LIB ${Qt5_IMAGEFORMAT_DLLS})
file(COPY ${LIB} DESTINATION "${DLL_DEST}plugins/imageformats/" FOLLOW_SYMLINK_CHAIN)
endforeach()
foreach(LIB ${Qt5_PLATFORMTHEME_DLLS})
file(COPY ${LIB} DESTINATION "${DLL_DEST}plugins/platformthemes/" FOLLOW_SYMLINK_CHAIN)
endforeach()
foreach(LIB ${Qt5_PLATFORM_DLLS})
file(COPY ${LIB} DESTINATION "${DLL_DEST}plugins/platforms/" FOLLOW_SYMLINK_CHAIN)
endforeach()
foreach(LIB ${Qt5_PLATFORMINPUTCONTEXT_DLLS})
file(COPY ${LIB} DESTINATION "${DLL_DEST}plugins/platforminputcontexts/" FOLLOW_SYMLINK_CHAIN)
endforeach()
foreach(LIB ${Qt5_XCBGLINTEGRATION_DLLS})
file(COPY ${LIB} DESTINATION "${DLL_DEST}plugins/xcbglintegrations/" FOLLOW_SYMLINK_CHAIN)
endforeach()
endif()
# Create an empty qt.conf file. Qt will detect that this file exists, and use the folder that its in as the root folder.
# This way it'll look for plugins in the root/plugins/ folder
add_custom_command(TARGET yuzu POST_BUILD

View File

@@ -45,21 +45,28 @@ target_include_directories(microprofile INTERFACE ./microprofile)
add_library(unicorn-headers INTERFACE)
target_include_directories(unicorn-headers INTERFACE ./unicorn/include)
# libusb
if (NOT LIBUSB_FOUND OR YUZU_USE_BUNDLED_LIBUSB)
add_subdirectory(libusb)
endif()
# SDL2
if (NOT SDL2_FOUND AND ENABLE_SDL2)
# Yuzu itself needs: Events Joystick Haptic Sensor Timers
# Yuzu-cmd also needs: Video (depends on Loadso/Dlopen)
set(SDL_UNUSED_SUBSYSTEMS
Atomic Audio Render Power Threads
File CPUinfo Filesystem Locale)
foreach(_SUB ${SDL_UNUSED_SUBSYSTEMS})
string(TOUPPER ${_SUB} _OPT)
option(SDL_${_OPT} "" OFF)
endforeach()
if (NOT WIN32)
# Yuzu itself needs: Events Joystick Haptic Sensor Timers
# Yuzu-cmd also needs: Video (depends on Loadso/Dlopen)
set(SDL_UNUSED_SUBSYSTEMS
Atomic Audio Render Power Threads
File CPUinfo Filesystem Locale)
foreach(_SUB ${SDL_UNUSED_SUBSYSTEMS})
string(TOUPPER ${_SUB} _OPT)
option(SDL_${_OPT} "" OFF)
endforeach()
option(HIDAPI "" ON)
endif()
set(SDL_STATIC ON)
set(SDL_SHARED OFF)
option(HIDAPI "" ON)
add_subdirectory(SDL EXCLUDE_FROM_ALL)
add_library(SDL2 ALIAS SDL2-static)

2
externals/SDL vendored

View File

@@ -1,155 +1,259 @@
# Ensure libusb compiles with UTF-8 encoding on MSVC
if(MSVC)
add_compile_options(/utf-8)
endif()
set(LIBUSB_FOUND ON CACHE BOOL "libusb is present" FORCE)
set(LIBUSB_VERSION "1.0.24" CACHE STRING "libusb version string" FORCE)
add_library(usb STATIC EXCLUDE_FROM_ALL
libusb/libusb/core.c
libusb/libusb/core.c
libusb/libusb/descriptor.c
libusb/libusb/hotplug.c
libusb/libusb/io.c
libusb/libusb/strerror.c
libusb/libusb/sync.c
)
set_target_properties(usb PROPERTIES VERSION 1.0.23)
if(WIN32)
target_include_directories(usb
BEFORE
PUBLIC
libusb/libusb
if (MINGW OR (${CMAKE_SYSTEM_NAME} MATCHES "Linux"))
# GNU toolchains for some reason doesn't work with the later half of this CMakeLists after
# updating to 1.0.24, so we do it the old-fashioned way for now.
PRIVATE
"${CMAKE_CURRENT_BINARY_DIR}"
)
set(LIBUSB_PREFIX "${CMAKE_CURRENT_BINARY_DIR}/libusb")
set(LIBUSB_SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/libusb")
if (NOT MINGW)
target_include_directories(usb BEFORE PRIVATE libusb/msvc)
# Workarounds for MSYS/MinGW
if (MSYS)
# CMake on Windows passes `C:/`, but we need `/C/` or `/c/` to use `configure`
string(REPLACE ":/" "/" LIBUSB_SRC_DIR "${LIBUSB_SRC_DIR}")
set(LIBUSB_SRC_DIR "/${LIBUSB_SRC_DIR}")
# And now that we are using /C/ for srcdir but everything else is using C:/, we need to
# compile everything in the source directory, else `configure` won't think the build
# environment is sane.
set(LIBUSB_PREFIX "${LIBUSB_SRC_DIR}")
endif()
# Works around other libraries providing their own definition of USB GUIDs (e.g. SDL2)
target_compile_definitions(usb PRIVATE "-DGUID_DEVINTERFACE_USB_DEVICE=(GUID){ 0xA5DCBF10, 0x6530, 0x11D2, {0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED}}")
else()
target_include_directories(usb
# turns out other projects also have "config.h", so make sure the
# LibUSB one comes first
BEFORE
set(LIBUSB_CONFIGURE "${LIBUSB_SRC_DIR}/configure")
set(LIBUSB_MAKEFILE "${LIBUSB_PREFIX}/Makefile")
PUBLIC
libusb/libusb
if (MINGW)
set(LIBUSB_LIBRARIES "${LIBUSB_PREFIX}/libusb/.libs/libusb-1.0.dll.a" CACHE PATH "libusb library path" FORCE)
set(LIBUSB_SHARED_LIBRARY "${LIBUSB_PREFIX}/libusb/.libs/libusb-1.0.dll")
set(LIBUSB_SHARED_LIBRARY_DEST "${CMAKE_BINARY_DIR}/bin/libusb-1.0.dll")
PRIVATE
"${CMAKE_CURRENT_BINARY_DIR}"
)
endif()
if(WIN32 OR CYGWIN)
target_sources(usb PRIVATE
libusb/libusb/os/threads_windows.c
libusb/libusb/os/windows_winusb.c
libusb/libusb/os/windows_usbdk.c
libusb/libusb/os/windows_nt_common.c
)
set(OS_WINDOWS TRUE)
elseif(APPLE)
target_sources(usb PRIVATE
libusb/libusb/os/darwin_usb.c
)
find_library(COREFOUNDATION_LIBRARY CoreFoundation)
find_library(IOKIT_LIBRARY IOKit)
find_library(OBJC_LIBRARY objc)
target_link_libraries(usb PRIVATE
${COREFOUNDATION_LIBRARY}
${IOKIT_LIBRARY}
${OBJC_LIBRARY}
)
set(OS_DARWIN TRUE)
elseif(ANDROID)
target_sources(usb PRIVATE
libusb/libusb/os/linux_usbfs.c
libusb/libusb/os/linux_netlink.c
)
find_library(LOG_LIBRARY log)
target_link_libraries(usb PRIVATE ${LOG_LIBRARY})
set(OS_LINUX TRUE)
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
target_sources(usb PRIVATE
libusb/libusb/os/linux_usbfs.c
)
find_package(Libudev)
if(LIBUDEV_FOUND)
target_sources(usb PRIVATE
libusb/libusb/os/linux_udev.c
)
target_link_libraries(usb PRIVATE "${LIBUDEV_LIBRARIES}")
target_include_directories(usb PRIVATE "${LIBUDEV_INCLUDE_DIR}")
set(HAVE_LIBUDEV TRUE)
set(USE_UDEV TRUE)
set(LIBUSB_CONFIGURE_ARGS --host=x86_64-w64-mingw32 --build=x86_64-windows)
else()
set(LIBUSB_LIBRARIES "${LIBUSB_PREFIX}/libusb/.libs/libusb-1.0.a" CACHE PATH "libusb library path" FORCE)
endif()
set(LIBUSB_INCLUDE_DIRS "${LIBUSB_SRC_DIR}/libusb" CACHE PATH "libusb headers path" FORCE)
# MINGW: causes "externals/libusb/libusb/libusb/os/windows_winusb.c:1427:2: error: conversion to non-scalar type requested", so cannot statically link it for now.
if (NOT MINGW)
set(LIBUSB_CFLAGS "-DGUID_DEVINTERFACE_USB_DEVICE=\\(GUID\\){0xA5DCBF10,0x6530,0x11D2,{0x90,0x1F,0x00,0xC0,0x4F,0xB9,0x51,0xED}}")
endif()
make_directory("${LIBUSB_PREFIX}")
add_custom_command(
OUTPUT
"${LIBUSB_LIBRARIES}"
COMMAND
make
WORKING_DIRECTORY
"${LIBUSB_PREFIX}"
)
add_custom_command(
OUTPUT
"${LIBUSB_MAKEFILE}"
COMMAND
env
CFLAGS="${LIBUSB_CFLAGS}"
sh "${LIBUSB_CONFIGURE}"
${LIBUSB_CONFIGURE_ARGS}
--srcdir="${LIBUSB_SRC_DIR}"
WORKING_DIRECTORY
"${LIBUSB_PREFIX}"
)
add_custom_command(
OUTPUT
"${LIBUSB_CONFIGURE}"
COMMAND
sh "${LIBUSB_SRC_DIR}/bootstrap.sh"
WORKING_DIRECTORY
"${LIBUSB_SRC_DIR}"
)
add_custom_command(
OUTPUT
"${LIBUSB_SHARED_LIBRARY_DEST}"
COMMAND
cp "${LIBUSB_SHARED_LIBRARY}" "${LIBUSB_SHARED_LIBRARY_DEST}"
)
add_custom_target(usb-bootstrap DEPENDS "${LIBUSB_CONFIGURE}")
add_custom_target(usb-configure DEPENDS "${LIBUSB_MAKEFILE}" usb-bootstrap)
add_custom_target(usb-build ALL DEPENDS "${LIBUSB_LIBRARIES}" usb-configure)
# Workaround since static linking didn't work out -- We need to copy the DLL to the bin directory
add_custom_target(usb-copy ALL DEPENDS "${LIBUSB_SHARED_LIBRARY_DEST}" usb-build)
add_library(usb INTERFACE)
add_dependencies(usb usb-copy)
target_link_libraries(usb INTERFACE "${LIBUSB_LIBRARIES}")
target_include_directories(usb INTERFACE "${LIBUSB_INCLUDE_DIRS}")
if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
Include(FindPkgConfig)
pkg_check_modules(LIBUDEV REQUIRED libudev)
if (LIBUDEV_FOUND)
target_include_directories(usb INTERFACE "${LIBUDEV_INCLUDE_DIRS}")
target_link_libraries(usb INTERFACE "${LIBUDEV_STATIC_LIBRARIES}")
endif()
endif()
else() # MINGW OR (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
# Ensure libusb compiles with UTF-8 encoding on MSVC
if(MSVC)
add_compile_options(/utf-8)
endif()
add_library(usb STATIC EXCLUDE_FROM_ALL
libusb/libusb/core.c
libusb/libusb/core.c
libusb/libusb/descriptor.c
libusb/libusb/hotplug.c
libusb/libusb/io.c
libusb/libusb/strerror.c
libusb/libusb/sync.c
)
set_target_properties(usb PROPERTIES VERSION 1.0.24)
if(WIN32)
target_include_directories(usb
BEFORE
PUBLIC
libusb/libusb
PRIVATE
"${CMAKE_CURRENT_BINARY_DIR}"
)
if (NOT MINGW)
target_include_directories(usb BEFORE PRIVATE libusb/msvc)
endif()
# Works around other libraries providing their own definition of USB GUIDs (e.g. SDL2)
target_compile_definitions(usb PRIVATE "-DGUID_DEVINTERFACE_USB_DEVICE=(GUID){ 0xA5DCBF10, 0x6530, 0x11D2, {0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED}}")
else()
target_include_directories(usb
# turns out other projects also have "config.h", so make sure the
# LibUSB one comes first
BEFORE
PUBLIC
libusb/libusb
PRIVATE
"${CMAKE_CURRENT_BINARY_DIR}"
)
endif()
if(WIN32 OR CYGWIN)
target_sources(usb PRIVATE
libusb/libusb/os/threads_windows.c
libusb/libusb/os/windows_winusb.c
libusb/libusb/os/windows_usbdk.c
libusb/libusb/os/windows_common.c
)
set(OS_WINDOWS TRUE)
elseif(APPLE)
target_sources(usb PRIVATE
libusb/libusb/os/darwin_usb.c
)
find_library(COREFOUNDATION_LIBRARY CoreFoundation)
find_library(IOKIT_LIBRARY IOKit)
find_library(OBJC_LIBRARY objc)
target_link_libraries(usb PRIVATE
${COREFOUNDATION_LIBRARY}
${IOKIT_LIBRARY}
${OBJC_LIBRARY}
)
set(OS_DARWIN TRUE)
elseif(ANDROID)
target_sources(usb PRIVATE
libusb/libusb/os/linux_usbfs.c
libusb/libusb/os/linux_netlink.c
)
find_library(LOG_LIBRARY log)
target_link_libraries(usb PRIVATE ${LOG_LIBRARY})
set(OS_LINUX TRUE)
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
target_sources(usb PRIVATE
libusb/libusb/os/linux_usbfs.c
)
find_package(Libudev)
if(LIBUDEV_FOUND)
target_sources(usb PRIVATE
libusb/libusb/os/linux_udev.c
)
target_link_libraries(usb PRIVATE "${LIBUDEV_LIBRARIES}")
target_include_directories(usb PRIVATE "${LIBUDEV_INCLUDE_DIR}")
set(HAVE_LIBUDEV TRUE)
set(USE_UDEV TRUE)
else()
target_sources(usb PRIVATE
libusb/libusb/os/linux_netlink.c
)
endif()
set(OS_LINUX TRUE)
elseif(${CMAKE_SYSTEM_NAME} MATCHES "NetBSD")
target_sources(usb PRIVATE
libusb/libusb/os/netbsd_usb.c
)
set(OS_NETBSD TRUE)
elseif(${CMAKE_SYSTEM_NAME} MATCHES "OpenBSD")
target_sources(usb PRIVATE
libusb/libusb/os/openbsd_usb.c
)
set(OS_OPENBSD TRUE)
endif()
set(OS_LINUX TRUE)
elseif(${CMAKE_SYSTEM_NAME} MATCHES "NetBSD")
target_sources(usb PRIVATE
libusb/libusb/os/netbsd_usb.c
)
set(OS_NETBSD TRUE)
elseif(${CMAKE_SYSTEM_NAME} MATCHES "OpenBSD")
target_sources(usb PRIVATE
libusb/libusb/os/openbsd_usb.c
)
set(OS_OPENBSD TRUE)
endif()
if(UNIX)
target_sources(usb PRIVATE
libusb/libusb/os/poll_posix.c
libusb/libusb/os/threads_posix.c
)
find_package(Threads REQUIRED)
if(THREADS_HAVE_PTHREAD_ARG)
target_compile_options(usb PUBLIC "-pthread")
if(UNIX)
target_sources(usb PRIVATE
libusb/libusb/os/events_posix.c
libusb/libusb/os/threads_posix.c
)
find_package(Threads REQUIRED)
if(THREADS_HAVE_PTHREAD_ARG)
target_compile_options(usb PUBLIC "-pthread")
endif()
if(CMAKE_THREAD_LIBS_INIT)
target_link_libraries(usb PRIVATE "${CMAKE_THREAD_LIBS_INIT}")
endif()
set(THREADS_POSIX TRUE)
elseif(WIN32)
target_sources(usb PRIVATE
libusb/libusb/os/events_windows.c
libusb/libusb/os/threads_windows.c
)
endif()
if(CMAKE_THREAD_LIBS_INIT)
target_link_libraries(usb PRIVATE "${CMAKE_THREAD_LIBS_INIT}")
include(CheckFunctionExists)
include(CheckIncludeFiles)
include(CheckTypeSize)
check_include_files(asm/types.h HAVE_ASM_TYPES_H)
check_function_exists(gettimeofday HAVE_GETTIMEOFDAY)
check_include_files(linux/filter.h HAVE_LINUX_FILTER_H)
check_include_files(linux/netlink.h HAVE_LINUX_NETLINK_H)
check_include_files(poll.h HAVE_POLL_H)
check_include_files(signal.h HAVE_SIGNAL_H)
check_include_files(strings.h HAVE_STRINGS_H)
check_type_size("struct timespec" STRUCT_TIMESPEC)
check_function_exists(syslog HAVE_SYSLOG_FUNC)
check_include_files(syslog.h HAVE_SYSLOG_H)
check_include_files(sys/socket.h HAVE_SYS_SOCKET_H)
check_include_files(sys/time.h HAVE_SYS_TIME_H)
check_include_files(sys/types.h HAVE_SYS_TYPES_H)
set(CMAKE_EXTRA_INCLUDE_FILES poll.h)
check_type_size("nfds_t" nfds_t)
unset(CMAKE_EXTRA_INCLUDE_FILES)
if(HAVE_NFDS_T)
set(POLL_NFDS_TYPE "nfds_t")
else()
set(POLL_NFDS_TYPE "unsigned int")
endif()
set(THREADS_POSIX TRUE)
elseif(WIN32)
target_sources(usb PRIVATE
libusb/libusb/os/poll_windows.c
libusb/libusb/os/threads_windows.c
)
endif()
include(CheckFunctionExists)
include(CheckIncludeFiles)
include(CheckTypeSize)
check_include_files(asm/types.h HAVE_ASM_TYPES_H)
check_function_exists(gettimeofday HAVE_GETTIMEOFDAY)
check_include_files(linux/filter.h HAVE_LINUX_FILTER_H)
check_include_files(linux/netlink.h HAVE_LINUX_NETLINK_H)
check_include_files(poll.h HAVE_POLL_H)
check_include_files(signal.h HAVE_SIGNAL_H)
check_include_files(strings.h HAVE_STRINGS_H)
check_type_size("struct timespec" STRUCT_TIMESPEC)
check_function_exists(syslog HAVE_SYSLOG_FUNC)
check_include_files(syslog.h HAVE_SYSLOG_H)
check_include_files(sys/socket.h HAVE_SYS_SOCKET_H)
check_include_files(sys/time.h HAVE_SYS_TIME_H)
check_include_files(sys/types.h HAVE_SYS_TYPES_H)
set(CMAKE_EXTRA_INCLUDE_FILES poll.h)
check_type_size("nfds_t" nfds_t)
unset(CMAKE_EXTRA_INCLUDE_FILES)
if(HAVE_NFDS_T)
set(POLL_NFDS_TYPE "nfds_t")
else()
set(POLL_NFDS_TYPE "unsigned int")
endif()
check_include_files(sys/timerfd.h USBI_TIMERFD_AVAILABLE)
check_include_files(sys/timerfd.h USBI_TIMERFD_AVAILABLE)
configure_file(config.h.in config.h)
configure_file(config.h.in config.h)
endif() # MINGW OR (${CMAKE_SYSTEM_NAME} MATCHES "Linux")

View File

@@ -197,7 +197,7 @@ ResultCode AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_param
ReleaseAndQueueBuffers();
return RESULT_SUCCESS;
return ResultSuccess;
}
void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) {

View File

@@ -407,7 +407,7 @@ ResultCode InfoUpdater::UpdateMixes(MixContext& mix_context, std::size_t mix_buf
// TODO(ogniK): Sort when splitter is suppoorted
return RESULT_SUCCESS;
return ResultSuccess;
}
bool InfoUpdater::UpdateSinks(SinkContext& sink_context) {

View File

@@ -21,14 +21,14 @@ find_package(Git QUIET)
add_custom_command(OUTPUT scm_rev.cpp
COMMAND ${CMAKE_COMMAND}
-DSRC_DIR="${CMAKE_SOURCE_DIR}"
-DBUILD_REPOSITORY="${BUILD_REPOSITORY}"
-DTITLE_BAR_FORMAT_IDLE="${TITLE_BAR_FORMAT_IDLE}"
-DTITLE_BAR_FORMAT_RUNNING="${TITLE_BAR_FORMAT_RUNNING}"
-DBUILD_TAG="${BUILD_TAG}"
-DBUILD_ID="${DISPLAY_VERSION}"
-DGIT_EXECUTABLE="${GIT_EXECUTABLE}"
-P "${CMAKE_SOURCE_DIR}/CMakeModules/GenerateSCMRev.cmake"
-DSRC_DIR=${CMAKE_SOURCE_DIR}
-DBUILD_REPOSITORY=${BUILD_REPOSITORY}
-DTITLE_BAR_FORMAT_IDLE=${TITLE_BAR_FORMAT_IDLE}
-DTITLE_BAR_FORMAT_RUNNING=${TITLE_BAR_FORMAT_RUNNING}
-DBUILD_TAG=${BUILD_TAG}
-DBUILD_ID=${DISPLAY_VERSION}
-DGIT_EXECUTABLE=${GIT_EXECUTABLE}
-P ${CMAKE_SOURCE_DIR}/CMakeModules/GenerateSCMRev.cmake
DEPENDS
# WARNING! It was too much work to try and make a common location for this list,
# so if you need to change it, please update CMakeModules/GenerateSCMRev.cmake as well
@@ -92,6 +92,7 @@ add_custom_command(OUTPUT scm_rev.cpp
"${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.h"
# technically we should regenerate if the git version changed, but its not worth the effort imo
"${CMAKE_SOURCE_DIR}/CMakeModules/GenerateSCMRev.cmake"
VERBATIM
)
add_library(common STATIC
@@ -109,7 +110,6 @@ add_library(common STATIC
cityhash.cpp
cityhash.h
common_funcs.h
common_paths.h
common_sizes.h
common_types.h
concepts.h
@@ -118,8 +118,16 @@ add_library(common STATIC
dynamic_library.h
fiber.cpp
fiber.h
file_util.cpp
file_util.h
fs/file.cpp
fs/file.h
fs/fs.cpp
fs/fs.h
fs/fs_paths.h
fs/fs_types.h
fs/fs_util.cpp
fs/fs_util.h
fs/path_util.cpp
fs/path_util.h
hash.h
hex_util.cpp
hex_util.h
@@ -147,6 +155,7 @@ add_library(common STATIC
param_package.cpp
param_package.h
parent_of_member.h
point.h
quaternion.h
ring_buffer.h
scm_rev.cpp

View File

@@ -97,17 +97,6 @@ __declspec(dllimport) void __stdcall DebugBreak(void);
return static_cast<T>(key) == 0; \
}
/// Evaluates a boolean expression, and returns a result unless that expression is true.
#define R_UNLESS(expr, res) \
{ \
if (!(expr)) { \
if (res.IsError()) { \
LOG_ERROR(Kernel, "Failed with result: {}", res.raw); \
} \
return res; \
} \
}
#define YUZU_NON_COPYABLE(cls) \
cls(const cls&) = delete; \
cls& operator=(const cls&) = delete
@@ -116,20 +105,6 @@ __declspec(dllimport) void __stdcall DebugBreak(void);
cls(cls&&) = delete; \
cls& operator=(cls&&) = delete
#define R_SUCCEEDED(res) (res.IsSuccess())
/// Evaluates an expression that returns a result, and returns the result if it would fail.
#define R_TRY(res_expr) \
{ \
const auto _tmp_r_try_rc = (res_expr); \
if (_tmp_r_try_rc.IsError()) { \
return _tmp_r_try_rc; \
} \
}
/// Evaluates a boolean expression, and succeeds if that expression is true.
#define R_SUCCEED_IF(expr) R_UNLESS(!(expr), RESULT_SUCCESS)
namespace Common {
[[nodiscard]] constexpr u32 MakeMagic(char a, char b, char c, char d) {

View File

@@ -1,52 +0,0 @@
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
// Directory separators, do we need this?
#define DIR_SEP "/"
#define DIR_SEP_CHR '/'
#ifndef MAX_PATH
#define MAX_PATH 260
#endif
// The user data dir
#define ROOT_DIR "."
#define USERDATA_DIR "user"
#ifdef USER_DIR
#define EMU_DATA_DIR USER_DIR
#else
#define EMU_DATA_DIR "yuzu"
#endif
// Dirs in both User and Sys
#define EUR_DIR "EUR"
#define USA_DIR "USA"
#define JAP_DIR "JAP"
// Subdirs in the User dir returned by GetUserPath(UserPath::UserDir)
#define CONFIG_DIR "config"
#define CACHE_DIR "cache"
#define SDMC_DIR "sdmc"
#define NAND_DIR "nand"
#define SYSDATA_DIR "sysdata"
#define KEYS_DIR "keys"
#define LOAD_DIR "load"
#define DUMP_DIR "dump"
#define SCREENSHOTS_DIR "screenshots"
#define SHADER_DIR "shader"
#define LOG_DIR "log"
// Filenames
// 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(UserPath::LogDir)
#define LOG_FILE "yuzu_log.txt"
// Sys files
#define SHARED_FONT "shared_font.bin"
#define AES_KEYS "aes_keys.txt"

File diff suppressed because it is too large Load Diff

View File

@@ -1,298 +0,0 @@
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include <cstdio>
#include <fstream>
#include <functional>
#include <limits>
#include <optional>
#include <string>
#include <string_view>
#include <type_traits>
#include <vector>
#include "common/common_types.h"
#ifdef _MSC_VER
#include "common/string_util.h"
#endif
namespace Common::FS {
// User paths for GetUserPath
enum class UserPath {
CacheDir,
ConfigDir,
KeysDir,
LogDir,
NANDDir,
RootDir,
SDMCDir,
LoadDir,
DumpDir,
ScreenshotsDir,
ShaderDir,
SysDataDir,
UserDir,
};
// FileSystem tree node/
struct FSTEntry {
bool isDirectory;
u64 size; // file length or number of entries from children
std::string physicalName; // name on disk
std::string virtualName; // name in FST names table
std::vector<FSTEntry> children;
};
// Returns true if file filename exists
[[nodiscard]] bool Exists(const std::string& filename);
// Returns true if filename is a directory
[[nodiscard]] bool IsDirectory(const std::string& filename);
// Returns the size of filename (64bit)
[[nodiscard]] u64 GetSize(const std::string& filename);
// Overloaded GetSize, accepts file descriptor
[[nodiscard]] u64 GetSize(int fd);
// Overloaded GetSize, accepts FILE*
[[nodiscard]] u64 GetSize(FILE* f);
// Returns true if successful, or path already exists.
bool CreateDir(const std::string& filename);
// Creates the full path of fullPath returns true on success
bool CreateFullPath(const std::string& fullPath);
// Deletes a given filename, return true on success
// Doesn't supports deleting a directory
bool Delete(const std::string& filename);
// Deletes a directory filename, returns true on success
bool DeleteDir(const std::string& filename);
// renames file srcFilename to destFilename, returns true on success
bool Rename(const std::string& srcFilename, const std::string& destFilename);
// copies file srcFilename to destFilename, returns true on success
bool Copy(const std::string& srcFilename, const std::string& destFilename);
// creates an empty file filename, returns true on success
bool CreateEmptyFile(const std::string& filename);
/**
* @param num_entries_out to be assigned by the callable with the number of iterated directory
* entries, never null
* @param directory the path to the enclosing directory
* @param virtual_name the entry name, without any preceding directory info
* @return whether handling the entry succeeded
*/
using DirectoryEntryCallable = std::function<bool(
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.
* If the callback returns failure, scanning halts and this function returns failure as well
* @param num_entries_out assigned by the function with the number of iterated directory entries,
* can be null
* @param directory the directory to scan
* @param callback The callback which will be called for each entry
* @return whether scanning the directory succeeded
*/
bool ForeachDirectoryEntry(u64* num_entries_out, const std::string& directory,
DirectoryEntryCallable callback);
/**
* Scans the directory tree, storing the results.
* @param directory the parent directory to start scanning from
* @param parent_entry FSTEntry where the filesystem tree results will be stored.
* @param recursion Number of children directories to read before giving up.
* @return the total number of files/directories found
*/
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);
// Returns the current directory
[[nodiscard]] std::optional<std::string> GetCurrentDir();
// Create directory and copy contents (does not overwrite existing files)
void CopyDir(const std::string& source_path, const std::string& dest_path);
// Set the current directory to given directory
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(UserPath path, const std::string& new_path = "");
[[nodiscard]] std::string GetHactoolConfigurationPath();
[[nodiscard]] std::string GetNANDRegistrationDir(bool system = false);
// Returns the path to where the sys file are
[[nodiscard]] std::string GetSysDirectory();
#ifdef __APPLE__
[[nodiscard]] std::string GetBundleDirectory();
#endif
#ifdef _WIN32
[[nodiscard]] const std::string& GetExeDirectory();
[[nodiscard]] std::string AppDataRoamingDirectory();
#endif
std::size_t WriteStringToFile(bool text_file, const std::string& filename, std::string_view str);
std::size_t ReadFileToString(bool text_file, const std::string& filename, std::string& str);
/**
* Splits the filename into 8.3 format
* Loosely implemented following https://en.wikipedia.org/wiki/8.3_filename
* @param filename The normal filename to use
* @param short_name A 9-char array in which the short name will be written
* @param extension A 4-char array in which the extension will be written
*/
void SplitFilename83(const std::string& filename, std::array<char, 9>& short_name,
std::array<char, 4>& extension);
// 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" }
[[nodiscard]] std::vector<std::string> SplitPathComponents(std::string_view filename);
// Gets all of the text up to the last '/' or '\' in the path.
[[nodiscard]] std::string_view GetParentPath(std::string_view path);
// Gets all of the text after the first '/' or '\' in the path.
[[nodiscard]] std::string_view GetPathWithoutTop(std::string_view path);
// Gets the filename of the path
[[nodiscard]] std::string_view GetFilename(std::string_view path);
// Gets the extension of the filename
[[nodiscard]] std::string_view GetExtensionFromFilename(std::string_view name);
// Removes the final '/' or '\' if one exists
[[nodiscard]] std::string_view RemoveTrailingSlash(std::string_view path);
// Creates a new vector containing indices [first, last) from the original.
template <typename T>
[[nodiscard]] std::vector<T> SliceVector(const std::vector<T>& vector, std::size_t first,
std::size_t last) {
if (first >= last) {
return {};
}
last = std::min<std::size_t>(last, vector.size());
return std::vector<T>(vector.begin() + first, vector.begin() + first + last);
}
enum class DirectorySeparator {
ForwardSlash,
BackwardSlash,
PlatformDefault,
};
// Removes trailing slash, makes all '\\' into '/', and removes duplicate '/'. Makes '/' into '\\'
// depending if directory_separator is BackwardSlash or PlatformDefault and running on windows
[[nodiscard]] std::string SanitizePath(
std::string_view path,
DirectorySeparator directory_separator = DirectorySeparator::ForwardSlash);
// To deal with Windows being dumb at Unicode
template <typename T>
void OpenFStream(T& fstream, const std::string& filename, std::ios_base::openmode openmode) {
#ifdef _MSC_VER
fstream.open(Common::UTF8ToUTF16W(filename), openmode);
#else
fstream.open(filename, openmode);
#endif
}
// simple wrapper for cstdlib file functions to
// hopefully will make error checking easier
// and make forgetting an fclose() harder
class IOFile : public NonCopyable {
public:
IOFile();
// flags is used for windows specific file open mode flags, which
// allows yuzu to open the logs in shared write mode, so that the file
// isn't considered "locked" while yuzu is open and people can open the log file and view it
IOFile(const std::string& filename, const char openmode[], int flags = 0);
~IOFile();
IOFile(IOFile&& other) noexcept;
IOFile& operator=(IOFile&& other) noexcept;
void Swap(IOFile& other) noexcept;
bool Open(const std::string& filename, const char openmode[], int flags = 0);
bool Close();
template <typename T>
std::size_t ReadArray(T* data, std::size_t length) const {
static_assert(std::is_trivially_copyable_v<T>,
"Given array does not consist of trivially copyable objects");
return ReadImpl(data, length, sizeof(T));
}
template <typename T>
std::size_t WriteArray(const T* data, std::size_t length) {
static_assert(std::is_trivially_copyable_v<T>,
"Given array does not consist of trivially copyable objects");
return WriteImpl(data, length, sizeof(T));
}
template <typename T>
std::size_t ReadBytes(T* data, std::size_t length) const {
static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable");
return ReadArray(reinterpret_cast<char*>(data), length);
}
template <typename T>
std::size_t WriteBytes(const T* data, std::size_t length) {
static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable");
return WriteArray(reinterpret_cast<const char*>(data), length);
}
template <typename T>
std::size_t WriteObject(const T& object) {
static_assert(!std::is_pointer_v<T>, "WriteObject arguments must not be a pointer");
return WriteArray(&object, 1);
}
std::size_t WriteString(std::string_view str) {
return WriteArray(str.data(), str.length());
}
[[nodiscard]] bool IsOpen() const {
return nullptr != m_file;
}
bool Seek(s64 off, int origin) const;
[[nodiscard]] u64 Tell() const;
[[nodiscard]] u64 GetSize() const;
bool Resize(u64 size);
bool Flush();
// clear error state
void Clear() {
std::clearerr(m_file);
}
private:
std::size_t ReadImpl(void* data, std::size_t length, std::size_t data_size) const;
std::size_t WriteImpl(const void* data, std::size_t length, std::size_t data_size);
std::FILE* m_file = nullptr;
};
} // namespace Common::FS

392
src/common/fs/file.cpp Normal file
View File

@@ -0,0 +1,392 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/fs/file.h"
#include "common/fs/fs.h"
#include "common/fs/path_util.h"
#include "common/logging/log.h"
#ifdef _WIN32
#include <io.h>
#include <share.h>
#else
#include <unistd.h>
#endif
#ifdef _MSC_VER
#define fileno _fileno
#define fseeko _fseeki64
#define ftello _ftelli64
#endif
namespace Common::FS {
namespace fs = std::filesystem;
namespace {
#ifdef _WIN32
/**
* Converts the file access mode and file type enums to a file access mode wide string.
*
* @param mode File access mode
* @param type File type
*
* @returns A pointer to a wide string representing the file access mode.
*/
[[nodiscard]] constexpr const wchar_t* AccessModeToWStr(FileAccessMode mode, FileType type) {
switch (type) {
case FileType::BinaryFile:
switch (mode) {
case FileAccessMode::Read:
return L"rb";
case FileAccessMode::Write:
return L"wb";
case FileAccessMode::Append:
return L"ab";
case FileAccessMode::ReadWrite:
return L"r+b";
case FileAccessMode::ReadAppend:
return L"a+b";
}
break;
case FileType::TextFile:
switch (mode) {
case FileAccessMode::Read:
return L"r";
case FileAccessMode::Write:
return L"w";
case FileAccessMode::Append:
return L"a";
case FileAccessMode::ReadWrite:
return L"r+";
case FileAccessMode::ReadAppend:
return L"a+";
}
break;
}
return L"";
}
/**
* Converts the file-share access flag enum to a Windows defined file-share access flag.
*
* @param flag File-share access flag
*
* @returns Windows defined file-share access flag.
*/
[[nodiscard]] constexpr int ToWindowsFileShareFlag(FileShareFlag flag) {
switch (flag) {
case FileShareFlag::ShareNone:
default:
return _SH_DENYRW;
case FileShareFlag::ShareReadOnly:
return _SH_DENYWR;
case FileShareFlag::ShareWriteOnly:
return _SH_DENYRD;
case FileShareFlag::ShareReadWrite:
return _SH_DENYNO;
}
}
#else
/**
* Converts the file access mode and file type enums to a file access mode string.
*
* @param mode File access mode
* @param type File type
*
* @returns A pointer to a string representing the file access mode.
*/
[[nodiscard]] constexpr const char* AccessModeToStr(FileAccessMode mode, FileType type) {
switch (type) {
case FileType::BinaryFile:
switch (mode) {
case FileAccessMode::Read:
return "rb";
case FileAccessMode::Write:
return "wb";
case FileAccessMode::Append:
return "ab";
case FileAccessMode::ReadWrite:
return "r+b";
case FileAccessMode::ReadAppend:
return "a+b";
}
break;
case FileType::TextFile:
switch (mode) {
case FileAccessMode::Read:
return "r";
case FileAccessMode::Write:
return "w";
case FileAccessMode::Append:
return "a";
case FileAccessMode::ReadWrite:
return "r+";
case FileAccessMode::ReadAppend:
return "a+";
}
break;
}
return "";
}
#endif
/**
* Converts the seek origin enum to a seek origin integer.
*
* @param origin Seek origin
*
* @returns Seek origin integer.
*/
[[nodiscard]] constexpr int ToSeekOrigin(SeekOrigin origin) {
switch (origin) {
case SeekOrigin::SetOrigin:
default:
return SEEK_SET;
case SeekOrigin::CurrentPosition:
return SEEK_CUR;
case SeekOrigin::End:
return SEEK_END;
}
}
} // Anonymous namespace
std::string ReadStringFromFile(const std::filesystem::path& path, FileType type) {
if (!IsFile(path)) {
return "";
}
IOFile io_file{path, FileAccessMode::Read, type};
return io_file.ReadString(io_file.GetSize());
}
size_t WriteStringToFile(const std::filesystem::path& path, FileType type,
std::string_view string) {
if (!IsFile(path)) {
return 0;
}
IOFile io_file{path, FileAccessMode::Write, type};
return io_file.WriteString(string);
}
size_t AppendStringToFile(const std::filesystem::path& path, FileType type,
std::string_view string) {
if (!Exists(path)) {
return WriteStringToFile(path, type, string);
}
if (!IsFile(path)) {
return 0;
}
IOFile io_file{path, FileAccessMode::Append, type};
return io_file.WriteString(string);
}
IOFile::IOFile() = default;
IOFile::IOFile(const std::string& path, FileAccessMode mode, FileType type, FileShareFlag flag) {
Open(path, mode, type, flag);
}
IOFile::IOFile(std::string_view path, FileAccessMode mode, FileType type, FileShareFlag flag) {
Open(path, mode, type, flag);
}
IOFile::IOFile(const fs::path& path, FileAccessMode mode, FileType type, FileShareFlag flag) {
Open(path, mode, type, flag);
}
IOFile::~IOFile() {
Close();
}
IOFile::IOFile(IOFile&& other) noexcept {
std::swap(file_path, other.file_path);
std::swap(file_access_mode, other.file_access_mode);
std::swap(file_type, other.file_type);
std::swap(file, other.file);
}
IOFile& IOFile::operator=(IOFile&& other) noexcept {
std::swap(file_path, other.file_path);
std::swap(file_access_mode, other.file_access_mode);
std::swap(file_type, other.file_type);
std::swap(file, other.file);
return *this;
}
fs::path IOFile::GetPath() const {
return file_path;
}
FileAccessMode IOFile::GetAccessMode() const {
return file_access_mode;
}
FileType IOFile::GetType() const {
return file_type;
}
void IOFile::Open(const fs::path& path, FileAccessMode mode, FileType type, FileShareFlag flag) {
Close();
file_path = path;
file_access_mode = mode;
file_type = type;
errno = 0;
#ifdef _WIN32
if (flag != FileShareFlag::ShareNone) {
file = _wfsopen(path.c_str(), AccessModeToWStr(mode, type), ToWindowsFileShareFlag(flag));
} else {
_wfopen_s(&file, path.c_str(), AccessModeToWStr(mode, type));
}
#else
file = std::fopen(path.c_str(), AccessModeToStr(mode, type));
#endif
if (!IsOpen()) {
const auto ec = std::error_code{errno, std::generic_category()};
LOG_ERROR(Common_Filesystem, "Failed to open the file at path={}, ec_message={}",
PathToUTF8String(file_path), ec.message());
}
}
void IOFile::Close() {
if (!IsOpen()) {
return;
}
errno = 0;
const auto close_result = std::fclose(file) == 0;
if (!close_result) {
const auto ec = std::error_code{errno, std::generic_category()};
LOG_ERROR(Common_Filesystem, "Failed to close the file at path={}, ec_message={}",
PathToUTF8String(file_path), ec.message());
}
file = nullptr;
}
bool IOFile::IsOpen() const {
return file != nullptr;
}
std::string IOFile::ReadString(size_t length) const {
std::vector<char> string_buffer(length);
const auto chars_read = ReadSpan<char>(string_buffer);
const auto string_size = chars_read != length ? chars_read : length;
return std::string{string_buffer.data(), string_size};
}
size_t IOFile::WriteString(std::span<const char> string) const {
return WriteSpan(string);
}
bool IOFile::Flush() const {
if (!IsOpen()) {
return false;
}
errno = 0;
const auto flush_result = std::fflush(file) == 0;
if (!flush_result) {
const auto ec = std::error_code{errno, std::generic_category()};
LOG_ERROR(Common_Filesystem, "Failed to flush the file at path={}, ec_message={}",
PathToUTF8String(file_path), ec.message());
}
return flush_result;
}
bool IOFile::SetSize(u64 size) const {
if (!IsOpen()) {
return false;
}
errno = 0;
#ifdef _WIN32
const auto set_size_result = _chsize_s(fileno(file), static_cast<s64>(size)) == 0;
#else
const auto set_size_result = ftruncate(fileno(file), static_cast<s64>(size)) == 0;
#endif
if (!set_size_result) {
const auto ec = std::error_code{errno, std::generic_category()};
LOG_ERROR(Common_Filesystem, "Failed to resize the file at path={}, size={}, ec_message={}",
PathToUTF8String(file_path), size, ec.message());
}
return set_size_result;
}
u64 IOFile::GetSize() const {
if (!IsOpen()) {
return 0;
}
std::error_code ec;
const auto file_size = fs::file_size(file_path, ec);
if (ec) {
LOG_ERROR(Common_Filesystem, "Failed to retrieve the file size of path={}, ec_message={}",
PathToUTF8String(file_path), ec.message());
return 0;
}
return file_size;
}
bool IOFile::Seek(s64 offset, SeekOrigin origin) const {
if (!IsOpen()) {
return false;
}
errno = 0;
const auto seek_result = fseeko(file, offset, ToSeekOrigin(origin)) == 0;
if (!seek_result) {
const auto ec = std::error_code{errno, std::generic_category()};
LOG_ERROR(Common_Filesystem,
"Failed to seek the file at path={}, offset={}, origin={}, ec_message={}",
PathToUTF8String(file_path), offset, origin, ec.message());
}
return seek_result;
}
s64 IOFile::Tell() const {
if (!IsOpen()) {
return 0;
}
errno = 0;
return ftello(file);
}
} // namespace Common::FS

453
src/common/fs/file.h Normal file
View File

@@ -0,0 +1,453 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <cstdio>
#include <filesystem>
#include <fstream>
#include <span>
#include <type_traits>
#include <vector>
#include "common/concepts.h"
#include "common/fs/fs_types.h"
#include "common/fs/fs_util.h"
namespace Common::FS {
enum class SeekOrigin {
SetOrigin, // Seeks from the start of the file.
CurrentPosition, // Seeks from the current file pointer position.
End, // Seeks from the end of the file.
};
/**
* Opens a file stream at path with the specified open mode.
*
* @param file_stream Reference to file stream
* @param path Filesystem path
* @param open_mode File stream open mode
*/
template <typename FileStream>
void OpenFileStream(FileStream& file_stream, const std::filesystem::path& path,
std::ios_base::openmode open_mode) {
file_stream.open(path, open_mode);
}
#ifdef _WIN32
template <typename FileStream, typename Path>
void OpenFileStream(FileStream& file_stream, const Path& path, std::ios_base::openmode open_mode) {
if constexpr (IsChar<typename Path::value_type>) {
file_stream.open(ToU8String(path), open_mode);
} else {
file_stream.open(std::filesystem::path{path}, open_mode);
}
}
#endif
/**
* Reads an entire file at path and returns a string of the contents read from the file.
* If the filesystem object at path is not a file, this function returns an empty string.
*
* @param path Filesystem path
* @param type File type
*
* @returns A string of the contents read from the file.
*/
[[nodiscard]] std::string ReadStringFromFile(const std::filesystem::path& path, FileType type);
#ifdef _WIN32
template <typename Path>
[[nodiscard]] std::string ReadStringFromFile(const Path& path, FileType type) {
if constexpr (IsChar<typename Path::value_type>) {
return ReadStringFromFile(ToU8String(path), type);
} else {
return ReadStringFromFile(std::filesystem::path{path}, type);
}
}
#endif
/**
* Writes a string to a file at path and returns the number of characters successfully written.
* If an file already exists at path, its contents will be erased.
* If the filesystem object at path is not a file, this function returns 0.
*
* @param path Filesystem path
* @param type File type
*
* @returns Number of characters successfully written.
*/
[[nodiscard]] size_t WriteStringToFile(const std::filesystem::path& path, FileType type,
std::string_view string);
#ifdef _WIN32
template <typename Path>
[[nodiscard]] size_t WriteStringToFile(const Path& path, FileType type, std::string_view string) {
if constexpr (IsChar<typename Path::value_type>) {
return WriteStringToFile(ToU8String(path), type, string);
} else {
return WriteStringToFile(std::filesystem::path{path}, type, string);
}
}
#endif
/**
* Appends a string to a file at path and returns the number of characters successfully written.
* If a file does not exist at path, WriteStringToFile is called instead.
* If the filesystem object at path is not a file, this function returns 0.
*
* @param path Filesystem path
* @param type File type
*
* @returns Number of characters successfully written.
*/
[[nodiscard]] size_t AppendStringToFile(const std::filesystem::path& path, FileType type,
std::string_view string);
#ifdef _WIN32
template <typename Path>
[[nodiscard]] size_t AppendStringToFile(const Path& path, FileType type, std::string_view string) {
if constexpr (IsChar<typename Path::value_type>) {
return AppendStringToFile(ToU8String(path), type, string);
} else {
return AppendStringToFile(std::filesystem::path{path}, type, string);
}
}
#endif
class IOFile final {
public:
IOFile();
explicit IOFile(const std::string& path, FileAccessMode mode,
FileType type = FileType::BinaryFile,
FileShareFlag flag = FileShareFlag::ShareReadOnly);
explicit IOFile(std::string_view path, FileAccessMode mode,
FileType type = FileType::BinaryFile,
FileShareFlag flag = FileShareFlag::ShareReadOnly);
/**
* An IOFile is a lightweight wrapper on C Library file operations.
* Automatically closes an open file on the destruction of an IOFile object.
*
* @param path Filesystem path
* @param mode File access mode
* @param type File type, default is BinaryFile. Use TextFile to open the file as a text file
* @param flag (Windows only) File-share access flag, default is ShareReadOnly
*/
explicit IOFile(const std::filesystem::path& path, FileAccessMode mode,
FileType type = FileType::BinaryFile,
FileShareFlag flag = FileShareFlag::ShareReadOnly);
~IOFile();
IOFile(const IOFile&) = delete;
IOFile& operator=(const IOFile&) = delete;
IOFile(IOFile&& other) noexcept;
IOFile& operator=(IOFile&& other) noexcept;
/**
* Gets the path of the file.
*
* @returns The path of the file.
*/
[[nodiscard]] std::filesystem::path GetPath() const;
/**
* Gets the access mode of the file.
*
* @returns The access mode of the file.
*/
[[nodiscard]] FileAccessMode GetAccessMode() const;
/**
* Gets the type of the file.
*
* @returns The type of the file.
*/
[[nodiscard]] FileType GetType() const;
/**
* Opens a file at path with the specified file access mode.
* This function behaves differently depending on the FileAccessMode.
* These behaviors are documented in each enum value of FileAccessMode.
*
* @param path Filesystem path
* @param mode File access mode
* @param type File type, default is BinaryFile. Use TextFile to open the file as a text file
* @param flag (Windows only) File-share access flag, default is ShareReadOnly
*/
void Open(const std::filesystem::path& path, FileAccessMode mode,
FileType type = FileType::BinaryFile,
FileShareFlag flag = FileShareFlag::ShareReadOnly);
#ifdef _WIN32
template <typename Path>
[[nodiscard]] void Open(const Path& path, FileAccessMode mode,
FileType type = FileType::BinaryFile,
FileShareFlag flag = FileShareFlag::ShareReadOnly) {
using ValueType = typename Path::value_type;
if constexpr (IsChar<ValueType>) {
Open(ToU8String(path), mode, type, flag);
} else {
Open(std::filesystem::path{path}, mode, type, flag);
}
}
#endif
/// Closes the file if it is opened.
void Close();
/**
* Checks whether the file is open.
* Use this to check whether the calls to Open() or Close() succeeded.
*
* @returns True if the file is open, false otherwise.
*/
[[nodiscard]] bool IsOpen() const;
/**
* Helper function which deduces the value type of a contiguous STL container used in ReadSpan.
* If T is not a contiguous STL container as defined by the concept IsSTLContainer, this calls
* ReadObject and T must be a trivially copyable object.
*
* See ReadSpan for more details if T is a contiguous container.
* See ReadObject for more details if T is a trivially copyable object.
*
* @tparam T Contiguous container or trivially copyable object
*
* @param data Container of T::value_type data or reference to object
*
* @returns Count of T::value_type data or objects successfully read.
*/
template <typename T>
[[nodiscard]] size_t Read(T& data) const {
if constexpr (IsSTLContainer<T>) {
using ContiguousType = typename T::value_type;
static_assert(std::is_trivially_copyable_v<ContiguousType>,
"Data type must be trivially copyable.");
return ReadSpan<ContiguousType>(data);
} else {
return ReadObject(data) ? 1 : 0;
}
}
/**
* Helper function which deduces the value type of a contiguous STL container used in WriteSpan.
* If T is not a contiguous STL container as defined by the concept IsSTLContainer, this calls
* WriteObject and T must be a trivially copyable object.
*
* See WriteSpan for more details if T is a contiguous container.
* See WriteObject for more details if T is a trivially copyable object.
*
* @tparam T Contiguous container or trivially copyable object
*
* @param data Container of T::value_type data or const reference to object
*
* @returns Count of T::value_type data or objects successfully written.
*/
template <typename T>
[[nodiscard]] size_t Write(const T& data) const {
if constexpr (IsSTLContainer<T>) {
using ContiguousType = typename T::value_type;
static_assert(std::is_trivially_copyable_v<ContiguousType>,
"Data type must be trivially copyable.");
return WriteSpan<ContiguousType>(data);
} else {
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
return WriteObject(data) ? 1 : 0;
}
}
/**
* Reads a span of T data from a file sequentially.
* This function reads from the current position of the file pointer and
* advances it by the (count of T * sizeof(T)) bytes successfully read.
*
* Failures occur when:
* - The file is not open
* - The opened file lacks read permissions
* - Attempting to read beyond the end-of-file
*
* @tparam T Data type
*
* @param data Span of T data
*
* @returns Count of T data successfully read.
*/
template <typename T>
[[nodiscard]] size_t ReadSpan(std::span<T> data) const {
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
if (!IsOpen()) {
return 0;
}
return std::fread(data.data(), sizeof(T), data.size(), file);
}
/**
* Writes a span of T data to a file sequentially.
* This function writes from the current position of the file pointer and
* advances it by the (count of T * sizeof(T)) bytes successfully written.
*
* Failures occur when:
* - The file is not open
* - The opened file lacks write permissions
*
* @tparam T Data type
*
* @param data Span of T data
*
* @returns Count of T data successfully written.
*/
template <typename T>
[[nodiscard]] size_t WriteSpan(std::span<const T> data) const {
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
if (!IsOpen()) {
return 0;
}
return std::fwrite(data.data(), sizeof(T), data.size(), file);
}
/**
* Reads a T object from a file sequentially.
* This function reads from the current position of the file pointer and
* advances it by the sizeof(T) bytes successfully read.
*
* Failures occur when:
* - The file is not open
* - The opened file lacks read permissions
* - Attempting to read beyond the end-of-file
*
* @tparam T Data type
*
* @param object Reference to object
*
* @returns True if the object is successfully read from the file, false otherwise.
*/
template <typename T>
[[nodiscard]] bool ReadObject(T& object) const {
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
static_assert(!std::is_pointer_v<T>, "T must not be a pointer to an object.");
if (!IsOpen()) {
return false;
}
return std::fread(&object, sizeof(T), 1, file) == 1;
}
/**
* Writes a T object to a file sequentially.
* This function writes from the current position of the file pointer and
* advances it by the sizeof(T) bytes successfully written.
*
* Failures occur when:
* - The file is not open
* - The opened file lacks write permissions
*
* @tparam T Data type
*
* @param object Const reference to object
*
* @returns True if the object is successfully written to the file, false otherwise.
*/
template <typename T>
[[nodiscard]] bool WriteObject(const T& object) const {
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
static_assert(!std::is_pointer_v<T>, "T must not be a pointer to an object.");
if (!IsOpen()) {
return false;
}
return std::fwrite(&object, sizeof(T), 1, file) == 1;
}
/**
* Specialized function to read a string of a given length from a file sequentially.
* This function writes from the current position of the file pointer and
* advances it by the number of characters successfully read.
* The size of the returned string may not match length if not all bytes are successfully read.
*
* @param length Length of the string
*
* @returns A string read from the file.
*/
[[nodiscard]] std::string ReadString(size_t length) const;
/**
* Specialized function to write a string to a file sequentially.
* This function writes from the current position of the file pointer and
* advances it by the number of characters successfully written.
*
* @param string Span of const char backed std::string or std::string_view
*
* @returns Number of characters successfully written.
*/
[[nodiscard]] size_t WriteString(std::span<const char> string) const;
/**
* Flushes any unwritten buffered data into the file.
*
* @returns True if the flush was successful, false otherwise.
*/
[[nodiscard]] bool Flush() const;
/**
* Resizes the file to a given size.
* If the file is resized to a smaller size, the remainder of the file is discarded.
* If the file is resized to a larger size, the new area appears as if zero-filled.
*
* Failures occur when:
* - The file is not open
*
* @param size File size in bytes
*
* @returns True if the file resize succeeded, false otherwise.
*/
[[nodiscard]] bool SetSize(u64 size) const;
/**
* Gets the size of the file.
*
* Failures occur when:
* - The file is not open
*
* @returns The file size in bytes of the file. Returns 0 on failure.
*/
[[nodiscard]] u64 GetSize() const;
/**
* Moves the current position of the file pointer with the specified offset and seek origin.
*
* @param offset Offset from seek origin
* @param origin Seek origin
*
* @returns True if the file pointer has moved to the specified offset, false otherwise.
*/
[[nodiscard]] bool Seek(s64 offset, SeekOrigin origin = SeekOrigin::SetOrigin) const;
/**
* Gets the current position of the file pointer.
*
* @returns The current position of the file pointer.
*/
[[nodiscard]] s64 Tell() const;
private:
std::filesystem::path file_path;
FileAccessMode file_access_mode{};
FileType file_type{};
std::FILE* file = nullptr;
};
} // namespace Common::FS

610
src/common/fs/fs.cpp Normal file
View File

@@ -0,0 +1,610 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/fs/file.h"
#include "common/fs/fs.h"
#include "common/fs/path_util.h"
#include "common/logging/log.h"
namespace Common::FS {
namespace fs = std::filesystem;
// File Operations
bool NewFile(const fs::path& path, u64 size) {
if (!ValidatePath(path)) {
LOG_ERROR(Common_Filesystem, "Input path is not valid, path={}", PathToUTF8String(path));
return false;
}
if (!Exists(path.parent_path())) {
LOG_ERROR(Common_Filesystem, "Parent directory of path={} does not exist",
PathToUTF8String(path));
return false;
}
if (Exists(path)) {
LOG_ERROR(Common_Filesystem, "Filesystem object at path={} exists", PathToUTF8String(path));
return false;
}
IOFile io_file{path, FileAccessMode::Write};
if (!io_file.IsOpen()) {
LOG_ERROR(Common_Filesystem, "Failed to create a file at path={}", PathToUTF8String(path));
return false;
}
if (!io_file.SetSize(size)) {
LOG_ERROR(Common_Filesystem, "Failed to resize the file at path={} to size={}",
PathToUTF8String(path), size);
return false;
}
io_file.Close();
LOG_DEBUG(Common_Filesystem, "Successfully created a file at path={} with size={}",
PathToUTF8String(path), size);
return true;
}
bool RemoveFile(const fs::path& path) {
if (!ValidatePath(path)) {
LOG_ERROR(Common_Filesystem, "Input path is not valid, path={}", PathToUTF8String(path));
return false;
}
if (!Exists(path)) {
LOG_DEBUG(Common_Filesystem, "Filesystem object at path={} does not exist",
PathToUTF8String(path));
return true;
}
if (!IsFile(path)) {
LOG_ERROR(Common_Filesystem, "Filesystem object at path={} is not a file",
PathToUTF8String(path));
return false;
}
std::error_code ec;
fs::remove(path, ec);
if (ec) {
LOG_ERROR(Common_Filesystem, "Failed to remove the file at path={}, ec_message={}",
PathToUTF8String(path), ec.message());
return false;
}
LOG_DEBUG(Common_Filesystem, "Successfully removed the file at path={}",
PathToUTF8String(path));
return true;
}
bool RenameFile(const fs::path& old_path, const fs::path& new_path) {
if (!ValidatePath(old_path) || !ValidatePath(new_path)) {
LOG_ERROR(Common_Filesystem,
"One or both input path(s) is not valid, old_path={}, new_path={}",
PathToUTF8String(old_path), PathToUTF8String(new_path));
return false;
}
if (!Exists(old_path)) {
LOG_ERROR(Common_Filesystem, "Filesystem object at old_path={} does not exist",
PathToUTF8String(old_path));
return false;
}
if (!IsFile(old_path)) {
LOG_ERROR(Common_Filesystem, "Filesystem object at old_path={} is not a file",
PathToUTF8String(old_path));
return false;
}
if (Exists(new_path)) {
LOG_ERROR(Common_Filesystem, "Filesystem object at new_path={} exists",
PathToUTF8String(new_path));
return false;
}
std::error_code ec;
fs::rename(old_path, new_path, ec);
if (ec) {
LOG_ERROR(Common_Filesystem,
"Failed to rename the file from old_path={} to new_path={}, ec_message={}",
PathToUTF8String(old_path), PathToUTF8String(new_path), ec.message());
return false;
}
LOG_DEBUG(Common_Filesystem, "Successfully renamed the file from old_path={} to new_path={}",
PathToUTF8String(old_path), PathToUTF8String(new_path));
return true;
}
std::shared_ptr<IOFile> FileOpen(const fs::path& path, FileAccessMode mode, FileType type,
FileShareFlag flag) {
if (!ValidatePath(path)) {
LOG_ERROR(Common_Filesystem, "Input path is not valid, path={}", PathToUTF8String(path));
return nullptr;
}
if (!IsFile(path)) {
LOG_ERROR(Common_Filesystem, "Filesystem object at path={} is not a file",
PathToUTF8String(path));
return nullptr;
}
auto io_file = std::make_shared<IOFile>(path, mode, type, flag);
if (!io_file->IsOpen()) {
io_file.reset();
LOG_ERROR(Common_Filesystem,
"Failed to open the file at path={} with mode={}, type={}, flag={}",
PathToUTF8String(path), mode, type, flag);
return nullptr;
}
LOG_DEBUG(Common_Filesystem,
"Successfully opened the file at path={} with mode={}, type={}, flag={}",
PathToUTF8String(path), mode, type, flag);
return io_file;
}
// Directory Operations
bool CreateDir(const fs::path& path) {
if (!ValidatePath(path)) {
LOG_ERROR(Common_Filesystem, "Input path is not valid, path={}", PathToUTF8String(path));
return false;
}
if (!Exists(path.parent_path())) {
LOG_ERROR(Common_Filesystem, "Parent directory of path={} does not exist",
PathToUTF8String(path));
return false;
}
if (IsDir(path)) {
LOG_DEBUG(Common_Filesystem, "Filesystem object at path={} exists and is a directory",
PathToUTF8String(path));
return true;
}
std::error_code ec;
fs::create_directory(path, ec);
if (ec) {
LOG_ERROR(Common_Filesystem, "Failed to create the directory at path={}, ec_message={}",
PathToUTF8String(path), ec.message());
return false;
}
LOG_DEBUG(Common_Filesystem, "Successfully created the directory at path={}",
PathToUTF8String(path));
return true;
}
bool CreateDirs(const fs::path& path) {
if (!ValidatePath(path)) {
LOG_ERROR(Common_Filesystem, "Input path is not valid, path={}", PathToUTF8String(path));
return false;
}
if (IsDir(path)) {
LOG_DEBUG(Common_Filesystem, "Filesystem object at path={} exists and is a directory",
PathToUTF8String(path));
return true;
}
std::error_code ec;
fs::create_directories(path, ec);
if (ec) {
LOG_ERROR(Common_Filesystem, "Failed to create the directories at path={}, ec_message={}",
PathToUTF8String(path), ec.message());
return false;
}
LOG_DEBUG(Common_Filesystem, "Successfully created the directories at path={}",
PathToUTF8String(path));
return true;
}
bool CreateParentDir(const fs::path& path) {
return CreateDir(path.parent_path());
}
bool CreateParentDirs(const fs::path& path) {
return CreateDirs(path.parent_path());
}
bool RemoveDir(const fs::path& path) {
if (!ValidatePath(path)) {
LOG_ERROR(Common_Filesystem, "Input path is not valid, path={}", PathToUTF8String(path));
return false;
}
if (!Exists(path)) {
LOG_DEBUG(Common_Filesystem, "Filesystem object at path={} does not exist",
PathToUTF8String(path));
return true;
}
if (!IsDir(path)) {
LOG_ERROR(Common_Filesystem, "Filesystem object at path={} is not a directory",
PathToUTF8String(path));
return false;
}
std::error_code ec;
fs::remove(path, ec);
if (ec) {
LOG_ERROR(Common_Filesystem, "Failed to remove the directory at path={}, ec_message={}",
PathToUTF8String(path), ec.message());
return false;
}
LOG_DEBUG(Common_Filesystem, "Successfully removed the directory at path={}",
PathToUTF8String(path));
return true;
}
bool RemoveDirRecursively(const fs::path& path) {
if (!ValidatePath(path)) {
LOG_ERROR(Common_Filesystem, "Input path is not valid, path={}", PathToUTF8String(path));
return false;
}
if (!Exists(path)) {
LOG_DEBUG(Common_Filesystem, "Filesystem object at path={} does not exist",
PathToUTF8String(path));
return true;
}
if (!IsDir(path)) {
LOG_ERROR(Common_Filesystem, "Filesystem object at path={} is not a directory",
PathToUTF8String(path));
return false;
}
std::error_code ec;
fs::remove_all(path, ec);
if (ec) {
LOG_ERROR(Common_Filesystem,
"Failed to remove the directory and its contents at path={}, ec_message={}",
PathToUTF8String(path), ec.message());
return false;
}
LOG_DEBUG(Common_Filesystem, "Successfully removed the directory and its contents at path={}",
PathToUTF8String(path));
return true;
}
bool RemoveDirContentsRecursively(const fs::path& path) {
if (!ValidatePath(path)) {
LOG_ERROR(Common_Filesystem, "Input path is not valid, path={}", PathToUTF8String(path));
return false;
}
if (!Exists(path)) {
LOG_DEBUG(Common_Filesystem, "Filesystem object at path={} does not exist",
PathToUTF8String(path));
return true;
}
if (!IsDir(path)) {
LOG_ERROR(Common_Filesystem, "Filesystem object at path={} is not a directory",
PathToUTF8String(path));
return false;
}
std::error_code ec;
for (const auto& entry : fs::recursive_directory_iterator(path, ec)) {
if (ec) {
LOG_ERROR(Common_Filesystem,
"Failed to completely enumerate the directory at path={}, ec_message={}",
PathToUTF8String(path), ec.message());
break;
}
fs::remove(entry.path(), ec);
if (ec) {
LOG_ERROR(Common_Filesystem,
"Failed to remove the filesystem object at path={}, ec_message={}",
PathToUTF8String(entry.path()), ec.message());
break;
}
}
if (ec) {
LOG_ERROR(Common_Filesystem,
"Failed to remove all the contents of the directory at path={}, ec_message={}",
PathToUTF8String(path), ec.message());
return false;
}
LOG_DEBUG(Common_Filesystem,
"Successfully removed all the contents of the directory at path={}",
PathToUTF8String(path));
return true;
}
bool RenameDir(const fs::path& old_path, const fs::path& new_path) {
if (!ValidatePath(old_path) || !ValidatePath(new_path)) {
LOG_ERROR(Common_Filesystem,
"One or both input path(s) is not valid, old_path={}, new_path={}",
PathToUTF8String(old_path), PathToUTF8String(new_path));
return false;
}
if (!Exists(old_path)) {
LOG_ERROR(Common_Filesystem, "Filesystem object at old_path={} does not exist",
PathToUTF8String(old_path));
return false;
}
if (!IsDir(old_path)) {
LOG_ERROR(Common_Filesystem, "Filesystem object at old_path={} is not a directory",
PathToUTF8String(old_path));
return false;
}
if (Exists(new_path)) {
LOG_ERROR(Common_Filesystem, "Filesystem object at new_path={} exists",
PathToUTF8String(new_path));
return false;
}
std::error_code ec;
fs::rename(old_path, new_path, ec);
if (ec) {
LOG_ERROR(Common_Filesystem,
"Failed to rename the file from old_path={} to new_path={}, ec_message={}",
PathToUTF8String(old_path), PathToUTF8String(new_path), ec.message());
return false;
}
LOG_DEBUG(Common_Filesystem, "Successfully renamed the file from old_path={} to new_path={}",
PathToUTF8String(old_path), PathToUTF8String(new_path));
return true;
}
void IterateDirEntries(const std::filesystem::path& path, const DirEntryCallable& callback,
DirEntryFilter filter) {
if (!ValidatePath(path)) {
LOG_ERROR(Common_Filesystem, "Input path is not valid, path={}", PathToUTF8String(path));
return;
}
if (!Exists(path)) {
LOG_ERROR(Common_Filesystem, "Filesystem object at path={} does not exist",
PathToUTF8String(path));
return;
}
if (!IsDir(path)) {
LOG_ERROR(Common_Filesystem, "Filesystem object at path={} is not a directory",
PathToUTF8String(path));
return;
}
bool callback_error = false;
std::error_code ec;
for (const auto& entry : fs::directory_iterator(path, ec)) {
if (ec) {
break;
}
if (True(filter & DirEntryFilter::File) &&
entry.status().type() == fs::file_type::regular) {
if (!callback(entry.path())) {
callback_error = true;
break;
}
}
if (True(filter & DirEntryFilter::Directory) &&
entry.status().type() == fs::file_type::directory) {
if (!callback(entry.path())) {
callback_error = true;
break;
}
}
}
if (callback_error || ec) {
LOG_ERROR(Common_Filesystem,
"Failed to visit all the directory entries of path={}, ec_message={}",
PathToUTF8String(path), ec.message());
return;
}
LOG_DEBUG(Common_Filesystem, "Successfully visited all the directory entries of path={}",
PathToUTF8String(path));
}
void IterateDirEntriesRecursively(const std::filesystem::path& path,
const DirEntryCallable& callback, DirEntryFilter filter) {
if (!ValidatePath(path)) {
LOG_ERROR(Common_Filesystem, "Input path is not valid, path={}", PathToUTF8String(path));
return;
}
if (!Exists(path)) {
LOG_ERROR(Common_Filesystem, "Filesystem object at path={} does not exist",
PathToUTF8String(path));
return;
}
if (!IsDir(path)) {
LOG_ERROR(Common_Filesystem, "Filesystem object at path={} is not a directory",
PathToUTF8String(path));
return;
}
bool callback_error = false;
std::error_code ec;
for (const auto& entry : fs::recursive_directory_iterator(path, ec)) {
if (ec) {
break;
}
if (True(filter & DirEntryFilter::File) &&
entry.status().type() == fs::file_type::regular) {
if (!callback(entry.path())) {
callback_error = true;
break;
}
}
if (True(filter & DirEntryFilter::Directory) &&
entry.status().type() == fs::file_type::directory) {
if (!callback(entry.path())) {
callback_error = true;
break;
}
}
}
if (callback_error || ec) {
LOG_ERROR(Common_Filesystem,
"Failed to visit all the directory entries of path={}, ec_message={}",
PathToUTF8String(path), ec.message());
return;
}
LOG_DEBUG(Common_Filesystem, "Successfully visited all the directory entries of path={}",
PathToUTF8String(path));
}
// Generic Filesystem Operations
bool Exists(const fs::path& path) {
return fs::exists(path);
}
bool IsFile(const fs::path& path) {
return fs::is_regular_file(path);
}
bool IsDir(const fs::path& path) {
return fs::is_directory(path);
}
fs::path GetCurrentDir() {
std::error_code ec;
const auto current_path = fs::current_path(ec);
if (ec) {
LOG_ERROR(Common_Filesystem, "Failed to get the current path, ec_message={}", ec.message());
return {};
}
return current_path;
}
bool SetCurrentDir(const fs::path& path) {
std::error_code ec;
fs::current_path(path, ec);
if (ec) {
LOG_ERROR(Common_Filesystem, "Failed to set the current path to path={}, ec_message={}",
PathToUTF8String(path), ec.message());
return false;
}
return true;
}
fs::file_type GetEntryType(const fs::path& path) {
std::error_code ec;
const auto file_status = fs::status(path, ec);
if (ec) {
LOG_ERROR(Common_Filesystem, "Failed to retrieve the entry type of path={}, ec_message={}",
PathToUTF8String(path), ec.message());
return fs::file_type::not_found;
}
return file_status.type();
}
u64 GetSize(const fs::path& path) {
std::error_code ec;
const auto file_size = fs::file_size(path, ec);
if (ec) {
LOG_ERROR(Common_Filesystem, "Failed to retrieve the file size of path={}, ec_message={}",
PathToUTF8String(path), ec.message());
return 0;
}
return file_size;
}
u64 GetFreeSpaceSize(const fs::path& path) {
std::error_code ec;
const auto space_info = fs::space(path, ec);
if (ec) {
LOG_ERROR(Common_Filesystem,
"Failed to retrieve the available free space of path={}, ec_message={}",
PathToUTF8String(path), ec.message());
return 0;
}
return space_info.free;
}
u64 GetTotalSpaceSize(const fs::path& path) {
std::error_code ec;
const auto space_info = fs::space(path, ec);
if (ec) {
LOG_ERROR(Common_Filesystem,
"Failed to retrieve the total capacity of path={}, ec_message={}",
PathToUTF8String(path), ec.message());
return 0;
}
return space_info.capacity;
}
} // namespace Common::FS

582
src/common/fs/fs.h Normal file
View File

@@ -0,0 +1,582 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <filesystem>
#include <memory>
#include "common/fs/fs_types.h"
#include "common/fs/fs_util.h"
namespace Common::FS {
class IOFile;
// File Operations
/**
* Creates a new file at path with the specified size.
*
* Failures occur when:
* - Input path is not valid
* - The input path's parent directory does not exist
* - Filesystem object at path exists
* - Filesystem at path is read only
*
* @param path Filesystem path
* @param size File size
*
* @returns True if the file creation succeeds, false otherwise.
*/
[[nodiscard]] bool NewFile(const std::filesystem::path& path, u64 size = 0);
#ifdef _WIN32
template <typename Path>
[[nodiscard]] bool NewFile(const Path& path, u64 size = 0) {
if constexpr (IsChar<typename Path::value_type>) {
return NewFile(ToU8String(path), size);
} else {
return NewFile(std::filesystem::path{path}, size);
}
}
#endif
/**
* Removes a file at path.
*
* Failures occur when:
* - Input path is not valid
* - Filesystem object at path is not a file
* - Filesystem at path is read only
*
* @param path Filesystem path
*
* @returns True if file removal succeeds or file does not exist, false otherwise.
*/
[[nodiscard]] bool RemoveFile(const std::filesystem::path& path);
#ifdef _WIN32
template <typename Path>
[[nodiscard]] bool RemoveFile(const Path& path) {
if constexpr (IsChar<typename Path::value_type>) {
return RemoveFile(ToU8String(path));
} else {
return RemoveFile(std::filesystem::path{path});
}
}
#endif
/**
* Renames a file from old_path to new_path.
*
* Failures occur when:
* - One or both input path(s) is not valid
* - Filesystem object at old_path does not exist
* - Filesystem object at old_path is not a file
* - Filesystem object at new_path exists
* - Filesystem at either path is read only
*
* @param old_path Old filesystem path
* @param new_path New filesystem path
*
* @returns True if file rename succeeds, false otherwise.
*/
[[nodiscard]] bool RenameFile(const std::filesystem::path& old_path,
const std::filesystem::path& new_path);
#ifdef _WIN32
template <typename Path1, typename Path2>
[[nodiscard]] bool RenameFile(const Path1& old_path, const Path2& new_path) {
using ValueType1 = typename Path1::value_type;
using ValueType2 = typename Path2::value_type;
if constexpr (IsChar<ValueType1> && IsChar<ValueType2>) {
return RenameFile(ToU8String(old_path), ToU8String(new_path));
} else if constexpr (IsChar<ValueType1> && !IsChar<ValueType2>) {
return RenameFile(ToU8String(old_path), new_path);
} else if constexpr (!IsChar<ValueType1> && IsChar<ValueType2>) {
return RenameFile(old_path, ToU8String(new_path));
} else {
return RenameFile(std::filesystem::path{old_path}, std::filesystem::path{new_path});
}
}
#endif
/**
* Opens a file at path with the specified file access mode.
* This function behaves differently depending on the FileAccessMode.
* These behaviors are documented in each enum value of FileAccessMode.
*
* Failures occur when:
* - Input path is not valid
* - Filesystem object at path is not a file
* - The file is not opened
*
* @param path Filesystem path
* @param mode File access mode
* @param type File type, default is BinaryFile. Use TextFile to open the file as a text file
* @param flag (Windows only) File-share access flag, default is ShareReadOnly
*
* @returns A shared pointer to the opened file. Returns nullptr on failure.
*/
[[nodiscard]] std::shared_ptr<IOFile> FileOpen(const std::filesystem::path& path,
FileAccessMode mode,
FileType type = FileType::BinaryFile,
FileShareFlag flag = FileShareFlag::ShareReadOnly);
#ifdef _WIN32
template <typename Path>
[[nodiscard]] std::shared_ptr<IOFile> FileOpen(const Path& path, FileAccessMode mode,
FileType type = FileType::BinaryFile,
FileShareFlag flag = FileShareFlag::ShareReadOnly) {
if constexpr (IsChar<typename Path::value_type>) {
return FileOpen(ToU8String(path), mode, type, flag);
} else {
return FileOpen(std::filesystem::path{path}, mode, type, flag);
}
}
#endif
// Directory Operations
/**
* Creates a directory at path.
* Note that this function will *always* assume that the input path is a directory. For example,
* if the input path is /path/to/directory/file.txt, it will create a directory called "file.txt".
* If you intend to create the parent directory of a file, use CreateParentDir instead.
*
* Failures occur when:
* - Input path is not valid
* - The input path's parent directory does not exist
* - Filesystem at path is read only
*
* @param path Filesystem path
*
* @returns True if directory creation succeeds or directory already exists, false otherwise.
*/
[[nodiscard]] bool CreateDir(const std::filesystem::path& path);
#ifdef _WIN32
template <typename Path>
[[nodiscard]] bool CreateDir(const Path& path) {
if constexpr (IsChar<typename Path::value_type>) {
return CreateDir(ToU8String(path));
} else {
return CreateDir(std::filesystem::path{path});
}
}
#endif
/**
* Recursively creates a directory at path.
* Note that this function will *always* assume that the input path is a directory. For example,
* if the input path is /path/to/directory/file.txt, it will create a directory called "file.txt".
* If you intend to create the parent directory of a file, use CreateParentDirs instead.
* Unlike CreateDir, this creates all of input path's parent directories if they do not exist.
*
* Failures occur when:
* - Input path is not valid
* - Filesystem at path is read only
*
* @param path Filesystem path
*
* @returns True if directory creation succeeds or directory already exists, false otherwise.
*/
[[nodiscard]] bool CreateDirs(const std::filesystem::path& path);
#ifdef _WIN32
template <typename Path>
[[nodiscard]] bool CreateDirs(const Path& path) {
if constexpr (IsChar<typename Path::value_type>) {
return CreateDirs(ToU8String(path));
} else {
return CreateDirs(std::filesystem::path{path});
}
}
#endif
/**
* Creates the parent directory of a given path.
* This function calls CreateDir(path.parent_path()), see CreateDir for more details.
*
* @param path Filesystem path
*
* @returns True if directory creation succeeds or directory already exists, false otherwise.
*/
[[nodiscard]] bool CreateParentDir(const std::filesystem::path& path);
#ifdef _WIN32
template <typename Path>
[[nodiscard]] bool CreateParentDir(const Path& path) {
if constexpr (IsChar<typename Path::value_type>) {
return CreateParentDir(ToU8String(path));
} else {
return CreateParentDir(std::filesystem::path{path});
}
}
#endif
/**
* Recursively creates the parent directory of a given path.
* This function calls CreateDirs(path.parent_path()), see CreateDirs for more details.
*
* @param path Filesystem path
*
* @returns True if directory creation succeeds or directory already exists, false otherwise.
*/
[[nodiscard]] bool CreateParentDirs(const std::filesystem::path& path);
#ifdef _WIN32
template <typename Path>
[[nodiscard]] bool CreateParentDirs(const Path& path) {
if constexpr (IsChar<typename Path::value_type>) {
return CreateParentDirs(ToU8String(path));
} else {
return CreateParentDirs(std::filesystem::path{path});
}
}
#endif
/**
* Removes a directory at path.
*
* Failures occur when:
* - Input path is not valid
* - Filesystem object at path is not a directory
* - The given directory is not empty
* - Filesystem at path is read only
*
* @param path Filesystem path
*
* @returns True if directory removal succeeds or directory does not exist, false otherwise.
*/
[[nodiscard]] bool RemoveDir(const std::filesystem::path& path);
#ifdef _WIN32
template <typename Path>
[[nodiscard]] bool RemoveDir(const Path& path) {
if constexpr (IsChar<typename Path::value_type>) {
return RemoveDir(ToU8String(path));
} else {
return RemoveDir(std::filesystem::path{path});
}
}
#endif
/**
* Removes all the contents within the given directory and removes the directory itself.
*
* Failures occur when:
* - Input path is not valid
* - Filesystem object at path is not a directory
* - Filesystem at path is read only
*
* @param path Filesystem path
*
* @returns True if the directory and all of its contents are removed successfully, false otherwise.
*/
[[nodiscard]] bool RemoveDirRecursively(const std::filesystem::path& path);
#ifdef _WIN32
template <typename Path>
[[nodiscard]] bool RemoveDirRecursively(const Path& path) {
if constexpr (IsChar<typename Path::value_type>) {
return RemoveDirRecursively(ToU8String(path));
} else {
return RemoveDirRecursively(std::filesystem::path{path});
}
}
#endif
/**
* Removes all the contents within the given directory without removing the directory itself.
*
* Failures occur when:
* - Input path is not valid
* - Filesystem object at path is not a directory
* - Filesystem at path is read only
*
* @param path Filesystem path
*
* @returns True if all of the directory's contents are removed successfully, false otherwise.
*/
[[nodiscard]] bool RemoveDirContentsRecursively(const std::filesystem::path& path);
#ifdef _WIN32
template <typename Path>
[[nodiscard]] bool RemoveDirContentsRecursively(const Path& path) {
if constexpr (IsChar<typename Path::value_type>) {
return RemoveDirContentsRecursively(ToU8String(path));
} else {
return RemoveDirContentsRecursively(std::filesystem::path{path});
}
}
#endif
/**
* Renames a directory from old_path to new_path.
*
* Failures occur when:
* - One or both input path(s) is not valid
* - Filesystem object at old_path does not exist
* - Filesystem object at old_path is not a directory
* - Filesystem object at new_path exists
* - Filesystem at either path is read only
*
* @param old_path Old filesystem path
* @param new_path New filesystem path
*
* @returns True if directory rename succeeds, false otherwise.
*/
[[nodiscard]] bool RenameDir(const std::filesystem::path& old_path,
const std::filesystem::path& new_path);
#ifdef _WIN32
template <typename Path1, typename Path2>
[[nodiscard]] bool RenameDir(const Path1& old_path, const Path2& new_path) {
using ValueType1 = typename Path1::value_type;
using ValueType2 = typename Path2::value_type;
if constexpr (IsChar<ValueType1> && IsChar<ValueType2>) {
return RenameDir(ToU8String(old_path), ToU8String(new_path));
} else if constexpr (IsChar<ValueType1> && !IsChar<ValueType2>) {
return RenameDir(ToU8String(old_path), new_path);
} else if constexpr (!IsChar<ValueType1> && IsChar<ValueType2>) {
return RenameDir(old_path, ToU8String(new_path));
} else {
return RenameDir(std::filesystem::path{old_path}, std::filesystem::path{new_path});
}
}
#endif
/**
* Iterates over the directory entries of a given directory.
* This does not iterate over the sub-directories of the given directory.
* The DirEntryCallable callback is called for each visited directory entry.
* A filter can be set to control which directory entries are visited based on their type.
* By default, both files and directories are visited.
* If the callback returns false or there is an error, the iteration is immediately halted.
*
* Failures occur when:
* - Input path is not valid
* - Filesystem object at path is not a directory
*
* @param path Filesystem path
* @param callback Callback to be called for each visited directory entry
* @param filter Directory entry type filter
*/
void IterateDirEntries(const std::filesystem::path& path, const DirEntryCallable& callback,
DirEntryFilter filter = DirEntryFilter::All);
#ifdef _WIN32
template <typename Path>
void IterateDirEntries(const Path& path, const DirEntryCallable& callback,
DirEntryFilter filter = DirEntryFilter::All) {
if constexpr (IsChar<typename Path::value_type>) {
IterateDirEntries(ToU8String(path), callback, filter);
} else {
IterateDirEntries(std::filesystem::path{path}, callback, filter);
}
}
#endif
/**
* Iterates over the directory entries of a given directory and its sub-directories.
* The DirEntryCallable callback is called for each visited directory entry.
* A filter can be set to control which directory entries are visited based on their type.
* By default, both files and directories are visited.
* If the callback returns false or there is an error, the iteration is immediately halted.
*
* Failures occur when:
* - Input path is not valid
* - Filesystem object at path does not exist
* - Filesystem object at path is not a directory
*
* @param path Filesystem path
* @param callback Callback to be called for each visited directory entry
* @param filter Directory entry type filter
*/
void IterateDirEntriesRecursively(const std::filesystem::path& path,
const DirEntryCallable& callback,
DirEntryFilter filter = DirEntryFilter::All);
#ifdef _WIN32
template <typename Path>
void IterateDirEntriesRecursively(const Path& path, const DirEntryCallable& callback,
DirEntryFilter filter = DirEntryFilter::All) {
if constexpr (IsChar<typename Path::value_type>) {
IterateDirEntriesRecursively(ToU8String(path), callback, filter);
} else {
IterateDirEntriesRecursively(std::filesystem::path{path}, callback, filter);
}
}
#endif
// Generic Filesystem Operations
/**
* Returns whether a filesystem object at path exists.
*
* @param path Filesystem path
*
* @returns True if a filesystem object at path exists, false otherwise.
*/
[[nodiscard]] bool Exists(const std::filesystem::path& path);
#ifdef _WIN32
template <typename Path>
[[nodiscard]] bool Exists(const Path& path) {
if constexpr (IsChar<typename Path::value_type>) {
return Exists(ToU8String(path));
} else {
return Exists(std::filesystem::path{path});
}
}
#endif
/**
* Returns whether a filesystem object at path is a file.
*
* @param path Filesystem path
*
* @returns True if a filesystem object at path is a file, false otherwise.
*/
[[nodiscard]] bool IsFile(const std::filesystem::path& path);
#ifdef _WIN32
template <typename Path>
[[nodiscard]] bool IsFile(const Path& path) {
if constexpr (IsChar<typename Path::value_type>) {
return IsFile(ToU8String(path));
} else {
return IsFile(std::filesystem::path{path});
}
}
#endif
/**
* Returns whether a filesystem object at path is a directory.
*
* @param path Filesystem path
*
* @returns True if a filesystem object at path is a directory, false otherwise.
*/
[[nodiscard]] bool IsDir(const std::filesystem::path& path);
#ifdef _WIN32
template <typename Path>
[[nodiscard]] bool IsDir(const Path& path) {
if constexpr (IsChar<typename Path::value_type>) {
return IsDir(ToU8String(path));
} else {
return IsDir(std::filesystem::path{path});
}
}
#endif
/**
* Gets the current working directory.
*
* @returns The current working directory. Returns an empty path on failure.
*/
[[nodiscard]] std::filesystem::path GetCurrentDir();
/**
* Sets the current working directory to path.
*
* @returns True if the current working directory is successfully set, false otherwise.
*/
[[nodiscard]] bool SetCurrentDir(const std::filesystem::path& path);
#ifdef _WIN32
template <typename Path>
[[nodiscard]] bool SetCurrentDir(const Path& path) {
if constexpr (IsChar<typename Path::value_type>) {
return SetCurrentDir(ToU8String(path));
} else {
return SetCurrentDir(std::filesystem::path{path});
}
}
#endif
/**
* Gets the entry type of the filesystem object at path.
*
* @param path Filesystem path
*
* @returns The entry type of the filesystem object. Returns file_type::not_found on failure.
*/
[[nodiscard]] std::filesystem::file_type GetEntryType(const std::filesystem::path& path);
#ifdef _WIN32
template <typename Path>
[[nodiscard]] std::filesystem::file_type GetEntryType(const Path& path) {
if constexpr (IsChar<typename Path::value_type>) {
return GetEntryType(ToU8String(path));
} else {
return GetEntryType(std::filesystem::path{path});
}
}
#endif
/**
* Gets the size of the filesystem object at path.
*
* @param path Filesystem path
*
* @returns The size in bytes of the filesystem object. Returns 0 on failure.
*/
[[nodiscard]] u64 GetSize(const std::filesystem::path& path);
#ifdef _WIN32
template <typename Path>
[[nodiscard]] u64 GetSize(const Path& path) {
if constexpr (IsChar<typename Path::value_type>) {
return GetSize(ToU8String(path));
} else {
return GetSize(std::filesystem::path{path});
}
}
#endif
/**
* Gets the free space size of the filesystem at path.
*
* @param path Filesystem path
*
* @returns The free space size in bytes of the filesystem at path. Returns 0 on failure.
*/
[[nodiscard]] u64 GetFreeSpaceSize(const std::filesystem::path& path);
#ifdef _WIN32
template <typename Path>
[[nodiscard]] u64 GetFreeSpaceSize(const Path& path) {
if constexpr (IsChar<typename Path::value_type>) {
return GetFreeSpaceSize(ToU8String(path));
} else {
return GetFreeSpaceSize(std::filesystem::path{path});
}
}
#endif
/**
* Gets the total capacity of the filesystem at path.
*
* @param path Filesystem path
*
* @returns The total capacity in bytes of the filesystem at path. Returns 0 on failure.
*/
[[nodiscard]] u64 GetTotalSpaceSize(const std::filesystem::path& path);
#ifdef _WIN32
template <typename Path>
[[nodiscard]] u64 GetTotalSpaceSize(const Path& path) {
if constexpr (IsChar<typename Path::value_type>) {
return GetTotalSpaceSize(ToU8String(path));
} else {
return GetTotalSpaceSize(std::filesystem::path{path});
}
}
#endif
} // namespace Common::FS

27
src/common/fs/fs_paths.h Normal file
View File

@@ -0,0 +1,27 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
// yuzu data directories
#define YUZU_DIR "yuzu"
#define PORTABLE_DIR "user"
// Sub-directories contained within a yuzu data directory
#define CACHE_DIR "cache"
#define CONFIG_DIR "config"
#define DUMP_DIR "dump"
#define KEYS_DIR "keys"
#define LOAD_DIR "load"
#define LOG_DIR "log"
#define NAND_DIR "nand"
#define SCREENSHOTS_DIR "screenshots"
#define SDMC_DIR "sdmc"
#define SHADER_DIR "shader"
// yuzu-specific files
#define LOG_FILE "yuzu_log.txt"

73
src/common/fs/fs_types.h Normal file
View File

@@ -0,0 +1,73 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <functional>
#include "common/common_funcs.h"
#include "common/common_types.h"
namespace Common::FS {
enum class FileAccessMode {
/**
* If the file at path exists, it opens the file for reading.
* If the file at path does not exist, it fails to open the file.
*/
Read = 1 << 0,
/**
* If the file at path exists, the existing contents of the file are erased.
* The empty file is then opened for writing.
* If the file at path does not exist, it creates and opens a new empty file for writing.
*/
Write = 1 << 1,
/**
* If the file at path exists, it opens the file for reading and writing.
* If the file at path does not exist, it fails to open the file.
*/
ReadWrite = Read | Write,
/**
* If the file at path exists, it opens the file for appending.
* If the file at path does not exist, it creates and opens a new empty file for appending.
*/
Append = 1 << 2,
/**
* If the file at path exists, it opens the file for both reading and appending.
* If the file at path does not exist, it creates and opens a new empty file for both
* reading and appending.
*/
ReadAppend = Read | Append,
};
enum class FileType {
BinaryFile,
TextFile,
};
enum class FileShareFlag {
ShareNone, // Provides exclusive access to the file.
ShareReadOnly, // Provides read only shared access to the file.
ShareWriteOnly, // Provides write only shared access to the file.
ShareReadWrite, // Provides read and write shared access to the file.
};
enum class DirEntryFilter {
File = 1 << 0,
Directory = 1 << 1,
All = File | Directory,
};
DECLARE_ENUM_FLAG_OPERATORS(DirEntryFilter);
/**
* A callback function which takes in the path of a directory entry.
*
* @param path The path of a directory entry
*
* @returns A boolean value.
* Return true to indicate whether the callback is successful, false otherwise.
*/
using DirEntryCallable = std::function<bool(const std::filesystem::path& path)>;
} // namespace Common::FS

27
src/common/fs/fs_util.cpp Normal file
View File

@@ -0,0 +1,27 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include "common/fs/fs_util.h"
namespace Common::FS {
std::u8string ToU8String(std::string_view utf8_string) {
return std::u8string{utf8_string.begin(), utf8_string.end()};
}
std::u8string BufferToU8String(std::span<const u8> buffer) {
return std::u8string{buffer.begin(), std::ranges::find(buffer, u8{0})};
}
std::string ToUTF8String(std::u8string_view u8_string) {
return std::string{u8_string.begin(), u8_string.end()};
}
std::string PathToUTF8String(const std::filesystem::path& path) {
return ToUTF8String(path.u8string());
}
} // namespace Common::FS

58
src/common/fs/fs_util.h Normal file
View File

@@ -0,0 +1,58 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <concepts>
#include <filesystem>
#include <span>
#include <string>
#include <string_view>
#include "common/common_types.h"
namespace Common::FS {
template <typename T>
concept IsChar = std::same_as<T, char>;
/**
* Converts a UTF-8 encoded std::string or std::string_view to a std::u8string.
*
* @param utf8_string UTF-8 encoded string
*
* @returns UTF-8 encoded std::u8string.
*/
[[nodiscard]] std::u8string ToU8String(std::string_view utf8_string);
/**
* Converts a buffer of bytes to a UTF8-encoded std::u8string.
* This converts from the start of the buffer until the first encountered null-terminator.
* If no null-terminator is found, this converts the entire buffer instead.
*
* @param buffer Buffer of bytes
*
* @returns UTF-8 encoded std::u8string.
*/
[[nodiscard]] std::u8string BufferToU8String(std::span<const u8> buffer);
/**
* Converts a std::u8string or std::u8string_view to a UTF-8 encoded std::string.
*
* @param u8_string UTF-8 encoded u8string
*
* @returns UTF-8 encoded std::string.
*/
[[nodiscard]] std::string ToUTF8String(std::u8string_view u8_string);
/**
* Converts a filesystem path to a UTF-8 encoded std::string.
*
* @param path Filesystem path
*
* @returns UTF-8 encoded std::string.
*/
[[nodiscard]] std::string PathToUTF8String(const std::filesystem::path& path);
} // namespace Common::FS

426
src/common/fs/path_util.cpp Normal file
View File

@@ -0,0 +1,426 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <unordered_map>
#include "common/fs/fs.h"
#include "common/fs/fs_paths.h"
#include "common/fs/path_util.h"
#include "common/logging/log.h"
#ifdef _WIN32
#include <shlobj.h> // Used in GetExeDirectory()
#else
#include <cstdlib> // Used in Get(Home/Data)Directory()
#include <pwd.h> // Used in GetHomeDirectory()
#include <sys/types.h> // Used in GetHomeDirectory()
#include <unistd.h> // Used in GetDataDirectory()
#endif
#ifdef __APPLE__
#include <sys/param.h> // Used in GetBundleDirectory()
// CFURL contains __attribute__ directives that gcc does not know how to parse, so we need to just
// ignore them if we're not using clang. The macro is only used to prevent linking against
// functions that don't exist on older versions of macOS, and the worst case scenario is a linker
// error, so this is perfectly safe, just inconvenient.
#ifndef __clang__
#define availability(...)
#endif
#include <CoreFoundation/CFBundle.h> // Used in GetBundleDirectory()
#include <CoreFoundation/CFString.h> // Used in GetBundleDirectory()
#include <CoreFoundation/CFURL.h> // Used in GetBundleDirectory()
#ifdef availability
#undef availability
#endif
#endif
#ifndef MAX_PATH
#ifdef _WIN32
// This is the maximum number of UTF-16 code units permissible in Windows file paths
#define MAX_PATH 260
#else
// This is the maximum number of UTF-8 code units permissible in all other OSes' file paths
#define MAX_PATH 1024
#endif
#endif
namespace Common::FS {
namespace fs = std::filesystem;
/**
* The PathManagerImpl is a singleton allowing to manage the mapping of
* YuzuPath enums to real filesystem paths.
* This class provides 2 functions: GetYuzuPathImpl and SetYuzuPathImpl.
* These are used by GetYuzuPath and SetYuzuPath respectively to get or modify
* the path mapped by the YuzuPath enum.
*/
class PathManagerImpl {
public:
static PathManagerImpl& GetInstance() {
static PathManagerImpl path_manager_impl;
return path_manager_impl;
}
PathManagerImpl(const PathManagerImpl&) = delete;
PathManagerImpl& operator=(const PathManagerImpl&) = delete;
PathManagerImpl(PathManagerImpl&&) = delete;
PathManagerImpl& operator=(PathManagerImpl&&) = delete;
[[nodiscard]] const fs::path& GetYuzuPathImpl(YuzuPath yuzu_path) {
return yuzu_paths.at(yuzu_path);
}
void SetYuzuPathImpl(YuzuPath yuzu_path, const fs::path& new_path) {
yuzu_paths.insert_or_assign(yuzu_path, new_path);
}
private:
PathManagerImpl() {
#ifdef _WIN32
auto yuzu_path = GetExeDirectory() / PORTABLE_DIR;
if (!IsDir(yuzu_path)) {
yuzu_path = GetAppDataRoamingDirectory() / YUZU_DIR;
}
GenerateYuzuPath(YuzuPath::YuzuDir, yuzu_path);
GenerateYuzuPath(YuzuPath::CacheDir, yuzu_path / CACHE_DIR);
GenerateYuzuPath(YuzuPath::ConfigDir, yuzu_path / CONFIG_DIR);
#else
auto yuzu_path = GetCurrentDir() / PORTABLE_DIR;
if (Exists(yuzu_path) && IsDir(yuzu_path)) {
GenerateYuzuPath(YuzuPath::YuzuDir, yuzu_path);
GenerateYuzuPath(YuzuPath::CacheDir, yuzu_path / CACHE_DIR);
GenerateYuzuPath(YuzuPath::ConfigDir, yuzu_path / CONFIG_DIR);
} else {
yuzu_path = GetDataDirectory("XDG_DATA_HOME") / YUZU_DIR;
GenerateYuzuPath(YuzuPath::YuzuDir, yuzu_path);
GenerateYuzuPath(YuzuPath::CacheDir, GetDataDirectory("XDG_CACHE_HOME") / YUZU_DIR);
GenerateYuzuPath(YuzuPath::ConfigDir, GetDataDirectory("XDG_CONFIG_HOME") / YUZU_DIR);
}
#endif
GenerateYuzuPath(YuzuPath::DumpDir, yuzu_path / DUMP_DIR);
GenerateYuzuPath(YuzuPath::KeysDir, yuzu_path / KEYS_DIR);
GenerateYuzuPath(YuzuPath::LoadDir, yuzu_path / LOAD_DIR);
GenerateYuzuPath(YuzuPath::LogDir, yuzu_path / LOG_DIR);
GenerateYuzuPath(YuzuPath::NANDDir, yuzu_path / NAND_DIR);
GenerateYuzuPath(YuzuPath::ScreenshotsDir, yuzu_path / SCREENSHOTS_DIR);
GenerateYuzuPath(YuzuPath::SDMCDir, yuzu_path / SDMC_DIR);
GenerateYuzuPath(YuzuPath::ShaderDir, yuzu_path / SHADER_DIR);
}
~PathManagerImpl() = default;
void GenerateYuzuPath(YuzuPath yuzu_path, const fs::path& new_path) {
void(FS::CreateDir(new_path));
SetYuzuPathImpl(yuzu_path, new_path);
}
std::unordered_map<YuzuPath, fs::path> yuzu_paths;
};
bool ValidatePath(const fs::path& path) {
if (path.empty()) {
LOG_ERROR(Common_Filesystem, "Input path is empty, path={}", PathToUTF8String(path));
return false;
}
#ifdef _WIN32
if (path.u16string().size() >= MAX_PATH) {
LOG_ERROR(Common_Filesystem, "Input path is too long, path={}", PathToUTF8String(path));
return false;
}
#else
if (path.u8string().size() >= MAX_PATH) {
LOG_ERROR(Common_Filesystem, "Input path is too long, path={}", PathToUTF8String(path));
return false;
}
#endif
return true;
}
fs::path ConcatPath(const fs::path& first, const fs::path& second) {
const bool second_has_dir_sep = IsDirSeparator(second.u8string().front());
if (!second_has_dir_sep) {
return (first / second).lexically_normal();
}
fs::path concat_path = first;
concat_path += second;
return concat_path.lexically_normal();
}
fs::path ConcatPathSafe(const fs::path& base, const fs::path& offset) {
const auto concatenated_path = ConcatPath(base, offset);
if (!IsPathSandboxed(base, concatenated_path)) {
return base;
}
return concatenated_path;
}
bool IsPathSandboxed(const fs::path& base, const fs::path& path) {
const auto base_string = RemoveTrailingSeparators(base.lexically_normal()).u8string();
const auto path_string = RemoveTrailingSeparators(path.lexically_normal()).u8string();
if (path_string.size() < base_string.size()) {
return false;
}
return base_string.compare(0, base_string.size(), path_string, 0, base_string.size()) == 0;
}
bool IsDirSeparator(char character) {
return character == '/' || character == '\\';
}
bool IsDirSeparator(char8_t character) {
return character == u8'/' || character == u8'\\';
}
fs::path RemoveTrailingSeparators(const fs::path& path) {
if (path.empty()) {
return path;
}
auto string_path = path.u8string();
while (IsDirSeparator(string_path.back())) {
string_path.pop_back();
}
return fs::path{string_path};
}
const fs::path& GetYuzuPath(YuzuPath yuzu_path) {
return PathManagerImpl::GetInstance().GetYuzuPathImpl(yuzu_path);
}
std::string GetYuzuPathString(YuzuPath yuzu_path) {
return PathToUTF8String(GetYuzuPath(yuzu_path));
}
void SetYuzuPath(YuzuPath yuzu_path, const fs::path& new_path) {
if (!FS::IsDir(new_path)) {
LOG_ERROR(Common_Filesystem, "Filesystem object at new_path={} is not a directory",
PathToUTF8String(new_path));
return;
}
PathManagerImpl::GetInstance().SetYuzuPathImpl(yuzu_path, new_path);
}
#ifdef _WIN32
fs::path GetExeDirectory() {
wchar_t exe_path[MAX_PATH];
GetModuleFileNameW(nullptr, exe_path, MAX_PATH);
if (!exe_path) {
LOG_ERROR(Common_Filesystem,
"Failed to get the path to the executable of the current process");
}
return fs::path{exe_path}.parent_path();
}
fs::path GetAppDataRoamingDirectory() {
PWSTR appdata_roaming_path = nullptr;
SHGetKnownFolderPath(FOLDERID_RoamingAppData, 0, nullptr, &appdata_roaming_path);
auto fs_appdata_roaming_path = fs::path{appdata_roaming_path};
CoTaskMemFree(appdata_roaming_path);
if (fs_appdata_roaming_path.empty()) {
LOG_ERROR(Common_Filesystem, "Failed to get the path to the %APPDATA% directory");
}
return fs_appdata_roaming_path;
}
#else
fs::path GetHomeDirectory() {
const char* home_env_var = getenv("HOME");
if (home_env_var) {
return fs::path{home_env_var};
}
LOG_INFO(Common_Filesystem,
"$HOME is not defined in the environment variables, "
"attempting to query passwd to get the home path of the current user");
const auto* pw = getpwuid(getuid());
if (!pw) {
LOG_ERROR(Common_Filesystem, "Failed to get the home path of the current user");
return {};
}
return fs::path{pw->pw_dir};
}
fs::path GetDataDirectory(const std::string& env_name) {
const char* data_env_var = getenv(env_name.c_str());
if (data_env_var) {
return fs::path{data_env_var};
}
if (env_name == "XDG_DATA_HOME") {
return GetHomeDirectory() / ".local/share";
} else if (env_name == "XDG_CACHE_HOME") {
return GetHomeDirectory() / ".cache";
} else if (env_name == "XDG_CONFIG_HOME") {
return GetHomeDirectory() / ".config";
}
return {};
}
#endif
#ifdef __APPLE__
fs::path GetBundleDirectory() {
char app_bundle_path[MAXPATHLEN];
// Get the main bundle for the app
CFURLRef bundle_ref = CFBundleCopyBundleURL(CFBundleGetMainBundle());
CFStringRef bundle_path = CFURLCopyFileSystemPath(bundle_ref, kCFURLPOSIXPathStyle);
CFStringGetFileSystemRepresentation(bundle_path, app_bundle_path, sizeof(app_bundle_path));
CFRelease(bundle_ref);
CFRelease(bundle_path);
return fs::path{app_bundle_path};
}
#endif
// vvvvvvvvvv Deprecated vvvvvvvvvv //
std::string_view RemoveTrailingSlash(std::string_view path) {
if (path.empty()) {
return path;
}
if (path.back() == '\\' || path.back() == '/') {
path.remove_suffix(1);
return path;
}
return path;
}
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(copy);
std::string item;
while (std::getline(stream, item, '/')) {
out.push_back(std::move(item));
}
return out;
}
std::string SanitizePath(std::string_view path_, DirectorySeparator directory_separator) {
std::string path(path_);
char type1 = directory_separator == DirectorySeparator::BackwardSlash ? '/' : '\\';
char type2 = directory_separator == DirectorySeparator::BackwardSlash ? '\\' : '/';
if (directory_separator == DirectorySeparator::PlatformDefault) {
#ifdef _WIN32
type1 = '/';
type2 = '\\';
#endif
}
std::replace(path.begin(), path.end(), type1, type2);
auto start = path.begin();
#ifdef _WIN32
// allow network paths which start with a double backslash (e.g. \\server\share)
if (start != path.end())
++start;
#endif
path.erase(std::unique(start, path.end(),
[type2](char c1, char c2) { return c1 == type2 && c2 == type2; }),
path.end());
return std::string(RemoveTrailingSlash(path));
}
std::string_view GetParentPath(std::string_view path) {
const auto name_bck_index = path.rfind('\\');
const auto name_fwd_index = path.rfind('/');
std::size_t name_index;
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);
}
return path.substr(0, name_index);
}
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_view GetExtensionFromFilename(std::string_view name) {
const std::size_t index = name.rfind('.');
if (index == std::string_view::npos) {
return {};
}
return name.substr(index + 1);
}
} // namespace Common::FS

300
src/common/fs/path_util.h Normal file
View File

@@ -0,0 +1,300 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <filesystem>
#include <vector>
#include "common/fs/fs_util.h"
namespace Common::FS {
enum class YuzuPath {
YuzuDir, // Where yuzu stores its data.
CacheDir, // Where cached filesystem data is stored.
ConfigDir, // Where config files are stored.
DumpDir, // Where dumped data is stored.
KeysDir, // Where key files are stored.
LoadDir, // Where cheat/mod files are stored.
LogDir, // Where log files are stored.
NANDDir, // Where the emulated NAND is stored.
ScreenshotsDir, // Where yuzu screenshots are stored.
SDMCDir, // Where the emulated SDMC is stored.
ShaderDir, // Where shaders are stored.
};
/**
* Validates a given path.
*
* A given path is valid if it meets these conditions:
* - The path is not empty
* - The path is not too long
*
* @param path Filesystem path
*
* @returns True if the path is valid, false otherwise.
*/
[[nodiscard]] bool ValidatePath(const std::filesystem::path& path);
#ifdef _WIN32
template <typename Path>
[[nodiscard]] bool ValidatePath(const Path& path) {
if constexpr (IsChar<typename Path::value_type>) {
return ValidatePath(ToU8String(path));
} else {
return ValidatePath(std::filesystem::path{path});
}
}
#endif
/**
* Concatenates two filesystem paths together.
*
* This is needed since the following occurs when using std::filesystem::path's operator/:
* first: "/first/path"
* second: "/second/path" (Note that the second path has a directory separator in the front)
* first / second yields "/second/path" when the desired result is first/path/second/path
*
* @param first First filesystem path
* @param second Second filesystem path
*
* @returns A concatenated filesystem path.
*/
[[nodiscard]] std::filesystem::path ConcatPath(const std::filesystem::path& first,
const std::filesystem::path& second);
#ifdef _WIN32
template <typename Path1, typename Path2>
[[nodiscard]] std::filesystem::path ConcatPath(const Path1& first, const Path2& second) {
using ValueType1 = typename Path1::value_type;
using ValueType2 = typename Path2::value_type;
if constexpr (IsChar<ValueType1> && IsChar<ValueType2>) {
return ConcatPath(ToU8String(first), ToU8String(second));
} else if constexpr (IsChar<ValueType1> && !IsChar<ValueType2>) {
return ConcatPath(ToU8String(first), second);
} else if constexpr (!IsChar<ValueType1> && IsChar<ValueType2>) {
return ConcatPath(first, ToU8String(second));
} else {
return ConcatPath(std::filesystem::path{first}, std::filesystem::path{second});
}
}
#endif
/**
* Safe variant of ConcatPath that takes in a base path and an offset path from the given base path.
*
* If ConcatPath(base, offset) resolves to a path that is sandboxed within the base path,
* this will return the concatenated path. Otherwise this will return the base path.
*
* @param base Base filesystem path
* @param offset Offset filesystem path
*
* @returns A concatenated filesystem path if it is within the base path,
* returns the base path otherwise.
*/
[[nodiscard]] std::filesystem::path ConcatPathSafe(const std::filesystem::path& base,
const std::filesystem::path& offset);
#ifdef _WIN32
template <typename Path1, typename Path2>
[[nodiscard]] std::filesystem::path ConcatPathSafe(const Path1& base, const Path2& offset) {
using ValueType1 = typename Path1::value_type;
using ValueType2 = typename Path2::value_type;
if constexpr (IsChar<ValueType1> && IsChar<ValueType2>) {
return ConcatPathSafe(ToU8String(base), ToU8String(offset));
} else if constexpr (IsChar<ValueType1> && !IsChar<ValueType2>) {
return ConcatPathSafe(ToU8String(base), offset);
} else if constexpr (!IsChar<ValueType1> && IsChar<ValueType2>) {
return ConcatPathSafe(base, ToU8String(offset));
} else {
return ConcatPathSafe(std::filesystem::path{base}, std::filesystem::path{offset});
}
}
#endif
/**
* Checks whether a given path is sandboxed within a given base path.
*
* @param base Base filesystem path
* @param path Filesystem path
*
* @returns True if the given path is sandboxed within the given base path, false otherwise.
*/
[[nodiscard]] bool IsPathSandboxed(const std::filesystem::path& base,
const std::filesystem::path& path);
#ifdef _WIN32
template <typename Path1, typename Path2>
[[nodiscard]] bool IsPathSandboxed(const Path1& base, const Path2& path) {
using ValueType1 = typename Path1::value_type;
using ValueType2 = typename Path2::value_type;
if constexpr (IsChar<ValueType1> && IsChar<ValueType2>) {
return IsPathSandboxed(ToU8String(base), ToU8String(path));
} else if constexpr (IsChar<ValueType1> && !IsChar<ValueType2>) {
return IsPathSandboxed(ToU8String(base), path);
} else if constexpr (!IsChar<ValueType1> && IsChar<ValueType2>) {
return IsPathSandboxed(base, ToU8String(path));
} else {
return IsPathSandboxed(std::filesystem::path{base}, std::filesystem::path{path});
}
}
#endif
/**
* Checks if a character is a directory separator (either a forward slash or backslash).
*
* @param character Character
*
* @returns True if the character is a directory separator, false otherwise.
*/
[[nodiscard]] bool IsDirSeparator(char character);
/**
* Checks if a character is a directory separator (either a forward slash or backslash).
*
* @param character Character
*
* @returns True if the character is a directory separator, false otherwise.
*/
[[nodiscard]] bool IsDirSeparator(char8_t character);
/**
* Removes any trailing directory separators if they exist in the given path.
*
* @param path Filesystem path
*
* @returns The filesystem path without any trailing directory separators.
*/
[[nodiscard]] std::filesystem::path RemoveTrailingSeparators(const std::filesystem::path& path);
#ifdef _WIN32
template <typename Path>
[[nodiscard]] std::filesystem::path RemoveTrailingSeparators(const Path& path) {
if constexpr (IsChar<typename Path::value_type>) {
return RemoveTrailingSeparators(ToU8String(path));
} else {
return RemoveTrailingSeparators(std::filesystem::path{path});
}
}
#endif
/**
* Gets the filesystem path associated with the YuzuPath enum.
*
* @param yuzu_path YuzuPath enum
*
* @returns The filesystem path associated with the YuzuPath enum.
*/
[[nodiscard]] const std::filesystem::path& GetYuzuPath(YuzuPath yuzu_path);
/**
* Gets the filesystem path associated with the YuzuPath enum as a UTF-8 encoded std::string.
*
* @param yuzu_path YuzuPath enum
*
* @returns The filesystem path associated with the YuzuPath enum as a UTF-8 encoded std::string.
*/
[[nodiscard]] std::string GetYuzuPathString(YuzuPath yuzu_path);
/**
* Sets a new filesystem path associated with the YuzuPath enum.
* If the filesystem object at new_path is not a directory, this function will not do anything.
*
* @param yuzu_path YuzuPath enum
* @param new_path New filesystem path
*/
void SetYuzuPath(YuzuPath yuzu_path, const std::filesystem::path& new_path);
#ifdef _WIN32
template <typename Path>
void SetYuzuPath(YuzuPath yuzu_path, const Path& new_path) {
if constexpr (IsChar<typename Path::value_type>) {
SetYuzuPath(yuzu_path, ToU8String(new_path));
} else {
SetYuzuPath(yuzu_path, std::filesystem::path{new_path});
}
}
#endif
#ifdef _WIN32
/**
* Gets the path of the directory containing the executable of the current process.
*
* @returns The path of the directory containing the executable of the current process.
*/
[[nodiscard]] std::filesystem::path GetExeDirectory();
/**
* Gets the path of the current user's %APPDATA% directory (%USERPROFILE%/AppData/Roaming).
*
* @returns The path of the current user's %APPDATA% directory.
*/
[[nodiscard]] std::filesystem::path GetAppDataRoamingDirectory();
#else
/**
* Gets the path of the directory specified by the #HOME environment variable.
* If $HOME is not defined, it will attempt to query the user database in passwd instead.
*
* @returns The path of the current user's home directory.
*/
[[nodiscard]] std::filesystem::path GetHomeDirectory();
/**
* Gets the relevant paths for yuzu to store its data based on the given XDG environment variable.
* See https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
* Defaults to $HOME/.local/share for main application data,
* $HOME/.cache for cached data, and $HOME/.config for configuration files.
*
* @param env_name XDG environment variable name
*
* @returns The path where yuzu should store its data.
*/
[[nodiscard]] std::filesystem::path GetDataDirectory(const std::string& env_name);
#endif
#ifdef __APPLE__
[[nodiscard]] std::filesystem::path GetBundleDirectory();
#endif
// vvvvvvvvvv Deprecated vvvvvvvvvv //
// Removes the final '/' or '\' if one exists
[[nodiscard]] std::string_view RemoveTrailingSlash(std::string_view path);
enum class DirectorySeparator {
ForwardSlash,
BackwardSlash,
PlatformDefault,
};
// 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" }
[[nodiscard]] std::vector<std::string> SplitPathComponents(std::string_view filename);
// Removes trailing slash, makes all '\\' into '/', and removes duplicate '/'. Makes '/' into '\\'
// depending if directory_separator is BackwardSlash or PlatformDefault and running on windows
[[nodiscard]] std::string SanitizePath(
std::string_view path,
DirectorySeparator directory_separator = DirectorySeparator::ForwardSlash);
// Gets all of the text up to the last '/' or '\' in the path.
[[nodiscard]] std::string_view GetParentPath(std::string_view path);
// Gets all of the text after the first '/' or '\' in the path.
[[nodiscard]] std::string_view GetPathWithoutTop(std::string_view path);
// Gets the filename of the path
[[nodiscard]] std::string_view GetFilename(std::string_view path);
// Gets the extension of the filename
[[nodiscard]] std::string_view GetExtensionFromFilename(std::string_view name);
} // namespace Common::FS

View File

@@ -11,13 +11,13 @@
#include <mutex>
#include <thread>
#include <vector>
#ifdef _WIN32
#include <share.h> // For _SH_DENYWR
#include <windows.h> // For OutputDebugStringW
#else
#define _SH_DENYWR 0
#endif
#include "common/assert.h"
#include "common/fs/fs.h"
#include "common/logging/backend.h"
#include "common/logging/log.h"
#include "common/logging/text_formatter.h"
@@ -148,19 +148,16 @@ void ColorConsoleBackend::Write(const Entry& entry) {
PrintColoredMessage(entry);
}
FileBackend::FileBackend(const std::string& filename) {
const auto old_filename = filename + ".old.txt";
FileBackend::FileBackend(const std::filesystem::path& filename) {
auto old_filename = filename;
old_filename += ".old.txt";
if (FS::Exists(old_filename)) {
FS::Delete(old_filename);
}
if (FS::Exists(filename)) {
FS::Rename(filename, old_filename);
}
// Existence checks are done within the functions themselves.
// We don't particularly care if these succeed or not.
void(FS::RemoveFile(old_filename));
void(FS::RenameFile(filename, old_filename));
// _SH_DENYWR allows read only access to the file for other programs.
// It is #defined to 0 on other platforms
file = FS::IOFile(filename, "w", _SH_DENYWR);
file = FS::IOFile(filename, FS::FileAccessMode::Write, FS::FileType::TextFile);
}
void FileBackend::Write(const Entry& entry) {
@@ -181,7 +178,7 @@ void FileBackend::Write(const Entry& entry) {
bytes_written += file.WriteString(FormatLogMessage(entry).append(1, '\n'));
if (entry.log_level >= Level::Error) {
file.Flush();
void(file.Flush());
}
}

View File

@@ -4,10 +4,11 @@
#pragma once
#include <chrono>
#include <filesystem>
#include <memory>
#include <string>
#include <string_view>
#include "common/file_util.h"
#include "common/fs/file.h"
#include "common/logging/filter.h"
#include "common/logging/log.h"
@@ -81,7 +82,7 @@ public:
*/
class FileBackend : public Backend {
public:
explicit FileBackend(const std::string& filename);
explicit FileBackend(const std::filesystem::path& filename);
static const char* Name() {
return "file";

View File

@@ -59,8 +59,7 @@ std::vector<u8> CompressDataLZ4HCMax(const u8* source, std::size_t source_size)
return CompressDataLZ4HC(source, source_size, LZ4HC_CLEVEL_MAX);
}
std::vector<u8> DecompressDataLZ4(const std::vector<u8>& compressed,
std::size_t uncompressed_size) {
std::vector<u8> DecompressDataLZ4(std::span<const u8> compressed, std::size_t uncompressed_size) {
std::vector<u8> uncompressed(uncompressed_size);
const int size_check = LZ4_decompress_safe(reinterpret_cast<const char*>(compressed.data()),
reinterpret_cast<char*>(uncompressed.data()),

View File

@@ -4,6 +4,7 @@
#pragma once
#include <span>
#include <vector>
#include "common/common_types.h"
@@ -53,7 +54,7 @@ namespace Common::Compression {
*
* @return the decompressed data.
*/
[[nodiscard]] std::vector<u8> DecompressDataLZ4(const std::vector<u8>& compressed,
[[nodiscard]] std::vector<u8> DecompressDataLZ4(std::span<const u8> compressed,
std::size_t uncompressed_size);
} // namespace Common::Compression
} // namespace Common::Compression

View File

@@ -2,24 +2,30 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <filesystem>
#include <stdlib.h>
#include <cstdlib>
#include <fmt/format.h>
#include "common/file_util.h"
#include "common/fs/file.h"
#include "common/fs/fs.h"
#include "common/fs/path_util.h"
#include "common/nvidia_flags.h"
namespace Common {
void ConfigureNvidiaEnvironmentFlags() {
#ifdef _WIN32
const std::string shader_path = Common::FS::SanitizePath(
fmt::format("{}/nvidia/", Common::FS::GetUserPath(Common::FS::UserPath::ShaderDir)));
const std::string windows_path =
Common::FS::SanitizePath(shader_path, Common::FS::DirectorySeparator::BackwardSlash);
void(Common::FS::CreateFullPath(shader_path + '/'));
void(_putenv(fmt::format("__GL_SHADER_DISK_CACHE_PATH={}", windows_path).c_str()));
const auto nvidia_shader_dir =
Common::FS::GetYuzuPath(Common::FS::YuzuPath::ShaderDir) / "nvidia";
if (!Common::FS::CreateDirs(nvidia_shader_dir)) {
return;
}
const auto windows_path_string =
Common::FS::PathToUTF8String(nvidia_shader_dir.lexically_normal());
void(_putenv(fmt::format("__GL_SHADER_DISK_CACHE_PATH={}", windows_path_string).c_str()));
void(_putenv("__GL_SHADER_DISK_CACHE_SKIP_CLEANUP=1"));
#endif
}

View File

@@ -14,6 +14,7 @@ void PageTable::Resize(size_t address_space_width_in_bits, size_t page_size_in_b
const size_t num_page_table_entries{1ULL << (address_space_width_in_bits - page_size_in_bits)};
pointers.resize(num_page_table_entries);
backing_addr.resize(num_page_table_entries);
current_address_space_width_in_bits = address_space_width_in_bits;
}
} // namespace Common

View File

@@ -98,6 +98,10 @@ struct PageTable {
*/
void Resize(size_t address_space_width_in_bits, size_t page_size_in_bits);
size_t GetAddressSpaceBits() const {
return current_address_space_width_in_bits;
}
/**
* Vector of memory pointers backing each page. An entry can only be non-null if the
* corresponding attribute element is of type `Memory`.
@@ -105,6 +109,8 @@ struct PageTable {
VirtualBuffer<PageInfo> pointers;
VirtualBuffer<u64> backing_addr;
size_t current_address_space_width_in_bits;
};
} // namespace Common

View File

@@ -109,7 +109,8 @@ struct OffsetOfCalculator {
}
}
return (next - start) * sizeof(MemberType) + Offset;
return static_cast<ptrdiff_t>(static_cast<size_t>(next - start) * sizeof(MemberType) +
Offset);
}
static constexpr std::ptrdiff_t OffsetOf(MemberType ParentType::*member) {

57
src/common/point.h Normal file
View File

@@ -0,0 +1,57 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <type_traits>
namespace Common {
// Represents a point within a 2D space.
template <typename T>
struct Point {
static_assert(std::is_arithmetic_v<T>, "T must be an arithmetic type!");
T x{};
T y{};
#define ARITHMETIC_OP(op, compound_op) \
friend constexpr Point operator op(const Point& lhs, const Point& rhs) noexcept { \
return { \
.x = static_cast<T>(lhs.x op rhs.x), \
.y = static_cast<T>(lhs.y op rhs.y), \
}; \
} \
friend constexpr Point operator op(const Point& lhs, T value) noexcept { \
return { \
.x = static_cast<T>(lhs.x op value), \
.y = static_cast<T>(lhs.y op value), \
}; \
} \
friend constexpr Point operator op(T value, const Point& rhs) noexcept { \
return { \
.x = static_cast<T>(value op rhs.x), \
.y = static_cast<T>(value op rhs.y), \
}; \
} \
friend constexpr Point& operator compound_op(Point& lhs, const Point& rhs) noexcept { \
lhs.x = static_cast<T>(lhs.x op rhs.x); \
lhs.y = static_cast<T>(lhs.y op rhs.y); \
return lhs; \
} \
friend constexpr Point& operator compound_op(Point& lhs, T value) noexcept { \
lhs.x = static_cast<T>(lhs.x op value); \
lhs.y = static_cast<T>(lhs.y op value); \
return lhs; \
}
ARITHMETIC_OP(+, +=)
ARITHMETIC_OP(-, -=)
ARITHMETIC_OP(*, *=)
ARITHMETIC_OP(/, /=)
#undef ARITHMETIC_OP
friend constexpr bool operator==(const Point&, const Point&) = default;
};
} // namespace Common

View File

@@ -5,7 +5,7 @@
#include <string_view>
#include "common/assert.h"
#include "common/file_util.h"
#include "common/fs/path_util.h"
#include "common/logging/log.h"
#include "common/settings.h"
@@ -34,6 +34,10 @@ void LogSettings() {
LOG_INFO(Config, "{}: {}", name, value);
};
const auto log_path = [](std::string_view name, const std::filesystem::path& path) {
LOG_INFO(Config, "{}: {}", name, Common::FS::PathToUTF8String(path));
};
LOG_INFO(Config, "yuzu Configuration:");
log_setting("Controls_UseDockedMode", values.use_docked_mode.GetValue());
log_setting("System_RngSeed", values.rng_seed.GetValue().value_or(0));
@@ -42,7 +46,7 @@ void LogSettings() {
log_setting("System_RegionIndex", values.region_index.GetValue());
log_setting("System_TimeZoneIndex", values.time_zone_index.GetValue());
log_setting("Core_UseMultiCore", values.use_multi_core.GetValue());
log_setting("CPU_Accuracy", values.cpu_accuracy);
log_setting("CPU_Accuracy", values.cpu_accuracy.GetValue());
log_setting("Renderer_UseResolutionFactor", values.resolution_factor.GetValue());
log_setting("Renderer_UseFrameLimit", values.use_frame_limit.GetValue());
log_setting("Renderer_FrameLimit", values.frame_limit.GetValue());
@@ -59,11 +63,11 @@ void LogSettings() {
log_setting("Audio_EnableAudioStretching", values.enable_audio_stretching.GetValue());
log_setting("Audio_OutputDevice", values.audio_device_id);
log_setting("DataStorage_UseVirtualSd", values.use_virtual_sd);
log_setting("DataStorage_CacheDir", Common::FS::GetUserPath(Common::FS::UserPath::CacheDir));
log_setting("DataStorage_ConfigDir", Common::FS::GetUserPath(Common::FS::UserPath::ConfigDir));
log_setting("DataStorage_LoadDir", Common::FS::GetUserPath(Common::FS::UserPath::LoadDir));
log_setting("DataStorage_NandDir", Common::FS::GetUserPath(Common::FS::UserPath::NANDDir));
log_setting("DataStorage_SdmcDir", Common::FS::GetUserPath(Common::FS::UserPath::SDMCDir));
log_path("DataStorage_CacheDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir));
log_path("DataStorage_ConfigDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::ConfigDir));
log_path("DataStorage_LoadDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::LoadDir));
log_path("DataStorage_NANDDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir));
log_path("DataStorage_SDMCDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::SDMCDir));
log_setting("Debugging_ProgramArgs", values.program_args);
log_setting("Services_BCATBackend", values.bcat_backend);
log_setting("Services_BCATBoxcatLocal", values.bcat_boxcat_local);
@@ -106,6 +110,12 @@ void RestoreGlobalState(bool is_powered_on) {
// Core
values.use_multi_core.SetGlobal(true);
// CPU
values.cpu_accuracy.SetGlobal(true);
values.cpuopt_unsafe_unfuse_fma.SetGlobal(true);
values.cpuopt_unsafe_reduce_fp_error.SetGlobal(true);
values.cpuopt_unsafe_inaccurate_nan.SetGlobal(true);
// Renderer
values.renderer_backend.SetGlobal(true);
values.vulkan_device.SetGlobal(true);
@@ -130,7 +140,6 @@ void RestoreGlobalState(bool is_powered_on) {
values.region_index.SetGlobal(true);
values.time_zone_index.SetGlobal(true);
values.rng_seed.SetGlobal(true);
values.custom_rtc.SetGlobal(true);
values.sound_index.SetGlobal(true);
// Controls

View File

@@ -115,7 +115,7 @@ struct Values {
Setting<bool> use_multi_core;
// Cpu
CPUAccuracy cpu_accuracy;
Setting<CPUAccuracy> cpu_accuracy;
bool cpuopt_page_tables;
bool cpuopt_block_linking;
@@ -126,9 +126,9 @@ struct Values {
bool cpuopt_misc_ir;
bool cpuopt_reduce_misalign_checks;
bool cpuopt_unsafe_unfuse_fma;
bool cpuopt_unsafe_reduce_fp_error;
bool cpuopt_unsafe_inaccurate_nan;
Setting<bool> cpuopt_unsafe_unfuse_fma;
Setting<bool> cpuopt_unsafe_reduce_fp_error;
Setting<bool> cpuopt_unsafe_inaccurate_nan;
// Renderer
Setting<RendererBackend> renderer_backend;
@@ -157,7 +157,7 @@ struct Values {
// System
Setting<std::optional<u32>> rng_seed;
// Measured in seconds since epoch
Setting<std::optional<std::chrono::seconds>> custom_rtc;
std::optional<std::chrono::seconds> custom_rtc;
// Set on game boot, reset on stop. Seconds difference between current time and `custom_rtc`
std::chrono::seconds custom_rtc_differential;

View File

@@ -9,7 +9,6 @@
#include <locale>
#include <sstream>
#include "common/common_paths.h"
#include "common/logging/log.h"
#include "common/string_util.h"
@@ -93,18 +92,6 @@ bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _
return true;
}
void BuildCompleteFilename(std::string& _CompleteFilename, const std::string& _Path,
const std::string& _Filename) {
_CompleteFilename = _Path;
// check for seperator
if (DIR_SEP_CHR != *_CompleteFilename.rbegin())
_CompleteFilename += DIR_SEP_CHR;
// add the filename
_CompleteFilename += _Filename;
}
void SplitString(const std::string& str, const char delim, std::vector<std::string>& output) {
std::istringstream iss(str);
output.resize(1);

View File

@@ -32,8 +32,6 @@ void SplitString(const std::string& str, char delim, std::vector<std::string>& o
bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _pFilename,
std::string* _pExtension);
void BuildCompleteFilename(std::string& _CompleteFilename, const std::string& _Path,
const std::string& _Filename);
[[nodiscard]] std::string ReplaceAll(std::string result, const std::string& src,
const std::string& dest);

View File

@@ -43,6 +43,8 @@
* The maximum height of a red-black tree is 2lg (n+1).
*/
#include "common/assert.h"
namespace Common {
template <typename T>
class RBHead {
@@ -325,6 +327,10 @@ void RB_REMOVE_COLOR(RBHead<Node>* head, Node* parent, Node* elm) {
while ((elm == nullptr || RB_IS_BLACK(elm)) && elm != head->Root() && parent != nullptr) {
if (RB_LEFT(parent) == elm) {
tmp = RB_RIGHT(parent);
if (!tmp) {
ASSERT_MSG(false, "tmp is invalid!");
break;
}
if (RB_IS_RED(tmp)) {
RB_SET_BLACKRED(tmp, parent);
RB_ROTATE_LEFT(head, parent, tmp);
@@ -366,6 +372,11 @@ void RB_REMOVE_COLOR(RBHead<Node>* head, Node* parent, Node* elm) {
tmp = RB_LEFT(parent);
}
if (!tmp) {
ASSERT_MSG(false, "tmp is invalid!");
break;
}
if ((RB_LEFT(tmp) == nullptr || RB_IS_BLACK(RB_LEFT(tmp))) &&
(RB_RIGHT(tmp) == nullptr || RB_IS_BLACK(RB_RIGHT(tmp)))) {
RB_SET_COLOR(tmp, EntryColor::Red);

View File

@@ -32,7 +32,7 @@ std::vector<u8> CompressDataZSTDDefault(const u8* source, std::size_t source_siz
return CompressDataZSTD(source, source_size, ZSTD_CLEVEL_DEFAULT);
}
std::vector<u8> DecompressDataZSTD(const std::vector<u8>& compressed) {
std::vector<u8> DecompressDataZSTD(std::span<const u8> compressed) {
const std::size_t decompressed_size =
ZSTD_getDecompressedSize(compressed.data(), compressed.size());
std::vector<u8> decompressed(decompressed_size);

View File

@@ -4,6 +4,7 @@
#pragma once
#include <span>
#include <vector>
#include "common/common_types.h"
@@ -40,6 +41,6 @@ namespace Common::Compression {
*
* @return the decompressed data.
*/
[[nodiscard]] std::vector<u8> DecompressDataZSTD(const std::vector<u8>& compressed);
[[nodiscard]] std::vector<u8> DecompressDataZSTD(std::span<const u8> compressed);
} // namespace Common::Compression
} // namespace Common::Compression

View File

@@ -65,9 +65,6 @@ public:
/// Step CPU by one instruction
virtual void Step() = 0;
/// Exits execution from a callback, the callback must rewind the stack
virtual void ExceptionalExit() = 0;
/// Clear all instruction cache
virtual void ClearInstructionCache() = 0;
@@ -159,8 +156,6 @@ public:
*/
virtual void SetTPIDR_EL0(u64 value) = 0;
virtual void ChangeProcessorID(std::size_t new_core_id) = 0;
virtual void SaveContext(ThreadContext32& ctx) = 0;
virtual void SaveContext(ThreadContext64& ctx) = 0;
virtual void LoadContext(const ThreadContext32& ctx) = 0;

View File

@@ -4,9 +4,9 @@
#include <cinttypes>
#include <memory>
#include <dynarmic/A32/a32.h>
#include <dynarmic/A32/config.h>
#include <dynarmic/A32/context.h>
#include <dynarmic/interface/A32/a32.h>
#include <dynarmic/interface/A32/config.h>
#include <dynarmic/interface/A32/context.h>
#include "common/assert.h"
#include "common/logging/log.h"
#include "common/page_table.h"
@@ -24,45 +24,46 @@ namespace Core {
class DynarmicCallbacks32 : public Dynarmic::A32::UserCallbacks {
public:
explicit DynarmicCallbacks32(ARM_Dynarmic_32& parent_) : parent{parent_} {}
explicit DynarmicCallbacks32(ARM_Dynarmic_32& parent_)
: parent{parent_}, memory(parent.system.Memory()) {}
u8 MemoryRead8(u32 vaddr) override {
return parent.system.Memory().Read8(vaddr);
return memory.Read8(vaddr);
}
u16 MemoryRead16(u32 vaddr) override {
return parent.system.Memory().Read16(vaddr);
return memory.Read16(vaddr);
}
u32 MemoryRead32(u32 vaddr) override {
return parent.system.Memory().Read32(vaddr);
return memory.Read32(vaddr);
}
u64 MemoryRead64(u32 vaddr) override {
return parent.system.Memory().Read64(vaddr);
return memory.Read64(vaddr);
}
void MemoryWrite8(u32 vaddr, u8 value) override {
parent.system.Memory().Write8(vaddr, value);
memory.Write8(vaddr, value);
}
void MemoryWrite16(u32 vaddr, u16 value) override {
parent.system.Memory().Write16(vaddr, value);
memory.Write16(vaddr, value);
}
void MemoryWrite32(u32 vaddr, u32 value) override {
parent.system.Memory().Write32(vaddr, value);
memory.Write32(vaddr, value);
}
void MemoryWrite64(u32 vaddr, u64 value) override {
parent.system.Memory().Write64(vaddr, value);
memory.Write64(vaddr, value);
}
bool MemoryWriteExclusive8(u32 vaddr, u8 value, u8 expected) override {
return parent.system.Memory().WriteExclusive8(vaddr, value, expected);
return memory.WriteExclusive8(vaddr, value, expected);
}
bool MemoryWriteExclusive16(u32 vaddr, u16 value, u16 expected) override {
return parent.system.Memory().WriteExclusive16(vaddr, value, expected);
return memory.WriteExclusive16(vaddr, value, expected);
}
bool MemoryWriteExclusive32(u32 vaddr, u32 value, u32 expected) override {
return parent.system.Memory().WriteExclusive32(vaddr, value, expected);
return memory.WriteExclusive32(vaddr, value, expected);
}
bool MemoryWriteExclusive64(u32 vaddr, u64 value, u64 expected) override {
return parent.system.Memory().WriteExclusive64(vaddr, value, expected);
return memory.WriteExclusive64(vaddr, value, expected);
}
void InterpreterFallback(u32 pc, std::size_t num_instructions) override {
@@ -78,7 +79,9 @@ public:
}
void CallSVC(u32 swi) override {
Kernel::Svc::Call(parent.system, swi);
parent.svc_called = true;
parent.svc_swi = swi;
parent.jit->HaltExecution();
}
void AddTicks(u64 ticks) override {
@@ -110,6 +113,7 @@ public:
}
ARM_Dynarmic_32& parent;
Core::Memory::Memory& memory;
std::size_t num_interpreted_instructions{};
static constexpr u64 minimum_run_cycles = 1000U;
};
@@ -142,7 +146,7 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
config.far_code_offset = 256 * 1024 * 1024;
// Safe optimizations
if (Settings::values.cpu_accuracy == Settings::CPUAccuracy::DebugMode) {
if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::DebugMode) {
if (!Settings::values.cpuopt_page_tables) {
config.page_table = nullptr;
}
@@ -170,15 +174,15 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
}
// Unsafe optimizations
if (Settings::values.cpu_accuracy == Settings::CPUAccuracy::Unsafe) {
if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) {
config.unsafe_optimizations = true;
if (Settings::values.cpuopt_unsafe_unfuse_fma) {
if (Settings::values.cpuopt_unsafe_unfuse_fma.GetValue()) {
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
}
if (Settings::values.cpuopt_unsafe_reduce_fp_error) {
if (Settings::values.cpuopt_unsafe_reduce_fp_error.GetValue()) {
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP;
}
if (Settings::values.cpuopt_unsafe_inaccurate_nan) {
if (Settings::values.cpuopt_unsafe_inaccurate_nan.GetValue()) {
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
}
}
@@ -187,11 +191,17 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
}
void ARM_Dynarmic_32::Run() {
jit->Run();
}
void ARM_Dynarmic_32::ExceptionalExit() {
jit->ExceptionalExit();
while (true) {
jit->Run();
if (!svc_called) {
break;
}
svc_called = false;
Kernel::Svc::Call(system, svc_swi);
if (shutdown) {
break;
}
}
}
void ARM_Dynarmic_32::Step() {
@@ -255,10 +265,6 @@ void ARM_Dynarmic_32::SetTPIDR_EL0(u64 value) {
cp15->uprw = static_cast<u32>(value);
}
void ARM_Dynarmic_32::ChangeProcessorID(std::size_t new_core_id) {
jit->ChangeProcessorID(new_core_id);
}
void ARM_Dynarmic_32::SaveContext(ThreadContext32& ctx) {
Dynarmic::A32::Context context;
jit->SaveContext(context);
@@ -279,6 +285,7 @@ void ARM_Dynarmic_32::LoadContext(const ThreadContext32& ctx) {
void ARM_Dynarmic_32::PrepareReschedule() {
jit->HaltExecution();
shutdown = true;
}
void ARM_Dynarmic_32::ClearInstructionCache() {

View File

@@ -7,9 +7,9 @@
#include <memory>
#include <unordered_map>
#include <dynarmic/A32/a32.h>
#include <dynarmic/A64/a64.h>
#include <dynarmic/exclusive_monitor.h>
#include <dynarmic/interface/A32/a32.h>
#include <dynarmic/interface/A64/a64.h>
#include <dynarmic/interface/exclusive_monitor.h>
#include "common/common_types.h"
#include "common/hash.h"
#include "core/arm/arm_interface.h"
@@ -42,13 +42,11 @@ public:
u32 GetPSTATE() const override;
void SetPSTATE(u32 pstate) override;
void Run() override;
void ExceptionalExit() override;
void Step() override;
VAddr GetTlsAddress() const override;
void SetTlsAddress(VAddr address) override;
void SetTPIDR_EL0(u64 value) override;
u64 GetTPIDR_EL0() const override;
void ChangeProcessorID(std::size_t new_core_id) override;
bool IsInThumbMode() const {
return (GetPSTATE() & 0x20) != 0;
@@ -83,6 +81,12 @@ private:
std::size_t core_index;
DynarmicExclusiveMonitor& exclusive_monitor;
std::shared_ptr<Dynarmic::A32::Jit> jit;
// SVC callback
u32 svc_swi{};
bool svc_called{};
bool shutdown{};
};
} // namespace Core

View File

@@ -4,8 +4,8 @@
#include <cinttypes>
#include <memory>
#include <dynarmic/A64/a64.h>
#include <dynarmic/A64/config.h>
#include <dynarmic/interface/A64/a64.h>
#include <dynarmic/interface/A64/config.h>
#include "common/assert.h"
#include "common/logging/log.h"
#include "common/page_table.h"
@@ -27,57 +27,56 @@ using Vector = Dynarmic::A64::Vector;
class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks {
public:
explicit DynarmicCallbacks64(ARM_Dynarmic_64& parent_) : parent{parent_} {}
explicit DynarmicCallbacks64(ARM_Dynarmic_64& parent_)
: parent{parent_}, memory(parent.system.Memory()) {}
u8 MemoryRead8(u64 vaddr) override {
return parent.system.Memory().Read8(vaddr);
return memory.Read8(vaddr);
}
u16 MemoryRead16(u64 vaddr) override {
return parent.system.Memory().Read16(vaddr);
return memory.Read16(vaddr);
}
u32 MemoryRead32(u64 vaddr) override {
return parent.system.Memory().Read32(vaddr);
return memory.Read32(vaddr);
}
u64 MemoryRead64(u64 vaddr) override {
return parent.system.Memory().Read64(vaddr);
return memory.Read64(vaddr);
}
Vector MemoryRead128(u64 vaddr) override {
auto& memory = parent.system.Memory();
return {memory.Read64(vaddr), memory.Read64(vaddr + 8)};
}
void MemoryWrite8(u64 vaddr, u8 value) override {
parent.system.Memory().Write8(vaddr, value);
memory.Write8(vaddr, value);
}
void MemoryWrite16(u64 vaddr, u16 value) override {
parent.system.Memory().Write16(vaddr, value);
memory.Write16(vaddr, value);
}
void MemoryWrite32(u64 vaddr, u32 value) override {
parent.system.Memory().Write32(vaddr, value);
memory.Write32(vaddr, value);
}
void MemoryWrite64(u64 vaddr, u64 value) override {
parent.system.Memory().Write64(vaddr, value);
memory.Write64(vaddr, value);
}
void MemoryWrite128(u64 vaddr, Vector value) override {
auto& memory = parent.system.Memory();
memory.Write64(vaddr, value[0]);
memory.Write64(vaddr + 8, value[1]);
}
bool MemoryWriteExclusive8(u64 vaddr, std::uint8_t value, std::uint8_t expected) override {
return parent.system.Memory().WriteExclusive8(vaddr, value, expected);
return memory.WriteExclusive8(vaddr, value, expected);
}
bool MemoryWriteExclusive16(u64 vaddr, std::uint16_t value, std::uint16_t expected) override {
return parent.system.Memory().WriteExclusive16(vaddr, value, expected);
return memory.WriteExclusive16(vaddr, value, expected);
}
bool MemoryWriteExclusive32(u64 vaddr, std::uint32_t value, std::uint32_t expected) override {
return parent.system.Memory().WriteExclusive32(vaddr, value, expected);
return memory.WriteExclusive32(vaddr, value, expected);
}
bool MemoryWriteExclusive64(u64 vaddr, std::uint64_t value, std::uint64_t expected) override {
return parent.system.Memory().WriteExclusive64(vaddr, value, expected);
return memory.WriteExclusive64(vaddr, value, expected);
}
bool MemoryWriteExclusive128(u64 vaddr, Vector value, Vector expected) override {
return parent.system.Memory().WriteExclusive128(vaddr, value, expected);
return memory.WriteExclusive128(vaddr, value, expected);
}
void InterpreterFallback(u64 pc, std::size_t num_instructions) override {
@@ -102,7 +101,9 @@ public:
}
void CallSVC(u32 swi) override {
Kernel::Svc::Call(parent.system, swi);
parent.svc_called = true;
parent.svc_swi = swi;
parent.jit->HaltExecution();
}
void AddTicks(u64 ticks) override {
@@ -137,6 +138,7 @@ public:
}
ARM_Dynarmic_64& parent;
Core::Memory::Memory& memory;
u64 tpidrro_el0 = 0;
u64 tpidr_el0 = 0;
static constexpr u64 minimum_run_cycles = 1000U;
@@ -182,7 +184,7 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
config.far_code_offset = 256 * 1024 * 1024;
// Safe optimizations
if (Settings::values.cpu_accuracy == Settings::CPUAccuracy::DebugMode) {
if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::DebugMode) {
if (!Settings::values.cpuopt_page_tables) {
config.page_table = nullptr;
}
@@ -210,15 +212,15 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
}
// Unsafe optimizations
if (Settings::values.cpu_accuracy == Settings::CPUAccuracy::Unsafe) {
if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) {
config.unsafe_optimizations = true;
if (Settings::values.cpuopt_unsafe_unfuse_fma) {
if (Settings::values.cpuopt_unsafe_unfuse_fma.GetValue()) {
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
}
if (Settings::values.cpuopt_unsafe_reduce_fp_error) {
if (Settings::values.cpuopt_unsafe_reduce_fp_error.GetValue()) {
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP;
}
if (Settings::values.cpuopt_unsafe_inaccurate_nan) {
if (Settings::values.cpuopt_unsafe_inaccurate_nan.GetValue()) {
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
}
}
@@ -227,11 +229,17 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
}
void ARM_Dynarmic_64::Run() {
jit->Run();
}
void ARM_Dynarmic_64::ExceptionalExit() {
jit->ExceptionalExit();
while (true) {
jit->Run();
if (!svc_called) {
break;
}
svc_called = false;
Kernel::Svc::Call(system, svc_swi);
if (shutdown) {
break;
}
}
}
void ARM_Dynarmic_64::Step() {
@@ -296,10 +304,6 @@ void ARM_Dynarmic_64::SetTPIDR_EL0(u64 value) {
cb->tpidr_el0 = value;
}
void ARM_Dynarmic_64::ChangeProcessorID(std::size_t new_core_id) {
jit->ChangeProcessorID(new_core_id);
}
void ARM_Dynarmic_64::SaveContext(ThreadContext64& ctx) {
ctx.cpu_registers = jit->GetRegisters();
ctx.sp = jit->GetSP();
@@ -324,6 +328,7 @@ void ARM_Dynarmic_64::LoadContext(const ThreadContext64& ctx) {
void ARM_Dynarmic_64::PrepareReschedule() {
jit->HaltExecution();
shutdown = true;
}
void ARM_Dynarmic_64::ClearInstructionCache() {

View File

@@ -7,7 +7,7 @@
#include <memory>
#include <unordered_map>
#include <dynarmic/A64/a64.h>
#include <dynarmic/interface/A64/a64.h>
#include "common/common_types.h"
#include "common/hash.h"
#include "core/arm/arm_interface.h"
@@ -40,12 +40,10 @@ public:
void SetPSTATE(u32 pstate) override;
void Run() override;
void Step() override;
void ExceptionalExit() override;
VAddr GetTlsAddress() const override;
void SetTlsAddress(VAddr address) override;
void SetTPIDR_EL0(u64 value) override;
u64 GetTPIDR_EL0() const override;
void ChangeProcessorID(std::size_t new_core_id) override;
void SaveContext(ThreadContext32& ctx) override {}
void SaveContext(ThreadContext64& ctx) override;
@@ -76,6 +74,12 @@ private:
DynarmicExclusiveMonitor& exclusive_monitor;
std::shared_ptr<Dynarmic::A64::Jit> jit;
// SVC callback
u32 svc_swi{};
bool svc_called{};
bool shutdown{};
};
} // namespace Core

View File

@@ -7,7 +7,7 @@
#include <memory>
#include <optional>
#include <dynarmic/A32/coprocessor.h>
#include <dynarmic/interface/A32/coprocessor.h>
#include "common/common_types.h"
namespace Core {

View File

@@ -7,7 +7,7 @@
#include <memory>
#include <unordered_map>
#include <dynarmic/exclusive_monitor.h>
#include <dynarmic/interface/exclusive_monitor.h>
#include "common/common_types.h"
#include "core/arm/dynarmic/arm_dynarmic_32.h"

View File

@@ -6,7 +6,7 @@
#include <memory>
#include <utility>
#include "common/file_util.h"
#include "common/fs/fs.h"
#include "common/logging/log.h"
#include "common/microprofile.h"
#include "common/settings.h"
@@ -121,7 +121,7 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
dir->GetName());
}
if (Common::FS::IsDirectory(path)) {
if (Common::FS::IsDir(path)) {
return vfs->OpenFile(path + "/main", FileSys::Mode::Read);
}
@@ -173,7 +173,7 @@ struct System::Impl {
const auto current_time = std::chrono::duration_cast<std::chrono::seconds>(
std::chrono::system_clock::now().time_since_epoch());
Settings::values.custom_rtc_differential =
Settings::values.custom_rtc.GetValue().value_or(current_time) - current_time;
Settings::values.custom_rtc.value_or(current_time) - current_time;
// Create a default fs if one doesn't already exist.
if (virtual_filesystem == nullptr)
@@ -289,7 +289,8 @@ struct System::Impl {
telemetry_session->AddField(performance, "Shutdown_EmulationSpeed",
perf_results.emulation_speed * 100.0);
telemetry_session->AddField(performance, "Shutdown_Framerate", perf_results.game_fps);
telemetry_session->AddField(performance, "Shutdown_Framerate",
perf_results.average_game_fps);
telemetry_session->AddField(performance, "Shutdown_Frametime",
perf_results.frametime * 1000.0);
telemetry_session->AddField(performance, "Mean_Frametime_MS",

View File

@@ -18,8 +18,9 @@
#include <mbedtls/cmac.h>
#include <mbedtls/sha256.h>
#include "common/common_funcs.h"
#include "common/common_paths.h"
#include "common/file_util.h"
#include "common/fs/file.h"
#include "common/fs/fs.h"
#include "common/fs/path_util.h"
#include "common/hex_util.h"
#include "common/logging/log.h"
#include "common/settings.h"
@@ -325,46 +326,55 @@ Key128 DeriveKeyblobMACKey(const Key128& keyblob_key, const Key128& mac_source)
}
std::optional<Key128> DeriveSDSeed() {
const Common::FS::IOFile save_43(Common::FS::GetUserPath(Common::FS::UserPath::NANDDir) +
"/system/save/8000000000000043",
"rb+");
const auto system_save_43_path =
Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/8000000000000043";
const Common::FS::IOFile save_43{system_save_43_path, Common::FS::FileAccessMode::Read,
Common::FS::FileType::BinaryFile};
if (!save_43.IsOpen()) {
return std::nullopt;
}
const Common::FS::IOFile sd_private(Common::FS::GetUserPath(Common::FS::UserPath::SDMCDir) +
"/Nintendo/Contents/private",
"rb+");
const auto sd_private_path =
Common::FS::GetYuzuPath(Common::FS::YuzuPath::SDMCDir) / "Nintendo/Contents/private";
const Common::FS::IOFile sd_private{sd_private_path, Common::FS::FileAccessMode::Read,
Common::FS::FileType::BinaryFile};
if (!sd_private.IsOpen()) {
return std::nullopt;
}
std::array<u8, 0x10> private_seed{};
if (sd_private.ReadBytes(private_seed.data(), private_seed.size()) != private_seed.size()) {
if (sd_private.Read(private_seed) != private_seed.size()) {
return std::nullopt;
}
std::array<u8, 0x10> buffer{};
std::size_t offset = 0;
for (; offset + 0x10 < save_43.GetSize(); ++offset) {
if (!save_43.Seek(offset, SEEK_SET)) {
s64 offset = 0;
for (; offset + 0x10 < static_cast<s64>(save_43.GetSize()); ++offset) {
if (!save_43.Seek(offset)) {
return std::nullopt;
}
if (save_43.Read(buffer) != buffer.size()) {
return std::nullopt;
}
save_43.ReadBytes(buffer.data(), buffer.size());
if (buffer == private_seed) {
break;
}
}
if (!save_43.Seek(offset + 0x10, SEEK_SET)) {
if (!save_43.Seek(offset + 0x10)) {
return std::nullopt;
}
Key128 seed{};
if (save_43.ReadBytes(seed.data(), seed.size()) != seed.size()) {
if (save_43.Read(seed) != seed.size()) {
return std::nullopt;
}
return seed;
}
@@ -435,7 +445,7 @@ std::vector<Ticket> GetTicketblob(const Common::FS::IOFile& ticket_save) {
}
std::vector<u8> buffer(ticket_save.GetSize());
if (ticket_save.ReadBytes(buffer.data(), buffer.size()) != buffer.size()) {
if (ticket_save.Read(buffer) != buffer.size()) {
return {};
}
@@ -566,27 +576,26 @@ std::optional<std::pair<Key128, Key128>> ParseTicket(const Ticket& ticket,
KeyManager::KeyManager() {
// Initialize keys
const std::string hactool_keys_dir = Common::FS::GetHactoolConfigurationPath();
const std::string yuzu_keys_dir = Common::FS::GetUserPath(Common::FS::UserPath::KeysDir);
const auto yuzu_keys_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::KeysDir);
if (!Common::FS::Exists(yuzu_keys_dir)) {
Common::FS::CreateDir(yuzu_keys_dir);
if (!Common::FS::CreateDir(yuzu_keys_dir)) {
LOG_ERROR(Core, "Failed to create the keys directory.");
}
if (Settings::values.use_dev_keys) {
dev_mode = true;
AttemptLoadKeyFile(yuzu_keys_dir, hactool_keys_dir, "dev.keys", false);
AttemptLoadKeyFile(yuzu_keys_dir, yuzu_keys_dir, "dev.keys_autogenerated", false);
LoadFromFile(yuzu_keys_dir / "dev.keys", false);
LoadFromFile(yuzu_keys_dir / "dev.keys_autogenerated", false);
} else {
dev_mode = false;
AttemptLoadKeyFile(yuzu_keys_dir, hactool_keys_dir, "prod.keys", false);
AttemptLoadKeyFile(yuzu_keys_dir, yuzu_keys_dir, "prod.keys_autogenerated", false);
LoadFromFile(yuzu_keys_dir / "prod.keys", false);
LoadFromFile(yuzu_keys_dir / "prod.keys_autogenerated", false);
}
AttemptLoadKeyFile(yuzu_keys_dir, hactool_keys_dir, "title.keys", true);
AttemptLoadKeyFile(yuzu_keys_dir, yuzu_keys_dir, "title.keys_autogenerated", true);
AttemptLoadKeyFile(yuzu_keys_dir, hactool_keys_dir, "console.keys", false);
AttemptLoadKeyFile(yuzu_keys_dir, yuzu_keys_dir, "console.keys_autogenerated", false);
LoadFromFile(yuzu_keys_dir / "title.keys", true);
LoadFromFile(yuzu_keys_dir / "title.keys_autogenerated", true);
LoadFromFile(yuzu_keys_dir / "console.keys", false);
LoadFromFile(yuzu_keys_dir / "console.keys_autogenerated", false);
}
static bool ValidCryptoRevisionString(std::string_view base, size_t begin, size_t length) {
@@ -597,9 +606,14 @@ static bool ValidCryptoRevisionString(std::string_view base, size_t begin, size_
[](u8 c) { return std::isxdigit(c); });
}
void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) {
void KeyManager::LoadFromFile(const std::filesystem::path& file_path, bool is_title_keys) {
if (!Common::FS::Exists(file_path)) {
return;
}
std::ifstream file;
Common::FS::OpenFStream(file, filename, std::ios_base::in);
Common::FS::OpenFileStream(file, file_path, std::ios_base::in);
if (!file.is_open()) {
return;
}
@@ -694,15 +708,6 @@ void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) {
}
}
void KeyManager::AttemptLoadKeyFile(const std::string& dir1, const std::string& dir2,
const std::string& filename, bool title) {
if (Common::FS::Exists(dir1 + DIR_SEP + filename)) {
LoadFromFile(dir1 + DIR_SEP + filename, title);
} else if (Common::FS::Exists(dir2 + DIR_SEP + filename)) {
LoadFromFile(dir2 + DIR_SEP + filename, title);
}
}
bool KeyManager::BaseDeriveNecessary() const {
const auto check_key_existence = [this](auto key_type, u64 index1 = 0, u64 index2 = 0) {
return !HasKey(key_type, index1, index2);
@@ -766,30 +771,35 @@ Key256 KeyManager::GetBISKey(u8 partition_id) const {
template <size_t Size>
void KeyManager::WriteKeyToFile(KeyCategory category, std::string_view keyname,
const std::array<u8, Size>& key) {
const std::string yuzu_keys_dir = Common::FS::GetUserPath(Common::FS::UserPath::KeysDir);
const auto yuzu_keys_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::KeysDir);
std::string filename = "title.keys_autogenerated";
if (category == KeyCategory::Standard) {
filename = dev_mode ? "dev.keys_autogenerated" : "prod.keys_autogenerated";
} else if (category == KeyCategory::Console) {
filename = "console.keys_autogenerated";
}
const auto path = yuzu_keys_dir + DIR_SEP + filename;
const auto path = yuzu_keys_dir / filename;
const auto add_info_text = !Common::FS::Exists(path);
Common::FS::CreateFullPath(path);
Common::FS::IOFile file{path, "a"};
Common::FS::IOFile file{path, Common::FS::FileAccessMode::Append,
Common::FS::FileType::TextFile};
if (!file.IsOpen()) {
return;
}
if (add_info_text) {
file.WriteString(
void(file.WriteString(
"# This file is autogenerated by Yuzu\n"
"# It serves to store keys that were automatically generated from the normal keys\n"
"# If you are experiencing issues involving keys, it may help to delete this file\n");
"# If you are experiencing issues involving keys, it may help to delete this file\n"));
}
file.WriteString(fmt::format("\n{} = {}", keyname, Common::HexToString(key)));
AttemptLoadKeyFile(yuzu_keys_dir, yuzu_keys_dir, filename, category == KeyCategory::Title);
void(file.WriteString(fmt::format("\n{} = {}", keyname, Common::HexToString(key))));
LoadFromFile(path, category == KeyCategory::Title);
}
void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) {
@@ -861,20 +871,17 @@ void KeyManager::SetKey(S256KeyType id, Key256 key, u64 field1, u64 field2) {
}
bool KeyManager::KeyFileExists(bool title) {
const std::string hactool_keys_dir = Common::FS::GetHactoolConfigurationPath();
const std::string yuzu_keys_dir = Common::FS::GetUserPath(Common::FS::UserPath::KeysDir);
const auto yuzu_keys_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::KeysDir);
if (title) {
return Common::FS::Exists(hactool_keys_dir + DIR_SEP + "title.keys") ||
Common::FS::Exists(yuzu_keys_dir + DIR_SEP + "title.keys");
return Common::FS::Exists(yuzu_keys_dir / "title.keys");
}
if (Settings::values.use_dev_keys) {
return Common::FS::Exists(hactool_keys_dir + DIR_SEP + "dev.keys") ||
Common::FS::Exists(yuzu_keys_dir + DIR_SEP + "dev.keys");
return Common::FS::Exists(yuzu_keys_dir / "dev.keys");
}
return Common::FS::Exists(hactool_keys_dir + DIR_SEP + "prod.keys") ||
Common::FS::Exists(yuzu_keys_dir + DIR_SEP + "prod.keys");
return Common::FS::Exists(yuzu_keys_dir / "prod.keys");
}
void KeyManager::DeriveSDSeedLazy() {
@@ -1115,15 +1122,21 @@ void KeyManager::PopulateTickets() {
return;
}
const Common::FS::IOFile save1(Common::FS::GetUserPath(Common::FS::UserPath::NANDDir) +
"/system/save/80000000000000e1",
"rb+");
const Common::FS::IOFile save2(Common::FS::GetUserPath(Common::FS::UserPath::NANDDir) +
"/system/save/80000000000000e2",
"rb+");
const auto system_save_e1_path =
Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/80000000000000e1";
const Common::FS::IOFile save_e1{system_save_e1_path, Common::FS::FileAccessMode::Read,
Common::FS::FileType::BinaryFile};
const auto system_save_e2_path =
Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/80000000000000e2";
const Common::FS::IOFile save_e2{system_save_e2_path, Common::FS::FileAccessMode::Read,
Common::FS::FileType::BinaryFile};
const auto blob2 = GetTicketblob(save_e2);
auto res = GetTicketblob(save_e1);
const auto blob2 = GetTicketblob(save2);
auto res = GetTicketblob(save1);
const auto idx = res.size();
res.insert(res.end(), blob2.begin(), blob2.end());

View File

@@ -5,6 +5,7 @@
#pragma once
#include <array>
#include <filesystem>
#include <map>
#include <optional>
#include <string>
@@ -283,9 +284,8 @@ private:
std::array<u8, 576> eticket_extended_kek{};
bool dev_mode;
void LoadFromFile(const std::string& filename, bool is_title_keys);
void AttemptLoadKeyFile(const std::string& dir1, const std::string& dir2,
const std::string& filename, bool title);
void LoadFromFile(const std::filesystem::path& file_path, bool is_title_keys);
template <size_t Size>
void WriteKeyToFile(KeyCategory category, std::string_view keyname,
const std::array<u8, Size>& key);

View File

@@ -3,7 +3,7 @@
// Refer to the license.txt file included.
#include <fmt/format.h>
#include "common/file_util.h"
#include "common/fs/path_util.h"
#include "core/file_sys/bis_factory.h"
#include "core/file_sys/mode.h"
#include "core/file_sys/registered_cache.h"
@@ -85,7 +85,7 @@ VirtualFile BISFactory::OpenPartitionStorage(BisPartitionId id,
VirtualFilesystem file_system) const {
auto& keys = Core::Crypto::KeyManager::Instance();
Core::Crypto::PartitionDataManager pdm{file_system->OpenDirectory(
Common::FS::GetUserPath(Common::FS::UserPath::SysDataDir), Mode::Read)};
Common::FS::GetYuzuPathString(Common::FS::YuzuPath::NANDDir), Mode::Read)};
keys.PopulateFromPartitionData(pdm);
switch (id) {

View File

@@ -10,11 +10,13 @@
namespace FileSys {
enum class Mode : u32 {
Read = 1,
Write = 2,
Read = 1 << 0,
Write = 1 << 1,
ReadWrite = Read | Write,
Append = 4,
Append = 1 << 2,
ReadAppend = Read | Append,
WriteAppend = Write | Append,
All = ReadWrite | Append,
};
DECLARE_ENUM_FLAG_OPERATORS(Mode)

View File

@@ -8,7 +8,6 @@
#include <iterator>
#include <utility>
#include "common/file_util.h"
#include "common/logging/log.h"
#include "core/file_sys/partition_filesystem.h"
#include "core/file_sys/vfs_offset.h"

View File

@@ -7,7 +7,6 @@
#include <cstddef>
#include <cstring>
#include "common/file_util.h"
#include "common/hex_util.h"
#include "common/logging/log.h"
#include "common/settings.h"

View File

@@ -150,7 +150,9 @@ void ProgramMetadata::Print() const {
LOG_DEBUG(Service_FS, " > Is Retail: {}", acid_header.is_retail ? "YES" : "NO");
LOG_DEBUG(Service_FS, "Title ID Min: 0x{:016X}", acid_header.title_id_min);
LOG_DEBUG(Service_FS, "Title ID Max: 0x{:016X}", acid_header.title_id_max);
LOG_DEBUG(Service_FS, "Filesystem Access: 0x{:016X}\n", acid_file_access.permissions);
u64_le permissions_l; // local copy to fix alignment error
std::memcpy(&permissions_l, &acid_file_access.permissions, sizeof(permissions_l));
LOG_DEBUG(Service_FS, "Filesystem Access: 0x{:016X}\n", permissions_l);
// Begin ACI0 printing (actual perms, unsigned)
LOG_DEBUG(Service_FS, "Magic: {:.4}", aci_header.magic.data());

View File

@@ -7,7 +7,7 @@
#include <regex>
#include <mbedtls/sha256.h>
#include "common/assert.h"
#include "common/file_util.h"
#include "common/fs/path_util.h"
#include "common/hex_util.h"
#include "common/logging/log.h"
#include "core/crypto/key_manager.h"

View File

@@ -53,7 +53,7 @@ ResultVal<VirtualFile> RomFSFactory::OpenPatchedRomFS(u64 title_id, ContentRecor
if (nca == nullptr) {
// TODO: Find the right error code to use here
return RESULT_UNKNOWN;
return ResultUnknown;
}
const PatchManager patch_manager{title_id, filesystem_controller, content_provider};
@@ -74,13 +74,13 @@ ResultVal<VirtualFile> RomFSFactory::Open(u64 title_id, StorageId storage,
const std::shared_ptr<NCA> res = GetEntry(title_id, storage, type);
if (res == nullptr) {
// TODO(DarkLordZach): Find the right error code to use here
return RESULT_UNKNOWN;
return ResultUnknown;
}
const auto romfs = res->GetRomFS();
if (romfs == nullptr) {
// TODO(DarkLordZach): Find the right error code to use here
return RESULT_UNKNOWN;
return ResultUnknown;
}
return MakeResult<VirtualFile>(romfs);

View File

@@ -91,7 +91,7 @@ ResultVal<VirtualDir> SaveDataFactory::Create(SaveDataSpaceId space,
// Return an error if the save data doesn't actually exist.
if (out == nullptr) {
// TODO(DarkLordZach): Find out correct error code.
return RESULT_UNKNOWN;
return ResultUnknown;
}
return MakeResult<VirtualDir>(std::move(out));
@@ -105,14 +105,14 @@ ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space,
auto out = dir->GetDirectoryRelative(save_directory);
if (out == nullptr && ShouldSaveDataBeAutomaticallyCreated(space, meta)) {
if (out == nullptr && (ShouldSaveDataBeAutomaticallyCreated(space, meta) && auto_create)) {
return Create(space, meta);
}
// Return an error if the save data doesn't actually exist.
if (out == nullptr) {
// TODO(Subv): Find out correct error code.
return RESULT_UNKNOWN;
return ResultUnknown;
}
return MakeResult<VirtualDir>(std::move(out));
@@ -199,4 +199,8 @@ void SaveDataFactory::WriteSaveDataSize(SaveDataType type, u64 title_id, u128 us
size_file->WriteObject(new_value);
}
void SaveDataFactory::SetAutoCreate(bool state) {
auto_create = state;
}
} // namespace FileSys

View File

@@ -104,9 +104,12 @@ public:
void WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id,
SaveDataSize new_value) const;
void SetAutoCreate(bool state);
private:
VirtualDir dir;
Core::System& system;
bool auto_create{true};
};
} // namespace FileSys

View File

@@ -5,8 +5,7 @@
#include <algorithm>
#include <numeric>
#include <string>
#include "common/common_paths.h"
#include "common/file_util.h"
#include "common/fs/path_util.h"
#include "common/logging/backend.h"
#include "core/file_sys/mode.h"
#include "core/file_sys/vfs.h"
@@ -122,15 +121,14 @@ VirtualDir VfsFilesystem::CopyDirectory(std::string_view old_path_, std::string_
return nullptr;
for (const auto& file : old_dir->GetFiles()) {
const auto x =
CopyFile(old_path + DIR_SEP + file->GetName(), new_path + DIR_SEP + file->GetName());
const auto x = CopyFile(old_path + '/' + file->GetName(), new_path + '/' + file->GetName());
if (x == nullptr)
return nullptr;
}
for (const auto& dir : old_dir->GetSubdirectories()) {
const auto x =
CopyDirectory(old_path + DIR_SEP + dir->GetName(), new_path + DIR_SEP + dir->GetName());
CopyDirectory(old_path + '/' + dir->GetName(), new_path + '/' + dir->GetName());
if (x == nullptr)
return nullptr;
}

View File

@@ -13,6 +13,7 @@
#pragma GCC diagnostic pop
#endif
#include "common/fs/path_util.h"
#include "common/logging/backend.h"
#include "core/file_sys/vfs.h"
#include "core/file_sys/vfs_libzip.h"

View File

@@ -7,8 +7,9 @@
#include <iterator>
#include <utility>
#include "common/assert.h"
#include "common/common_paths.h"
#include "common/file_util.h"
#include "common/fs/file.h"
#include "common/fs/fs.h"
#include "common/fs/path_util.h"
#include "common/logging/log.h"
#include "core/file_sys/vfs_real.h"
@@ -16,33 +17,31 @@ namespace FileSys {
namespace FS = Common::FS;
static std::string ModeFlagsToString(Mode mode) {
std::string mode_str;
namespace {
// Calculate the correct open mode for the file.
if (True(mode & Mode::Read) && True(mode & Mode::Write)) {
if (True(mode & Mode::Append)) {
mode_str = "a+";
} else {
mode_str = "r+";
}
} else {
if (True(mode & Mode::Read)) {
mode_str = "r";
} else if (True(mode & Mode::Append)) {
mode_str = "a";
} else if (True(mode & Mode::Write)) {
mode_str = "w";
} else {
UNREACHABLE_MSG("Invalid file open mode: {:02X}", static_cast<u8>(mode));
}
constexpr FS::FileAccessMode ModeFlagsToFileAccessMode(Mode mode) {
switch (mode) {
case Mode::Read:
return FS::FileAccessMode::Read;
case Mode::Write:
return FS::FileAccessMode::Write;
case Mode::ReadWrite:
return FS::FileAccessMode::ReadWrite;
case Mode::Append:
return FS::FileAccessMode::Append;
case Mode::ReadAppend:
return FS::FileAccessMode::ReadAppend;
case Mode::WriteAppend:
return FS::FileAccessMode::Append;
case Mode::All:
return FS::FileAccessMode::ReadAppend;
default:
return {};
}
mode_str += "b";
return mode_str;
}
} // Anonymous namespace
RealVfsFilesystem::RealVfsFilesystem() : VfsFilesystem(nullptr) {}
RealVfsFilesystem::~RealVfsFilesystem() = default;
@@ -63,7 +62,7 @@ VfsEntryType RealVfsFilesystem::GetEntryType(std::string_view path_) const {
if (!FS::Exists(path)) {
return VfsEntryType::None;
}
if (FS::IsDirectory(path)) {
if (FS::IsDir(path)) {
return VfsEntryType::Directory;
}
@@ -81,12 +80,13 @@ VirtualFile RealVfsFilesystem::OpenFile(std::string_view path_, Mode perms) {
}
}
if (!FS::Exists(path) && True(perms & Mode::WriteAppend)) {
FS::CreateEmptyFile(path);
auto backing = FS::FileOpen(path, ModeFlagsToFileAccessMode(perms), FS::FileType::BinaryFile);
if (!backing) {
return nullptr;
}
auto backing = std::make_shared<FS::IOFile>(path, ModeFlagsToString(perms).c_str());
cache.insert_or_assign(path, backing);
cache.insert_or_assign(path, std::move(backing));
// Cannot use make_shared as RealVfsFile constructor is private
return std::shared_ptr<RealVfsFile>(new RealVfsFile(*this, backing, path, perms));
@@ -94,25 +94,29 @@ VirtualFile RealVfsFilesystem::OpenFile(std::string_view path_, Mode perms) {
VirtualFile RealVfsFilesystem::CreateFile(std::string_view path_, Mode perms) {
const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
const auto path_fwd = FS::SanitizePath(path, FS::DirectorySeparator::ForwardSlash);
if (!FS::Exists(path)) {
FS::CreateFullPath(path_fwd);
if (!FS::CreateEmptyFile(path)) {
// Current usages of CreateFile expect to delete the contents of an existing file.
if (FS::IsFile(path)) {
FS::IOFile temp{path, FS::FileAccessMode::Write, FS::FileType::BinaryFile};
if (!temp.IsOpen()) {
return nullptr;
}
temp.Close();
return OpenFile(path, perms);
}
if (!FS::NewFile(path)) {
return nullptr;
}
return OpenFile(path, perms);
}
VirtualFile RealVfsFilesystem::CopyFile(std::string_view old_path_, std::string_view new_path_) {
const auto old_path = FS::SanitizePath(old_path_, FS::DirectorySeparator::PlatformDefault);
const auto new_path = FS::SanitizePath(new_path_, FS::DirectorySeparator::PlatformDefault);
if (!FS::Exists(old_path) || FS::Exists(new_path) || FS::IsDirectory(old_path) ||
!FS::Copy(old_path, new_path)) {
return nullptr;
}
return OpenFile(new_path, Mode::ReadWrite);
// Unused
return nullptr;
}
VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_view new_path_) {
@@ -127,13 +131,13 @@ VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_
file->Close();
}
if (!FS::Exists(old_path) || FS::Exists(new_path) || FS::IsDirectory(old_path) ||
!FS::Rename(old_path, new_path)) {
if (!FS::RenameFile(old_path, new_path)) {
return nullptr;
}
cache.erase(old_path);
if (file->Open(new_path, "r+b")) {
file->Open(new_path, FS::FileAccessMode::Read, FS::FileType::BinaryFile);
if (file->IsOpen()) {
cache.insert_or_assign(new_path, std::move(file));
} else {
LOG_ERROR(Service_FS, "Failed to open path {} in order to re-cache it", new_path);
@@ -157,7 +161,7 @@ bool RealVfsFilesystem::DeleteFile(std::string_view path_) {
cache.erase(path);
}
return FS::Delete(path);
return FS::RemoveFile(path);
}
VirtualDir RealVfsFilesystem::OpenDirectory(std::string_view path_, Mode perms) {
@@ -168,12 +172,8 @@ VirtualDir RealVfsFilesystem::OpenDirectory(std::string_view path_, Mode perms)
VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, Mode perms) {
const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
const auto path_fwd = FS::SanitizePath(path, FS::DirectorySeparator::ForwardSlash);
if (!FS::Exists(path)) {
FS::CreateFullPath(path_fwd);
if (!FS::CreateDir(path)) {
return nullptr;
}
if (!FS::CreateDirs(path)) {
return nullptr;
}
// Cannot use make_shared as RealVfsDirectory constructor is private
return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms));
@@ -181,13 +181,8 @@ VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, Mode perms
VirtualDir RealVfsFilesystem::CopyDirectory(std::string_view old_path_,
std::string_view new_path_) {
const auto old_path = FS::SanitizePath(old_path_, FS::DirectorySeparator::PlatformDefault);
const auto new_path = FS::SanitizePath(new_path_, FS::DirectorySeparator::PlatformDefault);
if (!FS::Exists(old_path) || FS::Exists(new_path) || !FS::IsDirectory(old_path)) {
return nullptr;
}
FS::CopyDir(old_path, new_path);
return OpenDirectory(new_path, Mode::ReadWrite);
// Unused
return nullptr;
}
VirtualDir RealVfsFilesystem::MoveDirectory(std::string_view old_path_,
@@ -195,8 +190,7 @@ VirtualDir RealVfsFilesystem::MoveDirectory(std::string_view old_path_,
const auto old_path = FS::SanitizePath(old_path_, FS::DirectorySeparator::PlatformDefault);
const auto new_path = FS::SanitizePath(new_path_, FS::DirectorySeparator::PlatformDefault);
if (!FS::Exists(old_path) || FS::Exists(new_path) || FS::IsDirectory(old_path) ||
!FS::Rename(old_path, new_path)) {
if (!FS::RenameDir(old_path, new_path)) {
return nullptr;
}
@@ -208,7 +202,7 @@ VirtualDir RealVfsFilesystem::MoveDirectory(std::string_view old_path_,
const auto file_old_path =
FS::SanitizePath(kv.first, FS::DirectorySeparator::PlatformDefault);
auto file_new_path = FS::SanitizePath(new_path + DIR_SEP + kv.first.substr(old_path.size()),
auto file_new_path = FS::SanitizePath(new_path + '/' + kv.first.substr(old_path.size()),
FS::DirectorySeparator::PlatformDefault);
const auto& cached = cache[file_old_path];
@@ -218,7 +212,8 @@ VirtualDir RealVfsFilesystem::MoveDirectory(std::string_view old_path_,
auto file = cached.lock();
cache.erase(file_old_path);
if (file->Open(file_new_path, "r+b")) {
file->Open(file_new_path, FS::FileAccessMode::Read, FS::FileType::BinaryFile);
if (file->IsOpen()) {
cache.insert_or_assign(std::move(file_new_path), std::move(file));
} else {
LOG_ERROR(Service_FS, "Failed to open path {} in order to re-cache it", file_new_path);
@@ -245,15 +240,13 @@ bool RealVfsFilesystem::DeleteDirectory(std::string_view path_) {
cache.erase(kv.first);
}
return FS::DeleteDirRecursively(path);
return FS::RemoveDirRecursively(path);
}
RealVfsFile::RealVfsFile(RealVfsFilesystem& base_, std::shared_ptr<FS::IOFile> backing_,
const std::string& path_, Mode perms_)
: base(base_), backing(std::move(backing_)), path(path_), parent_path(FS::GetParentPath(path_)),
path_components(FS::SplitPathComponents(path_)),
parent_components(FS::SliceVector(path_components, 0, path_components.size() - 1)),
perms(perms_) {}
path_components(FS::SplitPathComponents(path_)), perms(perms_) {}
RealVfsFile::~RealVfsFile() = default;
@@ -266,7 +259,7 @@ std::size_t RealVfsFile::GetSize() const {
}
bool RealVfsFile::Resize(std::size_t new_size) {
return backing->Resize(new_size);
return backing->SetSize(new_size);
}
VirtualDir RealVfsFile::GetContainingDirectory() const {
@@ -274,33 +267,33 @@ VirtualDir RealVfsFile::GetContainingDirectory() const {
}
bool RealVfsFile::IsWritable() const {
return True(perms & Mode::WriteAppend);
return True(perms & Mode::Write);
}
bool RealVfsFile::IsReadable() const {
return True(perms & Mode::ReadWrite);
return True(perms & Mode::Read);
}
std::size_t RealVfsFile::Read(u8* data, std::size_t length, std::size_t offset) const {
if (!backing->Seek(static_cast<s64>(offset), SEEK_SET)) {
if (!backing->Seek(static_cast<s64>(offset))) {
return 0;
}
return backing->ReadBytes(data, length);
return backing->ReadSpan(std::span{data, length});
}
std::size_t RealVfsFile::Write(const u8* data, std::size_t length, std::size_t offset) {
if (!backing->Seek(static_cast<s64>(offset), SEEK_SET)) {
if (!backing->Seek(static_cast<s64>(offset))) {
return 0;
}
return backing->WriteBytes(data, length);
return backing->WriteSpan(std::span{data, length});
}
bool RealVfsFile::Rename(std::string_view name) {
return base.MoveFile(path, parent_path + DIR_SEP + std::string(name)) != nullptr;
return base.MoveFile(path, parent_path + '/' + std::string(name)) != nullptr;
}
bool RealVfsFile::Close() {
return backing->Close();
void RealVfsFile::Close() {
backing->Close();
}
// TODO(DarkLordZach): MSVC would not let me combine the following two functions using 'if
@@ -313,15 +306,16 @@ std::vector<VirtualFile> RealVfsDirectory::IterateEntries<RealVfsFile, VfsFile>(
}
std::vector<VirtualFile> out;
FS::ForeachDirectoryEntry(
nullptr, path,
[&out, this](u64* entries_out, const std::string& directory, const std::string& filename) {
const std::string full_path = directory + DIR_SEP + filename;
if (!FS::IsDirectory(full_path)) {
out.emplace_back(base.OpenFile(full_path, perms));
}
return true;
});
const FS::DirEntryCallable callback = [this, &out](const std::filesystem::path& full_path) {
const auto full_path_string = FS::PathToUTF8String(full_path);
out.emplace_back(base.OpenFile(full_path_string, perms));
return true;
};
FS::IterateDirEntries(path, callback, FS::DirEntryFilter::File);
return out;
}
@@ -333,42 +327,41 @@ std::vector<VirtualDir> RealVfsDirectory::IterateEntries<RealVfsDirectory, VfsDi
}
std::vector<VirtualDir> out;
FS::ForeachDirectoryEntry(
nullptr, path,
[&out, this](u64* entries_out, const std::string& directory, const std::string& filename) {
const std::string full_path = directory + DIR_SEP + filename;
if (FS::IsDirectory(full_path)) {
out.emplace_back(base.OpenDirectory(full_path, perms));
}
return true;
});
const FS::DirEntryCallable callback = [this, &out](const std::filesystem::path& full_path) {
const auto full_path_string = FS::PathToUTF8String(full_path);
out.emplace_back(base.OpenDirectory(full_path_string, perms));
return true;
};
FS::IterateDirEntries(path, callback, FS::DirEntryFilter::Directory);
return out;
}
RealVfsDirectory::RealVfsDirectory(RealVfsFilesystem& base_, const std::string& path_, Mode perms_)
: base(base_), path(FS::RemoveTrailingSlash(path_)), parent_path(FS::GetParentPath(path)),
path_components(FS::SplitPathComponents(path)),
parent_components(FS::SliceVector(path_components, 0, path_components.size() - 1)),
perms(perms_) {
if (!FS::Exists(path) && True(perms & Mode::WriteAppend)) {
FS::CreateDir(path);
path_components(FS::SplitPathComponents(path)), perms(perms_) {
if (!FS::Exists(path) && True(perms & Mode::Write)) {
void(FS::CreateDirs(path));
}
}
RealVfsDirectory::~RealVfsDirectory() = default;
VirtualFile RealVfsDirectory::GetFileRelative(std::string_view relative_path) const {
const auto full_path = FS::SanitizePath(path + DIR_SEP + std::string(relative_path));
if (!FS::Exists(full_path) || FS::IsDirectory(full_path)) {
const auto full_path = FS::SanitizePath(path + '/' + std::string(relative_path));
if (!FS::Exists(full_path) || FS::IsDir(full_path)) {
return nullptr;
}
return base.OpenFile(full_path, perms);
}
VirtualDir RealVfsDirectory::GetDirectoryRelative(std::string_view relative_path) const {
const auto full_path = FS::SanitizePath(path + DIR_SEP + std::string(relative_path));
if (!FS::Exists(full_path) || !FS::IsDirectory(full_path)) {
const auto full_path = FS::SanitizePath(path + '/' + std::string(relative_path));
if (!FS::Exists(full_path) || !FS::IsDir(full_path)) {
return nullptr;
}
return base.OpenDirectory(full_path, perms);
@@ -383,17 +376,20 @@ VirtualDir RealVfsDirectory::GetSubdirectory(std::string_view name) const {
}
VirtualFile RealVfsDirectory::CreateFileRelative(std::string_view relative_path) {
const auto full_path = FS::SanitizePath(path + DIR_SEP + std::string(relative_path));
const auto full_path = FS::SanitizePath(path + '/' + std::string(relative_path));
if (!FS::CreateParentDirs(full_path)) {
return nullptr;
}
return base.CreateFile(full_path, perms);
}
VirtualDir RealVfsDirectory::CreateDirectoryRelative(std::string_view relative_path) {
const auto full_path = FS::SanitizePath(path + DIR_SEP + std::string(relative_path));
const auto full_path = FS::SanitizePath(path + '/' + std::string(relative_path));
return base.CreateDirectory(full_path, perms);
}
bool RealVfsDirectory::DeleteSubdirectoryRecursive(std::string_view name) {
const auto full_path = FS::SanitizePath(this->path + DIR_SEP + std::string(name));
const auto full_path = FS::SanitizePath(this->path + '/' + std::string(name));
return base.DeleteDirectory(full_path);
}
@@ -406,11 +402,11 @@ std::vector<VirtualDir> RealVfsDirectory::GetSubdirectories() const {
}
bool RealVfsDirectory::IsWritable() const {
return True(perms & Mode::WriteAppend);
return True(perms & Mode::Write);
}
bool RealVfsDirectory::IsReadable() const {
return True(perms & Mode::ReadWrite);
return True(perms & Mode::Read);
}
std::string RealVfsDirectory::GetName() const {
@@ -426,27 +422,27 @@ VirtualDir RealVfsDirectory::GetParentDirectory() const {
}
VirtualDir RealVfsDirectory::CreateSubdirectory(std::string_view name) {
const std::string subdir_path = (path + DIR_SEP).append(name);
const std::string subdir_path = (path + '/').append(name);
return base.CreateDirectory(subdir_path, perms);
}
VirtualFile RealVfsDirectory::CreateFile(std::string_view name) {
const std::string file_path = (path + DIR_SEP).append(name);
const std::string file_path = (path + '/').append(name);
return base.CreateFile(file_path, perms);
}
bool RealVfsDirectory::DeleteSubdirectory(std::string_view name) {
const std::string subdir_path = (path + DIR_SEP).append(name);
const std::string subdir_path = (path + '/').append(name);
return base.DeleteDirectory(subdir_path);
}
bool RealVfsDirectory::DeleteFile(std::string_view name) {
const std::string file_path = (path + DIR_SEP).append(name);
const std::string file_path = (path + '/').append(name);
return base.DeleteFile(file_path);
}
bool RealVfsDirectory::Rename(std::string_view name) {
const std::string new_name = (parent_path + DIR_SEP).append(name);
const std::string new_name = (parent_path + '/').append(name);
return base.MoveFile(path, new_name) != nullptr;
}
@@ -462,14 +458,17 @@ std::map<std::string, VfsEntryType, std::less<>> RealVfsDirectory::GetEntries()
}
std::map<std::string, VfsEntryType, std::less<>> out;
FS::ForeachDirectoryEntry(
nullptr, path,
[&out](u64* entries_out, const std::string& directory, const std::string& filename) {
const std::string full_path = directory + DIR_SEP + filename;
out.emplace(filename,
FS::IsDirectory(full_path) ? VfsEntryType::Directory : VfsEntryType::File);
return true;
});
const FS::DirEntryCallable callback = [&out](const std::filesystem::path& full_path) {
const auto filename = FS::PathToUTF8String(full_path.filename());
out.insert_or_assign(filename,
FS::IsDir(full_path) ? VfsEntryType::Directory : VfsEntryType::File);
return true;
};
FS::IterateDirEntries(path, callback);
return out;
}

View File

@@ -61,14 +61,13 @@ private:
RealVfsFile(RealVfsFilesystem& base, std::shared_ptr<Common::FS::IOFile> backing,
const std::string& path, Mode perms = Mode::Read);
bool Close();
void Close();
RealVfsFilesystem& base;
std::shared_ptr<Common::FS::IOFile> backing;
std::string path;
std::string parent_path;
std::vector<std::string> path_components;
std::vector<std::string> parent_components;
Mode perms;
};
@@ -110,7 +109,6 @@ private:
std::string path;
std::string parent_path;
std::vector<std::string> path_components;
std::vector<std::string> parent_components;
Mode perms;
};

View File

@@ -11,7 +11,7 @@
#include <mbedtls/md.h>
#include <mbedtls/sha256.h>
#include "common/file_util.h"
#include "common/fs/path_util.h"
#include "common/hex_util.h"
#include "common/string_util.h"
#include "core/crypto/aes_util.h"

View File

@@ -27,6 +27,10 @@ struct AnalogProperties {
float range;
float threshold;
};
template <typename StatusType>
struct InputCallback {
std::function<void(StatusType)> on_change;
};
/// An abstract class template for an input device (a button, an analog input, etc.).
template <typename StatusType>
@@ -50,6 +54,17 @@ public:
[[maybe_unused]] f32 freq_high) const {
return {};
}
void SetCallback(InputCallback<StatusType> callback_) {
callback = std::move(callback_);
}
void TriggerOnChange() {
if (callback.on_change) {
callback.on_change(GetStatus());
}
}
private:
InputCallback<StatusType> callback;
};
/// An abstract class template for a factory that can create input devices.

View File

@@ -80,16 +80,12 @@ public:
memset(cmdbuf, 0, sizeof(u32) * IPC::COMMAND_BUFFER_LENGTH);
ctx.ClearIncomingObjects();
IPC::CommandHeader header{};
// The entire size of the raw data section in u32 units, including the 16 bytes of mandatory
// padding.
u32 raw_data_size = ctx.IsTipc()
? normal_params_size - 1
: sizeof(IPC::DataPayloadHeader) / 4 + 4 + normal_params_size;
u32 raw_data_size = ctx.write_size =
ctx.IsTipc() ? normal_params_size - 1 : normal_params_size;
u32 num_handles_to_move{};
u32 num_domain_objects{};
const bool always_move_handles{
@@ -101,16 +97,20 @@ public:
}
if (ctx.Session()->IsDomain()) {
raw_data_size += static_cast<u32>(sizeof(DomainMessageHeader) / 4 + num_domain_objects);
raw_data_size +=
static_cast<u32>(sizeof(DomainMessageHeader) / sizeof(u32) + num_domain_objects);
ctx.write_size += num_domain_objects;
}
if (ctx.IsTipc()) {
header.type.Assign(ctx.GetCommandType());
} else {
raw_data_size += static_cast<u32>(sizeof(IPC::DataPayloadHeader) / sizeof(u32) + 4 +
normal_params_size);
}
ctx.data_size = static_cast<u32>(raw_data_size);
header.data_size.Assign(static_cast<u32>(raw_data_size));
if (num_handles_to_copy != 0 || num_handles_to_move != 0) {
header.data_size.Assign(raw_data_size);
if (num_handles_to_copy || num_handles_to_move) {
header.enable_handle_descriptor.Assign(1);
}
PushRaw(header);
@@ -143,7 +143,8 @@ public:
data_payload_index = index;
ctx.data_payload_offset = index;
ctx.domain_offset = index + raw_data_size / 4;
ctx.write_size += index;
ctx.domain_offset = static_cast<u32>(index + raw_data_size / sizeof(u32));
}
template <class T>
@@ -151,8 +152,8 @@ public:
if (context->Session()->IsDomain()) {
context->AddDomainObject(std::move(iface));
} else {
// kernel.CurrentProcess()->GetResourceLimit()->Reserve(
// Kernel::LimitableResource::Sessions, 1);
kernel.CurrentProcess()->GetResourceLimit()->Reserve(
Kernel::LimitableResource::Sessions, 1);
auto* session = Kernel::KSession::Create(kernel);
session->Initialize(nullptr, iface->GetServiceName());
@@ -167,24 +168,6 @@ public:
PushIpcInterface<T>(std::make_shared<T>(std::forward<Args>(args)...));
}
void ValidateHeader() {
const std::size_t num_domain_objects = context->NumDomainObjects();
const std::size_t num_move_objects = context->NumMoveObjects();
ASSERT_MSG(!num_domain_objects || !num_move_objects,
"cannot move normal handles and domain objects");
ASSERT_MSG((index - data_payload_index) == normal_params_size,
"normal_params_size value is incorrect");
ASSERT_MSG((num_domain_objects + num_move_objects) == num_objects_to_move,
"num_objects_to_move value is incorrect");
ASSERT_MSG(context->NumCopyObjects() == num_handles_to_copy,
"num_handles_to_copy value is incorrect");
}
// Validate on destruction, as there shouldn't be any case where we don't want it
~ResponseBuilder() {
ValidateHeader();
}
void PushImpl(s8 value);
void PushImpl(s16 value);
void PushImpl(s32 value);
@@ -404,7 +387,7 @@ public:
std::shared_ptr<T> PopIpcInterface() {
ASSERT(context->Session()->IsDomain());
ASSERT(context->GetDomainMessageHeader().input_object_count > 0);
return context->GetDomainRequestHandler<T>(Pop<u32>() - 1);
return context->GetDomainHandler<T>(Pop<u32>() - 1);
}
};

View File

@@ -30,16 +30,38 @@
namespace Kernel {
SessionRequestHandler::SessionRequestHandler() = default;
SessionRequestHandler::SessionRequestHandler(KernelCore& kernel_, const char* service_name_)
: kernel{kernel_}, service_thread{kernel.CreateServiceThread(service_name_)} {}
SessionRequestHandler::~SessionRequestHandler() = default;
SessionRequestHandler::~SessionRequestHandler() {
kernel.ReleaseServiceThread(service_thread);
}
SessionRequestManager::SessionRequestManager(KernelCore& kernel_) : kernel{kernel_} {}
SessionRequestManager::~SessionRequestManager() = default;
bool SessionRequestManager::HasSessionRequestHandler(const HLERequestContext& context) const {
if (IsDomain() && context.HasDomainMessageHeader()) {
const auto& message_header = context.GetDomainMessageHeader();
const auto object_id = message_header.object_id;
if (object_id > DomainHandlerCount()) {
LOG_CRITICAL(IPC, "object_id {} is too big!", object_id);
return false;
}
return DomainHandler(object_id - 1) != nullptr;
} else {
return session_handler != nullptr;
}
}
void SessionRequestHandler::ClientConnected(KServerSession* session) {
session->SetHleHandler(shared_from_this());
session->ClientConnected(shared_from_this());
}
void SessionRequestHandler::ClientDisconnected(KServerSession* session) {
session->SetHleHandler(nullptr);
session->ClientDisconnected();
}
HLERequestContext::HLERequestContext(KernelCore& kernel_, Core::Memory::Memory& memory_,
@@ -64,19 +86,15 @@ void HLERequestContext::ParseCommandBuffer(const KHandleTable& handle_table, u32
if (command_header->enable_handle_descriptor) {
handle_descriptor_header = rp.PopRaw<IPC::HandleDescriptorHeader>();
if (handle_descriptor_header->send_current_pid) {
rp.Skip(2, false);
pid = rp.Pop<u64>();
}
if (incoming) {
// Populate the object lists with the data in the IPC request.
for (u32 handle = 0; handle < handle_descriptor_header->num_handles_to_copy; ++handle) {
const u32 copy_handle{rp.Pop<Handle>()};
copy_handles.push_back(copy_handle);
copy_objects.push_back(handle_table.GetObject(copy_handle).GetPointerUnsafe());
incoming_copy_handles.push_back(rp.Pop<Handle>());
}
for (u32 handle = 0; handle < handle_descriptor_header->num_handles_to_move; ++handle) {
const u32 move_handle{rp.Pop<Handle>()};
move_handles.push_back(move_handle);
move_objects.push_back(handle_table.GetObject(move_handle).GetPointerUnsafe());
incoming_move_handles.push_back(rp.Pop<Handle>());
}
} else {
// For responses we just ignore the handles, they're empty and will be populated when
@@ -86,16 +104,16 @@ void HLERequestContext::ParseCommandBuffer(const KHandleTable& handle_table, u32
}
}
for (unsigned i = 0; i < command_header->num_buf_x_descriptors; ++i) {
for (u32 i = 0; i < command_header->num_buf_x_descriptors; ++i) {
buffer_x_desciptors.push_back(rp.PopRaw<IPC::BufferDescriptorX>());
}
for (unsigned i = 0; i < command_header->num_buf_a_descriptors; ++i) {
for (u32 i = 0; i < command_header->num_buf_a_descriptors; ++i) {
buffer_a_desciptors.push_back(rp.PopRaw<IPC::BufferDescriptorABW>());
}
for (unsigned i = 0; i < command_header->num_buf_b_descriptors; ++i) {
for (u32 i = 0; i < command_header->num_buf_b_descriptors; ++i) {
buffer_b_desciptors.push_back(rp.PopRaw<IPC::BufferDescriptorABW>());
}
for (unsigned i = 0; i < command_header->num_buf_w_descriptors; ++i) {
for (u32 i = 0; i < command_header->num_buf_w_descriptors; ++i) {
buffer_w_desciptors.push_back(rp.PopRaw<IPC::BufferDescriptorABW>());
}
@@ -148,14 +166,14 @@ void HLERequestContext::ParseCommandBuffer(const KHandleTable& handle_table, u32
IPC::CommandHeader::BufferDescriptorCFlag::OneDescriptor) {
buffer_c_desciptors.push_back(rp.PopRaw<IPC::BufferDescriptorC>());
} else {
unsigned num_buf_c_descriptors =
static_cast<unsigned>(command_header->buf_c_descriptor_flags.Value()) - 2;
u32 num_buf_c_descriptors =
static_cast<u32>(command_header->buf_c_descriptor_flags.Value()) - 2;
// This is used to detect possible underflows, in case something is broken
// with the two ifs above and the flags value is == 0 || == 1.
ASSERT(num_buf_c_descriptors < 14);
for (unsigned i = 0; i < num_buf_c_descriptors; ++i) {
for (u32 i = 0; i < num_buf_c_descriptors; ++i) {
buffer_c_desciptors.push_back(rp.PopRaw<IPC::BufferDescriptorC>());
}
}
@@ -173,12 +191,12 @@ ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(const KHandleTab
if (command_header->IsCloseCommand()) {
// Close does not populate the rest of the IPC header
return RESULT_SUCCESS;
return ResultSuccess;
}
std::copy_n(src_cmdbuf, IPC::COMMAND_BUFFER_LENGTH, cmd_buf.begin());
return RESULT_SUCCESS;
return ResultSuccess;
}
ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(KThread& requesting_thread) {
@@ -186,26 +204,14 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(KThread& requesting_t
auto& owner_process = *requesting_thread.GetOwnerProcess();
auto& handle_table = owner_process.GetHandleTable();
// The data_size already includes the payload header, the padding and the domain header.
std::size_t size{};
if (IsTipc()) {
size = cmd_buf.size();
} else {
size = data_payload_offset + data_size - sizeof(IPC::DataPayloadHeader) / sizeof(u32) - 4;
if (Session()->IsDomain()) {
size -= sizeof(IPC::DomainMessageHeader) / sizeof(u32);
}
}
for (auto& object : copy_objects) {
for (auto& object : outgoing_copy_objects) {
Handle handle{};
if (object) {
R_TRY(handle_table.Add(&handle, object));
}
cmd_buf[current_offset++] = handle;
}
for (auto& object : move_objects) {
for (auto& object : outgoing_move_objects) {
Handle handle{};
if (object) {
R_TRY(handle_table.Add(&handle, object));
@@ -220,9 +226,9 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(KThread& requesting_t
// TODO(Subv): This completely ignores C buffers.
if (Session()->IsDomain()) {
current_offset = domain_offset - static_cast<u32>(domain_objects.size());
for (const auto& object : domain_objects) {
server_session->AppendDomainRequestHandler(object);
current_offset = domain_offset - static_cast<u32>(outgoing_domain_objects.size());
for (const auto& object : outgoing_domain_objects) {
server_session->AppendDomainHandler(object);
cmd_buf[current_offset++] =
static_cast<u32_le>(server_session->NumDomainRequestHandlers());
}
@@ -230,9 +236,9 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(KThread& requesting_t
// Copy the translated command buffer back into the thread's command buffer area.
memory.WriteBlock(owner_process, requesting_thread.GetTLSAddress(), cmd_buf.data(),
size * sizeof(u32));
write_size * sizeof(u32));
return RESULT_SUCCESS;
return ResultSuccess;
}
std::vector<u8> HLERequestContext::ReadBuffer(std::size_t buffer_index) const {

View File

@@ -11,7 +11,8 @@
#include <string>
#include <type_traits>
#include <vector>
#include <boost/container/small_vector.hpp>
#include "common/assert.h"
#include "common/common_types.h"
#include "common/concepts.h"
#include "common/swap.h"
@@ -45,6 +46,7 @@ class KThread;
class KReadableEvent;
class KSession;
class KWritableEvent;
class ServiceThread;
enum class ThreadWakeupReason;
@@ -55,7 +57,7 @@ enum class ThreadWakeupReason;
*/
class SessionRequestHandler : public std::enable_shared_from_this<SessionRequestHandler> {
public:
SessionRequestHandler();
SessionRequestHandler(KernelCore& kernel, const char* service_name_);
virtual ~SessionRequestHandler();
/**
@@ -82,6 +84,87 @@ public:
* @param server_session ServerSession associated with the connection.
*/
void ClientDisconnected(KServerSession* session);
std::weak_ptr<ServiceThread> GetServiceThread() const {
return service_thread;
}
protected:
KernelCore& kernel;
std::weak_ptr<ServiceThread> service_thread;
};
using SessionRequestHandlerPtr = std::shared_ptr<SessionRequestHandler>;
/**
* Manages the underlying HLE requests for a session, and whether (or not) the session should be
* treated as a domain. This is managed separately from server sessions, as this state is shared
* when objects are cloned.
*/
class SessionRequestManager final {
public:
explicit SessionRequestManager(KernelCore& kernel);
~SessionRequestManager();
bool IsDomain() const {
return is_domain;
}
void ConvertToDomain() {
domain_handlers = {session_handler};
is_domain = true;
}
std::size_t DomainHandlerCount() const {
return domain_handlers.size();
}
bool HasSessionHandler() const {
return session_handler != nullptr;
}
SessionRequestHandler& SessionHandler() {
return *session_handler;
}
const SessionRequestHandler& SessionHandler() const {
return *session_handler;
}
void CloseDomainHandler(std::size_t index) {
if (index < DomainHandlerCount()) {
domain_handlers[index] = nullptr;
} else {
UNREACHABLE_MSG("Unexpected handler index {}", index);
}
}
SessionRequestHandlerPtr DomainHandler(std::size_t index) const {
ASSERT_MSG(index < DomainHandlerCount(), "Unexpected handler index {}", index);
return domain_handlers.at(index);
}
void AppendDomainHandler(SessionRequestHandlerPtr&& handler) {
domain_handlers.emplace_back(std::move(handler));
}
void SetSessionHandler(SessionRequestHandlerPtr&& handler) {
session_handler = std::move(handler);
}
std::weak_ptr<ServiceThread> GetServiceThread() const {
return session_handler->GetServiceThread();
}
bool HasSessionRequestHandler(const HLERequestContext& context) const;
private:
bool is_domain{};
SessionRequestHandlerPtr session_handler;
std::vector<SessionRequestHandlerPtr> domain_handlers;
private:
KernelCore& kernel;
};
/**
@@ -150,6 +233,10 @@ public:
return command_header->type;
}
u64 GetPID() const {
return pid;
}
u32 GetDataPayloadOffset() const {
return data_payload_offset;
}
@@ -220,53 +307,32 @@ public:
bool CanWriteBuffer(std::size_t buffer_index = 0) const;
Handle GetCopyHandle(std::size_t index) const {
return copy_handles.at(index);
return incoming_copy_handles.at(index);
}
Handle GetMoveHandle(std::size_t index) const {
return move_handles.at(index);
return incoming_move_handles.at(index);
}
void AddMoveObject(KAutoObject* object) {
move_objects.emplace_back(object);
outgoing_move_objects.emplace_back(object);
}
void AddCopyObject(KAutoObject* object) {
copy_objects.emplace_back(object);
outgoing_copy_objects.emplace_back(object);
}
void AddDomainObject(std::shared_ptr<SessionRequestHandler> object) {
domain_objects.emplace_back(std::move(object));
void AddDomainObject(SessionRequestHandlerPtr object) {
outgoing_domain_objects.emplace_back(std::move(object));
}
template <typename T>
std::shared_ptr<T> GetDomainRequestHandler(std::size_t index) const {
return std::static_pointer_cast<T>(domain_request_handlers.at(index));
std::shared_ptr<T> GetDomainHandler(std::size_t index) const {
return std::static_pointer_cast<T>(manager->DomainHandler(index));
}
void SetDomainRequestHandlers(
const std::vector<std::shared_ptr<SessionRequestHandler>>& handlers) {
domain_request_handlers = handlers;
}
/// Clears the list of objects so that no lingering objects are written accidentally to the
/// response buffer.
void ClearIncomingObjects() {
move_objects.clear();
copy_objects.clear();
domain_objects.clear();
}
std::size_t NumMoveObjects() const {
return move_objects.size();
}
std::size_t NumCopyObjects() const {
return copy_objects.size();
}
std::size_t NumDomainObjects() const {
return domain_objects.size();
void SetSessionRequestManager(std::shared_ptr<SessionRequestManager> manager_) {
manager = std::move(manager_);
}
std::string Description() const;
@@ -288,12 +354,12 @@ private:
Kernel::KServerSession* server_session{};
KThread* thread;
// TODO(yuriks): Check common usage of this and optimize size accordingly
boost::container::small_vector<Handle, 8> move_handles;
boost::container::small_vector<Handle, 8> copy_handles;
boost::container::small_vector<KAutoObject*, 8> move_objects;
boost::container::small_vector<KAutoObject*, 8> copy_objects;
boost::container::small_vector<std::shared_ptr<SessionRequestHandler>, 8> domain_objects;
std::vector<Handle> incoming_move_handles;
std::vector<Handle> incoming_copy_handles;
std::vector<KAutoObject*> outgoing_move_objects;
std::vector<KAutoObject*> outgoing_copy_objects;
std::vector<SessionRequestHandlerPtr> outgoing_domain_objects;
std::optional<IPC::CommandHeader> command_header;
std::optional<IPC::HandleDescriptorHeader> handle_descriptor_header;
@@ -305,13 +371,14 @@ private:
std::vector<IPC::BufferDescriptorABW> buffer_w_desciptors;
std::vector<IPC::BufferDescriptorC> buffer_c_desciptors;
u32_le command{};
u64 pid{};
u32 write_size{};
u32 data_payload_offset{};
u32 handles_offset{};
u32 domain_offset{};
u32 data_size{};
u32_le command{};
std::vector<std::shared_ptr<SessionRequestHandler>> domain_request_handlers;
std::shared_ptr<SessionRequestManager> manager;
bool is_thread_waiting{};
KernelCore& kernel;

View File

@@ -70,14 +70,22 @@ constexpr size_t SlabCountExtraKThread = 160;
template <typename T>
VAddr InitializeSlabHeap(Core::System& system, KMemoryLayout& memory_layout, VAddr address,
size_t num_objects) {
// TODO(bunnei): This is just a place holder. We should initialize the appropriate KSlabHeap for
// kernel object type T with the backing kernel memory pointer once we emulate kernel memory.
const size_t size = Common::AlignUp(sizeof(T) * num_objects, alignof(void*));
VAddr start = Common::AlignUp(address, alignof(T));
// This is intentionally empty. Once KSlabHeap is fully implemented, we can replace this with
// the pointer to emulated memory to pass along. Until then, KSlabHeap will just allocate/free
// host memory.
void* backing_kernel_memory{};
if (size > 0) {
const KMemoryRegion* region = memory_layout.FindVirtual(start + size - 1);
ASSERT(region != nullptr);
ASSERT(region->IsDerivedFrom(KMemoryRegionType_KernelSlab));
T::InitializeSlabHeap(system.Kernel(), system.Memory().GetKernelBuffer(start, size), size);
T::InitializeSlabHeap(system.Kernel(), backing_kernel_memory, size);
}
return start + size;

View File

@@ -97,7 +97,7 @@ ResultCode KAddressArbiter::Signal(VAddr addr, s32 count) {
while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
(it->GetAddressArbiterKey() == addr)) {
KThread* target_thread = std::addressof(*it);
target_thread->SetSyncedObject(nullptr, RESULT_SUCCESS);
target_thread->SetSyncedObject(nullptr, ResultSuccess);
ASSERT(target_thread->IsWaitingForAddressArbiter());
target_thread->Wakeup();
@@ -107,7 +107,7 @@ ResultCode KAddressArbiter::Signal(VAddr addr, s32 count) {
++num_waiters;
}
}
return RESULT_SUCCESS;
return ResultSuccess;
}
ResultCode KAddressArbiter::SignalAndIncrementIfEqual(VAddr addr, s32 value, s32 count) {
@@ -130,7 +130,7 @@ ResultCode KAddressArbiter::SignalAndIncrementIfEqual(VAddr addr, s32 value, s32
while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
(it->GetAddressArbiterKey() == addr)) {
KThread* target_thread = std::addressof(*it);
target_thread->SetSyncedObject(nullptr, RESULT_SUCCESS);
target_thread->SetSyncedObject(nullptr, ResultSuccess);
ASSERT(target_thread->IsWaitingForAddressArbiter());
target_thread->Wakeup();
@@ -140,7 +140,7 @@ ResultCode KAddressArbiter::SignalAndIncrementIfEqual(VAddr addr, s32 value, s32
++num_waiters;
}
}
return RESULT_SUCCESS;
return ResultSuccess;
}
ResultCode KAddressArbiter::SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32 value, s32 count) {
@@ -198,7 +198,7 @@ ResultCode KAddressArbiter::SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32
while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
(it->GetAddressArbiterKey() == addr)) {
KThread* target_thread = std::addressof(*it);
target_thread->SetSyncedObject(nullptr, RESULT_SUCCESS);
target_thread->SetSyncedObject(nullptr, ResultSuccess);
ASSERT(target_thread->IsWaitingForAddressArbiter());
target_thread->Wakeup();
@@ -208,7 +208,7 @@ ResultCode KAddressArbiter::SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32
++num_waiters;
}
}
return RESULT_SUCCESS;
return ResultSuccess;
}
ResultCode KAddressArbiter::WaitIfLessThan(VAddr addr, s32 value, bool decrement, s64 timeout) {

View File

@@ -37,7 +37,7 @@ public:
return SignalAndModifyByWaitingCountIfEqual(addr, value, count);
}
UNREACHABLE();
return RESULT_UNKNOWN;
return ResultUnknown;
}
[[nodiscard]] ResultCode WaitForAddress(VAddr addr, Svc::ArbitrationType type, s32 value,
@@ -51,7 +51,7 @@ public:
return WaitIfEqual(addr, value, timeout);
}
UNREACHABLE();
return RESULT_UNKNOWN;
return ResultUnknown;
}
private:

View File

@@ -7,10 +7,11 @@
#include <atomic>
#include <string>
#include <boost/intrusive/rbtree.hpp>
#include "common/assert.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/intrusive_red_black_tree.h"
#include "core/hle/kernel/k_class_token.h"
namespace Kernel {
@@ -175,7 +176,7 @@ private:
class KAutoObjectWithListContainer;
class KAutoObjectWithList : public KAutoObject {
class KAutoObjectWithList : public KAutoObject, public boost::intrusive::set_base_hook<> {
public:
explicit KAutoObjectWithList(KernelCore& kernel_) : KAutoObject(kernel_) {}
@@ -192,6 +193,10 @@ public:
}
}
friend bool operator<(const KAutoObjectWithList& left, const KAutoObjectWithList& right) {
return &left < &right;
}
public:
virtual u64 GetId() const {
return reinterpret_cast<u64>(this);
@@ -203,8 +208,6 @@ public:
private:
friend class KAutoObjectWithListContainer;
Common::IntrusiveRedBlackTreeNode list_node;
};
template <typename T>

View File

@@ -9,13 +9,13 @@ namespace Kernel {
void KAutoObjectWithListContainer::Register(KAutoObjectWithList* obj) {
KScopedLightLock lk(m_lock);
m_object_list.insert(*obj);
m_object_list.insert_unique(*obj);
}
void KAutoObjectWithListContainer::Unregister(KAutoObjectWithList* obj) {
KScopedLightLock lk(m_lock);
m_object_list.erase(m_object_list.iterator_to(*obj));
m_object_list.erase(*obj);
}
size_t KAutoObjectWithListContainer::GetOwnedCount(KProcess* owner) {

View File

@@ -6,6 +6,8 @@
#include <atomic>
#include <boost/intrusive/rbtree.hpp>
#include "common/assert.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
@@ -23,8 +25,7 @@ class KAutoObjectWithListContainer {
YUZU_NON_MOVEABLE(KAutoObjectWithListContainer);
public:
using ListType = Common::IntrusiveRedBlackTreeMemberTraits<
&KAutoObjectWithList::list_node>::TreeType<KAutoObjectWithList>;
using ListType = boost::intrusive::rbtree<KAutoObjectWithList>;
public:
class ListAccessor : public KScopedLightLock {

View File

@@ -84,50 +84,43 @@ static_assert(ClassToken<KTransferMemory> == ((0b10010001 << 8) | ClassToken<KAu
// Ensure that the token hierarchy reflects the class hierarchy.
// Base classes.
static_assert(!std::is_final<KSynchronizationObject>::value &&
std::is_base_of<KAutoObject, KSynchronizationObject>::value);
static_assert(!std::is_final<KReadableEvent>::value &&
std::is_base_of<KSynchronizationObject, KReadableEvent>::value);
static_assert(!std::is_final_v<KSynchronizationObject> &&
std::is_base_of_v<KAutoObject, KSynchronizationObject>);
static_assert(!std::is_final_v<KReadableEvent> &&
std::is_base_of_v<KSynchronizationObject, KReadableEvent>);
// Final classes
// static_assert(std::is_final<KInterruptEvent>::value &&
// std::is_base_of<KReadableEvent, KInterruptEvent>::value);
// static_assert(std::is_final<KDebug>::value &&
// std::is_base_of<KSynchronizationObject, KDebug>::value);
static_assert(std::is_final<KThread>::value &&
std::is_base_of<KSynchronizationObject, KThread>::value);
static_assert(std::is_final<KServerPort>::value &&
std::is_base_of<KSynchronizationObject, KServerPort>::value);
static_assert(std::is_final<KServerSession>::value &&
std::is_base_of<KSynchronizationObject, KServerSession>::value);
static_assert(std::is_final<KClientPort>::value &&
std::is_base_of<KSynchronizationObject, KClientPort>::value);
static_assert(std::is_final<KClientSession>::value &&
std::is_base_of<KAutoObject, KClientSession>::value);
static_assert(std::is_final<KProcess>::value &&
std::is_base_of<KSynchronizationObject, KProcess>::value);
static_assert(std::is_final<KResourceLimit>::value &&
std::is_base_of<KAutoObject, KResourceLimit>::value);
// static_assert(std::is_final<KLightSession>::value &&
// std::is_base_of<KAutoObject, KLightSession>::value);
static_assert(std::is_final<KPort>::value && std::is_base_of<KAutoObject, KPort>::value);
static_assert(std::is_final<KSession>::value && std::is_base_of<KAutoObject, KSession>::value);
static_assert(std::is_final<KSharedMemory>::value &&
std::is_base_of<KAutoObject, KSharedMemory>::value);
static_assert(std::is_final<KEvent>::value && std::is_base_of<KAutoObject, KEvent>::value);
static_assert(std::is_final<KWritableEvent>::value &&
std::is_base_of<KAutoObject, KWritableEvent>::value);
// static_assert(std::is_final<KLightClientSession>::value &&
// std::is_base_of<KAutoObject, KLightClientSession>::value);
// static_assert(std::is_final<KLightServerSession>::value &&
// std::is_base_of<KAutoObject, KLightServerSession>::value);
static_assert(std::is_final<KTransferMemory>::value &&
std::is_base_of<KAutoObject, KTransferMemory>::value);
// static_assert(std::is_final<KDeviceAddressSpace>::value &&
// std::is_base_of<KAutoObject, KDeviceAddressSpace>::value);
// static_assert(std::is_final<KSessionRequest>::value &&
// std::is_base_of<KAutoObject, KSessionRequest>::value);
// static_assert(std::is_final<KCodeMemory>::value &&
// std::is_base_of<KAutoObject, KCodeMemory>::value);
// static_assert(std::is_final_v<KInterruptEvent> &&
// std::is_base_of_v<KReadableEvent, KInterruptEvent>);
// static_assert(std::is_final_v<KDebug> &&
// std::is_base_of_v<KSynchronizationObject, KDebug>);
static_assert(std::is_final_v<KThread> && std::is_base_of_v<KSynchronizationObject, KThread>);
static_assert(std::is_final_v<KServerPort> &&
std::is_base_of_v<KSynchronizationObject, KServerPort>);
static_assert(std::is_final_v<KServerSession> &&
std::is_base_of_v<KSynchronizationObject, KServerSession>);
static_assert(std::is_final_v<KClientPort> &&
std::is_base_of_v<KSynchronizationObject, KClientPort>);
static_assert(std::is_final_v<KClientSession> && std::is_base_of_v<KAutoObject, KClientSession>);
static_assert(std::is_final_v<KProcess> && std::is_base_of_v<KSynchronizationObject, KProcess>);
static_assert(std::is_final_v<KResourceLimit> && std::is_base_of_v<KAutoObject, KResourceLimit>);
// static_assert(std::is_final_v<KLightSession> &&
// std::is_base_of_v<KAutoObject, KLightSession>);
static_assert(std::is_final_v<KPort> && std::is_base_of_v<KAutoObject, KPort>);
static_assert(std::is_final_v<KSession> && std::is_base_of_v<KAutoObject, KSession>);
static_assert(std::is_final_v<KSharedMemory> && std::is_base_of_v<KAutoObject, KSharedMemory>);
static_assert(std::is_final_v<KEvent> && std::is_base_of_v<KAutoObject, KEvent>);
static_assert(std::is_final_v<KWritableEvent> && std::is_base_of_v<KAutoObject, KWritableEvent>);
// static_assert(std::is_final_v<KLightClientSession> &&
// std::is_base_of_v<KAutoObject, KLightClientSession>);
// static_assert(std::is_final_v<KLightServerSession> &&
// std::is_base_of_v<KAutoObject, KLightServerSession>);
static_assert(std::is_final_v<KTransferMemory> && std::is_base_of_v<KAutoObject, KTransferMemory>);
// static_assert(std::is_final_v<KDeviceAddressSpace> &&
// std::is_base_of_v<KAutoObject, KDeviceAddressSpace>);
// static_assert(std::is_final_v<KSessionRequest> &&
// std::is_base_of_v<KAutoObject, KSessionRequest>);
// static_assert(std::is_final_v<KCodeMemory> &&
// std::is_base_of_v<KAutoObject, KCodeMemory>);
} // namespace Kernel

View File

@@ -16,11 +16,11 @@ namespace Kernel {
KClientPort::KClientPort(KernelCore& kernel_) : KSynchronizationObject{kernel_} {}
KClientPort::~KClientPort() = default;
void KClientPort::Initialize(KPort* parent_, s32 max_sessions_, std::string&& name_) {
void KClientPort::Initialize(KPort* parent_port_, s32 max_sessions_, std::string&& name_) {
// Set member variables.
num_sessions = 0;
peak_sessions = 0;
parent = parent_;
parent = parent_port_;
max_sessions = max_sessions_;
name = std::move(name_);
}
@@ -28,6 +28,9 @@ void KClientPort::Initialize(KPort* parent_, s32 max_sessions_, std::string&& na
void KClientPort::OnSessionFinalized() {
KScopedSchedulerLock sl{kernel};
// This might happen if a session was improperly used with this port.
ASSERT_MSG(num_sessions > 0, "num_sessions is invalid");
const auto prev = num_sessions--;
if (prev == max_sessions) {
this->NotifyAvailable();
@@ -56,16 +59,17 @@ bool KClientPort::IsSignaled() const {
return num_sessions < max_sessions;
}
ResultCode KClientPort::CreateSession(KClientSession** out) {
ResultCode KClientPort::CreateSession(KClientSession** out,
std::shared_ptr<SessionRequestManager> session_manager) {
// Reserve a new session from the resource limit.
// KScopedResourceReservation session_reservation(kernel.CurrentProcess()->GetResourceLimit(),
// LimitableResource::Sessions);
// R_UNLESS(session_reservation.Succeeded(), ResultLimitReached);
KScopedResourceReservation session_reservation(kernel.CurrentProcess()->GetResourceLimit(),
LimitableResource::Sessions);
R_UNLESS(session_reservation.Succeeded(), ResultLimitReached);
// Update the session counts.
{
// Atomically increment the number of sessions.
s32 new_sessions;
s32 new_sessions{};
{
const auto max = max_sessions;
auto cur_sessions = num_sessions.load(std::memory_order_acquire);
@@ -101,10 +105,10 @@ ResultCode KClientPort::CreateSession(KClientSession** out) {
}
// Initialize the session.
session->Initialize(this, parent->GetName());
session->Initialize(this, parent->GetName(), session_manager);
// Commit the session reservation.
// session_reservation.Commit();
session_reservation.Commit();
// Register the session.
KSession::Register(kernel, session);
@@ -119,7 +123,7 @@ ResultCode KClientPort::CreateSession(KClientSession** out) {
// We succeeded, so set the output.
session_guard.Cancel();
*out = std::addressof(session->GetClientSession());
return RESULT_SUCCESS;
return ResultSuccess;
}
} // namespace Kernel

View File

@@ -16,13 +16,14 @@ namespace Kernel {
class KClientSession;
class KernelCore;
class KPort;
class SessionRequestManager;
class KClientPort final : public KSynchronizationObject {
KERNEL_AUTOOBJECT_TRAITS(KClientPort, KSynchronizationObject);
public:
explicit KClientPort(KernelCore& kernel_);
virtual ~KClientPort() override;
~KClientPort() override;
void Initialize(KPort* parent_, s32 max_sessions_, std::string&& name_);
void OnSessionFinalized();
@@ -31,6 +32,9 @@ public:
const KPort* GetParent() const {
return parent;
}
KPort* GetParent() {
return parent;
}
s32 GetNumSessions() const {
return num_sessions;
@@ -46,10 +50,11 @@ public:
bool IsServerClosed() const;
// Overridden virtual functions.
virtual void Destroy() override;
virtual bool IsSignaled() const override;
void Destroy() override;
bool IsSignaled() const override;
ResultCode CreateSession(KClientSession** out);
ResultCode CreateSession(KClientSession** out,
std::shared_ptr<SessionRequestManager> session_manager = nullptr);
private:
std::atomic<s32> num_sessions{};

View File

@@ -34,15 +34,15 @@ class KClientSession final
public:
explicit KClientSession(KernelCore& kernel_);
virtual ~KClientSession();
~KClientSession() override;
void Initialize(KSession* parent_, std::string&& name_) {
void Initialize(KSession* parent_session_, std::string&& name_) {
// Set member variables.
parent = parent_;
parent = parent_session_;
name = std::move(name_);
}
virtual void Destroy() override;
void Destroy() override;
static void PostDestroy([[maybe_unused]] uintptr_t arg) {}
KSession* GetParent() const {

View File

@@ -86,7 +86,7 @@ ResultCode KConditionVariable::SignalToAddress(VAddr addr) {
next_value |= Svc::HandleWaitMask;
}
next_owner_thread->SetSyncedObject(nullptr, RESULT_SUCCESS);
next_owner_thread->SetSyncedObject(nullptr, ResultSuccess);
next_owner_thread->Wakeup();
}
@@ -100,7 +100,7 @@ ResultCode KConditionVariable::SignalToAddress(VAddr addr) {
}
}
return RESULT_SUCCESS;
return ResultSuccess;
}
ResultCode KConditionVariable::WaitForAddress(Handle handle, VAddr addr, u32 value) {
@@ -112,7 +112,7 @@ ResultCode KConditionVariable::WaitForAddress(Handle handle, VAddr addr, u32 val
ASSERT(owner_thread.IsNull());
{
KScopedSchedulerLock sl(kernel);
cur_thread->SetSyncedObject(nullptr, RESULT_SUCCESS);
cur_thread->SetSyncedObject(nullptr, ResultSuccess);
// Check if the thread should terminate.
R_UNLESS(!cur_thread->IsTerminationRequested(), ResultTerminationRequested);
@@ -124,7 +124,7 @@ ResultCode KConditionVariable::WaitForAddress(Handle handle, VAddr addr, u32 val
ResultInvalidCurrentMemory);
// If the tag isn't the handle (with wait mask), we're done.
R_UNLESS(test_tag == (handle | Svc::HandleWaitMask), RESULT_SUCCESS);
R_UNLESS(test_tag == (handle | Svc::HandleWaitMask), ResultSuccess);
// Get the lock owner thread.
owner_thread =
@@ -181,7 +181,7 @@ KThread* KConditionVariable::SignalImpl(KThread* thread) {
if (can_access) {
if (prev_tag == Svc::InvalidHandle) {
// If nobody held the lock previously, we're all good.
thread->SetSyncedObject(nullptr, RESULT_SUCCESS);
thread->SetSyncedObject(nullptr, ResultSuccess);
thread->Wakeup();
} else {
// Get the previous owner.
@@ -292,7 +292,7 @@ ResultCode KConditionVariable::Wait(VAddr addr, u64 key, u32 value, s64 timeout)
}
// Wake up the next owner.
next_owner_thread->SetSyncedObject(nullptr, RESULT_SUCCESS);
next_owner_thread->SetSyncedObject(nullptr, ResultSuccess);
next_owner_thread->Wakeup();
}

View File

@@ -20,23 +20,21 @@ class KEvent final : public KAutoObjectWithSlabHeapAndContainer<KEvent, KAutoObj
public:
explicit KEvent(KernelCore& kernel_);
virtual ~KEvent();
~KEvent() override;
void Initialize(std::string&& name);
virtual void Finalize() override;
void Finalize() override;
virtual bool IsInitialized() const override {
bool IsInitialized() const override {
return initialized;
}
virtual uintptr_t GetPostDestroyArgument() const override {
uintptr_t GetPostDestroyArgument() const override {
return reinterpret_cast<uintptr_t>(owner);
}
static void PostDestroy(uintptr_t arg);
virtual KProcess* GetOwner() const override {
KProcess* GetOwner() const override {
return owner;
}
@@ -48,6 +46,8 @@ public:
return writable_event;
}
static void PostDestroy(uintptr_t arg);
private:
KReadableEvent readable_event;
KWritableEvent writable_event;

View File

@@ -25,7 +25,7 @@ ResultCode KHandleTable::Finalize() {
}
}
return RESULT_SUCCESS;
return ResultSuccess;
}
bool KHandleTable::Remove(Handle handle) {
@@ -79,7 +79,7 @@ ResultCode KHandleTable::Add(Handle* out_handle, KAutoObject* obj, u16 type) {
*out_handle = EncodeHandle(static_cast<u16>(index), linear_id);
}
return RESULT_SUCCESS;
return ResultSuccess;
}
ResultCode KHandleTable::Reserve(Handle* out_handle) {
@@ -89,7 +89,7 @@ ResultCode KHandleTable::Reserve(Handle* out_handle) {
R_UNLESS(m_count < m_table_size, ResultOutOfHandles);
*out_handle = EncodeHandle(static_cast<u16>(this->AllocateEntry()), this->AllocateLinearId());
return RESULT_SUCCESS;
return ResultSuccess;
}
void KHandleTable::Unreserve(Handle handle) {

View File

@@ -50,7 +50,7 @@ public:
m_free_head_index = i;
}
return RESULT_SUCCESS;
return ResultSuccess;
}
size_t GetTableSize() const {

View File

@@ -86,7 +86,7 @@ ResultCode KMemoryManager::Allocate(KPageLinkedList& page_list, std::size_t num_
// Early return if we're allocating no pages
if (num_pages == 0) {
return RESULT_SUCCESS;
return ResultSuccess;
}
// Lock the pool that we're allocating from
@@ -146,14 +146,14 @@ ResultCode KMemoryManager::Allocate(KPageLinkedList& page_list, std::size_t num_
// We succeeded!
group_guard.Cancel();
return RESULT_SUCCESS;
return ResultSuccess;
}
ResultCode KMemoryManager::Free(KPageLinkedList& page_list, std::size_t num_pages, Pool pool,
Direction dir) {
// Early return if we're freeing no pages
if (!num_pages) {
return RESULT_SUCCESS;
return ResultSuccess;
}
// Lock the pool that we're freeing from
@@ -170,7 +170,7 @@ ResultCode KMemoryManager::Free(KPageLinkedList& page_list, std::size_t num_page
chosen_manager.Free(it.GetAddress(), min_num_pages);
}
return RESULT_SUCCESS;
return ResultSuccess;
}
std::size_t KMemoryManager::Impl::CalculateManagementOverheadSize(std::size_t region_size) {

View File

@@ -71,7 +71,7 @@ public:
ResultCode AddBlock(u64 address, u64 num_pages) {
if (!num_pages) {
return RESULT_SUCCESS;
return ResultSuccess;
}
if (!nodes.empty()) {
const auto node = nodes.back();
@@ -82,7 +82,7 @@ public:
}
}
nodes.push_back({address, num_pages});
return RESULT_SUCCESS;
return ResultSuccess;
}
private:

View File

@@ -292,7 +292,7 @@ ResultCode KPageTable::MapProcessCode(VAddr addr, std::size_t num_pages, KMemory
block_manager->Update(addr, num_pages, state, perm);
return RESULT_SUCCESS;
return ResultSuccess;
}
ResultCode KPageTable::MapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) {
@@ -329,14 +329,14 @@ ResultCode KPageTable::MapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, std:
KMemoryAttribute::Locked);
block_manager->Update(dst_addr, num_pages, KMemoryState::AliasCode);
return RESULT_SUCCESS;
return ResultSuccess;
}
ResultCode KPageTable::UnmapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) {
std::lock_guard lock{page_table_lock};
if (!size) {
return RESULT_SUCCESS;
return ResultSuccess;
}
const std::size_t num_pages{size / PageSize};
@@ -360,7 +360,7 @@ ResultCode KPageTable::UnmapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, st
block_manager->Update(src_addr, num_pages, KMemoryState::Normal,
KMemoryPermission::ReadAndWrite);
return RESULT_SUCCESS;
return ResultSuccess;
}
void KPageTable::MapPhysicalMemory(KPageLinkedList& page_linked_list, VAddr start, VAddr end) {
@@ -408,7 +408,7 @@ ResultCode KPageTable::MapPhysicalMemory(VAddr addr, std::size_t size) {
});
if (mapped_size == size) {
return RESULT_SUCCESS;
return ResultSuccess;
}
const std::size_t remaining_size{size - mapped_size};
@@ -440,14 +440,14 @@ ResultCode KPageTable::MapPhysicalMemory(VAddr addr, std::size_t size) {
KMemoryAttribute::None, KMemoryState::Normal,
KMemoryPermission::ReadAndWrite, KMemoryAttribute::None);
return RESULT_SUCCESS;
return ResultSuccess;
}
ResultCode KPageTable::UnmapPhysicalMemory(VAddr addr, std::size_t size) {
std::lock_guard lock{page_table_lock};
const VAddr end_addr{addr + size};
ResultCode result{RESULT_SUCCESS};
ResultCode result{ResultSuccess};
std::size_t mapped_size{};
// Verify that the region can be unmapped
@@ -468,7 +468,7 @@ ResultCode KPageTable::UnmapPhysicalMemory(VAddr addr, std::size_t size) {
}
if (!mapped_size) {
return RESULT_SUCCESS;
return ResultSuccess;
}
CASCADE_CODE(UnmapMemory(addr, size));
@@ -477,14 +477,14 @@ ResultCode KPageTable::UnmapPhysicalMemory(VAddr addr, std::size_t size) {
process->GetResourceLimit()->Release(LimitableResource::PhysicalMemory, mapped_size);
physical_memory_usage -= mapped_size;
return RESULT_SUCCESS;
return ResultSuccess;
}
ResultCode KPageTable::UnmapMemory(VAddr addr, std::size_t size) {
std::lock_guard lock{page_table_lock};
const VAddr end_addr{addr + size};
ResultCode result{RESULT_SUCCESS};
ResultCode result{ResultSuccess};
KPageLinkedList page_linked_list;
// Unmap each region within the range
@@ -513,7 +513,7 @@ ResultCode KPageTable::UnmapMemory(VAddr addr, std::size_t size) {
block_manager->Update(addr, num_pages, KMemoryState::Free);
return RESULT_SUCCESS;
return ResultSuccess;
}
ResultCode KPageTable::Map(VAddr dst_addr, VAddr src_addr, std::size_t size) {
@@ -552,7 +552,7 @@ ResultCode KPageTable::Map(VAddr dst_addr, VAddr src_addr, std::size_t size) {
block_manager->Update(dst_addr, num_pages, KMemoryState::Stack,
KMemoryPermission::ReadAndWrite);
return RESULT_SUCCESS;
return ResultSuccess;
}
ResultCode KPageTable::Unmap(VAddr dst_addr, VAddr src_addr, std::size_t size) {
@@ -594,7 +594,7 @@ ResultCode KPageTable::Unmap(VAddr dst_addr, VAddr src_addr, std::size_t size) {
block_manager->Update(src_addr, num_pages, src_state, KMemoryPermission::ReadAndWrite);
block_manager->Update(dst_addr, num_pages, KMemoryState::Free);
return RESULT_SUCCESS;
return ResultSuccess;
}
ResultCode KPageTable::MapPages(VAddr addr, const KPageLinkedList& page_linked_list,
@@ -616,7 +616,7 @@ ResultCode KPageTable::MapPages(VAddr addr, const KPageLinkedList& page_linked_l
cur_addr += node.GetNumPages() * PageSize;
}
return RESULT_SUCCESS;
return ResultSuccess;
}
ResultCode KPageTable::MapPages(VAddr addr, KPageLinkedList& page_linked_list, KMemoryState state,
@@ -638,7 +638,7 @@ ResultCode KPageTable::MapPages(VAddr addr, KPageLinkedList& page_linked_list, K
block_manager->Update(addr, num_pages, state, perm);
return RESULT_SUCCESS;
return ResultSuccess;
}
ResultCode KPageTable::UnmapPages(VAddr addr, const KPageLinkedList& page_linked_list) {
@@ -655,7 +655,7 @@ ResultCode KPageTable::UnmapPages(VAddr addr, const KPageLinkedList& page_linked
cur_addr += node.GetNumPages() * PageSize;
}
return RESULT_SUCCESS;
return ResultSuccess;
}
ResultCode KPageTable::UnmapPages(VAddr addr, KPageLinkedList& page_linked_list,
@@ -677,7 +677,7 @@ ResultCode KPageTable::UnmapPages(VAddr addr, KPageLinkedList& page_linked_list,
block_manager->Update(addr, num_pages, state, KMemoryPermission::None);
return RESULT_SUCCESS;
return ResultSuccess;
}
ResultCode KPageTable::SetCodeMemoryPermission(VAddr addr, std::size_t size,
@@ -708,7 +708,7 @@ ResultCode KPageTable::SetCodeMemoryPermission(VAddr addr, std::size_t size,
// Return early if there is nothing to change
if (state == prev_state && perm == prev_perm) {
return RESULT_SUCCESS;
return ResultSuccess;
}
if ((prev_perm & KMemoryPermission::Execute) != (perm & KMemoryPermission::Execute)) {
@@ -725,7 +725,7 @@ ResultCode KPageTable::SetCodeMemoryPermission(VAddr addr, std::size_t size,
block_manager->Update(addr, num_pages, state, perm);
return RESULT_SUCCESS;
return ResultSuccess;
}
KMemoryInfo KPageTable::QueryInfoImpl(VAddr addr) {
@@ -758,7 +758,7 @@ ResultCode KPageTable::ReserveTransferMemory(VAddr addr, std::size_t size, KMemo
block_manager->Update(addr, size / PageSize, state, perm, attribute | KMemoryAttribute::Locked);
return RESULT_SUCCESS;
return ResultSuccess;
}
ResultCode KPageTable::ResetTransferMemory(VAddr addr, std::size_t size) {
@@ -775,7 +775,7 @@ ResultCode KPageTable::ResetTransferMemory(VAddr addr, std::size_t size) {
block_manager->Update(addr, size / PageSize, state, KMemoryPermission::ReadAndWrite);
return RESULT_SUCCESS;
return ResultSuccess;
}
ResultCode KPageTable::SetMemoryAttribute(VAddr addr, std::size_t size, KMemoryAttribute mask,
@@ -797,13 +797,13 @@ ResultCode KPageTable::SetMemoryAttribute(VAddr addr, std::size_t size, KMemoryA
block_manager->Update(addr, size / PageSize, state, perm, attribute);
return RESULT_SUCCESS;
return ResultSuccess;
}
ResultCode KPageTable::SetHeapCapacity(std::size_t new_heap_capacity) {
std::lock_guard lock{page_table_lock};
heap_capacity = new_heap_capacity;
return RESULT_SUCCESS;
return ResultSuccess;
}
ResultVal<VAddr> KPageTable::SetHeapSize(std::size_t size) {
@@ -911,7 +911,7 @@ ResultCode KPageTable::LockForDeviceAddressSpace(VAddr addr, std::size_t size) {
},
perm);
return RESULT_SUCCESS;
return ResultSuccess;
}
ResultCode KPageTable::UnlockForDeviceAddressSpace(VAddr addr, std::size_t size) {
@@ -934,13 +934,13 @@ ResultCode KPageTable::UnlockForDeviceAddressSpace(VAddr addr, std::size_t size)
},
perm);
return RESULT_SUCCESS;
return ResultSuccess;
}
ResultCode KPageTable::InitializeMemoryLayout(VAddr start, VAddr end) {
block_manager = std::make_unique<KMemoryBlockManager>(start, end);
return RESULT_SUCCESS;
return ResultSuccess;
}
bool KPageTable::IsRegionMapped(VAddr address, u64 size) {
@@ -1006,7 +1006,7 @@ ResultCode KPageTable::Operate(VAddr addr, std::size_t num_pages, const KPageLin
addr += size;
}
return RESULT_SUCCESS;
return ResultSuccess;
}
ResultCode KPageTable::Operate(VAddr addr, std::size_t num_pages, KMemoryPermission perm,
@@ -1033,7 +1033,7 @@ ResultCode KPageTable::Operate(VAddr addr, std::size_t num_pages, KMemoryPermiss
default:
UNREACHABLE();
}
return RESULT_SUCCESS;
return ResultSuccess;
}
constexpr VAddr KPageTable::GetRegionAddress(KMemoryState state) const {
@@ -1164,7 +1164,7 @@ constexpr ResultCode KPageTable::CheckMemoryState(const KMemoryInfo& info, KMemo
return ResultInvalidCurrentMemory;
}
return RESULT_SUCCESS;
return ResultSuccess;
}
ResultCode KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm,
@@ -1223,7 +1223,7 @@ ResultCode KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermissi
*out_attr = first_attr & static_cast<KMemoryAttribute>(~ignore_attr);
}
return RESULT_SUCCESS;
return ResultSuccess;
}
} // namespace Kernel

View File

@@ -56,13 +56,10 @@ ResultCode KPort::EnqueueSession(KServerSession* session) {
R_UNLESS(state == State::Normal, ResultPortClosed);
if (server.HasHLEHandler()) {
server.GetHLEHandler()->ClientConnected(session);
} else {
server.EnqueueSession(session);
}
server.EnqueueSession(session);
server.GetSessionRequestHandler()->ClientConnected(server.AcceptSession());
return RESULT_SUCCESS;
return ResultSuccess;
}
} // namespace Kernel

View File

@@ -22,7 +22,7 @@ class KPort final : public KAutoObjectWithSlabHeapAndContainer<KPort, KAutoObjec
public:
explicit KPort(KernelCore& kernel_);
virtual ~KPort();
~KPort() override;
static void PostDestroy([[maybe_unused]] uintptr_t arg) {}
@@ -59,7 +59,6 @@ private:
ServerClosed = 3,
};
private:
KServerPort server;
KClientPort client;
State state{State::Invalid};

View File

@@ -142,7 +142,7 @@ ResultCode KProcess::Initialize(KProcess* process, Core::System& system, std::st
// Open a reference to the resource limit.
process->resource_limit->Open();
return RESULT_SUCCESS;
return ResultSuccess;
}
KResourceLimit* KProcess::GetResourceLimit() const {
@@ -258,7 +258,7 @@ ResultCode KProcess::AddSharedMemory(KSharedMemory* shmem, [[maybe_unused]] VAdd
// Open a reference to the shared memory.
shmem->Open();
return RESULT_SUCCESS;
return ResultSuccess;
}
void KProcess::RemoveSharedMemory(KSharedMemory* shmem, [[maybe_unused]] VAddr address,
@@ -291,7 +291,7 @@ ResultCode KProcess::Reset() {
// Clear signaled.
is_signaled = false;
return RESULT_SUCCESS;
return ResultSuccess;
}
ResultCode KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata,
@@ -524,7 +524,7 @@ ResultCode KProcess::AllocateMainThreadStack(std::size_t stack_size) {
main_thread_stack_top += main_thread_stack_size;
return RESULT_SUCCESS;
return ResultSuccess;
}
} // namespace Kernel

View File

@@ -310,7 +310,7 @@ public:
*
* @param metadata The provided metadata to load process specific info from.
*
* @returns RESULT_SUCCESS if all relevant metadata was able to be
* @returns ResultSuccess if all relevant metadata was able to be
* loaded and parsed. Otherwise, an error code is returned.
*/
ResultCode LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size);
@@ -331,19 +331,19 @@ public:
void LoadModule(CodeSet code_set, VAddr base_addr);
virtual bool IsInitialized() const override {
bool IsInitialized() const override {
return is_initialized;
}
static void PostDestroy([[maybe_unused]] uintptr_t arg) {}
virtual void Finalize();
void Finalize() override;
virtual u64 GetId() const override final {
u64 GetId() const override {
return GetProcessID();
}
virtual bool IsSignaled() const override;
bool IsSignaled() const override;
void PinCurrentThread();
void UnpinCurrentThread();

View File

@@ -36,13 +36,13 @@ ResultCode KReadableEvent::Signal() {
NotifyAvailable();
}
return RESULT_SUCCESS;
return ResultSuccess;
}
ResultCode KReadableEvent::Clear() {
Reset();
return RESULT_SUCCESS;
return ResultSuccess;
}
ResultCode KReadableEvent::Reset() {
@@ -53,7 +53,7 @@ ResultCode KReadableEvent::Reset() {
}
is_signaled = false;
return RESULT_SUCCESS;
return ResultSuccess;
}
} // namespace Kernel

View File

@@ -21,9 +21,9 @@ public:
explicit KReadableEvent(KernelCore& kernel_);
~KReadableEvent() override;
void Initialize(KEvent* parent_, std::string&& name_) {
void Initialize(KEvent* parent_event_, std::string&& name_) {
is_signaled = false;
parent = parent_;
parent = parent_event_;
name = std::move(name_);
}
@@ -31,8 +31,8 @@ public:
return parent;
}
virtual bool IsSignaled() const override;
virtual void Destroy() override;
bool IsSignaled() const override;
void Destroy() override;
ResultCode Signal();
ResultCode Clear();

View File

@@ -80,7 +80,7 @@ ResultCode KResourceLimit::SetLimitValue(LimitableResource which, s64 value) {
limit_values[index] = value;
return RESULT_SUCCESS;
return ResultSuccess;
}
bool KResourceLimit::Reserve(LimitableResource which, s64 value) {

View File

@@ -37,10 +37,10 @@ class KResourceLimit final
public:
explicit KResourceLimit(KernelCore& kernel_);
virtual ~KResourceLimit();
~KResourceLimit() override;
void Initialize(const Core::Timing::CoreTiming* core_timing_);
virtual void Finalize() override;
void Finalize() override;
s64 GetLimitValue(LimitableResource which) const;
s64 GetCurrentValue(LimitableResource which) const;

View File

@@ -659,7 +659,6 @@ void KScheduler::Unload(KThread* thread) {
if (thread) {
if (thread->IsCallingSvc()) {
system.ArmInterface(core_id).ExceptionalExit();
thread->ClearIsCallingSvc();
}
if (!thread->IsTerminationRequested()) {

View File

@@ -17,9 +17,9 @@ namespace Kernel {
KServerPort::KServerPort(KernelCore& kernel_) : KSynchronizationObject{kernel_} {}
KServerPort::~KServerPort() = default;
void KServerPort::Initialize(KPort* parent_, std::string&& name_) {
void KServerPort::Initialize(KPort* parent_port_, std::string&& name_) {
// Set member variables.
parent = parent_;
parent = parent_port_;
name = std::move(name_);
}

View File

@@ -25,33 +25,28 @@ class SessionRequestHandler;
class KServerPort final : public KSynchronizationObject {
KERNEL_AUTOOBJECT_TRAITS(KServerPort, KSynchronizationObject);
private:
using SessionList = boost::intrusive::list<KServerSession>;
public:
explicit KServerPort(KernelCore& kernel_);
virtual ~KServerPort() override;
~KServerPort() override;
using HLEHandler = std::shared_ptr<SessionRequestHandler>;
void Initialize(KPort* parent_, std::string&& name_);
void Initialize(KPort* parent_port_, std::string&& name_);
/// Whether or not this server port has an HLE handler available.
bool HasHLEHandler() const {
return hle_handler != nullptr;
bool HasSessionRequestHandler() const {
return session_handler != nullptr;
}
/// Gets the HLE handler for this port.
HLEHandler GetHLEHandler() const {
return hle_handler;
SessionRequestHandlerPtr GetSessionRequestHandler() const {
return session_handler;
}
/**
* Sets the HLE handler template for the port. ServerSessions crated by connecting to this port
* will inherit a reference to this handler.
*/
void SetHleHandler(HLEHandler hle_handler_) {
hle_handler = std::move(hle_handler_);
void SetSessionHandler(SessionRequestHandlerPtr&& handler) {
session_handler = std::move(handler);
}
void EnqueueSession(KServerSession* pending_session);
@@ -65,15 +60,16 @@ public:
bool IsLight() const;
// Overridden virtual functions.
virtual void Destroy() override;
virtual bool IsSignaled() const override;
void Destroy() override;
bool IsSignaled() const override;
private:
using SessionList = boost::intrusive::list<KServerSession>;
void CleanupSessions();
private:
SessionList session_list;
HLEHandler hle_handler;
SessionRequestHandlerPtr session_handler;
KPort* parent{};
};

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