Compare commits

...

209 Commits

Author SHA1 Message Date
Fernando Sahmkow
e9e671d101 GPU Garbage Collection: Fix regressions. 2022-04-01 01:36:24 +02:00
Fernando S
078a6cb0cb Merge pull request #8116 from ameerj/nvhost_ctrl_bad_param
nvhost_ctrl: Only mark EventState::Busy as BadParameter
2022-04-01 01:29:46 +02:00
bunnei
9b52ee4558 Merge pull request #8076 from ameerj/nv-vk-msaa-scale
Vulkan: Use 3D helpers for MSAA scaling on NV drivers 510+
2022-03-30 23:00:44 -07:00
bunnei
c6aa4ec687 Merge pull request #8120 from german77/signal
service: hid: Signal event on AcquireNpadStyleSetUpdateEventHandle
2022-03-30 22:45:40 -07:00
bunnei
365c6ad753 Merge pull request #8090 from bunnei/fix-skyline
Kernel Memory Updates (Part 7): Various fixes to code memory (Skyline support)
2022-03-30 18:23:37 -07:00
Narr the Reg
c04e65c254 service: hid: Signal event on AcquireNpadStyleSetUpdateEventHandle 2022-03-30 17:09:28 -06:00
bunnei
8e9d253687 Merge pull request #8107 from german77/fullscreen
yuzu: Only override fullscreen setting if gamepath or argument is provided
2022-03-29 17:43:10 -07:00
ameerj
69807dd208 nvhost_ctrl: Only mark EventState::Busy as BadParameter
Fixes an svc break in Kirby and the Forgotten Land with async GPU enabled.
2022-03-29 15:13:30 -04:00
Morph
6552968df4 Merge pull request #8109 from lat9nq/god-why
yuzu_cmd: Start the logging backend
2022-03-29 03:52:50 -04:00
bunnei
063d38a991 Merge pull request #8106 from ameerj/gl-extreme-crash
gl_rasterizer: Avoid scenario locking already owned mutex
2022-03-28 21:36:39 -07:00
lat9nq
0d7a0c0817 yuzu_cmd: Start the logging backend 2022-03-28 23:14:42 -04:00
german77
90c846d1ad yuzu: Only override fullscreen setting if gamepath or argument is provided 2022-03-28 18:52:27 -06:00
ameerj
3e489782e1 gl_rasterizer: Avoid scenario locking already owned mutex
gpu.TickWork() may lock the texture_cache and buffer_cache mutexes, which are owned by the thread prior to invoking TickWork().

Defer invoking gpu.TickWork() until the scope ends, where the owned mutexes are released.
2022-03-28 20:35:15 -04:00
bunnei
642913b0d1 Merge pull request #8098 from merryhime/ic-ivau
dynarmic: Invalidate CPU cache on all cores
2022-03-28 15:45:52 -07:00
Mai M
7382e7a5c8 Merge pull request #8095 from bylaws/master
shader_recompiler: Include <bit> header when std::count{r,l}_zero is used
2022-03-27 12:36:19 -04:00
merry
1383441b15 arm_dynarmic_64: Invalidate on all cores 2022-03-27 15:37:19 +01:00
merry
3a9a0d9cb3 externals: Update dynarmic
IC instructions now check for a need to halt execution
2022-03-27 15:37:06 +01:00
Fernando S
99654721eb Merge pull request #8088 from bunnei/fixup-nvflinger
Follow-up fixes for NVFlinger rewrite
2022-03-27 13:09:59 +02:00
bunnei
e043cc0bc3 Merge pull request #8092 from yuzu-emu/revert-8080-yo-momma-so-fat-that
Revert "Memory GPU <-> CPU: reduce infighting in the texture cache by adding CPU Cached memory."
2022-03-26 12:39:31 -07:00
bunnei
af04f8b8e9 Revert "Memory GPU <-> CPU: reduce infighting in the texture cache by adding CPU Cached memory." 2022-03-26 12:38:30 -07:00
bunnei
9408100a80 hle: kernel: k_page_table: Fix implementations of LockForCodeMemory & UnlockForCodeMemory. 2022-03-26 01:49:34 -07:00
bunnei
0c75913bf2 hle: kernel: k_page_table: Implement LockMemoryAndOpen & UnlockMemory. 2022-03-26 01:46:41 -07:00
bunnei
1b3dba329a hle: kernel: svc: MapProcessMemory: Fix usage of KPageLinkedList to use physical address space. 2022-03-26 01:35:37 -07:00
bunnei
3bc0c2a625 hle: kernel: svc: CreateCodeMemory: Remove log of 'out' host pointer.
- This does not seem terribly useful and is inconsistent with other usage.
2022-03-26 01:34:29 -07:00
bunnei
94543f129e hle: kernel: k_code_memory: Fix usage of KPageLinkedList to use physical address space. 2022-03-26 01:33:16 -07:00
bunnei
5b667f5e52 hle: kernel: k_page_table: Implement MakeAndOpenPageGroup & MakePageGroup. 2022-03-26 01:25:52 -07:00
bunnei
c975a51ae7 hle: kernel: k_page_table: Add IsHeapPhysicalAddress method. 2022-03-26 01:23:43 -07:00
bunnei
f29410d821 hle: kernel: k_page_linked_list: Add Empty method. 2022-03-26 01:02:42 -07:00
bunnei
4e2401c11a hle: kernel: svc: UnmapProcessCodeMemory: Fix inverted alignment check. 2022-03-26 01:01:55 -07:00
bunnei
664d8c8732 Merge pull request #8041 from Morph1984/inline-swkbd
applets/swkbd: Add support for an updated inline software keyboard
2022-03-25 23:19:11 -07:00
bunnei
25faca8ea7 hle: service: nvflinger: buffer_queue: Remove AutoLock and fix free buffer tracking. 2022-03-25 23:01:03 -07:00
bunnei
07c7f96fb2 hle: service: nvflinger: buffer_queue_consumer: Use scoped_lock instead of unique_lock. 2022-03-25 22:59:06 -07:00
bunnei
13aa0a1f24 hle: service: nvflinger: consumer_base: Use scoped_lock instead of unique_lock. 2022-03-25 22:58:52 -07:00
bunnei
741a8c3034 hle: service: nvflinger: Remove unused BufferQueue. 2022-03-25 22:58:23 -07:00
Morph
da46d924e9 Merge pull request #8080 from FernandoS27/yo-momma-so-fat-that
Memory GPU <-> CPU: reduce infighting in the texture cache by adding CPU Cached memory.
2022-03-25 12:09:40 -04:00
Fernando Sahmkow
a2d7b2f905 Memory: Don't protect reads on Normal accuracy. 2022-03-25 04:24:25 +01:00
Fernando Sahmkow
7a9d9e575b Texture Cache: Add Cached CPU system. 2022-03-25 04:24:05 +01:00
bunnei
ab6a5784fa Merge pull request #7720 from FernandoS27/yfc-gc
First Nugget: Reworked Garbage Collection to be smarter [originally from Project YFC]
2022-03-24 20:16:11 -07:00
Fernando S
0608336c60 Merge pull request #8050 from bunnei/nvflinger-rewrite
Rewrite of the NVFlinger implementation
2022-03-25 03:36:55 +01:00
Fernando S
d108c2873d Merge pull request #8068 from ameerj/shader-if-false
shader_recompiler/dead_code_elimination: Add DeadBranchElimination pass
2022-03-25 03:21:27 +01:00
Fernando Sahmkow
9872d4bc4f GC: Address Feedback. 2022-03-25 03:05:56 +01:00
bunnei
284934ebfd Update project license to GPL v3. 2022-03-24 18:51:23 -07:00
bunnei
c64e1ae104 hle: nvflinger: ConsumerBase: Mark ctor as explicit. 2022-03-24 18:13:34 -07:00
bunnei
02dbb2ed54 hle: vi: NativeWindow: Fix trivially copyable issues. 2022-03-24 18:13:34 -07:00
bunnei
c9d28c30f0 hle: nvdrv: nvdata: buffer_queue_producer: Minor cleanup. 2022-03-24 18:13:34 -07:00
bunnei
241ecae867 hle: nvdrv: nvdata: Cleanup NvFence static assert. 2022-03-24 18:13:34 -07:00
bunnei
81eefcaa76 hle: nvflinger: Remove unused unordered_map include. 2022-03-24 18:13:34 -07:00
bunnei
3b13f5e43b hle: nvflinger: buffer_queue_consumer: AcquireBuffer: Fix typo. 2022-03-24 18:13:34 -07:00
bunnei
4d9488033f hle: nvflinger: Merge Rect with Common::Rectangle. 2022-03-24 18:13:33 -07:00
bunnei
e524def8c0 hle: nvflinger: buffer_queue_core: Declare default dtor. 2022-03-24 18:13:33 -07:00
bunnei
8e35eedf22 hle: nvflinger: buffer_queue_producer: DequeueBuffer: Remove unnecessary lock. 2022-03-24 18:13:33 -07:00
bunnei
0ce308047e hle: nvflinger: consumer_base: StillTracking: Should be const. 2022-03-24 18:13:33 -07:00
bunnei
480c79edba hle: nvflinger: graphic_buffer_producer: Remove unnecessary pragma pack. 2022-03-24 18:13:33 -07:00
bunnei
7db60fe7da hle: nvflinger: parcel: Reserve token size. 2022-03-24 18:13:33 -07:00
bunnei
ddd5a2b1c6 hle: nvflinger: buffer_queue_core: StillTracking: Take const reference. 2022-03-24 18:13:33 -07:00
bunnei
53058ae73f hle: nvflinger: buffer_queue_core: Cleanup locking. 2022-03-24 18:13:33 -07:00
bunnei
650c9d0d62 hle: nvflinger: Use std::chrono for present_ns. 2022-03-24 18:13:33 -07:00
bunnei
ca12a77670 hle: nvflinger: Migrate android namespace -> Service::android. 2022-03-24 18:13:33 -07:00
bunnei
5849c9a4cd hle: nvflinger: BufferQueueProducer: Handle SetPreallocatedBuffer with empty buffer.
- Used by Naruto Ultimate Ninja Storm.
2022-03-24 18:13:33 -07:00
bunnei
7f4165fc05 hle: vi: Integrate new NVFlinger and HosBinderDriverServer service. 2022-03-24 18:13:33 -07:00
bunnei
a87812c6a1 hle: nvflinger: Add implementation for HosBinderDriverServer service. 2022-03-24 18:13:33 -07:00
bunnei
56284bff6c hle: nvflinger: Add implementation for BufferQueueProducer class. 2022-03-24 18:13:33 -07:00
bunnei
bfff7b58fd hle: nvflinger: Add implementation for BufferQueueCore class. 2022-03-24 18:13:33 -07:00
bunnei
6e7f687df4 hle: nvflinger: Add implementation for BufferQueueConsumer class. 2022-03-24 18:13:32 -07:00
bunnei
41983bc0ca hle: nvflinger: Add implementation for QueueBufferInput and QueueBufferOutput structs. 2022-03-24 18:13:32 -07:00
bunnei
0057159066 hle: nvflinger: Add implementation for BufferItemConsumer class. 2022-03-24 18:13:32 -07:00
bunnei
79e8cdf595 hle: nvflinger: Add implementation for ConsumerBase class. 2022-03-24 18:13:32 -07:00
bunnei
d25cb12bff hle: nvflinger: Add implementation for BufferSlot class. 2022-03-24 18:13:32 -07:00
bunnei
c723db39c7 hle: nvflinger: Add implementation for BufferItem class. 2022-03-24 18:13:32 -07:00
bunnei
d13e18a8ed hle: nvflinger: Move implementation for Parcel to its own header. 2022-03-24 18:13:32 -07:00
bunnei
a454670de5 hle: nvflinger: Add android buffer queue definitions to its own header. 2022-03-24 18:13:32 -07:00
bunnei
fe9945ad6c hle: nvflinger: Add IBinder interface. 2022-03-24 18:13:32 -07:00
bunnei
b9cbc708e8 hle: nvflinger: Add IConsumerListener interface. 2022-03-24 18:13:32 -07:00
bunnei
81e143a3d2 hle: nvflinger: Add ProducerListener interface. 2022-03-24 18:13:32 -07:00
bunnei
fd24d5a0a2 hle: nvflinger: Add android window enumerations to its own header. 2022-03-24 18:13:32 -07:00
bunnei
05ff9e8d4e hle: nvflinger: Add android Status flags to its own header. 2022-03-24 18:13:32 -07:00
bunnei
05d80fba38 hle: nvflinger: Move BufferTransformFlags to its own header. 2022-03-24 18:13:32 -07:00
bunnei
402273d91b hle: nvdrv: Rename Fence to NvFence to avoid naming conflicts. 2022-03-24 18:13:32 -07:00
bunnei
d456b9d554 hle: nvflinger: Move PixelFormat to its own header. 2022-03-24 18:13:32 -07:00
bunnei
5a8b9a9706 hle: nvflinger: Add implementation for GraphicBuffer class. 2022-03-24 18:13:32 -07:00
bunnei
8c27465325 hle: nvflinger: Add implementation for Fence class. 2022-03-24 18:13:32 -07:00
bunnei
d47575f2c5 hle: nvflinger: Add implementation for Rect class. 2022-03-24 18:13:32 -07:00
bunnei
0704b645df common: logging: Add a logger for NVFlinger. 2022-03-24 18:13:32 -07:00
Fernando Sahmkow
9edbbf2af4 Garbage Collection: Final tuning. 2022-03-25 01:51:52 +01:00
Fernando Sahmkow
5e982a7812 Buffer Cache: Tune to the levels of the new GC. 2022-03-25 01:51:51 +01:00
Fernando Sahmkow
ecb3342145 Garbage Collection: Redesign the algorithm to do a better use of memory. 2022-03-25 01:51:51 +01:00
Fernando S
8f2e5f5666 Merge pull request #8074 from liamwhite/cached-words
buffer_cache: reset cached write bits after flushing invalidations
2022-03-24 17:13:46 +01:00
bunnei
c50f2bd4f6 Merge pull request #8035 from lat9nq/disable-web-applet
yuzu qt: Disable the web applet by default
2022-03-23 21:08:20 -07:00
ameerj
8d45dd3b73 Vulkan: Use 3D helpers for MSAA scaling on NV drivers 510+
Nvidia Vulkan drivers 510+ crash when blitting MSAA images. Fall-back to 3D scale helpers for MSAA image scaling.
2022-03-23 23:30:17 -04:00
Liam
dea5de91d2 buffer_cache: reset cached write bits after flushing invalidations 2022-03-23 20:59:01 -04:00
ameerj
67159e3be7 dead_code_elimination_pass: Remove unreachable Phi arguments 2022-03-23 17:57:22 -04:00
Ameer J
fb4d80b169 Merge pull request #8073 from Shoegzer/compile_fixes
Add include to fix compiling
2022-03-23 16:31:02 -04:00
Shoegzer
a8896e940a Add include to fix compiling 2022-03-23 16:18:48 -04:00
bunnei
749efbb84b Merge pull request #8031 from Morph1984/cleanup-mii-please
applets: Cleanup MiiEdit applet implementation
2022-03-22 21:56:42 -07:00
bunnei
f8773e4ba0 Merge pull request #8070 from yuzu-emu/revert-8016-kill-mem-use
Revert "dynarmic: Reduce size of code caches"
2022-03-22 18:34:19 -07:00
bunnei
e439c31c9e Revert "dynarmic: Reduce size of code caches" 2022-03-22 18:32:54 -07:00
Billy Laws
dd862dff66 Include <bit> header when std::count{r,l}_zero is used
Needed for compilation with older libc++ releases
2022-03-22 21:11:24 +00:00
ameerj
f10d40a0a2 shader_recompiler/dead_code_elimination: Add DeadBranchElimination pass
This adds a pass to eliminate if(false) branches within the shader code
2022-03-22 02:39:31 -04:00
Morph
e6f200b960 applets/swkbd: Split software keyboard initialization
Since the CalcArg struct has been updated with a new size and fields, we have to split the initialization of the keyboard into multiple functions.
This also adds support for parsing the new CalcArg struct used by updated versions of Monster Hunter Rise.
2022-03-21 23:58:50 -04:00
Morph
e7c1e6fc27 applets/swkbd: Add new inline software keyboard types
These were added in newer firmware versions.
2022-03-21 23:58:50 -04:00
Morph
6e232b9efe applets/mii: Remove unused include 2022-03-21 23:57:31 -04:00
Morph
bcca849d0c applets/mii: Remove frontend parameters
These are unused for now as we do not support a frontend implementation.
2022-03-21 23:57:31 -04:00
Morph
c50401903f applets/mii: Cleanup MiiEdit applet implementation
This also enables proper support for MiiEdit applets which are used in games with firmware versions prior to 10.2.0 by handling the 2 different versions of applet inputs and outputs.
2022-03-21 23:57:31 -04:00
Morph
1d5f6b173d applets/mii: Cleanup MiiEdit applet types 2022-03-21 23:57:31 -04:00
Morph
7c44a4ede3 applets/mii: Move MiiEdit applet types into its own file 2022-03-21 23:57:31 -04:00
Morph
aa44ef7b64 service: Move mii enums and structs into its own file
Moves these into types.h, since other files also make use of these types.
2022-03-21 23:57:31 -04:00
Morph
e05e6853fa applets: Rename Mii to MiiEdit 2022-03-21 23:57:31 -04:00
Narr the Reg
f83cd2e8b9 Merge pull request #8067 from ameerj/qt-browser-include
qt_web_browser: Add missing includes
2022-03-21 21:33:09 -06:00
ameerj
b3cfccdb80 qt_web_browser: Add missing includes 2022-03-21 22:42:14 -04:00
Ameer J
75046a3351 Merge pull request #8038 from liamwhite/exit-register-detection
shader_recompiler/EXIT: increment output register on failed enable test
2022-03-21 21:24:07 -04:00
bunnei
82ac66f8a4 Merge pull request #8048 from ameerj/include-purge
general: Reduce unused includes across the project
2022-03-21 18:03:54 -07:00
bunnei
ff2e891022 Merge pull request #7812 from FernandoS27/made-straight-from-the-nut
BufferCache: Find direction of the stream buffer increase.
2022-03-20 15:23:53 -07:00
Fernando Sahmkow
3b0d233cbd BufferCache: Find direction of the stream buffer increase. 2022-03-20 21:37:23 +01:00
Mai M
628534a9ac Merge pull request #8054 from merryhime/dynarmic
dynarmic: Accelerate SHA256 and implement for A32 frontend
2022-03-20 10:37:38 -04:00
Merry
d1c0cdf4f2 dynarmic: Accelerate SHA256 and implement for A32 frontend
* Implements hardware acceleration for SHA256 instructions.
 * Adds SHA256 instructions introduced in ARMv8 to A32 frontend.
 * Implements polyfill for processors that do not support hardware
   accelerated SHA instructions.
2022-03-20 14:06:17 +00:00
Fernando S
7c05c987a3 Merge pull request #8036 from ameerj/starbit-nv
vk_texture_cache: Do not reinterpret DepthStencil source images
2022-03-20 13:35:59 +01:00
bunnei
150f6db4d1 Merge pull request #7840 from lioncash/bitor
texture_cache: Amend unintended bitwise OR in SynchronizeAliases
2022-03-20 02:19:30 -07:00
ameerj
866b7c20a8 general: Fix clang/gcc build errors 2022-03-20 02:25:09 -04:00
ameerj
a367db44cf yuzu_cmd: Reduce unused includes 2022-03-20 02:25:09 -04:00
ameerj
936829e873 yuzu: Reduce unused includes 2022-03-20 02:25:09 -04:00
ameerj
9b505758dc web_service: Reduce unused includes 2022-03-20 02:25:09 -04:00
ameerj
967ed01fcf input_common: Reduce unused includes 2022-03-20 02:25:09 -04:00
ameerj
574a2c4b77 shader_recompiler: Reduce unused includes 2022-03-20 02:25:08 -04:00
bunnei
5960d54722 Merge pull request #8040 from Morph1984/handle-table
KHandleTable: Optimize table entry layout
2022-03-19 23:17:37 -07:00
bunnei
474318ee37 Merge pull request #8047 from ameerj/msvc-test-disable
.ci/build-msvc: Disable YUZU_TESTS cmake variable
2022-03-19 16:28:17 -07:00
ameerj
923decae5a common: Reduce unused includes 2022-03-19 15:01:31 -04:00
ameerj
1bc7d61b57 video_core: Reduce unused includes 2022-03-19 15:01:31 -04:00
bunnei
17ebe211ec Merge pull request #8025 from lat9nq/cmd-specify-config
yuzu_cmd: Allow user to specify config file location
2022-03-19 01:33:50 -07:00
ameerj
c85a3e5a28 build-msvc: Disable tests 2022-03-19 02:35:09 -04:00
ameerj
8a8ea65fae common: Reduce unused includes 2022-03-19 02:23:33 -04:00
ameerj
ade596121b core: Reduce unused includes 2022-03-19 02:23:32 -04:00
bunnei
8c8b5359f2 Merge pull request #8028 from v1993/patch-9
bsd: Allow inexact match for address length in AcceptImpl
2022-03-18 18:06:13 -07:00
Liam
536d7ed7b1 Address review comments 2022-03-18 15:55:46 -04:00
Liam
d400b618a7 shader_recompiler/EXIT: skip render targets with no outputs 2022-03-18 09:26:25 -04:00
Morph
fe1182e916 Merge pull request #8039 from ameerj/core-include
general: Reduce core.h includes
2022-03-18 02:45:30 -04:00
ameerj
d618bba8a6 general: Reduce core.h includes 2022-03-18 02:13:02 -04:00
Morph
8b7d571b66 KHandleTable: Optimize table entry layout
Since the handle type is not being used, we can reduce the amount of space each entry takes up by 4 bytes.
2022-03-18 00:28:25 -04:00
Liam
6fa17f3372 shader_recompiler/EXIT: increment output register on failed enable test 2022-03-17 22:09:31 -04:00
ameerj
4d840aa903 vk_texture_cache: Do not reinterpret DepthStencil source images
Fixes star pointer interactions in Super Mario Galaxy on some drivers, notably Nvidia.

Co-Authored-By: Fernando S. <1731197+fernandos27@users.noreply.github.com>
2022-03-17 20:55:05 -04:00
Fernando S
cb86e7941b Merge pull request #8024 from liamwhite/const-indexing
Add shader support for const buffer indirect addressing
2022-03-18 00:36:31 +01:00
Fernando S
a616f49864 Merge pull request #8030 from liamwhite/s8d24-conversion
Vulkan: convert S8D24 <-> ABGR8
2022-03-18 00:36:06 +01:00
lat9nq
db637b5a4c yuzu qt: Save disable_web_applet setting
The web applet causes multiple issues with the rest of the application.
Disable it by default and add a debug option to re-enable it until a
proper solution can be found.
2022-03-17 19:20:15 -04:00
lat9nq
7760777c06 main: Update Disable Web Applet warning 2022-03-17 18:14:27 -04:00
lat9nq
fa46fb90fb configure_debug: Add option to set disable_web_applet
Allow the user to configure the web applet usage ahead of booting the
application.
2022-03-17 18:03:13 -04:00
lat9nq
1cbe23ed7b yuzu: Move disable_web_applet to UISettings 2022-03-17 16:55:30 -04:00
Liam
3009d0bd7d Address review comments 2022-03-17 14:48:18 -04:00
Liam
e228a40db8 shader_recompiler: Use functions for indirect const buffer accesses 2022-03-17 13:30:21 -04:00
Liam
3ac522ba41 Address review comments 2022-03-17 09:30:41 -04:00
bunnei
f55af65e82 Merge pull request #7964 from german77/miiii
applet: mii: Simple implementation of mii applet
2022-03-16 21:37:53 -07:00
Liam
6407f16d81 Address review comments 2022-03-16 18:00:42 -04:00
Liam
1415542f73 shader_recompiler: Implement LDC.IS address mode 2022-03-16 11:05:04 -04:00
Fernando S
2db5076ec9 Merge pull request #8013 from bunnei/kernel-slab-rework-v2
Kernel Memory Updates (Part 6): Use guest memory for slab heaps & update TLS.
2022-03-16 12:15:33 +01:00
Fernando S
c3c351e2c2 Merge pull request #8023 from ameerj/kirby-pop-in
maxwell_3d: Implement a safer CB data upload
2022-03-16 12:14:08 +01:00
bunnei
613558867c Merge pull request #8026 from lat9nq/ext-mem-ini
default_ini: List use_extended_memory_layout in default config file
2022-03-15 18:12:10 -07:00
Liam
bcc2d7e69b Vulkan: convert S8D24 <-> ABGR8 2022-03-15 20:05:21 -04:00
Valeri
9e633999d6 bsd: Allow inexact match for address length in AcceptImpl
Minecraft passes in zero for length, but this should account for all possible cases
2022-03-15 14:06:34 +03:00
lat9nq
24d51e1c92 yuzu_cmd: Allow user to specify config file location
Adds an option `-c` or `--config` with one required argument that allows
the user to specify to where the config file is located. Useful for
scripts that run specific games with different preferences for settings.
2022-03-15 03:48:40 -04:00
lat9nq
cb32d9aff8 default_ini: List use_extended_memory_layout in default config file 2022-03-15 03:13:55 -04:00
bunnei
59d2a38daa Merge pull request #8006 from BytesGalore/fix_cmake_missing_qt5_dbus
build(cmake): fix missing Qt5::DBus link target for bundled linux package
2022-03-14 18:56:39 -07:00
bunnei
e95bb782f0 core: hle: kernel: init_slab_setup: Move CalculateSlabHeapGapSize to global namespace. 2022-03-14 18:14:54 -07:00
bunnei
5f3e77d93e core: hle: kernel: Allocate dummy threads on host thread storage.
- Fixes a crash where on subsequent boots, long-lived host threads would have their dummy threads freed.
2022-03-14 18:14:54 -07:00
bunnei
82a2463062 core: hle: kernel: Downgrade dangling objects warning to debug.
- It is not impossible to leak kernel objects, so this is not really any issue anymore (albeit, still interesting).
2022-03-14 18:14:54 -07:00
bunnei
f7d1929816 core: hle: kernel: Make object list container global and ensure it is reset on each emulation session. 2022-03-14 18:14:54 -07:00
bunnei
51589c5e21 core: hle: kernel: Remove server session tracking.
- These are now allocated/managed by emulated memory, so we do not need to track and free them on shutdown.
2022-03-14 18:14:54 -07:00
bunnei
0defac2f2a core: hle: kernel: k_process: Remove handle table finalize, reset page table. 2022-03-14 18:14:54 -07:00
bunnei
813b2ef253 core: hle: kernel: k_process: Implement thread local storage accurately. 2022-03-14 18:14:54 -07:00
bunnei
3210bc2767 core: hle: kernel: k_page_table: Add implementations of MapPages, UnmapPages, and FindFreeArea for TLS. 2022-03-14 18:14:54 -07:00
bunnei
15d9b0418f core: hle: kernel: k_slab_heap: Refresh to use guest allocations. 2022-03-14 18:14:54 -07:00
bunnei
a25cd4bb4b core: hle: kernel: Update init_slab_heap, use device memory, and add KThreadLocalPage and KPageBuffer.
- Refreshes our slab initialization code to latest known behavior.
- Moves all guest kernel slabs into emulated device memory.
- Adds KThreadLocalPage and KPageBuffer, which we will use for accurate TLS management.
2022-03-14 18:14:54 -07:00
bunnei
91819726b1 core: hle: kernel: k_page_buffer: Add KThreadLocalPage primitive. 2022-03-14 18:14:53 -07:00
bunnei
08434842b3 core: hle: kernel: k_page_buffer: Add KPageBuffer primitive. 2022-03-14 18:14:53 -07:00
bunnei
4a28d8cebb core: hle: kernel: k_thread: Ensure host Fiber is freed. 2022-03-14 18:14:53 -07:00
bunnei
ed67e1dd10 core: hle: kernel: k_server_session: Ensure SessionRequestManager is freed. 2022-03-14 18:14:53 -07:00
bunnei
bfc4823e36 core: hle: service: kernel_helpers: Use system resource limit. 2022-03-14 18:14:53 -07:00
bunnei
8873c0c3db core: hle: service: sm: Fix KPort reference count. 2022-03-14 18:14:53 -07:00
bunnei
25c0acc388 core: hle: kernel: k_thread: Update to reflect tree changes. 2022-03-14 18:14:53 -07:00
bunnei
07c9d9bdbd core: hle: kernel: Use weak_ptr where possible for SessionRequestHandler and SessionRequestManager. 2022-03-14 18:14:53 -07:00
bunnei
ce33503adf core: hle: kernel: k_memory_layout: Update kernel slab memory sizes. 2022-03-14 18:14:53 -07:00
bunnei
0f0e1c25bc core: hle: kernel: svc_types: Add ThreadLocalRegionSize. 2022-03-14 18:14:53 -07:00
bunnei
944d9186ca core: hle: kernel: k_condition_variable: Update to reflect tree changes. 2022-03-14 18:14:53 -07:00
bunnei
158c5845ab core: hle: kernel: k_address_arbiter: Update to reflect tree changes. 2022-03-14 18:14:53 -07:00
bunnei
0fdf1d2a60 common: tree: Various updates. 2022-03-14 18:14:53 -07:00
bunnei
69c2faeb6a common: intrusive_red_black_tree: Various updates. 2022-03-14 18:14:53 -07:00
Liam
52895fab67 shader: add support for const buffer indirect addressing 2022-03-14 19:43:32 -04:00
ameerj
5119a57614 maxwell_3d: Implement a safer CB data upload
This makes constant buffer uploads safer and more accurate by updating the GPU memory as soon as the CB Data method is invoked. The previous implementation was deferring the updates until a different maxwell 3d method was detected, then writing all CB data at once.
2022-03-14 19:18:36 -04:00
Fernando S
cd07a43724 Merge pull request #8008 from ameerj/rescale-offsets-array
rescaling_pass: Fix rescaling Color2DArray ImageFetch offsets
2022-03-15 00:08:22 +01:00
Fernando S
f9e1f559b1 Merge pull request #8000 from liamwhite/hagi
Initial support for Wii Hagi emulator
2022-03-15 00:08:05 +01:00
bunnei
cc285b9924 Merge pull request #8015 from FernandoS27/fix-global-mem
Shader decompiler: Fix storage tracking in deko3d.
2022-03-14 16:03:23 -07:00
byte[]
be0e6a2bb4 Maxwell3D: Link to override constant definition in nouveau 2022-03-14 11:06:25 -04:00
Fernando S
0331b8d799 Merge pull request #8016 from merryhime/kill-mem-use
dynarmic: Reduce size of code caches
2022-03-14 16:04:46 +01:00
byte[]
364c67e49b Maxwell3D: restore original topology when topology overrides are disabled 2022-03-14 11:00:08 -04:00
Liam
37aa472269 Maxwell3D: Use override constants from nouveau
This fixes some incorrect rendering in Sunshine
2022-03-14 10:11:58 -04:00
Merry
220674d0d6 dynarmic: Reduce size of code caches 2022-03-13 22:17:14 +00:00
Fernando Sahmkow
185fc03c3c Shader decompiler: do constant propgation before texture pass. 2022-03-13 21:49:40 +01:00
Fernando Sahmkow
ec9f0f064e Shader decompiler: Fix storage tracking in deko3d. 2022-03-13 17:41:16 +01:00
bunnei
8decc8d1a5 Merge pull request #8007 from ameerj/vs-2022-errors
emit_spirv, vk_compute_pass: Resolve VS2022 compiler errors
2022-03-13 03:43:06 -07:00
merry
1f6bbb6257 Merge pull request #8009 from ameerj/dynarmic-exclusives-config
config: Write dynarmic exclusive memory configs
2022-03-13 07:42:38 +00:00
ameerj
6b164a80a1 config: Write dynarmic exclusive memory configs
Ensures the configs are written and saved between boots
2022-03-12 03:42:50 -05:00
ameerj
f87f8d4610 rescaling_pass: Fix rescaling Color2DArray ImageFetch offsets
ImageFetch offsets for 2D array coordinates have a different composite size than the coordinates. The rescaling pass was not taking this into account.

Fixes broken shaders when scaling is enabled in Astral Chain, and likely other titles.
2022-03-12 03:31:56 -05:00
ameerj
e8c50e709e emit_spirv, vk_compute_pass: Resolve VS2022 compiler errors 2022-03-12 02:54:33 -05:00
BytesGalore
948f6e1112 build(cmake): fix missing Qt5::DBus link target for bundled linux package 2022-03-12 08:40:33 +01:00
Liam
56c646d82c Maxwell3D: Restrict topology override effect to after the register is set 2022-03-11 19:42:12 -05:00
Liam
70e632f153 Maxwell3D: mark index buffers as dirty after updating counts 2022-03-11 08:51:22 -05:00
Liam
82c3042c0f TextureCacheRuntime: allow converting D24S8 to ABGR8
I can't see how this would be useful, but Galaxy uses it.
2022-03-10 20:25:34 -05:00
Liam
f1521183f8 Maxwell3D: read small-index draw and primitive topology override registers
This allows Galaxy and Sunshine to render for the first time.
2022-03-10 19:21:04 -05:00
german77
03d671fabc applet: mii: Simple implementation of mii applet 2022-02-28 18:53:41 -06:00
Lioncash
e015dc8264 texture_cache: Ensure has_blacklisted is always initialized
Resolves a -Wmaybe_uninitialized warning
2022-02-02 14:37:27 -05:00
Lioncash
7367e55d1d texture_cache: Remove dead code within SynchronizeAliases
Since these were being copied by value, none of the changes applied in
the loop would be reflected.

However, from the looks of it, this would already be applied within
CopyImage() anyways, so this can be removed.
2022-02-02 14:37:22 -05:00
Lioncash
856f576c05 texture_cache: Amend unintended bitwise OR in SynchronizeAliases 2022-02-02 14:20:58 -05:00
412 changed files with 7112 additions and 3697 deletions

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'] }} -DCMAKE_BUILD_TYPE=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} -DYUZU_TESTS=OFF -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

@@ -363,7 +363,11 @@ if(ENABLE_QT)
set(YUZU_QT_NO_CMAKE_SYSTEM_PATH "NO_CMAKE_SYSTEM_PATH")
endif()
find_package(Qt5 ${QT_VERSION} REQUIRED COMPONENTS Widgets ${QT_PREFIX_HINT} ${YUZU_QT_NO_CMAKE_SYSTEM_PATH})
if ((${CMAKE_SYSTEM_NAME} STREQUAL "Linux") AND YUZU_USE_BUNDLED_QT)
find_package(Qt5 ${QT_VERSION} REQUIRED COMPONENTS Widgets DBus ${QT_PREFIX_HINT} ${YUZU_QT_NO_CMAKE_SYSTEM_PATH})
else()
find_package(Qt5 ${QT_VERSION} REQUIRED COMPONENTS Widgets ${QT_PREFIX_HINT} ${YUZU_QT_NO_CMAKE_SYSTEM_PATH})
endif()
if (YUZU_USE_QT_WEB_ENGINE)
find_package(Qt5 COMPONENTS WebEngineCore WebEngineWidgets)
endif()

View File

@@ -77,6 +77,6 @@ If you wish to support us a different way, please join our [Discord](https://dis
## License
yuzu is licensed under the GPLv2 (or any later version). Refer to the [license.txt](https://github.com/yuzu-emu/yuzu/blob/master/license.txt) file.
yuzu is licensed under the GPLv3 (or any later version). Refer to the [license.txt](https://github.com/yuzu-emu/yuzu/blob/master/license.txt) file.
The [Skyline-Emulator Team](https://github.com/skyline-emu/skyline) is exempt from GPLv2 for the contributions from all these contributors [FernandoS27](https://github.com/FernandoS27), [lioncash](https://github.com/lioncash), [bunnei](https://github.com/bunnei), [ReinUsesLisp](https://github.com/ReinUsesLisp), [Morph1984](https://github.com/Morph1984), [ogniK5377](https://github.com/ogniK5377), [german77](https://github.com/german77), [ameerj](https://github.com/ameerj), [Kelebek1](https://github.com/Kelebek1) and [lat9nq](https://github.com/lat9nq). They may only use the code from these contributors under Mozilla Public License, version 2.0.
The [Skyline-Emulator Team](https://github.com/skyline-emu/skyline) is exempt from GPLv3 for the contributions from all these contributors [FernandoS27](https://github.com/FernandoS27), [lioncash](https://github.com/lioncash), [bunnei](https://github.com/bunnei), [ReinUsesLisp](https://github.com/ReinUsesLisp), [Morph1984](https://github.com/Morph1984), [ogniK5377](https://github.com/ogniK5377), [german77](https://github.com/german77), [ameerj](https://github.com/ameerj), [Kelebek1](https://github.com/Kelebek1) and [lat9nq](https://github.com/lat9nq). They may only use the code from these contributors under Mozilla Public License, version 2.0.

View File

@@ -1,285 +1,626 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
TERMS AND CONDITIONS
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
0. Definitions.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
"This License" refers to version 3 of the GNU General Public License.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
A "covered work" means either the unmodified Program or a work based
on the Program.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
1. Source Code.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
The Corresponding Source for a work in source code form is that
same work.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
2. Basic Permissions.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
13. Use with the GNU Affero General Public License.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
14. Revised Versions of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
NO WARRANTY
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
15. Disclaimer of Warranty.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
END OF TERMS AND CONDITIONS
16. Limitation of Liability.
How to Apply These Terms to Your New Programs
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
@@ -287,15 +628,15 @@ free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
@@ -303,63 +644,31 @@ the "copyright" line and a pointer to where the full notice is found.
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<https://www.gnu.org/licenses/>.
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.
The icons used in this project have the following licenses:
Icon Name | License | Origin/Author
--- | --- | ---
checked.png | CC BY-ND 3.0 | https://icons8.com
failed.png | CC BY-ND 3.0 | https://icons8.com
lock.png | CC BY-ND 3.0 | https://icons8.com
plus_folder.png (Default, Dark) | CC BY-ND 3.0 | https://icons8.com
bad_folder.png (Default, Dark) | CC BY-ND 3.0 | https://icons8.com
chip.png (Default, Dark) | CC BY-ND 3.0 | https://icons8.com
folder.png (Default, Dark) | CC BY-ND 3.0 | https://icons8.com
plus.png (Default, Dark) | CC0 1.0 | Designed by BreadFish64 from the Citra team
sd_card.png (Default, Dark) | CC BY-ND 3.0 | https://icons8.com
plus_folder.png (Colorful, Colorful Dark) | CC BY-ND 3.0 | https://icons8.com
bad_folder.png (Colorful, Colorful Dark) | CC BY-ND 3.0 | https://icons8.com
chip.png (Colorful, Colorful Dark) | CC BY-ND 3.0 | https://icons8.com
folder.png (Colorful, Colorful Dark) | CC BY-ND 3.0 | https://icons8.com
plus.png (Colorful, Colorful Dark) | CC BY-ND 3.0 | https://icons8.com
sd_card.png (Colorful, Colorful Dark) | CC BY-ND 3.0 | https://icons8.com
star.png | CC BY-ND 3.0 | https://icons8.com
Note:
Some icons are different in different themes, and they are separately listed
only when they have different licenses/origins.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<https://www.gnu.org/licenses/why-not-lgpl.html>.

View File

@@ -4,13 +4,12 @@
#pragma once
#include <cstring>
#include <memory>
#include "common/common_types.h"
#if _MSC_VER
#include <intrin.h>
#else
#include <cstring>
#endif
namespace Common {

View File

@@ -33,7 +33,6 @@
#include <cstddef>
#include <limits>
#include <type_traits>
#include "common/common_funcs.h"
#include "common/swap.h"
/*

View File

@@ -2,7 +2,6 @@
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include <cstring>
#include <string>
#include <utility>

View File

@@ -4,7 +4,6 @@
#include "common/fs/file.h"
#include "common/fs/fs.h"
#include "common/fs/path_util.h"
#include "common/logging/log.h"
#ifdef _WIN32

View File

@@ -6,10 +6,8 @@
#include <cstdio>
#include <filesystem>
#include <fstream>
#include <span>
#include <type_traits>
#include <vector>
#include "common/concepts.h"
#include "common/fs/fs_types.h"

View File

@@ -7,7 +7,6 @@
#include <functional>
#include "common/common_funcs.h"
#include "common/common_types.h"
namespace Common::FS {

View File

@@ -8,7 +8,6 @@
#include <filesystem>
#include <span>
#include <string>
#include <string_view>
#include "common/common_types.h"

View File

@@ -7,7 +7,6 @@
#include <array>
#include <cstddef>
#include <string>
#include <type_traits>
#include <vector>
#include <fmt/format.h>
#include "common/common_types.h"

View File

@@ -18,6 +18,7 @@
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include "common/scope_exit.h"
#endif // ^^^ Linux ^^^
@@ -27,7 +28,6 @@
#include "common/assert.h"
#include "common/host_memory.h"
#include "common/logging/log.h"
#include "common/scope_exit.h"
namespace Common {

View File

@@ -4,6 +4,7 @@
#pragma once
#include "common/common_funcs.h"
#include "common/parent_of_member.h"
#include "common/tree.h"
@@ -15,32 +16,33 @@ class IntrusiveRedBlackTreeImpl;
}
#pragma pack(push, 4)
struct IntrusiveRedBlackTreeNode {
YUZU_NON_COPYABLE(IntrusiveRedBlackTreeNode);
public:
using EntryType = RBEntry<IntrusiveRedBlackTreeNode>;
constexpr IntrusiveRedBlackTreeNode() = default;
void SetEntry(const EntryType& new_entry) {
entry = new_entry;
}
[[nodiscard]] EntryType& GetEntry() {
return entry;
}
[[nodiscard]] const EntryType& GetEntry() const {
return entry;
}
using RBEntry = freebsd::RBEntry<IntrusiveRedBlackTreeNode>;
private:
EntryType entry{};
RBEntry m_entry;
friend class impl::IntrusiveRedBlackTreeImpl;
public:
explicit IntrusiveRedBlackTreeNode() = default;
template <class, class, class>
friend class IntrusiveRedBlackTree;
[[nodiscard]] constexpr RBEntry& GetRBEntry() {
return m_entry;
}
[[nodiscard]] constexpr const RBEntry& GetRBEntry() const {
return m_entry;
}
constexpr void SetRBEntry(const RBEntry& entry) {
m_entry = entry;
}
};
static_assert(sizeof(IntrusiveRedBlackTreeNode) ==
3 * sizeof(void*) + std::max<size_t>(sizeof(freebsd::RBColor), 4));
#pragma pack(pop)
template <class T, class Traits, class Comparator>
class IntrusiveRedBlackTree;
@@ -48,12 +50,17 @@ class IntrusiveRedBlackTree;
namespace impl {
class IntrusiveRedBlackTreeImpl {
YUZU_NON_COPYABLE(IntrusiveRedBlackTreeImpl);
private:
template <class, class, class>
friend class ::Common::IntrusiveRedBlackTree;
using RootType = RBHead<IntrusiveRedBlackTreeNode>;
RootType root;
private:
using RootType = freebsd::RBHead<IntrusiveRedBlackTreeNode>;
private:
RootType m_root;
public:
template <bool Const>
@@ -81,149 +88,150 @@ public:
IntrusiveRedBlackTreeImpl::reference>;
private:
pointer node;
pointer m_node;
public:
explicit Iterator(pointer n) : node(n) {}
constexpr explicit Iterator(pointer n) : m_node(n) {}
bool operator==(const Iterator& rhs) const {
return this->node == rhs.node;
constexpr bool operator==(const Iterator& rhs) const {
return m_node == rhs.m_node;
}
bool operator!=(const Iterator& rhs) const {
constexpr bool operator!=(const Iterator& rhs) const {
return !(*this == rhs);
}
pointer operator->() const {
return this->node;
constexpr pointer operator->() const {
return m_node;
}
reference operator*() const {
return *this->node;
constexpr reference operator*() const {
return *m_node;
}
Iterator& operator++() {
this->node = GetNext(this->node);
constexpr Iterator& operator++() {
m_node = GetNext(m_node);
return *this;
}
Iterator& operator--() {
this->node = GetPrev(this->node);
constexpr Iterator& operator--() {
m_node = GetPrev(m_node);
return *this;
}
Iterator operator++(int) {
constexpr Iterator operator++(int) {
const Iterator it{*this};
++(*this);
return it;
}
Iterator operator--(int) {
constexpr Iterator operator--(int) {
const Iterator it{*this};
--(*this);
return it;
}
operator Iterator<true>() const {
return Iterator<true>(this->node);
constexpr operator Iterator<true>() const {
return Iterator<true>(m_node);
}
};
private:
// Define accessors using RB_* functions.
bool EmptyImpl() const {
return root.IsEmpty();
constexpr bool EmptyImpl() const {
return m_root.IsEmpty();
}
IntrusiveRedBlackTreeNode* GetMinImpl() const {
return RB_MIN(const_cast<RootType*>(&root));
constexpr IntrusiveRedBlackTreeNode* GetMinImpl() const {
return freebsd::RB_MIN(const_cast<RootType&>(m_root));
}
IntrusiveRedBlackTreeNode* GetMaxImpl() const {
return RB_MAX(const_cast<RootType*>(&root));
constexpr IntrusiveRedBlackTreeNode* GetMaxImpl() const {
return freebsd::RB_MAX(const_cast<RootType&>(m_root));
}
IntrusiveRedBlackTreeNode* RemoveImpl(IntrusiveRedBlackTreeNode* node) {
return RB_REMOVE(&root, node);
constexpr IntrusiveRedBlackTreeNode* RemoveImpl(IntrusiveRedBlackTreeNode* node) {
return freebsd::RB_REMOVE(m_root, node);
}
public:
static IntrusiveRedBlackTreeNode* GetNext(IntrusiveRedBlackTreeNode* node) {
return RB_NEXT(node);
static constexpr IntrusiveRedBlackTreeNode* GetNext(IntrusiveRedBlackTreeNode* node) {
return freebsd::RB_NEXT(node);
}
static IntrusiveRedBlackTreeNode* GetPrev(IntrusiveRedBlackTreeNode* node) {
return RB_PREV(node);
static constexpr IntrusiveRedBlackTreeNode* GetPrev(IntrusiveRedBlackTreeNode* node) {
return freebsd::RB_PREV(node);
}
static const IntrusiveRedBlackTreeNode* GetNext(const IntrusiveRedBlackTreeNode* node) {
static constexpr IntrusiveRedBlackTreeNode const* GetNext(
IntrusiveRedBlackTreeNode const* node) {
return static_cast<const IntrusiveRedBlackTreeNode*>(
GetNext(const_cast<IntrusiveRedBlackTreeNode*>(node)));
}
static const IntrusiveRedBlackTreeNode* GetPrev(const IntrusiveRedBlackTreeNode* node) {
static constexpr IntrusiveRedBlackTreeNode const* GetPrev(
IntrusiveRedBlackTreeNode const* node) {
return static_cast<const IntrusiveRedBlackTreeNode*>(
GetPrev(const_cast<IntrusiveRedBlackTreeNode*>(node)));
}
public:
constexpr IntrusiveRedBlackTreeImpl() {}
constexpr IntrusiveRedBlackTreeImpl() = default;
// Iterator accessors.
iterator begin() {
constexpr iterator begin() {
return iterator(this->GetMinImpl());
}
const_iterator begin() const {
constexpr const_iterator begin() const {
return const_iterator(this->GetMinImpl());
}
iterator end() {
constexpr iterator end() {
return iterator(static_cast<IntrusiveRedBlackTreeNode*>(nullptr));
}
const_iterator end() const {
constexpr const_iterator end() const {
return const_iterator(static_cast<const IntrusiveRedBlackTreeNode*>(nullptr));
}
const_iterator cbegin() const {
constexpr const_iterator cbegin() const {
return this->begin();
}
const_iterator cend() const {
constexpr const_iterator cend() const {
return this->end();
}
iterator iterator_to(reference ref) {
return iterator(&ref);
constexpr iterator iterator_to(reference ref) {
return iterator(std::addressof(ref));
}
const_iterator iterator_to(const_reference ref) const {
return const_iterator(&ref);
constexpr const_iterator iterator_to(const_reference ref) const {
return const_iterator(std::addressof(ref));
}
// Content management.
bool empty() const {
constexpr bool empty() const {
return this->EmptyImpl();
}
reference back() {
constexpr reference back() {
return *this->GetMaxImpl();
}
const_reference back() const {
constexpr const_reference back() const {
return *this->GetMaxImpl();
}
reference front() {
constexpr reference front() {
return *this->GetMinImpl();
}
const_reference front() const {
constexpr const_reference front() const {
return *this->GetMinImpl();
}
iterator erase(iterator it) {
constexpr iterator erase(iterator it) {
auto cur = std::addressof(*it);
auto next = GetNext(cur);
this->RemoveImpl(cur);
@@ -234,16 +242,16 @@ public:
} // namespace impl
template <typename T>
concept HasLightCompareType = requires {
{ std::is_same<typename T::LightCompareType, void>::value } -> std::convertible_to<bool>;
concept HasRedBlackKeyType = requires {
{ std::is_same<typename T::RedBlackKeyType, void>::value } -> std::convertible_to<bool>;
};
namespace impl {
template <typename T, typename Default>
consteval auto* GetLightCompareType() {
if constexpr (HasLightCompareType<T>) {
return static_cast<typename T::LightCompareType*>(nullptr);
consteval auto* GetRedBlackKeyType() {
if constexpr (HasRedBlackKeyType<T>) {
return static_cast<typename T::RedBlackKeyType*>(nullptr);
} else {
return static_cast<Default*>(nullptr);
}
@@ -252,16 +260,17 @@ namespace impl {
} // namespace impl
template <typename T, typename Default>
using LightCompareType = std::remove_pointer_t<decltype(impl::GetLightCompareType<T, Default>())>;
using RedBlackKeyType = std::remove_pointer_t<decltype(impl::GetRedBlackKeyType<T, Default>())>;
template <class T, class Traits, class Comparator>
class IntrusiveRedBlackTree {
YUZU_NON_COPYABLE(IntrusiveRedBlackTree);
public:
using ImplType = impl::IntrusiveRedBlackTreeImpl;
private:
ImplType impl{};
ImplType m_impl;
public:
template <bool Const>
@@ -277,9 +286,9 @@ public:
using iterator = Iterator<false>;
using const_iterator = Iterator<true>;
using light_value_type = LightCompareType<Comparator, value_type>;
using const_light_pointer = const light_value_type*;
using const_light_reference = const light_value_type&;
using key_type = RedBlackKeyType<Comparator, value_type>;
using const_key_pointer = const key_type*;
using const_key_reference = const key_type&;
template <bool Const>
class Iterator {
@@ -298,183 +307,201 @@ public:
IntrusiveRedBlackTree::reference>;
private:
ImplIterator iterator;
ImplIterator m_impl;
private:
explicit Iterator(ImplIterator it) : iterator(it) {}
constexpr explicit Iterator(ImplIterator it) : m_impl(it) {}
explicit Iterator(typename std::conditional<Const, ImplType::const_iterator,
ImplType::iterator>::type::pointer ptr)
: iterator(ptr) {}
constexpr explicit Iterator(typename ImplIterator::pointer p) : m_impl(p) {}
ImplIterator GetImplIterator() const {
return this->iterator;
constexpr ImplIterator GetImplIterator() const {
return m_impl;
}
public:
bool operator==(const Iterator& rhs) const {
return this->iterator == rhs.iterator;
constexpr bool operator==(const Iterator& rhs) const {
return m_impl == rhs.m_impl;
}
bool operator!=(const Iterator& rhs) const {
constexpr bool operator!=(const Iterator& rhs) const {
return !(*this == rhs);
}
pointer operator->() const {
return Traits::GetParent(std::addressof(*this->iterator));
constexpr pointer operator->() const {
return Traits::GetParent(std::addressof(*m_impl));
}
reference operator*() const {
return *Traits::GetParent(std::addressof(*this->iterator));
constexpr reference operator*() const {
return *Traits::GetParent(std::addressof(*m_impl));
}
Iterator& operator++() {
++this->iterator;
constexpr Iterator& operator++() {
++m_impl;
return *this;
}
Iterator& operator--() {
--this->iterator;
constexpr Iterator& operator--() {
--m_impl;
return *this;
}
Iterator operator++(int) {
constexpr Iterator operator++(int) {
const Iterator it{*this};
++this->iterator;
++m_impl;
return it;
}
Iterator operator--(int) {
constexpr Iterator operator--(int) {
const Iterator it{*this};
--this->iterator;
--m_impl;
return it;
}
operator Iterator<true>() const {
return Iterator<true>(this->iterator);
constexpr operator Iterator<true>() const {
return Iterator<true>(m_impl);
}
};
private:
static int CompareImpl(const IntrusiveRedBlackTreeNode* lhs,
const IntrusiveRedBlackTreeNode* rhs) {
static constexpr int CompareImpl(const IntrusiveRedBlackTreeNode* lhs,
const IntrusiveRedBlackTreeNode* rhs) {
return Comparator::Compare(*Traits::GetParent(lhs), *Traits::GetParent(rhs));
}
static int LightCompareImpl(const void* elm, const IntrusiveRedBlackTreeNode* rhs) {
return Comparator::Compare(*static_cast<const_light_pointer>(elm), *Traits::GetParent(rhs));
static constexpr int CompareKeyImpl(const_key_reference key,
const IntrusiveRedBlackTreeNode* rhs) {
return Comparator::Compare(key, *Traits::GetParent(rhs));
}
// Define accessors using RB_* functions.
IntrusiveRedBlackTreeNode* InsertImpl(IntrusiveRedBlackTreeNode* node) {
return RB_INSERT(&impl.root, node, CompareImpl);
constexpr IntrusiveRedBlackTreeNode* InsertImpl(IntrusiveRedBlackTreeNode* node) {
return freebsd::RB_INSERT(m_impl.m_root, node, CompareImpl);
}
IntrusiveRedBlackTreeNode* FindImpl(const IntrusiveRedBlackTreeNode* node) const {
return RB_FIND(const_cast<ImplType::RootType*>(&impl.root),
const_cast<IntrusiveRedBlackTreeNode*>(node), CompareImpl);
constexpr IntrusiveRedBlackTreeNode* FindImpl(IntrusiveRedBlackTreeNode const* node) const {
return freebsd::RB_FIND(const_cast<ImplType::RootType&>(m_impl.m_root),
const_cast<IntrusiveRedBlackTreeNode*>(node), CompareImpl);
}
IntrusiveRedBlackTreeNode* NFindImpl(const IntrusiveRedBlackTreeNode* node) const {
return RB_NFIND(const_cast<ImplType::RootType*>(&impl.root),
const_cast<IntrusiveRedBlackTreeNode*>(node), CompareImpl);
constexpr IntrusiveRedBlackTreeNode* NFindImpl(IntrusiveRedBlackTreeNode const* node) const {
return freebsd::RB_NFIND(const_cast<ImplType::RootType&>(m_impl.m_root),
const_cast<IntrusiveRedBlackTreeNode*>(node), CompareImpl);
}
IntrusiveRedBlackTreeNode* FindLightImpl(const_light_pointer lelm) const {
return RB_FIND_LIGHT(const_cast<ImplType::RootType*>(&impl.root),
static_cast<const void*>(lelm), LightCompareImpl);
constexpr IntrusiveRedBlackTreeNode* FindKeyImpl(const_key_reference key) const {
return freebsd::RB_FIND_KEY(const_cast<ImplType::RootType&>(m_impl.m_root), key,
CompareKeyImpl);
}
IntrusiveRedBlackTreeNode* NFindLightImpl(const_light_pointer lelm) const {
return RB_NFIND_LIGHT(const_cast<ImplType::RootType*>(&impl.root),
static_cast<const void*>(lelm), LightCompareImpl);
constexpr IntrusiveRedBlackTreeNode* NFindKeyImpl(const_key_reference key) const {
return freebsd::RB_NFIND_KEY(const_cast<ImplType::RootType&>(m_impl.m_root), key,
CompareKeyImpl);
}
constexpr IntrusiveRedBlackTreeNode* FindExistingImpl(
IntrusiveRedBlackTreeNode const* node) const {
return freebsd::RB_FIND_EXISTING(const_cast<ImplType::RootType&>(m_impl.m_root),
const_cast<IntrusiveRedBlackTreeNode*>(node), CompareImpl);
}
constexpr IntrusiveRedBlackTreeNode* FindExistingKeyImpl(const_key_reference key) const {
return freebsd::RB_FIND_EXISTING_KEY(const_cast<ImplType::RootType&>(m_impl.m_root), key,
CompareKeyImpl);
}
public:
constexpr IntrusiveRedBlackTree() = default;
// Iterator accessors.
iterator begin() {
return iterator(this->impl.begin());
constexpr iterator begin() {
return iterator(m_impl.begin());
}
const_iterator begin() const {
return const_iterator(this->impl.begin());
constexpr const_iterator begin() const {
return const_iterator(m_impl.begin());
}
iterator end() {
return iterator(this->impl.end());
constexpr iterator end() {
return iterator(m_impl.end());
}
const_iterator end() const {
return const_iterator(this->impl.end());
constexpr const_iterator end() const {
return const_iterator(m_impl.end());
}
const_iterator cbegin() const {
constexpr const_iterator cbegin() const {
return this->begin();
}
const_iterator cend() const {
constexpr const_iterator cend() const {
return this->end();
}
iterator iterator_to(reference ref) {
return iterator(this->impl.iterator_to(*Traits::GetNode(std::addressof(ref))));
constexpr iterator iterator_to(reference ref) {
return iterator(m_impl.iterator_to(*Traits::GetNode(std::addressof(ref))));
}
const_iterator iterator_to(const_reference ref) const {
return const_iterator(this->impl.iterator_to(*Traits::GetNode(std::addressof(ref))));
constexpr const_iterator iterator_to(const_reference ref) const {
return const_iterator(m_impl.iterator_to(*Traits::GetNode(std::addressof(ref))));
}
// Content management.
bool empty() const {
return this->impl.empty();
constexpr bool empty() const {
return m_impl.empty();
}
reference back() {
return *Traits::GetParent(std::addressof(this->impl.back()));
constexpr reference back() {
return *Traits::GetParent(std::addressof(m_impl.back()));
}
const_reference back() const {
return *Traits::GetParent(std::addressof(this->impl.back()));
constexpr const_reference back() const {
return *Traits::GetParent(std::addressof(m_impl.back()));
}
reference front() {
return *Traits::GetParent(std::addressof(this->impl.front()));
constexpr reference front() {
return *Traits::GetParent(std::addressof(m_impl.front()));
}
const_reference front() const {
return *Traits::GetParent(std::addressof(this->impl.front()));
constexpr const_reference front() const {
return *Traits::GetParent(std::addressof(m_impl.front()));
}
iterator erase(iterator it) {
return iterator(this->impl.erase(it.GetImplIterator()));
constexpr iterator erase(iterator it) {
return iterator(m_impl.erase(it.GetImplIterator()));
}
iterator insert(reference ref) {
constexpr iterator insert(reference ref) {
ImplType::pointer node = Traits::GetNode(std::addressof(ref));
this->InsertImpl(node);
return iterator(node);
}
iterator find(const_reference ref) const {
constexpr iterator find(const_reference ref) const {
return iterator(this->FindImpl(Traits::GetNode(std::addressof(ref))));
}
iterator nfind(const_reference ref) const {
constexpr iterator nfind(const_reference ref) const {
return iterator(this->NFindImpl(Traits::GetNode(std::addressof(ref))));
}
iterator find_light(const_light_reference ref) const {
return iterator(this->FindLightImpl(std::addressof(ref)));
constexpr iterator find_key(const_key_reference ref) const {
return iterator(this->FindKeyImpl(ref));
}
iterator nfind_light(const_light_reference ref) const {
return iterator(this->NFindLightImpl(std::addressof(ref)));
constexpr iterator nfind_key(const_key_reference ref) const {
return iterator(this->NFindKeyImpl(ref));
}
constexpr iterator find_existing(const_reference ref) const {
return iterator(this->FindExistingImpl(Traits::GetNode(std::addressof(ref))));
}
constexpr iterator find_existing_key(const_key_reference ref) const {
return iterator(this->FindExistingKeyImpl(ref));
}
};
template <auto T, class Derived = impl::GetParentType<T>>
template <auto T, class Derived = Common::impl::GetParentType<T>>
class IntrusiveRedBlackTreeMemberTraits;
template <class Parent, IntrusiveRedBlackTreeNode Parent::*Member, class Derived>
@@ -498,19 +525,16 @@ private:
return std::addressof(parent->*Member);
}
static constexpr Derived* GetParent(IntrusiveRedBlackTreeNode* node) {
return GetParentPointer<Member, Derived>(node);
static Derived* GetParent(IntrusiveRedBlackTreeNode* node) {
return Common::GetParentPointer<Member, Derived>(node);
}
static constexpr Derived const* GetParent(const IntrusiveRedBlackTreeNode* node) {
return GetParentPointer<Member, Derived>(node);
static Derived const* GetParent(IntrusiveRedBlackTreeNode const* node) {
return Common::GetParentPointer<Member, Derived>(node);
}
private:
static constexpr TypedStorage<Derived> DerivedStorage = {};
};
template <auto T, class Derived = impl::GetParentType<T>>
template <auto T, class Derived = Common::impl::GetParentType<T>>
class IntrusiveRedBlackTreeMemberTraitsDeferredAssert;
template <class Parent, IntrusiveRedBlackTreeNode Parent::*Member, class Derived>
@@ -521,11 +545,6 @@ public:
IntrusiveRedBlackTree<Derived, IntrusiveRedBlackTreeMemberTraitsDeferredAssert, Comparator>;
using TreeTypeImpl = impl::IntrusiveRedBlackTreeImpl;
static constexpr bool IsValid() {
TypedStorage<Derived> DerivedStorage = {};
return GetParent(GetNode(GetPointer(DerivedStorage))) == GetPointer(DerivedStorage);
}
private:
template <class, class, class>
friend class IntrusiveRedBlackTree;
@@ -540,30 +559,36 @@ private:
return std::addressof(parent->*Member);
}
static constexpr Derived* GetParent(IntrusiveRedBlackTreeNode* node) {
return GetParentPointer<Member, Derived>(node);
static Derived* GetParent(IntrusiveRedBlackTreeNode* node) {
return Common::GetParentPointer<Member, Derived>(node);
}
static constexpr Derived const* GetParent(const IntrusiveRedBlackTreeNode* node) {
return GetParentPointer<Member, Derived>(node);
static Derived const* GetParent(IntrusiveRedBlackTreeNode const* node) {
return Common::GetParentPointer<Member, Derived>(node);
}
};
template <class Derived>
class IntrusiveRedBlackTreeBaseNode : public IntrusiveRedBlackTreeNode {
class alignas(void*) IntrusiveRedBlackTreeBaseNode : public IntrusiveRedBlackTreeNode {
public:
using IntrusiveRedBlackTreeNode::IntrusiveRedBlackTreeNode;
constexpr Derived* GetPrev() {
return static_cast<Derived*>(impl::IntrusiveRedBlackTreeImpl::GetPrev(this));
return static_cast<Derived*>(static_cast<IntrusiveRedBlackTreeBaseNode*>(
impl::IntrusiveRedBlackTreeImpl::GetPrev(this)));
}
constexpr const Derived* GetPrev() const {
return static_cast<const Derived*>(impl::IntrusiveRedBlackTreeImpl::GetPrev(this));
return static_cast<const Derived*>(static_cast<const IntrusiveRedBlackTreeBaseNode*>(
impl::IntrusiveRedBlackTreeImpl::GetPrev(this)));
}
constexpr Derived* GetNext() {
return static_cast<Derived*>(impl::IntrusiveRedBlackTreeImpl::GetNext(this));
return static_cast<Derived*>(static_cast<IntrusiveRedBlackTreeBaseNode*>(
impl::IntrusiveRedBlackTreeImpl::GetNext(this)));
}
constexpr const Derived* GetNext() const {
return static_cast<const Derived*>(impl::IntrusiveRedBlackTreeImpl::GetNext(this));
return static_cast<const Derived*>(static_cast<const IntrusiveRedBlackTreeBaseNode*>(
impl::IntrusiveRedBlackTreeImpl::GetNext(this)));
}
};
@@ -581,19 +606,22 @@ private:
friend class impl::IntrusiveRedBlackTreeImpl;
static constexpr IntrusiveRedBlackTreeNode* GetNode(Derived* parent) {
return static_cast<IntrusiveRedBlackTreeNode*>(parent);
return static_cast<IntrusiveRedBlackTreeNode*>(
static_cast<IntrusiveRedBlackTreeBaseNode<Derived>*>(parent));
}
static constexpr IntrusiveRedBlackTreeNode const* GetNode(Derived const* parent) {
return static_cast<const IntrusiveRedBlackTreeNode*>(parent);
return static_cast<const IntrusiveRedBlackTreeNode*>(
static_cast<const IntrusiveRedBlackTreeBaseNode<Derived>*>(parent));
}
static constexpr Derived* GetParent(IntrusiveRedBlackTreeNode* node) {
return static_cast<Derived*>(node);
return static_cast<Derived*>(static_cast<IntrusiveRedBlackTreeBaseNode<Derived>*>(node));
}
static constexpr Derived const* GetParent(const IntrusiveRedBlackTreeNode* node) {
return static_cast<const Derived*>(node);
static constexpr Derived const* GetParent(IntrusiveRedBlackTreeNode const* node) {
return static_cast<const Derived*>(
static_cast<const IntrusiveRedBlackTreeBaseNode<Derived>*>(node));
}
};

View File

@@ -5,10 +5,8 @@
#include <atomic>
#include <chrono>
#include <climits>
#include <exception>
#include <stop_token>
#include <thread>
#include <vector>
#include <fmt/format.h>

View File

@@ -4,7 +4,6 @@
#pragma once
#include <filesystem>
#include "common/logging/filter.h"
namespace Common::Log {

View File

@@ -119,6 +119,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
SUB(Service, NPNS) \
SUB(Service, NS) \
SUB(Service, NVDRV) \
SUB(Service, NVFlinger) \
SUB(Service, OLSC) \
SUB(Service, PCIE) \
SUB(Service, PCTL) \

View File

@@ -7,7 +7,6 @@
#include <array>
#include <chrono>
#include <cstddef>
#include <string_view>
#include "common/logging/log.h"
namespace Common::Log {

View File

@@ -10,12 +10,10 @@
#endif
#include "common/assert.h"
#include "common/common_funcs.h"
#include "common/logging/filter.h"
#include "common/logging/log.h"
#include "common/logging/log_entry.h"
#include "common/logging/text_formatter.h"
#include "common/string_util.h"
namespace Common::Log {

View File

@@ -4,7 +4,6 @@
#pragma once
#include <cstddef>
#include <string>
namespace Common::Log {

View File

@@ -87,6 +87,7 @@ enum class Class : u8 {
Service_NPNS, ///< The NPNS service
Service_NS, ///< The NS services
Service_NVDRV, ///< The NVDRV (Nvidia driver) service
Service_NVFlinger, ///< The NVFlinger service
Service_OLSC, ///< The OLSC service
Service_PCIE, ///< The PCIe service
Service_PCTL, ///< The PCTL (Parental control) service

View File

@@ -4,6 +4,7 @@
#pragma once
#include <algorithm>
#include <cstdlib>
#include <type_traits>
@@ -20,10 +21,32 @@ struct Rectangle {
constexpr Rectangle() = default;
constexpr Rectangle(T width, T height) : right(width), bottom(height) {}
constexpr Rectangle(T left_, T top_, T right_, T bottom_)
: left(left_), top(top_), right(right_), bottom(bottom_) {}
[[nodiscard]] T GetWidth() const {
[[nodiscard]] constexpr T Left() const {
return left;
}
[[nodiscard]] constexpr T Top() const {
return top;
}
[[nodiscard]] constexpr T Right() const {
return right;
}
[[nodiscard]] constexpr T Bottom() const {
return bottom;
}
[[nodiscard]] constexpr bool IsEmpty() const {
return (GetWidth() <= 0) || (GetHeight() <= 0);
}
[[nodiscard]] constexpr T GetWidth() const {
if constexpr (std::is_floating_point_v<T>) {
return std::abs(right - left);
} else {
@@ -31,7 +54,7 @@ struct Rectangle {
}
}
[[nodiscard]] T GetHeight() const {
[[nodiscard]] constexpr T GetHeight() const {
if constexpr (std::is_floating_point_v<T>) {
return std::abs(bottom - top);
} else {
@@ -39,18 +62,35 @@ struct Rectangle {
}
}
[[nodiscard]] Rectangle<T> TranslateX(const T x) const {
[[nodiscard]] constexpr Rectangle<T> TranslateX(const T x) const {
return Rectangle{left + x, top, right + x, bottom};
}
[[nodiscard]] Rectangle<T> TranslateY(const T y) const {
[[nodiscard]] constexpr Rectangle<T> TranslateY(const T y) const {
return Rectangle{left, top + y, right, bottom + y};
}
[[nodiscard]] Rectangle<T> Scale(const float s) const {
[[nodiscard]] constexpr Rectangle<T> Scale(const float s) const {
return Rectangle{left, top, static_cast<T>(static_cast<float>(left + GetWidth()) * s),
static_cast<T>(static_cast<float>(top + GetHeight()) * s)};
}
[[nodiscard]] constexpr bool operator==(const Rectangle<T>& rhs) const {
return (left == rhs.left) && (top == rhs.top) && (right == rhs.right) &&
(bottom == rhs.bottom);
}
[[nodiscard]] constexpr bool operator!=(const Rectangle<T>& rhs) const {
return !operator==(rhs);
}
[[nodiscard]] constexpr bool Intersect(const Rectangle<T>& with, Rectangle<T>* result) const {
result->left = std::max(left, with.left);
result->top = std::max(top, with.top);
result->right = std::min(right, with.right);
result->bottom = std::min(bottom, with.bottom);
return !result->IsEmpty();
}
};
template <typename T>

View File

@@ -70,4 +70,4 @@ const MemoryInfo& GetMemInfo() {
return mem_info;
}
} // namespace Common
} // namespace Common

View File

@@ -6,7 +6,6 @@
#include <fmt/format.h>
#include "common/fs/file.h"
#include "common/fs/fs.h"
#include "common/fs/path_util.h"
#include "common/nvidia_flags.h"

View File

@@ -5,7 +5,6 @@
#pragma once
#include <atomic>
#include <tuple>
#include "common/common_types.h"
#include "common/virtual_buffer.h"

View File

@@ -7,7 +7,6 @@
#include <type_traits>
#include "common/assert.h"
#include "common/common_types.h"
namespace Common {
namespace detail {

View File

@@ -12,7 +12,6 @@
#include <new>
#include <type_traits>
#include <vector>
#include "common/common_types.h"
namespace Common {

View File

@@ -5,11 +5,9 @@
#include <algorithm>
#include <cctype>
#include <codecvt>
#include <cstdlib>
#include <locale>
#include <sstream>
#include "common/logging/log.h"
#include "common/string_util.h"
#ifdef _WIN32

View File

@@ -4,7 +4,6 @@
#include <algorithm>
#include <cstring>
#include "common/assert.h"
#include "common/scm_rev.h"
#include "common/telemetry.h"

View File

@@ -8,7 +8,6 @@
#include <map>
#include <memory>
#include <string>
#include <string_view>
#include "common/common_funcs.h"
#include "common/common_types.h"

View File

@@ -43,246 +43,445 @@
* The maximum height of a red-black tree is 2lg (n+1).
*/
#include "common/assert.h"
namespace Common::freebsd {
namespace Common {
template <typename T>
class RBHead {
public:
[[nodiscard]] T* Root() {
return rbh_root;
}
[[nodiscard]] const T* Root() const {
return rbh_root;
}
void SetRoot(T* root) {
rbh_root = root;
}
[[nodiscard]] bool IsEmpty() const {
return Root() == nullptr;
}
private:
T* rbh_root = nullptr;
};
enum class EntryColor {
Black,
Red,
enum class RBColor {
RB_BLACK = 0,
RB_RED = 1,
};
#pragma pack(push, 4)
template <typename T>
class RBEntry {
public:
[[nodiscard]] T* Left() {
return rbe_left;
constexpr RBEntry() = default;
[[nodiscard]] constexpr T* Left() {
return m_rbe_left;
}
[[nodiscard]] constexpr const T* Left() const {
return m_rbe_left;
}
[[nodiscard]] const T* Left() const {
return rbe_left;
constexpr void SetLeft(T* e) {
m_rbe_left = e;
}
void SetLeft(T* left) {
rbe_left = left;
[[nodiscard]] constexpr T* Right() {
return m_rbe_right;
}
[[nodiscard]] constexpr const T* Right() const {
return m_rbe_right;
}
[[nodiscard]] T* Right() {
return rbe_right;
constexpr void SetRight(T* e) {
m_rbe_right = e;
}
[[nodiscard]] const T* Right() const {
return rbe_right;
[[nodiscard]] constexpr T* Parent() {
return m_rbe_parent;
}
[[nodiscard]] constexpr const T* Parent() const {
return m_rbe_parent;
}
void SetRight(T* right) {
rbe_right = right;
constexpr void SetParent(T* e) {
m_rbe_parent = e;
}
[[nodiscard]] T* Parent() {
return rbe_parent;
[[nodiscard]] constexpr bool IsBlack() const {
return m_rbe_color == RBColor::RB_BLACK;
}
[[nodiscard]] constexpr bool IsRed() const {
return m_rbe_color == RBColor::RB_RED;
}
[[nodiscard]] constexpr RBColor Color() const {
return m_rbe_color;
}
[[nodiscard]] const T* Parent() const {
return rbe_parent;
}
void SetParent(T* parent) {
rbe_parent = parent;
}
[[nodiscard]] bool IsBlack() const {
return rbe_color == EntryColor::Black;
}
[[nodiscard]] bool IsRed() const {
return rbe_color == EntryColor::Red;
}
[[nodiscard]] EntryColor Color() const {
return rbe_color;
}
void SetColor(EntryColor color) {
rbe_color = color;
constexpr void SetColor(RBColor c) {
m_rbe_color = c;
}
private:
T* rbe_left = nullptr;
T* rbe_right = nullptr;
T* rbe_parent = nullptr;
EntryColor rbe_color{};
T* m_rbe_left{};
T* m_rbe_right{};
T* m_rbe_parent{};
RBColor m_rbe_color{RBColor::RB_BLACK};
};
#pragma pack(pop)
template <typename T>
struct CheckRBEntry {
static constexpr bool value = false;
};
template <typename T>
struct CheckRBEntry<RBEntry<T>> {
static constexpr bool value = true;
};
template <typename Node>
[[nodiscard]] RBEntry<Node>& RB_ENTRY(Node* node) {
return node->GetEntry();
template <typename T>
concept IsRBEntry = CheckRBEntry<T>::value;
template <typename T>
concept HasRBEntry = requires(T& t, const T& ct) {
{ t.GetRBEntry() } -> std::same_as<RBEntry<T>&>;
{ ct.GetRBEntry() } -> std::same_as<const RBEntry<T>&>;
};
template <typename T>
requires HasRBEntry<T>
class RBHead {
private:
T* m_rbh_root = nullptr;
public:
[[nodiscard]] constexpr T* Root() {
return m_rbh_root;
}
[[nodiscard]] constexpr const T* Root() const {
return m_rbh_root;
}
constexpr void SetRoot(T* root) {
m_rbh_root = root;
}
[[nodiscard]] constexpr bool IsEmpty() const {
return this->Root() == nullptr;
}
};
template <typename T>
requires HasRBEntry<T>
[[nodiscard]] constexpr RBEntry<T>& RB_ENTRY(T* t) {
return t->GetRBEntry();
}
template <typename T>
requires HasRBEntry<T>
[[nodiscard]] constexpr const RBEntry<T>& RB_ENTRY(const T* t) {
return t->GetRBEntry();
}
template <typename Node>
[[nodiscard]] const RBEntry<Node>& RB_ENTRY(const Node* node) {
return node->GetEntry();
template <typename T>
requires HasRBEntry<T>
[[nodiscard]] constexpr T* RB_LEFT(T* t) {
return RB_ENTRY(t).Left();
}
template <typename T>
requires HasRBEntry<T>
[[nodiscard]] constexpr const T* RB_LEFT(const T* t) {
return RB_ENTRY(t).Left();
}
template <typename Node>
[[nodiscard]] Node* RB_PARENT(Node* node) {
return RB_ENTRY(node).Parent();
template <typename T>
requires HasRBEntry<T>
[[nodiscard]] constexpr T* RB_RIGHT(T* t) {
return RB_ENTRY(t).Right();
}
template <typename T>
requires HasRBEntry<T>
[[nodiscard]] constexpr const T* RB_RIGHT(const T* t) {
return RB_ENTRY(t).Right();
}
template <typename Node>
[[nodiscard]] const Node* RB_PARENT(const Node* node) {
return RB_ENTRY(node).Parent();
template <typename T>
requires HasRBEntry<T>
[[nodiscard]] constexpr T* RB_PARENT(T* t) {
return RB_ENTRY(t).Parent();
}
template <typename T>
requires HasRBEntry<T>
[[nodiscard]] constexpr const T* RB_PARENT(const T* t) {
return RB_ENTRY(t).Parent();
}
template <typename Node>
void RB_SET_PARENT(Node* node, Node* parent) {
return RB_ENTRY(node).SetParent(parent);
template <typename T>
requires HasRBEntry<T>
constexpr void RB_SET_LEFT(T* t, T* e) {
RB_ENTRY(t).SetLeft(e);
}
template <typename T>
requires HasRBEntry<T>
constexpr void RB_SET_RIGHT(T* t, T* e) {
RB_ENTRY(t).SetRight(e);
}
template <typename T>
requires HasRBEntry<T>
constexpr void RB_SET_PARENT(T* t, T* e) {
RB_ENTRY(t).SetParent(e);
}
template <typename Node>
[[nodiscard]] Node* RB_LEFT(Node* node) {
return RB_ENTRY(node).Left();
template <typename T>
requires HasRBEntry<T>
[[nodiscard]] constexpr bool RB_IS_BLACK(const T* t) {
return RB_ENTRY(t).IsBlack();
}
template <typename T>
requires HasRBEntry<T>
[[nodiscard]] constexpr bool RB_IS_RED(const T* t) {
return RB_ENTRY(t).IsRed();
}
template <typename Node>
[[nodiscard]] const Node* RB_LEFT(const Node* node) {
return RB_ENTRY(node).Left();
template <typename T>
requires HasRBEntry<T>
[[nodiscard]] constexpr RBColor RB_COLOR(const T* t) {
return RB_ENTRY(t).Color();
}
template <typename Node>
void RB_SET_LEFT(Node* node, Node* left) {
return RB_ENTRY(node).SetLeft(left);
template <typename T>
requires HasRBEntry<T>
constexpr void RB_SET_COLOR(T* t, RBColor c) {
RB_ENTRY(t).SetColor(c);
}
template <typename Node>
[[nodiscard]] Node* RB_RIGHT(Node* node) {
return RB_ENTRY(node).Right();
template <typename T>
requires HasRBEntry<T>
constexpr void RB_SET(T* elm, T* parent) {
auto& rb_entry = RB_ENTRY(elm);
rb_entry.SetParent(parent);
rb_entry.SetLeft(nullptr);
rb_entry.SetRight(nullptr);
rb_entry.SetColor(RBColor::RB_RED);
}
template <typename Node>
[[nodiscard]] const Node* RB_RIGHT(const Node* node) {
return RB_ENTRY(node).Right();
template <typename T>
requires HasRBEntry<T>
constexpr void RB_SET_BLACKRED(T* black, T* red) {
RB_SET_COLOR(black, RBColor::RB_BLACK);
RB_SET_COLOR(red, RBColor::RB_RED);
}
template <typename Node>
void RB_SET_RIGHT(Node* node, Node* right) {
return RB_ENTRY(node).SetRight(right);
}
template <typename Node>
[[nodiscard]] bool RB_IS_BLACK(const Node* node) {
return RB_ENTRY(node).IsBlack();
}
template <typename Node>
[[nodiscard]] bool RB_IS_RED(const Node* node) {
return RB_ENTRY(node).IsRed();
}
template <typename Node>
[[nodiscard]] EntryColor RB_COLOR(const Node* node) {
return RB_ENTRY(node).Color();
}
template <typename Node>
void RB_SET_COLOR(Node* node, EntryColor color) {
return RB_ENTRY(node).SetColor(color);
}
template <typename Node>
void RB_SET(Node* node, Node* parent) {
auto& entry = RB_ENTRY(node);
entry.SetParent(parent);
entry.SetLeft(nullptr);
entry.SetRight(nullptr);
entry.SetColor(EntryColor::Red);
}
template <typename Node>
void RB_SET_BLACKRED(Node* black, Node* red) {
RB_SET_COLOR(black, EntryColor::Black);
RB_SET_COLOR(red, EntryColor::Red);
}
template <typename Node>
void RB_ROTATE_LEFT(RBHead<Node>* head, Node* elm, Node*& tmp) {
template <typename T>
requires HasRBEntry<T>
constexpr void RB_ROTATE_LEFT(RBHead<T>& head, T* elm, T*& tmp) {
tmp = RB_RIGHT(elm);
RB_SET_RIGHT(elm, RB_LEFT(tmp));
if (RB_RIGHT(elm) != nullptr) {
if (RB_SET_RIGHT(elm, RB_LEFT(tmp)); RB_RIGHT(elm) != nullptr) {
RB_SET_PARENT(RB_LEFT(tmp), elm);
}
RB_SET_PARENT(tmp, RB_PARENT(elm));
if (RB_PARENT(tmp) != nullptr) {
if (RB_SET_PARENT(tmp, RB_PARENT(elm)); RB_PARENT(tmp) != nullptr) {
if (elm == RB_LEFT(RB_PARENT(elm))) {
RB_SET_LEFT(RB_PARENT(elm), tmp);
} else {
RB_SET_RIGHT(RB_PARENT(elm), tmp);
}
} else {
head->SetRoot(tmp);
head.SetRoot(tmp);
}
RB_SET_LEFT(tmp, elm);
RB_SET_PARENT(elm, tmp);
}
template <typename Node>
void RB_ROTATE_RIGHT(RBHead<Node>* head, Node* elm, Node*& tmp) {
template <typename T>
requires HasRBEntry<T>
constexpr void RB_ROTATE_RIGHT(RBHead<T>& head, T* elm, T*& tmp) {
tmp = RB_LEFT(elm);
RB_SET_LEFT(elm, RB_RIGHT(tmp));
if (RB_LEFT(elm) != nullptr) {
if (RB_SET_LEFT(elm, RB_RIGHT(tmp)); RB_LEFT(elm) != nullptr) {
RB_SET_PARENT(RB_RIGHT(tmp), elm);
}
RB_SET_PARENT(tmp, RB_PARENT(elm));
if (RB_PARENT(tmp) != nullptr) {
if (RB_SET_PARENT(tmp, RB_PARENT(elm)); RB_PARENT(tmp) != nullptr) {
if (elm == RB_LEFT(RB_PARENT(elm))) {
RB_SET_LEFT(RB_PARENT(elm), tmp);
} else {
RB_SET_RIGHT(RB_PARENT(elm), tmp);
}
} else {
head->SetRoot(tmp);
head.SetRoot(tmp);
}
RB_SET_RIGHT(tmp, elm);
RB_SET_PARENT(elm, tmp);
}
template <typename Node>
void RB_INSERT_COLOR(RBHead<Node>* head, Node* elm) {
Node* parent = nullptr;
Node* tmp = nullptr;
template <typename T>
requires HasRBEntry<T>
constexpr void RB_REMOVE_COLOR(RBHead<T>& head, T* parent, T* elm) {
T* tmp;
while ((elm == nullptr || RB_IS_BLACK(elm)) && elm != head.Root()) {
if (RB_LEFT(parent) == elm) {
tmp = RB_RIGHT(parent);
if (RB_IS_RED(tmp)) {
RB_SET_BLACKRED(tmp, parent);
RB_ROTATE_LEFT(head, parent, tmp);
tmp = RB_RIGHT(parent);
}
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, RBColor::RB_RED);
elm = parent;
parent = RB_PARENT(elm);
} else {
if (RB_RIGHT(tmp) == nullptr || RB_IS_BLACK(RB_RIGHT(tmp))) {
T* oleft;
if ((oleft = RB_LEFT(tmp)) != nullptr) {
RB_SET_COLOR(oleft, RBColor::RB_BLACK);
}
RB_SET_COLOR(tmp, RBColor::RB_RED);
RB_ROTATE_RIGHT(head, tmp, oleft);
tmp = RB_RIGHT(parent);
}
RB_SET_COLOR(tmp, RB_COLOR(parent));
RB_SET_COLOR(parent, RBColor::RB_BLACK);
if (RB_RIGHT(tmp)) {
RB_SET_COLOR(RB_RIGHT(tmp), RBColor::RB_BLACK);
}
RB_ROTATE_LEFT(head, parent, tmp);
elm = head.Root();
break;
}
} else {
tmp = RB_LEFT(parent);
if (RB_IS_RED(tmp)) {
RB_SET_BLACKRED(tmp, parent);
RB_ROTATE_RIGHT(head, parent, tmp);
tmp = RB_LEFT(parent);
}
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, RBColor::RB_RED);
elm = parent;
parent = RB_PARENT(elm);
} else {
if (RB_LEFT(tmp) == nullptr || RB_IS_BLACK(RB_LEFT(tmp))) {
T* oright;
if ((oright = RB_RIGHT(tmp)) != nullptr) {
RB_SET_COLOR(oright, RBColor::RB_BLACK);
}
RB_SET_COLOR(tmp, RBColor::RB_RED);
RB_ROTATE_LEFT(head, tmp, oright);
tmp = RB_LEFT(parent);
}
RB_SET_COLOR(tmp, RB_COLOR(parent));
RB_SET_COLOR(parent, RBColor::RB_BLACK);
if (RB_LEFT(tmp)) {
RB_SET_COLOR(RB_LEFT(tmp), RBColor::RB_BLACK);
}
RB_ROTATE_RIGHT(head, parent, tmp);
elm = head.Root();
break;
}
}
}
if (elm) {
RB_SET_COLOR(elm, RBColor::RB_BLACK);
}
}
template <typename T>
requires HasRBEntry<T>
constexpr T* RB_REMOVE(RBHead<T>& head, T* elm) {
T* child = nullptr;
T* parent = nullptr;
T* old = elm;
RBColor color = RBColor::RB_BLACK;
if (RB_LEFT(elm) == nullptr) {
child = RB_RIGHT(elm);
} else if (RB_RIGHT(elm) == nullptr) {
child = RB_LEFT(elm);
} else {
T* left;
elm = RB_RIGHT(elm);
while ((left = RB_LEFT(elm)) != nullptr) {
elm = left;
}
child = RB_RIGHT(elm);
parent = RB_PARENT(elm);
color = RB_COLOR(elm);
if (child) {
RB_SET_PARENT(child, parent);
}
if (parent) {
if (RB_LEFT(parent) == elm) {
RB_SET_LEFT(parent, child);
} else {
RB_SET_RIGHT(parent, child);
}
} else {
head.SetRoot(child);
}
if (RB_PARENT(elm) == old) {
parent = elm;
}
elm->SetRBEntry(old->GetRBEntry());
if (RB_PARENT(old)) {
if (RB_LEFT(RB_PARENT(old)) == old) {
RB_SET_LEFT(RB_PARENT(old), elm);
} else {
RB_SET_RIGHT(RB_PARENT(old), elm);
}
} else {
head.SetRoot(elm);
}
RB_SET_PARENT(RB_LEFT(old), elm);
if (RB_RIGHT(old)) {
RB_SET_PARENT(RB_RIGHT(old), elm);
}
if (parent) {
left = parent;
}
if (color == RBColor::RB_BLACK) {
RB_REMOVE_COLOR(head, parent, child);
}
return old;
}
parent = RB_PARENT(elm);
color = RB_COLOR(elm);
if (child) {
RB_SET_PARENT(child, parent);
}
if (parent) {
if (RB_LEFT(parent) == elm) {
RB_SET_LEFT(parent, child);
} else {
RB_SET_RIGHT(parent, child);
}
} else {
head.SetRoot(child);
}
if (color == RBColor::RB_BLACK) {
RB_REMOVE_COLOR(head, parent, child);
}
return old;
}
template <typename T>
requires HasRBEntry<T>
constexpr void RB_INSERT_COLOR(RBHead<T>& head, T* elm) {
T *parent = nullptr, *tmp = nullptr;
while ((parent = RB_PARENT(elm)) != nullptr && RB_IS_RED(parent)) {
Node* gparent = RB_PARENT(parent);
T* gparent = RB_PARENT(parent);
if (parent == RB_LEFT(gparent)) {
tmp = RB_RIGHT(gparent);
if (tmp && RB_IS_RED(tmp)) {
RB_SET_COLOR(tmp, EntryColor::Black);
RB_SET_COLOR(tmp, RBColor::RB_BLACK);
RB_SET_BLACKRED(parent, gparent);
elm = gparent;
continue;
@@ -300,7 +499,7 @@ void RB_INSERT_COLOR(RBHead<Node>* head, Node* elm) {
} else {
tmp = RB_LEFT(gparent);
if (tmp && RB_IS_RED(tmp)) {
RB_SET_COLOR(tmp, EntryColor::Black);
RB_SET_COLOR(tmp, RBColor::RB_BLACK);
RB_SET_BLACKRED(parent, gparent);
elm = gparent;
continue;
@@ -318,194 +517,14 @@ void RB_INSERT_COLOR(RBHead<Node>* head, Node* elm) {
}
}
RB_SET_COLOR(head->Root(), EntryColor::Black);
RB_SET_COLOR(head.Root(), RBColor::RB_BLACK);
}
template <typename Node>
void RB_REMOVE_COLOR(RBHead<Node>* head, Node* parent, Node* elm) {
Node* tmp;
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);
tmp = RB_RIGHT(parent);
}
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);
elm = parent;
parent = RB_PARENT(elm);
} else {
if (RB_RIGHT(tmp) == nullptr || RB_IS_BLACK(RB_RIGHT(tmp))) {
Node* oleft;
if ((oleft = RB_LEFT(tmp)) != nullptr) {
RB_SET_COLOR(oleft, EntryColor::Black);
}
RB_SET_COLOR(tmp, EntryColor::Red);
RB_ROTATE_RIGHT(head, tmp, oleft);
tmp = RB_RIGHT(parent);
}
RB_SET_COLOR(tmp, RB_COLOR(parent));
RB_SET_COLOR(parent, EntryColor::Black);
if (RB_RIGHT(tmp)) {
RB_SET_COLOR(RB_RIGHT(tmp), EntryColor::Black);
}
RB_ROTATE_LEFT(head, parent, tmp);
elm = head->Root();
break;
}
} else {
tmp = RB_LEFT(parent);
if (RB_IS_RED(tmp)) {
RB_SET_BLACKRED(tmp, parent);
RB_ROTATE_RIGHT(head, parent, tmp);
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);
elm = parent;
parent = RB_PARENT(elm);
} else {
if (RB_LEFT(tmp) == nullptr || RB_IS_BLACK(RB_LEFT(tmp))) {
Node* oright;
if ((oright = RB_RIGHT(tmp)) != nullptr) {
RB_SET_COLOR(oright, EntryColor::Black);
}
RB_SET_COLOR(tmp, EntryColor::Red);
RB_ROTATE_LEFT(head, tmp, oright);
tmp = RB_LEFT(parent);
}
RB_SET_COLOR(tmp, RB_COLOR(parent));
RB_SET_COLOR(parent, EntryColor::Black);
if (RB_LEFT(tmp)) {
RB_SET_COLOR(RB_LEFT(tmp), EntryColor::Black);
}
RB_ROTATE_RIGHT(head, parent, tmp);
elm = head->Root();
break;
}
}
}
if (elm) {
RB_SET_COLOR(elm, EntryColor::Black);
}
}
template <typename Node>
Node* RB_REMOVE(RBHead<Node>* head, Node* elm) {
Node* child = nullptr;
Node* parent = nullptr;
Node* old = elm;
EntryColor color{};
const auto finalize = [&] {
if (color == EntryColor::Black) {
RB_REMOVE_COLOR(head, parent, child);
}
return old;
};
if (RB_LEFT(elm) == nullptr) {
child = RB_RIGHT(elm);
} else if (RB_RIGHT(elm) == nullptr) {
child = RB_LEFT(elm);
} else {
Node* left;
elm = RB_RIGHT(elm);
while ((left = RB_LEFT(elm)) != nullptr) {
elm = left;
}
child = RB_RIGHT(elm);
parent = RB_PARENT(elm);
color = RB_COLOR(elm);
if (child) {
RB_SET_PARENT(child, parent);
}
if (parent) {
if (RB_LEFT(parent) == elm) {
RB_SET_LEFT(parent, child);
} else {
RB_SET_RIGHT(parent, child);
}
} else {
head->SetRoot(child);
}
if (RB_PARENT(elm) == old) {
parent = elm;
}
elm->SetEntry(old->GetEntry());
if (RB_PARENT(old)) {
if (RB_LEFT(RB_PARENT(old)) == old) {
RB_SET_LEFT(RB_PARENT(old), elm);
} else {
RB_SET_RIGHT(RB_PARENT(old), elm);
}
} else {
head->SetRoot(elm);
}
RB_SET_PARENT(RB_LEFT(old), elm);
if (RB_RIGHT(old)) {
RB_SET_PARENT(RB_RIGHT(old), elm);
}
if (parent) {
left = parent;
}
return finalize();
}
parent = RB_PARENT(elm);
color = RB_COLOR(elm);
if (child) {
RB_SET_PARENT(child, parent);
}
if (parent) {
if (RB_LEFT(parent) == elm) {
RB_SET_LEFT(parent, child);
} else {
RB_SET_RIGHT(parent, child);
}
} else {
head->SetRoot(child);
}
return finalize();
}
// Inserts a node into the RB tree
template <typename Node, typename CompareFunction>
Node* RB_INSERT(RBHead<Node>* head, Node* elm, CompareFunction cmp) {
Node* parent = nullptr;
Node* tmp = head->Root();
template <typename T, typename Compare>
requires HasRBEntry<T>
constexpr T* RB_INSERT(RBHead<T>& head, T* elm, Compare cmp) {
T* parent = nullptr;
T* tmp = head.Root();
int comp = 0;
while (tmp) {
@@ -529,17 +548,17 @@ Node* RB_INSERT(RBHead<Node>* head, Node* elm, CompareFunction cmp) {
RB_SET_RIGHT(parent, elm);
}
} else {
head->SetRoot(elm);
head.SetRoot(elm);
}
RB_INSERT_COLOR(head, elm);
return nullptr;
}
// Finds the node with the same key as elm
template <typename Node, typename CompareFunction>
Node* RB_FIND(RBHead<Node>* head, Node* elm, CompareFunction cmp) {
Node* tmp = head->Root();
template <typename T, typename Compare>
requires HasRBEntry<T>
constexpr T* RB_FIND(RBHead<T>& head, T* elm, Compare cmp) {
T* tmp = head.Root();
while (tmp) {
const int comp = cmp(elm, tmp);
@@ -555,11 +574,11 @@ Node* RB_FIND(RBHead<Node>* head, Node* elm, CompareFunction cmp) {
return nullptr;
}
// Finds the first node greater than or equal to the search key
template <typename Node, typename CompareFunction>
Node* RB_NFIND(RBHead<Node>* head, Node* elm, CompareFunction cmp) {
Node* tmp = head->Root();
Node* res = nullptr;
template <typename T, typename Compare>
requires HasRBEntry<T>
constexpr T* RB_NFIND(RBHead<T>& head, T* elm, Compare cmp) {
T* tmp = head.Root();
T* res = nullptr;
while (tmp) {
const int comp = cmp(elm, tmp);
@@ -576,13 +595,13 @@ Node* RB_NFIND(RBHead<Node>* head, Node* elm, CompareFunction cmp) {
return res;
}
// Finds the node with the same key as lelm
template <typename Node, typename CompareFunction>
Node* RB_FIND_LIGHT(RBHead<Node>* head, const void* lelm, CompareFunction lcmp) {
Node* tmp = head->Root();
template <typename T, typename U, typename Compare>
requires HasRBEntry<T>
constexpr T* RB_FIND_KEY(RBHead<T>& head, const U& key, Compare cmp) {
T* tmp = head.Root();
while (tmp) {
const int comp = lcmp(lelm, tmp);
const int comp = cmp(key, tmp);
if (comp < 0) {
tmp = RB_LEFT(tmp);
} else if (comp > 0) {
@@ -595,14 +614,14 @@ Node* RB_FIND_LIGHT(RBHead<Node>* head, const void* lelm, CompareFunction lcmp)
return nullptr;
}
// Finds the first node greater than or equal to the search key
template <typename Node, typename CompareFunction>
Node* RB_NFIND_LIGHT(RBHead<Node>* head, const void* lelm, CompareFunction lcmp) {
Node* tmp = head->Root();
Node* res = nullptr;
template <typename T, typename U, typename Compare>
requires HasRBEntry<T>
constexpr T* RB_NFIND_KEY(RBHead<T>& head, const U& key, Compare cmp) {
T* tmp = head.Root();
T* res = nullptr;
while (tmp) {
const int comp = lcmp(lelm, tmp);
const int comp = cmp(key, tmp);
if (comp < 0) {
res = tmp;
tmp = RB_LEFT(tmp);
@@ -616,8 +635,43 @@ Node* RB_NFIND_LIGHT(RBHead<Node>* head, const void* lelm, CompareFunction lcmp)
return res;
}
template <typename Node>
Node* RB_NEXT(Node* elm) {
template <typename T, typename Compare>
requires HasRBEntry<T>
constexpr T* RB_FIND_EXISTING(RBHead<T>& head, T* elm, Compare cmp) {
T* tmp = head.Root();
while (true) {
const int comp = cmp(elm, tmp);
if (comp < 0) {
tmp = RB_LEFT(tmp);
} else if (comp > 0) {
tmp = RB_RIGHT(tmp);
} else {
return tmp;
}
}
}
template <typename T, typename U, typename Compare>
requires HasRBEntry<T>
constexpr T* RB_FIND_EXISTING_KEY(RBHead<T>& head, const U& key, Compare cmp) {
T* tmp = head.Root();
while (true) {
const int comp = cmp(key, tmp);
if (comp < 0) {
tmp = RB_LEFT(tmp);
} else if (comp > 0) {
tmp = RB_RIGHT(tmp);
} else {
return tmp;
}
}
}
template <typename T>
requires HasRBEntry<T>
constexpr T* RB_NEXT(T* elm) {
if (RB_RIGHT(elm)) {
elm = RB_RIGHT(elm);
while (RB_LEFT(elm)) {
@@ -636,8 +690,9 @@ Node* RB_NEXT(Node* elm) {
return elm;
}
template <typename Node>
Node* RB_PREV(Node* elm) {
template <typename T>
requires HasRBEntry<T>
constexpr T* RB_PREV(T* elm) {
if (RB_LEFT(elm)) {
elm = RB_LEFT(elm);
while (RB_RIGHT(elm)) {
@@ -656,30 +711,32 @@ Node* RB_PREV(Node* elm) {
return elm;
}
template <typename Node>
Node* RB_MINMAX(RBHead<Node>* head, bool is_min) {
Node* tmp = head->Root();
Node* parent = nullptr;
template <typename T>
requires HasRBEntry<T>
constexpr T* RB_MIN(RBHead<T>& head) {
T* tmp = head.Root();
T* parent = nullptr;
while (tmp) {
parent = tmp;
if (is_min) {
tmp = RB_LEFT(tmp);
} else {
tmp = RB_RIGHT(tmp);
}
tmp = RB_LEFT(tmp);
}
return parent;
}
template <typename Node>
Node* RB_MIN(RBHead<Node>* head) {
return RB_MINMAX(head, true);
template <typename T>
requires HasRBEntry<T>
constexpr T* RB_MAX(RBHead<T>& head) {
T* tmp = head.Root();
T* parent = nullptr;
while (tmp) {
parent = tmp;
tmp = RB_RIGHT(tmp);
}
return parent;
}
template <typename Node>
Node* RB_MAX(RBHead<Node>* head) {
return RB_MINMAX(head, false);
}
} // namespace Common
} // namespace Common::freebsd

View File

@@ -4,7 +4,6 @@
#pragma once
#include <cstring>
#include <utility>
#ifdef _MSC_VER
@@ -13,6 +12,7 @@
#pragma intrinsic(_umul128)
#pragma intrinsic(_udiv128)
#else
#include <cstring>
#include <x86intrin.h>
#endif

View File

@@ -7,7 +7,6 @@
#include <array>
#include <functional>
#include <string>
#include <string_view>
#include "common/common_types.h"

View File

@@ -4,7 +4,6 @@
#pragma once
#include <type_traits>
#include <utility>
namespace Common {

View File

@@ -2,8 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <cstdint>
#include "common/uint128.h"
#include "common/wall_clock.h"

View File

@@ -4,7 +4,6 @@
#include <array>
#include <cstring>
#include <iterator>
#include <span>
#include <string_view>
#include "common/bit_util.h"
#include "common/common_types.h"

View File

@@ -4,8 +4,6 @@
#include <array>
#include <chrono>
#include <limits>
#include <mutex>
#include <thread>
#include "common/atomic_ops.h"

View File

@@ -4,8 +4,6 @@
#pragma once
#include <optional>
#include "common/wall_clock.h"
namespace Common {

View File

@@ -122,6 +122,8 @@ add_library(core STATIC
frontend/applets/error.h
frontend/applets/general_frontend.cpp
frontend/applets/general_frontend.h
frontend/applets/mii_edit.cpp
frontend/applets/mii_edit.h
frontend/applets/profile_select.cpp
frontend/applets/profile_select.h
frontend/applets/software_keyboard.cpp
@@ -207,6 +209,8 @@ add_library(core STATIC
hle/kernel/k_memory_region.h
hle/kernel/k_memory_region_type.h
hle/kernel/k_page_bitmap.h
hle/kernel/k_page_buffer.cpp
hle/kernel/k_page_buffer.h
hle/kernel/k_page_heap.cpp
hle/kernel/k_page_heap.h
hle/kernel/k_page_linked_list.h
@@ -244,6 +248,8 @@ add_library(core STATIC
hle/kernel/k_system_control.h
hle/kernel/k_thread.cpp
hle/kernel/k_thread.h
hle/kernel/k_thread_local_page.cpp
hle/kernel/k_thread_local_page.h
hle/kernel/k_thread_queue.cpp
hle/kernel/k_thread_queue.h
hle/kernel/k_trace.h
@@ -300,6 +306,9 @@ add_library(core STATIC
hle/service/am/applets/applet_error.h
hle/service/am/applets/applet_general_backend.cpp
hle/service/am/applets/applet_general_backend.h
hle/service/am/applets/applet_mii_edit.cpp
hle/service/am/applets/applet_mii_edit.h
hle/service/am/applets/applet_mii_edit_types.h
hle/service/am/applets/applet_profile_select.cpp
hle/service/am/applets/applet_profile_select.h
hle/service/am/applets/applet_software_keyboard.cpp
@@ -526,10 +535,35 @@ add_library(core STATIC
hle/service/nvdrv/nvmemp.h
hle/service/nvdrv/syncpoint_manager.cpp
hle/service/nvdrv/syncpoint_manager.h
hle/service/nvflinger/buffer_queue.cpp
hle/service/nvflinger/buffer_queue.h
hle/service/nvflinger/binder.h
hle/service/nvflinger/buffer_item.h
hle/service/nvflinger/buffer_item_consumer.cpp
hle/service/nvflinger/buffer_item_consumer.h
hle/service/nvflinger/buffer_queue_consumer.cpp
hle/service/nvflinger/buffer_queue_consumer.h
hle/service/nvflinger/buffer_queue_core.cpp
hle/service/nvflinger/buffer_queue_core.h
hle/service/nvflinger/buffer_queue_defs.h
hle/service/nvflinger/buffer_queue_producer.cpp
hle/service/nvflinger/buffer_queue_producer.h
hle/service/nvflinger/buffer_slot.h
hle/service/nvflinger/buffer_transform_flags.h
hle/service/nvflinger/consumer_base.cpp
hle/service/nvflinger/consumer_base.h
hle/service/nvflinger/consumer_listener.h
hle/service/nvflinger/graphic_buffer_producer.cpp
hle/service/nvflinger/graphic_buffer_producer.h
hle/service/nvflinger/hos_binder_driver_server.cpp
hle/service/nvflinger/hos_binder_driver_server.h
hle/service/nvflinger/nvflinger.cpp
hle/service/nvflinger/nvflinger.h
hle/service/nvflinger/parcel.h
hle/service/nvflinger/pixel_format.h
hle/service/nvflinger/producer_listener.h
hle/service/nvflinger/status.h
hle/service/nvflinger/ui/fence.h
hle/service/nvflinger/ui/graphic_buffer.h
hle/service/nvflinger/window.h
hle/service/olsc/olsc.cpp
hle/service/olsc/olsc.h
hle/service/pcie/pcie.cpp

View File

@@ -93,17 +93,19 @@ public:
static constexpr u64 ICACHE_LINE_SIZE = 64;
const u64 cache_line_start = value & ~(ICACHE_LINE_SIZE - 1);
parent.InvalidateCacheRange(cache_line_start, ICACHE_LINE_SIZE);
parent.system.InvalidateCpuInstructionCacheRange(cache_line_start, ICACHE_LINE_SIZE);
break;
}
case Dynarmic::A64::InstructionCacheOperation::InvalidateAllToPoU:
parent.ClearInstructionCache();
parent.system.InvalidateCpuInstructionCaches();
break;
case Dynarmic::A64::InstructionCacheOperation::InvalidateAllToPoUInnerSharable:
default:
LOG_DEBUG(Core_ARM, "Unprocesseed instruction cache operation: {}", op);
break;
}
parent.jit->HaltExecution();
}
void ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) override {

View File

@@ -4,8 +4,6 @@
#pragma once
#include <unordered_map>
#include <dynarmic/interface/exclusive_monitor.h>
#include "common/common_types.h"

View File

@@ -38,7 +38,6 @@
#include "core/hle/service/apm/apm_controller.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/glue/glue_manager.h"
#include "core/hle/service/hid/hid.h"
#include "core/hle/service/service.h"
#include "core/hle/service/sm/sm.h"
#include "core/hle/service/time/time_manager.h"

View File

@@ -5,7 +5,6 @@
#pragma once
#include <cstddef>
#include <iterator>
#include "common/common_funcs.h"
#include "common/common_types.h"

View File

@@ -10,7 +10,10 @@
#include "common/hex_util.h"
#include "common/logging/log.h"
#include "common/settings.h"
#ifndef _WIN32
#include "common/string_util.h"
#endif
#include "core/core.h"
#include "core/file_sys/common_funcs.h"
#include "core/file_sys/content_archive.h"

View File

@@ -6,7 +6,9 @@
#include <array>
#include <vector>
#include "common/bit_field.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
#include "core/file_sys/vfs_types.h"

View File

@@ -0,0 +1,18 @@
// Copyright 2022 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/logging/log.h"
#include "core/frontend/applets/mii_edit.h"
namespace Core::Frontend {
MiiEditApplet::~MiiEditApplet() = default;
void DefaultMiiEditApplet::ShowMiiEdit(const std::function<void()>& callback) const {
LOG_WARNING(Service_AM, "(STUBBED) called");
callback();
}
} // namespace Core::Frontend

View File

@@ -0,0 +1,23 @@
// Copyright 2022 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <functional>
namespace Core::Frontend {
class MiiEditApplet {
public:
virtual ~MiiEditApplet();
virtual void ShowMiiEdit(const std::function<void()>& callback) const = 0;
};
class DefaultMiiEditApplet final : public MiiEditApplet {
public:
void ShowMiiEdit(const std::function<void()>& callback) const override;
};
} // namespace Core::Frontend

View File

@@ -385,7 +385,7 @@ public:
T PopRaw();
template <class T>
std::shared_ptr<T> PopIpcInterface() {
std::weak_ptr<T> PopIpcInterface() {
ASSERT(context->Session()->IsDomain());
ASSERT(context->GetDomainMessageHeader().input_object_count > 0);
return context->GetDomainHandler<T>(Pop<u32>() - 1);

View File

@@ -17,7 +17,6 @@
#include "core/hle/kernel/k_auto_object.h"
#include "core/hle/kernel/k_handle_table.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_readable_event.h"
#include "core/hle/kernel/k_server_session.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
@@ -45,7 +44,7 @@ bool SessionRequestManager::HasSessionRequestHandler(const HLERequestContext& co
LOG_CRITICAL(IPC, "object_id {} is too big!", object_id);
return false;
}
return DomainHandler(object_id - 1) != nullptr;
return DomainHandler(object_id - 1).lock() != nullptr;
} else {
return session_handler != nullptr;
}
@@ -53,9 +52,6 @@ bool SessionRequestManager::HasSessionRequestHandler(const HLERequestContext& co
void SessionRequestHandler::ClientConnected(KServerSession* session) {
session->ClientConnected(shared_from_this());
// Ensure our server session is tracked globally.
kernel.RegisterServerSession(session);
}
void SessionRequestHandler::ClientDisconnected(KServerSession* session) {

View File

@@ -94,6 +94,7 @@ protected:
std::weak_ptr<ServiceThread> service_thread;
};
using SessionRequestHandlerWeakPtr = std::weak_ptr<SessionRequestHandler>;
using SessionRequestHandlerPtr = std::shared_ptr<SessionRequestHandler>;
/**
@@ -139,7 +140,7 @@ public:
}
}
SessionRequestHandlerPtr DomainHandler(std::size_t index) const {
SessionRequestHandlerWeakPtr DomainHandler(std::size_t index) const {
ASSERT_MSG(index < DomainHandlerCount(), "Unexpected handler index {}", index);
return domain_handlers.at(index);
}
@@ -328,10 +329,10 @@ public:
template <typename T>
std::shared_ptr<T> GetDomainHandler(std::size_t index) const {
return std::static_pointer_cast<T>(manager->DomainHandler(index));
return std::static_pointer_cast<T>(manager.lock()->DomainHandler(index).lock());
}
void SetSessionRequestManager(std::shared_ptr<SessionRequestManager> manager_) {
void SetSessionRequestManager(std::weak_ptr<SessionRequestManager> manager_) {
manager = std::move(manager_);
}
@@ -374,7 +375,7 @@ private:
u32 handles_offset{};
u32 domain_offset{};
std::shared_ptr<SessionRequestManager> manager;
std::weak_ptr<SessionRequestManager> manager;
KernelCore& kernel;
Core::Memory::Memory& memory;

View File

@@ -7,19 +7,23 @@
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/core.h"
#include "core/device_memory.h"
#include "core/hardware_properties.h"
#include "core/hle/kernel/init/init_slab_setup.h"
#include "core/hle/kernel/k_code_memory.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_memory_layout.h"
#include "core/hle/kernel/k_memory_manager.h"
#include "core/hle/kernel/k_page_buffer.h"
#include "core/hle/kernel/k_port.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_resource_limit.h"
#include "core/hle/kernel/k_session.h"
#include "core/hle/kernel/k_shared_memory.h"
#include "core/hle/kernel/k_shared_memory_info.h"
#include "core/hle/kernel/k_system_control.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/k_thread_local_page.h"
#include "core/hle/kernel/k_transfer_memory.h"
namespace Kernel::Init {
@@ -32,9 +36,13 @@ namespace Kernel::Init {
HANDLER(KEvent, (SLAB_COUNT(KEvent)), ##__VA_ARGS__) \
HANDLER(KPort, (SLAB_COUNT(KPort)), ##__VA_ARGS__) \
HANDLER(KSharedMemory, (SLAB_COUNT(KSharedMemory)), ##__VA_ARGS__) \
HANDLER(KSharedMemoryInfo, (SLAB_COUNT(KSharedMemory) * 8), ##__VA_ARGS__) \
HANDLER(KTransferMemory, (SLAB_COUNT(KTransferMemory)), ##__VA_ARGS__) \
HANDLER(KCodeMemory, (SLAB_COUNT(KCodeMemory)), ##__VA_ARGS__) \
HANDLER(KSession, (SLAB_COUNT(KSession)), ##__VA_ARGS__) \
HANDLER(KThreadLocalPage, \
(SLAB_COUNT(KProcess) + (SLAB_COUNT(KProcess) + SLAB_COUNT(KThread)) / 8), \
##__VA_ARGS__) \
HANDLER(KResourceLimit, (SLAB_COUNT(KResourceLimit)), ##__VA_ARGS__)
namespace {
@@ -50,38 +58,46 @@ enum KSlabType : u32 {
// Constexpr counts.
constexpr size_t SlabCountKProcess = 80;
constexpr size_t SlabCountKThread = 800;
constexpr size_t SlabCountKEvent = 700;
constexpr size_t SlabCountKEvent = 900;
constexpr size_t SlabCountKInterruptEvent = 100;
constexpr size_t SlabCountKPort = 256 + 0x20; // Extra 0x20 ports over Nintendo for homebrew.
constexpr size_t SlabCountKPort = 384;
constexpr size_t SlabCountKSharedMemory = 80;
constexpr size_t SlabCountKTransferMemory = 200;
constexpr size_t SlabCountKCodeMemory = 10;
constexpr size_t SlabCountKDeviceAddressSpace = 300;
constexpr size_t SlabCountKSession = 933;
constexpr size_t SlabCountKSession = 1133;
constexpr size_t SlabCountKLightSession = 100;
constexpr size_t SlabCountKObjectName = 7;
constexpr size_t SlabCountKResourceLimit = 5;
constexpr size_t SlabCountKDebug = Core::Hardware::NUM_CPU_CORES;
constexpr size_t SlabCountKAlpha = 1;
constexpr size_t SlabCountKBeta = 6;
constexpr size_t SlabCountKIoPool = 1;
constexpr size_t SlabCountKIoRegion = 6;
constexpr size_t SlabCountExtraKThread = 160;
/// Helper function to translate from the slab virtual address to the reserved location in physical
/// memory.
static PAddr TranslateSlabAddrToPhysical(KMemoryLayout& memory_layout, VAddr slab_addr) {
slab_addr -= memory_layout.GetSlabRegionAddress();
return slab_addr + Core::DramMemoryMap::SlabHeapBase;
}
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{};
// This should use the virtual memory address passed in, but currently, we do not setup the
// kernel virtual memory layout. Instead, we simply map these at a region of physical memory
// that we reserve for the slab heaps.
// TODO(bunnei): Fix this once we support the kernel virtual memory layout.
if (size > 0) {
void* backing_kernel_memory{
system.DeviceMemory().GetPointer(TranslateSlabAddrToPhysical(memory_layout, start))};
const KMemoryRegion* region = memory_layout.FindVirtual(start + size - 1);
ASSERT(region != nullptr);
ASSERT(region->IsDerivedFrom(KMemoryRegionType_KernelSlab));
@@ -91,6 +107,12 @@ VAddr InitializeSlabHeap(Core::System& system, KMemoryLayout& memory_layout, VAd
return start + size;
}
size_t CalculateSlabHeapGapSize() {
constexpr size_t KernelSlabHeapGapSize = 2_MiB - 296_KiB;
static_assert(KernelSlabHeapGapSize <= KernelSlabHeapGapsSizeMax);
return KernelSlabHeapGapSize;
}
} // namespace
KSlabResourceCounts KSlabResourceCounts::CreateDefault() {
@@ -109,8 +131,8 @@ KSlabResourceCounts KSlabResourceCounts::CreateDefault() {
.num_KObjectName = SlabCountKObjectName,
.num_KResourceLimit = SlabCountKResourceLimit,
.num_KDebug = SlabCountKDebug,
.num_KAlpha = SlabCountKAlpha,
.num_KBeta = SlabCountKBeta,
.num_KIoPool = SlabCountKIoPool,
.num_KIoRegion = SlabCountKIoRegion,
};
}
@@ -136,11 +158,34 @@ size_t CalculateTotalSlabHeapSize(const KernelCore& kernel) {
#undef ADD_SLAB_SIZE
// Add the reserved size.
size += KernelSlabHeapGapsSize;
size += CalculateSlabHeapGapSize();
return size;
}
void InitializeKPageBufferSlabHeap(Core::System& system) {
auto& kernel = system.Kernel();
const auto& counts = kernel.SlabResourceCounts();
const size_t num_pages =
counts.num_KProcess + counts.num_KThread + (counts.num_KProcess + counts.num_KThread) / 8;
const size_t slab_size = num_pages * PageSize;
// Reserve memory from the system resource limit.
ASSERT(kernel.GetSystemResourceLimit()->Reserve(LimitableResource::PhysicalMemory, slab_size));
// Allocate memory for the slab.
constexpr auto AllocateOption = KMemoryManager::EncodeOption(
KMemoryManager::Pool::System, KMemoryManager::Direction::FromFront);
const PAddr slab_address =
kernel.MemoryManager().AllocateAndOpenContinuous(num_pages, 1, AllocateOption);
ASSERT(slab_address != 0);
// Initialize the slabheap.
KPageBuffer::InitializeSlabHeap(kernel, system.DeviceMemory().GetPointer(slab_address),
slab_size);
}
void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout) {
auto& kernel = system.Kernel();
@@ -160,13 +205,13 @@ void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout) {
}
// Create an array to represent the gaps between the slabs.
const size_t total_gap_size = KernelSlabHeapGapsSize;
const size_t total_gap_size = CalculateSlabHeapGapSize();
std::array<size_t, slab_types.size()> slab_gaps;
for (size_t i = 0; i < slab_gaps.size(); i++) {
for (auto& slab_gap : slab_gaps) {
// Note: This is an off-by-one error from Nintendo's intention, because GenerateRandomRange
// is inclusive. However, Nintendo also has the off-by-one error, and it's "harmless", so we
// will include it ourselves.
slab_gaps[i] = KSystemControl::GenerateRandomRange(0, total_gap_size);
slab_gap = KSystemControl::GenerateRandomRange(0, total_gap_size);
}
// Sort the array, so that we can treat differences between values as offsets to the starts of
@@ -177,13 +222,21 @@ void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout) {
}
}
for (size_t i = 0; i < slab_types.size(); i++) {
// Track the gaps, so that we can free them to the unused slab tree.
VAddr gap_start = address;
size_t gap_size = 0;
for (size_t i = 0; i < slab_gaps.size(); i++) {
// Add the random gap to the address.
address += (i == 0) ? slab_gaps[0] : slab_gaps[i] - slab_gaps[i - 1];
const auto cur_gap = (i == 0) ? slab_gaps[0] : slab_gaps[i] - slab_gaps[i - 1];
address += cur_gap;
gap_size += cur_gap;
#define INITIALIZE_SLAB_HEAP(NAME, COUNT, ...) \
case KSlabType_##NAME: \
address = InitializeSlabHeap<NAME>(system, memory_layout, address, COUNT); \
if (COUNT > 0) { \
address = InitializeSlabHeap<NAME>(system, memory_layout, address, COUNT); \
} \
break;
// Initialize the slabheap.
@@ -192,7 +245,13 @@ void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout) {
FOREACH_SLAB_TYPE(INITIALIZE_SLAB_HEAP)
// If we somehow get an invalid type, abort.
default:
UNREACHABLE();
UNREACHABLE_MSG("Unknown slab type: {}", slab_types[i]);
}
// If we've hit the end of a gap, free it.
if (gap_start + gap_size != address) {
gap_start = address;
gap_size = 0;
}
}
}

View File

@@ -32,12 +32,13 @@ struct KSlabResourceCounts {
size_t num_KObjectName;
size_t num_KResourceLimit;
size_t num_KDebug;
size_t num_KAlpha;
size_t num_KBeta;
size_t num_KIoPool;
size_t num_KIoRegion;
};
void InitializeSlabResourceCounts(KernelCore& kernel);
size_t CalculateTotalSlabHeapSize(const KernelCore& kernel);
void InitializeKPageBufferSlabHeap(Core::System& system);
void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout);
} // namespace Kernel::Init

View File

@@ -115,7 +115,7 @@ ResultCode KAddressArbiter::Signal(VAddr addr, s32 count) {
{
KScopedSchedulerLock sl(kernel);
auto it = thread_tree.nfind_light({addr, -1});
auto it = thread_tree.nfind_key({addr, -1});
while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
(it->GetAddressArbiterKey() == addr)) {
// End the thread's wait.
@@ -148,7 +148,7 @@ ResultCode KAddressArbiter::SignalAndIncrementIfEqual(VAddr addr, s32 value, s32
return ResultInvalidState;
}
auto it = thread_tree.nfind_light({addr, -1});
auto it = thread_tree.nfind_key({addr, -1});
while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
(it->GetAddressArbiterKey() == addr)) {
// End the thread's wait.
@@ -171,7 +171,7 @@ ResultCode KAddressArbiter::SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32
{
[[maybe_unused]] const KScopedSchedulerLock sl(kernel);
auto it = thread_tree.nfind_light({addr, -1});
auto it = thread_tree.nfind_key({addr, -1});
// Determine the updated value.
s32 new_value{};
if (count <= 0) {

View File

@@ -4,8 +4,6 @@
#pragma once
#include <atomic>
#include "common/bit_util.h"
#include "common/common_types.h"

View File

@@ -5,7 +5,6 @@
#include "common/alignment.h"
#include "common/common_types.h"
#include "core/device_memory.h"
#include "core/hle/kernel/k_auto_object.h"
#include "core/hle/kernel/k_code_memory.h"
#include "core/hle/kernel/k_light_lock.h"
#include "core/hle/kernel/k_memory_block.h"
@@ -29,7 +28,8 @@ ResultCode KCodeMemory::Initialize(Core::DeviceMemory& device_memory, VAddr addr
auto& page_table = m_owner->PageTable();
// Construct the page group.
m_page_group = KPageLinkedList(addr, Common::DivideUp(size, PageSize));
m_page_group =
KPageLinkedList(page_table.GetPhysicalAddr(addr), Common::DivideUp(size, PageSize));
// Lock the memory.
R_TRY(page_table.LockForCodeMemory(addr, size))

View File

@@ -9,7 +9,6 @@
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
#include "core/hle/kernel/k_synchronization_object.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/k_thread_queue.h"
#include "core/hle/kernel/kernel.h"
@@ -244,7 +243,7 @@ void KConditionVariable::Signal(u64 cv_key, s32 count) {
{
KScopedSchedulerLock sl(kernel);
auto it = thread_tree.nfind_light({cv_key, -1});
auto it = thread_tree.nfind_key({cv_key, -1});
while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
(it->GetConditionVariableKey() == cv_key)) {
KThread* target_thread = std::addressof(*it);

View File

@@ -63,7 +63,7 @@ bool KHandleTable::Remove(Handle handle) {
return true;
}
ResultCode KHandleTable::Add(Handle* out_handle, KAutoObject* obj, u16 type) {
ResultCode KHandleTable::Add(Handle* out_handle, KAutoObject* obj) {
KScopedDisableDispatch dd(kernel);
KScopedSpinLock lk(m_lock);
@@ -75,7 +75,7 @@ ResultCode KHandleTable::Add(Handle* out_handle, KAutoObject* obj, u16 type) {
const auto linear_id = this->AllocateLinearId();
const auto index = this->AllocateEntry();
m_entry_infos[index].info = {.linear_id = linear_id, .type = type};
m_entry_infos[index].linear_id = linear_id;
m_objects[index] = obj;
obj->Open();
@@ -116,7 +116,7 @@ void KHandleTable::Unreserve(Handle handle) {
}
}
void KHandleTable::Register(Handle handle, KAutoObject* obj, u16 type) {
void KHandleTable::Register(Handle handle, KAutoObject* obj) {
KScopedDisableDispatch dd(kernel);
KScopedSpinLock lk(m_lock);
@@ -132,7 +132,7 @@ void KHandleTable::Register(Handle handle, KAutoObject* obj, u16 type) {
// Set the entry.
ASSERT(m_objects[index] == nullptr);
m_entry_infos[index].info = {.linear_id = static_cast<u16>(linear_id), .type = type};
m_entry_infos[index].linear_id = static_cast<u16>(linear_id);
m_objects[index] = obj;
obj->Open();

View File

@@ -42,7 +42,7 @@ public:
m_free_head_index = -1;
// Free all entries.
for (s32 i = 0; i < static_cast<s32>(m_table_size); ++i) {
for (s16 i = 0; i < static_cast<s16>(m_table_size); ++i) {
m_objects[i] = nullptr;
m_entry_infos[i].next_free_index = i - 1;
m_free_head_index = i;
@@ -104,17 +104,8 @@ public:
ResultCode Reserve(Handle* out_handle);
void Unreserve(Handle handle);
template <typename T>
ResultCode Add(Handle* out_handle, T* obj) {
static_assert(std::is_base_of_v<KAutoObject, T>);
return this->Add(out_handle, obj, obj->GetTypeObj().GetClassToken());
}
template <typename T>
void Register(Handle handle, T* obj) {
static_assert(std::is_base_of_v<KAutoObject, T>);
return this->Register(handle, obj, obj->GetTypeObj().GetClassToken());
}
ResultCode Add(Handle* out_handle, KAutoObject* obj);
void Register(Handle handle, KAutoObject* obj);
template <typename T>
bool GetMultipleObjects(T** out, const Handle* handles, size_t num_handles) const {
@@ -160,9 +151,6 @@ public:
}
private:
ResultCode Add(Handle* out_handle, KAutoObject* obj, u16 type);
void Register(Handle handle, KAutoObject* obj, u16 type);
s32 AllocateEntry() {
ASSERT(m_count < m_table_size);
@@ -179,7 +167,7 @@ private:
ASSERT(m_count > 0);
m_objects[index] = nullptr;
m_entry_infos[index].next_free_index = m_free_head_index;
m_entry_infos[index].next_free_index = static_cast<s16>(m_free_head_index);
m_free_head_index = index;
@@ -278,19 +266,13 @@ private:
}
union EntryInfo {
struct {
u16 linear_id;
u16 type;
} info;
s32 next_free_index;
u16 linear_id;
s16 next_free_index;
constexpr u16 GetLinearId() const {
return info.linear_id;
return linear_id;
}
constexpr u16 GetType() const {
return info.type;
}
constexpr s32 GetNextFreeIndex() const {
constexpr s16 GetNextFreeIndex() const {
return next_free_index;
}
};

View File

@@ -57,11 +57,11 @@ constexpr std::size_t KernelPageTableHeapSize = GetMaximumOverheadSize(MainMemor
constexpr std::size_t KernelInitialPageHeapSize = 128_KiB;
constexpr std::size_t KernelSlabHeapDataSize = 5_MiB;
constexpr std::size_t KernelSlabHeapGapsSize = 2_MiB - 64_KiB;
constexpr std::size_t KernelSlabHeapSize = KernelSlabHeapDataSize + KernelSlabHeapGapsSize;
constexpr std::size_t KernelSlabHeapGapsSizeMax = 2_MiB - 64_KiB;
constexpr std::size_t KernelSlabHeapSize = KernelSlabHeapDataSize + KernelSlabHeapGapsSizeMax;
// NOTE: This is calculated from KThread slab counts, assuming KThread size <= 0x860.
constexpr std::size_t KernelSlabHeapAdditionalSize = 416_KiB;
constexpr std::size_t KernelSlabHeapAdditionalSize = 0x68000;
constexpr std::size_t KernelResourceSize =
KernelPageTableHeapSize + KernelInitialPageHeapSize + KernelSlabHeapSize;

View File

@@ -15,7 +15,6 @@
#include "core/hle/kernel/k_page_linked_list.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/svc_results.h"
#include "core/memory.h"
namespace Kernel {

View File

@@ -0,0 +1,19 @@
// Copyright 2022 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/alignment.h"
#include "common/assert.h"
#include "core/core.h"
#include "core/device_memory.h"
#include "core/hle/kernel/k_page_buffer.h"
#include "core/hle/kernel/memory_types.h"
namespace Kernel {
KPageBuffer* KPageBuffer::FromPhysicalAddress(Core::System& system, PAddr phys_addr) {
ASSERT(Common::IsAligned(phys_addr, PageSize));
return reinterpret_cast<KPageBuffer*>(system.DeviceMemory().GetPointer(phys_addr));
}
} // namespace Kernel

View File

@@ -0,0 +1,28 @@
// Copyright 2022 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include "common/common_types.h"
#include "core/hle/kernel/memory_types.h"
#include "core/hle/kernel/slab_helpers.h"
namespace Kernel {
class KPageBuffer final : public KSlabAllocated<KPageBuffer> {
public:
KPageBuffer() = default;
static KPageBuffer* FromPhysicalAddress(Core::System& system, PAddr phys_addr);
private:
[[maybe_unused]] alignas(PageSize) std::array<u8, PageSize> m_buffer{};
};
static_assert(sizeof(KPageBuffer) == PageSize);
static_assert(alignof(KPageBuffer) == PageSize);
} // namespace Kernel

View File

@@ -89,6 +89,10 @@ public:
return ResultSuccess;
}
bool Empty() const {
return nodes.empty();
}
private:
std::list<Node> nodes;
};

View File

@@ -424,6 +424,120 @@ ResultCode KPageTable::UnmapCodeMemory(VAddr dst_address, VAddr src_address, std
return ResultSuccess;
}
VAddr KPageTable::FindFreeArea(VAddr region_start, std::size_t region_num_pages,
std::size_t num_pages, std::size_t alignment, std::size_t offset,
std::size_t guard_pages) {
VAddr address = 0;
if (num_pages <= region_num_pages) {
if (this->IsAslrEnabled()) {
// Try to directly find a free area up to 8 times.
for (std::size_t i = 0; i < 8; i++) {
const std::size_t random_offset =
KSystemControl::GenerateRandomRange(
0, (region_num_pages - num_pages - guard_pages) * PageSize / alignment) *
alignment;
const VAddr candidate =
Common::AlignDown((region_start + random_offset), alignment) + offset;
KMemoryInfo info = this->QueryInfoImpl(candidate);
if (info.state != KMemoryState::Free) {
continue;
}
if (region_start > candidate) {
continue;
}
if (info.GetAddress() + guard_pages * PageSize > candidate) {
continue;
}
const VAddr candidate_end = candidate + (num_pages + guard_pages) * PageSize - 1;
if (candidate_end > info.GetLastAddress()) {
continue;
}
if (candidate_end > region_start + region_num_pages * PageSize - 1) {
continue;
}
address = candidate;
break;
}
// Fall back to finding the first free area with a random offset.
if (address == 0) {
// NOTE: Nintendo does not account for guard pages here.
// This may theoretically cause an offset to be chosen that cannot be mapped. We
// will account for guard pages.
const std::size_t offset_pages = KSystemControl::GenerateRandomRange(
0, region_num_pages - num_pages - guard_pages);
address = block_manager->FindFreeArea(region_start + offset_pages * PageSize,
region_num_pages - offset_pages, num_pages,
alignment, offset, guard_pages);
}
}
// Find the first free area.
if (address == 0) {
address = block_manager->FindFreeArea(region_start, region_num_pages, num_pages,
alignment, offset, guard_pages);
}
}
return address;
}
ResultCode KPageTable::MakePageGroup(KPageLinkedList& pg, VAddr addr, size_t num_pages) {
ASSERT(this->IsLockedByCurrentThread());
const size_t size = num_pages * PageSize;
// We're making a new group, not adding to an existing one.
R_UNLESS(pg.Empty(), ResultInvalidCurrentMemory);
// Begin traversal.
Common::PageTable::TraversalContext context;
Common::PageTable::TraversalEntry next_entry;
R_UNLESS(page_table_impl.BeginTraversal(next_entry, context, addr), ResultInvalidCurrentMemory);
// Prepare tracking variables.
PAddr cur_addr = next_entry.phys_addr;
size_t cur_size = next_entry.block_size - (cur_addr & (next_entry.block_size - 1));
size_t tot_size = cur_size;
// Iterate, adding to group as we go.
const auto& memory_layout = system.Kernel().MemoryLayout();
while (tot_size < size) {
R_UNLESS(page_table_impl.ContinueTraversal(next_entry, context),
ResultInvalidCurrentMemory);
if (next_entry.phys_addr != (cur_addr + cur_size)) {
const size_t cur_pages = cur_size / PageSize;
R_UNLESS(IsHeapPhysicalAddress(memory_layout, cur_addr), ResultInvalidCurrentMemory);
R_TRY(pg.AddBlock(cur_addr, cur_pages));
cur_addr = next_entry.phys_addr;
cur_size = next_entry.block_size;
} else {
cur_size += next_entry.block_size;
}
tot_size += next_entry.block_size;
}
// Ensure we add the right amount for the last block.
if (tot_size > size) {
cur_size -= (tot_size - size);
}
// Add the last block.
const size_t cur_pages = cur_size / PageSize;
R_UNLESS(IsHeapPhysicalAddress(memory_layout, cur_addr), ResultInvalidCurrentMemory);
R_TRY(pg.AddBlock(cur_addr, cur_pages));
return ResultSuccess;
}
ResultCode KPageTable::UnmapProcessMemory(VAddr dst_addr, std::size_t size,
KPageTable& src_page_table, VAddr src_addr) {
KScopedLightLock lk(general_lock);
@@ -1055,6 +1169,46 @@ ResultCode KPageTable::MapPages(VAddr address, KPageLinkedList& page_linked_list
return ResultSuccess;
}
ResultCode KPageTable::MapPages(VAddr* out_addr, std::size_t num_pages, std::size_t alignment,
PAddr phys_addr, bool is_pa_valid, VAddr region_start,
std::size_t region_num_pages, KMemoryState state,
KMemoryPermission perm) {
ASSERT(Common::IsAligned(alignment, PageSize) && alignment >= PageSize);
// Ensure this is a valid map request.
R_UNLESS(this->CanContain(region_start, region_num_pages * PageSize, state),
ResultInvalidCurrentMemory);
R_UNLESS(num_pages < region_num_pages, ResultOutOfMemory);
// Lock the table.
KScopedLightLock lk(general_lock);
// Find a random address to map at.
VAddr addr = this->FindFreeArea(region_start, region_num_pages, num_pages, alignment, 0,
this->GetNumGuardPages());
R_UNLESS(addr != 0, ResultOutOfMemory);
ASSERT(Common::IsAligned(addr, alignment));
ASSERT(this->CanContain(addr, num_pages * PageSize, state));
ASSERT(this->CheckMemoryState(addr, num_pages * PageSize, KMemoryState::All, KMemoryState::Free,
KMemoryPermission::None, KMemoryPermission::None,
KMemoryAttribute::None, KMemoryAttribute::None)
.IsSuccess());
// Perform mapping operation.
if (is_pa_valid) {
R_TRY(this->Operate(addr, num_pages, perm, OperationType::Map, phys_addr));
} else {
UNIMPLEMENTED();
}
// Update the blocks.
block_manager->Update(addr, num_pages, state, perm);
// We successfully mapped the pages.
*out_addr = addr;
return ResultSuccess;
}
ResultCode KPageTable::UnmapPages(VAddr addr, const KPageLinkedList& page_linked_list) {
ASSERT(this->IsLockedByCurrentThread());
@@ -1097,6 +1251,55 @@ ResultCode KPageTable::UnmapPages(VAddr addr, KPageLinkedList& page_linked_list,
return ResultSuccess;
}
ResultCode KPageTable::UnmapPages(VAddr address, std::size_t num_pages, KMemoryState state) {
// Check that the unmap is in range.
const std::size_t size = num_pages * PageSize;
R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory);
// Lock the table.
KScopedLightLock lk(general_lock);
// Check the memory state.
std::size_t num_allocator_blocks{};
R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size,
KMemoryState::All, state, KMemoryPermission::None,
KMemoryPermission::None, KMemoryAttribute::All,
KMemoryAttribute::None));
// Perform the unmap.
R_TRY(Operate(address, num_pages, KMemoryPermission::None, OperationType::Unmap));
// Update the blocks.
block_manager->Update(address, num_pages, KMemoryState::Free, KMemoryPermission::None);
return ResultSuccess;
}
ResultCode KPageTable::MakeAndOpenPageGroup(KPageLinkedList* out, VAddr address, size_t num_pages,
KMemoryState state_mask, KMemoryState state,
KMemoryPermission perm_mask, KMemoryPermission perm,
KMemoryAttribute attr_mask, KMemoryAttribute attr) {
// Ensure that the page group isn't null.
ASSERT(out != nullptr);
// Make sure that the region we're mapping is valid for the table.
const size_t size = num_pages * PageSize;
R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory);
// Lock the table.
KScopedLightLock lk(general_lock);
// Check if state allows us to create the group.
R_TRY(this->CheckMemoryState(address, size, state_mask | KMemoryState::FlagReferenceCounted,
state | KMemoryState::FlagReferenceCounted, perm_mask, perm,
attr_mask, attr));
// Create a new page group for the region.
R_TRY(this->MakePageGroup(*out, address, num_pages));
return ResultSuccess;
}
ResultCode KPageTable::SetProcessMemoryPermission(VAddr addr, std::size_t size,
Svc::MemoryPermission svc_perm) {
const size_t num_pages = size / PageSize;
@@ -1479,57 +1682,21 @@ ResultCode KPageTable::UnlockForDeviceAddressSpace(VAddr addr, std::size_t size)
}
ResultCode KPageTable::LockForCodeMemory(VAddr addr, std::size_t size) {
KScopedLightLock lk(general_lock);
KMemoryPermission new_perm = KMemoryPermission::NotMapped | KMemoryPermission::KernelReadWrite;
KMemoryPermission old_perm{};
if (const ResultCode result{CheckMemoryState(
nullptr, &old_perm, nullptr, nullptr, addr, size, KMemoryState::FlagCanCodeMemory,
KMemoryState::FlagCanCodeMemory, KMemoryPermission::All,
KMemoryPermission::UserReadWrite, KMemoryAttribute::All, KMemoryAttribute::None)};
result.IsError()) {
return result;
}
new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm;
block_manager->UpdateLock(
addr, size / PageSize,
[](KMemoryBlockManager::iterator block, KMemoryPermission permission) {
block->ShareToDevice(permission);
},
new_perm);
return ResultSuccess;
return this->LockMemoryAndOpen(
nullptr, nullptr, addr, size, KMemoryState::FlagCanCodeMemory,
KMemoryState::FlagCanCodeMemory, KMemoryPermission::All, KMemoryPermission::UserReadWrite,
KMemoryAttribute::All, KMemoryAttribute::None,
static_cast<KMemoryPermission>(KMemoryPermission::NotMapped |
KMemoryPermission::KernelReadWrite),
KMemoryAttribute::Locked);
}
ResultCode KPageTable::UnlockForCodeMemory(VAddr addr, std::size_t size) {
KScopedLightLock lk(general_lock);
KMemoryPermission new_perm = KMemoryPermission::UserReadWrite;
KMemoryPermission old_perm{};
if (const ResultCode result{CheckMemoryState(
nullptr, &old_perm, nullptr, nullptr, addr, size, KMemoryState::FlagCanCodeMemory,
KMemoryState::FlagCanCodeMemory, KMemoryPermission::None, KMemoryPermission::None,
KMemoryAttribute::All, KMemoryAttribute::Locked)};
result.IsError()) {
return result;
}
new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm;
block_manager->UpdateLock(
addr, size / PageSize,
[](KMemoryBlockManager::iterator block, KMemoryPermission permission) {
block->UnshareToDevice(permission);
},
new_perm);
return ResultSuccess;
return this->UnlockMemory(addr, size, KMemoryState::FlagCanCodeMemory,
KMemoryState::FlagCanCodeMemory, KMemoryPermission::None,
KMemoryPermission::None, KMemoryAttribute::All,
KMemoryAttribute::Locked, KMemoryPermission::UserReadWrite,
KMemoryAttribute::Locked, nullptr);
}
ResultCode KPageTable::InitializeMemoryLayout(VAddr start, VAddr end) {
@@ -1865,4 +2032,109 @@ ResultCode KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermissi
return ResultSuccess;
}
ResultCode KPageTable::LockMemoryAndOpen(KPageLinkedList* out_pg, PAddr* out_paddr, VAddr addr,
size_t size, KMemoryState state_mask, KMemoryState state,
KMemoryPermission perm_mask, KMemoryPermission perm,
KMemoryAttribute attr_mask, KMemoryAttribute attr,
KMemoryPermission new_perm, KMemoryAttribute lock_attr) {
// Validate basic preconditions.
ASSERT((lock_attr & attr) == KMemoryAttribute::None);
ASSERT((lock_attr & (KMemoryAttribute::IpcLocked | KMemoryAttribute::DeviceShared)) ==
KMemoryAttribute::None);
// Validate the lock request.
const size_t num_pages = size / PageSize;
R_UNLESS(this->Contains(addr, size), ResultInvalidCurrentMemory);
// Lock the table.
KScopedLightLock lk(general_lock);
// Check that the output page group is empty, if it exists.
if (out_pg) {
ASSERT(out_pg->GetNumPages() == 0);
}
// Check the state.
KMemoryState old_state{};
KMemoryPermission old_perm{};
KMemoryAttribute old_attr{};
size_t num_allocator_blocks{};
R_TRY(this->CheckMemoryState(std::addressof(old_state), std::addressof(old_perm),
std::addressof(old_attr), std::addressof(num_allocator_blocks),
addr, size, state_mask | KMemoryState::FlagReferenceCounted,
state | KMemoryState::FlagReferenceCounted, perm_mask, perm,
attr_mask, attr));
// Get the physical address, if we're supposed to.
if (out_paddr != nullptr) {
ASSERT(this->GetPhysicalAddressLocked(out_paddr, addr));
}
// Make the page group, if we're supposed to.
if (out_pg != nullptr) {
R_TRY(this->MakePageGroup(*out_pg, addr, num_pages));
}
// Decide on new perm and attr.
new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm;
KMemoryAttribute new_attr = static_cast<KMemoryAttribute>(old_attr | lock_attr);
// Update permission, if we need to.
if (new_perm != old_perm) {
R_TRY(Operate(addr, num_pages, new_perm, OperationType::ChangePermissions));
}
// Apply the memory block updates.
block_manager->Update(addr, num_pages, old_state, new_perm, new_attr);
return ResultSuccess;
}
ResultCode KPageTable::UnlockMemory(VAddr addr, size_t size, KMemoryState state_mask,
KMemoryState state, KMemoryPermission perm_mask,
KMemoryPermission perm, KMemoryAttribute attr_mask,
KMemoryAttribute attr, KMemoryPermission new_perm,
KMemoryAttribute lock_attr, const KPageLinkedList* pg) {
// Validate basic preconditions.
ASSERT((attr_mask & lock_attr) == lock_attr);
ASSERT((attr & lock_attr) == lock_attr);
// Validate the unlock request.
const size_t num_pages = size / PageSize;
R_UNLESS(this->Contains(addr, size), ResultInvalidCurrentMemory);
// Lock the table.
KScopedLightLock lk(general_lock);
// Check the state.
KMemoryState old_state{};
KMemoryPermission old_perm{};
KMemoryAttribute old_attr{};
size_t num_allocator_blocks{};
R_TRY(this->CheckMemoryState(std::addressof(old_state), std::addressof(old_perm),
std::addressof(old_attr), std::addressof(num_allocator_blocks),
addr, size, state_mask | KMemoryState::FlagReferenceCounted,
state | KMemoryState::FlagReferenceCounted, perm_mask, perm,
attr_mask, attr));
// Check the page group.
if (pg != nullptr) {
UNIMPLEMENTED_MSG("PageGroup support is unimplemented!");
}
// Decide on new perm and attr.
new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm;
KMemoryAttribute new_attr = static_cast<KMemoryAttribute>(old_attr & ~lock_attr);
// Update permission, if we need to.
if (new_perm != old_perm) {
R_TRY(Operate(addr, num_pages, new_perm, OperationType::ChangePermissions));
}
// Apply the memory block updates.
block_manager->Update(addr, num_pages, old_state, new_perm, new_attr);
return ResultSuccess;
}
} // namespace Kernel

View File

@@ -12,6 +12,7 @@
#include "core/file_sys/program_metadata.h"
#include "core/hle/kernel/k_light_lock.h"
#include "core/hle/kernel/k_memory_block.h"
#include "core/hle/kernel/k_memory_layout.h"
#include "core/hle/kernel/k_memory_manager.h"
#include "core/hle/result.h"
@@ -46,7 +47,14 @@ public:
ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size);
ResultCode MapPages(VAddr addr, KPageLinkedList& page_linked_list, KMemoryState state,
KMemoryPermission perm);
ResultCode MapPages(VAddr* out_addr, std::size_t num_pages, std::size_t alignment,
PAddr phys_addr, KMemoryState state, KMemoryPermission perm) {
return this->MapPages(out_addr, num_pages, alignment, phys_addr, true,
this->GetRegionAddress(state), this->GetRegionSize(state) / PageSize,
state, perm);
}
ResultCode UnmapPages(VAddr addr, KPageLinkedList& page_linked_list, KMemoryState state);
ResultCode UnmapPages(VAddr address, std::size_t num_pages, KMemoryState state);
ResultCode SetProcessMemoryPermission(VAddr addr, std::size_t size,
Svc::MemoryPermission svc_perm);
KMemoryInfo QueryInfo(VAddr addr);
@@ -64,6 +72,10 @@ public:
ResultCode UnlockForDeviceAddressSpace(VAddr addr, std::size_t size);
ResultCode LockForCodeMemory(VAddr addr, std::size_t size);
ResultCode UnlockForCodeMemory(VAddr addr, std::size_t size);
ResultCode MakeAndOpenPageGroup(KPageLinkedList* out, VAddr address, size_t num_pages,
KMemoryState state_mask, KMemoryState state,
KMemoryPermission perm_mask, KMemoryPermission perm,
KMemoryAttribute attr_mask, KMemoryAttribute attr);
Common::PageTable& PageTableImpl() {
return page_table_impl;
@@ -91,6 +103,9 @@ private:
ResultCode InitializeMemoryLayout(VAddr start, VAddr end);
ResultCode MapPages(VAddr addr, const KPageLinkedList& page_linked_list,
KMemoryPermission perm);
ResultCode MapPages(VAddr* out_addr, std::size_t num_pages, std::size_t alignment,
PAddr phys_addr, bool is_pa_valid, VAddr region_start,
std::size_t region_num_pages, KMemoryState state, KMemoryPermission perm);
ResultCode UnmapPages(VAddr addr, const KPageLinkedList& page_linked_list);
bool IsRegionMapped(VAddr address, u64 size);
bool IsRegionContiguous(VAddr addr, u64 size) const;
@@ -105,6 +120,9 @@ private:
VAddr GetRegionAddress(KMemoryState state) const;
std::size_t GetRegionSize(KMemoryState state) const;
VAddr FindFreeArea(VAddr region_start, std::size_t region_num_pages, std::size_t num_pages,
std::size_t alignment, std::size_t offset, std::size_t guard_pages);
ResultCode CheckMemoryStateContiguous(std::size_t* out_blocks_needed, VAddr addr,
std::size_t size, KMemoryState state_mask,
KMemoryState state, KMemoryPermission perm_mask,
@@ -137,7 +155,7 @@ private:
return CheckMemoryState(nullptr, nullptr, nullptr, out_blocks_needed, addr, size,
state_mask, state, perm_mask, perm, attr_mask, attr, ignore_attr);
}
ResultCode CheckMemoryState(VAddr addr, size_t size, KMemoryState state_mask,
ResultCode CheckMemoryState(VAddr addr, std::size_t size, KMemoryState state_mask,
KMemoryState state, KMemoryPermission perm_mask,
KMemoryPermission perm, KMemoryAttribute attr_mask,
KMemoryAttribute attr,
@@ -146,10 +164,37 @@ private:
attr_mask, attr, ignore_attr);
}
ResultCode LockMemoryAndOpen(KPageLinkedList* out_pg, PAddr* out_paddr, VAddr addr, size_t size,
KMemoryState state_mask, KMemoryState state,
KMemoryPermission perm_mask, KMemoryPermission perm,
KMemoryAttribute attr_mask, KMemoryAttribute attr,
KMemoryPermission new_perm, KMemoryAttribute lock_attr);
ResultCode UnlockMemory(VAddr addr, size_t size, KMemoryState state_mask, KMemoryState state,
KMemoryPermission perm_mask, KMemoryPermission perm,
KMemoryAttribute attr_mask, KMemoryAttribute attr,
KMemoryPermission new_perm, KMemoryAttribute lock_attr,
const KPageLinkedList* pg);
ResultCode MakePageGroup(KPageLinkedList& pg, VAddr addr, size_t num_pages);
bool IsLockedByCurrentThread() const {
return general_lock.IsLockedByCurrentThread();
}
bool IsHeapPhysicalAddress(const KMemoryLayout& layout, PAddr phys_addr) {
ASSERT(this->IsLockedByCurrentThread());
return layout.IsHeapPhysicalAddress(cached_physical_heap_region, phys_addr);
}
bool GetPhysicalAddressLocked(PAddr* out, VAddr virt_addr) const {
ASSERT(this->IsLockedByCurrentThread());
*out = GetPhysicalAddr(virt_addr);
return *out != 0;
}
mutable KLightLock general_lock;
mutable KLightLock map_physical_memory_lock;
@@ -210,7 +255,7 @@ public:
constexpr VAddr GetAliasCodeRegionSize() const {
return alias_code_region_end - alias_code_region_start;
}
size_t GetNormalMemorySize() {
std::size_t GetNormalMemorySize() {
KScopedLightLock lk(general_lock);
return GetHeapSize() + mapped_physical_memory_size;
}
@@ -309,6 +354,7 @@ private:
bool is_aslr_enabled{};
u32 heap_fill_value{};
const KMemoryRegion* cached_physical_heap_region{};
KMemoryManager::Pool memory_pool{KMemoryManager::Pool::Application};
KMemoryManager::Direction allocation_option{KMemoryManager::Direction::FromFront};

View File

@@ -57,7 +57,12 @@ ResultCode KPort::EnqueueSession(KServerSession* session) {
R_UNLESS(state == State::Normal, ResultPortClosed);
server.EnqueueSession(session);
server.GetSessionRequestHandler()->ClientConnected(server.AcceptSession());
if (auto session_ptr = server.GetSessionRequestHandler().lock()) {
session_ptr->ClientConnected(server.AcceptSession());
} else {
UNREACHABLE();
}
return ResultSuccess;
}

View File

@@ -13,7 +13,6 @@
#include "common/scope_exit.h"
#include "common/settings.h"
#include "core/core.h"
#include "core/device_memory.h"
#include "core/file_sys/program_metadata.h"
#include "core/hle/kernel/code_set.h"
#include "core/hle/kernel/k_memory_block_manager.h"
@@ -24,7 +23,6 @@
#include "core/hle/kernel/k_scoped_resource_reservation.h"
#include "core/hle/kernel/k_shared_memory.h"
#include "core/hle/kernel/k_shared_memory_info.h"
#include "core/hle/kernel/k_slab_heap.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/svc_results.h"
@@ -70,58 +68,6 @@ void SetupMainThread(Core::System& system, KProcess& owner_process, u32 priority
}
} // Anonymous namespace
// Represents a page used for thread-local storage.
//
// Each TLS page contains slots that may be used by processes and threads.
// Every process and thread is created with a slot in some arbitrary page
// (whichever page happens to have an available slot).
class TLSPage {
public:
static constexpr std::size_t num_slot_entries =
Core::Memory::PAGE_SIZE / Core::Memory::TLS_ENTRY_SIZE;
explicit TLSPage(VAddr address) : base_address{address} {}
bool HasAvailableSlots() const {
return !is_slot_used.all();
}
VAddr GetBaseAddress() const {
return base_address;
}
std::optional<VAddr> ReserveSlot() {
for (std::size_t i = 0; i < is_slot_used.size(); i++) {
if (is_slot_used[i]) {
continue;
}
is_slot_used[i] = true;
return base_address + (i * Core::Memory::TLS_ENTRY_SIZE);
}
return std::nullopt;
}
void ReleaseSlot(VAddr address) {
// Ensure that all given addresses are consistent with how TLS pages
// are intended to be used when releasing slots.
ASSERT(IsWithinPage(address));
ASSERT((address % Core::Memory::TLS_ENTRY_SIZE) == 0);
const std::size_t index = (address - base_address) / Core::Memory::TLS_ENTRY_SIZE;
is_slot_used[index] = false;
}
private:
bool IsWithinPage(VAddr address) const {
return base_address <= address && address < base_address + Core::Memory::PAGE_SIZE;
}
VAddr base_address;
std::bitset<num_slot_entries> is_slot_used;
};
ResultCode KProcess::Initialize(KProcess* process, Core::System& system, std::string process_name,
ProcessType type, KResourceLimit* res_limit) {
auto& kernel = system.Kernel();
@@ -404,7 +350,7 @@ ResultCode KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata,
}
// Create TLS region
tls_region_address = CreateTLSRegion();
R_TRY(this->CreateThreadLocalRegion(std::addressof(tls_region_address)));
memory_reservation.Commit();
return handle_table.Initialize(capabilities.GetHandleTableSize());
@@ -444,7 +390,7 @@ void KProcess::PrepareForTermination() {
stop_threads(kernel.System().GlobalSchedulerContext().GetThreadList());
FreeTLSRegion(tls_region_address);
this->DeleteThreadLocalRegion(tls_region_address);
tls_region_address = 0;
if (resource_limit) {
@@ -456,9 +402,6 @@ void KProcess::PrepareForTermination() {
}
void KProcess::Finalize() {
// Finalize the handle table and close any open handles.
handle_table.Finalize();
// Free all shared memory infos.
{
auto it = shared_memory_list.begin();
@@ -483,67 +426,110 @@ void KProcess::Finalize() {
resource_limit = nullptr;
}
// Finalize the page table.
page_table.reset();
// Perform inherited finalization.
KAutoObjectWithSlabHeapAndContainer<KProcess, KWorkerTask>::Finalize();
}
/**
* Attempts to find a TLS page that contains a free slot for
* use by a thread.
*
* @returns If a page with an available slot is found, then an iterator
* pointing to the page is returned. Otherwise the end iterator
* is returned instead.
*/
static auto FindTLSPageWithAvailableSlots(std::vector<TLSPage>& tls_pages) {
return std::find_if(tls_pages.begin(), tls_pages.end(),
[](const auto& page) { return page.HasAvailableSlots(); });
}
ResultCode KProcess::CreateThreadLocalRegion(VAddr* out) {
KThreadLocalPage* tlp = nullptr;
VAddr tlr = 0;
VAddr KProcess::CreateTLSRegion() {
KScopedSchedulerLock lock(kernel);
if (auto tls_page_iter{FindTLSPageWithAvailableSlots(tls_pages)};
tls_page_iter != tls_pages.cend()) {
return *tls_page_iter->ReserveSlot();
// See if we can get a region from a partially used TLP.
{
KScopedSchedulerLock sl{kernel};
if (auto it = partially_used_tlp_tree.begin(); it != partially_used_tlp_tree.end()) {
tlr = it->Reserve();
ASSERT(tlr != 0);
if (it->IsAllUsed()) {
tlp = std::addressof(*it);
partially_used_tlp_tree.erase(it);
fully_used_tlp_tree.insert(*tlp);
}
*out = tlr;
return ResultSuccess;
}
}
Page* const tls_page_ptr{kernel.GetUserSlabHeapPages().Allocate()};
ASSERT(tls_page_ptr);
// Allocate a new page.
tlp = KThreadLocalPage::Allocate(kernel);
R_UNLESS(tlp != nullptr, ResultOutOfMemory);
auto tlp_guard = SCOPE_GUARD({ KThreadLocalPage::Free(kernel, tlp); });
const VAddr start{page_table->GetKernelMapRegionStart()};
const VAddr size{page_table->GetKernelMapRegionEnd() - start};
const PAddr tls_map_addr{kernel.System().DeviceMemory().GetPhysicalAddr(tls_page_ptr)};
const VAddr tls_page_addr{page_table
->AllocateAndMapMemory(1, PageSize, true, start, size / PageSize,
KMemoryState::ThreadLocal,
KMemoryPermission::UserReadWrite,
tls_map_addr)
.ValueOr(0)};
// Initialize the new page.
R_TRY(tlp->Initialize(kernel, this));
ASSERT(tls_page_addr);
// Reserve a TLR.
tlr = tlp->Reserve();
ASSERT(tlr != 0);
std::memset(tls_page_ptr, 0, PageSize);
tls_pages.emplace_back(tls_page_addr);
// Insert into our tree.
{
KScopedSchedulerLock sl{kernel};
if (tlp->IsAllUsed()) {
fully_used_tlp_tree.insert(*tlp);
} else {
partially_used_tlp_tree.insert(*tlp);
}
}
const auto reserve_result{tls_pages.back().ReserveSlot()};
ASSERT(reserve_result.has_value());
return *reserve_result;
// We succeeded!
tlp_guard.Cancel();
*out = tlr;
return ResultSuccess;
}
void KProcess::FreeTLSRegion(VAddr tls_address) {
KScopedSchedulerLock lock(kernel);
const VAddr aligned_address = Common::AlignDown(tls_address, Core::Memory::PAGE_SIZE);
auto iter =
std::find_if(tls_pages.begin(), tls_pages.end(), [aligned_address](const auto& page) {
return page.GetBaseAddress() == aligned_address;
});
ResultCode KProcess::DeleteThreadLocalRegion(VAddr addr) {
KThreadLocalPage* page_to_free = nullptr;
// Something has gone very wrong if we're freeing a region
// with no actual page available.
ASSERT(iter != tls_pages.cend());
// Release the region.
{
KScopedSchedulerLock sl{kernel};
iter->ReleaseSlot(tls_address);
// Try to find the page in the partially used list.
auto it = partially_used_tlp_tree.find_key(Common::AlignDown(addr, PageSize));
if (it == partially_used_tlp_tree.end()) {
// If we don't find it, it has to be in the fully used list.
it = fully_used_tlp_tree.find_key(Common::AlignDown(addr, PageSize));
R_UNLESS(it != fully_used_tlp_tree.end(), ResultInvalidAddress);
// Release the region.
it->Release(addr);
// Move the page out of the fully used list.
KThreadLocalPage* tlp = std::addressof(*it);
fully_used_tlp_tree.erase(it);
if (tlp->IsAllFree()) {
page_to_free = tlp;
} else {
partially_used_tlp_tree.insert(*tlp);
}
} else {
// Release the region.
it->Release(addr);
// Handle the all-free case.
KThreadLocalPage* tlp = std::addressof(*it);
if (tlp->IsAllFree()) {
partially_used_tlp_tree.erase(it);
page_to_free = tlp;
}
}
}
// If we should free the page it was in, do so.
if (page_to_free != nullptr) {
page_to_free->Finalize();
KThreadLocalPage::Free(kernel, page_to_free);
}
return ResultSuccess;
}
void KProcess::LoadModule(CodeSet code_set, VAddr base_addr) {

View File

@@ -8,13 +8,13 @@
#include <cstddef>
#include <list>
#include <string>
#include <vector>
#include "common/common_types.h"
#include "core/hle/kernel/k_address_arbiter.h"
#include "core/hle/kernel/k_auto_object.h"
#include "core/hle/kernel/k_condition_variable.h"
#include "core/hle/kernel/k_handle_table.h"
#include "core/hle/kernel/k_synchronization_object.h"
#include "core/hle/kernel/k_thread_local_page.h"
#include "core/hle/kernel/k_worker_task.h"
#include "core/hle/kernel/process_capability.h"
#include "core/hle/kernel/slab_helpers.h"
@@ -362,10 +362,10 @@ public:
// Thread-local storage management
// Marks the next available region as used and returns the address of the slot.
[[nodiscard]] VAddr CreateTLSRegion();
[[nodiscard]] ResultCode CreateThreadLocalRegion(VAddr* out);
// Frees a used TLS slot identified by the given address
void FreeTLSRegion(VAddr tls_address);
ResultCode DeleteThreadLocalRegion(VAddr addr);
private:
void PinThread(s32 core_id, KThread* thread) {
@@ -413,13 +413,6 @@ private:
/// The ideal CPU core for this process, threads are scheduled on this core by default.
u8 ideal_core = 0;
/// The Thread Local Storage area is allocated as processes create threads,
/// each TLS area is 0x200 bytes, so one page (0x1000) is split up in 8 parts, and each part
/// holds the TLS for a specific thread. This vector contains which parts are in use for each
/// page as a bitmask.
/// This vector will grow as more pages are allocated for new threads.
std::vector<TLSPage> tls_pages;
/// Contains the parsed process capability descriptors.
ProcessCapabilities capabilities;
@@ -482,6 +475,12 @@ private:
KThread* exception_thread{};
KLightLock state_lock;
using TLPTree =
Common::IntrusiveRedBlackTreeBaseTraits<KThreadLocalPage>::TreeType<KThreadLocalPage>;
using TLPIterator = TLPTree::iterator;
TLPTree fully_used_tlp_tree;
TLPTree partially_used_tlp_tree;
};
} // namespace Kernel

View File

@@ -22,7 +22,6 @@
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/physical_core.h"
#include "core/hle/kernel/time_manager.h"
namespace Kernel {

View File

@@ -30,11 +30,11 @@ public:
/// Whether or not this server port has an HLE handler available.
bool HasSessionRequestHandler() const {
return session_handler != nullptr;
return !session_handler.expired();
}
/// Gets the HLE handler for this port.
SessionRequestHandlerPtr GetSessionRequestHandler() const {
SessionRequestHandlerWeakPtr GetSessionRequestHandler() const {
return session_handler;
}
@@ -42,7 +42,7 @@ public:
* Sets the HLE handler template for the port. ServerSessions crated by connecting to this port
* will inherit a reference to this handler.
*/
void SetSessionHandler(SessionRequestHandlerPtr&& handler) {
void SetSessionHandler(SessionRequestHandlerWeakPtr&& handler) {
session_handler = std::move(handler);
}
@@ -66,7 +66,7 @@ private:
void CleanupSessions();
SessionList session_list;
SessionRequestHandlerPtr session_handler;
SessionRequestHandlerWeakPtr session_handler;
KPort* parent{};
};

View File

@@ -27,10 +27,7 @@ namespace Kernel {
KServerSession::KServerSession(KernelCore& kernel_) : KSynchronizationObject{kernel_} {}
KServerSession::~KServerSession() {
// Ensure that the global list tracking server sessions does not hold on to a reference.
kernel.UnregisterServerSession(this);
}
KServerSession::~KServerSession() = default;
void KServerSession::Initialize(KSession* parent_session_, std::string&& name_,
std::shared_ptr<SessionRequestManager> manager_) {
@@ -49,6 +46,9 @@ void KServerSession::Destroy() {
parent->OnServerClosed();
parent->Close();
// Release host emulation members.
manager.reset();
}
void KServerSession::OnClientClosed() {
@@ -98,7 +98,12 @@ ResultCode KServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& co
UNREACHABLE();
return ResultSuccess; // Ignore error if asserts are off
}
return manager->DomainHandler(object_id - 1)->HandleSyncRequest(*this, context);
if (auto strong_ptr = manager->DomainHandler(object_id - 1).lock()) {
return strong_ptr->HandleSyncRequest(*this, context);
} else {
UNREACHABLE();
return ResultSuccess;
}
case IPC::DomainMessageHeader::CommandType::CloseVirtualHandle: {
LOG_DEBUG(IPC, "CloseVirtualHandle, object_id=0x{:08X}", object_id);

View File

@@ -16,39 +16,34 @@ class KernelCore;
namespace impl {
class KSlabHeapImpl final {
public:
class KSlabHeapImpl {
YUZU_NON_COPYABLE(KSlabHeapImpl);
YUZU_NON_MOVEABLE(KSlabHeapImpl);
public:
struct Node {
Node* next{};
};
public:
constexpr KSlabHeapImpl() = default;
constexpr ~KSlabHeapImpl() = default;
void Initialize(std::size_t size) {
ASSERT(head == nullptr);
obj_size = size;
}
constexpr std::size_t GetObjectSize() const {
return obj_size;
void Initialize() {
ASSERT(m_head == nullptr);
}
Node* GetHead() const {
return head;
return m_head;
}
void* Allocate() {
Node* ret = head.load();
Node* ret = m_head.load();
do {
if (ret == nullptr) {
break;
}
} while (!head.compare_exchange_weak(ret, ret->next));
} while (!m_head.compare_exchange_weak(ret, ret->next));
return ret;
}
@@ -56,170 +51,157 @@ public:
void Free(void* obj) {
Node* node = static_cast<Node*>(obj);
Node* cur_head = head.load();
Node* cur_head = m_head.load();
do {
node->next = cur_head;
} while (!head.compare_exchange_weak(cur_head, node));
} while (!m_head.compare_exchange_weak(cur_head, node));
}
private:
std::atomic<Node*> head{};
std::size_t obj_size{};
std::atomic<Node*> m_head{};
};
} // namespace impl
class KSlabHeapBase {
public:
template <bool SupportDynamicExpansion>
class KSlabHeapBase : protected impl::KSlabHeapImpl {
YUZU_NON_COPYABLE(KSlabHeapBase);
YUZU_NON_MOVEABLE(KSlabHeapBase);
private:
size_t m_obj_size{};
uintptr_t m_peak{};
uintptr_t m_start{};
uintptr_t m_end{};
private:
void UpdatePeakImpl(uintptr_t obj) {
static_assert(std::atomic_ref<uintptr_t>::is_always_lock_free);
std::atomic_ref<uintptr_t> peak_ref(m_peak);
const uintptr_t alloc_peak = obj + this->GetObjectSize();
uintptr_t cur_peak = m_peak;
do {
if (alloc_peak <= cur_peak) {
break;
}
} while (!peak_ref.compare_exchange_strong(cur_peak, alloc_peak));
}
public:
constexpr KSlabHeapBase() = default;
constexpr ~KSlabHeapBase() = default;
constexpr bool Contains(uintptr_t addr) const {
return start <= addr && addr < end;
bool Contains(uintptr_t address) const {
return m_start <= address && address < m_end;
}
constexpr std::size_t GetSlabHeapSize() const {
return (end - start) / GetObjectSize();
}
constexpr std::size_t GetObjectSize() const {
return impl.GetObjectSize();
}
constexpr uintptr_t GetSlabHeapAddress() const {
return start;
}
std::size_t GetObjectIndexImpl(const void* obj) const {
return (reinterpret_cast<uintptr_t>(obj) - start) / GetObjectSize();
}
std::size_t GetPeakIndex() const {
return GetObjectIndexImpl(reinterpret_cast<const void*>(peak));
}
void* AllocateImpl() {
return impl.Allocate();
}
void FreeImpl(void* obj) {
// Don't allow freeing an object that wasn't allocated from this heap
ASSERT(Contains(reinterpret_cast<uintptr_t>(obj)));
impl.Free(obj);
}
void InitializeImpl(std::size_t obj_size, void* memory, std::size_t memory_size) {
// Ensure we don't initialize a slab using null memory
void Initialize(size_t obj_size, void* memory, size_t memory_size) {
// Ensure we don't initialize a slab using null memory.
ASSERT(memory != nullptr);
// Initialize the base allocator
impl.Initialize(obj_size);
// Set our object size.
m_obj_size = obj_size;
// Set our tracking variables
const std::size_t num_obj = (memory_size / obj_size);
start = reinterpret_cast<uintptr_t>(memory);
end = start + num_obj * obj_size;
peak = start;
// Initialize the base allocator.
KSlabHeapImpl::Initialize();
// Free the objects
u8* cur = reinterpret_cast<u8*>(end);
// Set our tracking variables.
const size_t num_obj = (memory_size / obj_size);
m_start = reinterpret_cast<uintptr_t>(memory);
m_end = m_start + num_obj * obj_size;
m_peak = m_start;
for (std::size_t i{}; i < num_obj; i++) {
// Free the objects.
u8* cur = reinterpret_cast<u8*>(m_end);
for (size_t i = 0; i < num_obj; i++) {
cur -= obj_size;
impl.Free(cur);
KSlabHeapImpl::Free(cur);
}
}
private:
using Impl = impl::KSlabHeapImpl;
size_t GetSlabHeapSize() const {
return (m_end - m_start) / this->GetObjectSize();
}
Impl impl;
uintptr_t peak{};
uintptr_t start{};
uintptr_t end{};
size_t GetObjectSize() const {
return m_obj_size;
}
void* Allocate() {
void* obj = KSlabHeapImpl::Allocate();
return obj;
}
void Free(void* obj) {
// Don't allow freeing an object that wasn't allocated from this heap.
const bool contained = this->Contains(reinterpret_cast<uintptr_t>(obj));
ASSERT(contained);
KSlabHeapImpl::Free(obj);
}
size_t GetObjectIndex(const void* obj) const {
if constexpr (SupportDynamicExpansion) {
if (!this->Contains(reinterpret_cast<uintptr_t>(obj))) {
return std::numeric_limits<size_t>::max();
}
}
return (reinterpret_cast<uintptr_t>(obj) - m_start) / this->GetObjectSize();
}
size_t GetPeakIndex() const {
return this->GetObjectIndex(reinterpret_cast<const void*>(m_peak));
}
uintptr_t GetSlabHeapAddress() const {
return m_start;
}
size_t GetNumRemaining() const {
// Only calculate the number of remaining objects under debug configuration.
return 0;
}
};
template <typename T>
class KSlabHeap final : public KSlabHeapBase {
class KSlabHeap final : public KSlabHeapBase<false> {
private:
using BaseHeap = KSlabHeapBase<false>;
public:
enum class AllocationType {
Host,
Guest,
};
constexpr KSlabHeap() = default;
explicit constexpr KSlabHeap(AllocationType allocation_type_ = AllocationType::Host)
: KSlabHeapBase(), allocation_type{allocation_type_} {}
void Initialize(void* memory, std::size_t memory_size) {
if (allocation_type == AllocationType::Guest) {
InitializeImpl(sizeof(T), memory, memory_size);
}
void Initialize(void* memory, size_t memory_size) {
BaseHeap::Initialize(sizeof(T), memory, memory_size);
}
T* Allocate() {
switch (allocation_type) {
case AllocationType::Host:
// Fallback for cases where we do not yet support allocating guest memory from the slab
// heap, such as for kernel memory regions.
return new T;
T* obj = static_cast<T*>(BaseHeap::Allocate());
case AllocationType::Guest:
T* obj = static_cast<T*>(AllocateImpl());
if (obj != nullptr) {
new (obj) T();
}
return obj;
if (obj != nullptr) [[likely]] {
std::construct_at(obj);
}
UNREACHABLE_MSG("Invalid AllocationType {}", allocation_type);
return nullptr;
return obj;
}
T* AllocateWithKernel(KernelCore& kernel) {
switch (allocation_type) {
case AllocationType::Host:
// Fallback for cases where we do not yet support allocating guest memory from the slab
// heap, such as for kernel memory regions.
return new T(kernel);
T* Allocate(KernelCore& kernel) {
T* obj = static_cast<T*>(BaseHeap::Allocate());
case AllocationType::Guest:
T* obj = static_cast<T*>(AllocateImpl());
if (obj != nullptr) {
new (obj) T(kernel);
}
return obj;
if (obj != nullptr) [[likely]] {
std::construct_at(obj, kernel);
}
UNREACHABLE_MSG("Invalid AllocationType {}", allocation_type);
return nullptr;
return obj;
}
void Free(T* obj) {
switch (allocation_type) {
case AllocationType::Host:
// Fallback for cases where we do not yet support allocating guest memory from the slab
// heap, such as for kernel memory regions.
delete obj;
return;
case AllocationType::Guest:
FreeImpl(obj);
return;
}
UNREACHABLE_MSG("Invalid AllocationType {}", allocation_type);
BaseHeap::Free(obj);
}
constexpr std::size_t GetObjectIndex(const T* obj) const {
return GetObjectIndexImpl(obj);
size_t GetObjectIndex(const T* obj) const {
return BaseHeap::GetObjectIndex(obj);
}
private:
const AllocationType allocation_type;
};
} // namespace Kernel

View File

@@ -14,9 +14,7 @@
#include "common/common_types.h"
#include "common/fiber.h"
#include "common/logging/log.h"
#include "common/scope_exit.h"
#include "common/settings.h"
#include "common/thread_queue_list.h"
#include "core/core.h"
#include "core/cpu_manager.h"
#include "core/hardware_properties.h"
@@ -33,7 +31,6 @@
#include "core/hle/kernel/k_worker_task_manager.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/svc_results.h"
#include "core/hle/kernel/time_manager.h"
#include "core/hle/result.h"
#include "core/memory.h"
@@ -210,7 +207,7 @@ ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_s
if (owner != nullptr) {
// Setup the TLS, if needed.
if (type == ThreadType::User) {
tls_address = owner->CreateTLSRegion();
R_TRY(owner->CreateThreadLocalRegion(std::addressof(tls_address)));
}
parent = owner;
@@ -305,7 +302,7 @@ void KThread::Finalize() {
// If the thread has a local region, delete it.
if (tls_address != 0) {
parent->FreeTLSRegion(tls_address);
ASSERT(parent->DeleteThreadLocalRegion(tls_address).IsSuccess());
}
// Release any waiters.
@@ -326,6 +323,9 @@ void KThread::Finalize() {
}
}
// Release host emulation members.
host_context.reset();
// Perform inherited finalization.
KSynchronizationObject::Finalize();
}

View File

@@ -656,7 +656,7 @@ private:
static_assert(sizeof(SyncObjectBuffer::sync_objects) == sizeof(SyncObjectBuffer::handles));
struct ConditionVariableComparator {
struct LightCompareType {
struct RedBlackKeyType {
u64 cv_key{};
s32 priority{};
@@ -672,8 +672,8 @@ private:
template <typename T>
requires(
std::same_as<T, KThread> ||
std::same_as<T, LightCompareType>) static constexpr int Compare(const T& lhs,
const KThread& rhs) {
std::same_as<T, RedBlackKeyType>) static constexpr int Compare(const T& lhs,
const KThread& rhs) {
const u64 l_key = lhs.GetConditionVariableKey();
const u64 r_key = rhs.GetConditionVariableKey();

View File

@@ -0,0 +1,68 @@
// Copyright 2022 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/scope_exit.h"
#include "core/core.h"
#include "core/hle/kernel/k_memory_block.h"
#include "core/hle/kernel/k_page_buffer.h"
#include "core/hle/kernel/k_page_table.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_thread_local_page.h"
#include "core/hle/kernel/kernel.h"
namespace Kernel {
ResultCode KThreadLocalPage::Initialize(KernelCore& kernel, KProcess* process) {
// Set that this process owns us.
m_owner = process;
m_kernel = &kernel;
// Allocate a new page.
KPageBuffer* page_buf = KPageBuffer::Allocate(kernel);
R_UNLESS(page_buf != nullptr, ResultOutOfMemory);
auto page_buf_guard = SCOPE_GUARD({ KPageBuffer::Free(kernel, page_buf); });
// Map the address in.
const auto phys_addr = kernel.System().DeviceMemory().GetPhysicalAddr(page_buf);
R_TRY(m_owner->PageTable().MapPages(std::addressof(m_virt_addr), 1, PageSize, phys_addr,
KMemoryState::ThreadLocal,
KMemoryPermission::UserReadWrite));
// We succeeded.
page_buf_guard.Cancel();
return ResultSuccess;
}
ResultCode KThreadLocalPage::Finalize() {
// Get the physical address of the page.
const PAddr phys_addr = m_owner->PageTable().GetPhysicalAddr(m_virt_addr);
ASSERT(phys_addr);
// Unmap the page.
R_TRY(m_owner->PageTable().UnmapPages(this->GetAddress(), 1, KMemoryState::ThreadLocal));
// Free the page.
KPageBuffer::Free(*m_kernel, KPageBuffer::FromPhysicalAddress(m_kernel->System(), phys_addr));
return ResultSuccess;
}
VAddr KThreadLocalPage::Reserve() {
for (size_t i = 0; i < m_is_region_free.size(); i++) {
if (m_is_region_free[i]) {
m_is_region_free[i] = false;
return this->GetRegionAddress(i);
}
}
return 0;
}
void KThreadLocalPage::Release(VAddr addr) {
m_is_region_free[this->GetRegionIndex(addr)] = true;
}
} // namespace Kernel

View File

@@ -0,0 +1,111 @@
// Copyright 2022 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <algorithm>
#include <array>
#include "common/alignment.h"
#include "common/assert.h"
#include "common/common_types.h"
#include "common/intrusive_red_black_tree.h"
#include "core/hle/kernel/memory_types.h"
#include "core/hle/kernel/slab_helpers.h"
#include "core/hle/result.h"
namespace Kernel {
class KernelCore;
class KProcess;
class KThreadLocalPage final : public Common::IntrusiveRedBlackTreeBaseNode<KThreadLocalPage>,
public KSlabAllocated<KThreadLocalPage> {
public:
static constexpr size_t RegionsPerPage = PageSize / Svc::ThreadLocalRegionSize;
static_assert(RegionsPerPage > 0);
public:
constexpr explicit KThreadLocalPage(VAddr addr = {}) : m_virt_addr(addr) {
m_is_region_free.fill(true);
}
constexpr VAddr GetAddress() const {
return m_virt_addr;
}
ResultCode Initialize(KernelCore& kernel, KProcess* process);
ResultCode Finalize();
VAddr Reserve();
void Release(VAddr addr);
bool IsAllUsed() const {
return std::ranges::all_of(m_is_region_free.begin(), m_is_region_free.end(),
[](bool is_free) { return !is_free; });
}
bool IsAllFree() const {
return std::ranges::all_of(m_is_region_free.begin(), m_is_region_free.end(),
[](bool is_free) { return is_free; });
}
bool IsAnyUsed() const {
return !this->IsAllFree();
}
bool IsAnyFree() const {
return !this->IsAllUsed();
}
public:
using RedBlackKeyType = VAddr;
static constexpr RedBlackKeyType GetRedBlackKey(const RedBlackKeyType& v) {
return v;
}
static constexpr RedBlackKeyType GetRedBlackKey(const KThreadLocalPage& v) {
return v.GetAddress();
}
template <typename T>
requires(std::same_as<T, KThreadLocalPage> ||
std::same_as<T, RedBlackKeyType>) static constexpr int Compare(const T& lhs,
const KThreadLocalPage&
rhs) {
const VAddr lval = GetRedBlackKey(lhs);
const VAddr rval = GetRedBlackKey(rhs);
if (lval < rval) {
return -1;
} else if (lval == rval) {
return 0;
} else {
return 1;
}
}
private:
constexpr VAddr GetRegionAddress(size_t i) const {
return this->GetAddress() + i * Svc::ThreadLocalRegionSize;
}
constexpr bool Contains(VAddr addr) const {
return this->GetAddress() <= addr && addr < this->GetAddress() + PageSize;
}
constexpr size_t GetRegionIndex(VAddr addr) const {
ASSERT(Common::IsAligned(addr, Svc::ThreadLocalRegionSize));
ASSERT(this->Contains(addr));
return (addr - this->GetAddress()) / Svc::ThreadLocalRegionSize;
}
private:
VAddr m_virt_addr{};
KProcess* m_owner{};
KernelCore* m_kernel{};
std::array<bool, RegionsPerPage> m_is_region_free{};
};
} // namespace Kernel

View File

@@ -22,9 +22,7 @@
#include "core/arm/exclusive_monitor.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/core_timing_util.h"
#include "core/cpu_manager.h"
#include "core/device_memory.h"
#include "core/hardware_properties.h"
#include "core/hle/kernel/init/init_slab_setup.h"
#include "core/hle/kernel/k_client_port.h"
@@ -35,7 +33,6 @@
#include "core/hle/kernel/k_resource_limit.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_shared_memory.h"
#include "core/hle/kernel/k_slab_heap.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/k_worker_task_manager.h"
#include "core/hle/kernel/kernel.h"
@@ -52,7 +49,7 @@ namespace Kernel {
struct KernelCore::Impl {
explicit Impl(Core::System& system_, KernelCore& kernel_)
: time_manager{system_}, object_list_container{kernel_},
: time_manager{system_},
service_threads_manager{1, "yuzu:ServiceThreadsManager"}, system{system_} {}
void SetMulticore(bool is_multi) {
@@ -60,6 +57,7 @@ struct KernelCore::Impl {
}
void Initialize(KernelCore& kernel) {
global_object_list_container = std::make_unique<KAutoObjectWithListContainer>(kernel);
global_scheduler_context = std::make_unique<Kernel::GlobalSchedulerContext>(kernel);
global_handle_table = std::make_unique<Kernel::KHandleTable>(kernel);
global_handle_table->Initialize(KHandleTable::MaxTableSize);
@@ -76,7 +74,7 @@ struct KernelCore::Impl {
// Initialize kernel memory and resources.
InitializeSystemResourceLimit(kernel, system.CoreTiming());
InitializeMemoryLayout();
InitializePageSlab();
Init::InitializeKPageBufferSlabHeap(system);
InitializeSchedulers();
InitializeSuspendThreads();
InitializePreemption(kernel);
@@ -107,19 +105,6 @@ struct KernelCore::Impl {
for (auto* server_port : server_ports_) {
server_port->Close();
}
// Close all open server sessions.
std::unordered_set<KServerSession*> server_sessions_;
{
std::lock_guard lk(server_sessions_lock);
server_sessions_ = server_sessions;
server_sessions.clear();
}
for (auto* server_session : server_sessions_) {
server_session->Close();
}
// Ensure that the object list container is finalized and properly shutdown.
object_list_container.Finalize();
// Ensures all service threads gracefully shutdown.
ClearServiceThreads();
@@ -194,11 +179,15 @@ struct KernelCore::Impl {
{
std::lock_guard lk(registered_objects_lock);
if (registered_objects.size()) {
LOG_WARNING(Kernel, "{} kernel objects were dangling on shutdown!",
registered_objects.size());
LOG_DEBUG(Kernel, "{} kernel objects were dangling on shutdown!",
registered_objects.size());
registered_objects.clear();
}
}
// Ensure that the object list container is finalized and properly shutdown.
global_object_list_container->Finalize();
global_object_list_container.reset();
}
void InitializePhysicalCores() {
@@ -291,15 +280,16 @@ struct KernelCore::Impl {
// Gets the dummy KThread for the caller, allocating a new one if this is the first time
KThread* GetHostDummyThread() {
auto make_thread = [this]() {
KThread* thread = KThread::Create(system.Kernel());
auto initialize = [this](KThread* thread) {
ASSERT(KThread::InitializeDummyThread(thread).IsSuccess());
thread->SetName(fmt::format("DummyThread:{}", GetHostThreadId()));
return thread;
};
thread_local KThread* saved_thread = make_thread();
return saved_thread;
thread_local auto raw_thread = KThread(system.Kernel());
thread_local auto thread = initialize(&raw_thread);
return thread;
}
/// Registers a CPU core thread by allocating a host thread ID for it
@@ -660,22 +650,6 @@ struct KernelCore::Impl {
time_phys_addr, time_size, "Time:SharedMemory");
}
void InitializePageSlab() {
// Allocate slab heaps
user_slab_heap_pages =
std::make_unique<KSlabHeap<Page>>(KSlabHeap<Page>::AllocationType::Guest);
// TODO(ameerj): This should be derived, not hardcoded within the kernel
constexpr u64 user_slab_heap_size{0x3de000};
// Reserve slab heaps
ASSERT(
system_resource_limit->Reserve(LimitableResource::PhysicalMemory, user_slab_heap_size));
// Initialize slab heap
user_slab_heap_pages->Initialize(
system.DeviceMemory().GetPointer(Core::DramMemoryMap::SlabHeapBase),
user_slab_heap_size);
}
KClientPort* CreateNamedServicePort(std::string name) {
auto search = service_interface_factory.find(name);
if (search == service_interface_factory.end()) {
@@ -713,7 +687,6 @@ struct KernelCore::Impl {
}
std::mutex server_ports_lock;
std::mutex server_sessions_lock;
std::mutex registered_objects_lock;
std::mutex registered_in_use_objects_lock;
@@ -737,14 +710,13 @@ struct KernelCore::Impl {
// stores all the objects in place.
std::unique_ptr<KHandleTable> global_handle_table;
KAutoObjectWithListContainer object_list_container;
std::unique_ptr<KAutoObjectWithListContainer> global_object_list_container;
/// Map of named ports managed by the kernel, which can be retrieved using
/// the ConnectToPort SVC.
std::unordered_map<std::string, ServiceInterfaceFactory> service_interface_factory;
NamedPortTable named_ports;
std::unordered_set<KServerPort*> server_ports;
std::unordered_set<KServerSession*> server_sessions;
std::unordered_set<KAutoObject*> registered_objects;
std::unordered_set<KAutoObject*> registered_in_use_objects;
@@ -756,7 +728,6 @@ struct KernelCore::Impl {
// Kernel memory management
std::unique_ptr<KMemoryManager> memory_manager;
std::unique_ptr<KSlabHeap<Page>> user_slab_heap_pages;
// Shared memory for services
Kernel::KSharedMemory* hid_shared_mem{};
@@ -915,11 +886,11 @@ const Core::ExclusiveMonitor& KernelCore::GetExclusiveMonitor() const {
}
KAutoObjectWithListContainer& KernelCore::ObjectListContainer() {
return impl->object_list_container;
return *impl->global_object_list_container;
}
const KAutoObjectWithListContainer& KernelCore::ObjectListContainer() const {
return impl->object_list_container;
return *impl->global_object_list_container;
}
void KernelCore::InvalidateAllInstructionCaches() {
@@ -949,16 +920,6 @@ KClientPort* KernelCore::CreateNamedServicePort(std::string name) {
return impl->CreateNamedServicePort(std::move(name));
}
void KernelCore::RegisterServerSession(KServerSession* server_session) {
std::lock_guard lk(impl->server_sessions_lock);
impl->server_sessions.insert(server_session);
}
void KernelCore::UnregisterServerSession(KServerSession* server_session) {
std::lock_guard lk(impl->server_sessions_lock);
impl->server_sessions.erase(server_session);
}
void KernelCore::RegisterKernelObject(KAutoObject* object) {
std::lock_guard lk(impl->registered_objects_lock);
impl->registered_objects.insert(object);
@@ -1031,14 +992,6 @@ const KMemoryManager& KernelCore::MemoryManager() const {
return *impl->memory_manager;
}
KSlabHeap<Page>& KernelCore::GetUserSlabHeapPages() {
return *impl->user_slab_heap_pages;
}
const KSlabHeap<Page>& KernelCore::GetUserSlabHeapPages() const {
return *impl->user_slab_heap_pages;
}
Kernel::KSharedMemory& KernelCore::GetHidSharedMem() {
return *impl->hid_shared_mem;
}

View File

@@ -14,7 +14,6 @@
#include "core/hardware_properties.h"
#include "core/hle/kernel/k_auto_object.h"
#include "core/hle/kernel/k_slab_heap.h"
#include "core/hle/kernel/memory_types.h"
#include "core/hle/kernel/svc_common.h"
namespace Core {
@@ -43,6 +42,7 @@ class KHandleTable;
class KLinkedListNode;
class KMemoryLayout;
class KMemoryManager;
class KPageBuffer;
class KPort;
class KProcess;
class KResourceLimit;
@@ -52,6 +52,7 @@ class KSession;
class KSharedMemory;
class KSharedMemoryInfo;
class KThread;
class KThreadLocalPage;
class KTransferMemory;
class KWorkerTaskManager;
class KWritableEvent;
@@ -194,14 +195,6 @@ public:
/// Opens a port to a service previously registered with RegisterNamedService.
KClientPort* CreateNamedServicePort(std::string name);
/// Registers a server session with the gobal emulation state, to be freed on shutdown. This is
/// necessary because we do not emulate processes for HLE sessions.
void RegisterServerSession(KServerSession* server_session);
/// Unregisters a server session previously registered with RegisterServerSession when it was
/// destroyed during the current emulation session.
void UnregisterServerSession(KServerSession* server_session);
/// Registers all kernel objects with the global emulation state, this is purely for tracking
/// leaks after emulation has been shutdown.
void RegisterKernelObject(KAutoObject* object);
@@ -239,12 +232,6 @@ public:
/// Gets the virtual memory manager for the kernel.
const KMemoryManager& MemoryManager() const;
/// Gets the slab heap allocated for user space pages.
KSlabHeap<Page>& GetUserSlabHeapPages();
/// Gets the slab heap allocated for user space pages.
const KSlabHeap<Page>& GetUserSlabHeapPages() const;
/// Gets the shared memory object for HID services.
Kernel::KSharedMemory& GetHidSharedMem();
@@ -336,6 +323,10 @@ public:
return slab_heap_container->writeable_event;
} else if constexpr (std::is_same_v<T, KCodeMemory>) {
return slab_heap_container->code_memory;
} else if constexpr (std::is_same_v<T, KPageBuffer>) {
return slab_heap_container->page_buffer;
} else if constexpr (std::is_same_v<T, KThreadLocalPage>) {
return slab_heap_container->thread_local_page;
}
}
@@ -397,6 +388,8 @@ private:
KSlabHeap<KTransferMemory> transfer_memory;
KSlabHeap<KWritableEvent> writeable_event;
KSlabHeap<KCodeMemory> code_memory;
KSlabHeap<KPageBuffer> page_buffer;
KSlabHeap<KThreadLocalPage> thread_local_page;
};
std::unique_ptr<SlabHeapContainer> slab_heap_container;

View File

@@ -49,12 +49,9 @@ ServiceThread::Impl::Impl(KernelCore& kernel, std::size_t num_threads, const std
return;
}
// Allocate a dummy guest thread for this host thread.
kernel.RegisterHostThread();
// Ensure the dummy thread allocated for this host thread is closed on exit.
auto* dummy_thread = kernel.GetCurrentEmuThread();
SCOPE_EXIT({ dummy_thread->Close(); });
while (true) {
std::function<void()> task;

View File

@@ -59,7 +59,7 @@ class KAutoObjectWithSlabHeapAndContainer : public Base {
private:
static Derived* Allocate(KernelCore& kernel) {
return kernel.SlabHeap<Derived>().AllocateWithKernel(kernel);
return kernel.SlabHeap<Derived>().Allocate(kernel);
}
static void Free(KernelCore& kernel, Derived* obj) {

View File

@@ -1362,8 +1362,11 @@ static ResultCode MapProcessMemory(Core::System& system, VAddr dst_address, Hand
ResultInvalidMemoryRegion);
// Create a new page group.
KMemoryInfo kBlockInfo = dst_pt.QueryInfo(dst_address);
KPageLinkedList pg(kBlockInfo.GetAddress(), kBlockInfo.GetNumPages());
KPageLinkedList pg;
R_TRY(src_pt.MakeAndOpenPageGroup(
std::addressof(pg), src_address, size / PageSize, KMemoryState::FlagCanMapProcess,
KMemoryState::FlagCanMapProcess, KMemoryPermission::None, KMemoryPermission::None,
KMemoryAttribute::All, KMemoryAttribute::None));
// Map the group.
R_TRY(dst_pt.MapPages(dst_address, pg, KMemoryState::SharedCode,
@@ -1408,8 +1411,8 @@ static ResultCode UnmapProcessMemory(Core::System& system, VAddr dst_address, Ha
}
static ResultCode CreateCodeMemory(Core::System& system, Handle* out, VAddr address, size_t size) {
LOG_TRACE(Kernel_SVC, "called, handle_out={}, address=0x{:X}, size=0x{:X}",
static_cast<void*>(out), address, size);
LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, size=0x{:X}", address, size);
// Get kernel instance.
auto& kernel = system.Kernel();
@@ -1664,7 +1667,7 @@ static ResultCode UnmapProcessCodeMemory(Core::System& system, Handle process_ha
return ResultInvalidAddress;
}
if (size == 0 || Common::Is4KBAligned(size)) {
if (size == 0 || !Common::Is4KBAligned(size)) {
LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X}).", size);
return ResultInvalidSize;
}

View File

@@ -96,4 +96,6 @@ constexpr inline s32 IdealCoreNoUpdate = -3;
constexpr inline s32 LowestThreadPriority = 63;
constexpr inline s32 HighestThreadPriority = 0;
constexpr inline size_t ThreadLocalRegionSize = 0x200;
} // namespace Kernel::Svc

View File

@@ -16,7 +16,6 @@
#include "core/file_sys/control_metadata.h"
#include "core/file_sys/patch_manager.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/service/acc/acc.h"
#include "core/hle/service/acc/acc_aa.h"
#include "core/hle/service/acc/acc_su.h"

View File

@@ -7,6 +7,7 @@
#include <array>
#include <optional>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
#include "common/uuid.h"

View File

@@ -980,7 +980,7 @@ private:
LOG_DEBUG(Service_AM, "called");
IPC::RequestParser rp{ctx};
applet->GetBroker().PushNormalDataFromGame(rp.PopIpcInterface<IStorage>());
applet->GetBroker().PushNormalDataFromGame(rp.PopIpcInterface<IStorage>().lock());
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
@@ -1007,7 +1007,7 @@ private:
LOG_DEBUG(Service_AM, "called");
IPC::RequestParser rp{ctx};
applet->GetBroker().PushInteractiveDataFromGame(rp.PopIpcInterface<IStorage>());
applet->GetBroker().PushInteractiveDataFromGame(rp.PopIpcInterface<IStorage>().lock());
ASSERT(applet->IsInitialized());
applet->ExecuteInteractive();

View File

@@ -0,0 +1,139 @@
// Copyright 2022 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/core.h"
#include "core/frontend/applets/mii_edit.h"
#include "core/hle/service/am/am.h"
#include "core/hle/service/am/applets/applet_mii_edit.h"
#include "core/hle/service/mii/mii_manager.h"
namespace Service::AM::Applets {
MiiEdit::MiiEdit(Core::System& system_, LibraryAppletMode applet_mode_,
const Core::Frontend::MiiEditApplet& frontend_)
: Applet{system_, applet_mode_}, frontend{frontend_}, system{system_} {}
MiiEdit::~MiiEdit() = default;
void MiiEdit::Initialize() {
// Note: MiiEdit is not initialized with common arguments.
// Instead, it is initialized by an AppletInput storage with size 0x100 bytes.
// Do NOT call Applet::Initialize() here.
const auto storage = broker.PopNormalDataToApplet();
ASSERT(storage != nullptr);
const auto applet_input_data = storage->GetData();
ASSERT(applet_input_data.size() >= sizeof(MiiEditAppletInputCommon));
std::memcpy(&applet_input_common, applet_input_data.data(), sizeof(MiiEditAppletInputCommon));
LOG_INFO(Service_AM,
"Initializing MiiEdit Applet with MiiEditAppletVersion={} and MiiEditAppletMode={}",
applet_input_common.version, applet_input_common.applet_mode);
switch (applet_input_common.version) {
case MiiEditAppletVersion::Version3:
ASSERT(applet_input_data.size() ==
sizeof(MiiEditAppletInputCommon) + sizeof(MiiEditAppletInputV3));
std::memcpy(&applet_input_v3, applet_input_data.data() + sizeof(MiiEditAppletInputCommon),
sizeof(MiiEditAppletInputV3));
break;
case MiiEditAppletVersion::Version4:
ASSERT(applet_input_data.size() ==
sizeof(MiiEditAppletInputCommon) + sizeof(MiiEditAppletInputV4));
std::memcpy(&applet_input_v4, applet_input_data.data() + sizeof(MiiEditAppletInputCommon),
sizeof(MiiEditAppletInputV4));
break;
default:
UNIMPLEMENTED_MSG("Unknown MiiEditAppletVersion={} with size={}",
applet_input_common.version, applet_input_data.size());
ASSERT(applet_input_data.size() >=
sizeof(MiiEditAppletInputCommon) + sizeof(MiiEditAppletInputV4));
std::memcpy(&applet_input_v4, applet_input_data.data() + sizeof(MiiEditAppletInputCommon),
sizeof(MiiEditAppletInputV4));
break;
}
}
bool MiiEdit::TransactionComplete() const {
return is_complete;
}
ResultCode MiiEdit::GetStatus() const {
return ResultSuccess;
}
void MiiEdit::ExecuteInteractive() {
UNREACHABLE_MSG("Attempted to call interactive execution on non-interactive applet.");
}
void MiiEdit::Execute() {
if (is_complete) {
return;
}
// This is a default stub for each of the MiiEdit applet modes.
switch (applet_input_common.applet_mode) {
case MiiEditAppletMode::ShowMiiEdit:
case MiiEditAppletMode::AppendMii:
case MiiEditAppletMode::AppendMiiImage:
case MiiEditAppletMode::UpdateMiiImage:
MiiEditOutput(MiiEditResult::Success, 0);
break;
case MiiEditAppletMode::CreateMii:
case MiiEditAppletMode::EditMii: {
Service::Mii::MiiManager mii_manager;
const MiiEditCharInfo char_info{
.mii_info{applet_input_common.applet_mode == MiiEditAppletMode::EditMii
? applet_input_v4.char_info.mii_info
: mii_manager.BuildDefault(0)},
};
MiiEditOutputForCharInfoEditing(MiiEditResult::Success, char_info);
break;
}
default:
UNIMPLEMENTED_MSG("Unknown MiiEditAppletMode={}", applet_input_common.applet_mode);
MiiEditOutput(MiiEditResult::Success, 0);
break;
}
}
void MiiEdit::MiiEditOutput(MiiEditResult result, s32 index) {
const MiiEditAppletOutput applet_output{
.result{result},
.index{index},
};
std::vector<u8> out_data(sizeof(MiiEditAppletOutput));
std::memcpy(out_data.data(), &applet_output, sizeof(MiiEditAppletOutput));
is_complete = true;
broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::move(out_data)));
broker.SignalStateChanged();
}
void MiiEdit::MiiEditOutputForCharInfoEditing(MiiEditResult result,
const MiiEditCharInfo& char_info) {
const MiiEditAppletOutputForCharInfoEditing applet_output{
.result{result},
.char_info{char_info},
};
std::vector<u8> out_data(sizeof(MiiEditAppletOutputForCharInfoEditing));
std::memcpy(out_data.data(), &applet_output, sizeof(MiiEditAppletOutputForCharInfoEditing));
is_complete = true;
broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::move(out_data)));
broker.SignalStateChanged();
}
} // namespace Service::AM::Applets

View File

@@ -0,0 +1,45 @@
// Copyright 2022 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/result.h"
#include "core/hle/service/am/applets/applet_mii_edit_types.h"
#include "core/hle/service/am/applets/applets.h"
namespace Core {
class System;
} // namespace Core
namespace Service::AM::Applets {
class MiiEdit final : public Applet {
public:
explicit MiiEdit(Core::System& system_, LibraryAppletMode applet_mode_,
const Core::Frontend::MiiEditApplet& frontend_);
~MiiEdit() override;
void Initialize() override;
bool TransactionComplete() const override;
ResultCode GetStatus() const override;
void ExecuteInteractive() override;
void Execute() override;
void MiiEditOutput(MiiEditResult result, s32 index);
void MiiEditOutputForCharInfoEditing(MiiEditResult result, const MiiEditCharInfo& char_info);
private:
const Core::Frontend::MiiEditApplet& frontend;
Core::System& system;
MiiEditAppletInputCommon applet_input_common{};
MiiEditAppletInputV3 applet_input_v3{};
MiiEditAppletInputV4 applet_input_v4{};
bool is_complete{false};
};
} // namespace Service::AM::Applets

View File

@@ -0,0 +1,83 @@
// Copyright 2022 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/hle/service/mii/types.h"
namespace Service::AM::Applets {
enum class MiiEditAppletVersion : s32 {
Version3 = 0x3, // 1.0.0 - 10.1.1
Version4 = 0x4, // 10.2.0+
};
// This is nn::mii::AppletMode
enum class MiiEditAppletMode : u32 {
ShowMiiEdit = 0,
AppendMii = 1,
AppendMiiImage = 2,
UpdateMiiImage = 3,
CreateMii = 4,
EditMii = 5,
};
enum class MiiEditResult : u32 {
Success,
Cancel,
};
struct MiiEditCharInfo {
Service::Mii::MiiInfo mii_info{};
};
static_assert(sizeof(MiiEditCharInfo) == 0x58, "MiiEditCharInfo has incorrect size.");
struct MiiEditAppletInputCommon {
MiiEditAppletVersion version{};
MiiEditAppletMode applet_mode{};
};
static_assert(sizeof(MiiEditAppletInputCommon) == 0x8,
"MiiEditAppletInputCommon has incorrect size.");
struct MiiEditAppletInputV3 {
u32 special_mii_key_code{};
std::array<Common::UUID, 8> valid_uuids{};
Common::UUID used_uuid{};
INSERT_PADDING_BYTES(0x64);
};
static_assert(sizeof(MiiEditAppletInputV3) == 0x100 - sizeof(MiiEditAppletInputCommon),
"MiiEditAppletInputV3 has incorrect size.");
struct MiiEditAppletInputV4 {
u32 special_mii_key_code{};
MiiEditCharInfo char_info{};
INSERT_PADDING_BYTES(0x28);
Common::UUID used_uuid{};
INSERT_PADDING_BYTES(0x64);
};
static_assert(sizeof(MiiEditAppletInputV4) == 0x100 - sizeof(MiiEditAppletInputCommon),
"MiiEditAppletInputV4 has incorrect size.");
// This is nn::mii::AppletOutput
struct MiiEditAppletOutput {
MiiEditResult result{};
s32 index{};
INSERT_PADDING_BYTES(0x18);
};
static_assert(sizeof(MiiEditAppletOutput) == 0x20, "MiiEditAppletOutput has incorrect size.");
// This is nn::mii::AppletOutputForCharInfoEditing
struct MiiEditAppletOutputForCharInfoEditing {
MiiEditResult result{};
MiiEditCharInfo char_info{};
INSERT_PADDING_BYTES(0x24);
};
static_assert(sizeof(MiiEditAppletOutputForCharInfoEditing) == 0x80,
"MiiEditAppletOutputForCharInfoEditing has incorrect size.");
} // namespace Service::AM::Applets

View File

@@ -226,7 +226,7 @@ void SoftwareKeyboard::InitializeForeground() {
ASSERT(work_buffer_storage != nullptr);
if (swkbd_config_common.initial_string_length == 0) {
InitializeFrontendKeyboard();
InitializeFrontendNormalKeyboard();
return;
}
@@ -243,7 +243,7 @@ void SoftwareKeyboard::InitializeForeground() {
LOG_DEBUG(Service_AM, "\nInitial Text: {}", Common::UTF16ToUTF8(initial_text));
InitializeFrontendKeyboard();
InitializeFrontendNormalKeyboard();
}
void SoftwareKeyboard::InitializeBackground(LibraryAppletMode library_applet_mode) {
@@ -480,129 +480,173 @@ void SoftwareKeyboard::ChangeState(SwkbdState state) {
ReplyDefault();
}
void SoftwareKeyboard::InitializeFrontendKeyboard() {
if (is_background) {
const auto& appear_arg = swkbd_calc_arg.appear_arg;
void SoftwareKeyboard::InitializeFrontendNormalKeyboard() {
std::u16string ok_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
swkbd_config_common.ok_text.data(), swkbd_config_common.ok_text.size());
std::u16string ok_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
appear_arg.ok_text.data(), appear_arg.ok_text.size());
std::u16string header_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
swkbd_config_common.header_text.data(), swkbd_config_common.header_text.size());
const u32 max_text_length =
appear_arg.max_text_length > 0 && appear_arg.max_text_length <= DEFAULT_MAX_TEXT_LENGTH
? appear_arg.max_text_length
: DEFAULT_MAX_TEXT_LENGTH;
std::u16string sub_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
swkbd_config_common.sub_text.data(), swkbd_config_common.sub_text.size());
const u32 min_text_length =
appear_arg.min_text_length <= max_text_length ? appear_arg.min_text_length : 0;
std::u16string guide_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
swkbd_config_common.guide_text.data(), swkbd_config_common.guide_text.size());
const s32 initial_cursor_position =
current_cursor_position > 0 ? current_cursor_position : 0;
const u32 max_text_length =
swkbd_config_common.max_text_length > 0 &&
swkbd_config_common.max_text_length <= DEFAULT_MAX_TEXT_LENGTH
? swkbd_config_common.max_text_length
: DEFAULT_MAX_TEXT_LENGTH;
const auto text_draw_type =
max_text_length <= 32 ? SwkbdTextDrawType::Line : SwkbdTextDrawType::Box;
const u32 min_text_length = swkbd_config_common.min_text_length <= max_text_length
? swkbd_config_common.min_text_length
: 0;
Core::Frontend::KeyboardInitializeParameters initialize_parameters{
.ok_text{std::move(ok_text)},
.header_text{},
.sub_text{},
.guide_text{},
.initial_text{current_text},
.max_text_length{max_text_length},
.min_text_length{min_text_length},
.initial_cursor_position{initial_cursor_position},
.type{appear_arg.type},
.password_mode{SwkbdPasswordMode::Disabled},
.text_draw_type{text_draw_type},
.key_disable_flags{appear_arg.key_disable_flags},
.use_blur_background{false},
.enable_backspace_button{swkbd_calc_arg.enable_backspace_button},
.enable_return_button{appear_arg.enable_return_button},
.disable_cancel_button{appear_arg.disable_cancel_button},
};
const s32 initial_cursor_position = [this] {
switch (swkbd_config_common.initial_cursor_position) {
case SwkbdInitialCursorPosition::Start:
default:
return 0;
case SwkbdInitialCursorPosition::End:
return static_cast<s32>(initial_text.size());
}
}();
frontend.InitializeKeyboard(
true, std::move(initialize_parameters), {},
[this](SwkbdReplyType reply_type, std::u16string submitted_text, s32 cursor_position) {
SubmitTextInline(reply_type, submitted_text, cursor_position);
});
} else {
std::u16string ok_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
swkbd_config_common.ok_text.data(), swkbd_config_common.ok_text.size());
const auto text_draw_type = [this, max_text_length] {
switch (swkbd_config_common.text_draw_type) {
case SwkbdTextDrawType::Line:
default:
return max_text_length <= 32 ? SwkbdTextDrawType::Line : SwkbdTextDrawType::Box;
case SwkbdTextDrawType::Box:
case SwkbdTextDrawType::DownloadCode:
return swkbd_config_common.text_draw_type;
}
}();
std::u16string header_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
swkbd_config_common.header_text.data(), swkbd_config_common.header_text.size());
const auto enable_return_button =
text_draw_type == SwkbdTextDrawType::Box ? swkbd_config_common.enable_return_button : false;
std::u16string sub_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
swkbd_config_common.sub_text.data(), swkbd_config_common.sub_text.size());
const auto disable_cancel_button = swkbd_applet_version >= SwkbdAppletVersion::Version393227
? swkbd_config_new.disable_cancel_button
: false;
std::u16string guide_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
swkbd_config_common.guide_text.data(), swkbd_config_common.guide_text.size());
Core::Frontend::KeyboardInitializeParameters initialize_parameters{
.ok_text{std::move(ok_text)},
.header_text{std::move(header_text)},
.sub_text{std::move(sub_text)},
.guide_text{std::move(guide_text)},
.initial_text{initial_text},
.max_text_length{max_text_length},
.min_text_length{min_text_length},
.initial_cursor_position{initial_cursor_position},
.type{swkbd_config_common.type},
.password_mode{swkbd_config_common.password_mode},
.text_draw_type{text_draw_type},
.key_disable_flags{swkbd_config_common.key_disable_flags},
.use_blur_background{swkbd_config_common.use_blur_background},
.enable_backspace_button{true},
.enable_return_button{enable_return_button},
.disable_cancel_button{disable_cancel_button},
};
const u32 max_text_length =
swkbd_config_common.max_text_length > 0 &&
swkbd_config_common.max_text_length <= DEFAULT_MAX_TEXT_LENGTH
? swkbd_config_common.max_text_length
: DEFAULT_MAX_TEXT_LENGTH;
frontend.InitializeKeyboard(
false, std::move(initialize_parameters),
[this](SwkbdResult result, std::u16string submitted_text, bool confirmed) {
SubmitTextNormal(result, submitted_text, confirmed);
},
{});
}
const u32 min_text_length = swkbd_config_common.min_text_length <= max_text_length
? swkbd_config_common.min_text_length
: 0;
void SoftwareKeyboard::InitializeFrontendInlineKeyboard(
Core::Frontend::KeyboardInitializeParameters initialize_parameters) {
frontend.InitializeKeyboard(
true, std::move(initialize_parameters), {},
[this](SwkbdReplyType reply_type, std::u16string submitted_text, s32 cursor_position) {
SubmitTextInline(reply_type, submitted_text, cursor_position);
});
}
const s32 initial_cursor_position = [this] {
switch (swkbd_config_common.initial_cursor_position) {
case SwkbdInitialCursorPosition::Start:
default:
return 0;
case SwkbdInitialCursorPosition::End:
return static_cast<s32>(initial_text.size());
}
}();
void SoftwareKeyboard::InitializeFrontendInlineKeyboardOld() {
const auto& appear_arg = swkbd_calc_arg_old.appear_arg;
const auto text_draw_type = [this, max_text_length] {
switch (swkbd_config_common.text_draw_type) {
case SwkbdTextDrawType::Line:
default:
return max_text_length <= 32 ? SwkbdTextDrawType::Line : SwkbdTextDrawType::Box;
case SwkbdTextDrawType::Box:
case SwkbdTextDrawType::DownloadCode:
return swkbd_config_common.text_draw_type;
}
}();
std::u16string ok_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
appear_arg.ok_text.data(), appear_arg.ok_text.size());
const auto enable_return_button = text_draw_type == SwkbdTextDrawType::Box
? swkbd_config_common.enable_return_button
: false;
const u32 max_text_length =
appear_arg.max_text_length > 0 && appear_arg.max_text_length <= DEFAULT_MAX_TEXT_LENGTH
? appear_arg.max_text_length
: DEFAULT_MAX_TEXT_LENGTH;
const auto disable_cancel_button = swkbd_applet_version >= SwkbdAppletVersion::Version393227
? swkbd_config_new.disable_cancel_button
: false;
const u32 min_text_length =
appear_arg.min_text_length <= max_text_length ? appear_arg.min_text_length : 0;
Core::Frontend::KeyboardInitializeParameters initialize_parameters{
.ok_text{std::move(ok_text)},
.header_text{std::move(header_text)},
.sub_text{std::move(sub_text)},
.guide_text{std::move(guide_text)},
.initial_text{initial_text},
.max_text_length{max_text_length},
.min_text_length{min_text_length},
.initial_cursor_position{initial_cursor_position},
.type{swkbd_config_common.type},
.password_mode{swkbd_config_common.password_mode},
.text_draw_type{text_draw_type},
.key_disable_flags{swkbd_config_common.key_disable_flags},
.use_blur_background{swkbd_config_common.use_blur_background},
.enable_backspace_button{true},
.enable_return_button{enable_return_button},
.disable_cancel_button{disable_cancel_button},
};
const s32 initial_cursor_position = current_cursor_position > 0 ? current_cursor_position : 0;
frontend.InitializeKeyboard(
false, std::move(initialize_parameters),
[this](SwkbdResult result, std::u16string submitted_text, bool confirmed) {
SubmitTextNormal(result, submitted_text, confirmed);
},
{});
}
const auto text_draw_type =
max_text_length <= 32 ? SwkbdTextDrawType::Line : SwkbdTextDrawType::Box;
Core::Frontend::KeyboardInitializeParameters initialize_parameters{
.ok_text{std::move(ok_text)},
.header_text{},
.sub_text{},
.guide_text{},
.initial_text{current_text},
.max_text_length{max_text_length},
.min_text_length{min_text_length},
.initial_cursor_position{initial_cursor_position},
.type{appear_arg.type},
.password_mode{SwkbdPasswordMode::Disabled},
.text_draw_type{text_draw_type},
.key_disable_flags{appear_arg.key_disable_flags},
.use_blur_background{false},
.enable_backspace_button{swkbd_calc_arg_old.enable_backspace_button},
.enable_return_button{appear_arg.enable_return_button},
.disable_cancel_button{appear_arg.disable_cancel_button},
};
InitializeFrontendInlineKeyboard(std::move(initialize_parameters));
}
void SoftwareKeyboard::InitializeFrontendInlineKeyboardNew() {
const auto& appear_arg = swkbd_calc_arg_new.appear_arg;
std::u16string ok_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
appear_arg.ok_text.data(), appear_arg.ok_text.size());
const u32 max_text_length =
appear_arg.max_text_length > 0 && appear_arg.max_text_length <= DEFAULT_MAX_TEXT_LENGTH
? appear_arg.max_text_length
: DEFAULT_MAX_TEXT_LENGTH;
const u32 min_text_length =
appear_arg.min_text_length <= max_text_length ? appear_arg.min_text_length : 0;
const s32 initial_cursor_position = current_cursor_position > 0 ? current_cursor_position : 0;
const auto text_draw_type =
max_text_length <= 32 ? SwkbdTextDrawType::Line : SwkbdTextDrawType::Box;
Core::Frontend::KeyboardInitializeParameters initialize_parameters{
.ok_text{std::move(ok_text)},
.header_text{},
.sub_text{},
.guide_text{},
.initial_text{current_text},
.max_text_length{max_text_length},
.min_text_length{min_text_length},
.initial_cursor_position{initial_cursor_position},
.type{appear_arg.type},
.password_mode{SwkbdPasswordMode::Disabled},
.text_draw_type{text_draw_type},
.key_disable_flags{appear_arg.key_disable_flags},
.use_blur_background{false},
.enable_backspace_button{swkbd_calc_arg_new.enable_backspace_button},
.enable_return_button{appear_arg.enable_return_button},
.disable_cancel_button{appear_arg.disable_cancel_button},
};
InitializeFrontendInlineKeyboard(std::move(initialize_parameters));
}
void SoftwareKeyboard::ShowNormalKeyboard() {
@@ -614,14 +658,21 @@ void SoftwareKeyboard::ShowTextCheckDialog(SwkbdTextCheckResult text_check_resul
frontend.ShowTextCheckDialog(text_check_result, std::move(text_check_message));
}
void SoftwareKeyboard::ShowInlineKeyboard() {
void SoftwareKeyboard::ShowInlineKeyboard(
Core::Frontend::InlineAppearParameters appear_parameters) {
frontend.ShowInlineKeyboard(std::move(appear_parameters));
ChangeState(SwkbdState::InitializedIsShown);
}
void SoftwareKeyboard::ShowInlineKeyboardOld() {
if (swkbd_state != SwkbdState::InitializedIsHidden) {
return;
}
ChangeState(SwkbdState::InitializedIsAppearing);
const auto& appear_arg = swkbd_calc_arg.appear_arg;
const auto& appear_arg = swkbd_calc_arg_old.appear_arg;
const u32 max_text_length =
appear_arg.max_text_length > 0 && appear_arg.max_text_length <= DEFAULT_MAX_TEXT_LENGTH
@@ -634,21 +685,54 @@ void SoftwareKeyboard::ShowInlineKeyboard() {
Core::Frontend::InlineAppearParameters appear_parameters{
.max_text_length{max_text_length},
.min_text_length{min_text_length},
.key_top_scale_x{swkbd_calc_arg.key_top_scale_x},
.key_top_scale_y{swkbd_calc_arg.key_top_scale_y},
.key_top_translate_x{swkbd_calc_arg.key_top_translate_x},
.key_top_translate_y{swkbd_calc_arg.key_top_translate_y},
.key_top_scale_x{swkbd_calc_arg_old.key_top_scale_x},
.key_top_scale_y{swkbd_calc_arg_old.key_top_scale_y},
.key_top_translate_x{swkbd_calc_arg_old.key_top_translate_x},
.key_top_translate_y{swkbd_calc_arg_old.key_top_translate_y},
.type{appear_arg.type},
.key_disable_flags{appear_arg.key_disable_flags},
.key_top_as_floating{swkbd_calc_arg.key_top_as_floating},
.enable_backspace_button{swkbd_calc_arg.enable_backspace_button},
.key_top_as_floating{swkbd_calc_arg_old.key_top_as_floating},
.enable_backspace_button{swkbd_calc_arg_old.enable_backspace_button},
.enable_return_button{appear_arg.enable_return_button},
.disable_cancel_button{appear_arg.disable_cancel_button},
};
frontend.ShowInlineKeyboard(std::move(appear_parameters));
ShowInlineKeyboard(std::move(appear_parameters));
}
ChangeState(SwkbdState::InitializedIsShown);
void SoftwareKeyboard::ShowInlineKeyboardNew() {
if (swkbd_state != SwkbdState::InitializedIsHidden) {
return;
}
ChangeState(SwkbdState::InitializedIsAppearing);
const auto& appear_arg = swkbd_calc_arg_new.appear_arg;
const u32 max_text_length =
appear_arg.max_text_length > 0 && appear_arg.max_text_length <= DEFAULT_MAX_TEXT_LENGTH
? appear_arg.max_text_length
: DEFAULT_MAX_TEXT_LENGTH;
const u32 min_text_length =
appear_arg.min_text_length <= max_text_length ? appear_arg.min_text_length : 0;
Core::Frontend::InlineAppearParameters appear_parameters{
.max_text_length{max_text_length},
.min_text_length{min_text_length},
.key_top_scale_x{swkbd_calc_arg_new.key_top_scale_x},
.key_top_scale_y{swkbd_calc_arg_new.key_top_scale_y},
.key_top_translate_x{swkbd_calc_arg_new.key_top_translate_x},
.key_top_translate_y{swkbd_calc_arg_new.key_top_translate_y},
.type{appear_arg.type},
.key_disable_flags{appear_arg.key_disable_flags},
.key_top_as_floating{swkbd_calc_arg_new.key_top_as_floating},
.enable_backspace_button{swkbd_calc_arg_new.enable_backspace_button},
.enable_return_button{appear_arg.enable_return_button},
.disable_cancel_button{appear_arg.disable_cancel_button},
};
ShowInlineKeyboard(std::move(appear_parameters));
}
void SoftwareKeyboard::HideInlineKeyboard() {
@@ -693,6 +777,8 @@ void SoftwareKeyboard::RequestFinalize(const std::vector<u8>& request_data) {
void SoftwareKeyboard::RequestSetUserWordInfo(const std::vector<u8>& request_data) {
LOG_WARNING(Service_AM, "SetUserWordInfo is not implemented.");
ReplyReleasedUserWordInfo();
}
void SoftwareKeyboard::RequestSetCustomizeDic(const std::vector<u8>& request_data) {
@@ -702,53 +788,135 @@ void SoftwareKeyboard::RequestSetCustomizeDic(const std::vector<u8>& request_dat
void SoftwareKeyboard::RequestCalc(const std::vector<u8>& request_data) {
LOG_DEBUG(Service_AM, "Processing Request: Calc");
ASSERT(request_data.size() == sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArg));
ASSERT(request_data.size() >= sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon));
std::memcpy(&swkbd_calc_arg, request_data.data() + sizeof(SwkbdRequestCommand),
sizeof(SwkbdCalcArg));
std::memcpy(&swkbd_calc_arg_common, request_data.data() + sizeof(SwkbdRequestCommand),
sizeof(SwkbdCalcArgCommon));
if (swkbd_calc_arg.flags.set_input_text) {
switch (swkbd_calc_arg_common.calc_arg_size) {
case sizeof(SwkbdCalcArgCommon) + sizeof(SwkbdCalcArgOld):
ASSERT(request_data.size() ==
sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon) + sizeof(SwkbdCalcArgOld));
std::memcpy(&swkbd_calc_arg_old,
request_data.data() + sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon),
sizeof(SwkbdCalcArgOld));
RequestCalcOld();
break;
case sizeof(SwkbdCalcArgCommon) + sizeof(SwkbdCalcArgNew):
ASSERT(request_data.size() ==
sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon) + sizeof(SwkbdCalcArgNew));
std::memcpy(&swkbd_calc_arg_new,
request_data.data() + sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon),
sizeof(SwkbdCalcArgNew));
RequestCalcNew();
break;
default:
UNIMPLEMENTED_MSG("Unknown SwkbdCalcArg size={}", swkbd_calc_arg_common.calc_arg_size);
ASSERT(request_data.size() >=
sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon) + sizeof(SwkbdCalcArgNew));
std::memcpy(&swkbd_calc_arg_new,
request_data.data() + sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon),
sizeof(SwkbdCalcArgNew));
RequestCalcNew();
break;
}
}
void SoftwareKeyboard::RequestCalcOld() {
if (swkbd_calc_arg_common.flags.set_input_text) {
current_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
swkbd_calc_arg.input_text.data(), swkbd_calc_arg.input_text.size());
swkbd_calc_arg_old.input_text.data(), swkbd_calc_arg_old.input_text.size());
}
if (swkbd_calc_arg.flags.set_cursor_position) {
current_cursor_position = swkbd_calc_arg.cursor_position;
if (swkbd_calc_arg_common.flags.set_cursor_position) {
current_cursor_position = swkbd_calc_arg_old.cursor_position;
}
if (swkbd_calc_arg.flags.set_utf8_mode) {
inline_use_utf8 = swkbd_calc_arg.utf8_mode;
if (swkbd_calc_arg_common.flags.set_utf8_mode) {
inline_use_utf8 = swkbd_calc_arg_old.utf8_mode;
}
if (swkbd_state <= SwkbdState::InitializedIsHidden &&
swkbd_calc_arg.flags.unset_customize_dic) {
swkbd_calc_arg_common.flags.unset_customize_dic) {
ReplyUnsetCustomizeDic();
}
if (swkbd_state <= SwkbdState::InitializedIsHidden &&
swkbd_calc_arg.flags.unset_user_word_info) {
swkbd_calc_arg_common.flags.unset_user_word_info) {
ReplyReleasedUserWordInfo();
}
if (swkbd_state == SwkbdState::NotInitialized && swkbd_calc_arg.flags.set_initialize_arg) {
InitializeFrontendKeyboard();
if (swkbd_state == SwkbdState::NotInitialized &&
swkbd_calc_arg_common.flags.set_initialize_arg) {
InitializeFrontendInlineKeyboardOld();
ChangeState(SwkbdState::InitializedIsHidden);
ReplyFinishedInitialize();
}
if (!swkbd_calc_arg.flags.set_initialize_arg &&
(swkbd_calc_arg.flags.set_input_text || swkbd_calc_arg.flags.set_cursor_position)) {
if (!swkbd_calc_arg_common.flags.set_initialize_arg &&
(swkbd_calc_arg_common.flags.set_input_text ||
swkbd_calc_arg_common.flags.set_cursor_position)) {
InlineTextChanged();
}
if (swkbd_state == SwkbdState::InitializedIsHidden && swkbd_calc_arg.flags.appear) {
ShowInlineKeyboard();
if (swkbd_state == SwkbdState::InitializedIsHidden && swkbd_calc_arg_common.flags.appear) {
ShowInlineKeyboardOld();
return;
}
if (swkbd_state == SwkbdState::InitializedIsShown && swkbd_calc_arg.flags.disappear) {
if (swkbd_state == SwkbdState::InitializedIsShown && swkbd_calc_arg_common.flags.disappear) {
HideInlineKeyboard();
return;
}
}
void SoftwareKeyboard::RequestCalcNew() {
if (swkbd_calc_arg_common.flags.set_input_text) {
current_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
swkbd_calc_arg_new.input_text.data(), swkbd_calc_arg_new.input_text.size());
}
if (swkbd_calc_arg_common.flags.set_cursor_position) {
current_cursor_position = swkbd_calc_arg_new.cursor_position;
}
if (swkbd_calc_arg_common.flags.set_utf8_mode) {
inline_use_utf8 = swkbd_calc_arg_new.utf8_mode;
}
if (swkbd_state <= SwkbdState::InitializedIsHidden &&
swkbd_calc_arg_common.flags.unset_customize_dic) {
ReplyUnsetCustomizeDic();
}
if (swkbd_state <= SwkbdState::InitializedIsHidden &&
swkbd_calc_arg_common.flags.unset_user_word_info) {
ReplyReleasedUserWordInfo();
}
if (swkbd_state == SwkbdState::NotInitialized &&
swkbd_calc_arg_common.flags.set_initialize_arg) {
InitializeFrontendInlineKeyboardNew();
ChangeState(SwkbdState::InitializedIsHidden);
ReplyFinishedInitialize();
}
if (!swkbd_calc_arg_common.flags.set_initialize_arg &&
(swkbd_calc_arg_common.flags.set_input_text ||
swkbd_calc_arg_common.flags.set_cursor_position)) {
InlineTextChanged();
}
if (swkbd_state == SwkbdState::InitializedIsHidden && swkbd_calc_arg_common.flags.appear) {
ShowInlineKeyboardNew();
return;
}
if (swkbd_state == SwkbdState::InitializedIsShown && swkbd_calc_arg_common.flags.disappear) {
HideInlineKeyboard();
return;
}

View File

@@ -13,6 +13,11 @@ namespace Core {
class System;
}
namespace Core::Frontend {
struct KeyboardInitializeParameters;
struct InlineAppearParameters;
} // namespace Core::Frontend
namespace Service::AM::Applets {
class SoftwareKeyboard final : public Applet {
@@ -78,13 +83,22 @@ private:
void ChangeState(SwkbdState state);
/**
* Signals the frontend to initialize the software keyboard with common parameters.
* This initializes either the normal software keyboard or the inline software keyboard
* depending on the state of is_background.
* Signals the frontend to initialize the normal software keyboard with common parameters.
* Note that this does not cause the keyboard to appear.
* Use the respective Show*Keyboard() functions to cause the respective keyboards to appear.
* Use the ShowNormalKeyboard() functions to cause the keyboard to appear.
*/
void InitializeFrontendKeyboard();
void InitializeFrontendNormalKeyboard();
/**
* Signals the frontend to initialize the inline software keyboard with common parameters.
* Note that this does not cause the keyboard to appear.
* Use the ShowInlineKeyboard() to cause the keyboard to appear.
*/
void InitializeFrontendInlineKeyboard(
Core::Frontend::KeyboardInitializeParameters initialize_parameters);
void InitializeFrontendInlineKeyboardOld();
void InitializeFrontendInlineKeyboardNew();
/// Signals the frontend to show the normal software keyboard.
void ShowNormalKeyboard();
@@ -94,7 +108,10 @@ private:
std::u16string text_check_message);
/// Signals the frontend to show the inline software keyboard.
void ShowInlineKeyboard();
void ShowInlineKeyboard(Core::Frontend::InlineAppearParameters appear_parameters);
void ShowInlineKeyboardOld();
void ShowInlineKeyboardNew();
/// Signals the frontend to hide the inline software keyboard.
void HideInlineKeyboard();
@@ -111,6 +128,8 @@ private:
void RequestSetUserWordInfo(const std::vector<u8>& request_data);
void RequestSetCustomizeDic(const std::vector<u8>& request_data);
void RequestCalc(const std::vector<u8>& request_data);
void RequestCalcOld();
void RequestCalcNew();
void RequestSetCustomizedDictionaries(const std::vector<u8>& request_data);
void RequestUnsetCustomizedDictionaries(const std::vector<u8>& request_data);
void RequestSetChangedStringV2Flag(const std::vector<u8>& request_data);
@@ -149,7 +168,9 @@ private:
SwkbdState swkbd_state{SwkbdState::NotInitialized};
SwkbdInitializeArg swkbd_initialize_arg;
SwkbdCalcArg swkbd_calc_arg;
SwkbdCalcArgCommon swkbd_calc_arg_common;
SwkbdCalcArgOld swkbd_calc_arg_old;
SwkbdCalcArgNew swkbd_calc_arg_new;
bool use_changed_string_v2{false};
bool use_moved_cursor_v2{false};
bool inline_use_utf8{false};

View File

@@ -10,6 +10,7 @@
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
#include "common/uuid.h"
namespace Service::AM::Applets {
@@ -216,7 +217,7 @@ struct SwkbdInitializeArg {
};
static_assert(sizeof(SwkbdInitializeArg) == 0x8, "SwkbdInitializeArg has incorrect size.");
struct SwkbdAppearArg {
struct SwkbdAppearArgOld {
SwkbdType type{};
std::array<char16_t, MAX_OK_TEXT_LENGTH + 1> ok_text{};
char16_t left_optional_symbol_key{};
@@ -229,19 +230,46 @@ struct SwkbdAppearArg {
bool enable_return_button{};
INSERT_PADDING_BYTES(3);
u32 flags{};
INSERT_PADDING_WORDS(6);
bool is_use_save_data{};
INSERT_PADDING_BYTES(7);
Common::UUID user_id{};
};
static_assert(sizeof(SwkbdAppearArg) == 0x48, "SwkbdAppearArg has incorrect size.");
static_assert(sizeof(SwkbdAppearArgOld) == 0x48, "SwkbdAppearArg has incorrect size.");
struct SwkbdCalcArg {
struct SwkbdAppearArgNew {
SwkbdType type{};
std::array<char16_t, MAX_OK_TEXT_LENGTH + 1> ok_text{};
char16_t left_optional_symbol_key{};
char16_t right_optional_symbol_key{};
bool use_prediction{};
bool disable_cancel_button{};
SwkbdKeyDisableFlags key_disable_flags{};
u32 max_text_length{};
u32 min_text_length{};
bool enable_return_button{};
INSERT_PADDING_BYTES(3);
u32 flags{};
bool is_use_save_data{};
INSERT_PADDING_BYTES(7);
Common::UUID user_id{};
u64 start_sampling_number{};
INSERT_PADDING_WORDS(8);
};
static_assert(sizeof(SwkbdAppearArgNew) == 0x70, "SwkbdAppearArg has incorrect size.");
struct SwkbdCalcArgCommon {
u32 unknown{};
u16 calc_arg_size{};
INSERT_PADDING_BYTES(2);
SwkbdCalcArgFlags flags{};
SwkbdInitializeArg initialize_arg{};
};
static_assert(sizeof(SwkbdCalcArgCommon) == 0x18, "SwkbdCalcArgCommon has incorrect size.");
struct SwkbdCalcArgOld {
f32 volume{};
s32 cursor_position{};
SwkbdAppearArg appear_arg{};
SwkbdAppearArgOld appear_arg{};
std::array<char16_t, 0x1FA> input_text{};
bool utf8_mode{};
INSERT_PADDING_BYTES(1);
@@ -265,7 +293,39 @@ struct SwkbdCalcArg {
u8 se_group{};
INSERT_PADDING_BYTES(3);
};
static_assert(sizeof(SwkbdCalcArg) == 0x4A0, "SwkbdCalcArg has incorrect size.");
static_assert(sizeof(SwkbdCalcArgOld) == 0x4A0 - sizeof(SwkbdCalcArgCommon),
"SwkbdCalcArgOld has incorrect size.");
struct SwkbdCalcArgNew {
SwkbdAppearArgNew appear_arg{};
f32 volume{};
s32 cursor_position{};
std::array<char16_t, 0x1FA> input_text{};
bool utf8_mode{};
INSERT_PADDING_BYTES(1);
bool enable_backspace_button{};
INSERT_PADDING_BYTES(3);
bool key_top_as_floating{};
bool footer_scalable{};
bool alpha_enabled_in_input_mode{};
u8 input_mode_fade_type{};
bool disable_touch{};
bool disable_hardware_keyboard{};
INSERT_PADDING_BYTES(8);
f32 key_top_scale_x{};
f32 key_top_scale_y{};
f32 key_top_translate_x{};
f32 key_top_translate_y{};
f32 key_top_bg_alpha{};
f32 footer_bg_alpha{};
f32 balloon_scale{};
INSERT_PADDING_WORDS(4);
u8 se_group{};
INSERT_PADDING_BYTES(3);
INSERT_PADDING_WORDS(8);
};
static_assert(sizeof(SwkbdCalcArgNew) == 0x4E8 - sizeof(SwkbdCalcArgCommon),
"SwkbdCalcArgNew has incorrect size.");
struct SwkbdChangedStringArg {
u32 text_length{};

View File

@@ -9,6 +9,7 @@
#include "core/frontend/applets/controller.h"
#include "core/frontend/applets/error.h"
#include "core/frontend/applets/general_frontend.h"
#include "core/frontend/applets/mii_edit.h"
#include "core/frontend/applets/profile_select.h"
#include "core/frontend/applets/software_keyboard.h"
#include "core/frontend/applets/web_browser.h"
@@ -19,6 +20,7 @@
#include "core/hle/service/am/applets/applet_controller.h"
#include "core/hle/service/am/applets/applet_error.h"
#include "core/hle/service/am/applets/applet_general_backend.h"
#include "core/hle/service/am/applets/applet_mii_edit.h"
#include "core/hle/service/am/applets/applet_profile_select.h"
#include "core/hle/service/am/applets/applet_software_keyboard.h"
#include "core/hle/service/am/applets/applet_web_browser.h"
@@ -171,11 +173,12 @@ void Applet::Initialize() {
AppletFrontendSet::AppletFrontendSet() = default;
AppletFrontendSet::AppletFrontendSet(ControllerApplet controller_applet, ErrorApplet error_applet,
MiiEdit mii_edit_,
ParentalControlsApplet parental_controls_applet,
PhotoViewer photo_viewer_, ProfileSelect profile_select_,
SoftwareKeyboard software_keyboard_, WebBrowser web_browser_)
: controller{std::move(controller_applet)}, error{std::move(error_applet)},
parental_controls{std::move(parental_controls_applet)},
mii_edit{std::move(mii_edit_)}, parental_controls{std::move(parental_controls_applet)},
photo_viewer{std::move(photo_viewer_)}, profile_select{std::move(profile_select_)},
software_keyboard{std::move(software_keyboard_)}, web_browser{std::move(web_browser_)} {}
@@ -202,6 +205,10 @@ void AppletManager::SetAppletFrontendSet(AppletFrontendSet set) {
frontend.error = std::move(set.error);
}
if (set.mii_edit != nullptr) {
frontend.mii_edit = std::move(set.mii_edit);
}
if (set.parental_controls != nullptr) {
frontend.parental_controls = std::move(set.parental_controls);
}
@@ -238,6 +245,10 @@ void AppletManager::SetDefaultAppletsIfMissing() {
frontend.error = std::make_unique<Core::Frontend::DefaultErrorApplet>();
}
if (frontend.mii_edit == nullptr) {
frontend.mii_edit = std::make_unique<Core::Frontend::DefaultMiiEditApplet>();
}
if (frontend.parental_controls == nullptr) {
frontend.parental_controls =
std::make_unique<Core::Frontend::DefaultParentalControlsApplet>();
@@ -277,6 +288,8 @@ std::shared_ptr<Applet> AppletManager::GetApplet(AppletId id, LibraryAppletMode
return std::make_shared<ProfileSelect>(system, mode, *frontend.profile_select);
case AppletId::SoftwareKeyboard:
return std::make_shared<SoftwareKeyboard>(system, mode, *frontend.software_keyboard);
case AppletId::MiiEdit:
return std::make_shared<MiiEdit>(system, mode, *frontend.mii_edit);
case AppletId::Web:
case AppletId::Shop:
case AppletId::OfflineWeb:

View File

@@ -20,6 +20,7 @@ namespace Core::Frontend {
class ControllerApplet;
class ECommerceApplet;
class ErrorApplet;
class MiiEditApplet;
class ParentalControlsApplet;
class PhotoViewerApplet;
class ProfileSelectApplet;
@@ -178,6 +179,7 @@ protected:
struct AppletFrontendSet {
using ControllerApplet = std::unique_ptr<Core::Frontend::ControllerApplet>;
using ErrorApplet = std::unique_ptr<Core::Frontend::ErrorApplet>;
using MiiEdit = std::unique_ptr<Core::Frontend::MiiEditApplet>;
using ParentalControlsApplet = std::unique_ptr<Core::Frontend::ParentalControlsApplet>;
using PhotoViewer = std::unique_ptr<Core::Frontend::PhotoViewerApplet>;
using ProfileSelect = std::unique_ptr<Core::Frontend::ProfileSelectApplet>;
@@ -186,9 +188,9 @@ struct AppletFrontendSet {
AppletFrontendSet();
AppletFrontendSet(ControllerApplet controller_applet, ErrorApplet error_applet,
ParentalControlsApplet parental_controls_applet, PhotoViewer photo_viewer_,
ProfileSelect profile_select_, SoftwareKeyboard software_keyboard_,
WebBrowser web_browser_);
MiiEdit mii_edit_, ParentalControlsApplet parental_controls_applet,
PhotoViewer photo_viewer_, ProfileSelect profile_select_,
SoftwareKeyboard software_keyboard_, WebBrowser web_browser_);
~AppletFrontendSet();
AppletFrontendSet(const AppletFrontendSet&) = delete;
@@ -199,6 +201,7 @@ struct AppletFrontendSet {
ControllerApplet controller;
ErrorApplet error;
MiiEdit mii_edit;
ParentalControlsApplet parental_controls;
PhotoViewer photo_viewer;
ProfileSelect profile_select;

View File

@@ -2,7 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/settings.h"
#include "core/core_timing.h"
#include "core/hid/emulated_console.h"
#include "core/hid/hid_core.h"

View File

@@ -8,7 +8,6 @@
#include "common/common_types.h"
#include "common/quaternion.h"
#include "core/hid/hid_types.h"
#include "core/hle/service/hid/controllers/controller_base.h"
#include "core/hle/service/hid/ring_lifo.h"

View File

@@ -5,7 +5,6 @@
#pragma once
#include "common/common_types.h"
#include "common/swap.h"
namespace Core::Timing {
class CoreTiming;

View File

@@ -4,11 +4,8 @@
#pragma once
#include <array>
#include "common/bit_field.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
#include "core/hle/service/hid/controllers/controller_base.h"
#include "core/hle/service/hid/ring_lifo.h"

View File

@@ -4,11 +4,7 @@
#pragma once
#include <array>
#include "common/bit_field.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
#include "core/hle/service/hid/controllers/controller_base.h"
#include "core/hle/service/hid/ring_lifo.h"

View File

@@ -4,10 +4,7 @@
#pragma once
#include <array>
#include "common/bit_field.h"
#include "common/common_types.h"
#include "common/swap.h"
#include "core/hle/service/hid/controllers/controller_base.h"
#include "core/hle/service/hid/ring_lifo.h"

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