Compare commits

...

199 Commits

Author SHA1 Message Date
Zach Hilman
aef0d88165 configure_input: Use Joycons Docked instead of Connected as label 2018-11-18 23:22:36 -05:00
Zach Hilman
312ef596a5 configure_input_player: Set minimum width on controls 2018-11-18 23:22:36 -05:00
Zach Hilman
dd92db3fb0 configure_input: Properly update UI components on removal of player 2018-11-18 23:22:36 -05:00
Zach Hilman
e58c951a59 configure_input: Make None a controller option instead of checkbox 2018-11-18 23:22:36 -05:00
Zach Hilman
3a6cd5b3c8 hid: Use player-defined controller type as PREFERRED_CONTROLLER 2018-11-18 23:22:36 -05:00
Zach Hilman
3d1a221893 qt: Move controller button config to separate dialog
Handles button configuration for all controller layouts and debug pads. Configurable at construction.
2018-11-18 23:22:36 -05:00
Zach Hilman
afe8df5020 qt: Add UI to configure touchscreen parameters
This allows adjusting the finger, diameter, and angle of the emulated touchscreen. It also provides a warning to the user about what changing these parameters can do.
2018-11-18 23:22:36 -05:00
Zach Hilman
2e1dd9c649 qt: Add UI to configure mouse buttons
Supports setting the five mouse buttons to any valid controller button/keyboard key (Left, Right, Middle, Foward, Back)
2018-11-18 23:22:36 -05:00
Zach Hilman
f1aec256d7 configure_input: Add support for multiplayer and controller types
This moves the actual button configuration to a separate dialog and only has the enabled and type controls in the tab.
2018-11-18 23:22:36 -05:00
Zach Hilman
55ded706d6 hid/npad: Update NPad to use player controller bindings and type 2018-11-18 23:22:36 -05:00
Zach Hilman
e9145c3e16 hid/touchscreen: Update Touchscreen to use advanced parameters
Including finger ID, diamater x/y, and angle. Additionally, checks if the touchscreen is enabled.
2018-11-18 23:22:36 -05:00
Zach Hilman
3b25426bd9 hid: Add controller bindings for Mouse controller 2018-11-18 23:22:36 -05:00
Zach Hilman
0fd45e78f4 hid: Add keyboard bindings for Keyboard controller 2018-11-18 23:22:36 -05:00
Zach Hilman
06cf050c0a hid: Add controller bindings for DebugPad controller
Used by developers to test games, not present on retail systems. Some games are known to respond to DebugPad input though, for example Kirby Star Allies.
2018-11-18 23:22:36 -05:00
Zach Hilman
d1b7c65b9e yuzu/config: Add (de-)serialization for multiplayer
Defaults to full keyboard for keyboard -- It did not seem to be necessary to make the keyboard configurable (besides enabled/disabled).
2018-11-18 23:22:36 -05:00
Zach Hilman
b8f7f9651e yuzu_cmd/config: Add config deserialization for multiplayer 2018-11-18 23:22:36 -05:00
Zach Hilman
c77454b9d0 settings: Add settings for multiple players and controllers
Uses the PlayerInput struct to represent all of the data that constitutes a player.
2018-11-18 23:21:33 -05:00
Zach Hilman
fd5fa48674 settings: Add Native type for keyboard 2018-11-18 23:21:33 -05:00
Zach Hilman
152422bab1 settings: Add Native type for mouse buttons 2018-11-18 23:21:33 -05:00
David Marcec
0c3e7b7086 Added missing start/end touch attributes to touchscreen 2018-11-18 23:21:33 -05:00
David Marcec
f66c6fe554 Added debugpad skeleton 2018-11-18 23:21:33 -05:00
David Marcec
362b28d052 Added controller helper funcs 2018-11-18 23:21:33 -05:00
David Marcec
a69b9d73f5 Changed polling rate of hid and Right joycon rotation 2018-11-18 23:21:33 -05:00
David Marcec
7fbe2c83a7 Left joycon rotation button remapping 2018-11-18 23:21:33 -05:00
David Marcec
b9c1e4b0e7 Added automatic npad switch based on supported stylesets 2018-11-18 23:21:33 -05:00
David Marcec
beab38601b Added multi-input support and controller assignment at any port 2018-11-18 23:21:33 -05:00
bunnei
f02b125ac8 Merge pull request #1717 from FreddyFunk/swizzle-gob
textures/decoders: Replace magic numbers
2018-11-18 20:13:00 -08:00
bunnei
6dc33fb812 Merge pull request #1693 from Tinob/master
Missing ogl states
2018-11-18 19:59:10 -08:00
bunnei
5e6ad795cc Merge pull request #1555 from ccawley2011/clang-format-docker
travis: Use pre-built image for clang-format target
2018-11-18 19:54:38 -08:00
bunnei
997c3dc6ff Merge pull request #1619 from janisozaur/patch-12
Handle missing git info when building
2018-11-18 19:53:34 -08:00
Frederic L
11a1442229 Eliminated unnessessary memory allocation and copy (#1702) 2018-11-18 19:53:03 -08:00
bunnei
3e93c30630 Merge pull request #1640 from DarkLordZach/game-list-reload
game_list: Only reload game list after relevant settings changed
2018-11-18 19:24:53 -08:00
bunnei
e34d47e6e3 Merge pull request #1620 from DarkLordZach/ldr-ro
ldr_ro: Complete LDR:RO implementation
2018-11-18 19:23:38 -08:00
bunnei
f08b4cbbc8 Merge pull request #1718 from ogniK5377/lets-go-softlock
Implemented CalculateStandardUserSystemClockDifferenceByUser
2018-11-18 19:22:47 -08:00
Schplee
9a47e40dd6 Correctly sets default system language for yuzu-CLI (#1727)
* Correctly sets default system language for yuzu-CLI

A user reported that yuzu_cmd runs games in Japanese rather than the correct default of English (like yuzu-qt does correctly), this change fixes that.

* fix clang issue

deleted whitespace
2018-11-18 19:21:17 -08:00
bunnei
da238db6df Merge pull request #1730 from ReinUsesLisp/fix-intel
gl_rasterizer: Remove default clip distance
2018-11-18 19:19:43 -08:00
bunnei
611141e09f Merge pull request #1671 from DarkLordZach/vi-disconnect
vi: Implement TransactParcel for Disconnect and DetachBuffer
2018-11-18 19:18:46 -08:00
ReinUsesLisp
29e7c76d66 gl_rasterizer: Remove default clip distance 2018-11-18 23:57:52 -03:00
Mat M
9a1bac840e Merge pull request #1728 from FearlessTobi/reset-signal
svc: ResetSignal is not stubbed
2018-11-18 15:51:32 -05:00
Tobias
13f79cc5bd svc: ResetSignal is not stubbed
https://user-images.githubusercontent.com/20753089/48677874-b8e01c80-eb7b-11e8-8043-b99faa29022c.PNG
2018-11-18 21:49:17 +01:00
Rodolfo Bogado
4d1a0a24cc drop support for non separate alpha as it seems to cause issues in some games 2018-11-18 03:44:48 -03:00
Zach Hilman
51af996854 ldr_ro: Add error check for memory allocation failure 2018-11-17 21:40:26 -05:00
Rodolfo Bogado
81a9c5fe6f fix sampler configuration, thanks to Marcos for his investigation 2018-11-17 19:59:34 -03:00
Rodolfo Bogado
b312cca756 small type fix 2018-11-17 19:59:34 -03:00
Rodolfo Bogado
5297495c87 small fix for alphaToOne bit location 2018-11-17 19:59:34 -03:00
Rodolfo Bogado
e69eb3c760 fix for gcc compilation 2018-11-17 19:59:34 -03:00
Rodolfo Bogado
53b4a1af0f add AlphaToCoverage and AlphaToOne 2018-11-17 19:59:34 -03:00
Rodolfo Bogado
8ed7e1af2c add support for fragment_color_clamp 2018-11-17 19:59:33 -03:00
Rodolfo Bogado
02c22a3440 add missing MirrorOnceBorder support where supported 2018-11-17 19:59:33 -03:00
Rodolfo Bogado
1d60bb6544 set border color not depending on the wrap mode
only enable color mask for the first framebuffer id independent blending is disabled
2018-11-17 19:59:33 -03:00
Rodolfo Bogado
6a2aa6dbdb set default value for point size register 2018-11-17 19:59:33 -03:00
Rodolfo Bogado
1881e86c43 fix viewport and scissor behavior 2018-11-17 19:59:32 -03:00
Zach Hilman
c91dc417d5 vi: Implement TransactParcel for Disconnect and DetachBuffer
Used by homebrew on exit. According to switchbrew, returns an empty response parcel with one zero in it.
2018-11-17 17:10:19 -05:00
Hexagon12
a819116154 Merge pull request #1722 from MysticExile/enable-applictation-crash-report
am: Stub EnableApplicationCrashReport
2018-11-17 19:42:07 +02:00
MysticExile
03f274d8c1 Stubbed am:EnableApplicationCrashReport 2018-11-17 15:05:55 +01:00
bunnei
0072275d25 Merge pull request #1678 from FearlessTobi/amiibo-hotkeys
Port citra-emu/citra#4387: "yuzu: Add hotkey for Amiibo loading"
2018-11-16 20:20:41 -08:00
bunnei
6273a7aeee Merge pull request #1705 from Jcw87/mingw-jpeg
Include QT imageformat dependencies with releases
2018-11-16 20:20:03 -08:00
bunnei
0149b4245c Merge pull request #1711 from ogniK5377/bluetooth-lets-go
Added various bluetooth based cmds for palma
2018-11-16 20:08:36 -08:00
bunnei
6552d75be1 Merge pull request #1719 from bunnei/hwopus-fix
hwopus: DecodeInterleavedWithPerformance: Fix ordering of output parameters.
2018-11-16 20:08:17 -08:00
bunnei
224fcabe46 Merge pull request #1714 from lioncash/kern-err
kernel/errors: Clean up error codes
2018-11-16 20:07:55 -08:00
bunnei
585e6fd426 hwopus: DecodeInterleavedWithPerformance: Fix ordering of output parameters.
- Fixes audio issues with Pokemon: Let's Go Pikachu & Eevee.
2018-11-16 22:54:38 -05:00
David Marcec
c440e8b8e1 Implemented CalculateStandardUserSystemClockDifferenceByUser
Seems pokemon calls this sometimes and it caused "random crashes"
2018-11-17 14:01:16 +11:00
Frederic Laing
7a400e2191 textures/decoders: Replace magic numbers 2018-11-17 01:55:28 +01:00
bunnei
3c7ba00d73 Merge pull request #1712 from FearlessTobi/port-4426
Port citra-emu/citra#4426: "Common/Bitfield: store value as unsigned type"
2018-11-16 14:42:59 -08:00
Lioncash
d21b2164e9 kernel/errors: Clean up error codes
Similar to PR 1706, which cleans up the error codes for the filesystem
code, but done for the kernel error codes. This removes the ErrCodes
namespace and specifies the errors directly. This also fixes up any
straggling lines of code that weren't using the named error codes where
applicable.
2018-11-16 14:32:30 -05:00
Mat M
14cede1a0c Merge pull request #1638 from FreddyFunk/SetMemoryPermission-Stubbed
Implement SetMemoryPermission
2018-11-16 10:35:56 -05:00
bunnei
405dd03dae Merge pull request #1700 from FreddyFunk/cleanup
gl_rasterizer_chache: Minor cleanup
2018-11-16 07:07:30 -08:00
bunnei
241646f423 Merge pull request #1701 from FreddyFunk/decoders
textures/decoders: Minor cleanup
2018-11-16 07:07:11 -08:00
bunnei
5b8f70ea2e Merge pull request #1632 from DarkLordZach/keys-manager-optimizations
game_list: Optimize game list refresh
2018-11-16 07:02:37 -08:00
bunnei
0b701751da Merge pull request #1676 from lioncash/warn
gl_state: Amend compilation warnings
2018-11-16 07:00:03 -08:00
bunnei
238bc4a077 Merge pull request #1706 from lioncash/file-err
file_sys/errors: Clean up error code values
2018-11-16 06:59:07 -08:00
bunnei
e156834297 Merge pull request #1710 from ogniK5377/palma-lets-go
Added SetIsPalmaAllConnectable, SetPalmaBoostMode
2018-11-16 06:58:05 -08:00
Weiyi Wang
786995a81e Common/Bitfield: store value as unsigned type
Storing signed type causes the following behaviour: extractValue can do overflow/negative left shift. Now it only relies on two implementation-defined behaviours (which are almost always defined as we want): unsigned->signed conversion and signed right shift
2018-11-16 15:49:57 +01:00
David Marcec
e8899e7027 Added various bluetooth based cmds for palma
It seems palma is done through bluetooth, we need this for pokemon go however more research needs to be done when we actually get palma working. This is presumably used for transfering data between the controller and the console, it does not seem for actual input as far as I know.
2018-11-17 01:42:17 +11:00
David Marcec
e658118aa9 Added SetIsPalmaAllConnectable, SetPalmaBoostMode
Currently unclear what these do yet, will be researched at a later time when we want to implement palma.
2018-11-17 01:40:18 +11:00
Hexagon12
d104478f1b Merge pull request #1709 from ogniK5377/docked-mode-crash
Fixed switching operation modes when not running a game
2018-11-16 16:25:40 +02:00
David Marcec
4476fd29d6 Fixed switching operation modes when not running a game
The service manager seems to be a nullptr before a game boots
2018-11-16 20:08:02 +11:00
Jcw87
e634ce847f Remove whitespace 2018-11-15 22:44:18 -08:00
Jcw87
84364c9d4f Include imageformat dependencies with releases (appveyor) 2018-11-15 21:53:40 -08:00
Lioncash
5c9c33e8ad file_sys/errors: Remove currently unused filesystem error codes
Rather than keeping around unused values, we can just introduce them as
needed.
2018-11-16 00:18:22 -05:00
Lioncash
edd5b6f12f file_sys/errors: Get rid of the ErrCodes namespace
There's no real point to keeping the separate enum around, especially
given the name of the error code itself is supposed to document what the
value actually represents.
2018-11-16 00:13:50 -05:00
Lioncash
b725d1fdf7 file_sys/errors: Extract FS-related error codes to file_sys/errors.h
Keeps filesystem-related error codes in one spot.
2018-11-16 00:13:50 -05:00
Jcw87
287ee1cd63 Include imageformat dependencies with releases 2018-11-15 20:55:37 -08:00
David
87eca5b209 Fixed priority switching edge case for handheld (#1675)
* Fixed priority switching edge case for handheld

We accidently used controller index instead of npad id

* Moved NPadIdToIndex
2018-11-15 20:31:27 -08:00
bunnei
75640c9c71 Merge pull request #1699 from DarkLordZach/deterministic-rng-3
csrng: Use random integer distribution instead of raw engine
2018-11-15 20:18:47 -08:00
Zach Hilman
8cb2e7d881 csrng: Use random integer distribution instead of raw engine
Prevents returning the same value every single call.
2018-11-15 18:44:26 -05:00
bunnei
f7319b0d3c Merge pull request #1687 from lioncash/deduplication
kernel/thread: Deduplicate scheduler switching code
2018-11-15 14:47:42 -08:00
bunnei
97605e36f7 Merge pull request #1618 from DarkLordZach/dump-nso
patch_manager: Add support for dumping uncompressed NSOs
2018-11-15 14:46:10 -08:00
bunnei
98060c6f7b Merge pull request #1691 from lioncash/audren
service/audren_u: Forward RequestUpdateAuto through the same function as RequestUpdate
2018-11-15 14:44:36 -08:00
Frederic Laing
95d3965f31 textures/decoders: Minor cleanup 2018-11-15 21:04:17 +01:00
Frederic Laing
3844b5c0c5 gl_rasterizer_chache: Minor cleanup 2018-11-15 20:10:05 +01:00
Zach Hilman
c0a9abc3e1 ldr_ro: Implement UnloadNro (command 1)
Includes actual unmapping and address error checking.
2018-11-15 12:48:09 -05:00
Zach Hilman
056fa43dcd ldr_ro: Fully Implement LoadNro (command 0)
Includes NRO and BSS error checking, maximum loaded NRO check, NRR hash check, and proper remapping of BSS data.
2018-11-15 12:48:09 -05:00
Zach Hilman
5e8e7b6019 ldr_ro: Implement UnloadNrr (command 3)
Includes initialization check, proper address check, alignment check, and actual unloading of a loaded NRR.
2018-11-15 12:48:09 -05:00
Zach Hilman
6cd504feb9 ldr_ro: Fully implement LoadNrr (command 2)
Includes parameter error checking, hash enforcement, initialization check, and max NRR load check.
2018-11-15 12:48:09 -05:00
Zach Hilman
0276761a1e process: Make MirrorMemory take state to map new memory as
Credits to Subv
2018-11-15 12:48:09 -05:00
Zach Hilman
8aa17f0480 pl_u: Resize buffers in shared font data getter to what game requests
Fixes unmapped spam in SMP and buffer size errors in some other games
2018-11-15 12:47:36 -05:00
bunnei
8a537a2021 Merge pull request #1637 from FernandoS27/cache
Improved GPU Caches lookup Speed
2018-11-14 19:07:52 -08:00
bunnei
c6c74248fe Merge pull request #1697 from lioncash/acc
acc/profile_manager: Minor cleanup-related changes
2018-11-14 19:02:25 -08:00
bunnei
0478308094 Merge pull request #1696 from lioncash/acc-cond
service/acc: Correct error case within TrySelectUserWithoutInteraction()
2018-11-14 19:02:16 -08:00
bunnei
53b3c3ab7f Merge pull request #1695 from lioncash/tr
yuzu/configure_system: Mark the entropy mask string as nontranslatable
2018-11-14 15:32:04 -08:00
Lioncash
cd47af8af0 service/acc: Correct error case within TrySelectUserWithoutInteraction()
empty() in this case will always return false, since the returned
container is a std::array. Instead, check if all given users are invalid
before returning the error code.
2018-11-14 17:43:23 -05:00
Lioncash
9761936e02 profile_manager: Replace iterative loop with a ranged-for loop in ParseUserSaveFile() 2018-11-14 17:13:17 -05:00
Lioncash
1af13e0802 profile_manager: Move UUID Format function definitions into the cpp file
Avoids relying on fmt always being indirectly included.
2018-11-14 17:08:59 -05:00
bunnei
c681690358 Merge pull request #1690 from lioncash/nfp
nfp: Correct erroneous sizeof expression within GetTagInfo()
2018-11-14 11:55:09 -08:00
bunnei
7384b33c4f Merge pull request #1689 from lioncash/break
hid/npad: Add missing break in switch statement within Controller_NPad::OnUpdate
2018-11-14 11:54:45 -08:00
bunnei
c95ded3a4a Merge pull request #1688 from lioncash/unused
service: Mark MakeFunctionString with the [[maybe_unused]] attribute.
2018-11-14 11:54:29 -08:00
Lioncash
e6676afa18 yuzu/configure_system: Mark the entropy mask string as nontranslatable
There's no need for translators to concern themselves with the
validation mask used by the entry field.
2018-11-14 14:53:43 -05:00
bunnei
ebf1b58a22 Merge pull request #1684 from lioncash/common
common/string_util: Minor cleanup
2018-11-14 11:53:10 -08:00
bunnei
e1ea8cc721 Merge pull request #1679 from DarkLordZach/deterministic-rng-2
svc: Use proper random entropy generation algorithm
2018-11-14 11:52:27 -08:00
Lioncash
b4f63db04e nfp: Correct erroneous sizeof expression within GetTagInfo()
The previous expression would copy sizeof(size_t) amount of bytes (8 on
a 64-bit platform) rather than the full 10 bytes comprising the uuid
member.

Given the source and destination types are the same, we can just use an
assignment here instead.
2018-11-14 12:53:39 -05:00
Lioncash
3619b31fc0 service/audren_u: Forward RequestUpdateAuto through the same function as RequestUpdate
Based off RE, they both currently go through the same codepath with no
difference in behavior.
2018-11-14 02:36:21 -05:00
Lioncash
fcde356f15 hid/npad: Add missing break in switch statement within Controller_NPad::OnUpdate() 2018-11-14 00:59:17 -05:00
Lioncash
958fa15a4c service: Mark MakeFunctionString with the [[maybe_unused]] attribute.
When yuzu is compiled in release mode this function is unused, however,
when compiled in debug mode, it's used within a LOG_TRACE statement.
This prevents erroneous compilation warnings about an unused function
(that isn't actually totally unused).
2018-11-14 00:49:04 -05:00
Lioncash
f9db75fe40 kernel/thread: Deduplicate scheduler switching code
The code in both places was the same verbatim, so we can extract it to a
function to deduplicate the logic.
2018-11-14 00:02:42 -05:00
bunnei
3bd503d59c Merge pull request #1662 from FreddyFunk/CopySurface-Optimization
gl_rasterizer_cache: CopySurface optimization
2018-11-13 18:58:12 -08:00
bunnei
09f4150241 Merge pull request #1686 from DarkLordZach/move-open-yuzu-folder
qt: Move Open yuzu Folder action from Help to File
2018-11-13 18:56:44 -08:00
bunnei
d2b2b05b6a Merge pull request #1685 from lioncash/base
video_core/renderer_base: Remove GL include from the renderer base class files
2018-11-13 18:53:59 -08:00
bunnei
723bd89883 Merge pull request #1677 from FreddyFunk/skip-vao-binding-cleanup
engines/maxwell_3d: Minor cleanup
2018-11-13 18:53:38 -08:00
bunnei
70f189d7af Merge pull request #1680 from lioncash/mem
kernel/process: Migrate heap-related memory management out of the process class and into the vm manager
2018-11-13 18:52:18 -08:00
bunnei
a80467db57 Merge pull request #1682 from lioncash/audio
hle/audren_u: Implement Get/SetRenderingTimeLimit
2018-11-13 18:51:44 -08:00
bunnei
d2a630c41f Merge pull request #1683 from lioncash/typo
audio_core/audio_renderer: Fix typo in AuxInfo member name
2018-11-13 18:51:35 -08:00
bunnei
9b12623743 Merge pull request #1608 from DarkLordZach/save-data-reader
[ns|fsp_srv]: Implement various functions to boot Checkpoint
2018-11-13 18:51:08 -08:00
Lioncash
c7387e6504 string_util: Remove ArrayToString()
An old function from Dolphin. This is also unused, and pretty inflexible
when it comes to printing out different data types (for example, one
might not want to print out an array of u8s but a different type
instead. Given we use fmt, there's no need to keep this implementation
of the function around.
2018-11-13 18:14:11 -05:00
Lioncash
f1219e3a87 string_util: Remove TryParse()
This is an unused hold-over from Dolphin that was primarily used to
parse values out of the .ini files. Given we already have libraries that
do this for us, we don't need to keep this around.
2018-11-13 18:13:45 -05:00
Zach Hilman
6001af2b89 qt: Move Open yuzu Folder action from Help to File 2018-11-13 17:17:47 -05:00
Lioncash
4ed9ef15c4 video_core/renderer_base: Remove GL include from the renderer base class files
Keeps the base class source files implementation-agnostic.
2018-11-13 14:38:13 -05:00
Lioncash
9bc18eada8 string_util: Remove ThousandSeparate()
This is currently unused and doesn't really provide much value to keep
around either.
2018-11-13 14:04:26 -05:00
Lioncash
454cf1dc09 hle/audren_u: Implement Get/SetRenderingTimeLimit
These appear to be a basic getter and setter pair, so these are fairly
trivial to implement and get out of the way.
2018-11-13 13:49:09 -05:00
Lioncash
81a39181b0 audio_core/audio_renderer: Fix typo in AuxInfo member name 2018-11-13 13:32:13 -05:00
Lioncash
004277477a vm_manager: Unstub GetTotalHeapUsage()
Now that we've moved all of the heap-related stuff to the VMManager
class, we can unstub this function, as the necessary members are visible
now.
2018-11-13 13:08:26 -05:00
Lioncash
b8e885c6e5 kernel/process: Migrate heap-related memory management out of the process class and into the vm manager
Avoids a breach of responsibilities in the interface and keeps the
direct code for memory management within the VMManager class.
2018-11-13 13:08:19 -05:00
Zach Hilman
ab552e4a25 svc: Use proper random entropy generation algorithm 2018-11-13 12:26:03 -05:00
fearlessTobi
9ea8eb6b2e yuzu: Add hotkey for Amiibo loading 2018-11-13 15:10:39 +01:00
Frederic L
ab362aa7e5 gl_rasterizer: Minor cleanup
Minor code cleanup from unaddressed feedback in #1654
2018-11-13 14:07:23 +01:00
Lioncash
9a0fb7d9fb gl_state: Amend compilation warnings
Makes float -> integral conversions explicit via casts and also silences
a sign conversion warning.
2018-11-13 07:10:40 -05:00
bunnei
65bd03d74c Merge pull request #1628 from greggameplayer/Texture2DArray
Implement SurfaceTarget Texture2DArray
2018-11-12 21:13:47 -08:00
bunnei
7f3c2525e6 Merge pull request #1670 from DarkLordZach/deterministic-rng
csrng: Add config option to set RNG seed
2018-11-12 21:10:08 -08:00
bunnei
fd72d889bf Merge pull request #1665 from ogniK5377/GetClockSnapshot
Implement GetClockSnapshot, ToPosixTime & ToPosixTimeWithMyRule
2018-11-12 20:13:47 -08:00
Zach Hilman
cb1e63ef09 svc: Return random seed for svcGetInfo RandomEntropy 2018-11-12 21:46:21 -05:00
greggameplayer
c8b3f09876 Implement ASTC_2D_10X8 & ASTC_2D_10X8_SRGB (#1666)
* Implement ASTC_2D_10X8 & ASTC_2D_10X8_SRGB
( needed by Mario+Rabbids Kingdom Battle )

* Small placement correction
2018-11-12 18:34:54 -08:00
bunnei
d08b876c9d Merge pull request #1650 from FreddyFunk/cast
yuzu/main: Fix compiler warning
2018-11-12 18:32:54 -08:00
James Rowe
b4a6ce02ce Merge pull request #1674 from FearlessTobi/fullscreen-fix
yuzu: Add a missing "!" to fix the stuck-in-fullscreen bug
2018-11-12 13:00:25 -07:00
Tobias
9333ee29ca yuzu: Add a missing "!" to fix the stuck-in-fullscreen bug 2018-11-12 20:57:15 +01:00
Zach Hilman
2a16fd7ffc settings: Add config option to set RNG seed 2018-11-11 23:09:46 -05:00
Zach Hilman
4b4f883aef csrng: Use std::mt19937 engine for random number generation 2018-11-11 23:08:39 -05:00
bunnei
2c6efda235 Merge pull request #1660 from Tinob/master
Map more missing opengl states
2018-11-11 19:58:16 -08:00
bunnei
7474382266 Merge pull request #1652 from FreddyFunk/static-cast
configure_system: Fix compiler warning
2018-11-11 12:19:03 -08:00
bunnei
a264fac943 Merge pull request #1664 from FreddyFunk/cast2
gl_rasterizer: Fix compiler warnings
2018-11-11 12:18:29 -08:00
Rodolfo Bogado
72b1fae984 Use core extensions when available to set max anisotropic filtering level 2018-11-11 16:36:53 -03:00
Rodolfo Bogado
4e6c64bf8d Improve state management by splitting some of the states id separated function to avoid a full apply overhead 2018-11-11 16:36:53 -03:00
Rodolfo Bogado
4a6eff3b7b Try to fix problems with stencil test in some games, relax translation to opengl enums to avoid crashing and only generate logs of the errors. 2018-11-11 16:31:00 -03:00
Rodolfo Bogado
e9610ec0dd set sampler max lod, min lod, lod bias and max anisotropy 2018-11-11 16:31:00 -03:00
FernandoS27
3088e36237 Improved GPU Caches lookup Speed 2018-11-11 12:53:25 -04:00
bunnei
eaee73f95d Merge pull request #1669 from ReinUsesLisp/fixup-gs
gl_shader_decompiler: Guard out of bound geometry shader input reads
2018-11-11 08:28:20 -08:00
bunnei
c82bccab56 Merge pull request #1663 from lioncash/raster
rasterizer_cache: Remove reliance on the System singleton
2018-11-11 08:20:27 -08:00
bunnei
1916213311 Merge pull request #1648 from FernandoS27/texs-3-array
Implement 3 coordinate array in TEXS instruction
2018-11-11 08:18:27 -08:00
bunnei
8ea6261547 Merge pull request #1654 from degasus/dirty_flags
gl_rasterizer: Skip VAO binding if the state is clean.
2018-11-11 08:17:57 -08:00
James Rowe
93fca5d9cf Merge pull request #1656 from ogniK5377/message-queue
Ability to switch between docked and undocked mode in-game
2018-11-10 11:27:17 -07:00
James Rowe
c21a6c6b37 Merge pull request #1661 from lioncash/dtor
rasterizer_cache: Add missing virtual destructor to RasterizerCacheObject
2018-11-10 11:08:55 -07:00
David Marcec
48cd61d9c8 Added maybe_unused 2018-11-10 18:07:34 +11:00
David Marcec
ddc242dd51 Added ToPosixTime & ToPosixTimeWithMyRule
Added instead of using a seperate PR to prevent conflicts
2018-11-10 17:41:57 +11:00
ReinUsesLisp
8d4bb10d44 gl_shader_decompiler: Guard out of bound geometry shader input reads
Geometry shaders follow a pattern that results in out of bound reads.
This pattern is:
- VSETP to predicate
- Use that predicate to conditionally set a register a big number
- Use the register to access geometry shaders
At the time of writing this commit I don't know what's the intent of
this number. Some drivers argue about these out of bound reads. To avoid
this issue, input reads are guarded limiting reads to the highest
posible vertex input of the current topology (e.g. points to 1 and
triangles to 3).
2018-11-10 03:10:50 -03:00
David Marcec
84c6134264 Added consts and static 2018-11-10 12:31:48 +11:00
David Marcec
4f78f5c0df Implement GetClockSnapshot
Needed by megaman 11
2018-11-10 01:25:56 +11:00
Frederic Laing
e2bf581e3a gl_rasterizer_cache: Remove unnecessary memory allocation and copy in CopySurface 2018-11-08 16:50:09 +01:00
Frederic Laing
1d36aec267 gl_rasterizer: Fix compiler warnings 2018-11-08 13:33:30 +01:00
Lioncash
9de523fd90 rasterizer_cache: Add missing virtual destructor to RasterizerCacheObject
Ensures that destruction will always do the right thing in any context.
2018-11-08 00:31:39 -05:00
FernandoS27
d347623d6f Correct issue where texturelod could not be applied to 2darrayshadow 2018-11-07 21:48:45 -04:00
David Marcec
40db288a2a Renamed CheckIfOperationChanged to OnDockedModeChanged 2018-11-08 12:12:00 +11:00
FernandoS27
ad2f47b579 Implement 3 coordinate array in TEXS instruction 2018-11-07 17:04:30 -04:00
David Marcec
fd1ef25257 Fixups 2018-11-07 20:12:27 +11:00
David Marcec
41e99d8880 Ability to switch between docked and undocked mode in-game
Started implementation of the AM message queue mainly used in state getters. Added the ability to switch docked mode whilst in game without stopping emulation. Also removed some things which shouldn't be labelled as stubs as they're implemented correctly
2018-11-07 18:01:33 +11:00
Markus Wick
359db6a673 gl_rasterizer: Skip VAO binding if the state is clean. 2018-11-06 22:31:33 +01:00
Markus Wick
0590dd2971 gl_rasterizer: Split VAO and VB setup functions. 2018-11-06 22:31:33 +01:00
greggameplayer
d3b9599b2d Merge branch 'master' into Texture2DArray 2018-11-06 19:05:57 +01:00
Frederic Laing
d34d0bfc87 configure_system: Fix compiler warning 2018-11-06 16:38:10 +01:00
Frederic Laing
6c8b788d32 yuzu/main: Fix compiler warning 2018-11-06 15:22:24 +01:00
Frederic Laing
ba2cdcdc5a Implement SetMemoryPermission 2018-11-06 10:21:01 +01:00
Zach Hilman
52e7e8eed3 game_list: Only reload game list after relevant settings changed
Prevents unnecessary reloads on every configuration operation.
2018-11-03 20:38:39 -04:00
Frederic Laing
ab7472345b Stubbed SetMemoryPermission 2018-11-03 16:01:34 +01:00
greggameplayer
9249fadb9e correct syntax 2018-11-02 14:28:28 +01:00
greggameplayer
cb8e4a4633 Merge branch 'master' into Texture2DArray 2018-11-02 14:26:32 +01:00
Zach Hilman
2d2ef05d8c game_list: Make add-ons column optional
As the add-ons column takes the most processing time out of any (as it needs to search registration for updates/dlc, patch control NCAs, search for mods, etc.), an option was added to disable it. This does not affect the application of add-ons. In large game collections, this decreases game list refresh time by as much as 70%.
2018-11-01 20:27:12 -04:00
Zach Hilman
8f183a47dd filesystem: Cache RegisteredCacheUnion instead of constructing on demand
Prevents unnecessary re-reads of the metadata and unnecessary temporary objects.
2018-11-01 20:24:32 -04:00
Zach Hilman
97d425c304 file_sys: Use common KeyManager in NCA container types
Creates a single KeyManager for the entire container and then passes it into the NCA constructor, eliminating several unnecessary KeyManager reads.
2018-11-01 20:23:38 -04:00
Zach Hilman
e20db909ee content_archive: Add optional KeyManager parameter to constructor
Allows resuing a common KeyManager when a large amount of NCAs are handled by the same class. Should the parameter not be provided, a new KeyManager will be constructed, as was the default behavior prior to this.
2018-11-01 20:22:29 -04:00
greggameplayer
9ae972ab4e Implement SurfaceTarget Texture2DArray
( needed by Mario+Rabbids Kingdom Battle )
2018-10-31 04:29:15 +01:00
Michał Janiszewski
44c80e5d8b Handle missing git info when building 2018-10-29 23:39:33 +01:00
Zach Hilman
bdaa76c0db ns: Implement command 400: GetApplicationControlData
Returns the raw NACP bytes and the raw icon bytes into a title-provided buffer. Pulls from Registration Cache for control data, returning all zeros should it not exist.
2018-10-29 16:20:16 -04:00
Zach Hilman
1c0226815d patch_manager: Add support for dumping decompressed NSOs
When enabled in settings, PatchNSO will dump the unmodified NSO that it was passed to a file named <build id>.nso in the dump root for the current title ID.
2018-10-29 16:10:16 -04:00
Zach Hilman
48eb3742b9 settings: Add setting to control NSO dumping
Also adds UI option in Debug > Dump section, with the idea later things to be dumped (i.e. other game data or textures, etc) will use the same group box.
2018-10-29 16:09:08 -04:00
Zach Hilman
9078bb9854 bis_factory: Add getter for mod dump root for a title ID
Equates to yuzu_dir/dump/<title id>/
2018-10-29 16:08:03 -04:00
Zach Hilman
5ee19add1b fsp_srv: Implement ISaveDataInfoReader
An object to read SaveDataInfo objects, which describe a unique save on the system. This implementation iterates through all the directories in the save data space and uses the paths to reconstruct the metadata.
2018-10-29 13:54:39 -04:00
Zach Hilman
2e8177f0c9 fsp_srv: Implement command 61: OpenSaveDataInfoReaderBySaveDataSpaceId
Needed by Checkpoint. Returns an object that can iterate through all savedata on the system.
2018-10-29 13:54:39 -04:00
Zach Hilman
df264d2ccb savedata_factory: Expose accessors for SaveDataSpace 2018-10-29 13:54:38 -04:00
Zach Hilman
f2f679bf3f loader/nro: Call RegisterRomFS from Load
Allows NRO homebrew to use the RomFS in the ASET section.
2018-10-29 13:54:38 -04:00
Zach Hilman
b04e39107f control_metadata: Add GetRawBytes function to NACP
Returns the raw bytes of the NACP file. Needed for GetApplicationControlData which returns the raw, unprocessed NACP to the game.
2018-10-29 13:54:38 -04:00
Cameron Cawley
34eaf3a366 travis: Use pre-built image for clang-format target 2018-10-24 00:08:38 +01:00
133 changed files with 7391 additions and 2525 deletions

View File

@@ -1,3 +1,3 @@
#!/bin/bash -ex
docker run -v $(pwd):/yuzu ubuntu:18.04 /bin/bash -ex /yuzu/.travis/clang-format/docker.sh
docker run --env-file .travis/common/travis-ci.env -v $(pwd):/yuzu -v "$HOME/.ccache":/root/.ccache citraemu/build-environments:linux-clang-format /bin/bash -ex /yuzu/.travis/clang-format/docker.sh

View File

@@ -1,3 +1,3 @@
#!/bin/sh -ex
docker pull ubuntu:18.04
docker pull citraemu/build-environments:linux-clang-format

View File

@@ -1,8 +1,5 @@
#!/bin/bash -ex
apt-get update
apt-get install -y clang-format-6.0
# Run clang-format
cd /yuzu
./.travis/clang-format/script.sh

View File

@@ -57,3 +57,4 @@ done
pip3 install pefile
python3 .travis/linux-mingw/scan_dll.py package/*.exe "package/"
python3 .travis/linux-mingw/scan_dll.py package/imageformats/*.dll "package/"

View File

@@ -125,17 +125,6 @@ after_build:
Copy-Item -path "$CMAKE_SOURCE_DIR/license.txt" -destination $RELEASE_DIST
Copy-Item -path "$CMAKE_SOURCE_DIR/README.md" -destination $RELEASE_DIST
# copy all the dll dependencies to the release folder
. "./.appveyor/UtilityFunctions.ps1"
$DLLSearchPath = "C:\msys64\mingw64\bin;$env:PATH"
$MingwDLLs = RecursivelyGetDeps $DLLSearchPath "$RELEASE_DIST\yuzu.exe"
$MingwDLLs += RecursivelyGetDeps $DLLSearchPath "$RELEASE_DIST\yuzu_cmd.exe"
Write-Host "Detected the following dependencies:"
Write-Host $MingwDLLs
foreach ($file in $MingwDLLs) {
Copy-Item -path "$file" -force -destination "$RELEASE_DIST"
}
# copy the qt windows plugin dll to platforms
Copy-Item -path "C:/msys64/mingw64/share/qt5/plugins/platforms/qwindows.dll" -force -destination "$RELEASE_DIST/platforms"
@@ -145,6 +134,18 @@ after_build:
# copy the qt jpeg imageformat dll to platforms
Copy-Item -path "C:/msys64/mingw64/share/qt5/plugins/imageformats/qjpeg.dll" -force -destination "$RELEASE_DIST/imageformats"
# copy all the dll dependencies to the release folder
. "./.appveyor/UtilityFunctions.ps1"
$DLLSearchPath = "C:\msys64\mingw64\bin;$env:PATH"
$MingwDLLs = RecursivelyGetDeps $DLLSearchPath "$RELEASE_DIST\yuzu.exe"
$MingwDLLs += RecursivelyGetDeps $DLLSearchPath "$RELEASE_DIST\yuzu_cmd.exe"
$MingwDLLs += RecursivelyGetDeps $DLLSearchPath "$RELEASE_DIST\imageformats\qjpeg.dll"
Write-Host "Detected the following dependencies:"
Write-Host $MingwDLLs
foreach ($file in $MingwDLLs) {
Copy-Item -path "$file" -force -destination "$RELEASE_DIST"
}
7z a -tzip $MINGW_BUILD_ZIP $RELEASE_DIST\*
7z a $MINGW_SEVENZIP $RELEASE_DIST
}

View File

@@ -33,6 +33,10 @@ else()
endif()
if(NOT HEAD_HASH)
file(READ "@GIT_DATA@/head-ref" HEAD_HASH LIMIT 1024)
string(STRIP "${HEAD_HASH}" HEAD_HASH)
if(EXISTS "@GIT_DATA@/head-ref")
file(READ "@GIT_DATA@/head-ref" HEAD_HASH LIMIT 1024)
string(STRIP "${HEAD_HASH}" HEAD_HASH)
else()
set(HEAD_HASH "Unknown")
endif()
endif()

View File

@@ -143,7 +143,7 @@ struct AuxInfo {
std::array<u8, 24> output_mix_buffers;
u32_le mix_buffer_count;
u32_le sample_rate; // Stored in the aux buffer currently
u32_le sampe_count;
u32_le sample_count;
u64_le send_buffer_info;
u64_le send_buffer_base;

View File

@@ -117,21 +117,21 @@ private:
// We don't delete it because we want BitField to be trivially copyable.
constexpr BitField& operator=(const BitField&) = default;
// StorageType is T for non-enum types and the underlying type of T if
// UnderlyingType is T for non-enum types and the underlying type of T if
// T is an enumeration. Note that T is wrapped within an enable_if in the
// former case to workaround compile errors which arise when using
// std::underlying_type<T>::type directly.
using StorageType = typename std::conditional_t<std::is_enum<T>::value, std::underlying_type<T>,
std::enable_if<true, T>>::type;
using UnderlyingType = typename std::conditional_t<std::is_enum_v<T>, std::underlying_type<T>,
std::enable_if<true, T>>::type;
// Unsigned version of StorageType
using StorageTypeU = std::make_unsigned_t<StorageType>;
// We store the value as the unsigned type to avoid undefined behaviour on value shifting
using StorageType = std::make_unsigned_t<UnderlyingType>;
public:
/// Constants to allow limited introspection of fields if needed
static constexpr std::size_t position = Position;
static constexpr std::size_t bits = Bits;
static constexpr StorageType mask = (((StorageTypeU)~0) >> (8 * sizeof(T) - bits)) << position;
static constexpr StorageType mask = (((StorageType)~0) >> (8 * sizeof(T) - bits)) << position;
/**
* Formats a value by masking and shifting it according to the field parameters. A value
@@ -148,11 +148,12 @@ public:
* union in a constexpr context.
*/
static constexpr FORCE_INLINE T ExtractValue(const StorageType& storage) {
if (std::numeric_limits<T>::is_signed) {
if constexpr (std::numeric_limits<UnderlyingType>::is_signed) {
std::size_t shift = 8 * sizeof(T) - bits;
return (T)((storage << (shift - position)) >> shift);
return static_cast<T>(static_cast<UnderlyingType>(storage << (shift - position)) >>
shift);
} else {
return (T)((storage & mask) >> position);
return static_cast<T>((storage & mask) >> position);
}
}

View File

@@ -4,11 +4,10 @@
#include <algorithm>
#include <cctype>
#include <cerrno>
#include <codecvt>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <locale>
#include <sstream>
#include "common/common_paths.h"
#include "common/logging/log.h"
#include "common/string_util.h"
@@ -33,24 +32,6 @@ std::string ToUpper(std::string str) {
return str;
}
// For Debugging. Read out an u8 array.
std::string ArrayToString(const u8* data, std::size_t size, int line_len, bool spaces) {
std::ostringstream oss;
oss << std::setfill('0') << std::hex;
for (int line = 0; size; ++data, --size) {
oss << std::setw(2) << (int)*data;
if (line_len == ++line) {
oss << '\n';
line = 0;
} else if (spaces)
oss << ' ';
}
return oss.str();
}
std::string StringFromBuffer(const std::vector<u8>& data) {
return std::string(data.begin(), std::find(data.begin(), data.end(), '\0'));
}
@@ -75,40 +56,6 @@ std::string StripQuotes(const std::string& s) {
return s;
}
bool TryParse(const std::string& str, u32* const output) {
char* endptr = nullptr;
// Reset errno to a value other than ERANGE
errno = 0;
unsigned long value = strtoul(str.c_str(), &endptr, 0);
if (!endptr || *endptr)
return false;
if (errno == ERANGE)
return false;
#if ULONG_MAX > UINT_MAX
if (value >= 0x100000000ull && value <= 0xFFFFFFFF00000000ull)
return false;
#endif
*output = static_cast<u32>(value);
return true;
}
bool TryParse(const std::string& str, bool* const output) {
if ("1" == str || "true" == ToLower(str))
*output = true;
else if ("0" == str || "false" == ToLower(str))
*output = false;
else
return false;
return true;
}
std::string StringFromBool(bool value) {
return value ? "True" : "False";
}

View File

@@ -5,8 +5,6 @@
#pragma once
#include <cstddef>
#include <iomanip>
#include <sstream>
#include <string>
#include <vector>
#include "common/common_types.h"
@@ -19,44 +17,13 @@ std::string ToLower(std::string str);
/// Make a string uppercase
std::string ToUpper(std::string str);
std::string ArrayToString(const u8* data, std::size_t size, int line_len = 20, bool spaces = true);
std::string StringFromBuffer(const std::vector<u8>& data);
std::string StripSpaces(const std::string& s);
std::string StripQuotes(const std::string& s);
// Thousand separator. Turns 12345678 into 12,345,678
template <typename I>
std::string ThousandSeparate(I value, int spaces = 0) {
std::ostringstream oss;
// std::locale("") seems to be broken on many platforms
#if defined _WIN32 || (defined __linux__ && !defined __clang__)
oss.imbue(std::locale(""));
#endif
oss << std::setw(spaces) << value;
return oss.str();
}
std::string StringFromBool(bool value);
bool TryParse(const std::string& str, bool* output);
bool TryParse(const std::string& str, u32* output);
template <typename N>
static bool TryParse(const std::string& str, N* const output) {
std::istringstream iss(str);
N tmp = 0;
if (iss >> tmp) {
*output = tmp;
return true;
} else
return false;
}
std::string TabsToSpaces(int tab_size, std::string in);
void SplitString(const std::string& str, char delim, std::vector<std::string>& output);

View File

@@ -8,8 +8,9 @@
namespace FileSys {
BISFactory::BISFactory(VirtualDir nand_root_, VirtualDir load_root_)
BISFactory::BISFactory(VirtualDir nand_root_, VirtualDir load_root_, VirtualDir dump_root_)
: nand_root(std::move(nand_root_)), load_root(std::move(load_root_)),
dump_root(std::move(dump_root_)),
sysnand_cache(std::make_unique<RegisteredCache>(
GetOrCreateDirectoryRelative(nand_root, "/system/Contents/registered"))),
usrnand_cache(std::make_unique<RegisteredCache>(
@@ -32,4 +33,10 @@ VirtualDir BISFactory::GetModificationLoadRoot(u64 title_id) const {
return GetOrCreateDirectoryRelative(load_root, fmt::format("/{:016X}", title_id));
}
VirtualDir BISFactory::GetModificationDumpRoot(u64 title_id) const {
if (title_id == 0)
return nullptr;
return GetOrCreateDirectoryRelative(dump_root, fmt::format("/{:016X}", title_id));
}
} // namespace FileSys

View File

@@ -17,17 +17,19 @@ class RegisteredCache;
/// registered caches.
class BISFactory {
public:
explicit BISFactory(VirtualDir nand_root, VirtualDir load_root);
explicit BISFactory(VirtualDir nand_root, VirtualDir load_root, VirtualDir dump_root);
~BISFactory();
RegisteredCache* GetSystemNANDContents() const;
RegisteredCache* GetUserNANDContents() const;
VirtualDir GetModificationLoadRoot(u64 title_id) const;
VirtualDir GetModificationDumpRoot(u64 title_id) const;
private:
VirtualDir nand_root;
VirtualDir load_root;
VirtualDir dump_root;
std::unique_ptr<RegisteredCache> sysnand_cache;
std::unique_ptr<RegisteredCache> usrnand_cache;

View File

@@ -176,7 +176,7 @@ Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) {
for (const VirtualFile& file : partitions[static_cast<std::size_t>(part)]->GetFiles()) {
if (file->GetExtension() != "nca")
continue;
auto nca = std::make_shared<NCA>(file);
auto nca = std::make_shared<NCA>(file, nullptr, 0, keys);
// TODO(DarkLordZach): Add proper Rev1+ Support
if (nca->IsUpdate())
continue;

View File

@@ -9,6 +9,7 @@
#include <vector>
#include "common/common_types.h"
#include "common/swap.h"
#include "core/crypto/key_manager.h"
#include "core/file_sys/vfs.h"
namespace Loader {
@@ -107,5 +108,7 @@ private:
std::shared_ptr<NSP> secure_partition;
std::shared_ptr<NCA> program;
std::vector<std::shared_ptr<NCA>> ncas;
Core::Crypto::KeyManager keys;
};
} // namespace FileSys

View File

@@ -101,8 +101,9 @@ static bool IsValidNCA(const NCAHeader& header) {
return header.magic == Common::MakeMagic('N', 'C', 'A', '3');
}
NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_offset)
: file(std::move(file_)), bktr_base_romfs(std::move(bktr_base_romfs_)) {
NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_offset,
Core::Crypto::KeyManager keys_)
: file(std::move(file_)), bktr_base_romfs(std::move(bktr_base_romfs_)), keys(std::move(keys_)) {
if (file == nullptr) {
status = Loader::ResultStatus::ErrorNullFile;
return;

View File

@@ -79,7 +79,8 @@ inline bool IsDirectoryExeFS(const std::shared_ptr<VfsDirectory>& pfs) {
class NCA : public ReadOnlyVfsDirectory {
public:
explicit NCA(VirtualFile file, VirtualFile bktr_base_romfs = nullptr,
u64 bktr_base_ivfc_offset = 0);
u64 bktr_base_ivfc_offset = 0,
Core::Crypto::KeyManager keys = Core::Crypto::KeyManager());
~NCA() override;
Loader::ResultStatus GetStatus() const;

View File

@@ -66,4 +66,10 @@ std::string NACP::GetVersionString() const {
return Common::StringFromFixedZeroTerminatedBuffer(raw->version_string.data(),
raw->version_string.size());
}
std::vector<u8> NACP::GetRawBytes() const {
std::vector<u8> out(sizeof(RawNACP));
std::memcpy(out.data(), raw.get(), sizeof(RawNACP));
return out;
}
} // namespace FileSys

View File

@@ -81,6 +81,7 @@ public:
u64 GetTitleId() const;
u64 GetDLCBaseTitleId() const;
std::string GetVersionString() const;
std::vector<u8> GetRawBytes() const;
private:
std::unique_ptr<RawNACP> raw;

View File

@@ -8,25 +8,10 @@
namespace FileSys {
namespace ErrCodes {
enum {
NotFound = 1,
TitleNotFound = 1002,
SdCardNotFound = 2001,
RomFSNotFound = 2520,
};
}
constexpr ResultCode ERROR_PATH_NOT_FOUND(ErrorModule::FS, ErrCodes::NotFound);
// TODO(bunnei): Replace these with correct errors for Switch OS
constexpr ResultCode ERROR_INVALID_PATH(-1);
constexpr ResultCode ERROR_UNSUPPORTED_OPEN_FLAGS(-1);
constexpr ResultCode ERROR_INVALID_OPEN_FLAGS(-1);
constexpr ResultCode ERROR_FILE_NOT_FOUND(-1);
constexpr ResultCode ERROR_UNEXPECTED_FILE_OR_DIRECTORY(-1);
constexpr ResultCode ERROR_DIRECTORY_ALREADY_EXISTS(-1);
constexpr ResultCode ERROR_FILE_ALREADY_EXISTS(-1);
constexpr ResultCode ERROR_DIRECTORY_NOT_EMPTY(-1);
constexpr ResultCode ERROR_PATH_NOT_FOUND{ErrorModule::FS, 1};
constexpr ResultCode ERROR_ENTITY_NOT_FOUND{ErrorModule::FS, 1002};
constexpr ResultCode ERROR_SD_CARD_NOT_FOUND{ErrorModule::FS, 2001};
constexpr ResultCode ERROR_INVALID_OFFSET{ErrorModule::FS, 6061};
constexpr ResultCode ERROR_INVALID_SIZE{ErrorModule::FS, 6062};
} // namespace FileSys

View File

@@ -19,6 +19,7 @@
#include "core/file_sys/vfs_vector.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/loader/loader.h"
#include "core/settings.h"
namespace FileSys {
@@ -119,6 +120,18 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso) const {
const auto build_id_raw = Common::HexArrayToString(header.build_id);
const auto build_id = build_id_raw.substr(0, build_id_raw.find_last_not_of('0') + 1);
if (Settings::values.dump_nso) {
LOG_INFO(Loader, "Dumping NSO for build_id={}, title_id={:016X}", build_id, title_id);
const auto dump_dir = Service::FileSystem::GetModificationDumpRoot(title_id);
if (dump_dir != nullptr) {
const auto nso_dir = GetOrCreateDirectoryRelative(dump_dir, "/nso");
const auto file = nso_dir->CreateFile(fmt::format("{}.nso", build_id));
file->Resize(nso.size());
file->WriteBytes(nso);
}
}
LOG_INFO(Loader, "Patching NSO for build_id={}", build_id);
const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);

View File

@@ -106,9 +106,12 @@ static ContentRecordType GetCRTypeFromNCAType(NCAContentType type) {
VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& dir,
std::string_view path) const {
if (dir->GetFileRelative(path) != nullptr)
return dir->GetFileRelative(path);
if (dir->GetDirectoryRelative(path) != nullptr) {
const auto file = dir->GetFileRelative(path);
if (file != nullptr)
return file;
const auto nca_dir = dir->GetDirectoryRelative(path);
if (nca_dir != nullptr) {
const auto nca_dir = dir->GetDirectoryRelative(path);
VirtualFile file = nullptr;
@@ -225,7 +228,7 @@ void RegisteredCache::ProcessFiles(const std::vector<NcaID>& ids) {
if (file == nullptr)
continue;
const auto nca = std::make_shared<NCA>(parser(file, id));
const auto nca = std::make_shared<NCA>(parser(file, id), nullptr, 0, keys);
if (nca->GetStatus() != Loader::ResultStatus::Success ||
nca->GetType() != NCAContentType::Meta) {
continue;
@@ -315,7 +318,7 @@ std::unique_ptr<NCA> RegisteredCache::GetEntry(u64 title_id, ContentRecordType t
const auto raw = GetEntryRaw(title_id, type);
if (raw == nullptr)
return nullptr;
return std::make_unique<NCA>(raw);
return std::make_unique<NCA>(raw, nullptr, 0, keys);
}
std::unique_ptr<NCA> RegisteredCache::GetEntry(RegisteredCacheEntry entry) const {

View File

@@ -12,6 +12,7 @@
#include <vector>
#include <boost/container/flat_map.hpp>
#include "common/common_types.h"
#include "core/crypto/key_manager.h"
#include "core/file_sys/vfs.h"
namespace FileSys {
@@ -133,6 +134,8 @@ private:
VirtualDir dir;
RegisteredCacheParsingFunction parser;
Core::Crypto::KeyManager keys;
// maps tid -> NcaID of meta
boost::container::flat_map<u64, NcaID> meta_id;
// maps tid -> meta

View File

@@ -83,6 +83,24 @@ ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space, SaveDataDescr
return MakeResult<VirtualDir>(std::move(out));
}
VirtualDir SaveDataFactory::GetSaveDataSpaceDirectory(SaveDataSpaceId space) const {
return dir->GetDirectoryRelative(GetSaveDataSpaceIdPath(space));
}
std::string SaveDataFactory::GetSaveDataSpaceIdPath(SaveDataSpaceId space) {
switch (space) {
case SaveDataSpaceId::NandSystem:
return "/system/";
case SaveDataSpaceId::NandUser:
return "/user/";
case SaveDataSpaceId::TemporaryStorage:
return "/temp/";
default:
ASSERT_MSG(false, "Unrecognized SaveDataSpaceId: {:02X}", static_cast<u8>(space));
return "/unrecognized/"; ///< To prevent corruption when ignoring asserts.
}
}
std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType type, u64 title_id,
u128 user_id, u64 save_id) {
// According to switchbrew, if a save is of type SaveData and the title id field is 0, it should
@@ -90,21 +108,7 @@ std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType typ
if (type == SaveDataType::SaveData && title_id == 0)
title_id = Core::CurrentProcess()->GetTitleID();
std::string out;
switch (space) {
case SaveDataSpaceId::NandSystem:
out = "/system/";
break;
case SaveDataSpaceId::NandUser:
out = "/user/";
break;
case SaveDataSpaceId::TemporaryStorage:
out = "/temp/";
break;
default:
ASSERT_MSG(false, "Unrecognized SaveDataSpaceId: {:02X}", static_cast<u8>(space));
}
std::string out = GetSaveDataSpaceIdPath(space);
switch (type) {
case SaveDataType::SystemSaveData:

View File

@@ -52,6 +52,9 @@ public:
ResultVal<VirtualDir> Open(SaveDataSpaceId space, SaveDataDescriptor meta);
VirtualDir GetSaveDataSpaceDirectory(SaveDataSpaceId space) const;
static std::string GetSaveDataSpaceIdPath(SaveDataSpaceId space);
static std::string GetFullPath(SaveDataSpaceId space, SaveDataType type, u64 title_id,
u128 user_id, u64 save_id);

View File

@@ -252,7 +252,7 @@ void NSP::ReadNCAs(const std::vector<VirtualFile>& files) {
continue;
}
auto next_nca = std::make_shared<NCA>(next_file);
auto next_nca = std::make_shared<NCA>(next_file, nullptr, 0, keys);
if (next_nca->GetType() == NCAContentType::Program)
program_status[cnmt.GetTitleID()] = next_nca->GetStatus();
if (next_nca->GetStatus() == Loader::ResultStatus::Success ||

View File

@@ -70,6 +70,8 @@ private:
std::map<u64, std::map<ContentRecordType, std::shared_ptr<NCA>>> ncas;
std::vector<VirtualFile> ticket_files;
Core::Crypto::KeyManager keys;
VirtualFile romfs;
VirtualDir exefs;
};

View File

@@ -132,4 +132,11 @@ using MotionDevice = InputDevice<std::tuple<Math::Vec3<float>, Math::Vec3<float>
*/
using TouchDevice = InputDevice<std::tuple<float, float, bool>>;
/**
* A mouse device is an input device that returns a tuple of two floats and four ints.
* The first two floats are X and Y device coordinates of the mouse (from 0-1).
* The s32s are the mouse wheel.
*/
using MouseDevice = InputDevice<std::tuple<float, float, s32, s32>>;
} // namespace Input

View File

@@ -8,58 +8,28 @@
namespace Kernel {
namespace ErrCodes {
enum {
// Confirmed Switch OS error codes
MaxConnectionsReached = 7,
InvalidSize = 101,
InvalidAddress = 102,
HandleTableFull = 105,
InvalidMemoryState = 106,
InvalidMemoryPermissions = 108,
InvalidMemoryRange = 110,
InvalidThreadPriority = 112,
InvalidProcessorId = 113,
InvalidHandle = 114,
InvalidPointer = 115,
InvalidCombination = 116,
Timeout = 117,
SynchronizationCanceled = 118,
TooLarge = 119,
InvalidEnumValue = 120,
NoSuchEntry = 121,
AlreadyRegistered = 122,
SessionClosed = 123,
InvalidState = 125,
ResourceLimitExceeded = 132,
};
}
// Confirmed Switch kernel error codes
// WARNING: The kernel is quite inconsistent in it's usage of errors code. Make sure to always
// double check that the code matches before re-using the constant.
constexpr ResultCode ERR_HANDLE_TABLE_FULL(ErrorModule::Kernel, ErrCodes::HandleTableFull);
constexpr ResultCode ERR_SESSION_CLOSED_BY_REMOTE(ErrorModule::Kernel, ErrCodes::SessionClosed);
constexpr ResultCode ERR_PORT_NAME_TOO_LONG(ErrorModule::Kernel, ErrCodes::TooLarge);
constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED(ErrorModule::Kernel,
ErrCodes::MaxConnectionsReached);
constexpr ResultCode ERR_INVALID_ENUM_VALUE(ErrorModule::Kernel, ErrCodes::InvalidEnumValue);
constexpr ResultCode ERR_INVALID_COMBINATION_KERNEL(ErrorModule::Kernel,
ErrCodes::InvalidCombination);
constexpr ResultCode ERR_INVALID_ADDRESS(ErrorModule::Kernel, ErrCodes::InvalidAddress);
constexpr ResultCode ERR_INVALID_ADDRESS_STATE(ErrorModule::Kernel, ErrCodes::InvalidMemoryState);
constexpr ResultCode ERR_INVALID_MEMORY_PERMISSIONS(ErrorModule::Kernel,
ErrCodes::InvalidMemoryPermissions);
constexpr ResultCode ERR_INVALID_MEMORY_RANGE(ErrorModule::Kernel, ErrCodes::InvalidMemoryRange);
constexpr ResultCode ERR_INVALID_HANDLE(ErrorModule::Kernel, ErrCodes::InvalidHandle);
constexpr ResultCode ERR_INVALID_PROCESSOR_ID(ErrorModule::Kernel, ErrCodes::InvalidProcessorId);
constexpr ResultCode ERR_INVALID_SIZE(ErrorModule::Kernel, ErrCodes::InvalidSize);
constexpr ResultCode ERR_ALREADY_REGISTERED(ErrorModule::Kernel, ErrCodes::AlreadyRegistered);
constexpr ResultCode ERR_INVALID_STATE(ErrorModule::Kernel, ErrCodes::InvalidState);
constexpr ResultCode ERR_INVALID_THREAD_PRIORITY(ErrorModule::Kernel,
ErrCodes::InvalidThreadPriority);
constexpr ResultCode ERR_INVALID_POINTER(ErrorModule::Kernel, ErrCodes::InvalidPointer);
constexpr ResultCode ERR_NOT_FOUND(ErrorModule::Kernel, ErrCodes::NoSuchEntry);
constexpr ResultCode RESULT_TIMEOUT(ErrorModule::Kernel, ErrCodes::Timeout);
constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED{ErrorModule::Kernel, 7};
constexpr ResultCode ERR_INVALID_SIZE{ErrorModule::Kernel, 101};
constexpr ResultCode ERR_INVALID_ADDRESS{ErrorModule::Kernel, 102};
constexpr ResultCode ERR_HANDLE_TABLE_FULL{ErrorModule::Kernel, 105};
constexpr ResultCode ERR_INVALID_ADDRESS_STATE{ErrorModule::Kernel, 106};
constexpr ResultCode ERR_INVALID_MEMORY_PERMISSIONS{ErrorModule::Kernel, 108};
constexpr ResultCode ERR_INVALID_MEMORY_RANGE{ErrorModule::Kernel, 110};
constexpr ResultCode ERR_INVALID_PROCESSOR_ID{ErrorModule::Kernel, 113};
constexpr ResultCode ERR_INVALID_THREAD_PRIORITY{ErrorModule::Kernel, 112};
constexpr ResultCode ERR_INVALID_HANDLE{ErrorModule::Kernel, 114};
constexpr ResultCode ERR_INVALID_POINTER{ErrorModule::Kernel, 115};
constexpr ResultCode ERR_INVALID_COMBINATION{ErrorModule::Kernel, 116};
constexpr ResultCode RESULT_TIMEOUT{ErrorModule::Kernel, 117};
constexpr ResultCode ERR_SYNCHRONIZATION_CANCELED{ErrorModule::Kernel, 118};
constexpr ResultCode ERR_OUT_OF_RANGE{ErrorModule::Kernel, 119};
constexpr ResultCode ERR_INVALID_ENUM_VALUE{ErrorModule::Kernel, 120};
constexpr ResultCode ERR_NOT_FOUND{ErrorModule::Kernel, 121};
constexpr ResultCode ERR_ALREADY_REGISTERED{ErrorModule::Kernel, 122};
constexpr ResultCode ERR_SESSION_CLOSED_BY_REMOTE{ErrorModule::Kernel, 123};
constexpr ResultCode ERR_INVALID_STATE{ErrorModule::Kernel, 125};
constexpr ResultCode ERR_RESOURCE_LIMIT_EXCEEDED{ErrorModule::Kernel, 132};
} // namespace Kernel

View File

@@ -5,11 +5,9 @@
#include <algorithm>
#include <memory>
#include "common/assert.h"
#include "common/common_funcs.h"
#include "common/logging/log.h"
#include "core/core.h"
#include "core/file_sys/program_metadata.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/resource_limit.h"
@@ -17,6 +15,7 @@
#include "core/hle/kernel/thread.h"
#include "core/hle/kernel/vm_manager.h"
#include "core/memory.h"
#include "core/settings.h"
namespace Kernel {
@@ -35,6 +34,11 @@ SharedPtr<Process> Process::Create(KernelCore& kernel, std::string&& name) {
process->process_id = kernel.CreateNewProcessID();
process->svc_access_mask.set();
std::mt19937 rng(Settings::values.rng_seed.value_or(0));
std::uniform_int_distribution<u64> distribution;
std::generate(process->random_entropy.begin(), process->random_entropy.end(),
[&] { return distribution(rng); });
kernel.AppendNewProcess(process);
return process;
}
@@ -241,83 +245,15 @@ void Process::LoadModule(CodeSet module_, VAddr base_addr) {
}
ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission perms) {
if (target < vm_manager.GetHeapRegionBaseAddress() ||
target + size > vm_manager.GetHeapRegionEndAddress() || target + size < target) {
return ERR_INVALID_ADDRESS;
}
if (heap_memory == nullptr) {
// Initialize heap
heap_memory = std::make_shared<std::vector<u8>>();
heap_start = heap_end = target;
} else {
vm_manager.UnmapRange(heap_start, heap_end - heap_start);
}
// If necessary, expand backing vector to cover new heap extents.
if (target < heap_start) {
heap_memory->insert(begin(*heap_memory), heap_start - target, 0);
heap_start = target;
vm_manager.RefreshMemoryBlockMappings(heap_memory.get());
}
if (target + size > heap_end) {
heap_memory->insert(end(*heap_memory), (target + size) - heap_end, 0);
heap_end = target + size;
vm_manager.RefreshMemoryBlockMappings(heap_memory.get());
}
ASSERT(heap_end - heap_start == heap_memory->size());
CASCADE_RESULT(auto vma, vm_manager.MapMemoryBlock(target, heap_memory, target - heap_start,
size, MemoryState::Heap));
vm_manager.Reprotect(vma, perms);
heap_used = size;
return MakeResult<VAddr>(heap_end - size);
return vm_manager.HeapAllocate(target, size, perms);
}
ResultCode Process::HeapFree(VAddr target, u32 size) {
if (target < vm_manager.GetHeapRegionBaseAddress() ||
target + size > vm_manager.GetHeapRegionEndAddress() || target + size < target) {
return ERR_INVALID_ADDRESS;
}
if (size == 0) {
return RESULT_SUCCESS;
}
ResultCode result = vm_manager.UnmapRange(target, size);
if (result.IsError())
return result;
heap_used -= size;
return RESULT_SUCCESS;
return vm_manager.HeapFree(target, size);
}
ResultCode Process::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size) {
auto vma = vm_manager.FindVMA(src_addr);
ASSERT_MSG(vma != vm_manager.vma_map.end(), "Invalid memory address");
ASSERT_MSG(vma->second.backing_block, "Backing block doesn't exist for address");
// The returned VMA might be a bigger one encompassing the desired address.
auto vma_offset = src_addr - vma->first;
ASSERT_MSG(vma_offset + size <= vma->second.size,
"Shared memory exceeds bounds of mapped block");
const std::shared_ptr<std::vector<u8>>& backing_block = vma->second.backing_block;
std::size_t backing_block_offset = vma->second.offset + vma_offset;
CASCADE_RESULT(auto new_vma,
vm_manager.MapMemoryBlock(dst_addr, backing_block, backing_block_offset, size,
MemoryState::Mapped));
// Protect mirror with permissions from old region
vm_manager.Reprotect(new_vma, vma->second.permissions);
// Remove permissions from old region
vm_manager.Reprotect(vma, VMAPermission::None);
return RESULT_SUCCESS;
ResultCode Process::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size, MemoryState state) {
return vm_manager.MirrorMemory(dst_addr, src_addr, size, state);
}
ResultCode Process::UnmapMemory(VAddr dst_addr, VAddr /*src_addr*/, u64 size) {

View File

@@ -8,6 +8,7 @@
#include <bitset>
#include <cstddef>
#include <memory>
#include <random>
#include <string>
#include <vector>
#include <boost/container/static_vector.hpp>
@@ -119,6 +120,8 @@ struct CodeSet final {
class Process final : public Object {
public:
static constexpr std::size_t RANDOM_ENTROPY_SIZE = 4;
static SharedPtr<Process> Create(KernelCore& kernel, std::string&& name);
std::string GetTypeName() const override {
@@ -212,6 +215,11 @@ public:
total_process_running_time_ticks += ticks;
}
/// Gets 8 bytes of random data for svcGetInfo RandomEntropy
u64 GetRandomEntropy(std::size_t index) const {
return random_entropy.at(index);
}
/**
* Loads process-specifics configuration info with metadata provided
* by an executable.
@@ -251,7 +259,8 @@ public:
ResultVal<VAddr> HeapAllocate(VAddr target, u64 size, VMAPermission perms);
ResultCode HeapFree(VAddr target, u32 size);
ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size);
ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size,
MemoryState state = MemoryState::Mapped);
ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size);
@@ -292,17 +301,6 @@ private:
u32 allowed_thread_priority_mask = 0xFFFFFFFF;
u32 is_virtual_address_memory_enabled = 0;
// Memory used to back the allocations in the regular heap. A single vector is used to cover
// the entire virtual address space extents that bound the allocations, including any holes.
// This makes deallocation and reallocation of holes fast and keeps process memory contiguous
// in the emulator address space, allowing Memory::GetPointer to be reasonably safe.
std::shared_ptr<std::vector<u8>> heap_memory;
// The left/right bounds of the address space covered by heap_memory.
VAddr heap_start = 0;
VAddr heap_end = 0;
u64 heap_used = 0;
/// The Thread Local Storage area is allocated as processes create threads,
/// each TLS area is 0x200 bytes, so one page (0x1000) is split up in 8 parts, and each part
/// holds the TLS for a specific thread. This vector contains which parts are in use for each
@@ -321,6 +319,9 @@ private:
/// Per-process handle table for storing created object handles in.
HandleTable handle_table;
/// Random values for svcGetInfo RandomEntropy
std::array<u64, RANDOM_ENTROPY_SIZE> random_entropy;
std::string name;
};

View File

@@ -34,6 +34,7 @@
#include "core/hle/lock.h"
#include "core/hle/result.h"
#include "core/hle/service/service.h"
#include "core/settings.h"
namespace Kernel {
namespace {
@@ -122,6 +123,48 @@ static ResultCode SetHeapSize(VAddr* heap_addr, u64 heap_size) {
return RESULT_SUCCESS;
}
static ResultCode SetMemoryPermission(VAddr addr, u64 size, u32 prot) {
LOG_TRACE(Kernel_SVC, "called, addr=0x{:X}, size=0x{:X}, prot=0x{:X}", addr, size, prot);
if (!Common::Is4KBAligned(addr)) {
return ERR_INVALID_ADDRESS;
}
if (size == 0 || !Common::Is4KBAligned(size)) {
return ERR_INVALID_SIZE;
}
if (!IsValidAddressRange(addr, size)) {
return ERR_INVALID_ADDRESS_STATE;
}
const auto permission = static_cast<MemoryPermission>(prot);
if (permission != MemoryPermission::None && permission != MemoryPermission::Read &&
permission != MemoryPermission::ReadWrite) {
return ERR_INVALID_MEMORY_PERMISSIONS;
}
auto* const current_process = Core::CurrentProcess();
auto& vm_manager = current_process->VMManager();
if (!IsInsideAddressSpace(vm_manager, addr, size)) {
return ERR_INVALID_ADDRESS_STATE;
}
const VMManager::VMAHandle iter = vm_manager.FindVMA(addr);
if (iter == vm_manager.vma_map.end()) {
return ERR_INVALID_ADDRESS_STATE;
}
LOG_WARNING(Kernel_SVC, "Uniformity check on protected memory is not implemented.");
// TODO: Performs a uniformity check to make sure only protected memory is changed (it doesn't
// make sense to allow changing permissions on kernel memory itself, etc).
const auto converted_permissions = SharedMemory::ConvertPermissions(permission);
return vm_manager.ReprotectRange(addr, size, converted_permissions);
}
static ResultCode SetMemoryAttribute(VAddr addr, u64 size, u32 state0, u32 state1) {
LOG_WARNING(Kernel_SVC,
"(STUBBED) called, addr=0x{:X}, size=0x{:X}, state0=0x{:X}, state1=0x{:X}", addr,
@@ -171,7 +214,7 @@ static ResultCode ConnectToNamedPort(Handle* out_handle, VAddr port_name_address
// Read 1 char beyond the max allowed port name to detect names that are too long.
std::string port_name = Memory::ReadCString(port_name_address, PortNameMaxLength + 1);
if (port_name.size() > PortNameMaxLength) {
return ERR_PORT_NAME_TOO_LONG;
return ERR_OUT_OF_RANGE;
}
LOG_TRACE(Kernel_SVC, "called port_name={}", port_name);
@@ -267,8 +310,9 @@ static ResultCode WaitSynchronization(Handle* index, VAddr handles_address, u64
static constexpr u64 MaxHandles = 0x40;
if (handle_count > MaxHandles)
return ResultCode(ErrorModule::Kernel, ErrCodes::TooLarge);
if (handle_count > MaxHandles) {
return ERR_OUT_OF_RANGE;
}
auto* const thread = GetCurrentThread();
@@ -333,8 +377,7 @@ static ResultCode CancelSynchronization(Handle thread_handle) {
}
ASSERT(thread->GetStatus() == ThreadStatus::WaitSynchAny);
thread->SetWaitSynchronizationResult(
ResultCode(ErrorModule::Kernel, ErrCodes::SynchronizationCanceled));
thread->SetWaitSynchronizationResult(ERR_SYNCHRONIZATION_CANCELED);
thread->ResumeFromWait();
return RESULT_SUCCESS;
}
@@ -558,7 +601,16 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
*result = 0;
break;
case GetInfoType::RandomEntropy:
*result = 0;
if (handle != 0) {
return ERR_INVALID_HANDLE;
}
if (info_sub_id >= Process::RANDOM_ENTROPY_SIZE) {
return ERR_INVALID_COMBINATION;
}
*result = current_process->GetRandomEntropy(info_sub_id);
return RESULT_SUCCESS;
break;
case GetInfoType::ASLRRegionBaseAddr:
*result = vm_manager.GetASLRRegionBaseAddress();
@@ -591,7 +643,7 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
case GetInfoType::ThreadTickCount: {
constexpr u64 num_cpus = 4;
if (info_sub_id != 0xFFFFFFFFFFFFFFFF && info_sub_id >= num_cpus) {
return ERR_INVALID_COMBINATION_KERNEL;
return ERR_INVALID_COMBINATION;
}
const auto thread =
@@ -1129,7 +1181,7 @@ static ResultCode CloseHandle(Handle handle) {
/// Reset an event
static ResultCode ResetSignal(Handle handle) {
LOG_WARNING(Kernel_SVC, "(STUBBED) called handle 0x{:08X}", handle);
LOG_DEBUG(Kernel_SVC, "called handle 0x{:08X}", handle);
const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
auto event = handle_table.Get<Event>(handle);
@@ -1184,7 +1236,7 @@ static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) {
}
if (mask == 0) {
return ResultCode(ErrorModule::Kernel, ErrCodes::InvalidCombination);
return ERR_INVALID_COMBINATION;
}
/// This value is used to only change the affinity mask without changing the current ideal core.
@@ -1193,12 +1245,12 @@ static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) {
if (core == OnlyChangeMask) {
core = thread->GetIdealCore();
} else if (core >= Core::NUM_CPU_CORES && core != static_cast<u32>(-1)) {
return ResultCode(ErrorModule::Kernel, ErrCodes::InvalidProcessorId);
return ERR_INVALID_PROCESSOR_ID;
}
// Error out if the input core isn't enabled in the input mask.
if (core < Core::NUM_CPU_CORES && (mask & (1ull << core)) == 0) {
return ResultCode(ErrorModule::Kernel, ErrCodes::InvalidCombination);
return ERR_INVALID_COMBINATION;
}
thread->ChangeCore(core, mask);
@@ -1287,7 +1339,7 @@ struct FunctionDef {
static const FunctionDef SVC_Table[] = {
{0x00, nullptr, "Unknown"},
{0x01, SvcWrap<SetHeapSize>, "SetHeapSize"},
{0x02, nullptr, "SetMemoryPermission"},
{0x02, SvcWrap<SetMemoryPermission>, "SetMemoryPermission"},
{0x03, SvcWrap<SetMemoryAttribute>, "SetMemoryAttribute"},
{0x04, SvcWrap<MapMemory>, "MapMemory"},
{0x05, SvcWrap<UnmapMemory>, "UnmapMemory"},

View File

@@ -121,6 +121,11 @@ void SvcWrap() {
FuncReturn(func(Param(0), Param(1), Param(2)).raw);
}
template <ResultCode func(u64, u64, u32)>
void SvcWrap() {
FuncReturn(func(Param(0), Param(1), static_cast<u32>(Param(2))).raw);
}
template <ResultCode func(u32, u64, u64, u32)>
void SvcWrap() {
FuncReturn(

View File

@@ -142,36 +142,7 @@ void Thread::ResumeFromWait() {
status = ThreadStatus::Ready;
std::optional<s32> new_processor_id = GetNextProcessorId(affinity_mask);
if (!new_processor_id) {
new_processor_id = processor_id;
}
if (ideal_core != -1 &&
Core::System::GetInstance().Scheduler(ideal_core).GetCurrentThread() == nullptr) {
new_processor_id = ideal_core;
}
ASSERT(*new_processor_id < 4);
// Add thread to new core's scheduler
auto* next_scheduler = &Core::System::GetInstance().Scheduler(*new_processor_id);
if (*new_processor_id != processor_id) {
// Remove thread from previous core's scheduler
scheduler->RemoveThread(this);
next_scheduler->AddThread(this, current_priority);
}
processor_id = *new_processor_id;
// If the thread was ready, unschedule from the previous core and schedule on the new core
scheduler->UnscheduleThread(this, current_priority);
next_scheduler->ScheduleThread(this, current_priority);
// Change thread's scheduler
scheduler = next_scheduler;
Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule();
ChangeScheduler();
}
/**
@@ -364,42 +335,45 @@ void Thread::UpdatePriority() {
void Thread::ChangeCore(u32 core, u64 mask) {
ideal_core = core;
affinity_mask = mask;
ChangeScheduler();
}
void Thread::ChangeScheduler() {
if (status != ThreadStatus::Ready) {
return;
}
auto& system = Core::System::GetInstance();
std::optional<s32> new_processor_id{GetNextProcessorId(affinity_mask)};
if (!new_processor_id) {
new_processor_id = processor_id;
}
if (ideal_core != -1 &&
Core::System::GetInstance().Scheduler(ideal_core).GetCurrentThread() == nullptr) {
if (ideal_core != -1 && system.Scheduler(ideal_core).GetCurrentThread() == nullptr) {
new_processor_id = ideal_core;
}
ASSERT(*new_processor_id < 4);
// Add thread to new core's scheduler
auto* next_scheduler = &Core::System::GetInstance().Scheduler(*new_processor_id);
auto& next_scheduler = system.Scheduler(*new_processor_id);
if (*new_processor_id != processor_id) {
// Remove thread from previous core's scheduler
scheduler->RemoveThread(this);
next_scheduler->AddThread(this, current_priority);
next_scheduler.AddThread(this, current_priority);
}
processor_id = *new_processor_id;
// If the thread was ready, unschedule from the previous core and schedule on the new core
scheduler->UnscheduleThread(this, current_priority);
next_scheduler->ScheduleThread(this, current_priority);
next_scheduler.ScheduleThread(this, current_priority);
// Change thread's scheduler
scheduler = next_scheduler;
scheduler = &next_scheduler;
Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule();
system.CpuCore(processor_id).PrepareReschedule();
}
bool Thread::AllWaitObjectsReady() {

View File

@@ -374,6 +374,8 @@ private:
explicit Thread(KernelCore& kernel);
~Thread() override;
void ChangeScheduler();
Core::ARM_Interface::ThreadContext context{};
u32 thread_id = 0;

View File

@@ -243,6 +243,85 @@ ResultCode VMManager::ReprotectRange(VAddr target, u64 size, VMAPermission new_p
return RESULT_SUCCESS;
}
ResultVal<VAddr> VMManager::HeapAllocate(VAddr target, u64 size, VMAPermission perms) {
if (target < GetHeapRegionBaseAddress() || target + size > GetHeapRegionEndAddress() ||
target + size < target) {
return ERR_INVALID_ADDRESS;
}
if (heap_memory == nullptr) {
// Initialize heap
heap_memory = std::make_shared<std::vector<u8>>();
heap_start = heap_end = target;
} else {
UnmapRange(heap_start, heap_end - heap_start);
}
// If necessary, expand backing vector to cover new heap extents.
if (target < heap_start) {
heap_memory->insert(begin(*heap_memory), heap_start - target, 0);
heap_start = target;
RefreshMemoryBlockMappings(heap_memory.get());
}
if (target + size > heap_end) {
heap_memory->insert(end(*heap_memory), (target + size) - heap_end, 0);
heap_end = target + size;
RefreshMemoryBlockMappings(heap_memory.get());
}
ASSERT(heap_end - heap_start == heap_memory->size());
CASCADE_RESULT(auto vma, MapMemoryBlock(target, heap_memory, target - heap_start, size,
MemoryState::Heap));
Reprotect(vma, perms);
heap_used = size;
return MakeResult<VAddr>(heap_end - size);
}
ResultCode VMManager::HeapFree(VAddr target, u64 size) {
if (target < GetHeapRegionBaseAddress() || target + size > GetHeapRegionEndAddress() ||
target + size < target) {
return ERR_INVALID_ADDRESS;
}
if (size == 0) {
return RESULT_SUCCESS;
}
const ResultCode result = UnmapRange(target, size);
if (result.IsError()) {
return result;
}
heap_used -= size;
return RESULT_SUCCESS;
}
ResultCode VMManager::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size, MemoryState state) {
const auto vma = FindVMA(src_addr);
ASSERT_MSG(vma != vma_map.end(), "Invalid memory address");
ASSERT_MSG(vma->second.backing_block, "Backing block doesn't exist for address");
// The returned VMA might be a bigger one encompassing the desired address.
const auto vma_offset = src_addr - vma->first;
ASSERT_MSG(vma_offset + size <= vma->second.size,
"Shared memory exceeds bounds of mapped block");
const std::shared_ptr<std::vector<u8>>& backing_block = vma->second.backing_block;
const std::size_t backing_block_offset = vma->second.offset + vma_offset;
CASCADE_RESULT(auto new_vma,
MapMemoryBlock(dst_addr, backing_block, backing_block_offset, size, state));
// Protect mirror with permissions from old region
Reprotect(new_vma, vma->second.permissions);
// Remove permissions from old region
Reprotect(vma, VMAPermission::None);
return RESULT_SUCCESS;
}
void VMManager::RefreshMemoryBlockMappings(const std::vector<u8>* block) {
// If this ever proves to have a noticeable performance impact, allow users of the function to
// specify a specific range of addresses to limit the scan to.
@@ -495,8 +574,7 @@ u64 VMManager::GetTotalMemoryUsage() const {
}
u64 VMManager::GetTotalHeapUsage() const {
LOG_WARNING(Kernel, "(STUBBED) called");
return 0x0;
return heap_used;
}
VAddr VMManager::GetAddressSpaceBaseAddress() const {

View File

@@ -186,6 +186,12 @@ public:
/// Changes the permissions of a range of addresses, splitting VMAs as necessary.
ResultCode ReprotectRange(VAddr target, u64 size, VMAPermission new_perms);
ResultVal<VAddr> HeapAllocate(VAddr target, u64 size, VMAPermission perms);
ResultCode HeapFree(VAddr target, u64 size);
ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size,
MemoryState state = MemoryState::Mapped);
/**
* Scans all VMAs and updates the page table range of any that use the given vector as backing
* memory. This should be called after any operation that causes reallocation of the vector.
@@ -343,5 +349,15 @@ private:
VAddr tls_io_region_base = 0;
VAddr tls_io_region_end = 0;
// Memory used to back the allocations in the regular heap. A single vector is used to cover
// the entire virtual address space extents that bound the allocations, including any holes.
// This makes deallocation and reallocation of holes fast and keeps process memory contiguous
// in the emulator address space, allowing Memory::GetPointer to be reasonably safe.
std::shared_ptr<std::vector<u8>> heap_memory;
// The left/right bounds of the address space covered by heap_memory.
VAddr heap_start = 0;
VAddr heap_end = 0;
u64 heap_used = 0;
};
} // namespace Kernel

View File

@@ -19,8 +19,6 @@
enum class ErrorDescription : u32 {
Success = 0,
RemoteProcessDead = 301,
InvalidOffset = 6061,
InvalidLength = 6062,
};
/**

View File

@@ -252,8 +252,10 @@ void Module::Interface::TrySelectUserWithoutInteraction(Kernel::HLERequestContex
rb.PushRaw<u128>(INVALID_UUID);
return;
}
auto user_list = profile_manager->GetAllUsers();
if (user_list.empty()) {
const auto user_list = profile_manager->GetAllUsers();
if (std::all_of(user_list.begin(), user_list.end(),
[](const auto& user) { return user.uuid == INVALID_UUID; })) {
rb.Push(ResultCode(-1)); // TODO(ogniK): Find the correct error code
rb.PushRaw<u128>(INVALID_UUID);
return;

View File

@@ -2,8 +2,11 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <cstring>
#include <random>
#include <fmt/format.h>
#include "common/file_util.h"
#include "core/hle/service/acc/profile_manager.h"
#include "core/settings.h"
@@ -39,6 +42,19 @@ UUID UUID::Generate() {
return UUID{distribution(gen), distribution(gen)};
}
std::string UUID::Format() const {
return fmt::format("0x{:016X}{:016X}", uuid[1], uuid[0]);
}
std::string UUID::FormatSwitch() const {
std::array<u8, 16> s{};
std::memcpy(s.data(), uuid.data(), sizeof(u128));
return fmt::format("{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{"
":02x}{:02x}{:02x}{:02x}{:02x}",
s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7], s[8], s[9], s[10], s[11],
s[12], s[13], s[14], s[15]);
}
ProfileManager::ProfileManager() {
ParseUserSaveFile();
@@ -325,11 +341,12 @@ void ProfileManager::ParseUserSaveFile() {
return;
}
for (std::size_t i = 0; i < MAX_USERS; ++i) {
const auto& user = data.users[i];
for (const auto& user : data.users) {
if (user.uuid == UUID(INVALID_UUID)) {
continue;
}
if (user.uuid != UUID(INVALID_UUID))
AddUser({user.uuid, user.username, user.timestamp, {}, false});
AddUser({user.uuid, user.username, user.timestamp, {}, false});
}
std::stable_partition(profiles.begin(), profiles.end(),

View File

@@ -42,18 +42,9 @@ struct UUID {
void Invalidate() {
uuid = INVALID_UUID;
}
std::string Format() const {
return fmt::format("0x{:016X}{:016X}", uuid[1], uuid[0]);
}
std::string FormatSwitch() const {
std::array<u8, 16> s{};
std::memcpy(s.data(), uuid.data(), sizeof(u128));
return fmt::format("{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{"
":02x}{:02x}{:02x}{:02x}{:02x}",
s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7], s[8], s[9], s[10], s[11],
s[12], s[13], s[14], s[15]);
}
std::string Format() const;
std::string FormatSwitch() const;
};
static_assert(sizeof(UUID) == 16, "UUID is an invalid size!");

View File

@@ -203,8 +203,8 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger
ISelfController::~ISelfController() = default;
void ISelfController::SetFocusHandlingMode(Kernel::HLERequestContext& ctx) {
// Takes 3 input u8s with each field located immediately after the previous u8, these are
// bool flags. No output.
// Takes 3 input u8s with each field located immediately after the previous
// u8, these are bool flags. No output.
IPC::RequestParser rp{ctx};
@@ -258,8 +258,8 @@ void ISelfController::SetOperationModeChangedNotification(Kernel::HLERequestCont
}
void ISelfController::SetOutOfFocusSuspendingEnabled(Kernel::HLERequestContext& ctx) {
// Takes 3 input u8s with each field located immediately after the previous u8, these are
// bool flags. No output.
// Takes 3 input u8s with each field located immediately after the previous
// u8, these are bool flags. No output.
IPC::RequestParser rp{ctx};
bool enabled = rp.Pop<bool>();
@@ -302,8 +302,8 @@ void ISelfController::SetScreenShotImageOrientation(Kernel::HLERequestContext& c
}
void ISelfController::CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx) {
// TODO(Subv): Find out how AM determines the display to use, for now just create the layer
// in the Default display.
// TODO(Subv): Find out how AM determines the display to use, for now just
// create the layer in the Default display.
u64 display_id = nvflinger->OpenDisplay("Default");
u64 layer_id = nvflinger->CreateLayer(display_id);
@@ -338,7 +338,54 @@ void ISelfController::GetIdleTimeDetectionExtension(Kernel::HLERequestContext& c
LOG_WARNING(Service_AM, "(STUBBED) called");
}
ICommonStateGetter::ICommonStateGetter() : ServiceFramework("ICommonStateGetter") {
AppletMessageQueue::AppletMessageQueue() {
auto& kernel = Core::System::GetInstance().Kernel();
on_new_message = Kernel::Event::Create(kernel, Kernel::ResetType::Sticky,
"AMMessageQueue:OnMessageRecieved");
on_operation_mode_changed = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot,
"AMMessageQueue:OperationModeChanged");
}
AppletMessageQueue::~AppletMessageQueue() = default;
const Kernel::SharedPtr<Kernel::Event>& AppletMessageQueue::GetMesssageRecieveEvent() const {
return on_new_message;
}
const Kernel::SharedPtr<Kernel::Event>& AppletMessageQueue::GetOperationModeChangedEvent() const {
return on_operation_mode_changed;
}
void AppletMessageQueue::PushMessage(AppletMessage msg) {
messages.push(msg);
on_new_message->Signal();
}
AppletMessageQueue::AppletMessage AppletMessageQueue::PopMessage() {
if (messages.empty()) {
on_new_message->Clear();
return AppletMessage::NoMessage;
}
auto msg = messages.front();
messages.pop();
if (messages.empty()) {
on_new_message->Clear();
}
return msg;
}
std::size_t AppletMessageQueue::GetMessageCount() const {
return messages.size();
}
void AppletMessageQueue::OperationModeChanged() {
PushMessage(AppletMessage::OperationModeChanged);
PushMessage(AppletMessage::PerformanceModeChanged);
on_operation_mode_changed->Signal();
}
ICommonStateGetter::ICommonStateGetter(std::shared_ptr<AppletMessageQueue> msg_queue)
: ServiceFramework("ICommonStateGetter"), msg_queue(std::move(msg_queue)) {
// clang-format off
static const FunctionInfo functions[] = {
{0, &ICommonStateGetter::GetEventHandle, "GetEventHandle"},
@@ -388,21 +435,19 @@ void ICommonStateGetter::GetBootMode(Kernel::HLERequestContext& ctx) {
}
void ICommonStateGetter::GetEventHandle(Kernel::HLERequestContext& ctx) {
event->Signal();
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
rb.PushCopyObjects(event);
rb.PushCopyObjects(msg_queue->GetMesssageRecieveEvent());
LOG_WARNING(Service_AM, "(STUBBED) called");
LOG_DEBUG(Service_AM, "called");
}
void ICommonStateGetter::ReceiveMessage(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(15);
rb.PushEnum<AppletMessageQueue::AppletMessage>(msg_queue->PopMessage());
LOG_WARNING(Service_AM, "(STUBBED) called");
LOG_DEBUG(Service_AM, "called");
}
void ICommonStateGetter::GetCurrentFocusState(Kernel::HLERequestContext& ctx) {
@@ -414,13 +459,11 @@ void ICommonStateGetter::GetCurrentFocusState(Kernel::HLERequestContext& ctx) {
}
void ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent(Kernel::HLERequestContext& ctx) {
event->Signal();
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
rb.PushCopyObjects(event);
rb.PushCopyObjects(msg_queue->GetOperationModeChangedEvent());
LOG_WARNING(Service_AM, "(STUBBED) called");
LOG_DEBUG(Service_AM, "called");
}
void ICommonStateGetter::GetDefaultDisplayResolution(Kernel::HLERequestContext& ctx) {
@@ -444,7 +487,7 @@ void ICommonStateGetter::GetOperationMode(Kernel::HLERequestContext& ctx) {
rb.Push(RESULT_SUCCESS);
rb.Push(static_cast<u8>(use_docked_mode ? OperationMode::Docked : OperationMode::Handheld));
LOG_WARNING(Service_AM, "(STUBBED) called");
LOG_DEBUG(Service_AM, "called");
}
void ICommonStateGetter::GetPerformanceMode(Kernel::HLERequestContext& ctx) {
@@ -454,7 +497,7 @@ void ICommonStateGetter::GetPerformanceMode(Kernel::HLERequestContext& ctx) {
rb.Push(static_cast<u32>(use_docked_mode ? APM::PerformanceMode::Docked
: APM::PerformanceMode::Handheld));
LOG_WARNING(Service_AM, "(STUBBED) called");
LOG_DEBUG(Service_AM, "called");
}
class IStorageAccessor final : public ServiceFramework<IStorageAccessor> {
@@ -690,7 +733,7 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF
{70, nullptr, "RequestToShutdown"},
{71, nullptr, "RequestToReboot"},
{80, nullptr, "ExitAndRequestToShowThanksMessage"},
{90, nullptr, "EnableApplicationCrashReport"},
{90, &IApplicationFunctions::EnableApplicationCrashReport, "EnableApplicationCrashReport"},
{100, nullptr, "InitializeApplicationCopyrightFrameBuffer"},
{101, nullptr, "SetApplicationCopyrightImage"},
{102, nullptr, "SetApplicationCopyrightVisibility"},
@@ -709,6 +752,12 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF
IApplicationFunctions::~IApplicationFunctions() = default;
void IApplicationFunctions::EnableApplicationCrashReport(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_AM, "(STUBBED) called");
}
void IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed(
Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
@@ -778,7 +827,8 @@ void IApplicationFunctions::EnsureSaveData(Kernel::HLERequestContext& ctx) {
void IApplicationFunctions::SetTerminateResult(Kernel::HLERequestContext& ctx) {
// Takes an input u32 Result, no output.
// For example, in some cases official apps use this with error 0x2A2 then uses svcBreak.
// For example, in some cases official apps use this with error 0x2A2 then
// uses svcBreak.
IPC::RequestParser rp{ctx};
u32 result = rp.Pop<u32>();
@@ -840,8 +890,12 @@ void IApplicationFunctions::GetPseudoDeviceId(Kernel::HLERequestContext& ctx) {
void InstallInterfaces(SM::ServiceManager& service_manager,
std::shared_ptr<NVFlinger::NVFlinger> nvflinger) {
std::make_shared<AppletAE>(nvflinger)->InstallAsService(service_manager);
std::make_shared<AppletOE>(nvflinger)->InstallAsService(service_manager);
auto message_queue = std::make_shared<AppletMessageQueue>();
message_queue->PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged); // Needed on
// game boot
std::make_shared<AppletAE>(nvflinger, message_queue)->InstallAsService(service_manager);
std::make_shared<AppletOE>(nvflinger, message_queue)->InstallAsService(service_manager);
std::make_shared<IdleSys>()->InstallAsService(service_manager);
std::make_shared<OMM>()->InstallAsService(service_manager);
std::make_shared<SPSM>()->InstallAsService(service_manager);

View File

@@ -5,6 +5,7 @@
#pragma once
#include <memory>
#include <queue>
#include "core/hle/service/service.h"
namespace Kernel {
@@ -39,6 +40,31 @@ enum SystemLanguage {
TraditionalChinese = 16,
};
class AppletMessageQueue {
public:
enum class AppletMessage : u32 {
NoMessage = 0,
FocusStateChanged = 15,
OperationModeChanged = 30,
PerformanceModeChanged = 31,
};
AppletMessageQueue();
~AppletMessageQueue();
const Kernel::SharedPtr<Kernel::Event>& GetMesssageRecieveEvent() const;
const Kernel::SharedPtr<Kernel::Event>& GetOperationModeChangedEvent() const;
void PushMessage(AppletMessage msg);
AppletMessage PopMessage();
std::size_t GetMessageCount() const;
void OperationModeChanged();
private:
std::queue<AppletMessage> messages;
Kernel::SharedPtr<Kernel::Event> on_new_message;
Kernel::SharedPtr<Kernel::Event> on_operation_mode_changed;
};
class IWindowController final : public ServiceFramework<IWindowController> {
public:
IWindowController();
@@ -102,7 +128,7 @@ private:
class ICommonStateGetter final : public ServiceFramework<ICommonStateGetter> {
public:
ICommonStateGetter();
explicit ICommonStateGetter(std::shared_ptr<AppletMessageQueue> msg_queue);
~ICommonStateGetter() override;
private:
@@ -126,6 +152,7 @@ private:
void GetDefaultDisplayResolution(Kernel::HLERequestContext& ctx);
Kernel::SharedPtr<Kernel::Event> event;
std::shared_ptr<AppletMessageQueue> msg_queue;
};
class ILibraryAppletCreator final : public ServiceFramework<ILibraryAppletCreator> {
@@ -158,6 +185,7 @@ private:
void EndBlockingHomeButtonShortAndLongPressed(Kernel::HLERequestContext& ctx);
void BeginBlockingHomeButton(Kernel::HLERequestContext& ctx);
void EndBlockingHomeButton(Kernel::HLERequestContext& ctx);
void EnableApplicationCrashReport(Kernel::HLERequestContext& ctx);
};
class IHomeMenuFunctions final : public ServiceFramework<IHomeMenuFunctions> {

View File

@@ -12,8 +12,10 @@ namespace Service::AM {
class ILibraryAppletProxy final : public ServiceFramework<ILibraryAppletProxy> {
public:
explicit ILibraryAppletProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
: ServiceFramework("ILibraryAppletProxy"), nvflinger(std::move(nvflinger)) {
explicit ILibraryAppletProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
std::shared_ptr<AppletMessageQueue> msg_queue)
: ServiceFramework("ILibraryAppletProxy"), nvflinger(std::move(nvflinger)),
msg_queue(std::move(msg_queue)) {
static const FunctionInfo functions[] = {
{0, &ILibraryAppletProxy::GetCommonStateGetter, "GetCommonStateGetter"},
{1, &ILibraryAppletProxy::GetSelfController, "GetSelfController"},
@@ -32,7 +34,7 @@ private:
void GetCommonStateGetter(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<ICommonStateGetter>();
rb.PushIpcInterface<ICommonStateGetter>(msg_queue);
LOG_DEBUG(Service_AM, "called");
}
@@ -93,12 +95,15 @@ private:
}
std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
std::shared_ptr<AppletMessageQueue> msg_queue;
};
class ISystemAppletProxy final : public ServiceFramework<ISystemAppletProxy> {
public:
explicit ISystemAppletProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
: ServiceFramework("ISystemAppletProxy"), nvflinger(std::move(nvflinger)) {
explicit ISystemAppletProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
std::shared_ptr<AppletMessageQueue> msg_queue)
: ServiceFramework("ISystemAppletProxy"), nvflinger(std::move(nvflinger)),
msg_queue(std::move(msg_queue)) {
static const FunctionInfo functions[] = {
{0, &ISystemAppletProxy::GetCommonStateGetter, "GetCommonStateGetter"},
{1, &ISystemAppletProxy::GetSelfController, "GetSelfController"},
@@ -119,7 +124,7 @@ private:
void GetCommonStateGetter(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<ICommonStateGetter>();
rb.PushIpcInterface<ICommonStateGetter>(msg_queue);
LOG_DEBUG(Service_AM, "called");
}
@@ -186,31 +191,34 @@ private:
LOG_DEBUG(Service_AM, "called");
}
std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
std::shared_ptr<AppletMessageQueue> msg_queue;
};
void AppletAE::OpenSystemAppletProxy(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<ISystemAppletProxy>(nvflinger);
rb.PushIpcInterface<ISystemAppletProxy>(nvflinger, msg_queue);
LOG_DEBUG(Service_AM, "called");
}
void AppletAE::OpenLibraryAppletProxy(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<ILibraryAppletProxy>(nvflinger);
rb.PushIpcInterface<ILibraryAppletProxy>(nvflinger, msg_queue);
LOG_DEBUG(Service_AM, "called");
}
void AppletAE::OpenLibraryAppletProxyOld(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<ILibraryAppletProxy>(nvflinger);
rb.PushIpcInterface<ILibraryAppletProxy>(nvflinger, msg_queue);
LOG_DEBUG(Service_AM, "called");
}
AppletAE::AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
: ServiceFramework("appletAE"), nvflinger(std::move(nvflinger)) {
AppletAE::AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
std::shared_ptr<AppletMessageQueue> msg_queue)
: ServiceFramework("appletAE"), nvflinger(std::move(nvflinger)),
msg_queue(std::move(msg_queue)) {
// clang-format off
static const FunctionInfo functions[] = {
{100, &AppletAE::OpenSystemAppletProxy, "OpenSystemAppletProxy"},
@@ -228,4 +236,8 @@ AppletAE::AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
AppletAE::~AppletAE() = default;
const std::shared_ptr<AppletMessageQueue>& AppletAE::GetMessageQueue() const {
return msg_queue;
}
} // namespace Service::AM

View File

@@ -17,15 +17,19 @@ namespace AM {
class AppletAE final : public ServiceFramework<AppletAE> {
public:
explicit AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger);
explicit AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
std::shared_ptr<AppletMessageQueue> msg_queue);
~AppletAE() override;
const std::shared_ptr<AppletMessageQueue>& GetMessageQueue() const;
private:
void OpenSystemAppletProxy(Kernel::HLERequestContext& ctx);
void OpenLibraryAppletProxy(Kernel::HLERequestContext& ctx);
void OpenLibraryAppletProxyOld(Kernel::HLERequestContext& ctx);
std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
std::shared_ptr<AppletMessageQueue> msg_queue;
};
} // namespace AM

View File

@@ -12,8 +12,10 @@ namespace Service::AM {
class IApplicationProxy final : public ServiceFramework<IApplicationProxy> {
public:
explicit IApplicationProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
: ServiceFramework("IApplicationProxy"), nvflinger(std::move(nvflinger)) {
explicit IApplicationProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
std::shared_ptr<AppletMessageQueue> msg_queue)
: ServiceFramework("IApplicationProxy"), nvflinger(std::move(nvflinger)),
msg_queue(std::move(msg_queue)) {
// clang-format off
static const FunctionInfo functions[] = {
{0, &IApplicationProxy::GetCommonStateGetter, "GetCommonStateGetter"},
@@ -70,7 +72,7 @@ private:
void GetCommonStateGetter(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<ICommonStateGetter>();
rb.PushIpcInterface<ICommonStateGetter>(msg_queue);
LOG_DEBUG(Service_AM, "called");
}
@@ -89,17 +91,20 @@ private:
}
std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
std::shared_ptr<AppletMessageQueue> msg_queue;
};
void AppletOE::OpenApplicationProxy(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IApplicationProxy>(nvflinger);
rb.PushIpcInterface<IApplicationProxy>(nvflinger, msg_queue);
LOG_DEBUG(Service_AM, "called");
}
AppletOE::AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
: ServiceFramework("appletOE"), nvflinger(std::move(nvflinger)) {
AppletOE::AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
std::shared_ptr<AppletMessageQueue> msg_queue)
: ServiceFramework("appletOE"), nvflinger(std::move(nvflinger)),
msg_queue(std::move(msg_queue)) {
static const FunctionInfo functions[] = {
{0, &AppletOE::OpenApplicationProxy, "OpenApplicationProxy"},
};
@@ -108,4 +113,8 @@ AppletOE::AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
AppletOE::~AppletOE() = default;
const std::shared_ptr<AppletMessageQueue>& AppletOE::GetMessageQueue() const {
return msg_queue;
}
} // namespace Service::AM

View File

@@ -17,13 +17,17 @@ namespace AM {
class AppletOE final : public ServiceFramework<AppletOE> {
public:
explicit AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger);
explicit AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
std::shared_ptr<AppletMessageQueue> msg_queue);
~AppletOE() override;
const std::shared_ptr<AppletMessageQueue>& GetMessageQueue() const;
private:
void OpenApplicationProxy(Kernel::HLERequestContext& ctx);
std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
std::shared_ptr<AppletMessageQueue> msg_queue;
};
} // namespace AM

View File

@@ -28,13 +28,13 @@ public:
{1, &IAudioRenderer::GetSampleCount, "GetSampleCount"},
{2, &IAudioRenderer::GetMixBufferCount, "GetMixBufferCount"},
{3, &IAudioRenderer::GetState, "GetState"},
{4, &IAudioRenderer::RequestUpdate, "RequestUpdate"},
{4, &IAudioRenderer::RequestUpdateImpl, "RequestUpdate"},
{5, &IAudioRenderer::Start, "Start"},
{6, &IAudioRenderer::Stop, "Stop"},
{7, &IAudioRenderer::QuerySystemEvent, "QuerySystemEvent"},
{8, nullptr, "SetRenderingTimeLimit"},
{9, nullptr, "GetRenderingTimeLimit"},
{10, nullptr, "RequestUpdateAuto"},
{8, &IAudioRenderer::SetRenderingTimeLimit, "SetRenderingTimeLimit"},
{9, &IAudioRenderer::GetRenderingTimeLimit, "GetRenderingTimeLimit"},
{10, &IAudioRenderer::RequestUpdateImpl, "RequestUpdateAuto"},
{11, nullptr, "ExecuteAudioRendererRendering"},
};
// clang-format on
@@ -79,7 +79,7 @@ private:
LOG_DEBUG(Service_Audio, "called");
}
void RequestUpdate(Kernel::HLERequestContext& ctx) {
void RequestUpdateImpl(Kernel::HLERequestContext& ctx) {
ctx.WriteBuffer(renderer->UpdateAudioRenderer(ctx.ReadBuffer()));
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
@@ -110,8 +110,29 @@ private:
LOG_WARNING(Service_Audio, "(STUBBED) called");
}
void SetRenderingTimeLimit(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
rendering_time_limit_percent = rp.Pop<u32>();
ASSERT(rendering_time_limit_percent >= 0 && rendering_time_limit_percent <= 100);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_DEBUG(Service_Audio, "called. rendering_time_limit_percent={}",
rendering_time_limit_percent);
}
void GetRenderingTimeLimit(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Audio, "called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push(rendering_time_limit_percent);
}
Kernel::SharedPtr<Kernel::Event> system_event;
std::unique_ptr<AudioCore::AudioRenderer> renderer;
u32 rendering_time_limit_percent = 100;
};
class IAudioDevice final : public ServiceFramework<IAudioDevice> {

View File

@@ -77,8 +77,8 @@ private:
IPC::ResponseBuilder rb{ctx, 6};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(consumed);
rb.Push<u64>(performance);
rb.Push<u32>(sample_count);
rb.Push<u64>(performance);
ctx.WriteBuffer(samples.data(), samples.size() * sizeof(s16));
}

View File

@@ -2,12 +2,49 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/logging/log.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/event.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/service/btdrv/btdrv.h"
#include "core/hle/service/service.h"
#include "core/hle/service/sm/sm.h"
namespace Service::BtDrv {
class Bt final : public ServiceFramework<Bt> {
public:
explicit Bt() : ServiceFramework{"bt"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "Unknown0"},
{1, nullptr, "Unknown1"},
{2, nullptr, "Unknown2"},
{3, nullptr, "Unknown3"},
{4, nullptr, "Unknown4"},
{5, nullptr, "Unknown5"},
{6, nullptr, "Unknown6"},
{7, nullptr, "Unknown7"},
{8, nullptr, "Unknown8"},
{9, &Bt::RegisterEvent, "RegisterEvent"},
};
// clang-format on
RegisterHandlers(functions);
}
private:
void RegisterEvent(Kernel::HLERequestContext& ctx) {
auto& kernel = Core::System::GetInstance().Kernel();
register_event =
Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "BT:RegisterEvent");
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
rb.PushCopyObjects(register_event);
LOG_WARNING(Service_BTM, "(STUBBED) called");
}
Kernel::SharedPtr<Kernel::Event> register_event;
};
class BtDrv final : public ServiceFramework<BtDrv> {
public:
explicit BtDrv() : ServiceFramework{"btdrv"} {
@@ -67,6 +104,7 @@ public:
void InstallInterfaces(SM::ServiceManager& sm) {
std::make_shared<BtDrv>()->InstallAsService(sm);
std::make_shared<Bt>()->InstallAsService(sm);
}
} // namespace Service::BtDrv

View File

@@ -6,13 +6,118 @@
#include "common/logging/log.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/event.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/service/btm/btm.h"
#include "core/hle/service/service.h"
#include "core/hle/service/sm/sm.h"
namespace Service::BTM {
class IBtmUserCore final : public ServiceFramework<IBtmUserCore> {
public:
explicit IBtmUserCore() : ServiceFramework{"IBtmUserCore"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &IBtmUserCore::GetScanEvent, "GetScanEvent"},
{1, nullptr, "Unknown1"},
{2, nullptr, "Unknown2"},
{3, nullptr, "Unknown3"},
{4, nullptr, "Unknown4"},
{5, nullptr, "Unknown5"},
{6, nullptr, "Unknown6"},
{7, nullptr, "Unknown7"},
{8, nullptr, "Unknown8"},
{9, nullptr, "Unknown9"},
{10, nullptr, "Unknown10"},
{17, &IBtmUserCore::GetConnectionEvent, "GetConnectionEvent"},
{18, nullptr, "Unknown18"},
{19, nullptr, "Unknown19"},
{20, nullptr, "Unknown20"},
{21, nullptr, "Unknown21"},
{22, nullptr, "Unknown22"},
{23, nullptr, "Unknown23"},
{24, nullptr, "Unknown24"},
{25, nullptr, "Unknown25"},
{26, &IBtmUserCore::GetDiscoveryEvent, "AcquireBleServiceDiscoveryEventImpl"},
{27, nullptr, "Unknown27"},
{28, nullptr, "Unknown28"},
{29, nullptr, "Unknown29"},
{30, nullptr, "Unknown30"},
{31, nullptr, "Unknown31"},
{32, nullptr, "Unknown32"},
{33, &IBtmUserCore::GetConfigEvent, "GetConfigEvent"},
{34, nullptr, "Unknown34"},
{35, nullptr, "Unknown35"},
{36, nullptr, "Unknown36"},
{37, nullptr, "Unknown37"},
};
// clang-format on
RegisterHandlers(functions);
}
private:
void GetScanEvent(Kernel::HLERequestContext& ctx) {
auto& kernel = Core::System::GetInstance().Kernel();
scan_event =
Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IBtmUserCore:ScanEvent");
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
rb.PushCopyObjects(scan_event);
LOG_WARNING(Service_BTM, "(STUBBED) called");
}
void GetConnectionEvent(Kernel::HLERequestContext& ctx) {
auto& kernel = Core::System::GetInstance().Kernel();
connection_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot,
"IBtmUserCore:ConnectionEvent");
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
rb.PushCopyObjects(connection_event);
LOG_WARNING(Service_BTM, "(STUBBED) called");
}
void GetDiscoveryEvent(Kernel::HLERequestContext& ctx) {
auto& kernel = Core::System::GetInstance().Kernel();
service_discovery =
Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IBtmUserCore:Discovery");
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
rb.PushCopyObjects(service_discovery);
LOG_WARNING(Service_BTM, "(STUBBED) called");
}
void GetConfigEvent(Kernel::HLERequestContext& ctx) {
auto& kernel = Core::System::GetInstance().Kernel();
config_event =
Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IBtmUserCore:ConfigEvent");
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
rb.PushCopyObjects(config_event);
LOG_WARNING(Service_BTM, "(STUBBED) called");
}
Kernel::SharedPtr<Kernel::Event> scan_event;
Kernel::SharedPtr<Kernel::Event> connection_event;
Kernel::SharedPtr<Kernel::Event> service_discovery;
Kernel::SharedPtr<Kernel::Event> config_event;
};
class BTM_USR final : public ServiceFramework<BTM_USR> {
public:
explicit BTM_USR() : ServiceFramework{"btm:u"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &BTM_USR::GetCoreImpl, "GetCoreImpl"},
};
// clang-format on
RegisterHandlers(functions);
}
private:
void GetCoreImpl(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IBtmUserCore>();
LOG_DEBUG(Service_BTM, "called");
}
};
class BTM final : public ServiceFramework<BTM> {
public:
explicit BTM() : ServiceFramework{"btm"} {
@@ -116,6 +221,7 @@ void InstallInterfaces(SM::ServiceManager& sm) {
std::make_shared<BTM>()->InstallAsService(sm);
std::make_shared<BTM_DBG>()->InstallAsService(sm);
std::make_shared<BTM_SYS>()->InstallAsService(sm);
std::make_shared<BTM_USR>()->InstallAsService(sm);
}
} // namespace Service::BTM

View File

@@ -303,25 +303,42 @@ ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space,
static_cast<u8>(space), save_struct.DebugInfo());
if (save_data_factory == nullptr) {
return ResultCode(ErrorModule::FS, FileSys::ErrCodes::TitleNotFound);
return FileSys::ERROR_ENTITY_NOT_FOUND;
}
return save_data_factory->Open(space, save_struct);
}
ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space) {
LOG_TRACE(Service_FS, "Opening Save Data Space for space_id={:01X}", static_cast<u8>(space));
if (save_data_factory == nullptr) {
return FileSys::ERROR_ENTITY_NOT_FOUND;
}
return MakeResult(save_data_factory->GetSaveDataSpaceDirectory(space));
}
ResultVal<FileSys::VirtualDir> OpenSDMC() {
LOG_TRACE(Service_FS, "Opening SDMC");
if (sdmc_factory == nullptr) {
return ResultCode(ErrorModule::FS, FileSys::ErrCodes::SdCardNotFound);
return FileSys::ERROR_SD_CARD_NOT_FOUND;
}
return sdmc_factory->Open();
}
std::unique_ptr<FileSys::RegisteredCacheUnion> GetUnionContents() {
return std::make_unique<FileSys::RegisteredCacheUnion>(std::vector<FileSys::RegisteredCache*>{
GetSystemNANDContents(), GetUserNANDContents(), GetSDMCContents()});
std::shared_ptr<FileSys::RegisteredCacheUnion> registered_cache_union;
std::shared_ptr<FileSys::RegisteredCacheUnion> GetUnionContents() {
if (registered_cache_union == nullptr) {
registered_cache_union =
std::make_shared<FileSys::RegisteredCacheUnion>(std::vector<FileSys::RegisteredCache*>{
GetSystemNANDContents(), GetUserNANDContents(), GetSDMCContents()});
}
return registered_cache_union;
}
FileSys::RegisteredCache* GetSystemNANDContents() {
@@ -360,6 +377,15 @@ FileSys::VirtualDir GetModificationLoadRoot(u64 title_id) {
return bis_factory->GetModificationLoadRoot(title_id);
}
FileSys::VirtualDir GetModificationDumpRoot(u64 title_id) {
LOG_TRACE(Service_FS, "Opening mod dump root for tid={:016X}", title_id);
if (bis_factory == nullptr)
return nullptr;
return bis_factory->GetModificationDumpRoot(title_id);
}
void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) {
if (overwrite) {
bis_factory = nullptr;
@@ -373,13 +399,21 @@ void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) {
FileSys::Mode::ReadWrite);
auto load_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::LoadDir),
FileSys::Mode::ReadWrite);
auto dump_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::DumpDir),
FileSys::Mode::ReadWrite);
if (bis_factory == nullptr)
bis_factory = std::make_unique<FileSys::BISFactory>(nand_directory, load_directory);
if (save_data_factory == nullptr)
if (bis_factory == nullptr) {
bis_factory =
std::make_unique<FileSys::BISFactory>(nand_directory, load_directory, dump_directory);
}
if (save_data_factory == nullptr) {
save_data_factory = std::make_unique<FileSys::SaveDataFactory>(std::move(nand_directory));
if (sdmc_factory == nullptr)
}
if (sdmc_factory == nullptr) {
sdmc_factory = std::make_unique<FileSys::SDMCFactory>(std::move(sd_directory));
}
}
void InstallInterfaces(SM::ServiceManager& service_manager, FileSys::VfsFilesystem& vfs) {

View File

@@ -45,15 +45,17 @@ ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id, FileSys::StorageId stora
FileSys::ContentRecordType type);
ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space,
FileSys::SaveDataDescriptor save_struct);
ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space);
ResultVal<FileSys::VirtualDir> OpenSDMC();
std::unique_ptr<FileSys::RegisteredCacheUnion> GetUnionContents();
std::shared_ptr<FileSys::RegisteredCacheUnion> GetUnionContents();
FileSys::RegisteredCache* GetSystemNANDContents();
FileSys::RegisteredCache* GetUserNANDContents();
FileSys::RegisteredCache* GetSDMCContents();
FileSys::VirtualDir GetModificationLoadRoot(u64 title_id);
FileSys::VirtualDir GetModificationDumpRoot(u64 title_id);
// Creates the SaveData, SDMC, and BIS Factories. Should be called once and before any function
// above is called.

View File

@@ -11,6 +11,7 @@
#include "common/assert.h"
#include "common/common_types.h"
#include "common/hex_util.h"
#include "common/logging/log.h"
#include "common/string_util.h"
#include "core/file_sys/directory.h"
@@ -62,12 +63,12 @@ private:
// Error checking
if (length < 0) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidLength));
rb.Push(FileSys::ERROR_INVALID_SIZE);
return;
}
if (offset < 0) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidOffset));
rb.Push(FileSys::ERROR_INVALID_OFFSET);
return;
}
@@ -107,12 +108,12 @@ private:
// Error checking
if (length < 0) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidLength));
rb.Push(FileSys::ERROR_INVALID_SIZE);
return;
}
if (offset < 0) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidOffset));
rb.Push(FileSys::ERROR_INVALID_OFFSET);
return;
}
@@ -138,12 +139,12 @@ private:
// Error checking
if (length < 0) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidLength));
rb.Push(FileSys::ERROR_INVALID_SIZE);
return;
}
if (offset < 0) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidOffset));
rb.Push(FileSys::ERROR_INVALID_OFFSET);
return;
}
@@ -451,7 +452,147 @@ private:
VfsDirectoryServiceWrapper backend;
};
class ISaveDataInfoReader final : public ServiceFramework<ISaveDataInfoReader> {
public:
explicit ISaveDataInfoReader(FileSys::SaveDataSpaceId space)
: ServiceFramework("ISaveDataInfoReader") {
static const FunctionInfo functions[] = {
{0, &ISaveDataInfoReader::ReadSaveDataInfo, "ReadSaveDataInfo"},
};
RegisterHandlers(functions);
FindAllSaves(space);
}
void ReadSaveDataInfo(Kernel::HLERequestContext& ctx) {
// Calculate how many entries we can fit in the output buffer
const u64 count_entries = ctx.GetWriteBufferSize() / sizeof(SaveDataInfo);
// Cap at total number of entries.
const u64 actual_entries = std::min(count_entries, info.size() - next_entry_index);
// Determine data start and end
const auto* begin = reinterpret_cast<u8*>(info.data() + next_entry_index);
const auto* end = reinterpret_cast<u8*>(info.data() + next_entry_index + actual_entries);
const auto range_size = static_cast<std::size_t>(std::distance(begin, end));
next_entry_index += actual_entries;
// Write the data to memory
ctx.WriteBuffer(begin, range_size);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(static_cast<u32>(actual_entries));
}
private:
static u64 stoull_be(std::string_view str) {
if (str.size() != 16)
return 0;
const auto bytes = Common::HexStringToArray<0x8>(str);
u64 out{};
std::memcpy(&out, bytes.data(), sizeof(u64));
return Common::swap64(out);
}
void FindAllSaves(FileSys::SaveDataSpaceId space) {
const auto save_root = OpenSaveDataSpace(space);
ASSERT(save_root.Succeeded());
for (const auto& type : (*save_root)->GetSubdirectories()) {
if (type->GetName() == "save") {
for (const auto& save_id : type->GetSubdirectories()) {
for (const auto& user_id : save_id->GetSubdirectories()) {
const auto save_id_numeric = stoull_be(save_id->GetName());
auto user_id_numeric = Common::HexStringToArray<0x10>(user_id->GetName());
std::reverse(user_id_numeric.begin(), user_id_numeric.end());
if (save_id_numeric != 0) {
// System Save Data
info.emplace_back(SaveDataInfo{
0,
space,
FileSys::SaveDataType::SystemSaveData,
{},
user_id_numeric,
save_id_numeric,
0,
user_id->GetSize(),
{},
});
continue;
}
for (const auto& title_id : user_id->GetSubdirectories()) {
const auto device =
std::all_of(user_id_numeric.begin(), user_id_numeric.end(),
[](u8 val) { return val == 0; });
info.emplace_back(SaveDataInfo{
0,
space,
device ? FileSys::SaveDataType::DeviceSaveData
: FileSys::SaveDataType::SaveData,
{},
user_id_numeric,
save_id_numeric,
stoull_be(title_id->GetName()),
title_id->GetSize(),
{},
});
}
}
}
} else if (space == FileSys::SaveDataSpaceId::TemporaryStorage) {
// Temporary Storage
for (const auto& user_id : type->GetSubdirectories()) {
for (const auto& title_id : user_id->GetSubdirectories()) {
if (!title_id->GetFiles().empty() ||
!title_id->GetSubdirectories().empty()) {
auto user_id_numeric =
Common::HexStringToArray<0x10>(user_id->GetName());
std::reverse(user_id_numeric.begin(), user_id_numeric.end());
info.emplace_back(SaveDataInfo{
0,
space,
FileSys::SaveDataType::TemporaryStorage,
{},
user_id_numeric,
stoull_be(type->GetName()),
stoull_be(title_id->GetName()),
title_id->GetSize(),
{},
});
}
}
}
}
}
}
struct SaveDataInfo {
u64_le save_id_unknown;
FileSys::SaveDataSpaceId space;
FileSys::SaveDataType type;
INSERT_PADDING_BYTES(0x6);
std::array<u8, 0x10> user_id;
u64_le save_id;
u64_le title_id;
u64_le save_image_size;
INSERT_PADDING_BYTES(0x28);
};
static_assert(sizeof(SaveDataInfo) == 0x60, "SaveDataInfo has incorrect size.");
std::vector<SaveDataInfo> info;
u64 next_entry_index = 0;
};
FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "MountContent"},
{1, &FSP_SRV::Initialize, "Initialize"},
@@ -485,7 +626,7 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
{58, nullptr, "ReadSaveDataFileSystemExtraData"},
{59, nullptr, "WriteSaveDataFileSystemExtraData"},
{60, nullptr, "OpenSaveDataInfoReader"},
{61, nullptr, "OpenSaveDataInfoReaderBySaveDataSpaceId"},
{61, &FSP_SRV::OpenSaveDataInfoReaderBySaveDataSpaceId, "OpenSaveDataInfoReaderBySaveDataSpaceId"},
{62, nullptr, "OpenCacheStorageList"},
{64, nullptr, "OpenSaveDataInternalStorageFileSystem"},
{65, nullptr, "UpdateSaveDataMacForDebug"},
@@ -544,6 +685,7 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
{1009, nullptr, "GetAndClearMemoryReportInfo"},
{1100, nullptr, "OverrideSaveDataTransferTokenSignVerificationKey"},
};
// clang-format on
RegisterHandlers(functions);
}
@@ -602,7 +744,7 @@ void FSP_SRV::MountSaveData(Kernel::HLERequestContext& ctx) {
if (dir.Failed()) {
IPC::ResponseBuilder rb{ctx, 2, 0, 0};
rb.Push(ResultCode(ErrorModule::FS, FileSys::ErrCodes::TitleNotFound));
rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND);
return;
}
@@ -618,6 +760,15 @@ void FSP_SRV::OpenReadOnlySaveDataFileSystem(Kernel::HLERequestContext& ctx) {
MountSaveData(ctx);
}
void FSP_SRV::OpenSaveDataInfoReaderBySaveDataSpaceId(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto space = rp.PopRaw<FileSys::SaveDataSpaceId>();
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<ISaveDataInfoReader>(std::make_shared<ISaveDataInfoReader>(space));
}
void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_FS, "(STUBBED) called");
@@ -685,7 +836,7 @@ void FSP_SRV::OpenRomStorage(Kernel::HLERequestContext& ctx) {
static_cast<u8>(storage_id), title_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultCode(ErrorModule::FS, FileSys::ErrCodes::TitleNotFound));
rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND);
}
} // namespace Service::FileSystem

View File

@@ -25,6 +25,7 @@ private:
void CreateSaveData(Kernel::HLERequestContext& ctx);
void MountSaveData(Kernel::HLERequestContext& ctx);
void OpenReadOnlySaveDataFileSystem(Kernel::HLERequestContext& ctx);
void OpenSaveDataInfoReaderBySaveDataSpaceId(Kernel::HLERequestContext& ctx);
void GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx);
void OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx);
void OpenDataStorageByDataId(Kernel::HLERequestContext& ctx);

View File

@@ -6,9 +6,14 @@
#include "common/common_types.h"
#include "core/core_timing.h"
#include "core/hle/service/hid/controllers/debug_pad.h"
#include "core/settings.h"
namespace Service::HID {
constexpr s32 HID_JOYSTICK_MAX = 0x7fff;
constexpr s32 HID_JOYSTICK_MIN = -0x7fff;
enum class JoystickId : std::size_t { Joystick_Left, Joystick_Right };
Controller_DebugPad::Controller_DebugPad() = default;
Controller_DebugPad::~Controller_DebugPad() = default;
@@ -33,10 +38,43 @@ void Controller_DebugPad::OnUpdate(u8* data, std::size_t size) {
cur_entry.sampling_number = last_entry.sampling_number + 1;
cur_entry.sampling_number2 = cur_entry.sampling_number;
// TODO(ogniK): Update debug pad states
cur_entry.attribute.connected.Assign(1);
auto& pad = cur_entry.pad_state;
using namespace Settings::NativeButton;
pad.a.Assign(buttons[A - BUTTON_HID_BEGIN]->GetStatus());
pad.b.Assign(buttons[B - BUTTON_HID_BEGIN]->GetStatus());
pad.x.Assign(buttons[X - BUTTON_HID_BEGIN]->GetStatus());
pad.y.Assign(buttons[Y - BUTTON_HID_BEGIN]->GetStatus());
pad.l.Assign(buttons[L - BUTTON_HID_BEGIN]->GetStatus());
pad.r.Assign(buttons[R - BUTTON_HID_BEGIN]->GetStatus());
pad.zl.Assign(buttons[ZL - BUTTON_HID_BEGIN]->GetStatus());
pad.zr.Assign(buttons[ZR - BUTTON_HID_BEGIN]->GetStatus());
pad.plus.Assign(buttons[Plus - BUTTON_HID_BEGIN]->GetStatus());
pad.minus.Assign(buttons[Minus - BUTTON_HID_BEGIN]->GetStatus());
pad.d_left.Assign(buttons[DLeft - BUTTON_HID_BEGIN]->GetStatus());
pad.d_up.Assign(buttons[DUp - BUTTON_HID_BEGIN]->GetStatus());
pad.d_right.Assign(buttons[DRight - BUTTON_HID_BEGIN]->GetStatus());
pad.d_down.Assign(buttons[DDown - BUTTON_HID_BEGIN]->GetStatus());
const auto [stick_l_x_f, stick_l_y_f] =
analogs[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetStatus();
const auto [stick_r_x_f, stick_r_y_f] =
analogs[static_cast<std::size_t>(JoystickId::Joystick_Right)]->GetStatus();
cur_entry.l_stick.x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX);
cur_entry.l_stick.y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX);
cur_entry.r_stick.x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX);
cur_entry.r_stick.y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX);
std::memcpy(data, &shared_memory, sizeof(SharedMemory));
}
void Controller_DebugPad::OnLoadInputDevices() {}
void Controller_DebugPad::OnLoadInputDevices() {
std::transform(Settings::values.debug_pad_buttons.begin(),
Settings::values.debug_pad_buttons.end(), buttons.begin(),
Input::CreateDevice<Input::ButtonDevice>);
std::transform(Settings::values.debug_pad_analogs.begin(),
Settings::values.debug_pad_analogs.end(), analogs.begin(),
Input::CreateDevice<Input::AnalogDevice>);
}
} // namespace Service::HID

View File

@@ -5,10 +5,13 @@
#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/frontend/input.h"
#include "core/hle/service/hid/controllers/controller_base.h"
#include "core/settings.h"
namespace Service::HID {
class Controller_DebugPad final : public ControllerBase {
@@ -35,11 +38,40 @@ private:
};
static_assert(sizeof(AnalogStick) == 0x8);
struct PadState {
union {
u32_le raw{};
BitField<0, 1, u32_le> a;
BitField<1, 1, u32_le> b;
BitField<2, 1, u32_le> x;
BitField<3, 1, u32_le> y;
BitField<4, 1, u32_le> l;
BitField<5, 1, u32_le> r;
BitField<6, 1, u32_le> zl;
BitField<7, 1, u32_le> zr;
BitField<8, 1, u32_le> plus;
BitField<9, 1, u32_le> minus;
BitField<10, 1, u32_le> d_left;
BitField<11, 1, u32_le> d_up;
BitField<12, 1, u32_le> d_right;
BitField<13, 1, u32_le> d_down;
};
};
static_assert(sizeof(PadState) == 0x4, "PadState is an invalid size");
struct Attributes {
union {
u32_le raw{};
BitField<0, 1, u32_le> connected;
};
};
static_assert(sizeof(Attributes) == 0x4, "Attributes is an invalid size");
struct PadStates {
s64_le sampling_number;
s64_le sampling_number2;
u32_le attribute;
u32_le button_state;
Attributes attribute;
PadState pad_state;
AnalogStick r_stick;
AnalogStick l_stick;
};
@@ -52,5 +84,10 @@ private:
};
static_assert(sizeof(SharedMemory) == 0x400, "SharedMemory is an invalid size");
SharedMemory shared_memory{};
std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID>
buttons;
std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID>
analogs;
};
} // namespace Service::HID

View File

@@ -6,9 +6,11 @@
#include "common/common_types.h"
#include "core/core_timing.h"
#include "core/hle/service/hid/controllers/keyboard.h"
#include "core/settings.h"
namespace Service::HID {
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3800;
constexpr u8 KEYS_PER_BYTE = 8;
Controller_Keyboard::Controller_Keyboard() = default;
Controller_Keyboard::~Controller_Keyboard() = default;
@@ -34,10 +36,24 @@ void Controller_Keyboard::OnUpdate(u8* data, std::size_t size) {
cur_entry.sampling_number = last_entry.sampling_number + 1;
cur_entry.sampling_number2 = cur_entry.sampling_number;
// TODO(ogniK): Update keyboard states
for (std::size_t i = 0; i < keyboard_keys.size(); ++i) {
for (std::size_t k = 0; k < KEYS_PER_BYTE; ++k) {
cur_entry.key[i / KEYS_PER_BYTE] |= (keyboard_keys[i]->GetStatus() << k);
}
}
for (std::size_t i = 0; i < keyboard_mods.size(); ++i) {
cur_entry.modifier |= (keyboard_mods[i]->GetStatus() << i);
}
std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory));
}
void Controller_Keyboard::OnLoadInputDevices() {}
void Controller_Keyboard::OnLoadInputDevices() {
std::transform(Settings::values.keyboard_keys.begin(), Settings::values.keyboard_keys.end(),
keyboard_keys.begin(), Input::CreateDevice<Input::ButtonDevice>);
std::transform(Settings::values.keyboard_mods.begin(), Settings::values.keyboard_mods.end(),
keyboard_mods.begin(), Input::CreateDevice<Input::ButtonDevice>);
}
} // namespace Service::HID

View File

@@ -8,7 +8,9 @@
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
#include "core/frontend/input.h"
#include "core/hle/service/hid/controllers/controller_base.h"
#include "core/settings.h"
namespace Service::HID {
class Controller_Keyboard final : public ControllerBase {
@@ -46,5 +48,10 @@ private:
};
static_assert(sizeof(SharedMemory) == 0x400, "SharedMemory is an invalid size");
SharedMemory shared_memory{};
std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeKeyboard::NumKeyboardKeys>
keyboard_keys;
std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeKeyboard::NumKeyboardMods>
keyboard_mods;
};
} // namespace Service::HID

View File

@@ -5,6 +5,7 @@
#include <cstring>
#include "common/common_types.h"
#include "core/core_timing.h"
#include "core/frontend/emu_window.h"
#include "core/hle/service/hid/controllers/mouse.h"
namespace Service::HID {
@@ -14,7 +15,6 @@ Controller_Mouse::Controller_Mouse() = default;
Controller_Mouse::~Controller_Mouse() = default;
void Controller_Mouse::OnInit() {}
void Controller_Mouse::OnRelease() {}
void Controller_Mouse::OnUpdate(u8* data, std::size_t size) {
@@ -34,10 +34,29 @@ void Controller_Mouse::OnUpdate(u8* data, std::size_t size) {
cur_entry.sampling_number = last_entry.sampling_number + 1;
cur_entry.sampling_number2 = cur_entry.sampling_number;
// TODO(ogniK): Update mouse states
if (Settings::values.mouse_enabled) {
const auto [px, py, sx, sy] = mouse_device->GetStatus();
const auto x = static_cast<s32>(px * Layout::ScreenUndocked::Width);
const auto y = static_cast<s32>(py * Layout::ScreenUndocked::Height);
cur_entry.x = x;
cur_entry.y = y;
cur_entry.delta_x = x - last_entry.x;
cur_entry.delta_y = y - last_entry.y;
cur_entry.mouse_wheel_x = sx;
cur_entry.mouse_wheel_y = sy;
for (std::size_t i = 0; i < mouse_button_devices.size(); ++i) {
cur_entry.button |= (mouse_button_devices[i]->GetStatus() << i);
}
}
std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory));
}
void Controller_Mouse::OnLoadInputDevices() {}
void Controller_Mouse::OnLoadInputDevices() {
mouse_device = Input::CreateDevice<Input::MouseDevice>(Settings::values.mouse_device);
std::transform(Settings::values.mouse_buttons.begin(), Settings::values.mouse_buttons.end(),
mouse_button_devices.begin(), Input::CreateDevice<Input::ButtonDevice>);
}
} // namespace Service::HID

View File

@@ -7,7 +7,9 @@
#include <array>
#include "common/common_types.h"
#include "common/swap.h"
#include "core/frontend/input.h"
#include "core/hle/service/hid/controllers/controller_base.h"
#include "core/settings.h"
namespace Service::HID {
class Controller_Mouse final : public ControllerBase {
@@ -35,7 +37,8 @@ private:
s32_le y;
s32_le delta_x;
s32_le delta_y;
s32_le mouse_wheel;
s32_le mouse_wheel_x;
s32_le mouse_wheel_y;
s32_le button;
s32_le attribute;
};
@@ -46,5 +49,9 @@ private:
std::array<MouseState, 17> mouse_states;
};
SharedMemory shared_memory{};
std::unique_ptr<Input::MouseDevice> mouse_device;
std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeMouseButton::NumMouseButtons>
mouse_button_devices;
};
} // namespace Service::HID

View File

@@ -17,22 +17,13 @@
#include "core/settings.h"
namespace Service::HID {
constexpr u32 JOYCON_BODY_NEON_RED = 0xFF3C28;
constexpr u32 JOYCON_BUTTONS_NEON_RED = 0x1E0A0A;
constexpr u32 JOYCON_BODY_NEON_BLUE = 0x0AB9E6;
constexpr u32 JOYCON_BUTTONS_NEON_BLUE = 0x001E1E;
constexpr s32 HID_JOYSTICK_MAX = 0x7fff;
constexpr s32 HID_JOYSTICK_MIN = -0x7fff;
constexpr std::size_t NPAD_OFFSET = 0x9A00;
constexpr u32 BATTERY_FULL = 2;
constexpr u32 NPAD_HANDHELD = 32;
constexpr u32 NPAD_UNKNOWN = 16; // TODO(ogniK): What is this?
constexpr u32 MAX_NPAD_ID = 7;
constexpr Controller_NPad::NPadControllerType PREFERRED_CONTROLLER =
Controller_NPad::NPadControllerType::JoyDual;
constexpr std::array<u32, 10> npad_id_list{
0, 1, 2, 3, 4, 5, 6, 7, 32, 16,
0, 1, 2, 3, 4, 5, 6, 7, NPAD_HANDHELD, NPAD_UNKNOWN,
};
enum class JoystickId : std::size_t {
@@ -40,6 +31,66 @@ enum class JoystickId : std::size_t {
Joystick_Right,
};
static Controller_NPad::NPadControllerType MapSettingsTypeToNPad(Settings::ControllerType type) {
switch (type) {
case Settings::ControllerType::ProController:
return Controller_NPad::NPadControllerType::ProController;
case Settings::ControllerType::DualJoycon:
return Controller_NPad::NPadControllerType::JoyDual;
case Settings::ControllerType::LeftJoycon:
return Controller_NPad::NPadControllerType::JoyLeft;
case Settings::ControllerType::RightJoycon:
return Controller_NPad::NPadControllerType::JoyRight;
default:
UNREACHABLE();
return Controller_NPad::NPadControllerType::JoyDual;
}
}
std::size_t Controller_NPad::NPadIdToIndex(u32 npad_id) {
switch (npad_id) {
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
return npad_id;
case 8:
case NPAD_HANDHELD:
return 8;
case 9:
case NPAD_UNKNOWN:
return 9;
default:
UNIMPLEMENTED_MSG("Unknown npad id {}", npad_id);
return 0;
}
}
u32 Controller_NPad::IndexToNPad(std::size_t index) {
switch (index) {
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
return static_cast<u32>(index);
case 8:
return NPAD_HANDHELD;
case 9:
return NPAD_UNKNOWN;
default:
UNIMPLEMENTED_MSG("Unknown npad index {}", index);
return 0;
};
}
Controller_NPad::Controller_NPad() = default;
Controller_NPad::~Controller_NPad() = default;
@@ -56,22 +107,32 @@ void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) {
controller.joy_styles.handheld.Assign(1);
controller.device_type.handheld.Assign(1);
controller.pad_assignment = NPadAssignments::Dual;
controller.properties.is_vertical.Assign(1);
controller.properties.use_plus.Assign(1);
controller.properties.use_minus.Assign(1);
break;
case NPadControllerType::JoyDual:
controller.joy_styles.joycon_dual.Assign(1);
controller.device_type.joycon_left.Assign(1);
controller.device_type.joycon_right.Assign(1);
controller.properties.is_vertical.Assign(1);
controller.properties.use_plus.Assign(1);
controller.properties.use_minus.Assign(1);
controller.pad_assignment = NPadAssignments::Dual;
break;
case NPadControllerType::JoyLeft:
controller.joy_styles.joycon_left.Assign(1);
controller.device_type.joycon_left.Assign(1);
controller.pad_assignment = NPadAssignments::Dual;
controller.properties.is_horizontal.Assign(1);
controller.properties.use_minus.Assign(1);
controller.pad_assignment = NPadAssignments::Single;
break;
case NPadControllerType::JoyRight:
controller.joy_styles.joycon_right.Assign(1);
controller.device_type.joycon_right.Assign(1);
controller.pad_assignment = NPadAssignments::Dual;
controller.properties.is_horizontal.Assign(1);
controller.properties.use_plus.Assign(1);
controller.pad_assignment = NPadAssignments::Single;
break;
case NPadControllerType::Pokeball:
controller.joy_styles.pokeball.Assign(1);
@@ -81,6 +142,9 @@ void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) {
case NPadControllerType::ProController:
controller.joy_styles.pro_controller.Assign(1);
controller.device_type.pro_controller.Assign(1);
controller.properties.is_vertical.Assign(1);
controller.properties.use_plus.Assign(1);
controller.properties.use_minus.Assign(1);
controller.pad_assignment = NPadAssignments::Single;
break;
}
@@ -90,14 +154,12 @@ void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) {
controller.single_color.button_color = 0;
controller.dual_color_error = ColorReadError::ReadOk;
controller.left_color.body_color = JOYCON_BODY_NEON_BLUE;
controller.left_color.button_color = JOYCON_BUTTONS_NEON_BLUE;
controller.right_color.body_color = JOYCON_BODY_NEON_RED;
controller.right_color.button_color = JOYCON_BUTTONS_NEON_RED;
controller.left_color.body_color = Settings::values.players[controller_idx].body_color_left;
controller.left_color.button_color = Settings::values.players[controller_idx].button_color_left;
controller.right_color.body_color = Settings::values.players[controller_idx].body_color_right;
controller.right_color.button_color =
Settings::values.players[controller_idx].button_color_right;
controller.properties.is_vertical.Assign(1); // TODO(ogniK): Swap joycons orientations
controller.properties.use_plus.Assign(1);
controller.properties.use_minus.Assign(1);
controller.battery_level[0] = BATTERY_FULL;
controller.battery_level[1] = BATTERY_FULL;
controller.battery_level[2] = BATTERY_FULL;
@@ -121,26 +183,109 @@ void Controller_NPad::OnInit() {
style.pro_controller.Assign(1);
style.pokeball.Assign(1);
}
std::transform(
Settings::values.players.begin(), Settings::values.players.end(),
connected_controllers.begin(), [](const Settings::PlayerInput& player) {
return ControllerHolder{MapSettingsTypeToNPad(player.type), player.connected};
});
std::stable_partition(connected_controllers.begin(), connected_controllers.begin() + 8,
[](const ControllerHolder& holder) { return holder.is_connected; });
// Account for handheld
if (connected_controllers[8].is_connected)
connected_controllers[8].type = NPadControllerType::Handheld;
supported_npad_id_types.resize(npad_id_list.size());
std::memcpy(supported_npad_id_types.data(), npad_id_list.data(),
npad_id_list.size() * sizeof(u32));
// Add a default dual joycon controller if none are present.
if (std::none_of(connected_controllers.begin(), connected_controllers.end(),
[](const ControllerHolder& controller) { return controller.is_connected; })) {
supported_npad_id_types.resize(npad_id_list.size());
std::memcpy(supported_npad_id_types.data(), npad_id_list.data(),
npad_id_list.size() * sizeof(u32));
AddNewController(PREFERRED_CONTROLLER);
AddNewController(NPadControllerType::JoyDual);
}
for (std::size_t i = 0; i < connected_controllers.size(); ++i) {
const auto& controller = connected_controllers[i];
if (controller.is_connected) {
AddNewControllerAt(controller.type, IndexToNPad(i));
}
}
}
void Controller_NPad::OnLoadInputDevices() {
std::transform(Settings::values.buttons.begin() + Settings::NativeButton::BUTTON_HID_BEGIN,
Settings::values.buttons.begin() + Settings::NativeButton::BUTTON_HID_END,
buttons.begin(), Input::CreateDevice<Input::ButtonDevice>);
std::transform(Settings::values.analogs.begin() + Settings::NativeAnalog::STICK_HID_BEGIN,
Settings::values.analogs.begin() + Settings::NativeAnalog::STICK_HID_END,
sticks.begin(), Input::CreateDevice<Input::AnalogDevice>);
const auto& players = Settings::values.players;
for (std::size_t i = 0; i < players.size(); ++i) {
std::transform(players[i].buttons.begin() + Settings::NativeButton::BUTTON_HID_BEGIN,
players[i].buttons.begin() + Settings::NativeButton::BUTTON_HID_END,
buttons[i].begin(), Input::CreateDevice<Input::ButtonDevice>);
std::transform(players[i].analogs.begin() + Settings::NativeAnalog::STICK_HID_BEGIN,
players[i].analogs.begin() + Settings::NativeAnalog::STICK_HID_END,
sticks[i].begin(), Input::CreateDevice<Input::AnalogDevice>);
}
}
void Controller_NPad::OnRelease() {}
void Controller_NPad::RequestPadStateUpdate(u32 npad_id) {
const auto controller_idx = NPadIdToIndex(npad_id);
const auto controller_type = connected_controllers[controller_idx].type;
if (!connected_controllers[controller_idx].is_connected) {
return;
}
auto& pad_state = npad_pad_states[controller_idx].pad_states;
auto& lstick_entry = npad_pad_states[controller_idx].l_stick;
auto& rstick_entry = npad_pad_states[controller_idx].r_stick;
const auto& button_state = buttons[controller_idx];
const auto& analog_state = sticks[controller_idx];
using namespace Settings::NativeButton;
pad_state.a.Assign(button_state[A - BUTTON_HID_BEGIN]->GetStatus());
pad_state.b.Assign(button_state[B - BUTTON_HID_BEGIN]->GetStatus());
pad_state.x.Assign(button_state[X - BUTTON_HID_BEGIN]->GetStatus());
pad_state.y.Assign(button_state[Y - BUTTON_HID_BEGIN]->GetStatus());
pad_state.l_stick.Assign(button_state[LStick - BUTTON_HID_BEGIN]->GetStatus());
pad_state.r_stick.Assign(button_state[RStick - BUTTON_HID_BEGIN]->GetStatus());
pad_state.l.Assign(button_state[L - BUTTON_HID_BEGIN]->GetStatus());
pad_state.r.Assign(button_state[R - BUTTON_HID_BEGIN]->GetStatus());
pad_state.zl.Assign(button_state[ZL - BUTTON_HID_BEGIN]->GetStatus());
pad_state.zr.Assign(button_state[ZR - BUTTON_HID_BEGIN]->GetStatus());
pad_state.plus.Assign(button_state[Plus - BUTTON_HID_BEGIN]->GetStatus());
pad_state.minus.Assign(button_state[Minus - BUTTON_HID_BEGIN]->GetStatus());
pad_state.d_left.Assign(button_state[DLeft - BUTTON_HID_BEGIN]->GetStatus());
pad_state.d_up.Assign(button_state[DUp - BUTTON_HID_BEGIN]->GetStatus());
pad_state.d_right.Assign(button_state[DRight - BUTTON_HID_BEGIN]->GetStatus());
pad_state.d_down.Assign(button_state[DDown - BUTTON_HID_BEGIN]->GetStatus());
pad_state.l_stick_left.Assign(button_state[LStick_Left - BUTTON_HID_BEGIN]->GetStatus());
pad_state.l_stick_up.Assign(button_state[LStick_Up - BUTTON_HID_BEGIN]->GetStatus());
pad_state.l_stick_right.Assign(button_state[LStick_Right - BUTTON_HID_BEGIN]->GetStatus());
pad_state.l_stick_down.Assign(button_state[LStick_Down - BUTTON_HID_BEGIN]->GetStatus());
pad_state.r_stick_left.Assign(button_state[RStick_Left - BUTTON_HID_BEGIN]->GetStatus());
pad_state.r_stick_up.Assign(button_state[RStick_Up - BUTTON_HID_BEGIN]->GetStatus());
pad_state.r_stick_right.Assign(button_state[RStick_Right - BUTTON_HID_BEGIN]->GetStatus());
pad_state.r_stick_down.Assign(button_state[RStick_Down - BUTTON_HID_BEGIN]->GetStatus());
pad_state.left_sl.Assign(button_state[SL - BUTTON_HID_BEGIN]->GetStatus());
pad_state.left_sr.Assign(button_state[SR - BUTTON_HID_BEGIN]->GetStatus());
const auto [stick_l_x_f, stick_l_y_f] =
analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetStatus();
const auto [stick_r_x_f, stick_r_y_f] =
analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]->GetStatus();
lstick_entry.x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX);
lstick_entry.y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX);
rstick_entry.x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX);
rstick_entry.y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX);
}
void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) {
if (!IsControllerActivated())
return;
@@ -176,97 +321,9 @@ void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) {
if (controller_type == NPadControllerType::None || !connected_controllers[i].is_connected) {
continue;
}
// Pad states
ControllerPadState pad_state{};
using namespace Settings::NativeButton;
pad_state.a.Assign(buttons[A - BUTTON_HID_BEGIN]->GetStatus());
pad_state.b.Assign(buttons[B - BUTTON_HID_BEGIN]->GetStatus());
pad_state.x.Assign(buttons[X - BUTTON_HID_BEGIN]->GetStatus());
pad_state.y.Assign(buttons[Y - BUTTON_HID_BEGIN]->GetStatus());
pad_state.l_stick.Assign(buttons[LStick - BUTTON_HID_BEGIN]->GetStatus());
pad_state.r_stick.Assign(buttons[RStick - BUTTON_HID_BEGIN]->GetStatus());
pad_state.l.Assign(buttons[L - BUTTON_HID_BEGIN]->GetStatus());
pad_state.r.Assign(buttons[R - BUTTON_HID_BEGIN]->GetStatus());
pad_state.zl.Assign(buttons[ZL - BUTTON_HID_BEGIN]->GetStatus());
pad_state.zr.Assign(buttons[ZR - BUTTON_HID_BEGIN]->GetStatus());
pad_state.plus.Assign(buttons[Plus - BUTTON_HID_BEGIN]->GetStatus());
pad_state.minus.Assign(buttons[Minus - BUTTON_HID_BEGIN]->GetStatus());
pad_state.d_left.Assign(buttons[DLeft - BUTTON_HID_BEGIN]->GetStatus());
pad_state.d_up.Assign(buttons[DUp - BUTTON_HID_BEGIN]->GetStatus());
pad_state.d_right.Assign(buttons[DRight - BUTTON_HID_BEGIN]->GetStatus());
pad_state.d_down.Assign(buttons[DDown - BUTTON_HID_BEGIN]->GetStatus());
pad_state.l_stick_left.Assign(buttons[LStick_Left - BUTTON_HID_BEGIN]->GetStatus());
pad_state.l_stick_up.Assign(buttons[LStick_Up - BUTTON_HID_BEGIN]->GetStatus());
pad_state.l_stick_right.Assign(buttons[LStick_Right - BUTTON_HID_BEGIN]->GetStatus());
pad_state.l_stick_down.Assign(buttons[LStick_Down - BUTTON_HID_BEGIN]->GetStatus());
pad_state.r_stick_left.Assign(buttons[RStick_Left - BUTTON_HID_BEGIN]->GetStatus());
pad_state.r_stick_up.Assign(buttons[RStick_Up - BUTTON_HID_BEGIN]->GetStatus());
pad_state.r_stick_right.Assign(buttons[RStick_Right - BUTTON_HID_BEGIN]->GetStatus());
pad_state.r_stick_down.Assign(buttons[RStick_Down - BUTTON_HID_BEGIN]->GetStatus());
pad_state.sl.Assign(buttons[SL - BUTTON_HID_BEGIN]->GetStatus());
pad_state.sr.Assign(buttons[SR - BUTTON_HID_BEGIN]->GetStatus());
AnalogPosition lstick_entry{};
AnalogPosition rstick_entry{};
const auto [stick_l_x_f, stick_l_y_f] =
sticks[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetStatus();
const auto [stick_r_x_f, stick_r_y_f] =
sticks[static_cast<std::size_t>(JoystickId::Joystick_Right)]->GetStatus();
lstick_entry.x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX);
lstick_entry.y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX);
rstick_entry.x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX);
rstick_entry.y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX);
if (controller_type == NPadControllerType::JoyLeft ||
controller_type == NPadControllerType::JoyRight) {
if (npad.properties.is_horizontal) {
ControllerPadState state{};
AnalogPosition temp_lstick_entry{};
AnalogPosition temp_rstick_entry{};
if (controller_type == NPadControllerType::JoyLeft) {
state.d_down.Assign(pad_state.d_left.Value());
state.d_left.Assign(pad_state.d_up.Value());
state.d_right.Assign(pad_state.d_down.Value());
state.d_up.Assign(pad_state.d_right.Value());
state.l.Assign(pad_state.l.Value() | pad_state.sl.Value());
state.r.Assign(pad_state.r.Value() | pad_state.sr.Value());
state.zl.Assign(pad_state.zl.Value());
state.plus.Assign(pad_state.minus.Value());
temp_lstick_entry = lstick_entry;
temp_rstick_entry = rstick_entry;
std::swap(temp_lstick_entry.x, temp_lstick_entry.y);
std::swap(temp_rstick_entry.x, temp_rstick_entry.y);
temp_lstick_entry.y *= -1;
} else if (controller_type == NPadControllerType::JoyRight) {
state.x.Assign(pad_state.a.Value());
state.a.Assign(pad_state.b.Value());
state.b.Assign(pad_state.y.Value());
state.y.Assign(pad_state.b.Value());
state.l.Assign(pad_state.l.Value() | pad_state.sl.Value());
state.r.Assign(pad_state.r.Value() | pad_state.sr.Value());
state.zr.Assign(pad_state.zr.Value());
state.plus.Assign(pad_state.plus.Value());
temp_lstick_entry = lstick_entry;
temp_rstick_entry = rstick_entry;
std::swap(temp_lstick_entry.x, temp_lstick_entry.y);
std::swap(temp_rstick_entry.x, temp_rstick_entry.y);
temp_rstick_entry.x *= -1;
}
pad_state.raw = state.raw;
lstick_entry = temp_lstick_entry;
rstick_entry = temp_rstick_entry;
}
}
const u32 npad_index = static_cast<u32>(i);
RequestPadStateUpdate(npad_index);
auto& pad_state = npad_pad_states[npad_index];
auto& main_controller =
npad.main_controller_states.npad[npad.main_controller_states.common.last_entry_index];
@@ -281,20 +338,64 @@ void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) {
auto& libnx_entry = npad.libnx.npad[npad.libnx.common.last_entry_index];
if (hold_type == NpadHoldType::Horizontal) {
// TODO(ogniK): Remap buttons for different orientations
ControllerPadState state{};
AnalogPosition temp_lstick_entry{};
AnalogPosition temp_rstick_entry{};
if (controller_type == NPadControllerType::JoyLeft) {
state.d_down.Assign(pad_state.pad_states.d_left.Value());
state.d_left.Assign(pad_state.pad_states.d_up.Value());
state.d_right.Assign(pad_state.pad_states.d_down.Value());
state.d_up.Assign(pad_state.pad_states.d_right.Value());
state.l.Assign(pad_state.pad_states.l.Value() |
pad_state.pad_states.left_sl.Value());
state.r.Assign(pad_state.pad_states.r.Value() |
pad_state.pad_states.left_sr.Value());
state.zl.Assign(pad_state.pad_states.zl.Value());
state.plus.Assign(pad_state.pad_states.minus.Value());
temp_lstick_entry = pad_state.l_stick;
temp_rstick_entry = pad_state.r_stick;
std::swap(temp_lstick_entry.x, temp_lstick_entry.y);
std::swap(temp_rstick_entry.x, temp_rstick_entry.y);
temp_lstick_entry.y *= -1;
} else if (controller_type == NPadControllerType::JoyRight) {
state.x.Assign(pad_state.pad_states.a.Value());
state.a.Assign(pad_state.pad_states.b.Value());
state.b.Assign(pad_state.pad_states.y.Value());
state.y.Assign(pad_state.pad_states.b.Value());
state.l.Assign(pad_state.pad_states.l.Value() |
pad_state.pad_states.right_sl.Value());
state.r.Assign(pad_state.pad_states.r.Value() |
pad_state.pad_states.right_sr.Value());
state.zr.Assign(pad_state.pad_states.zr.Value());
state.plus.Assign(pad_state.pad_states.plus.Value());
temp_lstick_entry = pad_state.l_stick;
temp_rstick_entry = pad_state.r_stick;
std::swap(temp_lstick_entry.x, temp_lstick_entry.y);
std::swap(temp_rstick_entry.x, temp_rstick_entry.y);
temp_rstick_entry.x *= -1;
}
pad_state.pad_states.raw = state.raw;
pad_state.l_stick = temp_lstick_entry;
pad_state.r_stick = temp_rstick_entry;
}
libnx_entry.connection_status.raw = 0;
switch (controller_type) {
case NPadControllerType::Handheld:
handheld_entry.connection_status.raw = 0;
handheld_entry.connection_status.IsConnected.Assign(1);
if (!Settings::values.use_docked_mode) {
handheld_entry.connection_status.IsWired.Assign(1);
}
handheld_entry.pad_states.raw = pad_state.raw;
handheld_entry.l_stick = lstick_entry;
handheld_entry.r_stick = rstick_entry;
handheld_entry.connection_status.IsWired.Assign(1);
handheld_entry.connection_status.IsLeftJoyConnected.Assign(1);
handheld_entry.connection_status.IsRightJoyConnected.Assign(1);
handheld_entry.connection_status.IsLeftJoyWired.Assign(1);
handheld_entry.connection_status.IsRightJoyWired.Assign(1);
handheld_entry.pad.pad_states.raw = pad_state.pad_states.raw;
handheld_entry.pad.l_stick = pad_state.l_stick;
handheld_entry.pad.r_stick = pad_state.r_stick;
break;
case NPadControllerType::JoyDual:
dual_entry.connection_status.raw = 0;
@@ -307,24 +408,25 @@ void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) {
libnx_entry.connection_status.IsRightJoyConnected.Assign(1);
libnx_entry.connection_status.IsConnected.Assign(1);
dual_entry.pad_states.raw = pad_state.raw;
dual_entry.l_stick = lstick_entry;
dual_entry.r_stick = rstick_entry;
dual_entry.pad.pad_states.raw = pad_state.pad_states.raw;
dual_entry.pad.l_stick = pad_state.l_stick;
dual_entry.pad.r_stick = pad_state.r_stick;
break;
case NPadControllerType::JoyLeft:
left_entry.connection_status.raw = 0;
left_entry.connection_status.IsConnected.Assign(1);
left_entry.pad_states.raw = pad_state.raw;
left_entry.l_stick = lstick_entry;
left_entry.r_stick = rstick_entry;
left_entry.pad.pad_states.raw = pad_state.pad_states.raw;
left_entry.pad.l_stick = pad_state.l_stick;
left_entry.pad.r_stick = pad_state.r_stick;
break;
case NPadControllerType::JoyRight:
right_entry.connection_status.raw = 0;
right_entry.connection_status.IsConnected.Assign(1);
right_entry.pad_states.raw = pad_state.raw;
right_entry.l_stick = lstick_entry;
right_entry.r_stick = rstick_entry;
right_entry.pad.pad_states.raw = pad_state.pad_states.raw;
right_entry.pad.l_stick = pad_state.l_stick;
right_entry.pad.r_stick = pad_state.r_stick;
break;
case NPadControllerType::Pokeball:
pokeball_entry.connection_status.raw = 0;
@@ -332,30 +434,30 @@ void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) {
pokeball_entry.connection_status.IsConnected.Assign(1);
pokeball_entry.connection_status.IsWired.Assign(1);
pokeball_entry.pad_states.raw = pad_state.raw;
pokeball_entry.l_stick = lstick_entry;
pokeball_entry.r_stick = rstick_entry;
pokeball_entry.pad.pad_states.raw = pad_state.pad_states.raw;
pokeball_entry.pad.l_stick = pad_state.l_stick;
pokeball_entry.pad.r_stick = pad_state.r_stick;
break;
case NPadControllerType::ProController:
main_controller.connection_status.raw = 0;
main_controller.connection_status.IsConnected.Assign(1);
main_controller.connection_status.IsWired.Assign(1);
main_controller.pad_states.raw = pad_state.raw;
main_controller.l_stick = lstick_entry;
main_controller.r_stick = rstick_entry;
main_controller.pad.pad_states.raw = pad_state.pad_states.raw;
main_controller.pad.l_stick = pad_state.l_stick;
main_controller.pad.r_stick = pad_state.r_stick;
break;
}
// LibNX exclusively uses this section, so we always update it since LibNX doesn't activate
// any controllers.
libnx_entry.pad_states.raw = pad_state.raw;
libnx_entry.l_stick = lstick_entry;
libnx_entry.r_stick = rstick_entry;
libnx_entry.pad.pad_states.raw = pad_state.pad_states.raw;
libnx_entry.pad.l_stick = pad_state.l_stick;
libnx_entry.pad.r_stick = pad_state.r_stick;
}
std::memcpy(data + NPAD_OFFSET, shared_memory_entries.data(),
shared_memory_entries.size() * sizeof(NPadEntry));
} // namespace Service::HID
}
void Controller_NPad::SetSupportedStyleSet(NPadType style_set) {
style.raw = style_set.raw;
@@ -370,14 +472,29 @@ void Controller_NPad::SetSupportedNPadIdTypes(u8* data, std::size_t length) {
supported_npad_id_types.clear();
supported_npad_id_types.resize(length / sizeof(u32));
std::memcpy(supported_npad_id_types.data(), data, length);
bool had_controller_update = false;
for (std::size_t i = 0; i < connected_controllers.size(); i++) {
auto& controller = connected_controllers[i];
if (!controller.is_connected) {
continue;
}
if (!IsControllerSupported(PREFERRED_CONTROLLER)) {
controller.type = DecideBestController(PREFERRED_CONTROLLER);
InitNewlyAddedControler(i);
const auto requested_controller =
i <= MAX_NPAD_ID ? MapSettingsTypeToNPad(Settings::values.players[i].type)
: NPadControllerType::Handheld;
if (!IsControllerSupported(requested_controller)) {
const auto is_handheld = requested_controller == NPadControllerType::Handheld;
if (is_handheld) {
controller.type = NPadControllerType::None;
controller.is_connected = false;
AddNewController(requested_controller);
} else {
controller.type = requested_controller;
InitNewlyAddedControler(i);
}
had_controller_update = true;
}
if (had_controller_update) {
styleset_changed_event->Signal();
}
}
}
@@ -411,15 +528,7 @@ void Controller_NPad::VibrateController(const std::vector<u32>& controller_ids,
return;
}
for (std::size_t i = 0; i < controller_ids.size(); i++) {
std::size_t controller_pos = i;
// Handheld controller conversion
if (controller_pos == NPAD_HANDHELD) {
controller_pos = 8;
}
// Unknown controller conversion
if (controller_pos == NPAD_UNKNOWN) {
controller_pos = 9;
}
std::size_t controller_pos = NPadIdToIndex(static_cast<u32>(i));
if (connected_controllers[controller_pos].is_connected) {
// TODO(ogniK): Vibrate the physical controller
}
@@ -438,7 +547,9 @@ Kernel::SharedPtr<Kernel::Event> Controller_NPad::GetStyleSetChangedEvent() cons
Controller_NPad::Vibration Controller_NPad::GetLastVibration() const {
return last_processed_vibration;
}
void Controller_NPad::AddNewController(NPadControllerType controller) {
controller = DecideBestController(controller);
if (controller == NPadControllerType::Handheld) {
connected_controllers[8] = {controller, true};
InitNewlyAddedControler(8);
@@ -456,16 +567,54 @@ void Controller_NPad::AddNewController(NPadControllerType controller) {
InitNewlyAddedControler(controller_id);
}
void Controller_NPad::ConnectNPad(u32 npad_id) {
if (npad_id >= connected_controllers.size())
void Controller_NPad::AddNewControllerAt(NPadControllerType controller, u32 npad_id) {
controller = DecideBestController(controller);
if (controller == NPadControllerType::Handheld) {
connected_controllers[NPadIdToIndex(NPAD_HANDHELD)] = {controller, true};
InitNewlyAddedControler(NPadIdToIndex(NPAD_HANDHELD));
return;
connected_controllers[npad_id].is_connected = true;
}
connected_controllers[npad_id] = {controller, true};
InitNewlyAddedControler(npad_id);
}
void Controller_NPad::ConnectNPad(u32 npad_id) {
connected_controllers[NPadIdToIndex(npad_id)].is_connected = true;
}
void Controller_NPad::DisconnectNPad(u32 npad_id) {
if (npad_id >= connected_controllers.size())
return;
connected_controllers[npad_id].is_connected = false;
connected_controllers[NPadIdToIndex(npad_id)].is_connected = false;
}
bool Controller_NPad::IsControllerSupported(NPadControllerType controller) {
if (controller == NPadControllerType::Handheld) {
// Handheld is not even a supported type, lets stop here
if (std::find(supported_npad_id_types.begin(), supported_npad_id_types.end(),
NPAD_HANDHELD) == supported_npad_id_types.end()) {
return false;
}
// Handheld should not be supported in docked mode
if (Settings::values.use_docked_mode) {
return false;
}
}
switch (controller) {
case NPadControllerType::ProController:
return style.pro_controller;
case NPadControllerType::Handheld:
return style.handheld;
case NPadControllerType::JoyDual:
return style.joycon_dual;
case NPadControllerType::JoyLeft:
return style.joycon_left;
case NPadControllerType::JoyRight:
return style.joycon_right;
case NPadControllerType::Pokeball:
return style.pokeball;
default:
return false;
}
}
Controller_NPad::LedPattern Controller_NPad::GetLedPattern(u32 npad_id) {
@@ -499,6 +648,36 @@ void Controller_NPad::SetVibrationEnabled(bool can_vibrate) {
can_controllers_vibrate = can_vibrate;
}
void Controller_NPad::ClearAllConnectedControllers() {
for (auto& controller : connected_controllers) {
if (controller.is_connected && controller.type != NPadControllerType::None) {
controller.type = NPadControllerType::None;
controller.is_connected = false;
}
}
}
void Controller_NPad::DisconnectAllConnectedControllers() {
std::for_each(connected_controllers.begin(), connected_controllers.end(),
[](ControllerHolder& controller) { controller.is_connected = false; });
}
void Controller_NPad::ConnectAllDisconnectedControllers() {
std::for_each(connected_controllers.begin(), connected_controllers.end(),
[](ControllerHolder& controller) {
if (controller.type != NPadControllerType::None && !controller.is_connected) {
controller.is_connected = false;
}
});
}
void Controller_NPad::ClearAllControllers() {
std::for_each(connected_controllers.begin(), connected_controllers.end(),
[](ControllerHolder& controller) {
controller.type = NPadControllerType::None;
controller.is_connected = false;
});
}
bool Controller_NPad::IsControllerSupported(NPadControllerType controller) const {
const bool support_handheld =
std::find(supported_npad_id_types.begin(), supported_npad_id_types.end(), NPAD_HANDHELD) !=

View File

@@ -5,13 +5,18 @@
#pragma once
#include <array>
#include "common/bit_field.h"
#include "common/common_types.h"
#include "core/frontend/input.h"
#include "core/hle/kernel/event.h"
#include "core/hle/service/hid/controllers/controller_base.h"
#include "core/settings.h"
namespace Service::HID {
constexpr u32 NPAD_HANDHELD = 32;
constexpr u32 NPAD_UNKNOWN = 16; // TODO(ogniK): What is this?
class Controller_NPad final : public ControllerBase {
public:
Controller_NPad();
@@ -107,11 +112,19 @@ public:
Vibration GetLastVibration() const;
void AddNewController(NPadControllerType controller);
void AddNewControllerAt(NPadControllerType controller, u32 npad_id);
void ConnectNPad(u32 npad_id);
void DisconnectNPad(u32 npad_id);
LedPattern GetLedPattern(u32 npad_id);
void SetVibrationEnabled(bool can_vibrate);
void ClearAllConnectedControllers();
void DisconnectAllConnectedControllers();
void ConnectAllDisconnectedControllers();
void ClearAllControllers();
static std::size_t NPadIdToIndex(u32 npad_id);
static u32 IndexToNPad(std::size_t index);
private:
struct CommonHeader {
@@ -164,8 +177,11 @@ private:
BitField<23, 1, u64_le> r_stick_down;
// Not always active?
BitField<24, 1, u64_le> sl;
BitField<25, 1, u64_le> sr;
BitField<24, 1, u64_le> left_sl;
BitField<25, 1, u64_le> left_sr;
BitField<26, 1, u64_le> right_sl;
BitField<27, 1, u64_le> right_sr;
};
};
static_assert(sizeof(ControllerPadState) == 8, "ControllerPadState is an invalid size");
@@ -189,12 +205,17 @@ private:
};
static_assert(sizeof(ConnectionState) == 4, "ConnectionState is an invalid size");
struct GenericStates {
s64_le timestamp;
s64_le timestamp2;
struct ControllerPad {
ControllerPadState pad_states;
AnalogPosition l_stick;
AnalogPosition r_stick;
};
static_assert(sizeof(ControllerPad) == 0x18, "ControllerPad is an invalid size");
struct GenericStates {
s64_le timestamp;
s64_le timestamp2;
ControllerPad pad;
ConnectionState connection_status;
};
static_assert(sizeof(GenericStates) == 0x30, "NPadGenericStates is an invalid size");
@@ -266,15 +287,20 @@ private:
static_assert(sizeof(NPadEntry) == 0x5000, "NPadEntry is an invalid size");
struct ControllerHolder {
Controller_NPad::NPadControllerType type;
NPadControllerType type;
bool is_connected;
};
NPadType style{};
std::array<NPadEntry, 10> shared_memory_entries{};
std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID>
std::array<
std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID>,
10>
buttons;
std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID> sticks;
std::array<
std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID>,
10>
sticks;
std::vector<u32> supported_npad_id_types{};
NpadHoldType hold_type{NpadHoldType::Vertical};
Kernel::SharedPtr<Kernel::Event> styleset_changed_event;
@@ -285,5 +311,8 @@ private:
void InitNewlyAddedControler(std::size_t controller_idx);
bool IsControllerSupported(NPadControllerType controller) const;
NPadControllerType DecideBestController(NPadControllerType priority) const;
void RequestPadStateUpdate(u32 npad_id);
std::array<ControllerPad, 10> npad_pad_states{};
bool IsControllerSupported(NPadControllerType controller);
};
} // namespace Service::HID

View File

@@ -41,16 +41,17 @@ void Controller_Touchscreen::OnUpdate(u8* data, std::size_t size) {
const auto [x, y, pressed] = touch_device->GetStatus();
auto& touch_entry = cur_entry.states[0];
if (pressed) {
touch_entry.attribute.raw = 0;
if (pressed && Settings::values.touchscreen.enabled) {
touch_entry.x = static_cast<u16>(x * Layout::ScreenUndocked::Width);
touch_entry.y = static_cast<u16>(y * Layout::ScreenUndocked::Height);
touch_entry.diameter_x = 15;
touch_entry.diameter_y = 15;
touch_entry.rotation_angle = 0;
touch_entry.diameter_x = Settings::values.touchscreen.diameter_x;
touch_entry.diameter_y = Settings::values.touchscreen.diameter_y;
touch_entry.rotation_angle = Settings::values.touchscreen.rotation_angle;
const u64 tick = CoreTiming::GetTicks();
touch_entry.delta_time = tick - last_touch;
last_touch = tick;
touch_entry.finger = 0;
touch_entry.finger = Settings::values.touchscreen.finger;
cur_entry.entry_count = 1;
} else {
cur_entry.entry_count = 0;
@@ -60,6 +61,6 @@ void Controller_Touchscreen::OnUpdate(u8* data, std::size_t size) {
}
void Controller_Touchscreen::OnLoadInputDevices() {
touch_device = Input::CreateDevice<Input::TouchDevice>(Settings::values.touch_device);
touch_device = Input::CreateDevice<Input::TouchDevice>(Settings::values.touchscreen.device);
}
} // namespace Service::HID

View File

@@ -4,6 +4,7 @@
#pragma once
#include "common/bit_field.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
@@ -29,9 +30,18 @@ public:
void OnLoadInputDevices() override;
private:
struct Attributes {
union {
u32 raw{};
BitField<0, 1, u32_le> start_touch;
BitField<1, 1, u32_le> end_touch;
};
};
static_assert(sizeof(Attributes) == 0x4, "Attributes is an invalid size");
struct TouchState {
u64_le delta_time;
u32_le attribute;
Attributes attribute;
u32_le finger;
u32_le x;
u32_le y;

View File

@@ -34,8 +34,8 @@
namespace Service::HID {
// Updating period for each HID device.
// TODO(shinyquagsire23): These need better values.
constexpr u64 pad_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100;
// TODO(ogniK): Find actual polling rate of hid
constexpr u64 pad_update_ticks = CoreTiming::BASE_CLOCK_RATE / 66;
constexpr u64 accelerometer_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100;
constexpr u64 gyroscope_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100;
constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000;
@@ -286,10 +286,10 @@ public:
{519, nullptr, "GetPalmaOperationResult"},
{520, nullptr, "ReadPalmaPlayLog"},
{521, nullptr, "ResetPalmaPlayLog"},
{522, nullptr, "SetIsPalmaAllConnectable"},
{522, &Hid::SetIsPalmaAllConnectable, "SetIsPalmaAllConnectable"},
{523, nullptr, "SetIsPalmaPairedConnectable"},
{524, nullptr, "PairPalma"},
{525, nullptr, "SetPalmaBoostMode"},
{525, &Hid::SetPalmaBoostMode, "SetPalmaBoostMode"},
{1000, nullptr, "SetNpadCommunicationMode"},
{1001, nullptr, "GetNpadCommunicationMode"},
};
@@ -596,6 +596,18 @@ private:
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_HID, "(STUBBED) called");
}
void SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_HID, "(STUBBED) called");
}
void SetPalmaBoostMode(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_HID, "(STUBBED) called");
}
};
class HidDbg final : public ServiceFramework<HidDbg> {

View File

@@ -4,7 +4,10 @@
#include <memory>
#include <fmt/format.h>
#include <mbedtls/sha256.h>
#include "common/alignment.h"
#include "common/hex_util.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/process.h"
#include "core/hle/service/ldr/ldr.h"
@@ -13,6 +16,38 @@
namespace Service::LDR {
namespace ErrCodes {
enum {
InvalidMemoryState = 51,
InvalidNRO = 52,
InvalidNRR = 53,
MissingNRRHash = 54,
MaximumNRO = 55,
MaximumNRR = 56,
AlreadyLoaded = 57,
InvalidAlignment = 81,
InvalidSize = 82,
InvalidNROAddress = 84,
InvalidNRRAddress = 85,
NotInitialized = 87,
};
}
constexpr ResultCode ERROR_INVALID_MEMORY_STATE(ErrorModule::Loader, ErrCodes::InvalidMemoryState);
constexpr ResultCode ERROR_INVALID_NRO(ErrorModule::Loader, ErrCodes::InvalidNRO);
constexpr ResultCode ERROR_INVALID_NRR(ErrorModule::Loader, ErrCodes::InvalidNRR);
constexpr ResultCode ERROR_MISSING_NRR_HASH(ErrorModule::Loader, ErrCodes::MissingNRRHash);
constexpr ResultCode ERROR_MAXIMUM_NRO(ErrorModule::Loader, ErrCodes::MaximumNRO);
constexpr ResultCode ERROR_MAXIMUM_NRR(ErrorModule::Loader, ErrCodes::MaximumNRR);
constexpr ResultCode ERROR_ALREADY_LOADED(ErrorModule::Loader, ErrCodes::AlreadyLoaded);
constexpr ResultCode ERROR_INVALID_ALIGNMENT(ErrorModule::Loader, ErrCodes::InvalidAlignment);
constexpr ResultCode ERROR_INVALID_SIZE(ErrorModule::Loader, ErrCodes::InvalidSize);
constexpr ResultCode ERROR_INVALID_NRO_ADDRESS(ErrorModule::Loader, ErrCodes::InvalidNROAddress);
constexpr ResultCode ERROR_INVALID_NRR_ADDRESS(ErrorModule::Loader, ErrCodes::InvalidNRRAddress);
constexpr ResultCode ERROR_NOT_INITIALIZED(ErrorModule::Loader, ErrCodes::NotInitialized);
constexpr u64 MAXIMUM_LOADED_RO = 0x40;
class DebugMonitor final : public ServiceFramework<DebugMonitor> {
public:
explicit DebugMonitor() : ServiceFramework{"ldr:dmnt"} {
@@ -64,9 +99,9 @@ public:
// clang-format off
static const FunctionInfo functions[] = {
{0, &RelocatableObject::LoadNro, "LoadNro"},
{1, nullptr, "UnloadNro"},
{1, &RelocatableObject::UnloadNro, "UnloadNro"},
{2, &RelocatableObject::LoadNrr, "LoadNrr"},
{3, nullptr, "UnloadNrr"},
{3, &RelocatableObject::UnloadNrr, "UnloadNrr"},
{4, &RelocatableObject::Initialize, "Initialize"},
};
// clang-format on
@@ -75,9 +110,123 @@ public:
}
void LoadNrr(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
rp.Skip(2, false);
const VAddr nrr_addr{rp.Pop<VAddr>()};
const u64 nrr_size{rp.Pop<u64>()};
if (!initialized) {
LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_NOT_INITIALIZED);
return;
}
if (nrr.size() >= MAXIMUM_LOADED_RO) {
LOG_ERROR(Service_LDR, "Loading new NRR would exceed the maximum number of loaded NRRs "
"(0x40)! Failing...");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_MAXIMUM_NRR);
return;
}
// NRR Address does not fall on 0x1000 byte boundary
if (!Common::Is4KBAligned(nrr_addr)) {
LOG_ERROR(Service_LDR, "NRR Address has invalid alignment (actual {:016X})!", nrr_addr);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_INVALID_ALIGNMENT);
return;
}
// NRR Size is zero or causes overflow
if (nrr_addr + nrr_size <= nrr_addr || nrr_size == 0 || !Common::Is4KBAligned(nrr_size)) {
LOG_ERROR(Service_LDR, "NRR Size is invalid! (nrr_address={:016X}, nrr_size={:016X})",
nrr_addr, nrr_size);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_INVALID_SIZE);
return;
}
// Read NRR data from memory
std::vector<u8> nrr_data(nrr_size);
Memory::ReadBlock(nrr_addr, nrr_data.data(), nrr_size);
NRRHeader header;
std::memcpy(&header, nrr_data.data(), sizeof(NRRHeader));
if (header.magic != Common::MakeMagic('N', 'R', 'R', '0')) {
LOG_ERROR(Service_LDR, "NRR did not have magic 'NRR0' (actual {:08X})!", header.magic);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_INVALID_NRR);
return;
}
if (header.size != nrr_size) {
LOG_ERROR(Service_LDR,
"NRR header reported size did not match LoadNrr parameter size! "
"(header_size={:016X}, loadnrr_size={:016X})",
header.size, nrr_size);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_INVALID_SIZE);
return;
}
if (Core::CurrentProcess()->GetTitleID() != header.title_id) {
LOG_ERROR(Service_LDR,
"Attempting to load NRR with title ID other than current process. (actual "
"{:016X})!",
header.title_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_INVALID_NRR);
return;
}
std::vector<SHA256Hash> hashes;
// Copy all hashes in the NRR (specified by hash count/hash offset) into vector.
for (std::size_t i = header.hash_offset;
i < (header.hash_offset + (header.hash_count * sizeof(SHA256Hash))); i += 8) {
SHA256Hash hash;
std::memcpy(hash.data(), nrr_data.data() + i, sizeof(SHA256Hash));
hashes.emplace_back(hash);
}
nrr.insert_or_assign(nrr_addr, std::move(hashes));
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void UnloadNrr(Kernel::HLERequestContext& ctx) {
if (!initialized) {
LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_NOT_INITIALIZED);
return;
}
IPC::RequestParser rp{ctx};
rp.Skip(2, false);
const auto nrr_addr{rp.Pop<VAddr>()};
if (!Common::Is4KBAligned(nrr_addr)) {
LOG_ERROR(Service_LDR, "NRR Address has invalid alignment (actual {:016X})!", nrr_addr);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_INVALID_ALIGNMENT);
return;
}
const auto iter = nrr.find(nrr_addr);
if (iter == nrr.end()) {
LOG_ERROR(Service_LDR,
"Attempting to unload NRR which has not been loaded! (addr={:016X})",
nrr_addr);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_INVALID_NRR_ADDRESS);
return;
}
nrr.erase(iter);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_LDR, "(STUBBED) called");
}
void LoadNro(Kernel::HLERequestContext& ctx) {
@@ -88,33 +237,253 @@ public:
const VAddr bss_addr{rp.Pop<VAddr>()};
const u64 bss_size{rp.Pop<u64>()};
if (!initialized) {
LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_NOT_INITIALIZED);
return;
}
if (nro.size() >= MAXIMUM_LOADED_RO) {
LOG_ERROR(Service_LDR, "Loading new NRO would exceed the maximum number of loaded NROs "
"(0x40)! Failing...");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_MAXIMUM_NRO);
return;
}
// NRO Address does not fall on 0x1000 byte boundary
if (!Common::Is4KBAligned(nro_addr)) {
LOG_ERROR(Service_LDR, "NRO Address has invalid alignment (actual {:016X})!", nro_addr);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_INVALID_ALIGNMENT);
return;
}
// NRO Size or BSS Size is zero or causes overflow
const auto nro_size_valid =
nro_size != 0 && nro_addr + nro_size > nro_addr && Common::Is4KBAligned(nro_size);
const auto bss_size_valid =
nro_size + bss_size >= nro_size && (bss_size == 0 || bss_addr + bss_size > bss_addr);
if (!nro_size_valid || !bss_size_valid) {
LOG_ERROR(Service_LDR,
"NRO Size or BSS Size is invalid! (nro_address={:016X}, nro_size={:016X}, "
"bss_address={:016X}, bss_size={:016X})",
nro_addr, nro_size, bss_addr, bss_size);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_INVALID_SIZE);
return;
}
// Read NRO data from memory
std::vector<u8> nro_data(nro_size);
Memory::ReadBlock(nro_addr, nro_data.data(), nro_size);
// Load NRO as new executable module
const VAddr addr{*Core::CurrentProcess()->VMManager().FindFreeRegion(nro_size + bss_size)};
Loader::AppLoader_NRO::LoadNro(nro_data, fmt::format("nro-{:08x}", addr), addr);
SHA256Hash hash{};
mbedtls_sha256(nro_data.data(), nro_data.size(), hash.data(), 0);
// TODO(bunnei): This is an incomplete implementation. It was tested with Super Mario Party.
// It is currently missing:
// - Signature checks with LoadNRR
// - Checking if a module has already been loaded
// - Using/validating BSS, etc. params (these are used from NRO header instead)
// - Error checking
// - ...Probably other things
// NRO Hash is already loaded
if (std::any_of(nro.begin(), nro.end(), [&hash](const std::pair<VAddr, NROInfo>& info) {
return info.second.hash == hash;
})) {
LOG_ERROR(Service_LDR, "NRO is already loaded!");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_ALREADY_LOADED);
return;
}
// NRO Hash is not in any loaded NRR
if (!IsValidNROHash(hash)) {
LOG_ERROR(Service_LDR,
"NRO hash is not present in any currently loaded NRRs (hash={})!",
Common::HexArrayToString(hash));
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_MISSING_NRR_HASH);
return;
}
NROHeader header;
std::memcpy(&header, nro_data.data(), sizeof(NROHeader));
if (!IsValidNRO(header, nro_size, bss_size)) {
LOG_ERROR(Service_LDR, "NRO was invalid!");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_INVALID_NRO);
return;
}
// Load NRO as new executable module
auto* process = Core::CurrentProcess();
auto& vm_manager = process->VMManager();
auto map_address = vm_manager.FindFreeRegion(nro_size + bss_size);
if (!map_address.Succeeded() ||
*map_address + nro_size + bss_size > vm_manager.GetAddressSpaceEndAddress()) {
LOG_ERROR(Service_LDR,
"General error while allocation memory or no available memory to allocate!");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_INVALID_MEMORY_STATE);
return;
}
ASSERT(process->MirrorMemory(*map_address, nro_addr, nro_size,
Kernel::MemoryState::ModuleCodeStatic) == RESULT_SUCCESS);
ASSERT(process->UnmapMemory(nro_addr, 0, nro_size) == RESULT_SUCCESS);
if (bss_size > 0) {
ASSERT(process->MirrorMemory(*map_address + nro_size, bss_addr, bss_size,
Kernel::MemoryState::ModuleCodeStatic) == RESULT_SUCCESS);
ASSERT(process->UnmapMemory(bss_addr, 0, bss_size) == RESULT_SUCCESS);
}
vm_manager.ReprotectRange(*map_address, header.text_size,
Kernel::VMAPermission::ReadExecute);
vm_manager.ReprotectRange(*map_address + header.ro_offset, header.ro_size,
Kernel::VMAPermission::Read);
vm_manager.ReprotectRange(*map_address + header.rw_offset, header.rw_size,
Kernel::VMAPermission::ReadWrite);
Core::System::GetInstance().ArmInterface(0).ClearInstructionCache();
Core::System::GetInstance().ArmInterface(1).ClearInstructionCache();
Core::System::GetInstance().ArmInterface(2).ClearInstructionCache();
Core::System::GetInstance().ArmInterface(3).ClearInstructionCache();
nro.insert_or_assign(*map_address, NROInfo{hash, nro_size + bss_size});
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push(addr);
LOG_WARNING(Service_LDR, "(STUBBED) called");
rb.Push(*map_address);
}
void UnloadNro(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
rp.Skip(2, false);
const VAddr mapped_addr{rp.PopRaw<VAddr>()};
const VAddr heap_addr{rp.PopRaw<VAddr>()};
if (!initialized) {
LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_NOT_INITIALIZED);
return;
}
if (!Common::Is4KBAligned(mapped_addr) || !Common::Is4KBAligned(heap_addr)) {
LOG_ERROR(Service_LDR,
"NRO/BSS Address has invalid alignment (actual nro_addr={:016X}, "
"bss_addr={:016X})!",
mapped_addr, heap_addr);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_INVALID_ALIGNMENT);
return;
}
const auto iter = nro.find(mapped_addr);
if (iter == nro.end()) {
LOG_ERROR(Service_LDR,
"The NRO attempting to unmap was not mapped or has an invalid address "
"(actual {:016X})!",
mapped_addr);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_INVALID_NRO_ADDRESS);
return;
}
auto* process = Core::CurrentProcess();
auto& vm_manager = process->VMManager();
const auto& nro_size = iter->second.size;
ASSERT(process->MirrorMemory(heap_addr, mapped_addr, nro_size,
Kernel::MemoryState::ModuleCodeStatic) == RESULT_SUCCESS);
ASSERT(process->UnmapMemory(mapped_addr, 0, nro_size) == RESULT_SUCCESS);
Core::System::GetInstance().ArmInterface(0).ClearInstructionCache();
Core::System::GetInstance().ArmInterface(1).ClearInstructionCache();
Core::System::GetInstance().ArmInterface(2).ClearInstructionCache();
Core::System::GetInstance().ArmInterface(3).ClearInstructionCache();
nro.erase(iter);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void Initialize(Kernel::HLERequestContext& ctx) {
initialized = true;
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_LDR, "(STUBBED) called");
}
private:
using SHA256Hash = std::array<u8, 0x20>;
struct NROHeader {
u32_le entrypoint_insn;
u32_le mod_offset;
INSERT_PADDING_WORDS(2);
u32_le magic;
INSERT_PADDING_WORDS(1);
u32_le nro_size;
INSERT_PADDING_WORDS(1);
u32_le text_offset;
u32_le text_size;
u32_le ro_offset;
u32_le ro_size;
u32_le rw_offset;
u32_le rw_size;
u32_le bss_size;
INSERT_PADDING_WORDS(1);
std::array<u8, 0x20> build_id;
INSERT_PADDING_BYTES(0x20);
};
static_assert(sizeof(NROHeader) == 0x80, "NROHeader has invalid size.");
struct NRRHeader {
u32_le magic;
INSERT_PADDING_BYTES(0x1C);
u64_le title_id_mask;
u64_le title_id_pattern;
std::array<u8, 0x100> modulus;
std::array<u8, 0x100> signature_1;
std::array<u8, 0x100> signature_2;
u64_le title_id;
u32_le size;
INSERT_PADDING_BYTES(4);
u32_le hash_offset;
u32_le hash_count;
INSERT_PADDING_BYTES(8);
};
static_assert(sizeof(NRRHeader) == 0x350, "NRRHeader has incorrect size.");
struct NROInfo {
SHA256Hash hash;
u64 size;
};
bool initialized = false;
std::map<VAddr, NROInfo> nro;
std::map<VAddr, std::vector<SHA256Hash>> nrr;
bool IsValidNROHash(const SHA256Hash& hash) {
return std::any_of(
nrr.begin(), nrr.end(), [&hash](const std::pair<VAddr, std::vector<SHA256Hash>>& p) {
return std::find(p.second.begin(), p.second.end(), hash) != p.second.end();
});
}
static bool IsValidNRO(const NROHeader& header, u64 nro_size, u64 bss_size) {
return header.magic == Common::MakeMagic('N', 'R', 'O', '0') &&
header.nro_size == nro_size && header.bss_size == bss_size &&
header.ro_offset == header.text_offset + header.text_size &&
header.rw_offset == header.ro_offset + header.ro_size &&
nro_size == header.rw_offset + header.rw_size &&
Common::Is4KBAligned(header.text_size) && Common::Is4KBAligned(header.ro_size) &&
Common::Is4KBAligned(header.rw_size);
}
};
void InstallInterfaces(SM::ServiceManager& sm) {

View File

@@ -212,7 +212,7 @@ private:
IPC::ResponseBuilder rb{ctx, 2};
auto amiibo = nfp_interface.GetAmiiboBuffer();
TagInfo tag_info{};
std::memcpy(tag_info.uuid.data(), amiibo.uuid.data(), sizeof(tag_info.uuid.size()));
tag_info.uuid = amiibo.uuid;
tag_info.uuid_length = static_cast<u8>(tag_info.uuid.size());
tag_info.protocol = 1; // TODO(ogniK): Figure out actual values

View File

@@ -2,6 +2,9 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/logging/log.h"
#include "core/file_sys/control_metadata.h"
#include "core/file_sys/patch_manager.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/service/ns/ns.h"
@@ -118,7 +121,7 @@ public:
{305, nullptr, "TerminateSystemApplet"},
{306, nullptr, "LaunchOverlayApplet"},
{307, nullptr, "TerminateOverlayApplet"},
{400, nullptr, "GetApplicationControlData"},
{400, &IApplicationManagerInterface::GetApplicationControlData, "GetApplicationControlData"},
{401, nullptr, "InvalidateAllApplicationControlCache"},
{402, nullptr, "RequestDownloadApplicationControlData"},
{403, nullptr, "GetMaxApplicationControlCacheCount"},
@@ -243,6 +246,65 @@ public:
RegisterHandlers(functions);
}
void GetApplicationControlData(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto flag = rp.PopRaw<u64>();
LOG_DEBUG(Service_NS, "called with flag={:016X}", flag);
const auto title_id = rp.PopRaw<u64>();
const auto size = ctx.GetWriteBufferSize();
const FileSys::PatchManager pm{title_id};
const auto control = pm.GetControlMetadata();
std::vector<u8> out;
if (control.first != nullptr) {
if (size < 0x4000) {
LOG_ERROR(Service_NS,
"output buffer is too small! (actual={:016X}, expected_min=0x4000)",
size);
IPC::ResponseBuilder rb{ctx, 2};
// TODO(DarkLordZach): Find a better error code for this.
rb.Push(ResultCode(-1));
return;
}
out.resize(0x4000);
const auto bytes = control.first->GetRawBytes();
std::memcpy(out.data(), bytes.data(), bytes.size());
} else {
LOG_WARNING(Service_NS, "missing NACP data for title_id={:016X}, defaulting to zeros.",
title_id);
out.resize(std::min<u64>(0x4000, size));
}
if (control.second != nullptr) {
if (size < 0x4000 + control.second->GetSize()) {
LOG_ERROR(Service_NS,
"output buffer is too small! (actual={:016X}, expected_min={:016X})",
size, 0x4000 + control.second->GetSize());
IPC::ResponseBuilder rb{ctx, 2};
// TODO(DarkLordZach): Find a better error code for this.
rb.Push(ResultCode(-1));
return;
}
out.resize(0x4000 + control.second->GetSize());
control.second->Read(out.data() + 0x4000, control.second->GetSize());
} else {
LOG_WARNING(Service_NS, "missing icon data for title_id={:016X}, defaulting to zeros.",
title_id);
}
ctx.WriteBuffer(out);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(static_cast<u32>(out.size()));
}
};
class IApplicationVersionInterface final : public ServiceFramework<IApplicationVersionInterface> {

View File

@@ -351,6 +351,14 @@ void PL_U::GetSharedFontInOrderOfPriority(Kernel::HLERequestContext& ctx) {
font_sizes.push_back(region.size);
}
// Resize buffers if game requests smaller size output.
font_codes.resize(
std::min<std::size_t>(font_codes.size(), ctx.GetWriteBufferSize(0) / sizeof(u32)));
font_offsets.resize(
std::min<std::size_t>(font_offsets.size(), ctx.GetWriteBufferSize(1) / sizeof(u32)));
font_sizes.resize(
std::min<std::size_t>(font_sizes.size(), ctx.GetWriteBufferSize(2) / sizeof(u32)));
ctx.WriteBuffer(font_codes, 0);
ctx.WriteBuffer(font_offsets, 1);
ctx.WriteBuffer(font_sizes, 2);

View File

@@ -80,8 +80,8 @@ namespace Service {
* Creates a function string for logging, complete with the name (or header code, depending
* on what's passed in) the port name, and all the cmd_buff arguments.
*/
static std::string MakeFunctionString(const char* name, const char* port_name,
const u32* cmd_buff) {
[[maybe_unused]] static std::string MakeFunctionString(const char* name, const char* port_name,
const u32* cmd_buff) {
// Number of params == bits 0-5 + bits 6-11
int num_params = (cmd_buff[0] & 0x3F) + ((cmd_buff[0] >> 6) & 0x3F);

View File

@@ -3,18 +3,23 @@
// Refer to the license.txt file included.
#include <algorithm>
#include <chrono>
#include <cstdlib>
#include <ctime>
#include <functional>
#include <vector>
#include "common/logging/log.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/service/spl/csrng.h"
#include "core/hle/service/spl/module.h"
#include "core/hle/service/spl/spl.h"
#include "core/settings.h"
namespace Service::SPL {
Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
: ServiceFramework(name), module(std::move(module)) {}
: ServiceFramework(name), module(std::move(module)),
rng(Settings::values.rng_seed.value_or(std::time(nullptr))) {}
Module::Interface::~Interface() = default;
@@ -23,8 +28,9 @@ void Module::Interface::GetRandomBytes(Kernel::HLERequestContext& ctx) {
std::size_t size = ctx.GetWriteBufferSize();
std::uniform_int_distribution<u16> distribution(0, std::numeric_limits<u8>::max());
std::vector<u8> data(size);
std::generate(data.begin(), data.end(), std::rand);
std::generate(data.begin(), data.end(), [&] { return static_cast<u8>(distribution(rng)); });
ctx.WriteBuffer(data);

View File

@@ -4,6 +4,7 @@
#pragma once
#include <random>
#include "core/hle/service/service.h"
namespace Service::SPL {
@@ -19,6 +20,9 @@ public:
protected:
std::shared_ptr<Module> module;
private:
std::mt19937 rng;
};
};

View File

@@ -21,9 +21,10 @@ Time::Time(std::shared_ptr<Module> time, const char* name)
{102, nullptr, "GetStandardUserSystemClockInitialYear"},
{200, nullptr, "IsStandardNetworkSystemClockAccuracySufficient"},
{300, nullptr, "CalculateMonotonicSystemClockBaseTimePoint"},
{400, nullptr, "GetClockSnapshot"},
{400, &Time::GetClockSnapshot, "GetClockSnapshot"},
{401, nullptr, "GetClockSnapshotFromSystemClockContext"},
{500, nullptr, "CalculateStandardUserSystemClockDifferenceByUser"},
{500, &Time::CalculateStandardUserSystemClockDifferenceByUser,
"CalculateStandardUserSystemClockDifferenceByUser"},
{501, nullptr, "CalculateSpanBetween"},
};
RegisterHandlers(functions);

View File

@@ -15,6 +15,44 @@
namespace Service::Time {
static void PosixToCalendar(u64 posix_time, CalendarTime& calendar_time,
CalendarAdditionalInfo& additional_info,
[[maybe_unused]] const TimeZoneRule& /*rule*/) {
const std::time_t time(posix_time);
const std::tm* tm = std::localtime(&time);
if (tm == nullptr) {
calendar_time = {};
additional_info = {};
return;
}
calendar_time.year = tm->tm_year + 1900;
calendar_time.month = tm->tm_mon + 1;
calendar_time.day = tm->tm_mday;
calendar_time.hour = tm->tm_hour;
calendar_time.minute = tm->tm_min;
calendar_time.second = tm->tm_sec;
additional_info.day_of_week = tm->tm_wday;
additional_info.day_of_year = tm->tm_yday;
std::memcpy(additional_info.name.data(), "UTC", sizeof("UTC"));
additional_info.utc_offset = 0;
}
static u64 CalendarToPosix(const CalendarTime& calendar_time,
[[maybe_unused]] const TimeZoneRule& /*rule*/) {
std::tm time{};
time.tm_year = calendar_time.year - 1900;
time.tm_mon = calendar_time.month - 1;
time.tm_mday = calendar_time.day;
time.tm_hour = calendar_time.hour;
time.tm_min = calendar_time.minute;
time.tm_sec = calendar_time.second;
std::time_t epoch_time = std::mktime(&time);
return static_cast<u64>(epoch_time);
}
class ISystemClock final : public ServiceFramework<ISystemClock> {
public:
ISystemClock() : ServiceFramework("ISystemClock") {
@@ -80,8 +118,8 @@ public:
{5, nullptr, "GetTimeZoneRuleVersion"},
{100, &ITimeZoneService::ToCalendarTime, "ToCalendarTime"},
{101, &ITimeZoneService::ToCalendarTimeWithMyRule, "ToCalendarTimeWithMyRule"},
{201, nullptr, "ToPosixTime"},
{202, nullptr, "ToPosixTimeWithMyRule"},
{201, &ITimeZoneService::ToPosixTime, "ToPosixTime"},
{202, &ITimeZoneService::ToPosixTimeWithMyRule, "ToPosixTimeWithMyRule"},
};
RegisterHandlers(functions);
}
@@ -151,24 +189,29 @@ private:
rb.PushRaw(additional_info);
}
void PosixToCalendar(u64 posix_time, CalendarTime& calendar_time,
CalendarAdditionalInfo& additional_info, const TimeZoneRule& /*rule*/) {
std::time_t t(posix_time);
std::tm* tm = std::localtime(&t);
if (!tm) {
return;
}
calendar_time.year = tm->tm_year + 1900;
calendar_time.month = tm->tm_mon + 1;
calendar_time.day = tm->tm_mday;
calendar_time.hour = tm->tm_hour;
calendar_time.minute = tm->tm_min;
calendar_time.second = tm->tm_sec;
void ToPosixTime(Kernel::HLERequestContext& ctx) {
// TODO(ogniK): Figure out how to handle multiple times
LOG_WARNING(Service_Time, "(STUBBED) called");
IPC::RequestParser rp{ctx};
auto calendar_time = rp.PopRaw<CalendarTime>();
auto posix_time = CalendarToPosix(calendar_time, {});
additional_info.day_of_week = tm->tm_wday;
additional_info.day_of_year = tm->tm_yday;
std::memcpy(additional_info.name.data(), "UTC", sizeof("UTC"));
additional_info.utc_offset = 0;
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.PushRaw<u32>(1); // Amount of times we're returning
ctx.WriteBuffer(&posix_time, sizeof(u64));
}
void ToPosixTimeWithMyRule(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_Time, "(STUBBED) called");
IPC::RequestParser rp{ctx};
auto calendar_time = rp.PopRaw<CalendarTime>();
auto posix_time = CalendarToPosix(calendar_time, {});
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.PushRaw<u32>(1); // Amount of times we're returning
ctx.WriteBuffer(&posix_time, sizeof(u64));
}
};
@@ -207,6 +250,70 @@ void Module::Interface::GetStandardLocalSystemClock(Kernel::HLERequestContext& c
LOG_DEBUG(Service_Time, "called");
}
void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called");
IPC::RequestParser rp{ctx};
auto unknown_u8 = rp.PopRaw<u8>();
ClockSnapshot clock_snapshot{};
const s64 time_since_epoch{std::chrono::duration_cast<std::chrono::seconds>(
std::chrono::system_clock::now().time_since_epoch())
.count()};
CalendarTime calendar_time{};
const std::time_t time(time_since_epoch);
const std::tm* tm = std::localtime(&time);
if (tm == nullptr) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultCode(-1)); // TODO(ogniK): Find appropriate error code
return;
}
SteadyClockTimePoint steady_clock_time_point{CoreTiming::cyclesToMs(CoreTiming::GetTicks()) /
1000};
LocationName location_name{"UTC"};
calendar_time.year = tm->tm_year + 1900;
calendar_time.month = tm->tm_mon + 1;
calendar_time.day = tm->tm_mday;
calendar_time.hour = tm->tm_hour;
calendar_time.minute = tm->tm_min;
calendar_time.second = tm->tm_sec;
clock_snapshot.system_posix_time = time_since_epoch;
clock_snapshot.network_posix_time = time_since_epoch;
clock_snapshot.system_calendar_time = calendar_time;
clock_snapshot.network_calendar_time = calendar_time;
CalendarAdditionalInfo additional_info{};
PosixToCalendar(time_since_epoch, calendar_time, additional_info, {});
clock_snapshot.system_calendar_info = additional_info;
clock_snapshot.network_calendar_info = additional_info;
clock_snapshot.steady_clock_timepoint = steady_clock_time_point;
clock_snapshot.location_name = location_name;
clock_snapshot.clock_auto_adjustment_enabled = 1;
clock_snapshot.ipc_u8 = unknown_u8;
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
ctx.WriteBuffer(&clock_snapshot, sizeof(ClockSnapshot));
}
void Module::Interface::CalculateStandardUserSystemClockDifferenceByUser(
Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called");
IPC::RequestParser rp{ctx};
const auto snapshot_a = rp.PopRaw<ClockSnapshot>();
const auto snapshot_b = rp.PopRaw<ClockSnapshot>();
const u64 difference =
snapshot_b.user_clock_context.offset - snapshot_a.user_clock_context.offset;
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.PushRaw<u64>(difference);
}
Module::Interface::Interface(std::shared_ptr<Module> time, const char* name)
: ServiceFramework(name), time(std::move(time)) {}

View File

@@ -5,6 +5,7 @@
#pragma once
#include <array>
#include "common/common_funcs.h"
#include "core/hle/service/service.h"
namespace Service::Time {
@@ -53,6 +54,23 @@ struct SystemClockContext {
static_assert(sizeof(SystemClockContext) == 0x20,
"SystemClockContext structure has incorrect size");
struct ClockSnapshot {
SystemClockContext user_clock_context;
SystemClockContext network_clock_context;
s64_le system_posix_time;
s64_le network_posix_time;
CalendarTime system_calendar_time;
CalendarTime network_calendar_time;
CalendarAdditionalInfo system_calendar_info;
CalendarAdditionalInfo network_calendar_info;
SteadyClockTimePoint steady_clock_timepoint;
LocationName location_name;
u8 clock_auto_adjustment_enabled;
u8 ipc_u8;
INSERT_PADDING_BYTES(2);
};
static_assert(sizeof(ClockSnapshot) == 0xd0, "ClockSnapshot is an invalid size");
class Module final {
public:
class Interface : public ServiceFramework<Interface> {
@@ -65,6 +83,8 @@ public:
void GetStandardSteadyClock(Kernel::HLERequestContext& ctx);
void GetTimeZoneService(Kernel::HLERequestContext& ctx);
void GetStandardLocalSystemClock(Kernel::HLERequestContext& ctx);
void GetClockSnapshot(Kernel::HLERequestContext& ctx);
void CalculateStandardUserSystemClockDifferenceByUser(Kernel::HLERequestContext& ctx);
protected:
std::shared_ptr<Module> time;

View File

@@ -237,6 +237,22 @@ private:
Data data{};
};
/// Represents a parcel containing one int '0' as its data
/// Used by DetachBuffer and Disconnect
class IGBPEmptyResponseParcel : public Parcel {
protected:
void SerializeData() override {
Write(data);
}
private:
struct Data {
u32_le unk_0;
};
Data data{};
};
class IGBPSetPreallocatedBufferRequestParcel : public Parcel {
public:
explicit IGBPSetPreallocatedBufferRequestParcel(std::vector<u8> buffer)
@@ -554,6 +570,12 @@ private:
ctx.WriteBuffer(response.Serialize());
} else if (transaction == TransactionId::CancelBuffer) {
LOG_CRITICAL(Service_VI, "(STUBBED) called, transaction=CancelBuffer");
} else if (transaction == TransactionId::Disconnect ||
transaction == TransactionId::DetachBuffer) {
const auto buffer = ctx.ReadBuffer();
IGBPEmptyResponseParcel response{};
ctx.WriteBuffer(response.Serialize());
} else {
ASSERT_MSG(false, "Unimplemented");
}

View File

@@ -12,10 +12,12 @@
#include "common/swap.h"
#include "core/core.h"
#include "core/file_sys/control_metadata.h"
#include "core/file_sys/romfs_factory.h"
#include "core/file_sys/vfs_offset.h"
#include "core/gdbstub/gdbstub.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/vm_manager.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/loader/nro.h"
#include "core/loader/nso.h"
#include "core/memory.h"
@@ -168,17 +170,20 @@ static constexpr u32 PageAlignSize(u32 size) {
arg_data.size());
}
// Read MOD header
ModHeader mod_header{};
// Default .bss to NRO header bss size if MOD0 section doesn't exist
u32 bss_size{PageAlignSize(nro_header.bss_size)};
// Read MOD header
ModHeader mod_header{};
std::memcpy(&mod_header, program_image.data() + nro_header.module_header_offset,
sizeof(ModHeader));
const bool has_mod_header{mod_header.magic == Common::MakeMagic('M', 'O', 'D', '0')};
if (has_mod_header) {
// Resize program image to include .bss section and page align each section
bss_size = PageAlignSize(mod_header.bss_end_offset - mod_header.bss_start_offset);
}
codeset.DataSegment().size += bss_size;
program_image.resize(static_cast<u32>(program_image.size()) + bss_size);
@@ -208,6 +213,9 @@ ResultStatus AppLoader_NRO::Load(Kernel::Process& process) {
return ResultStatus::ErrorLoadingNRO;
}
if (romfs != nullptr)
Service::FileSystem::RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(*this));
process.Run(base_address, Kernel::THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE);
is_loaded = true;

View File

@@ -154,7 +154,7 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(const FileSys::VfsFile& file, VAd
program_image.resize(image_size);
// Apply patches if necessary
if (pm && pm->HasNSOPatch(nso_header.build_id)) {
if (pm && (pm->HasNSOPatch(nso_header.build_id) || Settings::values.dump_nso)) {
std::vector<u8> pi_header(program_image.size() + 0x100);
std::memcpy(pi_header.data(), &nso_header, sizeof(NsoHeader));
std::memcpy(pi_header.data() + 0x100, program_image.data(), program_image.size());

View File

@@ -10,6 +10,56 @@
namespace Settings {
namespace NativeButton {
const std::array<const char*, NumButtons> mapping = {{
"button_a",
"button_b",
"button_x",
"button_y",
"button_lstick",
"button_rstick",
"button_l",
"button_r",
"button_zl",
"button_zr",
"button_plus",
"button_minus",
"button_dleft",
"button_dup",
"button_dright",
"button_ddown",
"button_lstick_left",
"button_lstick_up",
"button_lstick_right",
"button_lstick_down",
"button_rstick_left",
"button_rstick_up",
"button_rstick_right",
"button_rstick_down",
"button_sl",
"button_sr",
"button_home",
"button_screenshot",
}};
}
namespace NativeAnalog {
const std::array<const char*, NumAnalogs> mapping = {{
"lstick",
"rstick",
}};
}
namespace NativeMouseButton {
const std::array<const char*, NumMouseButtons> mapping = {{
"left",
"right",
"middle",
"forward",
"back",
}};
}
Values values = {};
void Apply() {

View File

@@ -6,6 +6,7 @@
#include <array>
#include <atomic>
#include <optional>
#include <string>
#include "common/common_types.h"
@@ -59,36 +60,7 @@ constexpr int BUTTON_NS_END = NumButtons;
constexpr int NUM_BUTTONS_HID = BUTTON_HID_END - BUTTON_HID_BEGIN;
constexpr int NUM_BUTTONS_NS = BUTTON_NS_END - BUTTON_NS_BEGIN;
static const std::array<const char*, NumButtons> mapping = {{
"button_a",
"button_b",
"button_x",
"button_y",
"button_lstick",
"button_rstick",
"button_l",
"button_r",
"button_zl",
"button_zr",
"button_plus",
"button_minus",
"button_dleft",
"button_dup",
"button_dright",
"button_ddown",
"button_lstick_left",
"button_lstick_up",
"button_lstick_right",
"button_lstick_down",
"button_rstick_left",
"button_rstick_up",
"button_rstick_right",
"button_rstick_down",
"button_sl",
"button_sr",
"button_home",
"button_screenshot",
}};
extern const std::array<const char*, NumButtons> mapping;
} // namespace NativeButton
@@ -104,24 +76,298 @@ constexpr int STICK_HID_BEGIN = LStick;
constexpr int STICK_HID_END = NumAnalogs;
constexpr int NUM_STICKS_HID = NumAnalogs;
static const std::array<const char*, NumAnalogs> mapping = {{
"lstick",
"rstick",
}};
extern const std::array<const char*, NumAnalogs> mapping;
} // namespace NativeAnalog
namespace NativeMouseButton {
enum Values {
Left,
Right,
Middle,
Forward,
Back,
NumMouseButtons,
};
constexpr int MOUSE_HID_BEGIN = Left;
constexpr int MOUSE_HID_END = NumMouseButtons;
constexpr int NUM_MOUSE_HID = NumMouseButtons;
extern const std::array<const char*, NumMouseButtons> mapping;
} // namespace NativeMouseButton
namespace NativeKeyboard {
enum Keys {
None,
Error,
A = 4,
B,
C,
D,
E,
F,
G,
H,
I,
J,
K,
L,
M,
N,
O,
P,
Q,
R,
S,
T,
U,
V,
W,
X,
Y,
Z,
N1,
N2,
N3,
N4,
N5,
N6,
N7,
N8,
N9,
N0,
Enter,
Escape,
Backspace,
Tab,
Space,
Minus,
Equal,
LeftBrace,
RightBrace,
Backslash,
Tilde,
Semicolon,
Apostrophe,
Grave,
Comma,
Dot,
Slash,
CapsLockKey,
F1,
F2,
F3,
F4,
F5,
F6,
F7,
F8,
F9,
F10,
F11,
F12,
SystemRequest,
ScrollLockKey,
Pause,
Insert,
Home,
PageUp,
Delete,
End,
PageDown,
Right,
Left,
Down,
Up,
NumLockKey,
KPSlash,
KPAsterisk,
KPMinus,
KPPlus,
KPEnter,
KP1,
KP2,
KP3,
KP4,
KP5,
KP6,
KP7,
KP8,
KP9,
KP0,
KPDot,
Key102,
Compose,
Power,
KPEqual,
F13,
F14,
F15,
F16,
F17,
F18,
F19,
F20,
F21,
F22,
F23,
F24,
Open,
Help,
Properties,
Front,
Stop,
Repeat,
Undo,
Cut,
Copy,
Paste,
Find,
Mute,
VolumeUp,
VolumeDown,
CapsLockActive,
NumLockActive,
ScrollLockActive,
KPComma,
KPLeftParenthesis,
KPRightParenthesis,
LeftControlKey = 0xE0,
LeftShiftKey,
LeftAltKey,
LeftMetaKey,
RightControlKey,
RightShiftKey,
RightAltKey,
RightMetaKey,
MediaPlayPause,
MediaStopCD,
MediaPrevious,
MediaNext,
MediaEject,
MediaVolumeUp,
MediaVolumeDown,
MediaMute,
MediaWebsite,
MediaBack,
MediaForward,
MediaStop,
MediaFind,
MediaScrollUp,
MediaScrollDown,
MediaEdit,
MediaSleep,
MediaCoffee,
MediaRefresh,
MediaCalculator,
NumKeyboardKeys,
};
static_assert(NumKeyboardKeys == 0xFC, "Incorrect number of keyboard keys.");
enum Modifiers {
LeftControl,
LeftShift,
LeftAlt,
LeftMeta,
RightControl,
RightShift,
RightAlt,
RightMeta,
CapsLock,
ScrollLock,
NumLock,
NumKeyboardMods,
};
constexpr int KEYBOARD_KEYS_HID_BEGIN = None;
constexpr int KEYBOARD_KEYS_HID_END = NumKeyboardKeys;
constexpr int NUM_KEYBOARD_KEYS_HID = NumKeyboardKeys;
constexpr int KEYBOARD_MODS_HID_BEGIN = LeftControl;
constexpr int KEYBOARD_MODS_HID_END = NumKeyboardMods;
constexpr int NUM_KEYBOARD_MODS_HID = NumKeyboardMods;
} // namespace NativeKeyboard
using ButtonsRaw = std::array<std::string, NativeButton::NumButtons>;
using AnalogsRaw = std::array<std::string, NativeAnalog::NumAnalogs>;
using MouseButtonsRaw = std::array<std::string, NativeMouseButton::NumMouseButtons>;
using KeyboardKeysRaw = std::array<std::string, NativeKeyboard::NumKeyboardKeys>;
using KeyboardModsRaw = std::array<std::string, NativeKeyboard::NumKeyboardMods>;
constexpr u32 JOYCON_BODY_NEON_RED = 0xFF3C28;
constexpr u32 JOYCON_BUTTONS_NEON_RED = 0x1E0A0A;
constexpr u32 JOYCON_BODY_NEON_BLUE = 0x0AB9E6;
constexpr u32 JOYCON_BUTTONS_NEON_BLUE = 0x001E1E;
enum class ControllerType {
ProController,
DualJoycon,
RightJoycon,
LeftJoycon,
};
struct PlayerInput {
bool connected;
ControllerType type;
ButtonsRaw buttons;
AnalogsRaw analogs;
u32 body_color_right;
u32 button_color_right;
u32 body_color_left;
u32 button_color_left;
};
struct TouchscreenInput {
bool enabled;
std::string device;
u32 finger;
u32 diameter_x;
u32 diameter_y;
u32 rotation_angle;
};
struct Values {
// System
bool use_docked_mode;
bool enable_nfc;
int current_user;
int language_index;
std::optional<u32> rng_seed;
s32 current_user;
s32 language_index;
// Controls
std::array<std::string, NativeButton::NumButtons> buttons;
std::array<std::string, NativeAnalog::NumAnalogs> analogs;
std::array<PlayerInput, 10> players;
bool mouse_enabled;
std::string mouse_device;
MouseButtonsRaw mouse_buttons;
bool keyboard_enabled;
KeyboardKeysRaw keyboard_keys;
KeyboardModsRaw keyboard_mods;
bool debug_pad_enabled;
ButtonsRaw debug_pad_buttons;
AnalogsRaw debug_pad_analogs;
std::string motion_device;
std::string touch_device;
TouchscreenInput touchscreen;
std::atomic_bool is_device_reload_pending{true};
// Core
@@ -157,6 +403,7 @@ struct Values {
bool use_gdbstub;
u16 gdbstub_port;
std::string program_args;
bool dump_nso;
// WebService
bool enable_telemetry;

View File

@@ -21,6 +21,7 @@ add_library(video_core STATIC
macro_interpreter.h
memory_manager.cpp
memory_manager.h
rasterizer_cache.cpp
rasterizer_cache.h
rasterizer_interface.h
renderer_base.cpp

View File

@@ -34,8 +34,8 @@ void Maxwell3D::InitializeRegisterDefaults() {
// Depth range near/far is not always set, but is expected to be the default 0.0f, 1.0f. This is
// needed for ARMS.
for (std::size_t viewport{}; viewport < Regs::NumViewports; ++viewport) {
regs.viewport[viewport].depth_range_near = 0.0f;
regs.viewport[viewport].depth_range_far = 1.0f;
regs.viewports[viewport].depth_range_near = 0.0f;
regs.viewports[viewport].depth_range_far = 1.0f;
}
// Doom and Bomberman seems to use the uninitialized registers and just enable blend
// so initialize blend registers with sane values
@@ -53,6 +53,22 @@ void Maxwell3D::InitializeRegisterDefaults() {
regs.independent_blend[blend_index].factor_source_a = Regs::Blend::Factor::One;
regs.independent_blend[blend_index].factor_dest_a = Regs::Blend::Factor::Zero;
}
regs.stencil_front_op_fail = Regs::StencilOp::Keep;
regs.stencil_front_op_zfail = Regs::StencilOp::Keep;
regs.stencil_front_op_zpass = Regs::StencilOp::Keep;
regs.stencil_front_func_func = Regs::ComparisonOp::Always;
regs.stencil_front_func_mask = 0xFFFFFFFF;
regs.stencil_front_mask = 0xFFFFFFFF;
regs.stencil_two_side_enable = 1;
regs.stencil_back_op_fail = Regs::StencilOp::Keep;
regs.stencil_back_op_zfail = Regs::StencilOp::Keep;
regs.stencil_back_op_zpass = Regs::StencilOp::Keep;
regs.stencil_back_func_func = Regs::ComparisonOp::Always;
regs.stencil_back_func_mask = 0xFFFFFFFF;
regs.stencil_back_mask = 0xFFFFFFFF;
// TODO(Rodrigo): Most games do not set a point size. I think this is a case of a
// register carrying a default value. Assume it's OpenGL's default (1).
regs.point_size = 1.0f;
}
void Maxwell3D::CallMacroMethod(u32 method, std::vector<u32> parameters) {
@@ -108,7 +124,13 @@ void Maxwell3D::WriteReg(u32 method, u32 value, u32 remaining_params) {
debug_context->OnEvent(Tegra::DebugContext::Event::MaxwellCommandLoaded, nullptr);
}
regs.reg_array[method] = value;
if (regs.reg_array[method] != value) {
regs.reg_array[method] = value;
if (method >= MAXWELL3D_REG_INDEX(vertex_attrib_format) &&
method < MAXWELL3D_REG_INDEX(vertex_attrib_format) + regs.vertex_attrib_format.size()) {
dirty_flags.vertex_attrib_format = true;
}
}
switch (method) {
case MAXWELL3D_REG_INDEX(macros.data): {

View File

@@ -345,6 +345,14 @@ public:
Invert = 6,
IncrWrap = 7,
DecrWrap = 8,
KeepOGL = 0x1E00,
ZeroOGL = 0,
ReplaceOGL = 0x1E01,
IncrOGL = 0x1E02,
DecrOGL = 0x1E03,
InvertOGL = 0x150A,
IncrWrapOGL = 0x8507,
DecrWrapOGL = 0x8508,
};
enum class MemoryLayout : u32 {
@@ -472,6 +480,67 @@ public:
};
};
struct ViewportTransform {
f32 scale_x;
f32 scale_y;
f32 scale_z;
f32 translate_x;
f32 translate_y;
f32 translate_z;
INSERT_PADDING_WORDS(2);
MathUtil::Rectangle<s32> GetRect() const {
return {
GetX(), // left
GetY() + GetHeight(), // top
GetX() + GetWidth(), // right
GetY() // bottom
};
};
s32 GetX() const {
return static_cast<s32>(std::max(0.0f, translate_x - std::fabs(scale_x)));
}
s32 GetY() const {
return static_cast<s32>(std::max(0.0f, translate_y - std::fabs(scale_y)));
}
s32 GetWidth() const {
return static_cast<s32>(translate_x + std::fabs(scale_x)) - GetX();
}
s32 GetHeight() const {
return static_cast<s32>(translate_y + std::fabs(scale_y)) - GetY();
}
};
struct ScissorTest {
u32 enable;
union {
BitField<0, 16, u32> min_x;
BitField<16, 16, u32> max_x;
};
union {
BitField<0, 16, u32> min_y;
BitField<16, 16, u32> max_y;
};
u32 fill;
};
struct ViewPort {
union {
BitField<0, 16, u32> x;
BitField<16, 16, u32> width;
};
union {
BitField<0, 16, u32> y;
BitField<16, 16, u32> height;
};
float depth_range_near;
float depth_range_far;
};
bool IsShaderConfigEnabled(std::size_t index) const {
// The VertexB is always enabled.
if (index == static_cast<std::size_t>(Regs::ShaderProgram::VertexB)) {
@@ -497,55 +566,11 @@ public:
INSERT_PADDING_WORDS(0x2E);
RenderTargetConfig rt[NumRenderTargets];
std::array<RenderTargetConfig, NumRenderTargets> rt;
struct {
f32 scale_x;
f32 scale_y;
f32 scale_z;
f32 translate_x;
f32 translate_y;
f32 translate_z;
INSERT_PADDING_WORDS(2);
std::array<ViewportTransform, NumViewports> viewport_transform;
MathUtil::Rectangle<s32> GetRect() const {
return {
GetX(), // left
GetY() + GetHeight(), // top
GetX() + GetWidth(), // right
GetY() // bottom
};
};
s32 GetX() const {
return static_cast<s32>(std::max(0.0f, translate_x - std::fabs(scale_x)));
}
s32 GetY() const {
return static_cast<s32>(std::max(0.0f, translate_y - std::fabs(scale_y)));
}
s32 GetWidth() const {
return static_cast<s32>(translate_x + std::fabs(scale_x)) - GetX();
}
s32 GetHeight() const {
return static_cast<s32>(translate_y + std::fabs(scale_y)) - GetY();
}
} viewport_transform[NumViewports];
struct {
union {
BitField<0, 16, u32> x;
BitField<16, 16, u32> width;
};
union {
BitField<0, 16, u32> y;
BitField<16, 16, u32> height;
};
float depth_range_near;
float depth_range_far;
} viewport[NumViewports];
std::array<ViewPort, NumViewports> viewports;
INSERT_PADDING_WORDS(0x1D);
@@ -563,19 +588,9 @@ public:
INSERT_PADDING_WORDS(0x17);
struct {
u32 enable;
union {
BitField<0, 16, u32> min_x;
BitField<16, 16, u32> max_x;
};
union {
BitField<0, 16, u32> min_y;
BitField<16, 16, u32> max_y;
};
} scissor_test;
std::array<ScissorTest, NumViewports> scissor_test;
INSERT_PADDING_WORDS(0x52);
INSERT_PADDING_WORDS(0x15);
s32 stencil_back_func_ref;
u32 stencil_back_mask;
@@ -692,7 +707,9 @@ public:
u32 stencil_front_func_mask;
u32 stencil_front_mask;
INSERT_PADDING_WORDS(0x3);
INSERT_PADDING_WORDS(0x2);
u32 frag_color_clamp;
union {
BitField<4, 1, u32> triangle_rast_flip;
@@ -710,7 +727,12 @@ public:
u32 zeta_enable;
INSERT_PADDING_WORDS(0x8);
union {
BitField<0, 1, u32> alpha_to_coverage;
BitField<4, 1, u32> alpha_to_one;
} multisample_control;
INSERT_PADDING_WORDS(0x7);
struct {
u32 tsc_address_high;
@@ -1004,6 +1026,12 @@ public:
State state{};
MemoryManager& memory_manager;
struct DirtyFlags {
bool vertex_attrib_format = true;
};
DirtyFlags dirty_flags;
/// Reads a register value located at the input method address
u32 GetRegisterValue(u32 method) const;
@@ -1086,8 +1114,8 @@ private:
ASSERT_REG_POSITION(macros, 0x45);
ASSERT_REG_POSITION(tfb_enabled, 0x1D1);
ASSERT_REG_POSITION(rt, 0x200);
ASSERT_REG_POSITION(viewport_transform[0], 0x280);
ASSERT_REG_POSITION(viewport, 0x300);
ASSERT_REG_POSITION(viewport_transform, 0x280);
ASSERT_REG_POSITION(viewports, 0x300);
ASSERT_REG_POSITION(vertex_buffer, 0x35D);
ASSERT_REG_POSITION(clear_color[0], 0x360);
ASSERT_REG_POSITION(clear_depth, 0x364);
@@ -1122,10 +1150,12 @@ ASSERT_REG_POSITION(stencil_front_func_func, 0x4E4);
ASSERT_REG_POSITION(stencil_front_func_ref, 0x4E5);
ASSERT_REG_POSITION(stencil_front_func_mask, 0x4E6);
ASSERT_REG_POSITION(stencil_front_mask, 0x4E7);
ASSERT_REG_POSITION(frag_color_clamp, 0x4EA);
ASSERT_REG_POSITION(screen_y_control, 0x4EB);
ASSERT_REG_POSITION(vb_element_base, 0x50D);
ASSERT_REG_POSITION(point_size, 0x546);
ASSERT_REG_POSITION(zeta_enable, 0x54E);
ASSERT_REG_POSITION(multisample_control, 0x54F);
ASSERT_REG_POSITION(tsc, 0x557);
ASSERT_REG_POSITION(tic, 0x55D);
ASSERT_REG_POSITION(stencil_two_side_enable, 0x565);

View File

@@ -0,0 +1,7 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "video_core/rasterizer_cache.h"
RasterizerCacheObject::~RasterizerCacheObject() = default;

View File

@@ -5,6 +5,7 @@
#pragma once
#include <set>
#include <unordered_map>
#include <boost/icl/interval_map.hpp>
#include <boost/range/iterator_range_core.hpp>
@@ -15,6 +16,8 @@
class RasterizerCacheObject {
public:
virtual ~RasterizerCacheObject();
/// Gets the address of the shader in guest memory, required for cache management
virtual VAddr GetAddr() const = 0;
@@ -86,29 +89,25 @@ public:
/// Invalidates everything in the cache
void InvalidateAll() {
while (object_cache.begin() != object_cache.end()) {
Unregister(*object_cache.begin()->second.begin());
while (interval_cache.begin() != interval_cache.end()) {
Unregister(*interval_cache.begin()->second.begin());
}
}
protected:
/// Tries to get an object from the cache with the specified address
T TryGet(VAddr addr) const {
const ObjectInterval interval{addr};
for (auto& pair : boost::make_iterator_range(object_cache.equal_range(interval))) {
for (auto& cached_object : pair.second) {
if (cached_object->GetAddr() == addr) {
return cached_object;
}
}
}
const auto iter = map_cache.find(addr);
if (iter != map_cache.end())
return iter->second;
return nullptr;
}
/// Register an object into the cache
void Register(const T& object) {
object->SetIsRegistered(true);
object_cache.add({GetInterval(object), ObjectSet{object}});
interval_cache.add({GetInterval(object), ObjectSet{object}});
map_cache.insert({object->GetAddr(), object});
rasterizer.UpdatePagesCachedCount(object->GetAddr(), object->GetSizeInBytes(), 1);
}
@@ -116,13 +115,13 @@ protected:
void Unregister(const T& object) {
object->SetIsRegistered(false);
rasterizer.UpdatePagesCachedCount(object->GetAddr(), object->GetSizeInBytes(), -1);
// Only flush if use_accurate_gpu_emulation is enabled, as it incurs a performance hit
if (Settings::values.use_accurate_gpu_emulation) {
FlushObject(object);
}
object_cache.subtract({GetInterval(object), ObjectSet{object}});
interval_cache.subtract({GetInterval(object), ObjectSet{object}});
map_cache.erase(object->GetAddr());
}
/// Returns a ticks counter used for tracking when cached objects were last modified
@@ -139,7 +138,7 @@ private:
std::vector<T> objects;
const ObjectInterval interval{addr, addr + size};
for (auto& pair : boost::make_iterator_range(object_cache.equal_range(interval))) {
for (auto& pair : boost::make_iterator_range(interval_cache.equal_range(interval))) {
for (auto& cached_object : pair.second) {
if (!cached_object) {
continue;
@@ -165,15 +164,17 @@ private:
}
using ObjectSet = std::set<T>;
using ObjectCache = boost::icl::interval_map<VAddr, ObjectSet>;
using ObjectInterval = typename ObjectCache::interval_type;
using ObjectCache = std::unordered_map<VAddr, T>;
using IntervalCache = boost::icl::interval_map<VAddr, ObjectSet>;
using ObjectInterval = typename IntervalCache::interval_type;
static auto GetInterval(const T& object) {
return ObjectInterval::right_open(object->GetAddr(),
object->GetAddr() + object->GetSizeInBytes());
}
ObjectCache object_cache; ///< Cache of objects
u64 modified_ticks{}; ///< Counter of cache state ticks, used for in-order flushing
ObjectCache map_cache;
IntervalCache interval_cache; ///< Cache of objects
u64 modified_ticks{}; ///< Counter of cache state ticks, used for in-order flushing
VideoCore::RasterizerInterface& rasterizer;
};

View File

@@ -5,7 +5,6 @@
#include "core/frontend/emu_window.h"
#include "core/settings.h"
#include "video_core/renderer_base.h"
#include "video_core/renderer_opengl/gl_rasterizer.h"
namespace VideoCore {

View File

@@ -33,7 +33,8 @@ using Maxwell = Tegra::Engines::Maxwell3D::Regs;
using PixelFormat = VideoCore::Surface::PixelFormat;
using SurfaceType = VideoCore::Surface::SurfaceType;
MICROPROFILE_DEFINE(OpenGL_VAO, "OpenGL", "Vertex Array Setup", MP_RGB(128, 128, 192));
MICROPROFILE_DEFINE(OpenGL_VAO, "OpenGL", "Vertex Format Setup", MP_RGB(128, 128, 192));
MICROPROFILE_DEFINE(OpenGL_VB, "OpenGL", "Vertex Buffer Setup", MP_RGB(128, 128, 192));
MICROPROFILE_DEFINE(OpenGL_Shader, "OpenGL", "Shader Setup", MP_RGB(128, 128, 192));
MICROPROFILE_DEFINE(OpenGL_UBO, "OpenGL", "Const Buffer Setup", MP_RGB(128, 128, 192));
MICROPROFILE_DEFINE(OpenGL_Index, "OpenGL", "Index Buffer Setup", MP_RGB(128, 128, 192));
@@ -106,8 +107,6 @@ RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window, ScreenInfo
ASSERT_MSG(has_ARB_separate_shader_objects, "has_ARB_separate_shader_objects is unsupported");
OpenGLState::ApplyDefaultState();
// Clipping plane 0 is always enabled for PICA fixed clip plane z <= 0
state.clip_distance[0] = true;
// Create render framebuffer
framebuffer.Create();
@@ -123,18 +122,23 @@ RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window, ScreenInfo
RasterizerOpenGL::~RasterizerOpenGL() {}
void RasterizerOpenGL::SetupVertexArrays() {
MICROPROFILE_SCOPE(OpenGL_VAO);
const auto& gpu = Core::System::GetInstance().GPU().Maxwell3D();
void RasterizerOpenGL::SetupVertexFormat() {
auto& gpu = Core::System::GetInstance().GPU().Maxwell3D();
const auto& regs = gpu.regs;
if (!gpu.dirty_flags.vertex_attrib_format)
return;
gpu.dirty_flags.vertex_attrib_format = false;
MICROPROFILE_SCOPE(OpenGL_VAO);
auto [iter, is_cache_miss] = vertex_array_cache.try_emplace(regs.vertex_attrib_format);
auto& VAO = iter->second;
if (is_cache_miss) {
VAO.Create();
state.draw.vertex_array = VAO.handle;
state.Apply();
state.ApplyVertexBufferState();
// The index buffer binding is stored within the VAO. Stupid OpenGL, but easy to work
// around.
@@ -176,8 +180,13 @@ void RasterizerOpenGL::SetupVertexArrays() {
}
}
state.draw.vertex_array = VAO.handle;
state.draw.vertex_buffer = buffer_cache.GetHandle();
state.Apply();
state.ApplyVertexBufferState();
}
void RasterizerOpenGL::SetupVertexBuffer() {
MICROPROFILE_SCOPE(OpenGL_VB);
const auto& gpu = Core::System::GetInstance().GPU().Maxwell3D();
const auto& regs = gpu.regs;
// Upload all guest vertex arrays sequentially to our buffer
for (u32 index = 0; index < Maxwell::NumVertexArrays; ++index) {
@@ -204,6 +213,9 @@ void RasterizerOpenGL::SetupVertexArrays() {
glVertexBindingDivisor(index, 0);
}
}
// Implicit set by glBindVertexBuffer. Stupid glstate handling...
state.draw.vertex_buffer = buffer_cache.GetHandle();
}
DrawParameters RasterizerOpenGL::SetupDraw() {
@@ -328,8 +340,6 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
index++;
}
}
state.Apply();
}
std::size_t RasterizerOpenGL::CalculateVertexArraysSize() const {
@@ -398,8 +408,8 @@ void RasterizerOpenGL::UpdatePagesCachedCount(VAddr addr, u64 size, int delta) {
cached_pages.add({pages_interval, delta});
}
void RasterizerOpenGL::ConfigureFramebuffers(bool using_color_fb, bool using_depth_fb,
bool preserve_contents,
void RasterizerOpenGL::ConfigureFramebuffers(OpenGLState& current_state, bool using_color_fb,
bool using_depth_fb, bool preserve_contents,
std::optional<std::size_t> single_color_target) {
MICROPROFILE_SCOPE(OpenGL_Framebuffer);
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
@@ -415,9 +425,9 @@ void RasterizerOpenGL::ConfigureFramebuffers(bool using_color_fb, bool using_dep
ASSERT_MSG(regs.rt_separate_frag_data == 0, "Unimplemented");
// Bind the framebuffer surfaces
state.draw.draw_framebuffer = framebuffer.handle;
state.Apply();
state.framebuffer_srgb.enabled = regs.framebuffer_srgb != 0;
current_state.draw.draw_framebuffer = framebuffer.handle;
current_state.ApplyFramebufferState();
current_state.framebuffer_srgb.enabled = regs.framebuffer_srgb != 0;
if (using_color_fb) {
if (single_color_target) {
@@ -495,10 +505,7 @@ void RasterizerOpenGL::ConfigureFramebuffers(bool using_color_fb, bool using_dep
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0,
0);
}
SyncViewport();
state.Apply();
SyncViewport(current_state);
}
void RasterizerOpenGL::Clear() {
@@ -511,22 +518,23 @@ void RasterizerOpenGL::Clear() {
bool use_stencil{};
OpenGLState clear_state;
clear_state.draw.draw_framebuffer = framebuffer.handle;
clear_state.color_mask[0].red_enabled = regs.clear_buffers.R ? GL_TRUE : GL_FALSE;
clear_state.color_mask[0].green_enabled = regs.clear_buffers.G ? GL_TRUE : GL_FALSE;
clear_state.color_mask[0].blue_enabled = regs.clear_buffers.B ? GL_TRUE : GL_FALSE;
clear_state.color_mask[0].alpha_enabled = regs.clear_buffers.A ? GL_TRUE : GL_FALSE;
if (regs.clear_buffers.R || regs.clear_buffers.G || regs.clear_buffers.B ||
regs.clear_buffers.A) {
use_color = true;
}
if (use_color) {
clear_state.color_mask[0].red_enabled = regs.clear_buffers.R ? GL_TRUE : GL_FALSE;
clear_state.color_mask[0].green_enabled = regs.clear_buffers.G ? GL_TRUE : GL_FALSE;
clear_state.color_mask[0].blue_enabled = regs.clear_buffers.B ? GL_TRUE : GL_FALSE;
clear_state.color_mask[0].alpha_enabled = regs.clear_buffers.A ? GL_TRUE : GL_FALSE;
}
if (regs.clear_buffers.Z) {
ASSERT_MSG(regs.zeta_enable != 0, "Tried to clear Z but buffer is not enabled!");
use_depth = true;
// Always enable the depth write when clearing the depth buffer. The depth write mask is
// ignored when clearing the buffer in the Switch, but OpenGL obeys it so we set it to true.
// ignored when clearing the buffer in the Switch, but OpenGL obeys it so we set it to
// true.
clear_state.depth.test_enabled = true;
clear_state.depth.test_func = GL_ALWAYS;
}
@@ -543,11 +551,8 @@ void RasterizerOpenGL::Clear() {
ScopeAcquireGLContext acquire_context{emu_window};
ConfigureFramebuffers(use_color, use_depth || use_stencil, false,
ConfigureFramebuffers(clear_state, use_color, use_depth || use_stencil, false,
regs.clear_buffers.RT.Value());
// Copy the sRGB setting to the clear state to avoid problem with
// specific driver implementations
clear_state.framebuffer_srgb.enabled = state.framebuffer_srgb.enabled;
clear_state.Apply();
if (use_color) {
@@ -573,8 +578,10 @@ void RasterizerOpenGL::DrawArrays() {
ScopeAcquireGLContext acquire_context{emu_window};
ConfigureFramebuffers();
ConfigureFramebuffers(state);
SyncColorMask();
SyncFragmentColorClampState();
SyncMultiSampleState();
SyncDepthTestState();
SyncStencilTestState();
SyncBlendState();
@@ -594,7 +601,7 @@ void RasterizerOpenGL::DrawArrays() {
const bool is_indexed = accelerate_draw == AccelDraw::Indexed;
state.draw.vertex_buffer = buffer_cache.GetHandle();
state.Apply();
state.ApplyVertexBufferState();
std::size_t buffer_size = CalculateVertexArraysSize();
@@ -621,7 +628,8 @@ void RasterizerOpenGL::DrawArrays() {
buffer_cache.Map(buffer_size);
SetupVertexArrays();
SetupVertexFormat();
SetupVertexBuffer();
DrawParameters params = SetupDraw();
SetupShaders(params.primitive_mode);
@@ -634,7 +642,7 @@ void RasterizerOpenGL::DrawArrays() {
params.DispatchDraw();
// Disable scissor test
state.scissor.enabled = false;
state.viewports[0].scissor.enabled = false;
accelerate_draw = AccelDraw::Disabled;
@@ -727,7 +735,6 @@ void RasterizerOpenGL::SamplerInfo::Create() {
void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::TSCEntry& config) {
const GLuint s = sampler.handle;
if (mag_filter != config.mag_filter) {
mag_filter = config.mag_filter;
glSamplerParameteri(
@@ -769,15 +776,51 @@ void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::TSCEntr
MaxwellToGL::DepthCompareFunc(depth_compare_func));
}
if (wrap_u == Tegra::Texture::WrapMode::Border || wrap_v == Tegra::Texture::WrapMode::Border ||
wrap_p == Tegra::Texture::WrapMode::Border) {
const GLvec4 new_border_color = {{config.border_color_r, config.border_color_g,
config.border_color_b, config.border_color_a}};
if (border_color != new_border_color) {
border_color = new_border_color;
glSamplerParameterfv(s, GL_TEXTURE_BORDER_COLOR, border_color.data());
GLvec4 new_border_color;
if (config.srgb_conversion) {
new_border_color[0] = config.srgb_border_color_r / 255.0f;
new_border_color[1] = config.srgb_border_color_g / 255.0f;
new_border_color[2] = config.srgb_border_color_g / 255.0f;
} else {
new_border_color[0] = config.border_color_r;
new_border_color[1] = config.border_color_g;
new_border_color[2] = config.border_color_b;
}
new_border_color[3] = config.border_color_a;
if (border_color != new_border_color) {
border_color = new_border_color;
glSamplerParameterfv(s, GL_TEXTURE_BORDER_COLOR, border_color.data());
}
const float anisotropic_max = static_cast<float>(1 << config.max_anisotropy.Value());
if (anisotropic_max != max_anisotropic) {
max_anisotropic = anisotropic_max;
if (GLAD_GL_ARB_texture_filter_anisotropic) {
glSamplerParameterf(s, GL_TEXTURE_MAX_ANISOTROPY, max_anisotropic);
} else if (GLAD_GL_EXT_texture_filter_anisotropic) {
glSamplerParameterf(s, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_anisotropic);
}
}
const float lod_min = static_cast<float>(config.min_lod_clamp.Value()) / 256.0f;
if (lod_min != min_lod) {
min_lod = lod_min;
glSamplerParameterf(s, GL_TEXTURE_MIN_LOD, min_lod);
}
const float lod_max = static_cast<float>(config.max_lod_clamp.Value()) / 256.0f;
if (lod_max != max_lod) {
max_lod = lod_max;
glSamplerParameterf(s, GL_TEXTURE_MAX_LOD, max_lod);
}
const u32 bias = config.mip_lod_bias.Value();
// Sign extend the 13-bit value.
const u32 mask = 1U << (13 - 1);
const float bias_lod = static_cast<s32>((bias ^ mask) - mask) / 256.f;
if (lod_bias != bias_lod) {
lod_bias = bias_lod;
glSamplerParameterf(s, GL_TEXTURE_LOD_BIAS, lod_bias);
}
}
u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, Shader& shader,
@@ -897,17 +940,17 @@ u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, Shader& shader,
return current_unit + static_cast<u32>(entries.size());
}
void RasterizerOpenGL::SyncViewport() {
void RasterizerOpenGL::SyncViewport(OpenGLState& current_state) {
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
for (size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) {
for (std::size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumViewports; i++) {
const MathUtil::Rectangle<s32> viewport_rect{regs.viewport_transform[i].GetRect()};
auto& viewport = state.viewports[i];
auto& viewport = current_state.viewports[i];
viewport.x = viewport_rect.left;
viewport.y = viewport_rect.bottom;
viewport.width = static_cast<GLsizei>(viewport_rect.GetWidth());
viewport.height = static_cast<GLsizei>(viewport_rect.GetHeight());
viewport.depth_range_far = regs.viewport[i].depth_range_far;
viewport.depth_range_near = regs.viewport[i].depth_range_near;
viewport.width = static_cast<GLfloat>(viewport_rect.GetWidth());
viewport.height = static_cast<GLfloat>(viewport_rect.GetHeight());
viewport.depth_range_far = regs.viewports[i].depth_range_far;
viewport.depth_range_near = regs.viewports[i].depth_range_near;
}
}
@@ -970,9 +1013,6 @@ void RasterizerOpenGL::SyncStencilTestState() {
return;
}
// TODO(bunnei): Verify behavior when this is not set
ASSERT(regs.stencil_two_side_enable);
state.stencil.front.test_func = MaxwellToGL::ComparisonOp(regs.stencil_front_func_func);
state.stencil.front.test_ref = regs.stencil_front_func_ref;
state.stencil.front.test_mask = regs.stencil_front_func_mask;
@@ -980,19 +1020,30 @@ void RasterizerOpenGL::SyncStencilTestState() {
state.stencil.front.action_depth_fail = MaxwellToGL::StencilOp(regs.stencil_front_op_zfail);
state.stencil.front.action_depth_pass = MaxwellToGL::StencilOp(regs.stencil_front_op_zpass);
state.stencil.front.write_mask = regs.stencil_front_mask;
state.stencil.back.test_func = MaxwellToGL::ComparisonOp(regs.stencil_back_func_func);
state.stencil.back.test_ref = regs.stencil_back_func_ref;
state.stencil.back.test_mask = regs.stencil_back_func_mask;
state.stencil.back.action_stencil_fail = MaxwellToGL::StencilOp(regs.stencil_back_op_fail);
state.stencil.back.action_depth_fail = MaxwellToGL::StencilOp(regs.stencil_back_op_zfail);
state.stencil.back.action_depth_pass = MaxwellToGL::StencilOp(regs.stencil_back_op_zpass);
state.stencil.back.write_mask = regs.stencil_back_mask;
if (regs.stencil_two_side_enable) {
state.stencil.back.test_func = MaxwellToGL::ComparisonOp(regs.stencil_back_func_func);
state.stencil.back.test_ref = regs.stencil_back_func_ref;
state.stencil.back.test_mask = regs.stencil_back_func_mask;
state.stencil.back.action_stencil_fail = MaxwellToGL::StencilOp(regs.stencil_back_op_fail);
state.stencil.back.action_depth_fail = MaxwellToGL::StencilOp(regs.stencil_back_op_zfail);
state.stencil.back.action_depth_pass = MaxwellToGL::StencilOp(regs.stencil_back_op_zpass);
state.stencil.back.write_mask = regs.stencil_back_mask;
} else {
state.stencil.back.test_func = GL_ALWAYS;
state.stencil.back.test_ref = 0;
state.stencil.back.test_mask = 0xFFFFFFFF;
state.stencil.back.write_mask = 0xFFFFFFFF;
state.stencil.back.action_stencil_fail = GL_KEEP;
state.stencil.back.action_depth_fail = GL_KEEP;
state.stencil.back.action_depth_pass = GL_KEEP;
}
}
void RasterizerOpenGL::SyncColorMask() {
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
for (size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) {
const std::size_t count =
regs.independent_blend_enable ? Tegra::Engines::Maxwell3D::Regs::NumRenderTargets : 1;
for (std::size_t i = 0; i < count; i++) {
const auto& source = regs.color_mask[regs.color_mask_common ? 0 : i];
auto& dest = state.color_mask[i];
dest.red_enabled = (source.R == 0) ? GL_FALSE : GL_TRUE;
@@ -1002,6 +1053,17 @@ void RasterizerOpenGL::SyncColorMask() {
}
}
void RasterizerOpenGL::SyncMultiSampleState() {
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
state.multisample_control.alpha_to_coverage = regs.multisample_control.alpha_to_coverage != 0;
state.multisample_control.alpha_to_one = regs.multisample_control.alpha_to_one != 0;
}
void RasterizerOpenGL::SyncFragmentColorClampState() {
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
state.fragment_color_clamp.enabled = regs.frag_color_clamp != 0;
}
void RasterizerOpenGL::SyncBlendState() {
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
@@ -1013,43 +1075,40 @@ void RasterizerOpenGL::SyncBlendState() {
state.independant_blend.enabled = regs.independent_blend_enable;
if (!state.independant_blend.enabled) {
auto& blend = state.blend[0];
blend.enabled = regs.blend.enable[0] != 0;
blend.separate_alpha = regs.blend.separate_alpha;
blend.rgb_equation = MaxwellToGL::BlendEquation(regs.blend.equation_rgb);
blend.src_rgb_func = MaxwellToGL::BlendFunc(regs.blend.factor_source_rgb);
blend.dst_rgb_func = MaxwellToGL::BlendFunc(regs.blend.factor_dest_rgb);
if (blend.separate_alpha) {
blend.a_equation = MaxwellToGL::BlendEquation(regs.blend.equation_a);
blend.src_a_func = MaxwellToGL::BlendFunc(regs.blend.factor_source_a);
blend.dst_a_func = MaxwellToGL::BlendFunc(regs.blend.factor_dest_a);
const auto& src = regs.blend;
blend.enabled = src.enable[0] != 0;
if (blend.enabled) {
blend.rgb_equation = MaxwellToGL::BlendEquation(src.equation_rgb);
blend.src_rgb_func = MaxwellToGL::BlendFunc(src.factor_source_rgb);
blend.dst_rgb_func = MaxwellToGL::BlendFunc(src.factor_dest_rgb);
blend.a_equation = MaxwellToGL::BlendEquation(src.equation_a);
blend.src_a_func = MaxwellToGL::BlendFunc(src.factor_source_a);
blend.dst_a_func = MaxwellToGL::BlendFunc(src.factor_dest_a);
}
for (size_t i = 1; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) {
for (std::size_t i = 1; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) {
state.blend[i].enabled = false;
}
return;
}
for (size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) {
for (std::size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) {
auto& blend = state.blend[i];
const auto& src = regs.independent_blend[i];
blend.enabled = regs.blend.enable[i] != 0;
if (!blend.enabled)
continue;
blend.separate_alpha = regs.independent_blend[i].separate_alpha;
blend.rgb_equation = MaxwellToGL::BlendEquation(regs.independent_blend[i].equation_rgb);
blend.src_rgb_func = MaxwellToGL::BlendFunc(regs.independent_blend[i].factor_source_rgb);
blend.dst_rgb_func = MaxwellToGL::BlendFunc(regs.independent_blend[i].factor_dest_rgb);
if (blend.separate_alpha) {
blend.a_equation = MaxwellToGL::BlendEquation(regs.independent_blend[i].equation_a);
blend.src_a_func = MaxwellToGL::BlendFunc(regs.independent_blend[i].factor_source_a);
blend.dst_a_func = MaxwellToGL::BlendFunc(regs.independent_blend[i].factor_dest_a);
}
blend.rgb_equation = MaxwellToGL::BlendEquation(src.equation_rgb);
blend.src_rgb_func = MaxwellToGL::BlendFunc(src.factor_source_rgb);
blend.dst_rgb_func = MaxwellToGL::BlendFunc(src.factor_dest_rgb);
blend.a_equation = MaxwellToGL::BlendEquation(src.equation_a);
blend.src_a_func = MaxwellToGL::BlendFunc(src.factor_source_a);
blend.dst_a_func = MaxwellToGL::BlendFunc(src.factor_dest_a);
}
}
void RasterizerOpenGL::SyncLogicOpState() {
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
// TODO(Subv): Support more than just render target 0.
state.logic_op.enabled = regs.logic_op.enable != 0;
if (!state.logic_op.enabled)
@@ -1062,19 +1121,21 @@ void RasterizerOpenGL::SyncLogicOpState() {
}
void RasterizerOpenGL::SyncScissorTest() {
// TODO: what is the correct behavior here, a single scissor for all targets
// or scissor disabled for the rest of the targets?
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
state.scissor.enabled = (regs.scissor_test.enable != 0);
if (regs.scissor_test.enable == 0) {
return;
for (std::size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumViewports; i++) {
const auto& src = regs.scissor_test[i];
auto& dst = state.viewports[i].scissor;
dst.enabled = (src.enable != 0);
if (dst.enabled == 0) {
return;
}
const u32 width = src.max_x - src.min_x;
const u32 height = src.max_y - src.min_y;
dst.x = src.min_x;
dst.y = src.min_y;
dst.width = width;
dst.height = height;
}
const u32 width = regs.scissor_test.max_x - regs.scissor_test.min_x;
const u32 height = regs.scissor_test.max_y - regs.scissor_test.min_y;
state.scissor.x = regs.scissor_test.min_x;
state.scissor.y = regs.scissor_test.min_y;
state.scissor.width = width;
state.scissor.height = height;
}
void RasterizerOpenGL::SyncTransformFeedback() {
@@ -1088,20 +1149,15 @@ void RasterizerOpenGL::SyncTransformFeedback() {
void RasterizerOpenGL::SyncPointState() {
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
// TODO(Rodrigo): Most games do not set a point size. I think this is a case of a
// register carrying a default value. For now, if the point size is zero, assume it's
// OpenGL's default (1).
state.point.size = regs.point_size == 0 ? 1 : regs.point_size;
state.point.size = regs.point_size;
}
void RasterizerOpenGL::CheckAlphaTests() {
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
if (regs.alpha_test_enabled != 0 && regs.rt_control.count > 1) {
LOG_CRITICAL(
Render_OpenGL,
"Alpha Testing is enabled with Multiple Render Targets, this behavior is undefined.");
LOG_CRITICAL(Render_OpenGL, "Alpha Testing is enabled with Multiple Render Targets, "
"this behavior is undefined.");
UNREACHABLE();
}
}

View File

@@ -88,7 +88,7 @@ private:
/// SamplerInfo struct.
void Create();
/// Syncs the sampler object with the config, updating any necessary state.
void SyncWithConfig(const Tegra::Texture::TSCEntry& config);
void SyncWithConfig(const Tegra::Texture::TSCEntry& info);
private:
Tegra::Texture::TextureFilter mag_filter;
@@ -100,6 +100,10 @@ private:
bool uses_depth_compare;
Tegra::Texture::DepthCompareFunc depth_compare_func;
GLvec4 border_color;
float min_lod;
float max_lod;
float lod_bias;
float max_anisotropic;
};
/**
@@ -109,8 +113,8 @@ private:
* @param preserve_contents If true, tries to preserve data from a previously used framebuffer.
* @param single_color_target Specifies if a single color buffer target should be used.
*/
void ConfigureFramebuffers(bool use_color_fb = true, bool using_depth_fb = true,
bool preserve_contents = true,
void ConfigureFramebuffers(OpenGLState& current_state, bool use_color_fb = true,
bool using_depth_fb = true, bool preserve_contents = true,
std::optional<std::size_t> single_color_target = {});
/*
@@ -134,7 +138,7 @@ private:
GLenum primitive_mode, u32 current_unit);
/// Syncs the viewport and depth range to match the guest state
void SyncViewport();
void SyncViewport(OpenGLState& current_state);
/// Syncs the clip enabled status to match the guest state
void SyncClipEnabled();
@@ -160,6 +164,12 @@ private:
/// Syncs the LogicOp state to match the guest state
void SyncLogicOpState();
/// Syncs the the color clamp state
void SyncFragmentColorClampState();
/// Syncs the alpha coverage and alpha to one
void SyncMultiSampleState();
/// Syncs the scissor test state to match the guest state
void SyncScissorTest();
@@ -207,7 +217,8 @@ private:
std::size_t CalculateIndexBufferSize() const;
void SetupVertexArrays();
void SetupVertexFormat();
void SetupVertexBuffer();
DrawParameters SetupDraw();

View File

@@ -314,6 +314,8 @@ static constexpr std::array<FormatTuple, VideoCore::Surface::MaxPixelFormat> tex
{GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_5X4_SRGB
{GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_5X5
{GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_5X5_SRGB
{GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_10X8
{GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_10X8_SRGB
// Depth formats
{GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT, ComponentType::Float, false}, // Z32F
@@ -379,11 +381,8 @@ void MortonCopy(u32 stride, u32 block_height, u32 height, u32 block_depth, u32 d
const u32 tile_size_y{GetDefaultBlockHeight(format)};
if (morton_to_gl) {
const std::vector<u8> data =
Tegra::Texture::UnswizzleTexture(addr, tile_size_x, tile_size_y, bytes_per_pixel,
stride, height, depth, block_height, block_depth);
const std::size_t size_to_copy{std::min(gl_buffer_size, data.size())};
memcpy(gl_buffer, data.data(), size_to_copy);
Tegra::Texture::UnswizzleTexture(gl_buffer, addr, tile_size_x, tile_size_y, bytes_per_pixel,
stride, height, depth, block_height, block_depth);
} else {
Tegra::Texture::CopySwizzledData((stride + tile_size_x - 1) / tile_size_x,
(height + tile_size_y - 1) / tile_size_y, depth,
@@ -456,6 +455,8 @@ static constexpr GLConversionArray morton_to_gl_fns = {
MortonCopy<true, PixelFormat::ASTC_2D_5X4_SRGB>,
MortonCopy<true, PixelFormat::ASTC_2D_5X5>,
MortonCopy<true, PixelFormat::ASTC_2D_5X5_SRGB>,
MortonCopy<true, PixelFormat::ASTC_2D_10X8>,
MortonCopy<true, PixelFormat::ASTC_2D_10X8_SRGB>,
MortonCopy<true, PixelFormat::Z32F>,
MortonCopy<true, PixelFormat::Z16>,
MortonCopy<true, PixelFormat::Z24S8>,
@@ -526,6 +527,8 @@ static constexpr GLConversionArray gl_to_morton_fns = {
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
MortonCopy<false, PixelFormat::Z32F>,
MortonCopy<false, PixelFormat::Z16>,
MortonCopy<false, PixelFormat::Z24S8>,
@@ -544,8 +547,8 @@ void SwizzleFunc(const GLConversionArray& functions, const SurfaceParams& params
if (params.is_layered) {
u64 offset = params.GetMipmapLevelOffset(mip_level);
u64 offset_gl = 0;
u64 layer_size = params.LayerMemorySize();
u64 gl_size = params.LayerSizeGL(mip_level);
const u64 layer_size = params.LayerMemorySize();
const u64 gl_size = params.LayerSizeGL(mip_level);
for (u32 i = 0; i < params.depth; i++) {
functions[static_cast<std::size_t>(params.pixel_format)](
params.MipWidth(mip_level), params.MipBlockHeight(mip_level),
@@ -555,7 +558,7 @@ void SwizzleFunc(const GLConversionArray& functions, const SurfaceParams& params
offset_gl += gl_size;
}
} else {
u64 offset = params.GetMipmapLevelOffset(mip_level);
const u64 offset = params.GetMipmapLevelOffset(mip_level);
functions[static_cast<std::size_t>(params.pixel_format)](
params.MipWidth(mip_level), params.MipBlockHeight(mip_level),
params.MipHeight(mip_level), params.MipBlockDepth(mip_level), depth, gl_buffer.data(),
@@ -580,7 +583,7 @@ static bool BlitSurface(const Surface& src_surface, const Surface& dst_surface,
state.draw.draw_framebuffer = draw_fb_handle;
// Set sRGB enabled if the destination surfaces need it
state.framebuffer_srgb.enabled = dst_params.srgb_conversion;
state.Apply();
state.ApplyFramebufferState();
u32 buffers{};
@@ -709,18 +712,18 @@ static void FastCopySurface(const Surface& src_surface, const Surface& dst_surfa
MICROPROFILE_DEFINE(OpenGL_CopySurface, "OpenGL", "CopySurface", MP_RGB(128, 192, 64));
static void CopySurface(const Surface& src_surface, const Surface& dst_surface,
GLuint copy_pbo_handle, GLenum src_attachment = 0,
GLenum dst_attachment = 0, std::size_t cubemap_face = 0) {
const GLuint copy_pbo_handle, const GLenum src_attachment = 0,
const GLenum dst_attachment = 0, const std::size_t cubemap_face = 0) {
MICROPROFILE_SCOPE(OpenGL_CopySurface);
ASSERT_MSG(dst_attachment == 0, "Unimplemented");
const auto& src_params{src_surface->GetSurfaceParams()};
const auto& dst_params{dst_surface->GetSurfaceParams()};
auto source_format = GetFormatTuple(src_params.pixel_format, src_params.component_type);
auto dest_format = GetFormatTuple(dst_params.pixel_format, dst_params.component_type);
const auto source_format = GetFormatTuple(src_params.pixel_format, src_params.component_type);
const auto dest_format = GetFormatTuple(dst_params.pixel_format, dst_params.component_type);
std::size_t buffer_size = std::max(src_params.size_in_bytes, dst_params.size_in_bytes);
const std::size_t buffer_size = std::max(src_params.size_in_bytes, dst_params.size_in_bytes);
glBindBuffer(GL_PIXEL_PACK_BUFFER, copy_pbo_handle);
glBufferData(GL_PIXEL_PACK_BUFFER, buffer_size, nullptr, GL_STREAM_DRAW_ARB);
@@ -744,13 +747,10 @@ static void CopySurface(const Surface& src_surface, const Surface& dst_surface,
LOG_DEBUG(HW_GPU, "Trying to upload extra texture data from the CPU during "
"reinterpretation but the texture is tiled.");
}
std::size_t remaining_size = dst_params.size_in_bytes - src_params.size_in_bytes;
std::vector<u8> data(remaining_size);
std::memcpy(data.data(), Memory::GetPointer(dst_params.addr + src_params.size_in_bytes),
data.size());
const std::size_t remaining_size = dst_params.size_in_bytes - src_params.size_in_bytes;
glBufferSubData(GL_PIXEL_PACK_BUFFER, src_params.size_in_bytes, remaining_size,
data.data());
Memory::GetPointer(dst_params.addr + src_params.size_in_bytes));
}
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
@@ -932,7 +932,9 @@ static void ConvertFormatAsNeeded_LoadGLBuffer(std::vector<u8>& data, PixelForma
case PixelFormat::ASTC_2D_8X8_SRGB:
case PixelFormat::ASTC_2D_8X5_SRGB:
case PixelFormat::ASTC_2D_5X4_SRGB:
case PixelFormat::ASTC_2D_5X5_SRGB: {
case PixelFormat::ASTC_2D_5X5_SRGB:
case PixelFormat::ASTC_2D_10X8:
case PixelFormat::ASTC_2D_10X8_SRGB: {
// Convert ASTC pixel formats to RGBA8, as most desktop GPUs do not support ASTC.
u32 block_width{};
u32 block_height{};
@@ -967,7 +969,11 @@ static void ConvertFormatAsNeeded_FlushGLBuffer(std::vector<u8>& data, PixelForm
case PixelFormat::ASTC_2D_4X4:
case PixelFormat::ASTC_2D_8X8:
case PixelFormat::ASTC_2D_4X4_SRGB:
case PixelFormat::ASTC_2D_8X8_SRGB: {
case PixelFormat::ASTC_2D_8X8_SRGB:
case PixelFormat::ASTC_2D_5X5:
case PixelFormat::ASTC_2D_5X5_SRGB:
case PixelFormat::ASTC_2D_10X8:
case PixelFormat::ASTC_2D_10X8_SRGB: {
LOG_CRITICAL(HW_GPU, "Conversion of format {} after texture flushing is not implemented",
static_cast<u32>(pixel_format));
UNREACHABLE();
@@ -1336,6 +1342,7 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface,
break;
case SurfaceTarget::TextureCubemap:
case SurfaceTarget::Texture3D:
case SurfaceTarget::Texture2DArray:
case SurfaceTarget::TextureCubeArray:
AccurateCopySurface(old_surface, new_surface);
break;

View File

@@ -121,12 +121,16 @@ GLint CachedShader::GetUniformLocation(const GLShader::SamplerEntry& sampler) {
}
GLuint CachedShader::LazyGeometryProgram(OGLProgram& target_program,
const std::string& glsl_topology,
const std::string& glsl_topology, u32 max_vertices,
const std::string& debug_name) {
if (target_program.handle != 0) {
return target_program.handle;
}
const std::string source{geometry_programs.code + "layout (" + glsl_topology + ") in;\n"};
std::string source = "#version 430 core\n";
source += "layout (" + glsl_topology + ") in;\n";
source += "#define MAX_VERTEX_INPUT " + std::to_string(max_vertices) + '\n';
source += geometry_programs.code;
OGLShader shader;
shader.Create(source.c_str(), GL_GEOMETRY_SHADER);
target_program.Create(true, shader.handle);

View File

@@ -48,22 +48,23 @@ public:
}
switch (primitive_mode) {
case GL_POINTS:
return LazyGeometryProgram(geometry_programs.points, "points", "ShaderPoints");
return LazyGeometryProgram(geometry_programs.points, "points", 1, "ShaderPoints");
case GL_LINES:
case GL_LINE_STRIP:
return LazyGeometryProgram(geometry_programs.lines, "lines", "ShaderLines");
return LazyGeometryProgram(geometry_programs.lines, "lines", 2, "ShaderLines");
case GL_LINES_ADJACENCY:
case GL_LINE_STRIP_ADJACENCY:
return LazyGeometryProgram(geometry_programs.lines_adjacency, "lines_adjacency",
return LazyGeometryProgram(geometry_programs.lines_adjacency, "lines_adjacency", 4,
"ShaderLinesAdjacency");
case GL_TRIANGLES:
case GL_TRIANGLE_STRIP:
case GL_TRIANGLE_FAN:
return LazyGeometryProgram(geometry_programs.triangles, "triangles", "ShaderTriangles");
return LazyGeometryProgram(geometry_programs.triangles, "triangles", 3,
"ShaderTriangles");
case GL_TRIANGLES_ADJACENCY:
case GL_TRIANGLE_STRIP_ADJACENCY:
return LazyGeometryProgram(geometry_programs.triangles_adjacency, "triangles_adjacency",
"ShaderLines");
6, "ShaderTrianglesAdjacency");
default:
UNREACHABLE_MSG("Unknown primitive mode.");
}
@@ -78,7 +79,7 @@ public:
private:
/// Generates a geometry shader or returns one that already exists.
GLuint LazyGeometryProgram(OGLProgram& target_program, const std::string& glsl_topology,
const std::string& debug_name);
u32 max_vertices, const std::string& debug_name);
VAddr addr;
Maxwell::ShaderProgram program_type;

View File

@@ -494,10 +494,10 @@ public:
// instruction for now.
if (stage == Maxwell3D::Regs::ShaderStage::Geometry) {
// TODO(Rodrigo): nouveau sets some attributes after setting emitting a geometry
// shader. These instructions use a dirty register as buffer index. To avoid some
// drivers from complaining for the out of boundary writes, guard them.
const std::string buf_index{"min(" + GetRegisterAsInteger(buf_reg) + ", " +
std::to_string(MAX_GEOMETRY_BUFFERS - 1) + ')'};
// shader. These instructions use a dirty register as buffer index, to avoid some
// drivers from complaining about out of boundary writes, guard them.
const std::string buf_index{"((" + GetRegisterAsInteger(buf_reg) + ") % " +
std::to_string(MAX_GEOMETRY_BUFFERS) + ')'};
shader.AddLine("amem[" + buf_index + "][" +
std::to_string(static_cast<u32>(attribute)) + ']' +
GetSwizzle(elem) + " = " + src + ';');
@@ -811,7 +811,11 @@ private:
std::optional<Register> vertex = {}) {
auto GeometryPass = [&](const std::string& name) {
if (stage == Maxwell3D::Regs::ShaderStage::Geometry && vertex) {
return "gs_" + name + '[' + GetRegisterAsInteger(*vertex, 0, false) + ']';
// TODO(Rodrigo): Guard geometry inputs against out of bound reads. Some games set
// an 0x80000000 index for those and the shader fails to build. Find out why this
// happens and what's its intent.
return "gs_" + name + '[' + GetRegisterAsInteger(*vertex, 0, false) +
" % MAX_VERTEX_INPUT]";
}
return name;
};
@@ -2742,12 +2746,12 @@ private:
}
case 3: {
if (is_array) {
UNIMPLEMENTED_MSG("3-coordinate arrays not fully implemented");
const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
const std::string y = regs.GetRegisterAsFloat(instr.gpr20);
coord = "vec2 coords = vec2(" + x + ", " + y + ");";
texture_type = Tegra::Shader::TextureType::Texture2D;
is_array = false;
const std::string index = regs.GetRegisterAsInteger(instr.gpr8);
const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 2);
const std::string z = regs.GetRegisterAsFloat(instr.gpr20);
coord =
"vec4 coords = vec4(" + x + ", " + y + ", " + z + ", " + index + ");";
} else {
const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
@@ -2777,7 +2781,11 @@ private:
break;
}
case Tegra::Shader::TextureProcessMode::LZ: {
texture = "textureLod(" + sampler + ", coords, 0.0)";
if (depth_compare && is_array) {
texture = "texture(" + sampler + ", coords)";
} else {
texture = "textureLod(" + sampler + ", coords, 0.0)";
}
break;
}
case Tegra::Shader::TextureProcessMode::LL: {

View File

@@ -82,8 +82,8 @@ void main() {
}
ProgramResult GenerateGeometryShader(const ShaderSetup& setup) {
std::string out = "#version 430 core\n";
out += "#extension GL_ARB_separate_shader_objects : enable\n\n";
// Version is intentionally skipped in shader generation, it's added by the lazy compilation.
std::string out = "#extension GL_ARB_separate_shader_objects : enable\n\n";
out += Decompiler::GetCommonDeclarations();
out += "bool exec_geometry();\n";

View File

@@ -67,6 +67,7 @@ public:
glUseProgramStages(pipeline.handle, GL_FRAGMENT_SHADER_BIT, fs);
state.draw.shader_program = 0;
state.draw.program_pipeline = pipeline.handle;
state.geometry_shaders.enabled = (gs != 0);
}
private:

View File

@@ -14,7 +14,10 @@ OpenGLState OpenGLState::cur_state;
bool OpenGLState::s_rgb_used;
OpenGLState::OpenGLState() {
// These all match default OpenGL values
geometry_shaders.enabled = false;
framebuffer_srgb.enabled = false;
multisample_control.alpha_to_coverage = false;
multisample_control.alpha_to_one = false;
cull.enabled = false;
cull.mode = GL_BACK;
cull.front_face = GL_CCW;
@@ -50,12 +53,12 @@ OpenGLState::OpenGLState() {
item.height = 0;
item.depth_range_near = 0.0f;
item.depth_range_far = 1.0f;
item.scissor.enabled = false;
item.scissor.x = 0;
item.scissor.y = 0;
item.scissor.width = 0;
item.scissor.height = 0;
}
scissor.enabled = false;
scissor.x = 0;
scissor.y = 0;
scissor.width = 0;
scissor.height = 0;
for (auto& item : blend) {
item.enabled = true;
item.rgb_equation = GL_FUNC_ADD;
@@ -88,6 +91,7 @@ OpenGLState::OpenGLState() {
clip_distance = {};
point.size = 1;
fragment_color_clamp.enabled = false;
}
void OpenGLState::ApplyDefaultState() {
@@ -136,7 +140,7 @@ void OpenGLState::ApplyCulling() const {
}
void OpenGLState::ApplyColorMask() const {
if (GLAD_GL_ARB_viewport_array) {
if (GLAD_GL_ARB_viewport_array && independant_blend.enabled) {
for (size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) {
const auto& updated = color_mask[i];
const auto& current = cur_state.color_mask[i];
@@ -230,26 +234,10 @@ void OpenGLState::ApplyStencilTest() const {
}
}
void OpenGLState::ApplyScissor() const {
const bool scissor_changed = scissor.enabled != cur_state.scissor.enabled;
if (scissor_changed) {
if (scissor.enabled) {
glEnable(GL_SCISSOR_TEST);
} else {
glDisable(GL_SCISSOR_TEST);
}
}
if (scissor.enabled &&
(scissor_changed || scissor.x != cur_state.scissor.x || scissor.y != cur_state.scissor.y ||
scissor.width != cur_state.scissor.width || scissor.height != cur_state.scissor.height)) {
glScissor(scissor.x, scissor.y, scissor.width, scissor.height);
}
}
void OpenGLState::ApplyViewport() const {
if (GLAD_GL_ARB_viewport_array) {
for (GLuint i = 0;
i < static_cast<GLuint>(Tegra::Engines::Maxwell3D::Regs::NumRenderTargets); i++) {
if (GLAD_GL_ARB_viewport_array && geometry_shaders.enabled) {
for (GLuint i = 0; i < static_cast<GLuint>(Tegra::Engines::Maxwell3D::Regs::NumViewports);
i++) {
const auto& current = cur_state.viewports[i];
const auto& updated = viewports[i];
if (updated.x != current.x || updated.y != current.y ||
@@ -260,18 +248,50 @@ void OpenGLState::ApplyViewport() const {
updated.depth_range_far != current.depth_range_far) {
glDepthRangeIndexed(i, updated.depth_range_near, updated.depth_range_far);
}
const bool scissor_changed = updated.scissor.enabled != current.scissor.enabled;
if (scissor_changed) {
if (updated.scissor.enabled) {
glEnablei(GL_SCISSOR_TEST, i);
} else {
glDisablei(GL_SCISSOR_TEST, i);
}
}
if (updated.scissor.enabled &&
(scissor_changed || updated.scissor.x != current.scissor.x ||
updated.scissor.y != current.scissor.y ||
updated.scissor.width != current.scissor.width ||
updated.scissor.height != current.scissor.height)) {
glScissorIndexed(i, updated.scissor.x, updated.scissor.y, updated.scissor.width,
updated.scissor.height);
}
}
} else {
const auto& current = cur_state.viewports[0];
const auto& updated = viewports[0];
if (updated.x != current.x || updated.y != current.y || updated.width != current.width ||
updated.height != current.height) {
glViewport(updated.x, updated.y, updated.width, updated.height);
glViewport(static_cast<GLint>(updated.x), static_cast<GLint>(updated.y),
static_cast<GLsizei>(updated.width), static_cast<GLsizei>(updated.height));
}
if (updated.depth_range_near != current.depth_range_near ||
updated.depth_range_far != current.depth_range_far) {
glDepthRange(updated.depth_range_near, updated.depth_range_far);
}
const bool scissor_changed = updated.scissor.enabled != current.scissor.enabled;
if (scissor_changed) {
if (updated.scissor.enabled) {
glEnable(GL_SCISSOR_TEST);
} else {
glDisable(GL_SCISSOR_TEST);
}
}
if (updated.scissor.enabled && (scissor_changed || updated.scissor.x != current.scissor.x ||
updated.scissor.y != current.scissor.y ||
updated.scissor.width != current.scissor.width ||
updated.scissor.height != current.scissor.height)) {
glScissor(updated.scissor.x, updated.scissor.y, updated.scissor.width,
updated.scissor.height);
}
}
}
@@ -289,31 +309,20 @@ void OpenGLState::ApplyGlobalBlending() const {
if (!updated.enabled) {
return;
}
if (updated.separate_alpha) {
if (blend_changed || updated.src_rgb_func != current.src_rgb_func ||
updated.dst_rgb_func != current.dst_rgb_func ||
updated.src_a_func != current.src_a_func || updated.dst_a_func != current.dst_a_func) {
glBlendFuncSeparate(updated.src_rgb_func, updated.dst_rgb_func, updated.src_a_func,
updated.dst_a_func);
}
if (blend_changed || updated.src_rgb_func != current.src_rgb_func ||
updated.dst_rgb_func != current.dst_rgb_func || updated.src_a_func != current.src_a_func ||
updated.dst_a_func != current.dst_a_func) {
glBlendFuncSeparate(updated.src_rgb_func, updated.dst_rgb_func, updated.src_a_func,
updated.dst_a_func);
}
if (blend_changed || updated.rgb_equation != current.rgb_equation ||
updated.a_equation != current.a_equation) {
glBlendEquationSeparate(updated.rgb_equation, updated.a_equation);
}
} else {
if (blend_changed || updated.src_rgb_func != current.src_rgb_func ||
updated.dst_rgb_func != current.dst_rgb_func) {
glBlendFunc(updated.src_rgb_func, updated.dst_rgb_func);
}
if (blend_changed || updated.rgb_equation != current.rgb_equation) {
glBlendEquation(updated.rgb_equation);
}
if (blend_changed || updated.rgb_equation != current.rgb_equation ||
updated.a_equation != current.a_equation) {
glBlendEquationSeparate(updated.rgb_equation, updated.a_equation);
}
}
void OpenGLState::ApplyTargetBlending(int target, bool force) const {
void OpenGLState::ApplyTargetBlending(std::size_t target, bool force) const {
const Blend& updated = blend[target];
const Blend& current = cur_state.blend[target];
const bool blend_changed = updated.enabled != current.enabled || force;
@@ -327,29 +336,17 @@ void OpenGLState::ApplyTargetBlending(int target, bool force) const {
if (!updated.enabled) {
return;
}
if (updated.separate_alpha) {
if (blend_changed || updated.src_rgb_func != current.src_rgb_func ||
updated.dst_rgb_func != current.dst_rgb_func ||
updated.src_a_func != current.src_a_func || updated.dst_a_func != current.dst_a_func) {
glBlendFuncSeparateiARB(static_cast<GLuint>(target), updated.src_rgb_func,
updated.dst_rgb_func, updated.src_a_func, updated.dst_a_func);
}
if (blend_changed || updated.src_rgb_func != current.src_rgb_func ||
updated.dst_rgb_func != current.dst_rgb_func || updated.src_a_func != current.src_a_func ||
updated.dst_a_func != current.dst_a_func) {
glBlendFuncSeparateiARB(static_cast<GLuint>(target), updated.src_rgb_func,
updated.dst_rgb_func, updated.src_a_func, updated.dst_a_func);
}
if (blend_changed || updated.rgb_equation != current.rgb_equation ||
updated.a_equation != current.a_equation) {
glBlendEquationSeparateiARB(static_cast<GLuint>(target), updated.rgb_equation,
updated.a_equation);
}
} else {
if (blend_changed || updated.src_rgb_func != current.src_rgb_func ||
updated.dst_rgb_func != current.dst_rgb_func) {
glBlendFunciARB(static_cast<GLuint>(target), updated.src_rgb_func,
updated.dst_rgb_func);
}
if (blend_changed || updated.rgb_equation != current.rgb_equation) {
glBlendEquationiARB(static_cast<GLuint>(target), updated.rgb_equation);
}
if (blend_changed || updated.rgb_equation != current.rgb_equation ||
updated.a_equation != current.a_equation) {
glBlendEquationSeparateiARB(static_cast<GLuint>(target), updated.rgb_equation,
updated.a_equation);
}
}
@@ -427,7 +424,7 @@ void OpenGLState::ApplySamplers() const {
}
}
void OpenGLState::Apply() const {
void OpenGLState::ApplyFramebufferState() const {
// Framebuffer
if (draw.read_framebuffer != cur_state.draw.read_framebuffer) {
glBindFramebuffer(GL_READ_FRAMEBUFFER, draw.read_framebuffer);
@@ -435,7 +432,9 @@ void OpenGLState::Apply() const {
if (draw.draw_framebuffer != cur_state.draw.draw_framebuffer) {
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, draw.draw_framebuffer);
}
}
void OpenGLState::ApplyVertexBufferState() const {
// Vertex array
if (draw.vertex_array != cur_state.draw.vertex_array) {
glBindVertexArray(draw.vertex_array);
@@ -445,7 +444,11 @@ void OpenGLState::Apply() const {
if (draw.vertex_buffer != cur_state.draw.vertex_buffer) {
glBindBuffer(GL_ARRAY_BUFFER, draw.vertex_buffer);
}
}
void OpenGLState::Apply() const {
ApplyFramebufferState();
ApplyVertexBufferState();
// Uniform buffer
if (draw.uniform_buffer != cur_state.draw.uniform_buffer) {
glBindBuffer(GL_UNIFORM_BUFFER, draw.uniform_buffer);
@@ -474,9 +477,29 @@ void OpenGLState::Apply() const {
if (point.size != cur_state.point.size) {
glPointSize(point.size);
}
if (GLAD_GL_ARB_color_buffer_float) {
if (fragment_color_clamp.enabled != cur_state.fragment_color_clamp.enabled) {
glClampColor(GL_CLAMP_FRAGMENT_COLOR_ARB,
fragment_color_clamp.enabled ? GL_TRUE : GL_FALSE);
}
}
if (multisample_control.alpha_to_coverage != cur_state.multisample_control.alpha_to_coverage) {
if (multisample_control.alpha_to_coverage) {
glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE);
} else {
glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE);
}
}
if (multisample_control.alpha_to_one != cur_state.multisample_control.alpha_to_one) {
if (multisample_control.alpha_to_one) {
glEnable(GL_SAMPLE_ALPHA_TO_ONE);
} else {
glDisable(GL_SAMPLE_ALPHA_TO_ONE);
}
}
ApplyColorMask();
ApplyViewport();
ApplyScissor();
ApplyStencilTest();
ApplySRgb();
ApplyCulling();

View File

@@ -39,6 +39,19 @@ public:
bool enabled; // GL_FRAMEBUFFER_SRGB
} framebuffer_srgb;
struct {
bool alpha_to_coverage; // GL_ALPHA_TO_COVERAGE
bool alpha_to_one; // GL_ALPHA_TO_ONE
} multisample_control;
struct {
bool enabled; // GL_CLAMP_FRAGMENT_COLOR_ARB
} fragment_color_clamp;
struct {
bool enabled; // viewports arrays are only supported when geometry shaders are enabled.
} geometry_shaders;
struct {
bool enabled; // GL_CULL_FACE
GLenum mode; // GL_CULL_FACE_MODE
@@ -79,7 +92,6 @@ public:
struct Blend {
bool enabled; // GL_BLEND
bool separate_alpha; // Independent blend enabled
GLenum rgb_equation; // GL_BLEND_EQUATION_RGB
GLenum a_equation; // GL_BLEND_EQUATION_ALPHA
GLenum src_rgb_func; // GL_BLEND_SRC_RGB
@@ -150,16 +162,15 @@ public:
GLfloat height;
GLfloat depth_range_near; // GL_DEPTH_RANGE
GLfloat depth_range_far; // GL_DEPTH_RANGE
struct {
bool enabled; // GL_SCISSOR_TEST
GLint x;
GLint y;
GLsizei width;
GLsizei height;
} scissor;
};
std::array<viewport, Tegra::Engines::Maxwell3D::Regs::NumRenderTargets> viewports;
struct {
bool enabled; // GL_SCISSOR_TEST
GLint x;
GLint y;
GLsizei width;
GLsizei height;
} scissor;
std::array<viewport, Tegra::Engines::Maxwell3D::Regs::NumViewports> viewports;
struct {
float size; // GL_POINT_SIZE
@@ -181,6 +192,10 @@ public:
}
/// Apply this state as the current OpenGL state
void Apply() const;
/// Apply only the state afecting the framebuffer
void ApplyFramebufferState() const;
/// Apply only the state afecting the vertex buffer
void ApplyVertexBufferState() const;
/// Set the initial OpenGL state
static void ApplyDefaultState();
/// Resets any references to the given resource
@@ -204,13 +219,12 @@ private:
void ApplyPrimitiveRestart() const;
void ApplyStencilTest() const;
void ApplyViewport() const;
void ApplyTargetBlending(int target, bool force) const;
void ApplyTargetBlending(std::size_t target, bool force) const;
void ApplyGlobalBlending() const;
void ApplyBlending() const;
void ApplyLogicOp() const;
void ApplyTextures() const;
void ApplySamplers() const;
void ApplyScissor() const;
};
} // namespace OpenGL

View File

@@ -159,10 +159,8 @@ inline GLenum TextureFilterMode(Tegra::Texture::TextureFilter filter_mode,
}
}
}
LOG_CRITICAL(Render_OpenGL, "Unimplemented texture filter mode={}",
static_cast<u32>(filter_mode));
UNREACHABLE();
return {};
LOG_ERROR(Render_OpenGL, "Unimplemented texture filter mode={}", static_cast<u32>(filter_mode));
return GL_LINEAR;
}
inline GLenum WrapMode(Tegra::Texture::WrapMode wrap_mode) {
@@ -182,10 +180,15 @@ inline GLenum WrapMode(Tegra::Texture::WrapMode wrap_mode) {
return GL_CLAMP_TO_BORDER;
case Tegra::Texture::WrapMode::MirrorOnceClampToEdge:
return GL_MIRROR_CLAMP_TO_EDGE;
case Tegra::Texture::WrapMode::MirrorOnceBorder:
if (GL_EXT_texture_mirror_clamp) {
return GL_MIRROR_CLAMP_TO_BORDER_EXT;
} else {
return GL_MIRROR_CLAMP_TO_EDGE;
}
}
LOG_CRITICAL(Render_OpenGL, "Unimplemented texture wrap mode={}", static_cast<u32>(wrap_mode));
UNREACHABLE();
return {};
LOG_ERROR(Render_OpenGL, "Unimplemented texture wrap mode={}", static_cast<u32>(wrap_mode));
return GL_REPEAT;
}
inline GLenum DepthCompareFunc(Tegra::Texture::DepthCompareFunc func) {
@@ -207,10 +210,9 @@ inline GLenum DepthCompareFunc(Tegra::Texture::DepthCompareFunc func) {
case Tegra::Texture::DepthCompareFunc::Always:
return GL_ALWAYS;
}
LOG_CRITICAL(Render_OpenGL, "Unimplemented texture depth compare function ={}",
static_cast<u32>(func));
UNREACHABLE();
return {};
LOG_ERROR(Render_OpenGL, "Unimplemented texture depth compare function ={}",
static_cast<u32>(func));
return GL_GREATER;
}
inline GLenum BlendEquation(Maxwell::Blend::Equation equation) {
@@ -226,9 +228,8 @@ inline GLenum BlendEquation(Maxwell::Blend::Equation equation) {
case Maxwell::Blend::Equation::Max:
return GL_MAX;
}
LOG_CRITICAL(Render_OpenGL, "Unimplemented blend equation={}", static_cast<u32>(equation));
UNREACHABLE();
return {};
LOG_ERROR(Render_OpenGL, "Unimplemented blend equation={}", static_cast<u32>(equation));
return GL_FUNC_ADD;
}
inline GLenum BlendFunc(Maxwell::Blend::Factor factor) {
@@ -291,9 +292,8 @@ inline GLenum BlendFunc(Maxwell::Blend::Factor factor) {
case Maxwell::Blend::Factor::OneMinusConstantAlphaGL:
return GL_ONE_MINUS_CONSTANT_ALPHA;
}
LOG_CRITICAL(Render_OpenGL, "Unimplemented blend factor={}", static_cast<u32>(factor));
UNREACHABLE();
return {};
LOG_ERROR(Render_OpenGL, "Unimplemented blend factor={}", static_cast<u32>(factor));
return GL_ZERO;
}
inline GLenum SwizzleSource(Tegra::Texture::SwizzleSource source) {
@@ -312,9 +312,8 @@ inline GLenum SwizzleSource(Tegra::Texture::SwizzleSource source) {
case Tegra::Texture::SwizzleSource::OneFloat:
return GL_ONE;
}
LOG_CRITICAL(Render_OpenGL, "Unimplemented swizzle source={}", static_cast<u32>(source));
UNREACHABLE();
return {};
LOG_ERROR(Render_OpenGL, "Unimplemented swizzle source={}", static_cast<u32>(source));
return GL_ZERO;
}
inline GLenum ComparisonOp(Maxwell::ComparisonOp comparison) {
@@ -344,33 +343,39 @@ inline GLenum ComparisonOp(Maxwell::ComparisonOp comparison) {
case Maxwell::ComparisonOp::AlwaysOld:
return GL_ALWAYS;
}
LOG_CRITICAL(Render_OpenGL, "Unimplemented comparison op={}", static_cast<u32>(comparison));
UNREACHABLE();
return {};
LOG_ERROR(Render_OpenGL, "Unimplemented comparison op={}", static_cast<u32>(comparison));
return GL_ALWAYS;
}
inline GLenum StencilOp(Maxwell::StencilOp stencil) {
switch (stencil) {
case Maxwell::StencilOp::Keep:
case Maxwell::StencilOp::KeepOGL:
return GL_KEEP;
case Maxwell::StencilOp::Zero:
case Maxwell::StencilOp::ZeroOGL:
return GL_ZERO;
case Maxwell::StencilOp::Replace:
case Maxwell::StencilOp::ReplaceOGL:
return GL_REPLACE;
case Maxwell::StencilOp::Incr:
case Maxwell::StencilOp::IncrOGL:
return GL_INCR;
case Maxwell::StencilOp::Decr:
case Maxwell::StencilOp::DecrOGL:
return GL_DECR;
case Maxwell::StencilOp::Invert:
case Maxwell::StencilOp::InvertOGL:
return GL_INVERT;
case Maxwell::StencilOp::IncrWrap:
case Maxwell::StencilOp::IncrWrapOGL:
return GL_INCR_WRAP;
case Maxwell::StencilOp::DecrWrap:
case Maxwell::StencilOp::DecrWrapOGL:
return GL_DECR_WRAP;
}
LOG_CRITICAL(Render_OpenGL, "Unimplemented stencil op={}", static_cast<u32>(stencil));
UNREACHABLE();
return {};
LOG_ERROR(Render_OpenGL, "Unimplemented stencil op={}", static_cast<u32>(stencil));
return GL_KEEP;
}
inline GLenum FrontFace(Maxwell::Cull::FrontFace front_face) {
@@ -380,9 +385,8 @@ inline GLenum FrontFace(Maxwell::Cull::FrontFace front_face) {
case Maxwell::Cull::FrontFace::CounterClockWise:
return GL_CCW;
}
LOG_CRITICAL(Render_OpenGL, "Unimplemented front face cull={}", static_cast<u32>(front_face));
UNREACHABLE();
return {};
LOG_ERROR(Render_OpenGL, "Unimplemented front face cull={}", static_cast<u32>(front_face));
return GL_CCW;
}
inline GLenum CullFace(Maxwell::Cull::CullFace cull_face) {
@@ -394,9 +398,8 @@ inline GLenum CullFace(Maxwell::Cull::CullFace cull_face) {
case Maxwell::Cull::CullFace::FrontAndBack:
return GL_FRONT_AND_BACK;
}
LOG_CRITICAL(Render_OpenGL, "Unimplemented cull face={}", static_cast<u32>(cull_face));
UNREACHABLE();
return {};
LOG_ERROR(Render_OpenGL, "Unimplemented cull face={}", static_cast<u32>(cull_face));
return GL_BACK;
}
inline GLenum LogicOp(Maxwell::LogicOperation operation) {
@@ -434,9 +437,8 @@ inline GLenum LogicOp(Maxwell::LogicOperation operation) {
case Maxwell::LogicOperation::Set:
return GL_SET;
}
LOG_CRITICAL(Render_OpenGL, "Unimplemented logic operation={}", static_cast<u32>(operation));
UNREACHABLE();
return {};
LOG_ERROR(Render_OpenGL, "Unimplemented logic operation={}", static_cast<u32>(operation));
return GL_COPY;
}
} // namespace MaxwellToGL

View File

@@ -306,6 +306,8 @@ PixelFormat PixelFormatFromTextureFormat(Tegra::Texture::TextureFormat format,
return is_srgb ? PixelFormat::ASTC_2D_8X8_SRGB : PixelFormat::ASTC_2D_8X8;
case Tegra::Texture::TextureFormat::ASTC_2D_8X5:
return is_srgb ? PixelFormat::ASTC_2D_8X5_SRGB : PixelFormat::ASTC_2D_8X5;
case Tegra::Texture::TextureFormat::ASTC_2D_10X8:
return is_srgb ? PixelFormat::ASTC_2D_10X8_SRGB : PixelFormat::ASTC_2D_10X8;
case Tegra::Texture::TextureFormat::R16_G16:
switch (component_type) {
case Tegra::Texture::ComponentType::FLOAT:
@@ -453,6 +455,8 @@ bool IsPixelFormatASTC(PixelFormat format) {
case PixelFormat::ASTC_2D_5X5_SRGB:
case PixelFormat::ASTC_2D_8X8_SRGB:
case PixelFormat::ASTC_2D_8X5_SRGB:
case PixelFormat::ASTC_2D_10X8:
case PixelFormat::ASTC_2D_10X8_SRGB:
return true;
default:
return false;

View File

@@ -74,19 +74,21 @@ enum class PixelFormat {
ASTC_2D_5X4_SRGB = 56,
ASTC_2D_5X5 = 57,
ASTC_2D_5X5_SRGB = 58,
ASTC_2D_10X8 = 59,
ASTC_2D_10X8_SRGB = 60,
MaxColorFormat,
// Depth formats
Z32F = 59,
Z16 = 60,
Z32F = 61,
Z16 = 62,
MaxDepthFormat,
// DepthStencil formats
Z24S8 = 61,
S8Z24 = 62,
Z32FS8 = 63,
Z24S8 = 63,
S8Z24 = 64,
Z32FS8 = 65,
MaxDepthStencilFormat,
@@ -193,6 +195,8 @@ static constexpr u32 GetCompressionFactor(PixelFormat format) {
4, // ASTC_2D_5X4_SRGB
4, // ASTC_2D_5X5
4, // ASTC_2D_5X5_SRGB
4, // ASTC_2D_10X8
4, // ASTC_2D_10X8_SRGB
1, // Z32F
1, // Z16
1, // Z24S8
@@ -208,70 +212,72 @@ static constexpr u32 GetDefaultBlockWidth(PixelFormat format) {
if (format == PixelFormat::Invalid)
return 0;
constexpr std::array<u32, MaxPixelFormat> block_width_table = {{
1, // ABGR8U
1, // ABGR8S
1, // ABGR8UI
1, // B5G6R5U
1, // A2B10G10R10U
1, // A1B5G5R5U
1, // R8U
1, // R8UI
1, // RGBA16F
1, // RGBA16U
1, // RGBA16UI
1, // R11FG11FB10F
1, // RGBA32UI
4, // DXT1
4, // DXT23
4, // DXT45
4, // DXN1
4, // DXN2UNORM
4, // DXN2SNORM
4, // BC7U
4, // BC6H_UF16
4, // BC6H_SF16
4, // ASTC_2D_4X4
1, // G8R8U
1, // G8R8S
1, // BGRA8
1, // RGBA32F
1, // RG32F
1, // R32F
1, // R16F
1, // R16U
1, // R16S
1, // R16UI
1, // R16I
1, // RG16
1, // RG16F
1, // RG16UI
1, // RG16I
1, // RG16S
1, // RGB32F
1, // RGBA8_SRGB
1, // RG8U
1, // RG8S
1, // RG32UI
1, // R32UI
8, // ASTC_2D_8X8
8, // ASTC_2D_8X5
5, // ASTC_2D_5X4
1, // BGRA8_SRGB
4, // DXT1_SRGB
4, // DXT23_SRGB
4, // DXT45_SRGB
4, // BC7U_SRGB
4, // ASTC_2D_4X4_SRGB
8, // ASTC_2D_8X8_SRGB
8, // ASTC_2D_8X5_SRGB
5, // ASTC_2D_5X4_SRGB
5, // ASTC_2D_5X5
5, // ASTC_2D_5X5_SRGB
1, // Z32F
1, // Z16
1, // Z24S8
1, // S8Z24
1, // Z32FS8
1, // ABGR8U
1, // ABGR8S
1, // ABGR8UI
1, // B5G6R5U
1, // A2B10G10R10U
1, // A1B5G5R5U
1, // R8U
1, // R8UI
1, // RGBA16F
1, // RGBA16U
1, // RGBA16UI
1, // R11FG11FB10F
1, // RGBA32UI
4, // DXT1
4, // DXT23
4, // DXT45
4, // DXN1
4, // DXN2UNORM
4, // DXN2SNORM
4, // BC7U
4, // BC6H_UF16
4, // BC6H_SF16
4, // ASTC_2D_4X4
1, // G8R8U
1, // G8R8S
1, // BGRA8
1, // RGBA32F
1, // RG32F
1, // R32F
1, // R16F
1, // R16U
1, // R16S
1, // R16UI
1, // R16I
1, // RG16
1, // RG16F
1, // RG16UI
1, // RG16I
1, // RG16S
1, // RGB32F
1, // RGBA8_SRGB
1, // RG8U
1, // RG8S
1, // RG32UI
1, // R32UI
8, // ASTC_2D_8X8
8, // ASTC_2D_8X5
5, // ASTC_2D_5X4
1, // BGRA8_SRGB
4, // DXT1_SRGB
4, // DXT23_SRGB
4, // DXT45_SRGB
4, // BC7U_SRGB
4, // ASTC_2D_4X4_SRGB
8, // ASTC_2D_8X8_SRGB
8, // ASTC_2D_8X5_SRGB
5, // ASTC_2D_5X4_SRGB
5, // ASTC_2D_5X5
5, // ASTC_2D_5X5_SRGB
10, // ASTC_2D_10X8
10, // ASTC_2D_10X8_SRGB
1, // Z32F
1, // Z16
1, // Z24S8
1, // S8Z24
1, // Z32FS8
}};
ASSERT(static_cast<std::size_t>(format) < block_width_table.size());
return block_width_table[static_cast<std::size_t>(format)];
@@ -341,6 +347,8 @@ static constexpr u32 GetDefaultBlockHeight(PixelFormat format) {
4, // ASTC_2D_5X4_SRGB
5, // ASTC_2D_5X5
5, // ASTC_2D_5X5_SRGB
8, // ASTC_2D_10X8
8, // ASTC_2D_10X8_SRGB
1, // Z32F
1, // Z16
1, // Z24S8
@@ -416,6 +424,8 @@ static constexpr u32 GetFormatBpp(PixelFormat format) {
128, // ASTC_2D_5X4_SRGB
128, // ASTC_2D_5X5
128, // ASTC_2D_5X5_SRGB
128, // ASTC_2D_10X8
128, // ASTC_2D_10X8_SRGB
32, // Z32F
16, // Z16
32, // Z24S8

View File

@@ -37,25 +37,28 @@ struct alignas(64) SwizzleTable {
std::array<std::array<u16, M>, N> values{};
};
constexpr auto legacy_swizzle_table = SwizzleTable<8, 64, 1>();
constexpr auto fast_swizzle_table = SwizzleTable<8, 4, 16>();
constexpr u32 gob_size_x = 64;
constexpr u32 gob_size_y = 8;
constexpr u32 gob_size_z = 1;
constexpr u32 gob_size = gob_size_x * gob_size_y * gob_size_z;
constexpr u32 fast_swizzle_align = 16;
constexpr auto legacy_swizzle_table = SwizzleTable<gob_size_y, gob_size_x, gob_size_z>();
constexpr auto fast_swizzle_table = SwizzleTable<gob_size_y, 4, fast_swizzle_align>();
/**
* This function manages ALL the GOBs(Group of Bytes) Inside a single block.
* Instead of going gob by gob, we map the coordinates inside a block and manage from
* those. Block_Width is assumed to be 1.
*/
void PreciseProcessBlock(u8* swizzled_data, u8* unswizzled_data, const bool unswizzle,
void PreciseProcessBlock(u8* const swizzled_data, u8* const unswizzled_data, const bool unswizzle,
const u32 x_start, const u32 y_start, const u32 z_start, const u32 x_end,
const u32 y_end, const u32 z_end, const u32 tile_offset,
const u32 xy_block_size, const u32 layer_z, const u32 stride_x,
const u32 bytes_per_pixel, const u32 out_bytes_per_pixel) {
std::array<u8*, 2> data_ptrs;
u32 z_address = tile_offset;
const u32 gob_size_x = 64;
const u32 gob_size_y = 8;
const u32 gob_size_z = 1;
const u32 gob_size = gob_size_x * gob_size_y * gob_size_z;
for (u32 z = z_start; z < z_end; z++) {
u32 y_address = z_address;
u32 pixel_base = layer_z * z + y_start * stride_x;
@@ -81,7 +84,7 @@ void PreciseProcessBlock(u8* swizzled_data, u8* unswizzled_data, const bool unsw
* Instead of going gob by gob, we map the coordinates inside a block and manage from
* those. Block_Width is assumed to be 1.
*/
void FastProcessBlock(u8* swizzled_data, u8* unswizzled_data, const bool unswizzle,
void FastProcessBlock(u8* const swizzled_data, u8* const unswizzled_data, const bool unswizzle,
const u32 x_start, const u32 y_start, const u32 z_start, const u32 x_end,
const u32 y_end, const u32 z_end, const u32 tile_offset,
const u32 xy_block_size, const u32 layer_z, const u32 stride_x,
@@ -90,23 +93,19 @@ void FastProcessBlock(u8* swizzled_data, u8* unswizzled_data, const bool unswizz
u32 z_address = tile_offset;
const u32 x_startb = x_start * bytes_per_pixel;
const u32 x_endb = x_end * bytes_per_pixel;
const u32 copy_size = 16;
const u32 gob_size_x = 64;
const u32 gob_size_y = 8;
const u32 gob_size_z = 1;
const u32 gob_size = gob_size_x * gob_size_y * gob_size_z;
for (u32 z = z_start; z < z_end; z++) {
u32 y_address = z_address;
u32 pixel_base = layer_z * z + y_start * stride_x;
for (u32 y = y_start; y < y_end; y++) {
const auto& table = fast_swizzle_table[y % gob_size_y];
for (u32 xb = x_startb; xb < x_endb; xb += copy_size) {
const u32 swizzle_offset{y_address + table[(xb / copy_size) % 4]};
for (u32 xb = x_startb; xb < x_endb; xb += fast_swizzle_align) {
const u32 swizzle_offset{y_address + table[(xb / fast_swizzle_align) % 4]};
const u32 out_x = xb * out_bytes_per_pixel / bytes_per_pixel;
const u32 pixel_index{out_x + pixel_base};
data_ptrs[unswizzle] = swizzled_data + swizzle_offset;
data_ptrs[!unswizzle] = unswizzled_data + pixel_index;
std::memcpy(data_ptrs[0], data_ptrs[1], copy_size);
std::memcpy(data_ptrs[0], data_ptrs[1], fast_swizzle_align);
}
pixel_base += stride_x;
if ((y + 1) % gob_size_y == 0)
@@ -126,23 +125,21 @@ void FastProcessBlock(u8* swizzled_data, u8* unswizzled_data, const bool unswizz
* https://envytools.readthedocs.io/en/latest/hw/memory/g80-surface.html#blocklinear-surfaces
*/
template <bool fast>
void SwizzledData(u8* swizzled_data, u8* unswizzled_data, const bool unswizzle, const u32 width,
const u32 height, const u32 depth, const u32 bytes_per_pixel,
void SwizzledData(u8* const swizzled_data, u8* const unswizzled_data, const bool unswizzle,
const u32 width, const u32 height, const u32 depth, const u32 bytes_per_pixel,
const u32 out_bytes_per_pixel, const u32 block_height, const u32 block_depth) {
auto div_ceil = [](const u32 x, const u32 y) { return ((x + y - 1) / y); };
const u32 stride_x = width * out_bytes_per_pixel;
const u32 layer_z = height * stride_x;
const u32 gob_x_bytes = 64;
const u32 gob_elements_x = gob_x_bytes / bytes_per_pixel;
const u32 gob_elements_y = 8;
const u32 gob_elements_z = 1;
const u32 gob_elements_x = gob_size_x / bytes_per_pixel;
constexpr u32 gob_elements_y = gob_size_y;
constexpr u32 gob_elements_z = gob_size_z;
const u32 block_x_elements = gob_elements_x;
const u32 block_y_elements = gob_elements_y * block_height;
const u32 block_z_elements = gob_elements_z * block_depth;
const u32 blocks_on_x = div_ceil(width, block_x_elements);
const u32 blocks_on_y = div_ceil(height, block_y_elements);
const u32 blocks_on_z = div_ceil(depth, block_z_elements);
const u32 gob_size = gob_x_bytes * gob_elements_y * gob_elements_z;
const u32 xy_block_size = gob_size * block_height;
const u32 block_size = xy_block_size * block_depth;
u32 tile_offset = 0;
@@ -171,9 +168,9 @@ void SwizzledData(u8* swizzled_data, u8* unswizzled_data, const bool unswizzle,
}
void CopySwizzledData(u32 width, u32 height, u32 depth, u32 bytes_per_pixel,
u32 out_bytes_per_pixel, u8* swizzled_data, u8* unswizzled_data,
u32 out_bytes_per_pixel, u8* const swizzled_data, u8* const unswizzled_data,
bool unswizzle, u32 block_height, u32 block_depth) {
if (bytes_per_pixel % 3 != 0 && (width * bytes_per_pixel) % 16 == 0) {
if (bytes_per_pixel % 3 != 0 && (width * bytes_per_pixel) % fast_swizzle_align == 0) {
SwizzledData<true>(swizzled_data, unswizzled_data, unswizzle, width, height, depth,
bytes_per_pixel, out_bytes_per_pixel, block_height, block_depth);
} else {
@@ -202,6 +199,8 @@ u32 BytesPerPixel(TextureFormat format) {
case TextureFormat::ASTC_2D_5X4:
case TextureFormat::ASTC_2D_8X8:
case TextureFormat::ASTC_2D_8X5:
case TextureFormat::ASTC_2D_10X8:
case TextureFormat::ASTC_2D_5X5:
case TextureFormat::A8R8G8B8:
case TextureFormat::A2B10G10R10:
case TextureFormat::BF10GF11RF11:
@@ -227,29 +226,38 @@ u32 BytesPerPixel(TextureFormat format) {
}
}
void UnswizzleTexture(u8* const unswizzled_data, VAddr address, u32 tile_size_x, u32 tile_size_y,
u32 bytes_per_pixel, u32 width, u32 height, u32 depth, u32 block_height,
u32 block_depth) {
CopySwizzledData((width + tile_size_x - 1) / tile_size_x,
(height + tile_size_y - 1) / tile_size_y, depth, bytes_per_pixel,
bytes_per_pixel, Memory::GetPointer(address), unswizzled_data, true,
block_height, block_depth);
}
std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size_x, u32 tile_size_y,
u32 bytes_per_pixel, u32 width, u32 height, u32 depth,
u32 block_height, u32 block_depth) {
std::vector<u8> unswizzled_data(width * height * depth * bytes_per_pixel);
CopySwizzledData((width + tile_size_x - 1) / tile_size_x,
(height + tile_size_y - 1) / tile_size_y, depth, bytes_per_pixel,
bytes_per_pixel, Memory::GetPointer(address), unswizzled_data.data(), true,
block_height, block_depth);
UnswizzleTexture(unswizzled_data.data(), address, tile_size_x, tile_size_y, bytes_per_pixel,
width, height, depth, block_height, block_depth);
return unswizzled_data;
}
void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32 swizzled_width,
u32 bytes_per_pixel, VAddr swizzled_data, VAddr unswizzled_data,
u32 block_height) {
const u32 image_width_in_gobs{(swizzled_width * bytes_per_pixel + 63) / 64};
const u32 image_width_in_gobs{(swizzled_width * bytes_per_pixel + (gob_size_x - 1)) /
gob_size_x};
for (u32 line = 0; line < subrect_height; ++line) {
const u32 gob_address_y =
(line / (8 * block_height)) * 512 * block_height * image_width_in_gobs +
(line % (8 * block_height) / 8) * 512;
const auto& table = legacy_swizzle_table[line % 8];
(line / (gob_size_y * block_height)) * gob_size * block_height * image_width_in_gobs +
((line % (gob_size_y * block_height)) / gob_size_y) * gob_size;
const auto& table = legacy_swizzle_table[line % gob_size_y];
for (u32 x = 0; x < subrect_width; ++x) {
const u32 gob_address = gob_address_y + (x * bytes_per_pixel / 64) * 512 * block_height;
const u32 swizzled_offset = gob_address + table[(x * bytes_per_pixel) % 64];
const u32 gob_address =
gob_address_y + (x * bytes_per_pixel / gob_size_x) * gob_size * block_height;
const u32 swizzled_offset = gob_address + table[(x * bytes_per_pixel) % gob_size_x];
const VAddr source_line = unswizzled_data + line * source_pitch + x * bytes_per_pixel;
const VAddr dest_addr = swizzled_data + swizzled_offset;
@@ -263,13 +271,13 @@ void UnswizzleSubrect(u32 subrect_width, u32 subrect_height, u32 dest_pitch, u32
u32 block_height, u32 offset_x, u32 offset_y) {
for (u32 line = 0; line < subrect_height; ++line) {
const u32 y2 = line + offset_y;
const u32 gob_address_y =
(y2 / (8 * block_height)) * 512 * block_height + (y2 % (8 * block_height) / 8) * 512;
const auto& table = legacy_swizzle_table[y2 % 8];
const u32 gob_address_y = (y2 / (gob_size_y * block_height)) * gob_size * block_height +
((y2 % (gob_size_y * block_height)) / gob_size_y) * gob_size;
const auto& table = legacy_swizzle_table[y2 % gob_size_y];
for (u32 x = 0; x < subrect_width; ++x) {
const u32 x2 = (x + offset_x) * bytes_per_pixel;
const u32 gob_address = gob_address_y + (x2 / 64) * 512 * block_height;
const u32 swizzled_offset = gob_address + table[x2 % 64];
const u32 gob_address = gob_address_y + (x2 / gob_size_x) * gob_size * block_height;
const u32 swizzled_offset = gob_address + table[x2 % gob_size_x];
const VAddr dest_line = unswizzled_data + line * dest_pitch + x * bytes_per_pixel;
const VAddr source_addr = swizzled_data + swizzled_offset;
@@ -294,6 +302,8 @@ std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat
case TextureFormat::BC6H_SF16:
case TextureFormat::ASTC_2D_4X4:
case TextureFormat::ASTC_2D_8X8:
case TextureFormat::ASTC_2D_5X5:
case TextureFormat::ASTC_2D_10X8:
case TextureFormat::A8R8G8B8:
case TextureFormat::A2B10G10R10:
case TextureFormat::A1B5G5R5:
@@ -321,12 +331,9 @@ std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat
std::size_t CalculateSize(bool tiled, u32 bytes_per_pixel, u32 width, u32 height, u32 depth,
u32 block_height, u32 block_depth) {
if (tiled) {
const u32 gobs_in_x = 64;
const u32 gobs_in_y = 8;
const u32 gobs_in_z = 1;
const u32 aligned_width = Common::AlignUp(width * bytes_per_pixel, gobs_in_x);
const u32 aligned_height = Common::AlignUp(height, gobs_in_y * block_height);
const u32 aligned_depth = Common::AlignUp(depth, gobs_in_z * block_depth);
const u32 aligned_width = Common::AlignUp(width * bytes_per_pixel, gob_size_x);
const u32 aligned_height = Common::AlignUp(height, gob_size_y * block_height);
const u32 aligned_depth = Common::AlignUp(depth, gob_size_z * block_depth);
return aligned_width * aligned_height * aligned_depth;
} else {
return width * height * depth * bytes_per_pixel;

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