Compare commits

...

193 Commits

Author SHA1 Message Date
ReinUsesLisp
9ebc27234d bootmanager: Bypass input focus issues 2019-03-25 17:10:34 -03:00
ReinUsesLisp
bbb396d7f1 bootmanager: Bypass resizing issue 2019-03-25 17:10:34 -03:00
ReinUsesLisp
9ff72ca9f2 bootmanager: Delete container to avoid crash on game restarting
While we are at it, remove nullptr checks for deletion, since the C++
standard defines that delete does it by its own
2019-03-25 17:10:34 -03:00
James Rowe
5f2d9f282a QT: Hide GLWidget immediately after showing.
With the loading screen merged, we don't want to actually show at this
point, but it still needs to be shown to actually create the context.
Turns out you can just show and hide it immediately and it'll work.
2019-01-21 16:21:44 -07:00
James Rowe
f2a2f818b6 SDL Frontend: Add shared context support 2019-01-21 16:00:01 -07:00
James Rowe
c6a0ab9792 QT Frontend: Migrate to QOpenGLWindow 2019-01-21 16:00:01 -07:00
bunnei
125599c2d5 Merge pull request #2038 from jroweboy/loading-progress-bar
Loading progress bar upgrades
2019-01-21 14:12:47 -05:00
James Rowe
3049ea45d3 Change const char* to const char[] 2019-01-21 10:28:32 -07:00
James Rowe
372245e0b5 Fix mingw compile error and warnings 2019-01-21 09:39:45 -07:00
James Rowe
3ca0af8bb3 Add fade out effect to the loading screen 2019-01-21 09:20:16 -07:00
James Rowe
3740adb6f5 Set Minimum Size to the same as renderwindow 2019-01-21 08:51:37 -07:00
James Rowe
aa427bb2a7 Remove blue box around loading screen 2019-01-21 08:50:23 -07:00
James Rowe
636cc2a496 Change the background color of Stage Complete to yuzu blue 2019-01-20 19:14:14 -07:00
James Rowe
ea73ffe202 Rename step 1 and step 2 to be a little more descriptive 2019-01-20 18:40:25 -07:00
James Rowe
56541b1ae5 Prevent estimated time from flashing after slow shader compilation starts 2019-01-20 18:31:35 -07:00
James Rowe
4bce57b149 Move progress bar style into constexpr strings 2019-01-20 18:20:21 -07:00
James Rowe
63783db1b3 Hide progress bar on Prepare step 2019-01-20 15:09:14 -07:00
James Rowe
e8bd6b1fcc QT: Upgrade the Loading Bar to look much better 2019-01-20 14:47:35 -07:00
bunnei
1c733bf175 Merge pull request #2034 from jroweboy/loading-widget
QT Frontend: Add a Loading screen with progressbar
2019-01-20 15:45:07 -05:00
bunnei
197d0d9d24 Merge pull request #2008 from ReinUsesLisp/dirty-framebuffers
gl_rasterizer_cache: Use dirty flags for framebuffers
2019-01-20 14:06:26 -05:00
bunnei
cbf8bea9d5 Merge pull request #2002 from ReinUsesLisp/dsa-vao-buffer
gl_rasterizer: Use DSA for VAOs and buffers
2019-01-20 14:06:01 -05:00
bunnei
eff61c5c42 Merge pull request #2032 from lioncash/web
yuzu/configuration/configure_web: Amend verification string
2019-01-20 13:26:47 -05:00
bunnei
f9e69faf4a Merge pull request #2025 from DarkLordZach/loader-banner-logo
loader: Add getters for application banner and logo
2019-01-20 13:26:27 -05:00
James Rowe
69da267540 Add a workaround if QMovie isn't available 2019-01-19 23:34:03 -07:00
James Rowe
08fcf41b0a QT Frontend: Add a Loading screen with progressbar
With shader caches on the horizon, one requirement is to provide visible
feedback for the progress. The shader cache reportedly takes several
minutes to load for large caches that were invalidated, and as such we
should provide a loading screen with progress.

Adds a loading screen widget that will be shown until the first frame of
the game is swapped. This was chosen in case shader caches are not being
used, several games still take more than a few seconds to launch and
could benefit from a loading screen.
2019-01-19 23:34:03 -07:00
bunnei
83f8d1aa2e Merge pull request #2031 from lioncash/priv
yuzu/web_browser: Minor cleanup
2019-01-19 12:57:09 -05:00
bunnei
966405d64b Merge pull request #2033 from ReinUsesLisp/fixup-clip-warning
gl_rasterizer: Silent unsafe mix warning
2019-01-19 12:56:40 -05:00
bunnei
d0e4e43e3c Merge pull request #2036 from lioncash/unused-class
file_sys/directory: Remove unused DirectoryBackend class
2019-01-19 12:56:16 -05:00
Lioncash
96644385ca file_sys/directory: Remove unused DirectoryBackend class
This isn't used at all, so we can just get rid of it.
2019-01-18 14:33:50 -05:00
ReinUsesLisp
a1b1ea47ed gl_rasterizer: Silent unsafe mix warning 2019-01-18 03:25:28 -03:00
Lioncash
549164d425 yuzu/configuration/configure_web: Remove an unused lambda capture
'this' isn't actually used within the lambda, since what we need
from the class is already assigned within the capture section of
the lambda.
2019-01-17 11:39:49 -05:00
Lioncash
b8b87ec01f yuzu/configuration/configure_web: Use an ellipsis with 'Verifying' text
It's a common UI pattern to use an ellipsis to indicate an ongoing
action, rather than just specifying the word by itself.
2019-01-17 11:35:59 -05:00
Lioncash
5961928543 core/frontend/applets/web_browser: Include missing headers
Gets rid of a few indirect inclusions.
2019-01-17 11:25:37 -05:00
Lioncash
a661025637 core/frontend/applets/web_browser: Make OpenPage() non-const
This is a function that definitely doesn't always have a non-modifying
behavior across all implementations, so this should be made non-const.

This gets rid of the need to mark data members as mutable to work around
the fact mutating data members needs to occur.
2019-01-17 11:19:52 -05:00
Lioncash
66978a772d yuzu/web_browser: std::move std::function instances in OpenPage()
Avoids the need to potentially reallocate the contained callbacks.
2019-01-17 11:10:35 -05:00
Lioncash
e4fa77ef6a yuzu/web_browser: Make slot functions private
These currently aren't used by anything other than the QtWebBrowser
class itself, and can be made private.
2019-01-17 11:08:05 -05:00
Zach Hilman
b273b19576 loader: Propagate NCA logo section to ReadBanner and ReadLogo 2019-01-15 16:01:04 -05:00
Zach Hilman
318bf7c8e3 content_archive: Add getter for logo section of NCA 2019-01-15 16:00:29 -05:00
Hexagon12
7c6bb8c17f Merge pull request #2020 from otaviopace/remove-spaces
audio_core: remove unnecessary spaces on comments
2019-01-14 16:49:37 +02:00
Otávio Pace
215ca770df audio_core: remove unnecessary spaces on comments 2019-01-14 00:22:05 -02:00
bunnei
bc879ae880 Merge pull request #1848 from FreddyFunk/QJsonArray
game_list: Remove a reference of a reference
2019-01-12 02:01:13 -05:00
bunnei
c9ef8b0af1 Merge pull request #1959 from DarkLordZach/custom-rtc
settings: Add support for setting the RTC manually
2019-01-10 17:05:21 -05:00
bunnei
83e8ad2331 Merge pull request #1939 from DarkLordZach/web-applet
applets: Implement HLE web browser applet (LibAppletOff)
2019-01-10 17:04:38 -05:00
ReinUsesLisp
877a978a22 gl_rasterizer: Workaround Intel VAO DSA bug
There is a bug on Intel's blob driver where it fails to properly build a
vertex array object if it's not bound even after creating it with
glCreateVertexArrays. This workaround binds it after creating it to
bypass the issue.
2019-01-09 02:40:19 -03:00
bunnei
912f2a520a Merge pull request #2010 from ReinUsesLisp/gmem
gl_global_cache: Add dummy global cache manager
2019-01-08 16:44:53 -05:00
ReinUsesLisp
3121408a90 gl_global_cache: Add dummy global cache manager 2019-01-08 17:47:45 -03:00
Zach Hilman
ac7d8983eb settings: Fix comment structure 2019-01-07 19:40:28 -05:00
Zach Hilman
05dbb47af5 settings: Use std::chrono::seconds instead of s64 for RTC 2019-01-07 19:19:40 -05:00
Zach Hilman
dbb1eb9c29 time: Use custom RTC settings if applicable for game 2019-01-07 19:19:40 -05:00
Zach Hilman
21f1b2889d core: Set custom RTC differential on game boot 2019-01-07 19:19:40 -05:00
Zach Hilman
26c9f12271 qt: Provide UI to edit custom RTC settings 2019-01-07 19:19:40 -05:00
Zach Hilman
c6016856d8 settings: Add custom RTC settings
Stored as signed seconds since epoch.
2019-01-07 19:18:45 -05:00
ReinUsesLisp
19cf995225 gl_rasterizer: Skip framebuffer configuration if rendertargets have not been changed 2019-01-07 16:23:23 -03:00
bunnei
23ebd4920e Merge pull request #1999 from ReinUsesLisp/dirty-shader
gl_shader_cache: Use dirty flags for shaders
2019-01-07 14:22:30 -05:00
ReinUsesLisp
b683e41fca gl_rasterizer_cache: Use dirty flags for the depth buffer 2019-01-07 16:22:28 -03:00
ReinUsesLisp
179ee963db gl_rasterizer_cache: Use dirty flags for color buffers 2019-01-07 16:20:39 -03:00
ReinUsesLisp
0ab17ab406 gl_shader_cache: Use dirty flags for shaders 2019-01-07 16:13:12 -03:00
bunnei
17a68e5ebe Merge pull request #1989 from lioncash/set
service/vi: Unstub IApplicationDisplayService's SetLayerScalingMode
2019-01-07 12:59:33 -05:00
bunnei
e04d75f44c Merge pull request #1992 from DarkLordZach/move-profile-manager-ui
qt: Move profile manager to own UI tab
2019-01-07 11:41:37 -05:00
bunnei
37ac1bb576 Merge pull request #1990 from ReinUsesLisp/copy-surface-stream-copy
gl_rasterizer_cache: Use GL_STREAM_COPY for PBOs
2019-01-07 11:34:05 -05:00
ReinUsesLisp
5933b3ea96 gl_stream_buffer: Use DSA for buffer management 2019-01-06 16:49:24 -03:00
ReinUsesLisp
35c095898b gl_rasterizer: Use DSA for vertex array objects 2019-01-06 16:49:24 -03:00
ReinUsesLisp
ea4928393f gl_state: Drop uniform buffer state tracking 2019-01-06 00:28:01 -03:00
Zach Hilman
2378ecd0e8 build: Copy web engine resources to correct location 2019-01-05 15:27:15 -05:00
ReinUsesLisp
fc8a8789da gl_rasterizer_cache: Use GL_STREAM_COPY for PBOs
Since the data is doing the path CPU -> GPU -> GPU copy is the most
approximate hint. Using GL_STREAM_DRAW generated a performance warning
on Nvidia's stack. Changing this hint removed the warning.
2019-01-05 02:27:55 -03:00
bunnei
59c665b28e Merge pull request #1988 from lioncash/res
service/vi: Correct reported dimensions from IApplicationDisplayService's GetDisplayResolution()
2019-01-05 00:26:29 -05:00
Lioncash
9e8737b535 service/vi: Correct scaling mode conversions
These values are not equivalent, based off RE. The internal value is put
into a lookup table with the following values:

[3, 0, 1, 2, 4]

So the values absolutely do not map 1:1 like the comment was indicating.
2019-01-04 21:45:18 -05:00
Lioncash
56e51da1d9 service/vi: Factor out scaling mode conversions from the IPC function itself
Avoids entangling the IPC buffer appending with the actual operation of
converting the scaling values over. This also inserts the proper error
handling for invalid scaling values.
2019-01-04 21:45:18 -05:00
Lioncash
40aa1ea9f9 service/vi: Unstub IApplicationDisplayService' SetLayerScalingMode()
This appears to only check if the scaling mode can actually be
handled, rather than actually setting the scaling mode for the layer.

This implements the same error handling performed on the passed in
values.
2019-01-04 21:45:14 -05:00
bunnei
a01832b2e0 Merge pull request #1981 from ogniK5377/open-app-area-create
Return no application area when games try to open an application area
2019-01-04 21:42:23 -05:00
bunnei
1c7696e0d9 Merge pull request #1980 from ogniK5377/applet-msg-update
Proper no message handling for AM::PopMessage
2019-01-04 21:42:12 -05:00
Lioncash
bf036b46fc service/vi: Correct reported dimensions from IApplicationDisplayService's GetDisplayResolution()
Within the actual service, it makes no distinguishing between docked and
undocked modes. This will always return the constants values reporting
1280x720 as the dimensions.
2019-01-04 19:13:32 -05:00
Zach Hilman
54d7b664da qt: Move profile manager to own UI tab 2019-01-04 17:32:13 -05:00
Zach Hilman
0f887daa72 build: Copy QtWebEngineProcess[d].exe to release dir on windows 2019-01-04 10:34:29 -05:00
Mat M
c5262b1890 Merge pull request #1984 from ogniK5377/remove-pulse
Removed pulse event type
2019-01-04 09:18:41 -05:00
David Marcec
4f41fd84ff Removed pulse event type
Pulse is considered a hack and nothing should be using it. We should completely remove it
2019-01-04 20:47:20 +11:00
bunnei
de4f931d13 Merge pull request #1975 from lioncash/vi
service/vi: Minor updates and corrections to the DisplayInfo struct
2019-01-03 22:45:27 -05:00
bunnei
548fe0fc62 Merge pull request #1979 from ogniK5377/30-fps
Fixed botw deadlock due to incorrect event signal type
2019-01-03 22:42:22 -05:00
David Marcec
48a526dba1 Return no application area when games try to open an application area
This will prompt CreateApplicationArea
2019-01-04 13:22:25 +11:00
David Marcec
f2536cafe5 Proper no message handling for AM::PopMessage
When we have no messages, we should be returning an error code.
2019-01-04 13:11:17 +11:00
Hexagon12
9ec9709f68 Merge pull request #1724 from FearlessTobi/port-4412
Port citra-emu/citra#4412: "CONTRIBUTING.md: migrate to the wiki"
2019-01-03 18:12:33 +02:00
David Marcec
5af0340066 Fixed botw deadlock(and possibly 30 fps games rendering too fast? needs testing to confirm)
Upon investigating the issue with #1878, I found that games are the ones who handle the vsync event resetting and not us.
2019-01-03 20:32:47 +11:00
bunnei
2c0f831468 Merge pull request #1976 from lioncash/display
service/vi: Implement OpenDefaultDisplay in terms of OpenDisplay
2019-01-02 21:51:20 -05:00
bunnei
56a97647a3 Merge pull request #1978 from lioncash/enabled
service/vi: Implement SetDisplayEnabled()
2019-01-02 21:34:12 -05:00
bunnei
a6aa710b84 Merge pull request #1942 from DarkLordZach/profile-select-game-boot
qt: Add setting to prompt for user on game boot
2019-01-02 21:05:27 -05:00
bunnei
11f990975c Merge pull request #1941 from DarkLordZach/profile-select-save-data
qt: Use ProfileSelectionDialog when selecting user for save data
2019-01-02 21:05:02 -05:00
Lioncash
ceeff8ebfd service/vi: Implement SetDisplayEnabled()
This IPC command is simply a stub inside the actual service itself, and
just returns a successful error code regardless of input. This is likely
only retained in the service interface to not break older code that relied
upon it succeeding in some way.
2019-01-02 20:48:04 -05:00
bunnei
7538fa4a67 Merge pull request #1977 from lioncash/vi-log
service/vi: Log more information where applicable
2019-01-02 20:36:40 -05:00
Lioncash
5bfd810ab5 service/vi: Log more information where applicable
In many cases, we didn't bother to log out any of the popped data
members. This logs them out to the console within the logging call to
provide more contextual information.
2019-01-02 19:42:10 -05:00
Lioncash
dea1db0414 service/vi: Implement OpenDefaultDisplay in terms of OpenDisplay
Internally within the vi services, this is essentially all that
OpenDefaultDisplay does, so it's trivial to just do the same, and
forward the default display string into the function.
2019-01-02 18:16:36 -05:00
bunnei
c91d2bac45 Merge pull request #1961 from ReinUsesLisp/tex-view-2d
gl_rasterizer_cache: Texture view if shader samples array but OGL is not
2019-01-02 17:51:32 -05:00
Lioncash
351f010cfc service/vi: Correct initial width and height values
Based off RE, it appears that almost all display types seem to use
1920x1080 except for a few (null display, edid display).
2019-01-02 17:36:03 -05:00
Lioncash
fe3bf4f075 service/vi: Document unknown DisplayInfo struct members
It appears that the two members indicate whether a display has a bounded
number of layers (and if set, the second member indicates the total
number of layers).
2019-01-02 17:35:58 -05:00
bunnei
6a0e258dde Merge pull request #1944 from FearlessTobi/port-4187
Port citra-emu/citra#4187: "Qt/Configure: Use sidebar to divide tabs into smaller groups"
2019-01-02 17:24:38 -05:00
bunnei
dd0b17a5f0 Merge pull request #1969 from lioncash/cast
yuzu: Fix truncation warnings within UI code
2019-01-02 17:20:52 -05:00
bunnei
c8c8412901 Merge pull request #1970 from lioncash/header
core/kernel: Remove unnecessary inclusions
2019-01-02 14:24:00 -05:00
Lioncash
cd8924520c yuzu/configure_general: Silence truncation warnings in loadConfiguration()
The QPixmap API expects an unsigned int.
2018-12-31 19:14:48 -05:00
Lioncash
f5f35caf20 yuzu/config: Silence truncation warnings 2018-12-31 19:11:29 -05:00
Lioncash
1f0c8bfeda core/kernel: Remove unnecessary inclusions
Gets rid of a few unnecessary header dependencies in some source files.
2018-12-31 19:04:16 -05:00
Zach Hilman
15501477e7 Update Qt MSVC external to 5.12.0 2018-12-31 13:19:23 -05:00
bunnei
f96bb2520b Merge pull request #1966 from lioncash/backtrace
arm_interface: Minor cleanup
2018-12-31 11:11:00 -05:00
bunnei
09ffa0cb21 Merge pull request #1967 from lioncash/thread
kernel/svc: Sanitize core and priority masks within CreateThread
2018-12-31 11:10:05 -05:00
bunnei
244b46af5a Merge pull request #1965 from lioncash/fmt
externals: Update fmt to 5.3.0
2018-12-31 11:09:37 -05:00
Lioncash
b4242633ad kernel/svc: Correct misleading error message within CreateThread()
This is a bounds check to ensure that the thread priority is within the
valid range of 0-64. If it exceeds 64, that doesn't necessarily mean
that an actual priority of 64 was expected (it actually means whoever
called the function screwed up their math).

Instead clarify the message to indicate the allowed range of thread
priorities.
2018-12-30 21:29:38 -05:00
Lioncash
3a8d38be7e kernel/svc: Sanitize core number and thread priorities in CreateThread()
Now that we handle the kernel capability descriptors we can correct
CreateThread to properly check against the core and priority masks
like the actual kernel does.
2018-12-30 21:23:56 -05:00
Lioncash
8769604144 kernel/process: Rename GetAllowedProcessorMask() and GetAllowedThreadPriorityMask()
Makes them consistent with their kernel capability counterparts.
2018-12-30 21:09:46 -05:00
Lioncash
205e6d3b97 kernel/svc: Simplify thread core ID sanitizing in CreateThread
Rather than use a switch here, this can be collapsed into a simple range
check, which is a little easier on the eyes.
2018-12-30 20:59:54 -05:00
Lioncash
039e58a984 arm_interface: Make include path relative for arm_interface.h
Makes it consistent with the rest of the includes.
2018-12-30 20:46:29 -05:00
Lioncash
a17dd30057 arm_interface: Make LogBacktrace() a const member function
This function doesn't modify instance state, so it can be made const.
2018-12-30 20:44:48 -05:00
Lioncash
776ce5d74c arm_interface: Mark variables as const where applicable in LogBacktrace()
Two of these variables have fixed values, so we can make that
immediately obvious from the get-go.
2018-12-30 20:43:17 -05:00
Lioncash
387bffda5e arm_interface: Remove unnecessary semicolon
Namespaces don't require the use of a semicolon. Silences a -Wextra-semi
warning.
2018-12-30 20:41:33 -05:00
Sebastian Valle
e5dfbe22ee Merge pull request #1956 from lioncash/process-thread
kernel/process: Start the main thread using the specified ideal core
2018-12-30 20:32:41 -05:00
bunnei
331c252509 Merge pull request #1847 from ogniK5377/backtrace-break
Print backtrace on svcBreak
2018-12-29 22:58:13 -05:00
bunnei
f80229b676 Merge pull request #1964 from lioncash/time
service/time: Minor cleanup
2018-12-29 22:57:38 -05:00
ReinUsesLisp
97fb6179b9 gl_rasterizer_cache: Texture view if shader samples array but OGL is not
When a shader samples a texture array but that texture in OpenGL is
created without layers, use a texture view to increase the texture
hierarchy. For example, instead of binding a GL_TEXTURE_2D bind a
GL_TEXTURE_2D_ARRAY view.
2018-12-29 23:49:12 -03:00
Lioncash
fa97f50bff service/time: Minor cleanup to GetClockSnapshot()
Moves some variables closer to their actual usage sites.
2018-12-29 21:42:13 -05:00
Lioncash
614de1dba7 externals: Update fmt to 5.3.0
Updates fmt from 5.2.1 to 5.3.0
2018-12-29 21:33:32 -05:00
Lioncash
af51f123fa service/time: Fill in some structures and remove padding where not necessary 2018-12-29 18:26:32 -05:00
David Marcec
22d4e10664 Moved log backtrace to arm_interface.cpp. Added printing of error code to fatal 2018-12-29 12:55:19 +11:00
bunnei
8495f1df2a Merge pull request #1955 from bunnei/g8r8-fix
gpu: Remove PixelFormat G8R8U and G8R8S, as they do not seem to exist.
2018-12-28 20:23:57 -05:00
Zach Hilman
0c5ede492f travis: Use correct package for linux Qt5WebEngine 2018-12-28 19:29:49 -05:00
Zach Hilman
cb930c4b5a web_browser: Add bounds checking to applet interface 2018-12-28 18:20:29 -05:00
bunnei
2020ba06e1 gpu: Remove PixelFormat G8R8U and G8R8S, as they do not seem to exist.
- Fixes UI rendering issues in The Legend of Zelda: Breath of the Wild.
2018-12-28 15:36:45 -05:00
bunnei
af9336df57 Merge pull request #1958 from lioncash/audio
audio_core: Convert LOG_CRITICAL + UNREACHABLE over to UNIMPLEMENTED/UNIMPLEMENTED_MSG
2018-12-28 15:32:41 -05:00
Zach Hilman
ef4c4e239d cmake: Add USE_QT_WEB_ENGINE flag and update build system 2018-12-28 15:32:39 -05:00
Zach Hilman
45da3be40e main: Add main window integrations for QtWebBrowserApplet 2018-12-28 15:32:39 -05:00
Zach Hilman
e00e1fc755 qt: Implement Qt frontend to web browser
Using a custom reimplementation of QWebEngineView and an injector script.
2018-12-28 15:32:39 -05:00
Zach Hilman
32bfa92c71 core: Add getter and setter for WebBrowserApplet frontend 2018-12-28 15:32:39 -05:00
Zach Hilman
85a3368e6d frontend: Add frontend responder for web browser 2018-12-28 15:32:39 -05:00
Zach Hilman
69f622be36 applets: Implement LibAppletOff (Web) applet 2018-12-28 15:32:39 -05:00
Zach Hilman
4c20a39828 loader: Add accessor for Manual RomFS 2018-12-28 15:32:39 -05:00
Zach Hilman
621b25b6be hid: Make Hid service accessible and add GetPressState 2018-12-28 15:32:03 -05:00
Zach Hilman
abbcc8e61e romfs: Add SingleDiscard extraction type
Needed for manual RomFS extraction, as Full generates an extra directory and Truncated generates variable results.
2018-12-28 15:30:36 -05:00
Zach Hilman
94db6e5f3f am: Add size parameter to am:IStorage logging 2018-12-28 15:30:36 -05:00
Lioncash
a73c7c73eb audio_core: Convert LOG_CRITICAL + UNREACHABLE over to UNIMPLEMENTED/UNIMPLEMENTED_MSG
These two macros being used in tandem were used prior to the
introduction of UNIMPLEMENTED and UNIMPLEMENTED_MSG. This provides
equivalent behavior, just with less typing/reading involved.
2018-12-28 14:13:58 -05:00
Lioncash
a81ff6f54c kernel/process: Start the main thread using the specified ideal core
This matches kernel behavior in that processes are started using their
specified ideal core, rather than always starting on core 0.
2018-12-27 21:50:16 -05:00
Lioncash
f80bc712ea kernel: Rename 'default' CPU core to 'ideal' core
This makes the naming more closely match its meaning. It's just a
preferred core, not a required default core. This also makes the usages
of this term consistent across the thread and process implementations.
2018-12-27 21:48:49 -05:00
Lioncash
771431f625 kernel/thread: Move process thread initialization into process.cpp
This function isn't a general purpose function that should be exposed to
everything, given it's specific to initializing the main thread for a
Process instance.

Given that, it's a tad bit more sensible to place this within
process.cpp, which keeps it visible only to the code that actually needs
it.
2018-12-27 20:32:30 -05:00
bunnei
19bfbb28ce Merge pull request #1954 from lioncash/npdm
file_sys/program_metadata: Print out more descriptive address space descriptions
2018-12-27 19:58:36 -05:00
bunnei
357bc956ae Merge pull request #1953 from lioncash/mem
kernel/process: Remove most allocation functions from Process' interface
2018-12-27 19:57:51 -05:00
Lioncash
9aa68212d9 file_sys/program_metadata: Print out more descriptive address space descriptions
Provides extra information that makes it easier to tell if an executable
being run is using a 36-bit address space or a 39-bit address space.
While we don't support AArch32 executables yet, this also puts in
distinguishing information for the 32-bit address space types as well.
2018-12-27 19:16:45 -05:00
spycrab
c6da772b24 Qt/Configure: Use sidebar to divide tabs into smaller groups 2018-12-28 01:14:57 +01:00
Lioncash
fbeaa330a3 kernel/process: Remove most allocation functions from Process' interface
In all cases that these functions are needed, the VMManager can just be
retrieved and used instead of providing the same functions in Process'
interface.

This also makes it a little nicer dependency-wise, since it gets rid of
cases where the VMManager interface was being used, and then switched
over to using the interface for a Process instance. Instead, it makes
all accesses uniform and uses the VMManager instance for all necessary
tasks.

All the basic memory mapping functions did was forward to the Process'
VMManager instance anyways.
2018-12-27 19:08:47 -05:00
bunnei
e84e4fd3f8 Merge pull request #1951 from Tinob/master
Add missing uintBitsToFloat to SetRegisterToHalfFloat
2018-12-27 13:55:21 -05:00
Rodolfo Bogado
fbe900ba6d Add missing uintBitsToFloat to SetRegisterToHalfFloat 2018-12-27 14:39:10 -03:00
bunnei
795335af0f Merge pull request #1928 from lioncash/caps
kernel: Handle kernel capability descriptors
2018-12-27 11:15:34 -05:00
bunnei
fa9acc26d9 Merge pull request #1892 from Tinob/master
Improve Zero flag implementation
2018-12-27 11:06:59 -05:00
bunnei
8e23e40a72 Merge pull request #1929 from bunnei/fix-hid
hid: Fix SetNpadJoyHoldType and improve logging.
2018-12-27 11:04:56 -05:00
bunnei
2991c747c9 Merge pull request #1945 from bunnei/fix-hid-horiz
npad: Remove code to invert input in horizontal mode.
2018-12-27 11:04:40 -05:00
bunnei
7477c12fe2 Merge pull request #1949 from lioncash/unmap
kernel/vm_manager: Reset region attributes when unmapping a VMA
2018-12-27 11:04:26 -05:00
bunnei
c4515d305b Merge pull request #1879 from DarkLordZach/am-save-data-size
am: Implement GetSaveDataSize and ExtendSaveData using files
2018-12-27 11:04:05 -05:00
Zach Hilman
c643f364b4 am: Implement GetSaveDataSize and ExtendSaveData
These functions come in a pair and are needed by Smash Ultimate, Minecraft, and Skyrim, amongst others.
2018-12-27 00:18:00 -05:00
Zach Hilman
2e6b67a079 filesystem: Populate save data sizes from control data 2018-12-27 00:18:00 -05:00
Zach Hilman
4082c4eda6 savedata_factory: Partially implement IVFC save sizes using files
This stores a file in the save directory called '.yuzu_save_size' which stores the two save sizes (normal area and journaled area) sequentially as u64s.
2018-12-27 00:18:00 -05:00
Zach Hilman
0756f29a2c loader: Add accessor for game control data 2018-12-27 00:16:55 -05:00
Zach Hilman
417e1ef09c control_metadata: Update NACP fields with latest Switchbrew data 2018-12-27 00:16:55 -05:00
Zach Hilman
5c4259ec1a control_metadata: Use value member instead of unique_ptr to store struct
Serves no actual purpose in this instance besides making NACP's copy assignment deleted, which is not intended behavior.
2018-12-27 00:16:55 -05:00
Zach Hilman
4a6ba58073 vfs: Add reinterpret_casts to WriteArray and Object
Allows these functions to compile when T is not u8.
2018-12-27 00:16:55 -05:00
bunnei
73c9ffc422 Merge pull request #1946 from lioncash/decl
renderer_opengl: Correct forward declaration of FramebufferLayout
2018-12-26 20:39:54 -05:00
bunnei
17fa0ffff9 Merge pull request #1948 from lioncash/translatable
configure_per_general: Mark UI strings as translatable in the constructor
2018-12-26 20:39:30 -05:00
Lioncash
1392597ede kernel/vm_manager: Reset region attributes when unmapping a VMA
Like the other members related to memory regions, the attributes need to
be reset back to their defaults as well.
2018-12-26 20:15:29 -05:00
bunnei
c9269a4a4b Merge pull request #1947 from lioncash/init
configure_input_simple: Make input profile array constexpr
2018-12-26 18:10:56 -05:00
Lioncash
0c18d47348 configure_per_general: Mark UI strings as translatable in the constructor
These are user-facing strings, so they should be translatable.
2018-12-26 17:45:15 -05:00
Lioncash
faa9110541 configure_input_simple: Make input profile array constexpr
Calling tr() from a file-scope array isn't advisable, since it can be
executed before the Qt libraries are even fully initialized, which can
lead to crashes.

Instead, the translatable strings should be annotated, and the tr()
function should be called at the string's usage site.
2018-12-26 17:36:36 -05:00
Lioncash
67fa21e143 renderer_opengl: Correct forward declaration of FramebufferLayout
This is actually a struct, not a class, which can lead to compilation
warnings.
2018-12-26 17:32:32 -05:00
Rodolfo Bogado
33056dd833 Apply CC test to the final value to be stored in the register 2018-12-26 18:16:31 -03:00
David
8047873a66 Fixed shader linking error due to TLDS (#1934)
* Fixed shader linking error due to TLDS

coord should be coords

* Fix remaining coords
2018-12-26 15:55:39 -05:00
bunnei
ae582b6669 Merge pull request #1849 from encounter/svcSetThreadActivity
svc: Implement SetThreadActivity (thread suspension)
2018-12-26 15:54:14 -05:00
bunnei
46b8b03015 Merge pull request #1943 from ReinUsesLisp/fixup-texs
shader_bytecode: Fixup TEXS.F16 encoding
2018-12-26 15:49:41 -05:00
bunnei
7e622c5575 npad: Remove code to invert input in horizontal mode.
- This was incorrect, the game appears to handle this for us.
- Fixes horizontal mode with Puyo Puyo Tetris and Super Mario Odyssey.
2018-12-26 15:47:11 -05:00
Zach Hilman
9c2d83cb9c qt: Use ProfileSelectionDialog when selecting user for save data
This allows us to present a much nicer UI to the user over a simple combo box and is made easy with the modular nature of the profile-selection applet frontend.
2018-12-25 10:52:50 -05:00
Zach Hilman
4d61ac08aa qt: Add setting to prompt for user on game boot
Using the QtProfileSelectorDialog, this implementation is trivial. This mimics the real switch behavior of asking which user on every game boot, but it is default disabled as that might get inconvenient.
2018-12-25 10:42:14 -05:00
Rodolfo Bogado
bbf8d6bf01 Includde saturation in the evaluation of the control code 2018-12-22 19:19:18 -03:00
Rodolfo Bogado
946777601b Handle RZ cases evaluating the expression instead of the register value. 2018-12-22 19:19:18 -03:00
Rodolfo Bogado
7e72b5e453 complete emulation of ZeroFlag 2018-12-22 19:19:18 -03:00
bunnei
1120e0b4d2 hid: Fix SetNpadJoyHoldType and improve logging. 2018-12-21 13:37:39 -05:00
Lioncash
002ae08bbd kernel/process: Hook up the process capability parser to the process itself
While we're at it, we can also toss out the leftover capability parsing
from Citra.
2018-12-21 07:05:34 -05:00
Lioncash
d09fb82113 kernel/process_capability: Handle debug capability flags 2018-12-21 07:05:34 -05:00
Lioncash
10824c5d63 kernel/process_capability: Handle handle table capability flags
This just specifies the handle table size. There's also a section of
reserved bits that are checked against.
2018-12-21 07:05:34 -05:00
Lioncash
e0e84aede0 kernel/process_capability: Handle kernel version capability flags 2018-12-21 07:05:34 -05:00
Lioncash
010bc677f3 kernel/process_capability: Handle program capability flags 2018-12-21 07:05:34 -05:00
Lioncash
0f216d20e3 kernel/process_capability: Handle interrupt capability flags
Similar to the service capability flags, however, we currently don't
emulate the GIC, so this currently handles all interrupts as being valid
for the time being.
2018-12-21 07:05:34 -05:00
Lioncash
3dc59b74ec kernel/process_capability: Handle syscall capability flags 2018-12-21 07:05:34 -05:00
Lioncash
27caf71204 kernel/process_capability: Handle the priority mask and core mask flags
Handles the priority mask and core mask flags to allow building up the
masks to determine the usable thread priorities and cores for a kernel
process instance.
2018-12-21 07:05:34 -05:00
Lioncash
6ff5135521 kernel/process: Introduce process capability parsing skeleton
We've had the old kernel capability parser from Citra, however, this is
unused code and doesn't actually map to how the kernel on the Switch
does it. This introduces the basic functional skeleton for parsing
process capabilities.
2018-12-21 07:05:31 -05:00
Lioncash
fc8da2d5e3 common: Add basic bit manipulation utility function to Common 2018-12-21 07:04:18 -05:00
David Marcec
08d5663cb8 Moved backtrace to ArmInterface 2018-12-19 14:10:51 +11:00
Luke Street
a3d78b77f8 debugger: Set paused thread color 2018-12-04 02:25:34 -05:00
Luke Street
3e75175d02 svc: Implement SetThreadActivity (thread suspension) 2018-12-04 01:23:50 -05:00
Frederic Laing
6d7514ccec game_list: Remove a reference of a reference 2018-12-03 19:26:36 +01:00
David Marcec
5102c91256 Moved backtrace to ArmInterface
Added to both dynarmic and unicorn
2018-12-03 20:13:48 +11:00
David Marcec
7149332712 Print backtrace on svcBreak
When we get an svcBreak we get a backtrace now
2018-12-03 19:12:09 +11:00
Tobias
4a32d92e6e CONTRIBUTING.md: migrate to the wiki 2018-11-17 17:48:40 +01:00
146 changed files with 5128 additions and 2244 deletions

View File

@@ -1,12 +1,12 @@
#!/bin/bash -ex
apt-get update
apt-get install --no-install-recommends -y build-essential git libqt5opengl5-dev libsdl2-dev libssl-dev python qtbase5-dev wget cmake ninja-build ccache
apt-get install --no-install-recommends -y build-essential git libqt5opengl5-dev libsdl2-dev libssl-dev python qtbase5-dev qtwebengine5-dev wget cmake ninja-build ccache
cd /yuzu
mkdir build && cd build
cmake .. -DYUZU_USE_BUNDLED_UNICORN=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/lib/ccache/gcc -DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DUSE_DISCORD_PRESENCE=ON -G Ninja
cmake .. -DYUZU_USE_BUNDLED_UNICORN=ON -DYUZU_USE_QT_WEB_ENGINE=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/lib/ccache/gcc -DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DUSE_DISCORD_PRESENCE=ON -G Ninja
ninja
ccache -s

View File

@@ -9,7 +9,7 @@ export PATH="/usr/local/opt/ccache/libexec:$PATH"
mkdir build && cd build
cmake --version
cmake .. -DYUZU_USE_BUNDLED_UNICORN=ON -DCMAKE_BUILD_TYPE=Release -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DUSE_DISCORD_PRESENCE=ON
cmake .. -DYUZU_USE_BUNDLED_UNICORN=ON -DYUZU_USE_QT_WEB_ENGINE=ON -DCMAKE_BUILD_TYPE=Release -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DUSE_DISCORD_PRESENCE=ON
make -j4
ccache -s

View File

@@ -19,6 +19,8 @@ option(ENABLE_WEB_SERVICE "Enable web services (telemetry, etc.)" ON)
option(YUZU_USE_BUNDLED_UNICORN "Build/Download bundled Unicorn" ON)
option(YUZU_USE_QT_WEB_ENGINE "Use QtWebEngine for web applet implementation" OFF)
option(ENABLE_CUBEB "Enables the cubeb audio backend" ON)
option(USE_DISCORD_PRESENCE "Enables Discord Rich Presence" OFF)
@@ -302,7 +304,7 @@ endif()
if (ENABLE_QT)
if (YUZU_USE_BUNDLED_QT)
if ((MSVC_VERSION GREATER_EQUAL 1910 AND MSVC_VERSION LESS 1920) AND ARCHITECTURE_x86_64)
set(QT_VER qt-5.10.0-msvc2015_64)
set(QT_VER qt-5.12.0-msvc2017_64)
else()
message(FATAL_ERROR "No bundled Qt binaries for your toolchain. Disable YUZU_USE_BUNDLED_QT and provide your own.")
endif()
@@ -319,6 +321,10 @@ if (ENABLE_QT)
endif()
find_package(Qt5 REQUIRED COMPONENTS Widgets OpenGL ${QT_PREFIX_HINT})
if (YUZU_USE_QT_WEB_ENGINE)
find_package(Qt5 REQUIRED COMPONENTS WebEngineCore WebEngineWidgets ${QT_PREFIX_HINT})
endif ()
endif()
# Platform-specific library requirements

View File

@@ -5,6 +5,7 @@ function(copy_yuzu_Qt5_deps target_dir)
set(Qt5_PLATFORMS_DIR "${Qt5_DIR}/../../../plugins/platforms/")
set(Qt5_STYLES_DIR "${Qt5_DIR}/../../../plugins/styles/")
set(Qt5_IMAGEFORMATS_DIR "${Qt5_DIR}/../../../plugins/imageformats/")
set(Qt5_RESOURCES_DIR "${Qt5_DIR}/../../../resources/")
set(PLATFORMS ${DLL_DEST}platforms/)
set(STYLES ${DLL_DEST}styles/)
set(IMAGEFORMATS ${DLL_DEST}imageformats/)
@@ -17,7 +18,35 @@ function(copy_yuzu_Qt5_deps target_dir)
Qt5OpenGL$<$<CONFIG:Debug>:d>.*
Qt5Widgets$<$<CONFIG:Debug>:d>.*
)
if (YUZU_USE_QT_WEB_ENGINE)
windows_copy_files(${target_dir} ${Qt5_DLL_DIR} ${DLL_DEST}
Qt5Network$<$<CONFIG:Debug>:d>.*
Qt5Positioning$<$<CONFIG:Debug>:d>.*
Qt5PrintSupport$<$<CONFIG:Debug>:d>.*
Qt5Qml$<$<CONFIG:Debug>:d>.*
Qt5Quick$<$<CONFIG:Debug>:d>.*
Qt5QuickWidgets$<$<CONFIG:Debug>:d>.*
Qt5WebChannel$<$<CONFIG:Debug>:d>.*
Qt5WebEngine$<$<CONFIG:Debug>:d>.*
Qt5WebEngineCore$<$<CONFIG:Debug>:d>.*
Qt5WebEngineWidgets$<$<CONFIG:Debug>:d>.*
QtWebEngineProcess$<$<CONFIG:Debug>:d>.*
)
windows_copy_files(${target_dir} ${Qt5_RESOURCES_DIR} ${DLL_DEST}
qtwebengine_resources.pak
qtwebengine_devtools_resources.pak
qtwebengine_resources_100p.pak
qtwebengine_resources_200p.pak
icudtl.dat
)
endif ()
windows_copy_files(yuzu ${Qt5_PLATFORMS_DIR} ${PLATFORMS} qwindows$<$<CONFIG:Debug>:d>.*)
windows_copy_files(yuzu ${Qt5_STYLES_DIR} ${STYLES} qwindowsvistastyle$<$<CONFIG:Debug>:d>.*)
windows_copy_files(yuzu ${Qt5_IMAGEFORMATS_DIR} ${IMAGEFORMATS} qjpeg$<$<CONFIG:Debug>:d>.*)
windows_copy_files(yuzu ${Qt5_IMAGEFORMATS_DIR} ${IMAGEFORMATS}
qjpeg$<$<CONFIG:Debug>:d>.*
qgif$<$<CONFIG:Debug>:d>.*
)
endfunction(copy_yuzu_Qt5_deps)

View File

@@ -1,136 +1 @@
# Reporting Issues
**The issue tracker is not a support forum.** Unless you can provide precise *technical information* regarding an issue, you *should not post in it*. If you need support, first read the [FAQ](https://github.com/yuzu-emu/yuzu/wiki/FAQ) and then either visit our [Discord server](https://discordapp.com/invite/u77vRWY), [our forum](https://community.citra-emu.org) or ask in a general emulation forum such as [/r/emulation](https://www.reddit.com/r/emulation/). If you post support questions, generic messages to the developers or vague reports without technical details, they will be closed and locked.
If you believe you have a valid issue report, please post text or a screenshot from the log (the console window that opens alongside yuzu) and build version (hex string visible in the titlebar and zip filename), as well as your hardware and software information if applicable.
# Contributing
yuzu is a brand new project, so we have a great opportunity to keep things clean and well organized early on. As such, coding style is very important when making commits. We run clang-format on our CI to check the code. Please use it to format your code when contributing. However, it doesn't cover all the rules below. Some of them aren't very strict rules since we want to be flexible and we understand that under certain circumstances some of them can be counterproductive. Just try to follow as many of them as possible.
# Using clang format (version 6.0)
When generating the native build script for your toolset, cmake will try to find the correct version of clang format (or will download it on windows). Before running cmake, please install clang format version 6.0 for your platform as follows:
* Windows: do nothing; cmake will download a pre built binary for MSVC and MINGW. MSVC users can additionally install a clang format Visual Studio extension to add features like format on save.
* OSX: run `brew install clang-format`.
* Linux: use your package manager to get an appropriate binary.
If clang format is found, then cmake will add a custom build target that can be run at any time to run clang format against *all* source files and update the formatting in them. This should be used before making a pull request so that the reviewers can spend more time reviewing the code instead of having to worry about minor style violations. On MSVC, you can run clang format by building the clang-format project in the solution. On OSX, you can either use the Makefile target `make clang-format` or by building the clang-format target in XCode. For Makefile builds, you can use the clang-format target with `make clang-format`
### General Rules
* A lot of code was taken from other projects (e.g. Citra, Dolphin, PPSSPP, Gekko). In general, when editing other people's code, follow the style of the module you're in (or better yet, fix the style if it drastically differs from our guide).
* Line width is typically 100 characters. Please do not use 80-characters.
* Don't ever introduce new external dependencies into Core
* Don't use any platform specific code in Core
* Use namespaces often
* Avoid the use of C-style casts and instead prefer C++-style `static_cast` and `reinterpret_cast`. Try to avoid using `dynamic_cast`. Never use `const_cast`.
### Naming Rules
* Functions: `PascalCase`
* Variables: `lower_case_underscored`. Prefix with `g_` if global.
* Classes: `PascalCase`
* Files and Directories: `lower_case_underscored`
* Namespaces: `PascalCase`, `_` may also be used for clarity (e.g. `ARM_InitCore`)
### Indentation/Whitespace Style
Follow the indentation/whitespace style shown below. Do not use tabs, use 4-spaces instead.
### Comments
* For regular comments, use C++ style (`//`) comments, even for multi-line ones.
* For doc-comments (Doxygen comments), use `/// ` if it's a single line, else use the `/**` `*/` style featured in the example. Start the text on the second line, not the first containing `/**`.
* For items that are both defined and declared in two separate files, put the doc-comment only next to the associated declaration. (In a header file, usually.) Otherwise, put it next to the implementation. Never duplicate doc-comments in both places.
```cpp
// Includes should be sorted lexicographically
// STD includes first
#include <map>
#include <memory>
// then, library includes
#include <nihstro/shared_binary.h>
// finally, yuzu includes
#include "common/math_util.h"
#include "common/vector_math.h"
// each major module is separated
#include "video_core/pica.h"
#include "video_core/video_core.h"
namespace Example {
// Namespace contents are not indented
// Declare globals at the top
int g_foo{}; // {} can be used to initialize types as 0, false, or nullptr
char* g_some_pointer{}; // Pointer * and reference & stick to the type name, and make sure to initialize as nullptr!
/// A colorful enum.
enum SomeEnum {
ColorRed, ///< The color of fire.
ColorGreen, ///< The color of grass.
ColorBlue, ///< Not actually the color of water.
};
/**
* Very important struct that does a lot of stuff.
* Note that the asterisks are indented by one space to align to the first line.
*/
struct Position {
int x{}, y{}; // Always intitialize member variables!
};
// Use "typename" rather than "class" here
template <typename T>
void FooBar() {
const std::string some_string{ "prefer uniform initialization" };
int some_array[]{
5,
25,
7,
42,
};
if (note == the_space_after_the_if) {
CallAfunction();
} else {
// Use a space after the // when commenting
}
// Place a single space after the for loop semicolons, prefer pre-increment
for (int i{}; i != 25; ++i) {
// This is how we write loops
}
DoStuff(this, function, call, takes, up, multiple,
lines, like, this);
if (this || condition_takes_up_multiple &&
lines && like && this || everything ||
alright || then) {
// Leave a blank space before the if block body if the condition was continued across
// several lines.
}
switch (var) {
// No indentation for case label
case 1: {
int case_var{ var + 3 };
DoSomething(case_var);
break;
}
case 3:
DoSomething(var);
return;
default:
// Yes, even break for the last case
break;
}
std::vector<T> you_can_declare, a_few, variables, like_this;
}
}
```
**The Contributor's Guide has moved to [the Citra wiki](https://github.com/citra-emu/citra/wiki/Contributing).**

View File

@@ -42,7 +42,7 @@ before_build:
$COMPAT = if ($env:ENABLE_COMPATIBILITY_REPORTING -eq $null) {0} else {$env:ENABLE_COMPATIBILITY_REPORTING}
if ($env:BUILD_TYPE -eq 'msvc') {
# redirect stderr and change the exit code to prevent powershell from cancelling the build if cmake prints a warning
cmd /C 'cmake -G "Visual Studio 15 2017 Win64" -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_BUNDLED_SDL2=1 -DYUZU_USE_BUNDLED_UNICORN=1 -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${COMPAT} -DUSE_DISCORD_PRESENCE=ON .. 2>&1 && exit 0'
cmd /C 'cmake -G "Visual Studio 15 2017 Win64" -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_BUNDLED_SDL2=1 -DYUZU_USE_BUNDLED_UNICORN=1 -DYUZU_USE_QT_WEB_ENGINE=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${COMPAT} -DUSE_DISCORD_PRESENCE=ON .. 2>&1 && exit 0'
} else {
C:\msys64\usr\bin\bash.exe -lc "cmake -G 'MSYS Makefiles' -DYUZU_BUILD_UNICORN=1 -DCMAKE_BUILD_TYPE=Release -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${COMPAT} -DUSE_DISCORD_PRESENCE=ON .. 2>&1"
}
@@ -94,6 +94,7 @@ after_build:
Copy-Item "$BUILD_DIR\*" -Destination $RELEASE_DIST -Recurse
rm "$RELEASE_DIST\*.exe"
Get-ChildItem "$BUILD_DIR" -Recurse -Filter "yuzu*.exe" | Copy-Item -destination $RELEASE_DIST
Get-ChildItem "$BUILD_DIR" -Recurse -Filter "QtWebEngineProcess*.exe" | Copy-Item -destination $RELEASE_DIST
Copy-Item .\license.txt -Destination $RELEASE_DIST
Copy-Item .\README.md -Destination $RELEASE_DIST
7z a -tzip $MSVC_BUILD_ZIP $RELEASE_DIST\*

2
externals/fmt vendored

View File

@@ -22,8 +22,7 @@ static Stream::Format ChannelsToStreamFormat(u32 num_channels) {
return Stream::Format::Multi51Channel16;
}
LOG_CRITICAL(Audio, "Unimplemented num_channels={}", num_channels);
UNREACHABLE();
UNIMPLEMENTED_MSG("Unimplemented num_channels={}", num_channels);
return {};
}

View File

@@ -260,8 +260,7 @@ void AudioRenderer::VoiceState::RefreshBuffer() {
break;
}
default:
LOG_CRITICAL(Audio, "Unimplemented sample_format={}", info.sample_format);
UNREACHABLE();
UNIMPLEMENTED_MSG("Unimplemented sample_format={}", info.sample_format);
break;
}
@@ -280,8 +279,7 @@ void AudioRenderer::VoiceState::RefreshBuffer() {
break;
}
default:
LOG_CRITICAL(Audio, "Unimplemented channel_count={}", info.channel_count);
UNREACHABLE();
UNIMPLEMENTED_MSG("Unimplemented channel_count={}", info.channel_count);
break;
}

View File

@@ -28,8 +28,7 @@ u32 Stream::GetNumChannels() const {
case Format::Multi51Channel16:
return 6;
}
LOG_CRITICAL(Audio, "Unimplemented format={}", static_cast<u32>(format));
UNREACHABLE();
UNIMPLEMENTED_MSG("Unimplemented format={}", static_cast<u32>(format));
return {};
}
@@ -49,7 +48,7 @@ void Stream::Play() {
void Stream::Stop() {
state = State::Stopped;
ASSERT_MSG(false, "Unimplemented");
UNIMPLEMENTED();
}
Stream::State Stream::GetState() const {
@@ -120,7 +119,7 @@ bool Stream::QueueBuffer(BufferPtr&& buffer) {
}
bool Stream::ContainsBuffer(Buffer::Tag tag) const {
ASSERT_MSG(false, "Unimplemented");
UNIMPLEMENTED();
return {};
}

View File

@@ -53,8 +53,8 @@ std::size_t TimeStretcher::Process(const s16* in, std::size_t num_in, s16* out,
const double lpf_gain = 1.0 - std::exp(-time_delta / lpf_time_scale);
m_stretch_ratio += lpf_gain * (current_ratio - m_stretch_ratio);
// Place a lower limit of 5% speed. When a game boots up, there will be
// many silence samples. These do not need to be timestretched.
// Place a lower limit of 5% speed. When a game boots up, there will be
// many silence samples. These do not need to be timestretched.
m_stretch_ratio = std::max(m_stretch_ratio, 0.05);
m_sound_touch.setTempo(m_stretch_ratio);

View File

@@ -44,6 +44,7 @@ add_library(common STATIC
detached_tasks.cpp
detached_tasks.h
bit_field.h
bit_util.h
cityhash.cpp
cityhash.h
color.h

61
src/common/bit_util.h Normal file
View File

@@ -0,0 +1,61 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <climits>
#include <cstddef>
#ifdef _MSC_VER
#include <intrin.h>
#endif
#include "common/common_types.h"
namespace Common {
/// Gets the size of a specified type T in bits.
template <typename T>
constexpr std::size_t BitSize() {
return sizeof(T) * CHAR_BIT;
}
#ifdef _MSC_VER
inline u32 CountLeadingZeroes32(u32 value) {
unsigned long leading_zero = 0;
if (_BitScanReverse(&leading_zero, value) != 0) {
return 31 - leading_zero;
}
return 32;
}
inline u64 CountLeadingZeroes64(u64 value) {
unsigned long leading_zero = 0;
if (_BitScanReverse64(&leading_zero, value) != 0) {
return 63 - leading_zero;
}
return 64;
}
#else
inline u32 CountLeadingZeroes32(u32 value) {
if (value == 0) {
return 32;
}
return __builtin_clz(value);
}
inline u64 CountLeadingZeroes64(u64 value) {
if (value == 0) {
return 64;
}
return __builtin_clzll(value);
}
#endif
} // namespace Common

View File

@@ -1,5 +1,6 @@
add_library(core STATIC
arm/arm_interface.h
arm/arm_interface.cpp
arm/exclusive_monitor.cpp
arm/exclusive_monitor.h
arm/unicorn/arm_unicorn.cpp
@@ -87,6 +88,8 @@ add_library(core STATIC
frontend/applets/profile_select.h
frontend/applets/software_keyboard.cpp
frontend/applets/software_keyboard.h
frontend/applets/web_browser.cpp
frontend/applets/web_browser.h
frontend/emu_window.cpp
frontend/emu_window.h
frontend/framebuffer_layout.cpp
@@ -115,6 +118,8 @@ add_library(core STATIC
hle/kernel/object.h
hle/kernel/process.cpp
hle/kernel/process.h
hle/kernel/process_capability.cpp
hle/kernel/process_capability.h
hle/kernel/readable_event.cpp
hle/kernel/readable_event.h
hle/kernel/resource_limit.cpp
@@ -170,6 +175,8 @@ add_library(core STATIC
hle/service/am/applets/software_keyboard.h
hle/service/am/applets/stub_applet.cpp
hle/service/am/applets/stub_applet.h
hle/service/am/applets/web_browser.cpp
hle/service/am/applets/web_browser.h
hle/service/am/idle.cpp
hle/service/am/idle.h
hle/service/am/omm.cpp

View File

@@ -0,0 +1,27 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/common_types.h"
#include "common/logging/log.h"
#include "core/arm/arm_interface.h"
#include "core/memory.h"
namespace Core {
void ARM_Interface::LogBacktrace() const {
VAddr fp = GetReg(29);
VAddr lr = GetReg(30);
const VAddr sp = GetReg(13);
const VAddr pc = GetPC();
LOG_ERROR(Core_ARM, "Backtrace, sp={:016X}, pc={:016X}", sp, pc);
while (true) {
LOG_ERROR(Core_ARM, "{:016X}", lr);
if (!fp) {
break;
}
lr = Memory::Read64(fp + 8) - 4;
fp = Memory::Read64(fp);
}
}
} // namespace Core

View File

@@ -141,6 +141,14 @@ public:
/// Prepare core for thread reschedule (if needed to correctly handle state)
virtual void PrepareReschedule() = 0;
/// fp (= r29) points to the last frame record.
/// Note that this is the frame record for the *previous* frame, not the current one.
/// Note we need to subtract 4 from our last read to get the proper address
/// Frame records are two words long:
/// fp+0 : pointer to previous frame record
/// fp+8 : value of lr for frame
void LogBacktrace() const;
};
} // namespace Core

View File

@@ -10,6 +10,7 @@
#include "core/core.h"
#include "core/core_timing.h"
#include "core/hle/kernel/svc.h"
#include "core/memory.h"
namespace Core {

View File

@@ -30,8 +30,11 @@
#include "core/hle/service/sm/sm.h"
#include "core/loader/loader.h"
#include "core/perf_stats.h"
#include "core/settings.h"
#include "core/telemetry_session.h"
#include "frontend/applets/profile_select.h"
#include "frontend/applets/software_keyboard.h"
#include "frontend/applets/web_browser.h"
#include "video_core/debug_utils/debug_utils.h"
#include "video_core/gpu.h"
#include "video_core/renderer_base.h"
@@ -94,6 +97,11 @@ struct System::Impl {
CoreTiming::Init();
kernel.Initialize();
const auto current_time = std::chrono::duration_cast<std::chrono::seconds>(
std::chrono::system_clock::now().time_since_epoch());
Settings::values.custom_rtc_differential =
Settings::values.custom_rtc.value_or(current_time) - current_time;
// Create a default fs if one doesn't already exist.
if (virtual_filesystem == nullptr)
virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>();
@@ -103,6 +111,8 @@ struct System::Impl {
profile_selector = std::make_unique<Core::Frontend::DefaultProfileSelectApplet>();
if (software_keyboard == nullptr)
software_keyboard = std::make_unique<Core::Frontend::DefaultSoftwareKeyboardApplet>();
if (web_browser == nullptr)
web_browser = std::make_unique<Core::Frontend::DefaultWebBrowserApplet>();
auto main_process = Kernel::Process::Create(kernel, "main");
kernel.MakeCurrentProcess(main_process.get());
@@ -199,6 +209,11 @@ struct System::Impl {
// Close app loader
app_loader.reset();
// Clear all applets
profile_selector.reset();
software_keyboard.reset();
web_browser.reset();
LOG_DEBUG(Core, "Shutdown OK");
}
@@ -233,6 +248,7 @@ struct System::Impl {
/// Frontend applets
std::unique_ptr<Core::Frontend::ProfileSelectApplet> profile_selector;
std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> software_keyboard;
std::unique_ptr<Core::Frontend::WebBrowserApplet> web_browser;
/// Service manager
std::shared_ptr<Service::SM::ServiceManager> service_manager;
@@ -427,22 +443,34 @@ std::shared_ptr<FileSys::VfsFilesystem> System::GetFilesystem() const {
return impl->virtual_filesystem;
}
void System::SetProfileSelector(std::unique_ptr<Core::Frontend::ProfileSelectApplet> applet) {
void System::SetProfileSelector(std::unique_ptr<Frontend::ProfileSelectApplet> applet) {
impl->profile_selector = std::move(applet);
}
const Core::Frontend::ProfileSelectApplet& System::GetProfileSelector() const {
const Frontend::ProfileSelectApplet& System::GetProfileSelector() const {
return *impl->profile_selector;
}
void System::SetSoftwareKeyboard(std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> applet) {
void System::SetSoftwareKeyboard(std::unique_ptr<Frontend::SoftwareKeyboardApplet> applet) {
impl->software_keyboard = std::move(applet);
}
const Core::Frontend::SoftwareKeyboardApplet& System::GetSoftwareKeyboard() const {
const Frontend::SoftwareKeyboardApplet& System::GetSoftwareKeyboard() const {
return *impl->software_keyboard;
}
void System::SetWebBrowser(std::unique_ptr<Frontend::WebBrowserApplet> applet) {
impl->web_browser = std::move(applet);
}
Frontend::WebBrowserApplet& System::GetWebBrowser() {
return *impl->web_browser;
}
const Frontend::WebBrowserApplet& System::GetWebBrowser() const {
return *impl->web_browser;
}
System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) {
return impl->Init(*this, emu_window);
}

View File

@@ -11,11 +11,12 @@
#include "common/common_types.h"
#include "core/file_sys/vfs_types.h"
#include "core/hle/kernel/object.h"
#include "frontend/applets/profile_select.h"
namespace Core::Frontend {
class EmuWindow;
class ProfileSelectApplet;
class SoftwareKeyboardApplet;
class WebBrowserApplet;
} // namespace Core::Frontend
namespace FileSys {
@@ -242,13 +243,18 @@ public:
std::shared_ptr<FileSys::VfsFilesystem> GetFilesystem() const;
void SetProfileSelector(std::unique_ptr<Core::Frontend::ProfileSelectApplet> applet);
void SetProfileSelector(std::unique_ptr<Frontend::ProfileSelectApplet> applet);
const Core::Frontend::ProfileSelectApplet& GetProfileSelector() const;
const Frontend::ProfileSelectApplet& GetProfileSelector() const;
void SetSoftwareKeyboard(std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> applet);
void SetSoftwareKeyboard(std::unique_ptr<Frontend::SoftwareKeyboardApplet> applet);
const Core::Frontend::SoftwareKeyboardApplet& GetSoftwareKeyboard() const;
const Frontend::SoftwareKeyboardApplet& GetSoftwareKeyboard() const;
void SetWebBrowser(std::unique_ptr<Frontend::WebBrowserApplet> applet);
Frontend::WebBrowserApplet& GetWebBrowser();
const Frontend::WebBrowserApplet& GetWebBrowser() const;
private:
System();

View File

@@ -359,6 +359,8 @@ bool NCA::ReadPFS0Section(const NCASectionHeader& section, const NCASectionTable
dirs.push_back(std::move(npfs));
if (IsDirectoryExeFS(dirs.back()))
exefs = dirs.back();
else if (IsDirectoryLogoPartition(dirs.back()))
logo = dirs.back();
} else {
if (has_rights_id)
status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek;
@@ -546,4 +548,8 @@ u64 NCA::GetBaseIVFCOffset() const {
return ivfc_offset;
}
VirtualDir NCA::GetLogoPartition() const {
return logo;
}
} // namespace FileSys

View File

@@ -74,6 +74,13 @@ inline bool IsDirectoryExeFS(const std::shared_ptr<VfsDirectory>& pfs) {
return pfs->GetFile("main") != nullptr && pfs->GetFile("main.npdm") != nullptr;
}
inline bool IsDirectoryLogoPartition(const VirtualDir& pfs) {
// NintendoLogo is the static image in the top left corner while StartupMovie is the animation
// in the bottom right corner.
return pfs->GetFile("NintendoLogo.png") != nullptr &&
pfs->GetFile("StartupMovie.gif") != nullptr;
}
// An implementation of VfsDirectory that represents a Nintendo Content Archive (NCA) conatiner.
// After construction, use GetStatus to determine if the file is valid and ready to be used.
class NCA : public ReadOnlyVfsDirectory {
@@ -102,6 +109,8 @@ public:
// Returns the base ivfc offset used in BKTR patching.
u64 GetBaseIVFCOffset() const;
VirtualDir GetLogoPartition() const;
private:
bool CheckSupportedNCA(const NCAHeader& header);
bool HandlePotentialHeaderDecryption();
@@ -122,6 +131,7 @@ private:
VirtualFile romfs = nullptr;
VirtualDir exefs = nullptr;
VirtualDir logo = nullptr;
VirtualFile file;
VirtualFile bktr_base_romfs;
u64 ivfc_offset = 0;

View File

@@ -36,18 +36,20 @@ std::string LanguageEntry::GetDeveloperName() const {
developer_name.size());
}
NACP::NACP(VirtualFile file) : raw(std::make_unique<RawNACP>()) {
file->ReadObject(raw.get());
NACP::NACP() = default;
NACP::NACP(VirtualFile file) {
file->ReadObject(&raw);
}
NACP::~NACP() = default;
const LanguageEntry& NACP::GetLanguageEntry(Language language) const {
if (language != Language::Default) {
return raw->language_entries.at(static_cast<u8>(language));
return raw.language_entries.at(static_cast<u8>(language));
}
for (const auto& language_entry : raw->language_entries) {
for (const auto& language_entry : raw.language_entries) {
if (!language_entry.GetApplicationName().empty())
return language_entry;
}
@@ -65,21 +67,29 @@ std::string NACP::GetDeveloperName(Language language) const {
}
u64 NACP::GetTitleId() const {
return raw->title_id;
return raw.title_id;
}
u64 NACP::GetDLCBaseTitleId() const {
return raw->dlc_base_title_id;
return raw.dlc_base_title_id;
}
std::string NACP::GetVersionString() const {
return Common::StringFromFixedZeroTerminatedBuffer(raw->version_string.data(),
raw->version_string.size());
return Common::StringFromFixedZeroTerminatedBuffer(raw.version_string.data(),
raw.version_string.size());
}
u64 NACP::GetDefaultNormalSaveSize() const {
return raw.normal_save_data_size;
}
u64 NACP::GetDefaultJournalSaveSize() const {
return raw.journal_sava_data_size;
}
std::vector<u8> NACP::GetRawBytes() const {
std::vector<u8> out(sizeof(RawNACP));
std::memcpy(out.data(), raw.get(), sizeof(RawNACP));
std::memcpy(out.data(), &raw, sizeof(RawNACP));
return out;
}
} // namespace FileSys

View File

@@ -28,17 +28,30 @@ static_assert(sizeof(LanguageEntry) == 0x300, "LanguageEntry has incorrect size.
// The raw file format of a NACP file.
struct RawNACP {
std::array<LanguageEntry, 16> language_entries;
INSERT_PADDING_BYTES(0x38);
std::array<u8, 0x25> isbn;
u8 startup_user_account;
INSERT_PADDING_BYTES(2);
u32_le application_attribute;
u32_le supported_languages;
u32_le parental_control;
bool screenshot_enabled;
u8 video_capture_mode;
bool data_loss_confirmation;
INSERT_PADDING_BYTES(1);
u64_le title_id;
INSERT_PADDING_BYTES(0x20);
std::array<u8, 0x20> rating_age;
std::array<char, 0x10> version_string;
u64_le dlc_base_title_id;
u64_le title_id_2;
INSERT_PADDING_BYTES(0x28);
u64_le normal_save_data_size;
u64_le journal_sava_data_size;
INSERT_PADDING_BYTES(0x18);
u64_le product_code;
u64_le title_id_3;
std::array<u64_le, 0x7> title_id_array;
INSERT_PADDING_BYTES(0x8);
std::array<u64_le, 0x8> local_communication;
u8 logo_type;
u8 logo_handling;
bool runtime_add_on_content_install;
INSERT_PADDING_BYTES(5);
u64_le title_id_update;
std::array<u8, 0x40> bcat_passphrase;
INSERT_PADDING_BYTES(0xEC0);
@@ -72,6 +85,7 @@ extern const std::array<const char*, 15> LANGUAGE_NAMES;
// These store application name, dev name, title id, and other miscellaneous data.
class NACP {
public:
explicit NACP();
explicit NACP(VirtualFile file);
~NACP();
@@ -81,10 +95,12 @@ public:
u64 GetTitleId() const;
u64 GetDLCBaseTitleId() const;
std::string GetVersionString() const;
u64 GetDefaultNormalSaveSize() const;
u64 GetDefaultJournalSaveSize() const;
std::vector<u8> GetRawBytes() const;
private:
std::unique_ptr<RawNACP> raw;
RawNACP raw{};
};
} // namespace FileSys

View File

@@ -39,27 +39,4 @@ static_assert(sizeof(Entry) == 0x310, "Directory Entry struct isn't exactly 0x31
static_assert(offsetof(Entry, type) == 0x304, "Wrong offset for type in Entry.");
static_assert(offsetof(Entry, file_size) == 0x308, "Wrong offset for file_size in Entry.");
class DirectoryBackend : NonCopyable {
public:
DirectoryBackend() {}
virtual ~DirectoryBackend() {}
/**
* List files contained in the directory
* @param count Number of entries to return at once in entries
* @param entries Buffer to read data into
* @return Number of entries listed
*/
virtual u64 Read(const u64 count, Entry* entries) = 0;
/// Returns the number of entries still left to read.
virtual u64 GetEntryCount() const = 0;
/**
* Close the directory
* @return true if the directory closed correctly
*/
virtual bool Close() const = 0;
};
} // namespace FileSys

View File

@@ -40,6 +40,13 @@ Loader::ResultStatus ProgramMetadata::Load(VirtualFile file) {
if (sizeof(FileAccessHeader) != file->ReadObject(&aci_file_access, aci_header.fah_offset))
return Loader::ResultStatus::ErrorBadFileAccessHeader;
aci_kernel_capabilities.resize(aci_header.kac_size / sizeof(u32));
const u64 read_size = aci_header.kac_size;
const u64 read_offset = npdm_header.aci_offset + aci_header.kac_offset;
if (file->ReadBytes(aci_kernel_capabilities.data(), read_size, read_offset) != read_size) {
return Loader::ResultStatus::ErrorBadKernelCapabilityDescriptors;
}
return Loader::ResultStatus::Success;
}
@@ -71,6 +78,10 @@ u64 ProgramMetadata::GetFilesystemPermissions() const {
return aci_file_access.permissions;
}
const ProgramMetadata::KernelCapabilityDescriptors& ProgramMetadata::GetKernelCapabilities() const {
return aci_kernel_capabilities;
}
void ProgramMetadata::Print() const {
LOG_DEBUG(Service_FS, "Magic: {:.4}", npdm_header.magic.data());
LOG_DEBUG(Service_FS, "Main thread priority: 0x{:02X}", npdm_header.main_thread_priority);
@@ -81,16 +92,20 @@ void ProgramMetadata::Print() const {
LOG_DEBUG(Service_FS, " > 64-bit instructions: {}",
npdm_header.has_64_bit_instructions ? "YES" : "NO");
auto address_space = "Unknown";
const char* address_space = "Unknown";
switch (npdm_header.address_space_type) {
case ProgramAddressSpaceType::Is36Bit:
address_space = "64-bit (36-bit address space)";
break;
case ProgramAddressSpaceType::Is39Bit:
address_space = "64-bit";
address_space = "64-bit (39-bit address space)";
break;
case ProgramAddressSpaceType::Is32Bit:
case ProgramAddressSpaceType::Is32BitNoMap:
address_space = "32-bit";
break;
case ProgramAddressSpaceType::Is32BitNoMap:
address_space = "32-bit (no map region)";
break;
}
LOG_DEBUG(Service_FS, " > Address space: {}\n", address_space);

View File

@@ -5,6 +5,7 @@
#pragma once
#include <array>
#include <vector>
#include "common/bit_field.h"
#include "common/common_types.h"
#include "common/swap.h"
@@ -38,6 +39,8 @@ enum class ProgramFilePermission : u64 {
*/
class ProgramMetadata {
public:
using KernelCapabilityDescriptors = std::vector<u32>;
ProgramMetadata();
~ProgramMetadata();
@@ -50,6 +53,7 @@ public:
u32 GetMainThreadStackSize() const;
u64 GetTitleID() const;
u64 GetFilesystemPermissions() const;
const KernelCapabilityDescriptors& GetKernelCapabilities() const;
void Print() const;
@@ -154,6 +158,8 @@ private:
FileAccessControl acid_file_access;
FileAccessHeader aci_file_access;
KernelCapabilityDescriptors aci_kernel_capabilities;
};
} // namespace FileSys

View File

@@ -119,6 +119,9 @@ VirtualDir ExtractRomFS(VirtualFile file, RomFSExtractionType type) {
VirtualDir out = std::move(root);
if (type == RomFSExtractionType::SingleDiscard)
return out->GetSubdirectories().front();
while (out->GetSubdirectories().size() == 1 && out->GetFiles().empty()) {
if (out->GetSubdirectories().front()->GetName() == "data" &&
type == RomFSExtractionType::Truncated)

View File

@@ -33,8 +33,9 @@ struct IVFCHeader {
static_assert(sizeof(IVFCHeader) == 0xE0, "IVFCHeader has incorrect size.");
enum class RomFSExtractionType {
Full, // Includes data directory
Truncated, // Traverses into data directory
Full, // Includes data directory
Truncated, // Traverses into data directory
SingleDiscard, // Traverses into the first subdirectory of root
};
// Converts a RomFS binary blob to VFS Filesystem

View File

@@ -13,6 +13,8 @@
namespace FileSys {
constexpr char SAVE_DATA_SIZE_FILENAME[] = ".yuzu_save_size";
std::string SaveDataDescriptor::DebugInfo() const {
return fmt::format("[type={:02X}, title_id={:016X}, user_id={:016X}{:016X}, save_id={:016X}]",
static_cast<u8>(type), title_id, user_id[1], user_id[0], save_id);
@@ -132,4 +134,32 @@ std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType typ
}
}
SaveDataSize SaveDataFactory::ReadSaveDataSize(SaveDataType type, u64 title_id,
u128 user_id) const {
const auto path = GetFullPath(SaveDataSpaceId::NandUser, type, title_id, user_id, 0);
const auto dir = GetOrCreateDirectoryRelative(this->dir, path);
const auto size_file = dir->GetFile(SAVE_DATA_SIZE_FILENAME);
if (size_file == nullptr || size_file->GetSize() < sizeof(SaveDataSize))
return {0, 0};
SaveDataSize out;
if (size_file->ReadObject(&out) != sizeof(SaveDataSize))
return {0, 0};
return out;
}
void SaveDataFactory::WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id,
SaveDataSize new_value) {
const auto path = GetFullPath(SaveDataSpaceId::NandUser, type, title_id, user_id, 0);
const auto dir = GetOrCreateDirectoryRelative(this->dir, path);
const auto size_file = dir->CreateFile(SAVE_DATA_SIZE_FILENAME);
if (size_file == nullptr)
return;
size_file->Resize(sizeof(SaveDataSize));
size_file->WriteObject(new_value);
}
} // namespace FileSys

View File

@@ -46,6 +46,11 @@ struct SaveDataDescriptor {
};
static_assert(sizeof(SaveDataDescriptor) == 0x40, "SaveDataDescriptor has incorrect size.");
struct SaveDataSize {
u64 normal;
u64 journal;
};
/// File system interface to the SaveData archive
class SaveDataFactory {
public:
@@ -60,6 +65,9 @@ public:
static std::string GetFullPath(SaveDataSpaceId space, SaveDataType type, u64 title_id,
u128 user_id, u64 save_id);
SaveDataSize ReadSaveDataSize(SaveDataType type, u64 title_id, u128 user_id) const;
void WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id, SaveDataSize new_value);
private:
VirtualDir dir;
};

View File

@@ -150,7 +150,7 @@ public:
template <typename T>
std::size_t WriteArray(const T* data, std::size_t number_elements, std::size_t offset = 0) {
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
return Write(data, number_elements * sizeof(T), offset);
return Write(reinterpret_cast<const u8*>(data), number_elements * sizeof(T), offset);
}
// Writes size bytes starting at memory location data to offset in file.
@@ -166,7 +166,7 @@ public:
template <typename T>
std::size_t WriteObject(const T& data, std::size_t offset = 0) {
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
return Write(&data, sizeof(T), offset);
return Write(reinterpret_cast<const u8*>(&data), sizeof(T), offset);
}
// Renames the file to name. Returns whether or not the operation was successsful.

View File

@@ -0,0 +1,24 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/logging/log.h"
#include "core/frontend/applets/web_browser.h"
namespace Core::Frontend {
WebBrowserApplet::~WebBrowserApplet() = default;
DefaultWebBrowserApplet::~DefaultWebBrowserApplet() = default;
void DefaultWebBrowserApplet::OpenPage(std::string_view filename,
std::function<void()> unpack_romfs_callback,
std::function<void()> finished_callback) {
LOG_INFO(Service_AM,
"(STUBBED) called - No suitable web browser implementation found to open website page "
"at '{}'!",
filename);
finished_callback();
}
} // namespace Core::Frontend

View File

@@ -0,0 +1,28 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <functional>
#include <string_view>
namespace Core::Frontend {
class WebBrowserApplet {
public:
virtual ~WebBrowserApplet();
virtual void OpenPage(std::string_view url, std::function<void()> unpack_romfs_callback,
std::function<void()> finished_callback) = 0;
};
class DefaultWebBrowserApplet final : public WebBrowserApplet {
public:
~DefaultWebBrowserApplet() override;
void OpenPage(std::string_view url, std::function<void()> unpack_romfs_callback,
std::function<void()> finished_callback) override;
};
} // namespace Core::Frontend

View File

@@ -12,6 +12,23 @@
namespace Core::Frontend {
/**
* Represents a graphics context that can be used for background computation or drawing. If the
* graphics backend doesn't require the context, then the implementation of these methods can be
* stubs
*/
class GraphicsContext {
public:
/// Makes the graphics context current for the caller thread
virtual void MakeCurrent() = 0;
/// Releases (dunno if this is the "right" word) the context from the caller thread
virtual void DoneCurrent() = 0;
/// Swap buffers to display the next frame
virtual void SwapBuffers() = 0;
};
/**
* Abstraction class used to provide an interface between emulation code and the frontend
* (e.g. SDL, QGLWidget, GLFW, etc...).
@@ -30,7 +47,7 @@ namespace Core::Frontend {
* - DO NOT TREAT THIS CLASS AS A GUI TOOLKIT ABSTRACTION LAYER. That's not what it is. Please
* re-read the upper points again and think about it if you don't see this.
*/
class EmuWindow {
class EmuWindow : public GraphicsContext {
public:
/// Data structure to store emuwindow configuration
struct WindowConfig {
@@ -40,17 +57,21 @@ public:
std::pair<unsigned, unsigned> min_client_area_size;
};
/// Swap buffers to display the next frame
virtual void SwapBuffers() = 0;
/// Polls window events
virtual void PollEvents() = 0;
/// Makes the graphics context current for the caller thread
virtual void MakeCurrent() = 0;
/// Releases (dunno if this is the "right" word) the GLFW context from the caller thread
virtual void DoneCurrent() = 0;
/**
* Returns a GraphicsContext that the frontend provides that is shared with the emu window. This
* context can be used from other threads for background graphics computation. If the frontend
* is using a graphics backend that doesn't need anything specific to run on a different thread,
* then it can use a stubbed implemenation for GraphicsContext.
*
* If the return value is null, then the core should assume that the frontend cannot provide a
* Shared Context
*/
virtual std::unique_ptr<GraphicsContext> CreateSharedContext() const {
return nullptr;
}
/**
* Signal that a touch pressed event has occurred (e.g. mouse click pressed)

View File

@@ -18,7 +18,7 @@
#include "core/hle/kernel/client_session.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/server_port.h"
#include "core/hle/kernel/server_session.h"
namespace IPC {

View File

@@ -8,6 +8,7 @@
#include "core/hle/kernel/server_session.h"
#include "core/hle/kernel/session.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/result.h"
namespace Kernel {

View File

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

View File

@@ -11,6 +11,7 @@ namespace Kernel {
// Confirmed Switch kernel error codes
constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED{ErrorModule::Kernel, 7};
constexpr ResultCode ERR_INVALID_CAPABILITY_DESCRIPTOR{ErrorModule::Kernel, 14};
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};
@@ -27,9 +28,10 @@ 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_BUSY{ErrorModule::Kernel, 122};
constexpr ResultCode ERR_SESSION_CLOSED_BY_REMOTE{ErrorModule::Kernel, 123};
constexpr ResultCode ERR_INVALID_STATE{ErrorModule::Kernel, 125};
constexpr ResultCode ERR_RESERVED_VALUE{ErrorModule::Kernel, 126};
constexpr ResultCode ERR_RESOURCE_LIMIT_EXCEEDED{ErrorModule::Kernel, 132};
} // namespace Kernel

View File

@@ -43,6 +43,9 @@ enum KernelHandle : Handle {
*/
class HandleTable final : NonCopyable {
public:
/// This is the maximum limit of handles allowed per process in Horizon
static constexpr std::size_t MAX_COUNT = 1024;
HandleTable();
~HandleTable();
@@ -91,9 +94,6 @@ public:
void Clear();
private:
/// This is the maximum limit of handles allowed per process in Horizon
static constexpr std::size_t MAX_COUNT = 1024;
/// Stores the Object referenced by the handle or null if the slot is empty.
std::array<SharedPtr<Object>, MAX_COUNT> objects;

View File

@@ -22,11 +22,16 @@
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/readable_event.h"
#include "core/hle/kernel/server_session.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/kernel/writable_event.h"
#include "core/memory.h"
namespace Kernel {
SessionRequestHandler::SessionRequestHandler() = default;
SessionRequestHandler::~SessionRequestHandler() = default;
void SessionRequestHandler::ClientConnected(SharedPtr<ServerSession> server_session) {
server_session->SetHleHandler(shared_from_this());
connected_sessions.push_back(std::move(server_session));

View File

@@ -14,8 +14,6 @@
#include "common/swap.h"
#include "core/hle/ipc.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/server_session.h"
#include "core/hle/kernel/thread.h"
namespace Service {
class ServiceFrameworkBase;
@@ -27,9 +25,13 @@ class Domain;
class HandleTable;
class HLERequestContext;
class Process;
class ServerSession;
class Thread;
class ReadableEvent;
class WritableEvent;
enum class ThreadWakeupReason;
/**
* Interface implemented by HLE Session handlers.
* This can be provided to a ServerSession in order to hook into several relevant events
@@ -37,7 +39,8 @@ class WritableEvent;
*/
class SessionRequestHandler : public std::enable_shared_from_this<SessionRequestHandler> {
public:
virtual ~SessionRequestHandler() = default;
SessionRequestHandler();
virtual ~SessionRequestHandler();
/**
* Handles a sync request from the emulated application.

View File

@@ -2,7 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <array>
#include <atomic>
#include <memory>
#include <mutex>

View File

@@ -36,7 +36,6 @@ enum class HandleType : u32 {
enum class ResetType {
OneShot, ///< Reset automatically on object acquisition
Sticky, ///< Never reset automatically
Pulse, ///< Reset automatically on wakeup
};
class Object : NonCopyable {

View File

@@ -20,6 +20,35 @@
#include "core/settings.h"
namespace Kernel {
namespace {
/**
* Sets up the primary application thread
*
* @param owner_process The parent process for the main thread
* @param kernel The kernel instance to create the main thread under.
* @param entry_point The address at which the thread should start execution
* @param priority The priority to give the main thread
*/
void SetupMainThread(Process& owner_process, KernelCore& kernel, VAddr entry_point, u32 priority) {
// Setup page table so we can write to memory
SetCurrentPageTable(&owner_process.VMManager().page_table);
// Initialize new "main" thread
const VAddr stack_top = owner_process.VMManager().GetTLSIORegionEndAddress();
auto thread_res = Thread::Create(kernel, "main", entry_point, priority, 0,
owner_process.GetIdealCore(), stack_top, owner_process);
SharedPtr<Thread> thread = std::move(thread_res).Unwrap();
// Register 1 must be a handle to the main thread
const Handle guest_handle = owner_process.GetHandleTable().Create(thread).Unwrap();
thread->SetGuestHandle(guest_handle);
thread->GetContext().cpu_registers[1] = guest_handle;
// Threads by default are dormant, wake up the main thread so it runs when the scheduler fires
thread->ResumeFromWait();
}
} // Anonymous namespace
CodeSet::CodeSet() = default;
CodeSet::~CodeSet() = default;
@@ -28,13 +57,11 @@ SharedPtr<Process> Process::Create(KernelCore& kernel, std::string&& name) {
SharedPtr<Process> process(new Process(kernel));
process->name = std::move(name);
process->flags.raw = 0;
process->flags.memory_region.Assign(MemoryRegion::APPLICATION);
process->resource_limit = kernel.GetSystemResourceLimit();
process->status = ProcessStatus::Created;
process->program_id = 0;
process->process_id = kernel.CreateNewProcessID();
process->svc_access_mask.set();
process->capabilities.InitializeForMetadatalessProcess();
std::mt19937 rng(Settings::values.rng_seed.value_or(0));
std::uniform_int_distribution<u64> distribution;
@@ -64,83 +91,15 @@ ResultCode Process::ClearSignalState() {
return RESULT_SUCCESS;
}
void Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata) {
ResultCode Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata) {
program_id = metadata.GetTitleID();
ideal_processor = metadata.GetMainThreadCore();
ideal_core = metadata.GetMainThreadCore();
is_64bit_process = metadata.Is64BitProgram();
vm_manager.Reset(metadata.GetAddressSpaceType());
}
void Process::ParseKernelCaps(const u32* kernel_caps, std::size_t len) {
for (std::size_t i = 0; i < len; ++i) {
u32 descriptor = kernel_caps[i];
u32 type = descriptor >> 20;
if (descriptor == 0xFFFFFFFF) {
// Unused descriptor entry
continue;
} else if ((type & 0xF00) == 0xE00) { // 0x0FFF
// Allowed interrupts list
LOG_WARNING(Loader, "ExHeader allowed interrupts list ignored");
} else if ((type & 0xF80) == 0xF00) { // 0x07FF
// Allowed syscalls mask
unsigned int index = ((descriptor >> 24) & 7) * 24;
u32 bits = descriptor & 0xFFFFFF;
while (bits && index < svc_access_mask.size()) {
svc_access_mask.set(index, bits & 1);
++index;
bits >>= 1;
}
} else if ((type & 0xFF0) == 0xFE0) { // 0x00FF
// Handle table size
handle_table_size = descriptor & 0x3FF;
} else if ((type & 0xFF8) == 0xFF0) { // 0x007F
// Misc. flags
flags.raw = descriptor & 0xFFFF;
} else if ((type & 0xFFE) == 0xFF8) { // 0x001F
// Mapped memory range
if (i + 1 >= len || ((kernel_caps[i + 1] >> 20) & 0xFFE) != 0xFF8) {
LOG_WARNING(Loader, "Incomplete exheader memory range descriptor ignored.");
continue;
}
u32 end_desc = kernel_caps[i + 1];
++i; // Skip over the second descriptor on the next iteration
AddressMapping mapping;
mapping.address = descriptor << 12;
VAddr end_address = end_desc << 12;
if (mapping.address < end_address) {
mapping.size = end_address - mapping.address;
} else {
mapping.size = 0;
}
mapping.read_only = (descriptor & (1 << 20)) != 0;
mapping.unk_flag = (end_desc & (1 << 20)) != 0;
address_mappings.push_back(mapping);
} else if ((type & 0xFFF) == 0xFFE) { // 0x000F
// Mapped memory page
AddressMapping mapping;
mapping.address = descriptor << 12;
mapping.size = Memory::PAGE_SIZE;
mapping.read_only = false;
mapping.unk_flag = false;
address_mappings.push_back(mapping);
} else if ((type & 0xFE0) == 0xFC0) { // 0x01FF
// Kernel version
kernel_version = descriptor & 0xFFFF;
int minor = kernel_version & 0xFF;
int major = (kernel_version >> 8) & 0xFF;
LOG_INFO(Loader, "ExHeader kernel version: {}.{}", major, minor);
} else {
LOG_ERROR(Loader, "Unhandled kernel caps descriptor: 0x{:08X}", descriptor);
}
}
const auto& caps = metadata.GetKernelCapabilities();
return capabilities.InitializeForUserProcess(caps.data(), caps.size(), vm_manager);
}
void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) {
@@ -156,7 +115,7 @@ void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) {
vm_manager.LogLayout();
ChangeStatus(ProcessStatus::Running);
Kernel::SetupMainThread(kernel, entry_point, main_thread_priority, *this);
SetupMainThread(*this, kernel, entry_point, main_thread_priority);
}
void Process::PrepareForTermination() {
@@ -268,22 +227,6 @@ void Process::LoadModule(CodeSet module_, VAddr base_addr) {
Core::System::GetInstance().ArmInterface(3).ClearInstructionCache();
}
ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission perms) {
return vm_manager.HeapAllocate(target, size, perms);
}
ResultCode Process::HeapFree(VAddr target, u32 size) {
return vm_manager.HeapFree(target, size);
}
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) {
return vm_manager.UnmapRange(dst_addr, size);
}
Kernel::Process::Process(KernelCore& kernel) : WaitObject{kernel} {}
Kernel::Process::~Process() {}

View File

@@ -11,10 +11,9 @@
#include <string>
#include <vector>
#include <boost/container/static_vector.hpp>
#include "common/bit_field.h"
#include "common/common_types.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/kernel/process_capability.h"
#include "core/hle/kernel/vm_manager.h"
#include "core/hle/kernel/wait_object.h"
#include "core/hle/result.h"
@@ -27,6 +26,7 @@ namespace Kernel {
class KernelCore;
class ResourceLimit;
class Thread;
struct AddressMapping {
// Address and size must be page-aligned
@@ -42,24 +42,6 @@ enum class MemoryRegion : u16 {
BASE = 3,
};
union ProcessFlags {
u16 raw;
BitField<0, 1, u16>
allow_debug; ///< Allows other processes to attach to and debug this process.
BitField<1, 1, u16> force_debug; ///< Allows this process to attach to processes even if they
/// don't have allow_debug set.
BitField<2, 1, u16> allow_nonalphanum;
BitField<3, 1, u16> shared_page_writable; ///< Shared page is mapped with write permissions.
BitField<4, 1, u16> privileged_priority; ///< Can use priority levels higher than 24.
BitField<5, 1, u16> allow_main_args;
BitField<6, 1, u16> shared_device_mem;
BitField<7, 1, u16> runnable_on_sleep;
BitField<8, 4, MemoryRegion>
memory_region; ///< Default region for memory allocations for this process
BitField<12, 1, u16> loaded_high; ///< Application loaded high (not at 0x00100000).
};
/**
* Indicates the status of a Process instance.
*
@@ -186,19 +168,19 @@ public:
/// Gets the resource limit descriptor for this process
SharedPtr<ResourceLimit> GetResourceLimit() const;
/// Gets the default CPU ID for this process
u8 GetDefaultProcessorID() const {
return ideal_processor;
/// Gets the ideal CPU core ID for this process
u8 GetIdealCore() const {
return ideal_core;
}
/// Gets the bitmask of allowed CPUs that this process' threads can run on.
u32 GetAllowedProcessorMask() const {
return allowed_processor_mask;
/// Gets the bitmask of allowed cores that this process' threads can run on.
u64 GetCoreMask() const {
return capabilities.GetCoreMask();
}
/// Gets the bitmask of allowed thread priorities.
u32 GetAllowedThreadPriorityMask() const {
return allowed_thread_priority_mask;
u64 GetPriorityMask() const {
return capabilities.GetPriorityMask();
}
u32 IsVirtualMemoryEnabled() const {
@@ -239,15 +221,12 @@ public:
* Loads process-specifics configuration info with metadata provided
* by an executable.
*
* @param metadata The provided metadata to load process specific info.
* @param metadata The provided metadata to load process specific info from.
*
* @returns RESULT_SUCCESS if all relevant metadata was able to be
* loaded and parsed. Otherwise, an error code is returned.
*/
void LoadFromMetadata(const FileSys::ProgramMetadata& metadata);
/**
* Parses a list of kernel capability descriptors (as found in the ExHeader) and applies them
* to this process.
*/
void ParseKernelCaps(const u32* kernel_caps, std::size_t len);
ResultCode LoadFromMetadata(const FileSys::ProgramMetadata& metadata);
/**
* Applies address space changes and launches the process main thread.
@@ -263,7 +242,7 @@ public:
void LoadModule(CodeSet module_, VAddr base_addr);
///////////////////////////////////////////////////////////////////////////////////////////////
// Memory Management
// Thread-local storage management
// Marks the next available region as used and returns the address of the slot.
VAddr MarkNextAvailableTLSSlotAsUsed(Thread& thread);
@@ -271,13 +250,6 @@ public:
// Frees a used TLS slot identified by the given address
void FreeTLSSlot(VAddr tls_address);
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, MemoryState state);
ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size);
private:
explicit Process(KernelCore& kernel);
~Process() override;
@@ -308,22 +280,8 @@ private:
/// Resource limit descriptor for this process
SharedPtr<ResourceLimit> resource_limit;
/// The process may only call SVCs which have the corresponding bit set.
std::bitset<0x80> svc_access_mask;
/// Maximum size of the handle table for the process.
u32 handle_table_size = 0x200;
/// Special memory ranges mapped into this processes address space. This is used to give
/// processes access to specific I/O regions and device memory.
boost::container::static_vector<AddressMapping, 8> address_mappings;
ProcessFlags flags;
/// Kernel compatibility version for this process
u16 kernel_version = 0;
/// The default CPU for this process, threads are scheduled on this cpu by default.
u8 ideal_processor = 0;
/// Bitmask of allowed CPUs that this process' threads can run on. TODO(Subv): Actually parse
/// this value from the process header.
u32 allowed_processor_mask = THREADPROCESSORID_DEFAULT_MASK;
u32 allowed_thread_priority_mask = 0xFFFFFFFF;
/// The ideal CPU core for this process, threads are scheduled on this core by default.
u8 ideal_core = 0;
u32 is_virtual_address_memory_enabled = 0;
/// The Thread Local Storage area is allocated as processes create threads,
@@ -333,6 +291,9 @@ private:
/// This vector will grow as more pages are allocated for new threads.
std::vector<std::bitset<8>> tls_slots;
/// Contains the parsed process capability descriptors.
ProcessCapabilities capabilities;
/// Whether or not this process is AArch64, or AArch32.
/// By default, we currently assume this is true, unless otherwise
/// specified by metadata provided to the process during loading.

View File

@@ -0,0 +1,355 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/bit_util.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/process_capability.h"
#include "core/hle/kernel/vm_manager.h"
namespace Kernel {
namespace {
// clang-format off
// Shift offsets for kernel capability types.
enum : u32 {
CapabilityOffset_PriorityAndCoreNum = 3,
CapabilityOffset_Syscall = 4,
CapabilityOffset_MapPhysical = 6,
CapabilityOffset_MapIO = 7,
CapabilityOffset_Interrupt = 11,
CapabilityOffset_ProgramType = 13,
CapabilityOffset_KernelVersion = 14,
CapabilityOffset_HandleTableSize = 15,
CapabilityOffset_Debug = 16,
};
// Combined mask of all parameters that may be initialized only once.
constexpr u32 InitializeOnceMask = (1U << CapabilityOffset_PriorityAndCoreNum) |
(1U << CapabilityOffset_ProgramType) |
(1U << CapabilityOffset_KernelVersion) |
(1U << CapabilityOffset_HandleTableSize) |
(1U << CapabilityOffset_Debug);
// Packed kernel version indicating 10.4.0
constexpr u32 PackedKernelVersion = 0x520000;
// Indicates possible types of capabilities that can be specified.
enum class CapabilityType : u32 {
Unset = 0U,
PriorityAndCoreNum = (1U << CapabilityOffset_PriorityAndCoreNum) - 1,
Syscall = (1U << CapabilityOffset_Syscall) - 1,
MapPhysical = (1U << CapabilityOffset_MapPhysical) - 1,
MapIO = (1U << CapabilityOffset_MapIO) - 1,
Interrupt = (1U << CapabilityOffset_Interrupt) - 1,
ProgramType = (1U << CapabilityOffset_ProgramType) - 1,
KernelVersion = (1U << CapabilityOffset_KernelVersion) - 1,
HandleTableSize = (1U << CapabilityOffset_HandleTableSize) - 1,
Debug = (1U << CapabilityOffset_Debug) - 1,
Ignorable = 0xFFFFFFFFU,
};
// clang-format on
constexpr CapabilityType GetCapabilityType(u32 value) {
return static_cast<CapabilityType>((~value & (value + 1)) - 1);
}
u32 GetFlagBitOffset(CapabilityType type) {
const auto value = static_cast<u32>(type);
return static_cast<u32>(Common::BitSize<u32>() - Common::CountLeadingZeroes32(value));
}
} // Anonymous namespace
ResultCode ProcessCapabilities::InitializeForKernelProcess(const u32* capabilities,
std::size_t num_capabilities,
VMManager& vm_manager) {
Clear();
// Allow all cores and priorities.
core_mask = 0xF;
priority_mask = 0xFFFFFFFFFFFFFFFF;
kernel_version = PackedKernelVersion;
return ParseCapabilities(capabilities, num_capabilities, vm_manager);
}
ResultCode ProcessCapabilities::InitializeForUserProcess(const u32* capabilities,
std::size_t num_capabilities,
VMManager& vm_manager) {
Clear();
return ParseCapabilities(capabilities, num_capabilities, vm_manager);
}
void ProcessCapabilities::InitializeForMetadatalessProcess() {
// Allow all cores and priorities
core_mask = 0xF;
priority_mask = 0xFFFFFFFFFFFFFFFF;
kernel_version = PackedKernelVersion;
// Allow all system calls and interrupts.
svc_capabilities.set();
interrupt_capabilities.set();
// Allow using the maximum possible amount of handles
handle_table_size = static_cast<u32>(HandleTable::MAX_COUNT);
// Allow all debugging capabilities.
is_debuggable = true;
can_force_debug = true;
}
ResultCode ProcessCapabilities::ParseCapabilities(const u32* capabilities,
std::size_t num_capabilities,
VMManager& vm_manager) {
u32 set_flags = 0;
u32 set_svc_bits = 0;
for (std::size_t i = 0; i < num_capabilities; ++i) {
const u32 descriptor = capabilities[i];
const auto type = GetCapabilityType(descriptor);
if (type == CapabilityType::MapPhysical) {
i++;
// The MapPhysical type uses two descriptor flags for its parameters.
// If there's only one, then there's a problem.
if (i >= num_capabilities) {
return ERR_INVALID_COMBINATION;
}
const auto size_flags = capabilities[i];
if (GetCapabilityType(size_flags) != CapabilityType::MapPhysical) {
return ERR_INVALID_COMBINATION;
}
const auto result = HandleMapPhysicalFlags(descriptor, size_flags, vm_manager);
if (result.IsError()) {
return result;
}
} else {
const auto result =
ParseSingleFlagCapability(set_flags, set_svc_bits, descriptor, vm_manager);
if (result.IsError()) {
return result;
}
}
}
return RESULT_SUCCESS;
}
ResultCode ProcessCapabilities::ParseSingleFlagCapability(u32& set_flags, u32& set_svc_bits,
u32 flag, VMManager& vm_manager) {
const auto type = GetCapabilityType(flag);
if (type == CapabilityType::Unset) {
return ERR_INVALID_CAPABILITY_DESCRIPTOR;
}
// Bail early on ignorable entries, as one would expect,
// ignorable descriptors can be ignored.
if (type == CapabilityType::Ignorable) {
return RESULT_SUCCESS;
}
// Ensure that the give flag hasn't already been initialized before.
// If it has been, then bail.
const u32 flag_length = GetFlagBitOffset(type);
const u32 set_flag = 1U << flag_length;
if ((set_flag & set_flags & InitializeOnceMask) != 0) {
return ERR_INVALID_COMBINATION;
}
set_flags |= set_flag;
switch (type) {
case CapabilityType::PriorityAndCoreNum:
return HandlePriorityCoreNumFlags(flag);
case CapabilityType::Syscall:
return HandleSyscallFlags(set_svc_bits, flag);
case CapabilityType::MapIO:
return HandleMapIOFlags(flag, vm_manager);
case CapabilityType::Interrupt:
return HandleInterruptFlags(flag);
case CapabilityType::ProgramType:
return HandleProgramTypeFlags(flag);
case CapabilityType::KernelVersion:
return HandleKernelVersionFlags(flag);
case CapabilityType::HandleTableSize:
return HandleHandleTableFlags(flag);
case CapabilityType::Debug:
return HandleDebugFlags(flag);
default:
break;
}
return ERR_INVALID_CAPABILITY_DESCRIPTOR;
}
void ProcessCapabilities::Clear() {
svc_capabilities.reset();
interrupt_capabilities.reset();
core_mask = 0;
priority_mask = 0;
handle_table_size = 0;
kernel_version = 0;
program_type = ProgramType::SysModule;
is_debuggable = false;
can_force_debug = false;
}
ResultCode ProcessCapabilities::HandlePriorityCoreNumFlags(u32 flags) {
if (priority_mask != 0 || core_mask != 0) {
return ERR_INVALID_CAPABILITY_DESCRIPTOR;
}
const u32 core_num_min = (flags >> 16) & 0xFF;
const u32 core_num_max = (flags >> 24) & 0xFF;
if (core_num_min > core_num_max) {
return ERR_INVALID_COMBINATION;
}
const u32 priority_min = (flags >> 10) & 0x3F;
const u32 priority_max = (flags >> 4) & 0x3F;
if (priority_min > priority_max) {
return ERR_INVALID_COMBINATION;
}
// The switch only has 4 usable cores.
if (core_num_max >= 4) {
return ERR_INVALID_PROCESSOR_ID;
}
const auto make_mask = [](u64 min, u64 max) {
const u64 range = max - min + 1;
const u64 mask = (1ULL << range) - 1;
return mask << min;
};
core_mask = make_mask(core_num_min, core_num_max);
priority_mask = make_mask(priority_min, priority_max);
return RESULT_SUCCESS;
}
ResultCode ProcessCapabilities::HandleSyscallFlags(u32& set_svc_bits, u32 flags) {
const u32 index = flags >> 29;
const u32 svc_bit = 1U << index;
// If we've already set this svc before, bail.
if ((set_svc_bits & svc_bit) != 0) {
return ERR_INVALID_COMBINATION;
}
set_svc_bits |= svc_bit;
const u32 svc_mask = (flags >> 5) & 0xFFFFFF;
for (u32 i = 0; i < 24; ++i) {
const u32 svc_number = index * 24 + i;
if ((svc_mask & (1U << i)) == 0) {
continue;
}
if (svc_number >= svc_capabilities.size()) {
return ERR_OUT_OF_RANGE;
}
svc_capabilities[svc_number] = true;
}
return RESULT_SUCCESS;
}
ResultCode ProcessCapabilities::HandleMapPhysicalFlags(u32 flags, u32 size_flags,
VMManager& vm_manager) {
// TODO(Lioncache): Implement once the memory manager can handle this.
return RESULT_SUCCESS;
}
ResultCode ProcessCapabilities::HandleMapIOFlags(u32 flags, VMManager& vm_manager) {
// TODO(Lioncache): Implement once the memory manager can handle this.
return RESULT_SUCCESS;
}
ResultCode ProcessCapabilities::HandleInterruptFlags(u32 flags) {
constexpr u32 interrupt_ignore_value = 0x3FF;
const u32 interrupt0 = (flags >> 12) & 0x3FF;
const u32 interrupt1 = (flags >> 22) & 0x3FF;
for (u32 interrupt : {interrupt0, interrupt1}) {
if (interrupt == interrupt_ignore_value) {
continue;
}
// NOTE:
// This should be checking a generic interrupt controller value
// as part of the calculation, however, given we don't currently
// emulate that, it's sufficient to mark every interrupt as defined.
if (interrupt >= interrupt_capabilities.size()) {
return ERR_OUT_OF_RANGE;
}
interrupt_capabilities[interrupt] = true;
}
return RESULT_SUCCESS;
}
ResultCode ProcessCapabilities::HandleProgramTypeFlags(u32 flags) {
const u32 reserved = flags >> 17;
if (reserved != 0) {
return ERR_RESERVED_VALUE;
}
program_type = static_cast<ProgramType>((flags >> 14) & 0b111);
return RESULT_SUCCESS;
}
ResultCode ProcessCapabilities::HandleKernelVersionFlags(u32 flags) {
// Yes, the internal member variable is checked in the actual kernel here.
// This might look odd for options that are only allowed to be initialized
// just once, however the kernel has a separate initialization function for
// kernel processes and userland processes. The kernel variant sets this
// member variable ahead of time.
const u32 major_version = kernel_version >> 19;
if (major_version != 0 || flags < 0x80000) {
return ERR_INVALID_CAPABILITY_DESCRIPTOR;
}
kernel_version = flags;
return RESULT_SUCCESS;
}
ResultCode ProcessCapabilities::HandleHandleTableFlags(u32 flags) {
const u32 reserved = flags >> 26;
if (reserved != 0) {
return ERR_RESERVED_VALUE;
}
handle_table_size = (flags >> 16) & 0x3FF;
return RESULT_SUCCESS;
}
ResultCode ProcessCapabilities::HandleDebugFlags(u32 flags) {
const u32 reserved = flags >> 19;
if (reserved != 0) {
return ERR_RESERVED_VALUE;
}
is_debuggable = (flags & 0x20000) != 0;
can_force_debug = (flags & 0x40000) != 0;
return RESULT_SUCCESS;
}
} // namespace Kernel

View File

@@ -0,0 +1,264 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <bitset>
#include "common/common_types.h"
union ResultCode;
namespace Kernel {
class VMManager;
/// The possible types of programs that may be indicated
/// by the program type capability descriptor.
enum class ProgramType {
SysModule,
Application,
Applet,
};
/// Handles kernel capability descriptors that are provided by
/// application metadata. These descriptors provide information
/// that alters certain parameters for kernel process instance
/// that will run said application (or applet).
///
/// Capabilities are a sequence of flag descriptors, that indicate various
/// configurations and constraints for a particular process.
///
/// Flag types are indicated by a sequence of set low bits. E.g. the
/// types are indicated with the low bits as follows (where x indicates "don't care"):
///
/// - Priority and core mask : 0bxxxxxxxxxxxx0111
/// - Allowed service call mask: 0bxxxxxxxxxxx01111
/// - Map physical memory : 0bxxxxxxxxx0111111
/// - Map IO memory : 0bxxxxxxxx01111111
/// - Interrupts : 0bxxxx011111111111
/// - Application type : 0bxx01111111111111
/// - Kernel version : 0bx011111111111111
/// - Handle table size : 0b0111111111111111
/// - Debugger flags : 0b1111111111111111
///
/// These are essentially a bit offset subtracted by 1 to create a mask.
/// e.g. The first entry in the above list is simply bit 3 (value 8 -> 0b1000)
/// subtracted by one (7 -> 0b0111)
///
/// An example of a bit layout (using the map physical layout):
/// <example>
/// The MapPhysical type indicates a sequence entry pair of:
///
/// [initial, memory_flags], where:
///
/// initial:
/// bits:
/// 7-24: Starting page to map memory at.
/// 25 : Indicates if the memory should be mapped as read only.
///
/// memory_flags:
/// bits:
/// 7-20 : Number of pages to map
/// 21-25: Seems to be reserved (still checked against though)
/// 26 : Whether or not the memory being mapped is IO memory, or physical memory
/// </example>
///
class ProcessCapabilities {
public:
using InterruptCapabilities = std::bitset<1024>;
using SyscallCapabilities = std::bitset<128>;
ProcessCapabilities() = default;
ProcessCapabilities(const ProcessCapabilities&) = delete;
ProcessCapabilities(ProcessCapabilities&&) = default;
ProcessCapabilities& operator=(const ProcessCapabilities&) = delete;
ProcessCapabilities& operator=(ProcessCapabilities&&) = default;
/// Initializes this process capabilities instance for a kernel process.
///
/// @param capabilities The capabilities to parse
/// @param num_capabilities The number of capabilities to parse.
/// @param vm_manager The memory manager to use for handling any mapping-related
/// operations (such as mapping IO memory, etc).
///
/// @returns RESULT_SUCCESS if this capabilities instance was able to be initialized,
/// otherwise, an error code upon failure.
///
ResultCode InitializeForKernelProcess(const u32* capabilities, std::size_t num_capabilities,
VMManager& vm_manager);
/// Initializes this process capabilities instance for a userland process.
///
/// @param capabilities The capabilities to parse.
/// @param num_capabilities The total number of capabilities to parse.
/// @param vm_manager The memory manager to use for handling any mapping-related
/// operations (such as mapping IO memory, etc).
///
/// @returns RESULT_SUCCESS if this capabilities instance was able to be initialized,
/// otherwise, an error code upon failure.
///
ResultCode InitializeForUserProcess(const u32* capabilities, std::size_t num_capabilities,
VMManager& vm_manager);
/// Initializes this process capabilities instance for a process that does not
/// have any metadata to parse.
///
/// This is necessary, as we allow running raw executables, and the internal
/// kernel process capabilities also determine what CPU cores the process is
/// allowed to run on, and what priorities are allowed for threads. It also
/// determines the max handle table size, what the program type is, whether or
/// not the process can be debugged, or whether it's possible for a process to
/// forcibly debug another process.
///
/// Given the above, this essentially enables all capabilities across the board
/// for the process. It allows the process to:
///
/// - Run on any core
/// - Use any thread priority
/// - Use the maximum amount of handles a process is allowed to.
/// - Be debuggable
/// - Forcibly debug other processes.
///
/// Note that this is not a behavior that the kernel allows a process to do via
/// a single function like this. This is yuzu-specific behavior to handle
/// executables with no capability descriptors whatsoever to derive behavior from.
/// It being yuzu-specific is why this is also not the default behavior and not
/// done by default in the constructor.
///
void InitializeForMetadatalessProcess();
/// Gets the allowable core mask
u64 GetCoreMask() const {
return core_mask;
}
/// Gets the allowable priority mask
u64 GetPriorityMask() const {
return priority_mask;
}
/// Gets the SVC access permission bits
const SyscallCapabilities& GetServiceCapabilities() const {
return svc_capabilities;
}
/// Gets the valid interrupt bits.
const InterruptCapabilities& GetInterruptCapabilities() const {
return interrupt_capabilities;
}
/// Gets the program type for this process.
ProgramType GetProgramType() const {
return program_type;
}
/// Gets the number of total allowable handles for the process' handle table.
u32 GetHandleTableSize() const {
return handle_table_size;
}
/// Gets the kernel version value.
u32 GetKernelVersion() const {
return kernel_version;
}
/// Whether or not this process can be debugged.
bool IsDebuggable() const {
return is_debuggable;
}
/// Whether or not this process can forcibly debug another
/// process, even if that process is not considered debuggable.
bool CanForceDebug() const {
return can_force_debug;
}
private:
/// Attempts to parse a given sequence of capability descriptors.
///
/// @param capabilities The sequence of capability descriptors to parse.
/// @param num_capabilities The number of descriptors within the given sequence.
/// @param vm_manager The memory manager that will perform any memory
/// mapping if necessary.
///
/// @return RESULT_SUCCESS if no errors occur, otherwise an error code.
///
ResultCode ParseCapabilities(const u32* capabilities, std::size_t num_capabilities,
VMManager& vm_manager);
/// Attempts to parse a capability descriptor that is only represented by a
/// single flag set.
///
/// @param set_flags Running set of flags that are used to catch
/// flags being initialized more than once when they shouldn't be.
/// @param set_svc_bits Running set of bits representing the allowed supervisor calls mask.
/// @param flag The flag to attempt to parse.
/// @param vm_manager The memory manager that will perform any memory
/// mapping if necessary.
///
/// @return RESULT_SUCCESS if no errors occurred, otherwise an error code.
///
ResultCode ParseSingleFlagCapability(u32& set_flags, u32& set_svc_bits, u32 flag,
VMManager& vm_manager);
/// Clears the internal state of this process capability instance. Necessary,
/// to have a sane starting point due to us allowing running executables without
/// configuration metadata. We assume a process is not going to have metadata,
/// and if it turns out that the process does, in fact, have metadata, then
/// we attempt to parse it. Thus, we need this to reset data members back to
/// a good state.
///
/// DO NOT ever make this a public member function. This isn't an invariant
/// anything external should depend upon (and if anything comes to rely on it,
/// you should immediately be questioning the design of that thing, not this
/// class. If the kernel itself can run without depending on behavior like that,
/// then so can yuzu).
///
void Clear();
/// Handles flags related to the priority and core number capability flags.
ResultCode HandlePriorityCoreNumFlags(u32 flags);
/// Handles flags related to determining the allowable SVC mask.
ResultCode HandleSyscallFlags(u32& set_svc_bits, u32 flags);
/// Handles flags related to mapping physical memory pages.
ResultCode HandleMapPhysicalFlags(u32 flags, u32 size_flags, VMManager& vm_manager);
/// Handles flags related to mapping IO pages.
ResultCode HandleMapIOFlags(u32 flags, VMManager& vm_manager);
/// Handles flags related to the interrupt capability flags.
ResultCode HandleInterruptFlags(u32 flags);
/// Handles flags related to the program type.
ResultCode HandleProgramTypeFlags(u32 flags);
/// Handles flags related to the handle table size.
ResultCode HandleHandleTableFlags(u32 flags);
/// Handles flags related to the kernel version capability flags.
ResultCode HandleKernelVersionFlags(u32 flags);
/// Handles flags related to debug-specific capabilities.
ResultCode HandleDebugFlags(u32 flags);
SyscallCapabilities svc_capabilities;
InterruptCapabilities interrupt_capabilities;
u64 core_mask = 0;
u64 priority_mask = 0;
u32 handle_table_size = 0;
u32 kernel_version = 0;
ProgramType program_type = ProgramType::SysModule;
bool is_debuggable = false;
bool can_force_debug = false;
};
} // namespace Kernel

View File

@@ -46,9 +46,6 @@ ResultCode ReadableEvent::Reset() {
void ReadableEvent::WakeupAllWaitingThreads() {
WaitObject::WakeupAllWaitingThreads();
if (reset_type == ResetType::Pulse)
signaled = false;
}
} // namespace Kernel

View File

@@ -6,6 +6,7 @@
#include <utility>
#include "common/assert.h"
#include "common/common_types.h"
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hle/ipc_helpers.h"

View File

@@ -8,7 +8,6 @@
#include <string>
#include <vector>
#include "common/common_types.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/wait_object.h"
#include "core/hle/result.h"

View File

@@ -190,10 +190,16 @@ static ResultCode SetHeapSize(VAddr* heap_addr, u64 heap_size) {
return ERR_INVALID_SIZE;
}
auto& process = *Core::CurrentProcess();
const VAddr heap_base = process.VMManager().GetHeapRegionBaseAddress();
CASCADE_RESULT(*heap_addr,
process.HeapAllocate(heap_base, heap_size, VMAPermission::ReadWrite));
auto& vm_manager = Core::CurrentProcess()->VMManager();
const VAddr heap_base = vm_manager.GetHeapRegionBaseAddress();
const auto alloc_result =
vm_manager.HeapAllocate(heap_base, heap_size, VMAPermission::ReadWrite);
if (alloc_result.Failed()) {
return alloc_result.Code();
}
*heap_addr = *alloc_result;
return RESULT_SUCCESS;
}
@@ -307,15 +313,14 @@ static ResultCode MapMemory(VAddr dst_addr, VAddr src_addr, u64 size) {
LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr,
src_addr, size);
auto* const current_process = Core::CurrentProcess();
const auto& vm_manager = current_process->VMManager();
auto& vm_manager = Core::CurrentProcess()->VMManager();
const auto result = MapUnmapMemorySanityChecks(vm_manager, dst_addr, src_addr, size);
if (result != RESULT_SUCCESS) {
if (result.IsError()) {
return result;
}
return current_process->MirrorMemory(dst_addr, src_addr, size, MemoryState::Stack);
return vm_manager.MirrorMemory(dst_addr, src_addr, size, MemoryState::Stack);
}
/// Unmaps a region that was previously mapped with svcMapMemory
@@ -323,15 +328,14 @@ static ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size) {
LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr,
src_addr, size);
auto* const current_process = Core::CurrentProcess();
const auto& vm_manager = current_process->VMManager();
auto& vm_manager = Core::CurrentProcess()->VMManager();
const auto result = MapUnmapMemorySanityChecks(vm_manager, dst_addr, src_addr, size);
if (result != RESULT_SUCCESS) {
if (result.IsError()) {
return result;
}
return current_process->UnmapMemory(dst_addr, src_addr, size);
return vm_manager.UnmapRange(dst_addr, size);
}
/// Connect to an OS service given the port name, returns the handle to the port to out
@@ -680,6 +684,9 @@ static void Break(u32 reason, u64 info1, u64 info2) {
"Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}",
reason, info1, info2);
handle_debug_buffer(info1, info2);
Core::System::GetInstance()
.ArmInterface(static_cast<std::size_t>(GetCurrentThread()->GetProcessorID()))
.LogBacktrace();
ASSERT(false);
Core::CurrentProcess()->PrepareForTermination();
@@ -708,8 +715,8 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
enum class GetInfoType : u64 {
// 1.0.0+
AllowedCpuIdBitmask = 0,
AllowedThreadPrioBitmask = 1,
AllowedCPUCoreMask = 0,
AllowedThreadPriorityMask = 1,
MapRegionBaseAddr = 2,
MapRegionSize = 3,
HeapRegionBaseAddr = 4,
@@ -740,8 +747,8 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
const auto info_id_type = static_cast<GetInfoType>(info_id);
switch (info_id_type) {
case GetInfoType::AllowedCpuIdBitmask:
case GetInfoType::AllowedThreadPrioBitmask:
case GetInfoType::AllowedCPUCoreMask:
case GetInfoType::AllowedThreadPriorityMask:
case GetInfoType::MapRegionBaseAddr:
case GetInfoType::MapRegionSize:
case GetInfoType::HeapRegionBaseAddr:
@@ -767,12 +774,12 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
}
switch (info_id_type) {
case GetInfoType::AllowedCpuIdBitmask:
*result = process->GetAllowedProcessorMask();
case GetInfoType::AllowedCPUCoreMask:
*result = process->GetCoreMask();
return RESULT_SUCCESS;
case GetInfoType::AllowedThreadPrioBitmask:
*result = process->GetAllowedThreadPriorityMask();
case GetInfoType::AllowedThreadPriorityMask:
*result = process->GetPriorityMask();
return RESULT_SUCCESS;
case GetInfoType::MapRegionBaseAddr:
@@ -932,8 +939,35 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
}
/// Sets the thread activity
static ResultCode SetThreadActivity(Handle handle, u32 unknown) {
LOG_WARNING(Kernel_SVC, "(STUBBED) called, handle=0x{:08X}, unknown=0x{:08X}", handle, unknown);
static ResultCode SetThreadActivity(Handle handle, u32 activity) {
LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, activity=0x{:08X}", handle, activity);
if (activity > static_cast<u32>(ThreadActivity::Paused)) {
return ERR_INVALID_ENUM_VALUE;
}
const auto* current_process = Core::CurrentProcess();
const SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle);
if (!thread) {
LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", handle);
return ERR_INVALID_HANDLE;
}
if (thread->GetOwnerProcess() != current_process) {
LOG_ERROR(Kernel_SVC,
"The current process does not own the current thread, thread_handle={:08X} "
"thread_pid={}, "
"current_process_pid={}",
handle, thread->GetOwnerProcess()->GetProcessID(),
current_process->GetProcessID());
return ERR_INVALID_HANDLE;
}
if (thread == GetCurrentThread()) {
LOG_ERROR(Kernel_SVC, "The thread handle specified is the current running thread");
return ERR_BUSY;
}
thread->SetActivity(static_cast<ThreadActivity>(activity));
return RESULT_SUCCESS;
}
@@ -960,7 +994,7 @@ static ResultCode GetThreadContext(VAddr thread_context, Handle handle) {
if (thread == GetCurrentThread()) {
LOG_ERROR(Kernel_SVC, "The thread handle specified is the current running thread");
return ERR_ALREADY_REGISTERED;
return ERR_BUSY;
}
Core::ARM_Interface::ThreadContext ctx = thread->GetContext();
@@ -1185,29 +1219,35 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V
"threadpriority=0x{:08X}, processorid=0x{:08X} : created handle=0x{:08X}",
entry_point, arg, stack_top, priority, processor_id, *out_handle);
auto* const current_process = Core::CurrentProcess();
if (processor_id == THREADPROCESSORID_IDEAL) {
// Set the target CPU to the one specified by the process.
processor_id = current_process->GetIdealCore();
ASSERT(processor_id != THREADPROCESSORID_IDEAL);
}
if (processor_id < THREADPROCESSORID_0 || processor_id > THREADPROCESSORID_3) {
LOG_ERROR(Kernel_SVC, "Invalid thread processor ID: {}", processor_id);
return ERR_INVALID_PROCESSOR_ID;
}
const u64 core_mask = current_process->GetCoreMask();
if ((core_mask | (1ULL << processor_id)) != core_mask) {
LOG_ERROR(Kernel_SVC, "Invalid thread core specified ({})", processor_id);
return ERR_INVALID_PROCESSOR_ID;
}
if (priority > THREADPRIO_LOWEST) {
LOG_ERROR(Kernel_SVC, "An invalid priority was specified, expected {} but got {}",
THREADPRIO_LOWEST, priority);
LOG_ERROR(Kernel_SVC,
"Invalid thread priority specified ({}). Must be within the range 0-64",
priority);
return ERR_INVALID_THREAD_PRIORITY;
}
auto* const current_process = Core::CurrentProcess();
if (processor_id == THREADPROCESSORID_DEFAULT) {
// Set the target CPU to the one specified in the process' exheader.
processor_id = current_process->GetDefaultProcessorID();
ASSERT(processor_id != THREADPROCESSORID_DEFAULT);
}
switch (processor_id) {
case THREADPROCESSORID_0:
case THREADPROCESSORID_1:
case THREADPROCESSORID_2:
case THREADPROCESSORID_3:
break;
default:
LOG_ERROR(Kernel_SVC, "Invalid thread processor ID: {}", processor_id);
return ERR_INVALID_PROCESSOR_ID;
if (((1ULL << priority) & current_process->GetPriorityMask()) == 0) {
LOG_ERROR(Kernel_SVC, "Invalid thread priority specified ({})", priority);
return ERR_INVALID_THREAD_PRIORITY;
}
const std::string name = fmt::format("thread-{:X}", entry_point);
@@ -1245,7 +1285,10 @@ static ResultCode StartThread(Handle thread_handle) {
ASSERT(thread->GetStatus() == ThreadStatus::Dormant);
thread->ResumeFromWait();
Core::System::GetInstance().CpuCore(thread->GetProcessorID()).PrepareReschedule();
if (thread->GetStatus() == ThreadStatus::Ready) {
Core::System::GetInstance().CpuCore(thread->GetProcessorID()).PrepareReschedule();
}
return RESULT_SUCCESS;
}
@@ -1602,13 +1645,13 @@ static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) {
return ERR_INVALID_HANDLE;
}
if (core == static_cast<u32>(THREADPROCESSORID_DEFAULT)) {
const u8 default_processor_id = thread->GetOwnerProcess()->GetDefaultProcessorID();
if (core == static_cast<u32>(THREADPROCESSORID_IDEAL)) {
const u8 ideal_cpu_core = thread->GetOwnerProcess()->GetIdealCore();
ASSERT(default_processor_id != static_cast<u8>(THREADPROCESSORID_DEFAULT));
ASSERT(ideal_cpu_core != static_cast<u8>(THREADPROCESSORID_IDEAL));
// Set the target CPU to the one specified in the process' exheader.
core = default_processor_id;
// Set the target CPU to the ideal core specified by the process.
core = ideal_cpu_core;
mask = 1ULL << core;
}

View File

@@ -12,7 +12,6 @@
#include "common/assert.h"
#include "common/common_types.h"
#include "common/logging/log.h"
#include "common/math_util.h"
#include "common/thread_queue_list.h"
#include "core/arm/arm_interface.h"
#include "core/core.h"
@@ -50,7 +49,7 @@ void Thread::Stop() {
// Clean up thread from ready queue
// This is only needed when the thread is terminated forcefully (SVC TerminateProcess)
if (status == ThreadStatus::Ready) {
if (status == ThreadStatus::Ready || status == ThreadStatus::Paused) {
scheduler->UnscheduleThread(this, current_priority);
}
@@ -140,6 +139,11 @@ void Thread::ResumeFromWait() {
wakeup_callback = nullptr;
if (activity == ThreadActivity::Paused) {
status = ThreadStatus::Paused;
return;
}
status = ThreadStatus::Ready;
ChangeScheduler();
@@ -227,29 +231,6 @@ void Thread::BoostPriority(u32 priority) {
current_priority = priority;
}
SharedPtr<Thread> SetupMainThread(KernelCore& kernel, VAddr entry_point, u32 priority,
Process& owner_process) {
// Setup page table so we can write to memory
SetCurrentPageTable(&owner_process.VMManager().page_table);
// Initialize new "main" thread
const VAddr stack_top = owner_process.VMManager().GetTLSIORegionEndAddress();
auto thread_res = Thread::Create(kernel, "main", entry_point, priority, 0, THREADPROCESSORID_0,
stack_top, owner_process);
SharedPtr<Thread> thread = std::move(thread_res).Unwrap();
// Register 1 must be a handle to the main thread
const Handle guest_handle = owner_process.GetHandleTable().Create(thread).Unwrap();
thread->SetGuestHandle(guest_handle);
thread->GetContext().cpu_registers[1] = guest_handle;
// Threads by default are dormant, wake up the main thread so it runs when the scheduler fires
thread->ResumeFromWait();
return thread;
}
void Thread::SetWaitSynchronizationResult(ResultCode result) {
context.cpu_registers[0] = result.raw;
}
@@ -391,6 +372,23 @@ bool Thread::InvokeWakeupCallback(ThreadWakeupReason reason, SharedPtr<Thread> t
return wakeup_callback(reason, std::move(thread), std::move(object), index);
}
void Thread::SetActivity(ThreadActivity value) {
activity = value;
if (value == ThreadActivity::Paused) {
// Set status if not waiting
if (status == ThreadStatus::Ready) {
status = ThreadStatus::Paused;
} else if (status == ThreadStatus::Running) {
status = ThreadStatus::Paused;
Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule();
}
} else if (status == ThreadStatus::Paused) {
// Ready to reschedule
ResumeFromWait();
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
/**

View File

@@ -30,12 +30,12 @@ enum ThreadPriority : u32 {
};
enum ThreadProcessorId : s32 {
THREADPROCESSORID_DEFAULT = -2, ///< Run thread on default core specified by exheader
THREADPROCESSORID_0 = 0, ///< Run thread on core 0
THREADPROCESSORID_1 = 1, ///< Run thread on core 1
THREADPROCESSORID_2 = 2, ///< Run thread on core 2
THREADPROCESSORID_3 = 3, ///< Run thread on core 3
THREADPROCESSORID_MAX = 4, ///< Processor ID must be less than this
THREADPROCESSORID_IDEAL = -2, ///< Run thread on the ideal core specified by the process.
THREADPROCESSORID_0 = 0, ///< Run thread on core 0
THREADPROCESSORID_1 = 1, ///< Run thread on core 1
THREADPROCESSORID_2 = 2, ///< Run thread on core 2
THREADPROCESSORID_3 = 3, ///< Run thread on core 3
THREADPROCESSORID_MAX = 4, ///< Processor ID must be less than this
/// Allowed CPU mask
THREADPROCESSORID_DEFAULT_MASK = (1 << THREADPROCESSORID_0) | (1 << THREADPROCESSORID_1) |
@@ -45,6 +45,7 @@ enum ThreadProcessorId : s32 {
enum class ThreadStatus {
Running, ///< Currently running
Ready, ///< Ready to run
Paused, ///< Paused by SetThreadActivity or debug
WaitHLEEvent, ///< Waiting for hle event to finish
WaitSleep, ///< Waiting due to a SleepThread SVC
WaitIPC, ///< Waiting for the reply from an IPC request
@@ -61,6 +62,11 @@ enum class ThreadWakeupReason {
Timeout // The thread was woken up due to a wait timeout.
};
enum class ThreadActivity : u32 {
Normal = 0,
Paused = 1,
};
class Thread final : public WaitObject {
public:
using TLSMemory = std::vector<u8>;
@@ -371,6 +377,12 @@ public:
return affinity_mask;
}
ThreadActivity GetActivity() const {
return activity;
}
void SetActivity(ThreadActivity value);
private:
explicit Thread(KernelCore& kernel);
~Thread() override;
@@ -439,18 +451,9 @@ private:
TLSMemoryPtr tls_memory = std::make_shared<TLSMemory>();
std::string name;
};
/**
* Sets up the primary application thread
* @param kernel The kernel instance to create the main thread under.
* @param entry_point The address at which the thread should start execution
* @param priority The priority to give the main thread
* @param owner_process The parent process for the main thread
* @return A shared pointer to the main thread
*/
SharedPtr<Thread> SetupMainThread(KernelCore& kernel, VAddr entry_point, u32 priority,
Process& owner_process);
ThreadActivity activity = ThreadActivity::Normal;
};
/**
* Gets the current thread

View File

@@ -68,9 +68,6 @@ void Timer::Clear() {
void Timer::WakeupAllWaitingThreads() {
WaitObject::WakeupAllWaitingThreads();
if (reset_type == ResetType::Pulse)
signaled = false;
}
void Timer::Signal(int cycles_late) {

View File

@@ -190,6 +190,7 @@ VMManager::VMAIter VMManager::Unmap(VMAIter vma_handle) {
vma.type = VMAType::Free;
vma.permissions = VMAPermission::None;
vma.state = MemoryState::Unmapped;
vma.attribute = MemoryAttribute::None;
vma.backing_block = nullptr;
vma.offset = 0;

View File

@@ -4,11 +4,11 @@
#include <algorithm>
#include "common/assert.h"
#include "common/common_types.h"
#include "common/logging/log.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/kernel/timer.h"
namespace Kernel {

View File

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

View File

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

View File

@@ -8,6 +8,7 @@
#include <stack>
#include "audio_core/audio_renderer.h"
#include "core/core.h"
#include "core/file_sys/savedata_factory.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
@@ -22,6 +23,7 @@
#include "core/hle/service/am/applets/profile_select.h"
#include "core/hle/service/am/applets/software_keyboard.h"
#include "core/hle/service/am/applets/stub_applet.h"
#include "core/hle/service/am/applets/web_browser.h"
#include "core/hle/service/am/idle.h"
#include "core/hle/service/am/omm.h"
#include "core/hle/service/am/spsm.h"
@@ -37,11 +39,13 @@
namespace Service::AM {
constexpr ResultCode ERR_NO_DATA_IN_CHANNEL{ErrorModule::AM, 0x2};
constexpr ResultCode ERR_NO_MESSAGES{ErrorModule::AM, 0x3};
constexpr ResultCode ERR_SIZE_OUT_OF_BOUNDS{ErrorModule::AM, 0x1F7};
enum class AppletId : u32 {
ProfileSelect = 0x10,
SoftwareKeyboard = 0x11,
LibAppletOff = 0x17,
};
constexpr u32 POP_LAUNCH_PARAMETER_MAGIC = 0xC79497CA;
@@ -459,9 +463,17 @@ void ICommonStateGetter::GetEventHandle(Kernel::HLERequestContext& ctx) {
void ICommonStateGetter::ReceiveMessage(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
const auto message = msg_queue->PopMessage();
IPC::ResponseBuilder rb{ctx, 3};
if (message == AppletMessageQueue::AppletMessage::NoMessage) {
LOG_ERROR(Service_AM, "Message queue is empty");
rb.Push(ERR_NO_MESSAGES);
rb.PushEnum<AppletMessageQueue::AppletMessage>(message);
return;
}
rb.Push(RESULT_SUCCESS);
rb.PushEnum<AppletMessageQueue::AppletMessage>(msg_queue->PopMessage());
rb.PushEnum<AppletMessageQueue::AppletMessage>(message);
}
void ICommonStateGetter::GetCurrentFocusState(Kernel::HLERequestContext& ctx) {
@@ -720,10 +732,10 @@ void IStorageAccessor::Write(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const u64 offset{rp.Pop<u64>()};
LOG_DEBUG(Service_AM, "called, offset={}", offset);
const std::vector<u8> data{ctx.ReadBuffer()};
LOG_DEBUG(Service_AM, "called, offset={}, size={}", offset, data.size());
if (data.size() > backing.buffer.size() - offset) {
LOG_ERROR(Service_AM,
"offset is out of bounds, backing_buffer_sz={}, data_size={}, offset={}",
@@ -743,10 +755,10 @@ void IStorageAccessor::Read(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const u64 offset{rp.Pop<u64>()};
LOG_DEBUG(Service_AM, "called, offset={}", offset);
const std::size_t size{ctx.GetWriteBufferSize()};
LOG_DEBUG(Service_AM, "called, offset={}, size={}", offset, size);
if (size > backing.buffer.size() - offset) {
LOG_ERROR(Service_AM, "offset is out of bounds, backing_buffer_sz={}, size={}, offset={}",
backing.buffer.size(), size, offset);
@@ -781,6 +793,8 @@ static std::shared_ptr<Applets::Applet> GetAppletFromId(AppletId id) {
return std::make_shared<Applets::ProfileSelect>();
case AppletId::SoftwareKeyboard:
return std::make_shared<Applets::SoftwareKeyboard>();
case AppletId::LibAppletOff:
return std::make_shared<Applets::WebBrowser>();
default:
LOG_ERROR(Service_AM, "Unimplemented AppletId [{:08X}]! -- Falling back to stub!",
static_cast<u32>(id));
@@ -865,8 +879,8 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF
{22, &IApplicationFunctions::SetTerminateResult, "SetTerminateResult"},
{23, &IApplicationFunctions::GetDisplayVersion, "GetDisplayVersion"},
{24, nullptr, "GetLaunchStorageInfoForDebug"},
{25, nullptr, "ExtendSaveData"},
{26, nullptr, "GetSaveDataSize"},
{25, &IApplicationFunctions::ExtendSaveData, "ExtendSaveData"},
{26, &IApplicationFunctions::GetSaveDataSize, "GetSaveDataSize"},
{30, &IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed, "BeginBlockingHomeButtonShortAndLongPressed"},
{31, &IApplicationFunctions::EndBlockingHomeButtonShortAndLongPressed, "EndBlockingHomeButtonShortAndLongPressed"},
{32, &IApplicationFunctions::BeginBlockingHomeButton, "BeginBlockingHomeButton"},
@@ -1043,6 +1057,48 @@ void IApplicationFunctions::GetPseudoDeviceId(Kernel::HLERequestContext& ctx) {
rb.Push<u64>(0);
}
void IApplicationFunctions::ExtendSaveData(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto type{rp.PopRaw<FileSys::SaveDataType>()};
rp.Skip(1, false);
const auto user_id{rp.PopRaw<u128>()};
const auto new_normal_size{rp.PopRaw<u64>()};
const auto new_journal_size{rp.PopRaw<u64>()};
LOG_DEBUG(Service_AM,
"called with type={:02X}, user_id={:016X}{:016X}, new_normal={:016X}, "
"new_journal={:016X}",
static_cast<u8>(type), user_id[1], user_id[0], new_normal_size, new_journal_size);
FileSystem::WriteSaveDataSize(type, Core::CurrentProcess()->GetTitleID(), user_id,
{new_normal_size, new_journal_size});
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
// The following value is used upon failure to help the system recover.
// Since we always succeed, this should be 0.
rb.Push<u64>(0);
}
void IApplicationFunctions::GetSaveDataSize(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto type{rp.PopRaw<FileSys::SaveDataType>()};
rp.Skip(1, false);
const auto user_id{rp.PopRaw<u128>()};
LOG_DEBUG(Service_AM, "called with type={:02X}, user_id={:016X}{:016X}", static_cast<u8>(type),
user_id[1], user_id[0]);
const auto size =
FileSystem::ReadSaveDataSize(type, Core::CurrentProcess()->GetTitleID(), user_id);
IPC::ResponseBuilder rb{ctx, 6};
rb.Push(RESULT_SUCCESS);
rb.Push(size.normal);
rb.Push(size.journal);
}
void InstallInterfaces(SM::ServiceManager& service_manager,
std::shared_ptr<NVFlinger::NVFlinger> nvflinger) {
auto message_queue = std::make_shared<AppletMessageQueue>();

View File

@@ -206,6 +206,8 @@ private:
void SetGamePlayRecordingState(Kernel::HLERequestContext& ctx);
void NotifyRunning(Kernel::HLERequestContext& ctx);
void GetPseudoDeviceId(Kernel::HLERequestContext& ctx);
void ExtendSaveData(Kernel::HLERequestContext& ctx);
void GetSaveDataSize(Kernel::HLERequestContext& ctx);
void BeginBlockingHomeButtonShortAndLongPressed(Kernel::HLERequestContext& ctx);
void EndBlockingHomeButtonShortAndLongPressed(Kernel::HLERequestContext& ctx);
void BeginBlockingHomeButton(Kernel::HLERequestContext& ctx);

View File

@@ -6,7 +6,7 @@
#include "common/assert.h"
#include "core/core.h"
#include "core/hle/kernel/readable_event.h"
#include "core/hle/kernel/server_port.h"
#include "core/hle/kernel/server_session.h"
#include "core/hle/kernel/writable_event.h"
#include "core/hle/service/am/am.h"
#include "core/hle/service/am/applets/applets.h"

View File

@@ -7,7 +7,7 @@
#include <memory>
#include <queue>
#include "common/swap.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/writable_event.h"
union ResultCode;

View File

@@ -7,7 +7,7 @@
#include "common/assert.h"
#include "common/string_util.h"
#include "core/core.h"
#include "core/frontend/applets/software_keyboard.h"
#include "core/frontend/applets/profile_select.h"
#include "core/hle/service/am/am.h"
#include "core/hle/service/am/applets/profile_select.h"

View File

@@ -0,0 +1,190 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <array>
#include <cstring>
#include <vector>
#include "common/assert.h"
#include "common/common_funcs.h"
#include "common/common_paths.h"
#include "common/file_util.h"
#include "common/hex_util.h"
#include "common/logging/log.h"
#include "common/string_util.h"
#include "core/core.h"
#include "core/file_sys/content_archive.h"
#include "core/file_sys/mode.h"
#include "core/file_sys/nca_metadata.h"
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/romfs.h"
#include "core/file_sys/vfs_types.h"
#include "core/frontend/applets/web_browser.h"
#include "core/hle/kernel/process.h"
#include "core/hle/service/am/applets/web_browser.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/loader/loader.h"
namespace Service::AM::Applets {
// TODO(DarkLordZach): There are other arguments in the WebBuffer structure that are currently not
// parsed, for example footer mode and left stick mode. Some of these are not particularly relevant,
// but some may be worth an implementation.
constexpr u16 WEB_ARGUMENT_URL_TYPE = 0x6;
struct WebBufferHeader {
u16 count;
INSERT_PADDING_BYTES(6);
};
static_assert(sizeof(WebBufferHeader) == 0x8, "WebBufferHeader has incorrect size.");
struct WebArgumentHeader {
u16 type;
u16 size;
u32 offset;
};
static_assert(sizeof(WebArgumentHeader) == 0x8, "WebArgumentHeader has incorrect size.");
struct WebArgumentResult {
u32 result_code;
std::array<char, 0x1000> last_url;
u64 last_url_size;
};
static_assert(sizeof(WebArgumentResult) == 0x1010, "WebArgumentResult has incorrect size.");
static std::vector<u8> GetArgumentDataForTagType(const std::vector<u8>& data, u16 type) {
WebBufferHeader header;
ASSERT(sizeof(WebBufferHeader) <= data.size());
std::memcpy(&header, data.data(), sizeof(WebBufferHeader));
u64 offset = sizeof(WebBufferHeader);
for (u16 i = 0; i < header.count; ++i) {
WebArgumentHeader arg;
ASSERT(offset + sizeof(WebArgumentHeader) <= data.size());
std::memcpy(&arg, data.data() + offset, sizeof(WebArgumentHeader));
offset += sizeof(WebArgumentHeader);
if (arg.type == type) {
std::vector<u8> out(arg.size);
offset += arg.offset;
ASSERT(offset + arg.size <= data.size());
std::memcpy(out.data(), data.data() + offset, out.size());
return out;
}
offset += arg.offset + arg.size;
}
return {};
}
static FileSys::VirtualFile GetManualRomFS() {
auto& loader{Core::System::GetInstance().GetAppLoader()};
FileSys::VirtualFile out;
if (loader.ReadManualRomFS(out) == Loader::ResultStatus::Success)
return out;
const auto& installed{FileSystem::GetUnionContents()};
const auto res = installed.GetEntry(Core::System::GetInstance().CurrentProcess()->GetTitleID(),
FileSys::ContentRecordType::Manual);
if (res != nullptr)
return res->GetRomFS();
return nullptr;
}
WebBrowser::WebBrowser() = default;
WebBrowser::~WebBrowser() = default;
void WebBrowser::Initialize() {
Applet::Initialize();
complete = false;
temporary_dir.clear();
filename.clear();
status = RESULT_SUCCESS;
const auto web_arg_storage = broker.PopNormalDataToApplet();
ASSERT(web_arg_storage != nullptr);
const auto& web_arg = web_arg_storage->GetData();
const auto url_data = GetArgumentDataForTagType(web_arg, WEB_ARGUMENT_URL_TYPE);
filename = Common::StringFromFixedZeroTerminatedBuffer(
reinterpret_cast<const char*>(url_data.data()), url_data.size());
temporary_dir = FileUtil::SanitizePath(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) +
"web_applet_manual",
FileUtil::DirectorySeparator::PlatformDefault);
FileUtil::DeleteDirRecursively(temporary_dir);
manual_romfs = GetManualRomFS();
if (manual_romfs == nullptr) {
status = ResultCode(-1);
LOG_ERROR(Service_AM, "Failed to find manual for current process!");
}
filename =
FileUtil::SanitizePath(temporary_dir + DIR_SEP + "html-document" + DIR_SEP + filename,
FileUtil::DirectorySeparator::PlatformDefault);
}
bool WebBrowser::TransactionComplete() const {
return complete;
}
ResultCode WebBrowser::GetStatus() const {
return status;
}
void WebBrowser::ExecuteInteractive() {
UNIMPLEMENTED_MSG("Unexpected interactive data recieved!");
}
void WebBrowser::Execute() {
if (complete)
return;
if (status != RESULT_SUCCESS) {
complete = true;
return;
}
auto& frontend{Core::System::GetInstance().GetWebBrowser()};
frontend.OpenPage(filename, [this] { UnpackRomFS(); }, [this] { Finalize(); });
}
void WebBrowser::UnpackRomFS() {
if (unpacked)
return;
ASSERT(manual_romfs != nullptr);
const auto dir =
FileSys::ExtractRomFS(manual_romfs, FileSys::RomFSExtractionType::SingleDiscard);
const auto& vfs{Core::System::GetInstance().GetFilesystem()};
const auto temp_dir = vfs->CreateDirectory(temporary_dir, FileSys::Mode::ReadWrite);
FileSys::VfsRawCopyD(dir, temp_dir);
unpacked = true;
}
void WebBrowser::Finalize() {
complete = true;
WebArgumentResult out{};
out.result_code = 0;
out.last_url_size = 0;
std::vector<u8> data(sizeof(WebArgumentResult));
std::memcpy(data.data(), &out, sizeof(WebArgumentResult));
broker.PushNormalDataFromApplet(IStorage{data});
broker.SignalStateChanged();
FileUtil::DeleteDirRecursively(temporary_dir);
}
} // namespace Service::AM::Applets

View File

@@ -0,0 +1,44 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/file_sys/vfs_types.h"
#include "core/hle/service/am/am.h"
#include "core/hle/service/am/applets/applets.h"
namespace Service::AM::Applets {
class WebBrowser final : public Applet {
public:
WebBrowser();
~WebBrowser() override;
void Initialize() override;
bool TransactionComplete() const override;
ResultCode GetStatus() const override;
void ExecuteInteractive() override;
void Execute() override;
// Callback to be fired when the frontend needs the manual RomFS unpacked to temporary
// directory. This is a blocking call and may take a while as some manuals can be up to 100MB in
// size. Attempting to access files at filename before invocation is likely to not work.
void UnpackRomFS();
// Callback to be fired when the frontend is finished browsing. This will delete the temporary
// manual RomFS extracted files, so ensure this is only called at actual finalization.
void Finalize();
private:
bool complete = false;
bool unpacked = false;
ResultCode status = RESULT_SUCCESS;
FileSys::VirtualFile manual_romfs;
std::string temporary_dir;
std::string filename;
};
} // namespace Service::AM::Applets

View File

@@ -111,7 +111,8 @@ static void GenerateErrorReport(ResultCode error_code, const FatalInfo& info) {
}
static void ThrowFatalError(ResultCode error_code, FatalType fatal_type, const FatalInfo& info) {
LOG_ERROR(Service_Fatal, "Threw fatal error type {}", static_cast<u32>(fatal_type));
LOG_ERROR(Service_Fatal, "Threw fatal error type {} with error code 0x{:X}",
static_cast<u32>(fatal_type), error_code.raw);
switch (fatal_type) {
case FatalType::ErrorReportAndScreen:
GenerateErrorReport(error_code, info);

View File

@@ -8,18 +8,23 @@
#include "common/file_util.h"
#include "core/core.h"
#include "core/file_sys/bis_factory.h"
#include "core/file_sys/control_metadata.h"
#include "core/file_sys/errors.h"
#include "core/file_sys/mode.h"
#include "core/file_sys/partition_filesystem.h"
#include "core/file_sys/patch_manager.h"
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/romfs_factory.h"
#include "core/file_sys/savedata_factory.h"
#include "core/file_sys/sdmc_factory.h"
#include "core/file_sys/vfs.h"
#include "core/file_sys/vfs_offset.h"
#include "core/hle/kernel/process.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/filesystem/fsp_ldr.h"
#include "core/hle/service/filesystem/fsp_pr.h"
#include "core/hle/service/filesystem/fsp_srv.h"
#include "core/loader/loader.h"
namespace Service::FileSystem {
@@ -28,6 +33,10 @@ namespace Service::FileSystem {
// TODO(DarkLordZach): Eventually make this configurable in settings.
constexpr u64 EMULATED_SD_REPORTED_SIZE = 32000000000;
// A default size for normal/journal save data size if application control metadata cannot be found.
// This should be large enough to satisfy even the most extreme requirements (~4.2GB)
constexpr u64 SUFFICIENT_SAVE_DATA_SIZE = 0xF0000000;
static FileSys::VirtualDir GetDirectoryRelativeWrapped(FileSys::VirtualDir base,
std::string_view dir_name_) {
std::string dir_name(FileUtil::SanitizePath(dir_name_));
@@ -341,6 +350,44 @@ ResultVal<FileSys::VirtualDir> OpenSDMC() {
return sdmc_factory->Open();
}
FileSys::SaveDataSize ReadSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id) {
if (save_data_factory == nullptr) {
return {0, 0};
}
const auto value = save_data_factory->ReadSaveDataSize(type, title_id, user_id);
if (value.normal == 0 && value.journal == 0) {
FileSys::SaveDataSize new_size{SUFFICIENT_SAVE_DATA_SIZE, SUFFICIENT_SAVE_DATA_SIZE};
FileSys::NACP nacp;
const auto res = Core::System::GetInstance().GetAppLoader().ReadControlData(nacp);
if (res != Loader::ResultStatus::Success) {
FileSys::PatchManager pm{Core::CurrentProcess()->GetTitleID()};
auto [nacp_unique, discard] = pm.GetControlMetadata();
if (nacp_unique != nullptr) {
new_size = {nacp_unique->GetDefaultNormalSaveSize(),
nacp_unique->GetDefaultJournalSaveSize()};
}
} else {
new_size = {nacp.GetDefaultNormalSaveSize(), nacp.GetDefaultJournalSaveSize()};
}
WriteSaveDataSize(type, title_id, user_id, new_size);
return new_size;
}
return value;
}
void WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id,
FileSys::SaveDataSize new_value) {
if (save_data_factory != nullptr)
save_data_factory->WriteSaveDataSize(type, title_id, user_id, new_value);
}
FileSys::RegisteredCacheUnion GetUnionContents() {
return FileSys::RegisteredCacheUnion{
{GetSystemNANDContents(), GetUserNANDContents(), GetSDMCContents()}};

View File

@@ -21,9 +21,11 @@ class SDMCFactory;
enum class ContentRecordType : u8;
enum class Mode : u32;
enum class SaveDataSpaceId : u8;
enum class SaveDataType : u8;
enum class StorageId : u8;
struct SaveDataDescriptor;
struct SaveDataSize;
} // namespace FileSys
namespace Service {
@@ -48,6 +50,10 @@ ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space,
ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space);
ResultVal<FileSys::VirtualDir> OpenSDMC();
FileSys::SaveDataSize ReadSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id);
void WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id,
FileSys::SaveDataSize new_value);
FileSys::RegisteredCacheUnion GetUnionContents();
FileSys::RegisteredCache* GetSystemNANDContents();

View File

@@ -339,52 +339,6 @@ void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) {
npad.pokeball_states.npad[npad.pokeball_states.common.last_entry_index];
auto& libnx_entry = npad.libnx.npad[npad.libnx.common.last_entry_index];
if (hold_type == NpadHoldType::Horizontal) {
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) {
@@ -456,6 +410,8 @@ void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) {
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;
press_state |= static_cast<u32>(pad_state.pad_states.raw);
}
std::memcpy(data + NPAD_OFFSET, shared_memory_entries.data(),
shared_memory_entries.size() * sizeof(NPadEntry));
@@ -682,6 +638,10 @@ void Controller_NPad::ClearAllControllers() {
});
}
u32 Controller_NPad::GetAndResetPressState() {
return std::exchange(press_state, 0);
}
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

@@ -124,6 +124,10 @@ public:
void ConnectAllDisconnectedControllers();
void ClearAllControllers();
// Logical OR for all buttons presses on all controllers
// Specifically for cheat engine and other features.
u32 GetAndResetPressState();
static std::size_t NPadIdToIndex(u32 npad_id);
static u32 IndexToNPad(std::size_t index);
@@ -292,6 +296,8 @@ private:
bool is_connected;
};
u32 press_state{};
NPadType style{};
std::array<NPadEntry, 10> shared_memory_entries{};
std::array<

File diff suppressed because it is too large Load Diff

View File

@@ -4,12 +4,122 @@
#pragma once
#include "controllers/controller_base.h"
#include "core/hle/service/service.h"
namespace CoreTiming {
struct EventType;
}
namespace Kernel {
class SharedMemory;
}
namespace SM {
class ServiceManager;
}
namespace Service::HID {
enum class HidController : std::size_t {
DebugPad,
Touchscreen,
Mouse,
Keyboard,
XPad,
Unknown1,
Unknown2,
Unknown3,
SixAxisSensor,
NPad,
Gesture,
MaxControllers,
};
class IAppletResource final : public ServiceFramework<IAppletResource> {
public:
IAppletResource();
~IAppletResource() override;
void ActivateController(HidController controller);
void DeactivateController(HidController controller);
template <typename T>
T& GetController(HidController controller) {
return static_cast<T&>(*controllers[static_cast<size_t>(controller)]);
}
template <typename T>
const T& GetController(HidController controller) const {
return static_cast<T&>(*controllers[static_cast<size_t>(controller)]);
}
private:
template <typename T>
void MakeController(HidController controller) {
controllers[static_cast<std::size_t>(controller)] = std::make_unique<T>();
}
void GetSharedMemoryHandle(Kernel::HLERequestContext& ctx);
void UpdateControllers(u64 userdata, int cycles_late);
Kernel::SharedPtr<Kernel::SharedMemory> shared_mem;
CoreTiming::EventType* pad_update_event;
std::array<std::unique_ptr<ControllerBase>, static_cast<size_t>(HidController::MaxControllers)>
controllers{};
};
class Hid final : public ServiceFramework<Hid> {
public:
Hid();
~Hid() override;
std::shared_ptr<IAppletResource> GetAppletResource();
private:
void CreateAppletResource(Kernel::HLERequestContext& ctx);
void ActivateXpad(Kernel::HLERequestContext& ctx);
void ActivateDebugPad(Kernel::HLERequestContext& ctx);
void ActivateTouchScreen(Kernel::HLERequestContext& ctx);
void ActivateMouse(Kernel::HLERequestContext& ctx);
void ActivateKeyboard(Kernel::HLERequestContext& ctx);
void ActivateGesture(Kernel::HLERequestContext& ctx);
void ActivateNpadWithRevision(Kernel::HLERequestContext& ctx);
void StartSixAxisSensor(Kernel::HLERequestContext& ctx);
void SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx);
void IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx);
void SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx);
void GetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx);
void SetSupportedNpadIdType(Kernel::HLERequestContext& ctx);
void ActivateNpad(Kernel::HLERequestContext& ctx);
void AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx);
void DisconnectNpad(Kernel::HLERequestContext& ctx);
void GetPlayerLedPattern(Kernel::HLERequestContext& ctx);
void SetNpadJoyHoldType(Kernel::HLERequestContext& ctx);
void GetNpadJoyHoldType(Kernel::HLERequestContext& ctx);
void SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx);
void BeginPermitVibrationSession(Kernel::HLERequestContext& ctx);
void EndPermitVibrationSession(Kernel::HLERequestContext& ctx);
void SendVibrationValue(Kernel::HLERequestContext& ctx);
void SendVibrationValues(Kernel::HLERequestContext& ctx);
void GetActualVibrationValue(Kernel::HLERequestContext& ctx);
void SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx);
void MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx);
void SetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx);
void GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx);
void CreateActiveVibrationDeviceList(Kernel::HLERequestContext& ctx);
void ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx);
void StartConsoleSixAxisSensor(Kernel::HLERequestContext& ctx);
void StopSixAxisSensor(Kernel::HLERequestContext& ctx);
void SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx);
void SetPalmaBoostMode(Kernel::HLERequestContext& ctx);
std::shared_ptr<IAppletResource> applet_resource;
};
/// Reload input devices. Used when input configuration changed
void ReloadInputDevices();

View File

@@ -318,14 +318,18 @@ public:
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);
ASSERT(vm_manager
.MirrorMemory(*map_address, nro_addr, nro_size,
Kernel::MemoryState::ModuleCodeStatic)
.IsSuccess());
ASSERT(vm_manager.UnmapRange(nro_addr, nro_size).IsSuccess());
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);
ASSERT(vm_manager
.MirrorMemory(*map_address + nro_size, bss_addr, bss_size,
Kernel::MemoryState::ModuleCodeStatic)
.IsSuccess());
ASSERT(vm_manager.UnmapRange(bss_addr, bss_size).IsSuccess());
}
vm_manager.ReprotectRange(*map_address, header.text_size,
@@ -380,13 +384,14 @@ public:
return;
}
auto* process = Core::CurrentProcess();
auto& vm_manager = process->VMManager();
auto& vm_manager = Core::CurrentProcess()->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);
ASSERT(vm_manager
.MirrorMemory(heap_addr, mapped_addr, nro_size,
Kernel::MemoryState::ModuleCodeStatic)
.IsSuccess());
ASSERT(vm_manager.UnmapRange(mapped_addr, nro_size).IsSuccess());
Core::System::GetInstance().InvalidateCpuInstructionCaches();

View File

@@ -9,9 +9,9 @@
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/readable_event.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/kernel/writable_event.h"
#include "core/hle/lock.h"
#include "core/hle/service/hid/hid.h"
#include "core/hle/service/nfp/nfp.h"
#include "core/hle/service/nfp/nfp_user.h"
@@ -20,7 +20,8 @@ namespace Service::NFP {
namespace ErrCodes {
constexpr ResultCode ERR_TAG_FAILED(ErrorModule::NFP,
-1); // TODO(ogniK): Find the actual error code
}
constexpr ResultCode ERR_NO_APPLICATION_AREA(ErrorModule::NFP, 152);
} // namespace ErrCodes
Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
: ServiceFramework(name), module(std::move(module)) {
@@ -292,10 +293,9 @@ private:
}
void OpenApplicationArea(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_NFP, "called");
// We don't need to worry about this since we can just open the file
LOG_WARNING(Service_NFP, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
rb.Push(ErrCodes::ERR_NO_APPLICATION_AREA);
}
void GetApplicationAreaSize(Kernel::HLERequestContext& ctx) {

View File

@@ -166,7 +166,7 @@ Layer::~Layer() = default;
Display::Display(u64 id, std::string name) : id(id), name(std::move(name)) {
auto& kernel = Core::System::GetInstance().Kernel();
vsync_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Pulse,
vsync_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Sticky,
fmt::format("Display VSync Event {}", id));
}

View File

@@ -12,9 +12,16 @@
#include "core/hle/kernel/client_session.h"
#include "core/hle/service/time/interface.h"
#include "core/hle/service/time/time.h"
#include "core/settings.h"
namespace Service::Time {
static std::chrono::seconds GetSecondsSinceEpoch() {
return std::chrono::duration_cast<std::chrono::seconds>(
std::chrono::system_clock::now().time_since_epoch()) +
Settings::values.custom_rtc_differential;
}
static void PosixToCalendar(u64 posix_time, CalendarTime& calendar_time,
CalendarAdditionalInfo& additional_info,
[[maybe_unused]] const TimeZoneRule& /*rule*/) {
@@ -68,9 +75,7 @@ public:
private:
void GetCurrentTime(Kernel::HLERequestContext& ctx) {
const s64 time_since_epoch{std::chrono::duration_cast<std::chrono::seconds>(
std::chrono::system_clock::now().time_since_epoch())
.count()};
const s64 time_since_epoch{GetSecondsSinceEpoch().count()};
LOG_DEBUG(Service_Time, "called");
IPC::ResponseBuilder rb{ctx, 4};
@@ -264,14 +269,9 @@ void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called");
IPC::RequestParser rp{ctx};
auto unknown_u8 = rp.PopRaw<u8>();
const auto initial_type = 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 s64 time_since_epoch{GetSecondsSinceEpoch().count()};
const std::time_t time(time_since_epoch);
const std::tm* tm = std::localtime(&time);
if (tm == nullptr) {
@@ -280,16 +280,19 @@ void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) {
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"};
const SteadyClockTimePoint steady_clock_time_point{
CoreTiming::cyclesToMs(CoreTiming::GetTicks()) / 1000, {}};
CalendarTime calendar_time{};
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;
ClockSnapshot clock_snapshot{};
clock_snapshot.system_posix_time = time_since_epoch;
clock_snapshot.network_posix_time = time_since_epoch;
clock_snapshot.system_calendar_time = calendar_time;
@@ -302,9 +305,10 @@ void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) {
clock_snapshot.network_calendar_info = additional_info;
clock_snapshot.steady_clock_timepoint = steady_clock_time_point;
clock_snapshot.location_name = location_name;
clock_snapshot.location_name = LocationName{"UTC"};
clock_snapshot.clock_auto_adjustment_enabled = 1;
clock_snapshot.ipc_u8 = unknown_u8;
clock_snapshot.type = initial_type;
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
ctx.WriteBuffer(&clock_snapshot, sizeof(ClockSnapshot));

View File

@@ -22,7 +22,6 @@ struct CalendarTime {
u8 hour;
u8 minute;
u8 second;
INSERT_PADDING_BYTES(1);
};
static_assert(sizeof(CalendarTime) == 0x8, "CalendarTime structure has incorrect size");
@@ -30,7 +29,7 @@ struct CalendarAdditionalInfo {
u32_le day_of_week;
u32_le day_of_year;
std::array<u8, 8> name;
INSERT_PADDING_BYTES(1);
u8 is_dst;
s32_le utc_offset;
};
static_assert(sizeof(CalendarAdditionalInfo) == 0x18,
@@ -42,8 +41,10 @@ struct TimeZoneRule {
};
struct SteadyClockTimePoint {
using SourceID = std::array<u8, 16>;
u64_le value;
INSERT_PADDING_WORDS(4);
SourceID source_id;
};
static_assert(sizeof(SteadyClockTimePoint) == 0x18, "SteadyClockTimePoint is incorrect size");
@@ -66,8 +67,9 @@ struct ClockSnapshot {
SteadyClockTimePoint steady_clock_timepoint;
LocationName location_name;
u8 clock_auto_adjustment_enabled;
u8 ipc_u8;
INSERT_PADDING_BYTES(2);
u8 type;
u8 version;
INSERT_PADDING_BYTES(1);
};
static_assert(sizeof(ClockSnapshot) == 0xd0, "ClockSnapshot is an invalid size");

View File

@@ -19,6 +19,7 @@
#include "core/core_timing.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/readable_event.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/kernel/writable_event.h"
#include "core/hle/service/nvdrv/nvdrv.h"
#include "core/hle/service/nvflinger/buffer_queue.h"
@@ -31,12 +32,26 @@
namespace Service::VI {
constexpr ResultCode ERR_OPERATION_FAILED{ErrorModule::VI, 1};
constexpr ResultCode ERR_UNSUPPORTED{ErrorModule::VI, 6};
struct DisplayInfo {
/// The name of this particular display.
char display_name[0x40]{"Default"};
u64 unknown_1{1};
u64 unknown_2{1};
u64 width{1280};
u64 height{720};
/// Whether or not the display has a limited number of layers.
u8 has_limited_layers{1};
INSERT_PADDING_BYTES(7){};
/// Indicates the total amount of layers supported by the display.
/// @note This is only valid if has_limited_layers is set.
u64 max_layers{1};
/// Maximum width in pixels.
u64 width{1920};
/// Maximum height in pixels.
u64 height{1080};
};
static_assert(sizeof(DisplayInfo) == 0x60, "DisplayInfo has wrong size");
@@ -502,10 +517,12 @@ private:
void TransactParcel(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
u32 id = rp.Pop<u32>();
auto transaction = static_cast<TransactionId>(rp.Pop<u32>());
u32 flags = rp.Pop<u32>();
LOG_DEBUG(Service_VI, "called, transaction={:X}", static_cast<u32>(transaction));
const u32 id = rp.Pop<u32>();
const auto transaction = static_cast<TransactionId>(rp.Pop<u32>());
const u32 flags = rp.Pop<u32>();
LOG_DEBUG(Service_VI, "called. id=0x{:08X} transaction={:X}, flags=0x{:08X}", id,
static_cast<u32>(transaction), flags);
auto buffer_queue = nv_flinger->GetBufferQueue(id);
@@ -593,9 +610,10 @@ private:
void AdjustRefcount(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
u32 id = rp.Pop<u32>();
s32 addval = rp.PopRaw<s32>();
u32 type = rp.Pop<u32>();
const u32 id = rp.Pop<u32>();
const s32 addval = rp.PopRaw<s32>();
const u32 type = rp.Pop<u32>();
LOG_WARNING(Service_VI, "(STUBBED) called id={}, addval={:08X}, type={:08X}", id, addval,
type);
@@ -605,11 +623,12 @@ private:
void GetNativeHandle(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
u32 id = rp.Pop<u32>();
u32 unknown = rp.Pop<u32>();
const u32 id = rp.Pop<u32>();
const u32 unknown = rp.Pop<u32>();
LOG_WARNING(Service_VI, "(STUBBED) called id={}, unknown={:08X}", id, unknown);
auto buffer_queue = nv_flinger->GetBufferQueue(id);
const auto buffer_queue = nv_flinger->GetBufferQueue(id);
// TODO(Subv): Find out what this actually is.
IPC::ResponseBuilder rb{ctx, 2, 1};
@@ -674,11 +693,12 @@ public:
private:
void SetLayerZ(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_VI, "(STUBBED) called");
IPC::RequestParser rp{ctx};
u64 layer_id = rp.Pop<u64>();
u64 z_value = rp.Pop<u64>();
const u64 layer_id = rp.Pop<u64>();
const u64 z_value = rp.Pop<u64>();
LOG_WARNING(Service_VI, "(STUBBED) called. layer_id=0x{:016X}, z_value=0x{:016X}", layer_id,
z_value);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
@@ -686,8 +706,9 @@ private:
void SetLayerVisibility(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
u64 layer_id = rp.Pop<u64>();
bool visibility = rp.Pop<bool>();
const u64 layer_id = rp.Pop<u64>();
const bool visibility = rp.Pop<bool>();
LOG_WARNING(Service_VI, "(STUBBED) called, layer_id=0x{:08X}, visibility={}", layer_id,
visibility);
@@ -796,25 +817,27 @@ public:
private:
void CloseDisplay(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_VI, "(STUBBED) called");
IPC::RequestParser rp{ctx};
u64 display = rp.Pop<u64>();
const u64 display = rp.Pop<u64>();
LOG_WARNING(Service_VI, "(STUBBED) called. display=0x{:016X}", display);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void CreateManagedLayer(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_VI, "(STUBBED) called");
IPC::RequestParser rp{ctx};
u32 unknown = rp.Pop<u32>();
const u32 unknown = rp.Pop<u32>();
rp.Skip(1, false);
u64 display = rp.Pop<u64>();
u64 aruid = rp.Pop<u64>();
const u64 display = rp.Pop<u64>();
const u64 aruid = rp.Pop<u64>();
u64 layer_id = nv_flinger->CreateLayer(display);
LOG_WARNING(Service_VI,
"(STUBBED) called. unknown=0x{:08X}, display=0x{:016X}, aruid=0x{:016X}",
unknown, display, aruid);
const u64 layer_id = nv_flinger->CreateLayer(display);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
@@ -822,11 +845,12 @@ private:
}
void AddToLayerStack(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_VI, "(STUBBED) called");
IPC::RequestParser rp{ctx};
u32 stack = rp.Pop<u32>();
u64 layer_id = rp.Pop<u64>();
const u32 stack = rp.Pop<u32>();
const u64 layer_id = rp.Pop<u64>();
LOG_WARNING(Service_VI, "(STUBBED) called. stack=0x{:08X}, layer_id=0x{:016X}", stack,
layer_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
@@ -834,8 +858,9 @@ private:
void SetLayerVisibility(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
u64 layer_id = rp.Pop<u64>();
bool visibility = rp.Pop<bool>();
const u64 layer_id = rp.Pop<u64>();
const bool visibility = rp.Pop<bool>();
LOG_WARNING(Service_VI, "(STUBBED) called, layer_id=0x{:X}, visibility={}", layer_id,
visibility);
@@ -852,6 +877,22 @@ public:
~IApplicationDisplayService() = default;
private:
enum class ConvertedScaleMode : u64 {
Freeze = 0,
ScaleToWindow = 1,
ScaleAndCrop = 2,
None = 3,
PreserveAspectRatio = 4,
};
enum class NintendoScaleMode : u32 {
None = 0,
Freeze = 1,
ScaleToWindow = 2,
ScaleAndCrop = 3,
PreserveAspectRatio = 4,
};
void GetRelayService(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_VI, "(STUBBED) called");
@@ -888,10 +929,23 @@ private:
LOG_WARNING(Service_VI, "(STUBBED) called");
IPC::RequestParser rp{ctx};
auto name_buf = rp.PopRaw<std::array<u8, 0x40>>();
auto end = std::find(name_buf.begin(), name_buf.end(), '\0');
const auto name_buf = rp.PopRaw<std::array<char, 0x40>>();
std::string name(name_buf.begin(), end);
OpenDisplayImpl(ctx, std::string_view{name_buf.data(), name_buf.size()});
}
void OpenDefaultDisplay(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_VI, "called");
OpenDisplayImpl(ctx, "Default");
}
void OpenDisplayImpl(Kernel::HLERequestContext& ctx, std::string_view name) {
const auto trim_pos = name.find('\0');
if (trim_pos != std::string_view::npos) {
name.remove_suffix(name.size() - trim_pos);
}
ASSERT_MSG(name == "Default", "Non-default displays aren't supported yet");
@@ -901,45 +955,65 @@ private:
}
void CloseDisplay(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_VI, "(STUBBED) called");
IPC::RequestParser rp{ctx};
u64 display_id = rp.Pop<u64>();
const u64 display_id = rp.Pop<u64>();
LOG_WARNING(Service_VI, "(STUBBED) called. display_id=0x{:016X}", display_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
// This literally does nothing internally in the actual service itself,
// and just returns a successful result code regardless of the input.
void SetDisplayEnabled(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_VI, "called.");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void GetDisplayResolution(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_VI, "(STUBBED) called");
IPC::RequestParser rp{ctx};
u64 display_id = rp.Pop<u64>();
const u64 display_id = rp.Pop<u64>();
LOG_DEBUG(Service_VI, "called. display_id=0x{:016X}", display_id);
IPC::ResponseBuilder rb{ctx, 6};
rb.Push(RESULT_SUCCESS);
if (Settings::values.use_docked_mode) {
rb.Push(static_cast<u64>(DisplayResolution::DockedWidth) *
static_cast<u32>(Settings::values.resolution_factor));
rb.Push(static_cast<u64>(DisplayResolution::DockedHeight) *
static_cast<u32>(Settings::values.resolution_factor));
} else {
rb.Push(static_cast<u64>(DisplayResolution::UndockedWidth) *
static_cast<u32>(Settings::values.resolution_factor));
rb.Push(static_cast<u64>(DisplayResolution::UndockedHeight) *
static_cast<u32>(Settings::values.resolution_factor));
}
// This only returns the fixed values of 1280x720 and makes no distinguishing
// between docked and undocked dimensions. We take the liberty of applying
// the resolution scaling factor here.
rb.Push(static_cast<u64>(DisplayResolution::UndockedWidth) *
static_cast<u32>(Settings::values.resolution_factor));
rb.Push(static_cast<u64>(DisplayResolution::UndockedHeight) *
static_cast<u32>(Settings::values.resolution_factor));
}
void SetLayerScalingMode(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_VI, "(STUBBED) called");
IPC::RequestParser rp{ctx};
u32 scaling_mode = rp.Pop<u32>();
u64 unknown = rp.Pop<u64>();
const auto scaling_mode = rp.PopEnum<NintendoScaleMode>();
const u64 unknown = rp.Pop<u64>();
LOG_DEBUG(Service_VI, "called. scaling_mode=0x{:08X}, unknown=0x{:016X}",
static_cast<u32>(scaling_mode), unknown);
IPC::ResponseBuilder rb{ctx, 2};
if (scaling_mode > NintendoScaleMode::PreserveAspectRatio) {
LOG_ERROR(Service_VI, "Invalid scaling mode provided.");
rb.Push(ERR_OPERATION_FAILED);
return;
}
if (scaling_mode != NintendoScaleMode::ScaleToWindow &&
scaling_mode != NintendoScaleMode::PreserveAspectRatio) {
LOG_ERROR(Service_VI, "Unsupported scaling mode supplied.");
rb.Push(ERR_UNSUPPORTED);
return;
}
rb.Push(RESULT_SUCCESS);
}
@@ -957,19 +1031,19 @@ private:
}
void OpenLayer(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_VI, "called");
IPC::RequestParser rp{ctx};
auto name_buf = rp.PopRaw<std::array<u8, 0x40>>();
auto end = std::find(name_buf.begin(), name_buf.end(), '\0');
const auto name_buf = rp.PopRaw<std::array<u8, 0x40>>();
const auto end = std::find(name_buf.begin(), name_buf.end(), '\0');
std::string display_name(name_buf.begin(), end);
const std::string display_name(name_buf.begin(), end);
u64 layer_id = rp.Pop<u64>();
u64 aruid = rp.Pop<u64>();
const u64 layer_id = rp.Pop<u64>();
const u64 aruid = rp.Pop<u64>();
u64 display_id = nv_flinger->OpenDisplay(display_name);
u32 buffer_queue_id = nv_flinger->GetBufferQueueId(display_id, layer_id);
LOG_DEBUG(Service_VI, "called. layer_id=0x{:016X}, aruid=0x{:016X}", layer_id, aruid);
const u64 display_id = nv_flinger->OpenDisplay(display_name);
const u32 buffer_queue_id = nv_flinger->GetBufferQueueId(display_id, layer_id);
NativeWindow native_window{buffer_queue_id};
IPC::ResponseBuilder rb{ctx, 4};
@@ -978,17 +1052,17 @@ private:
}
void CreateStrayLayer(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_VI, "called");
IPC::RequestParser rp{ctx};
u32 flags = rp.Pop<u32>();
const u32 flags = rp.Pop<u32>();
rp.Pop<u32>(); // padding
u64 display_id = rp.Pop<u64>();
const u64 display_id = rp.Pop<u64>();
LOG_DEBUG(Service_VI, "called. flags=0x{:08X}, display_id=0x{:016X}", flags, display_id);
// TODO(Subv): What's the difference between a Stray and a Managed layer?
u64 layer_id = nv_flinger->CreateLayer(display_id);
u32 buffer_queue_id = nv_flinger->GetBufferQueueId(display_id, layer_id);
const u64 layer_id = nv_flinger->CreateLayer(display_id);
const u32 buffer_queue_id = nv_flinger->GetBufferQueueId(display_id, layer_id);
NativeWindow native_window{buffer_queue_id};
IPC::ResponseBuilder rb{ctx, 6};
@@ -998,73 +1072,59 @@ private:
}
void DestroyStrayLayer(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_VI, "(STUBBED) called");
IPC::RequestParser rp{ctx};
u64 layer_id = rp.Pop<u64>();
const u64 layer_id = rp.Pop<u64>();
LOG_WARNING(Service_VI, "(STUBBED) called. layer_id=0x{:016X}", layer_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void GetDisplayVsyncEvent(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_VI, "(STUBBED) called");
IPC::RequestParser rp{ctx};
u64 display_id = rp.Pop<u64>();
const u64 display_id = rp.Pop<u64>();
auto vsync_event = nv_flinger->GetVsyncEvent(display_id);
LOG_WARNING(Service_VI, "(STUBBED) called. display_id=0x{:016X}", display_id);
const auto vsync_event = nv_flinger->GetVsyncEvent(display_id);
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
rb.PushCopyObjects(vsync_event);
}
enum class ConvertedScaleMode : u64 {
None = 0, // VI seems to name this as "Unknown" but lots of games pass it, assume it's no
// scaling/default
Freeze = 1,
ScaleToWindow = 2,
Crop = 3,
NoCrop = 4,
};
// This struct is different, currently it's 1:1 but this might change in the future.
enum class NintendoScaleMode : u32 {
None = 0,
Freeze = 1,
ScaleToWindow = 2,
Crop = 3,
NoCrop = 4,
};
void ConvertScalingMode(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
auto mode = rp.PopEnum<NintendoScaleMode>();
const auto mode = rp.PopEnum<NintendoScaleMode>();
LOG_DEBUG(Service_VI, "called mode={}", static_cast<u32>(mode));
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
const auto converted_mode = ConvertScalingModeImpl(mode);
if (converted_mode.Succeeded()) {
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.PushEnum(*converted_mode);
} else {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(converted_mode.Code());
}
}
static ResultVal<ConvertedScaleMode> ConvertScalingModeImpl(NintendoScaleMode mode) {
switch (mode) {
case NintendoScaleMode::None:
rb.PushEnum(ConvertedScaleMode::None);
break;
return MakeResult(ConvertedScaleMode::None);
case NintendoScaleMode::Freeze:
rb.PushEnum(ConvertedScaleMode::Freeze);
break;
return MakeResult(ConvertedScaleMode::Freeze);
case NintendoScaleMode::ScaleToWindow:
rb.PushEnum(ConvertedScaleMode::ScaleToWindow);
break;
case NintendoScaleMode::Crop:
rb.PushEnum(ConvertedScaleMode::Crop);
break;
case NintendoScaleMode::NoCrop:
rb.PushEnum(ConvertedScaleMode::NoCrop);
break;
return MakeResult(ConvertedScaleMode::ScaleToWindow);
case NintendoScaleMode::ScaleAndCrop:
return MakeResult(ConvertedScaleMode::ScaleAndCrop);
case NintendoScaleMode::PreserveAspectRatio:
return MakeResult(ConvertedScaleMode::PreserveAspectRatio);
default:
UNIMPLEMENTED_MSG("Unknown scaling mode {}", static_cast<u32>(mode));
rb.PushEnum(ConvertedScaleMode::None);
break;
return ERR_OPERATION_FAILED;
}
}
@@ -1082,9 +1142,9 @@ IApplicationDisplayService::IApplicationDisplayService(
"GetIndirectDisplayTransactionService"},
{1000, &IApplicationDisplayService::ListDisplays, "ListDisplays"},
{1010, &IApplicationDisplayService::OpenDisplay, "OpenDisplay"},
{1011, nullptr, "OpenDefaultDisplay"},
{1011, &IApplicationDisplayService::OpenDefaultDisplay, "OpenDefaultDisplay"},
{1020, &IApplicationDisplayService::CloseDisplay, "CloseDisplay"},
{1101, nullptr, "SetDisplayEnabled"},
{1101, &IApplicationDisplayService::SetDisplayEnabled, "SetDisplayEnabled"},
{1102, &IApplicationDisplayService::GetDisplayResolution, "GetDisplayResolution"},
{2020, &IApplicationDisplayService::OpenLayer, "OpenLayer"},
{2021, nullptr, "CloseLayer"},

View File

@@ -129,7 +129,10 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(Kernel::Process& process)
return ResultStatus::Error32BitISA;
}
process.LoadFromMetadata(metadata);
if (process.LoadFromMetadata(metadata).IsError()) {
return ResultStatus::ErrorUnableToParseKernelMetadata;
}
const FileSys::PatchManager pm(metadata.GetTitleID());
// Load NSO modules

View File

@@ -93,7 +93,7 @@ std::string GetFileTypeString(FileType type) {
return "unknown";
}
constexpr std::array<const char*, 60> RESULT_MESSAGES{
constexpr std::array<const char*, 62> RESULT_MESSAGES{
"The operation completed successfully.",
"The loader requested to load is already loaded.",
"The operation is not implemented.",
@@ -103,6 +103,7 @@ constexpr std::array<const char*, 60> RESULT_MESSAGES{
"The NPDM has a bad ACI header,",
"The NPDM file has a bad file access control.",
"The NPDM has a bad file access header.",
"The NPDM has bad kernel capability descriptors.",
"The PFS/HFS partition has a bad header.",
"The PFS/HFS partition has incorrect size as determined by the header.",
"The NCA file has a bad header.",
@@ -125,6 +126,7 @@ constexpr std::array<const char*, 60> RESULT_MESSAGES{
"The file could not be found or does not exist.",
"The game is missing a program metadata file (main.npdm).",
"The game uses the currently-unimplemented 32-bit architecture.",
"Unable to completely parse the kernel metadata when loading the emulated process",
"The RomFS could not be found.",
"The ELF file has incorrect size as determined by the header.",
"There was a general error loading the NRO into emulated memory.",

View File

@@ -15,6 +15,10 @@
#include "core/file_sys/control_metadata.h"
#include "core/file_sys/vfs.h"
namespace FileSys {
class NACP;
} // namespace FileSys
namespace Kernel {
struct AddressMapping;
class Process;
@@ -67,6 +71,7 @@ enum class ResultStatus : u16 {
ErrorBadACIHeader,
ErrorBadFileAccessControl,
ErrorBadFileAccessHeader,
ErrorBadKernelCapabilityDescriptors,
ErrorBadPFSHeader,
ErrorIncorrectPFSFileSize,
ErrorBadNCAHeader,
@@ -89,6 +94,7 @@ enum class ResultStatus : u16 {
ErrorNullFile,
ErrorMissingNPDM,
Error32BitISA,
ErrorUnableToParseKernelMetadata,
ErrorNoRomFS,
ErrorIncorrectELFFileSize,
ErrorLoadingNRO,
@@ -172,6 +178,8 @@ public:
/**
* Get the banner (typically banner section) of the application
* In the context of NX, this is the animation that displays in the bottom right of the screen
* when a game boots. Stored in GIF format.
* @param buffer Reference to buffer to store data
* @return ResultStatus result of function
*/
@@ -181,6 +189,8 @@ public:
/**
* Get the logo (typically logo section) of the application
* In the context of NX, this is the static image that displays in the top left of the screen
* when a game boots. Stored in JPEG format.
* @param buffer Reference to buffer to store data
* @return ResultStatus result of function
*/
@@ -245,11 +255,20 @@ public:
}
/**
* Get the developer of the application
* @param developer Reference to store the application developer into
* Get the control data (CNMT) of the application
* @param control Reference to store the application control data into
* @return ResultStatus result of function
*/
virtual ResultStatus ReadDeveloper(std::string& developer) {
virtual ResultStatus ReadControlData(FileSys::NACP& control) {
return ResultStatus::ErrorNotImplemented;
}
/**
* Get the RomFS of the manual of the application
* @param file The raw manual RomFS of the game
* @return ResultStatus result of function
*/
virtual ResultStatus ReadManualRomFS(FileSys::VirtualFile& file) {
return ResultStatus::ErrorNotImplemented;
}

View File

@@ -79,4 +79,13 @@ u64 AppLoader_NAX::ReadRomFSIVFCOffset() const {
ResultStatus AppLoader_NAX::ReadProgramId(u64& out_program_id) {
return nca_loader->ReadProgramId(out_program_id);
}
ResultStatus AppLoader_NAX::ReadBanner(std::vector<u8>& buffer) {
return nca_loader->ReadBanner(buffer);
}
ResultStatus AppLoader_NAX::ReadLogo(std::vector<u8>& buffer) {
return nca_loader->ReadLogo(buffer);
}
} // namespace Loader

View File

@@ -39,6 +39,9 @@ public:
u64 ReadRomFSIVFCOffset() const override;
ResultStatus ReadProgramId(u64& out_program_id) override;
ResultStatus ReadBanner(std::vector<u8>& buffer) override;
ResultStatus ReadLogo(std::vector<u8>& buffer) override;
private:
std::unique_ptr<FileSys::NAX> nax;
std::unique_ptr<AppLoader_NCA> nca_loader;

View File

@@ -84,4 +84,23 @@ ResultStatus AppLoader_NCA::ReadProgramId(u64& out_program_id) {
return ResultStatus::Success;
}
ResultStatus AppLoader_NCA::ReadBanner(std::vector<u8>& buffer) {
if (nca == nullptr || nca->GetStatus() != ResultStatus::Success)
return ResultStatus::ErrorNotInitialized;
const auto logo = nca->GetLogoPartition();
if (logo == nullptr)
return ResultStatus::ErrorNoIcon;
buffer = logo->GetFile("StartupMovie.gif")->ReadAllBytes();
return ResultStatus::Success;
}
ResultStatus AppLoader_NCA::ReadLogo(std::vector<u8>& buffer) {
if (nca == nullptr || nca->GetStatus() != ResultStatus::Success)
return ResultStatus::ErrorNotInitialized;
const auto logo = nca->GetLogoPartition();
if (logo == nullptr)
return ResultStatus::ErrorNoIcon;
buffer = logo->GetFile("NintendoLogo.png")->ReadAllBytes();
return ResultStatus::Success;
}
} // namespace Loader

View File

@@ -39,6 +39,9 @@ public:
u64 ReadRomFSIVFCOffset() const override;
ResultStatus ReadProgramId(u64& out_program_id) override;
ResultStatus ReadBanner(std::vector<u8>& buffer) override;
ResultStatus ReadLogo(std::vector<u8>& buffer) override;
private:
std::unique_ptr<FileSys::NCA> nca;
std::unique_ptr<AppLoader_DeconstructedRomDirectory> directory_loader;

View File

@@ -152,10 +152,27 @@ ResultStatus AppLoader_NSP::ReadTitle(std::string& title) {
return ResultStatus::Success;
}
ResultStatus AppLoader_NSP::ReadDeveloper(std::string& developer) {
ResultStatus AppLoader_NSP::ReadControlData(FileSys::NACP& nacp) {
if (nacp_file == nullptr)
return ResultStatus::ErrorNoControl;
developer = nacp_file->GetDeveloperName();
nacp = *nacp_file;
return ResultStatus::Success;
}
ResultStatus AppLoader_NSP::ReadManualRomFS(FileSys::VirtualFile& file) {
const auto nca = nsp->GetNCA(nsp->GetProgramTitleID(), FileSys::ContentRecordType::Manual);
if (nsp->GetStatus() != ResultStatus::Success || nca == nullptr)
return ResultStatus::ErrorNoRomFS;
file = nca->GetRomFS();
return file == nullptr ? ResultStatus::ErrorNoRomFS : ResultStatus::Success;
}
ResultStatus AppLoader_NSP::ReadBanner(std::vector<u8>& buffer) {
return secondary_loader->ReadBanner(buffer);
}
ResultStatus AppLoader_NSP::ReadLogo(std::vector<u8>& buffer) {
return secondary_loader->ReadLogo(buffer);
}
} // namespace Loader

View File

@@ -43,7 +43,11 @@ public:
ResultStatus ReadProgramId(u64& out_program_id) override;
ResultStatus ReadIcon(std::vector<u8>& buffer) override;
ResultStatus ReadTitle(std::string& title) override;
ResultStatus ReadDeveloper(std::string& developer) override;
ResultStatus ReadControlData(FileSys::NACP& nacp) override;
ResultStatus ReadManualRomFS(FileSys::VirtualFile& file) override;
ResultStatus ReadBanner(std::vector<u8>& buffer) override;
ResultStatus ReadLogo(std::vector<u8>& buffer) override;
private:
std::unique_ptr<FileSys::NSP> nsp;

View File

@@ -121,10 +121,28 @@ ResultStatus AppLoader_XCI::ReadTitle(std::string& title) {
return ResultStatus::Success;
}
ResultStatus AppLoader_XCI::ReadDeveloper(std::string& developer) {
ResultStatus AppLoader_XCI::ReadControlData(FileSys::NACP& control) {
if (nacp_file == nullptr)
return ResultStatus::ErrorNoControl;
developer = nacp_file->GetDeveloperName();
control = *nacp_file;
return ResultStatus::Success;
}
ResultStatus AppLoader_XCI::ReadManualRomFS(FileSys::VirtualFile& file) {
const auto nca = xci->GetSecurePartitionNSP()->GetNCA(xci->GetProgramTitleID(),
FileSys::ContentRecordType::Manual);
if (xci->GetStatus() != ResultStatus::Success || nca == nullptr)
return ResultStatus::ErrorXCIMissingPartition;
file = nca->GetRomFS();
return file == nullptr ? ResultStatus::ErrorNoRomFS : ResultStatus::Success;
}
ResultStatus AppLoader_XCI::ReadBanner(std::vector<u8>& buffer) {
return nca_loader->ReadBanner(buffer);
}
ResultStatus AppLoader_XCI::ReadLogo(std::vector<u8>& buffer) {
return nca_loader->ReadLogo(buffer);
}
} // namespace Loader

View File

@@ -43,7 +43,11 @@ public:
ResultStatus ReadProgramId(u64& out_program_id) override;
ResultStatus ReadIcon(std::vector<u8>& buffer) override;
ResultStatus ReadTitle(std::string& title) override;
ResultStatus ReadDeveloper(std::string& developer) override;
ResultStatus ReadControlData(FileSys::NACP& control) override;
ResultStatus ReadManualRomFS(FileSys::VirtualFile& file) override;
ResultStatus ReadBanner(std::vector<u8>& buffer) override;
ResultStatus ReadLogo(std::vector<u8>& buffer) override;
private:
std::unique_ptr<FileSys::XCI> xci;

View File

@@ -6,6 +6,7 @@
#include <array>
#include <atomic>
#include <chrono>
#include <map>
#include <optional>
#include <string>
@@ -350,6 +351,11 @@ struct Values {
bool use_docked_mode;
bool enable_nfc;
std::optional<u32> rng_seed;
// Measured in seconds since epoch
std::optional<std::chrono::seconds> custom_rtc;
// Set on game boot, reset on stop. Seconds difference between current time and `custom_rtc`
std::chrono::seconds custom_rtc_differential;
s32 current_user;
s32 language_index;

View File

@@ -30,6 +30,8 @@ add_library(video_core STATIC
renderer_base.h
renderer_opengl/gl_buffer_cache.cpp
renderer_opengl/gl_buffer_cache.h
renderer_opengl/gl_global_cache.cpp
renderer_opengl/gl_global_cache.h
renderer_opengl/gl_primitive_assembler.cpp
renderer_opengl/gl_primitive_assembler.h
renderer_opengl/gl_rasterizer.cpp

View File

@@ -135,6 +135,33 @@ void Maxwell3D::CallMethod(const GPU::MethodCall& method_call) {
if (regs.reg_array[method_call.method] != method_call.argument) {
regs.reg_array[method_call.method] = method_call.argument;
// Color buffers
constexpr u32 first_rt_reg = MAXWELL3D_REG_INDEX(rt);
constexpr u32 registers_per_rt = sizeof(regs.rt[0]) / sizeof(u32);
if (method_call.method >= first_rt_reg &&
method_call.method < first_rt_reg + registers_per_rt * Regs::NumRenderTargets) {
const std::size_t rt_index = (method_call.method - first_rt_reg) / registers_per_rt;
dirty_flags.color_buffer |= 1u << static_cast<u32>(rt_index);
}
// Zeta buffer
constexpr u32 registers_in_zeta = sizeof(regs.zeta) / sizeof(u32);
if (method_call.method == MAXWELL3D_REG_INDEX(zeta_enable) ||
method_call.method == MAXWELL3D_REG_INDEX(zeta_width) ||
method_call.method == MAXWELL3D_REG_INDEX(zeta_height) ||
(method_call.method >= MAXWELL3D_REG_INDEX(zeta) &&
method_call.method < MAXWELL3D_REG_INDEX(zeta) + registers_in_zeta)) {
dirty_flags.zeta_buffer = true;
}
// Shader
constexpr u32 shader_registers_count =
sizeof(regs.shader_config[0]) * Regs::MaxShaderProgram / sizeof(u32);
if (method_call.method >= MAXWELL3D_REG_INDEX(shader_config[0]) &&
method_call.method < MAXWELL3D_REG_INDEX(shader_config[0]) + shader_registers_count) {
dirty_flags.shaders = true;
}
// Vertex format
if (method_call.method >= MAXWELL3D_REG_INDEX(vertex_attrib_format) &&
method_call.method <

View File

@@ -1089,10 +1089,18 @@ public:
MemoryManager& memory_manager;
struct DirtyFlags {
u8 color_buffer = 0xFF;
bool zeta_buffer = true;
bool shaders = true;
bool vertex_attrib_format = true;
u32 vertex_array = 0xFFFFFFFF;
void OnMemoryWrite() {
color_buffer = 0xFF;
zeta_buffer = true;
shaders = true;
vertex_array = 0xFFFFFFFF;
}
};

View File

@@ -66,8 +66,6 @@ static constexpr ConversionArray morton_to_linear_fns = {
MortonCopy<true, PixelFormat::BC6H_UF16>,
MortonCopy<true, PixelFormat::BC6H_SF16>,
MortonCopy<true, PixelFormat::ASTC_2D_4X4>,
MortonCopy<true, PixelFormat::G8R8U>,
MortonCopy<true, PixelFormat::G8R8S>,
MortonCopy<true, PixelFormat::BGRA8>,
MortonCopy<true, PixelFormat::RGBA32F>,
MortonCopy<true, PixelFormat::RG32F>,
@@ -138,8 +136,6 @@ static constexpr ConversionArray linear_to_morton_fns = {
MortonCopy<false, PixelFormat::BC6H_SF16>,
// TODO(Subv): Swizzling ASTC formats are not supported
nullptr,
MortonCopy<false, PixelFormat::G8R8U>,
MortonCopy<false, PixelFormat::G8R8S>,
MortonCopy<false, PixelFormat::BGRA8>,
MortonCopy<false, PixelFormat::RGBA32F>,
MortonCopy<false, PixelFormat::RG32F>,

View File

@@ -4,6 +4,7 @@
#pragma once
#include <functional>
#include "common/common_types.h"
#include "video_core/engines/fermi_2d.h"
#include "video_core/gpu.h"
@@ -11,6 +12,14 @@
namespace VideoCore {
enum class LoadCallbackStage {
Prepare,
Decompile,
Build,
Complete,
};
using DiskResourceLoadCallback = std::function<void(LoadCallbackStage, std::size_t, std::size_t)>;
class RasterizerInterface {
public:
virtual ~RasterizerInterface() {}

View File

@@ -14,7 +14,7 @@
namespace OpenGL {
OGLBufferCache::OGLBufferCache(RasterizerOpenGL& rasterizer, std::size_t size)
: RasterizerCache{rasterizer}, stream_buffer(GL_ARRAY_BUFFER, size) {}
: RasterizerCache{rasterizer}, stream_buffer(size, true) {}
GLintptr OGLBufferCache::UploadMemory(Tegra::GPUVAddr gpu_addr, std::size_t size,
std::size_t alignment, bool cache) {

View File

@@ -0,0 +1,24 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <glad/glad.h>
#include "video_core/renderer_opengl/gl_global_cache.h"
#include "video_core/renderer_opengl/gl_rasterizer.h"
#include "video_core/renderer_opengl/utils.h"
namespace OpenGL {
CachedGlobalRegion::CachedGlobalRegion(VAddr addr, u32 size) : addr{addr}, size{size} {
buffer.Create();
// Bind and unbind the buffer so it gets allocated by the driver
glBindBuffer(GL_SHADER_STORAGE_BUFFER, buffer.handle);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
LabelGLObject(GL_BUFFER, buffer.handle, addr, "GlobalMemory");
}
GlobalRegionCacheOpenGL::GlobalRegionCacheOpenGL(RasterizerOpenGL& rasterizer)
: RasterizerCache{rasterizer} {}
} // namespace OpenGL

View File

@@ -0,0 +1,60 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <memory>
#include <glad/glad.h>
#include "common/common_types.h"
#include "video_core/rasterizer_cache.h"
#include "video_core/renderer_opengl/gl_resource_manager.h"
namespace OpenGL {
namespace GLShader {
class GlobalMemoryEntry;
} // namespace GLShader
class RasterizerOpenGL;
class CachedGlobalRegion;
using GlobalRegion = std::shared_ptr<CachedGlobalRegion>;
class CachedGlobalRegion final : public RasterizerCacheObject {
public:
explicit CachedGlobalRegion(VAddr addr, u32 size);
/// Gets the address of the shader in guest memory, required for cache management
VAddr GetAddr() const {
return addr;
}
/// Gets the size of the shader in guest memory, required for cache management
std::size_t GetSizeInBytes() const {
return size;
}
/// Gets the GL program handle for the buffer
GLuint GetBufferHandle() const {
return buffer.handle;
}
// TODO(Rodrigo): When global memory is written (STG), implement flushing
void Flush() override {
UNIMPLEMENTED();
}
private:
VAddr addr{};
u32 size{};
OGLBuffer buffer;
};
class GlobalRegionCacheOpenGL final : public RasterizerCache<GlobalRegion> {
public:
explicit GlobalRegionCacheOpenGL(RasterizerOpenGL& rasterizer);
};
} // namespace OpenGL

View File

@@ -101,7 +101,7 @@ struct FramebufferCacheKey {
RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window, ScreenInfo& info)
: res_cache{*this}, shader_cache{*this}, emu_window{window}, screen_info{info},
buffer_cache(*this, STREAM_BUFFER_SIZE) {
buffer_cache(*this, STREAM_BUFFER_SIZE), global_cache{*this} {
// Create sampler objects
for (std::size_t i = 0; i < texture_samplers.size(); ++i) {
texture_samplers[i].Create();
@@ -135,27 +135,31 @@ void RasterizerOpenGL::CheckExtensions() {
}
}
void RasterizerOpenGL::SetupVertexFormat() {
GLuint RasterizerOpenGL::SetupVertexFormat() {
auto& gpu = Core::System::GetInstance().GPU().Maxwell3D();
const auto& regs = gpu.regs;
if (!gpu.dirty_flags.vertex_attrib_format)
return;
if (!gpu.dirty_flags.vertex_attrib_format) {
return state.draw.vertex_array;
}
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;
auto& vao_entry = iter->second;
if (is_cache_miss) {
VAO.Create();
state.draw.vertex_array = VAO.handle;
state.ApplyVertexBufferState();
vao_entry.Create();
const GLuint vao = vao_entry.handle;
// The index buffer binding is stored within the VAO. Stupid OpenGL, but easy to work
// around.
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer_cache.GetHandle());
// Eventhough we are using DSA to create this vertex array, there is a bug on Intel's blob
// that fails to properly create the vertex array if it's not bound even after creating it
// with glCreateVertexArrays
state.draw.vertex_array = vao;
state.ApplyVertexArrayState();
glVertexArrayElementBuffer(vao, buffer_cache.GetHandle());
// Use the vertex array as-is, assumes that the data is formatted correctly for OpenGL.
// Enables the first 16 vertex attributes always, as we don't know which ones are actually
@@ -163,7 +167,7 @@ void RasterizerOpenGL::SetupVertexFormat() {
// for now to avoid OpenGL errors.
// TODO(Subv): Analyze the shader to identify which attributes are actually used and don't
// assume every shader uses them all.
for (unsigned index = 0; index < 16; ++index) {
for (u32 index = 0; index < 16; ++index) {
const auto& attrib = regs.vertex_attrib_format[index];
// Ignore invalid attributes.
@@ -178,28 +182,29 @@ void RasterizerOpenGL::SetupVertexFormat() {
ASSERT(buffer.IsEnabled());
glEnableVertexAttribArray(index);
glEnableVertexArrayAttrib(vao, index);
if (attrib.type == Tegra::Engines::Maxwell3D::Regs::VertexAttribute::Type::SignedInt ||
attrib.type ==
Tegra::Engines::Maxwell3D::Regs::VertexAttribute::Type::UnsignedInt) {
glVertexAttribIFormat(index, attrib.ComponentCount(),
MaxwellToGL::VertexType(attrib), attrib.offset);
glVertexArrayAttribIFormat(vao, index, attrib.ComponentCount(),
MaxwellToGL::VertexType(attrib), attrib.offset);
} else {
glVertexAttribFormat(index, attrib.ComponentCount(),
MaxwellToGL::VertexType(attrib),
attrib.IsNormalized() ? GL_TRUE : GL_FALSE, attrib.offset);
glVertexArrayAttribFormat(
vao, index, attrib.ComponentCount(), MaxwellToGL::VertexType(attrib),
attrib.IsNormalized() ? GL_TRUE : GL_FALSE, attrib.offset);
}
glVertexAttribBinding(index, attrib.buffer);
glVertexArrayAttribBinding(vao, index, attrib.buffer);
}
}
state.draw.vertex_array = VAO.handle;
state.ApplyVertexBufferState();
// Rebinding the VAO invalidates the vertex buffer bindings.
gpu.dirty_flags.vertex_array = 0xFFFFFFFF;
state.draw.vertex_array = vao_entry.handle;
return vao_entry.handle;
}
void RasterizerOpenGL::SetupVertexBuffer() {
void RasterizerOpenGL::SetupVertexBuffer(GLuint vao) {
auto& gpu = Core::System::GetInstance().GPU().Maxwell3D();
const auto& regs = gpu.regs;
@@ -217,7 +222,7 @@ void RasterizerOpenGL::SetupVertexBuffer() {
if (!vertex_array.IsEnabled())
continue;
Tegra::GPUVAddr start = vertex_array.StartAddress();
const Tegra::GPUVAddr start = vertex_array.StartAddress();
const Tegra::GPUVAddr end = regs.vertex_array_limit[index].LimitAddress();
ASSERT(end > start);
@@ -225,21 +230,18 @@ void RasterizerOpenGL::SetupVertexBuffer() {
const GLintptr vertex_buffer_offset = buffer_cache.UploadMemory(start, size);
// Bind the vertex array to the buffer at the current offset.
glBindVertexBuffer(index, buffer_cache.GetHandle(), vertex_buffer_offset,
vertex_array.stride);
glVertexArrayVertexBuffer(vao, index, buffer_cache.GetHandle(), vertex_buffer_offset,
vertex_array.stride);
if (regs.instanced_arrays.IsInstancingEnabled(index) && vertex_array.divisor != 0) {
// Enable vertex buffer instancing with the specified divisor.
glVertexBindingDivisor(index, vertex_array.divisor);
glVertexArrayBindingDivisor(vao, index, vertex_array.divisor);
} else {
// Disable the vertex buffer instancing.
glVertexBindingDivisor(index, 0);
glVertexArrayBindingDivisor(vao, index, 0);
}
}
// Implicit set by glBindVertexBuffer. Stupid glstate handling...
state.draw.vertex_buffer = buffer_cache.GetHandle();
gpu.dirty_flags.vertex_array = 0;
}
@@ -293,7 +295,7 @@ DrawParameters RasterizerOpenGL::SetupDraw() {
void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
MICROPROFILE_SCOPE(OpenGL_Shader);
const auto& gpu = Core::System::GetInstance().GPU().Maxwell3D();
auto& gpu = Core::System::GetInstance().GPU().Maxwell3D();
// Next available bindpoints to use when uploading the const buffers and textures to the GLSL
// shaders. The constbuffer bindpoint starts after the shader stage configuration bind points.
@@ -365,7 +367,7 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
// (sometimes it's half the screen, sometimes three quarters). To avoid this, enable the
// clip distances only when it's written by a shader stage.
for (std::size_t i = 0; i < Maxwell::NumClipDistances; ++i) {
clip_distances[i] |= shader->GetShaderEntries().clip_distances[i];
clip_distances[i] = clip_distances[i] || shader->GetShaderEntries().clip_distances[i];
}
// When VertexA is enabled, we have dual vertex shaders
@@ -376,6 +378,8 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
}
SyncClipEnabled(clip_distances);
gpu.dirty_flags.shaders = false;
}
void RasterizerOpenGL::SetupCachedFramebuffer(const FramebufferCacheKey& fbkey,
@@ -486,7 +490,19 @@ void RasterizerOpenGL::ConfigureFramebuffers(OpenGLState& current_state, bool us
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;
const auto& gpu = Core::System::GetInstance().GPU().Maxwell3D();
const auto& regs = gpu.regs;
const FramebufferConfigState fb_config_state{using_color_fb, using_depth_fb, preserve_contents,
single_color_target};
if (fb_config_state == current_framebuffer_config_state && gpu.dirty_flags.color_buffer == 0 &&
!gpu.dirty_flags.zeta_buffer) {
// Only skip if the previous ConfigureFramebuffers call was from the same kind (multiple or
// single color targets). This is done because the guest registers may not change but the
// host framebuffer may contain different attachments
return;
}
current_framebuffer_config_state = fb_config_state;
Surface depth_surface;
if (using_depth_fb) {
@@ -689,9 +705,6 @@ void RasterizerOpenGL::DrawArrays() {
// Draw the vertex batch
const bool is_indexed = accelerate_draw == AccelDraw::Indexed;
state.draw.vertex_buffer = buffer_cache.GetHandle();
state.ApplyVertexBufferState();
std::size_t buffer_size = CalculateVertexArraysSize();
// Add space for index buffer (keeping in mind non-core primitives)
@@ -721,8 +734,9 @@ void RasterizerOpenGL::DrawArrays() {
gpu.dirty_flags.vertex_array = 0xFFFFFFFF;
}
SetupVertexFormat();
SetupVertexBuffer();
const GLuint vao = SetupVertexFormat();
SetupVertexBuffer(vao);
DrawParameters params = SetupDraw();
SetupShaders(params.primitive_mode);
@@ -761,6 +775,7 @@ void RasterizerOpenGL::InvalidateRegion(VAddr addr, u64 size) {
MICROPROFILE_SCOPE(OpenGL_CacheManagement);
res_cache.InvalidateRegion(addr, size);
shader_cache.InvalidateRegion(addr, size);
global_cache.InvalidateRegion(addr, size);
buffer_cache.InvalidateRegion(addr, size);
}
@@ -1014,8 +1029,11 @@ u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, Shader& shader,
texture_samplers[current_bindpoint].SyncWithConfig(texture.tsc);
Surface surface = res_cache.GetTextureSurface(texture, entry);
if (surface != nullptr) {
state.texture_units[current_bindpoint].texture = surface->Texture().handle;
state.texture_units[current_bindpoint].target = surface->Target();
const GLuint handle =
entry.IsArray() ? surface->TextureLayer().handle : surface->Texture().handle;
const GLenum target = entry.IsArray() ? surface->TargetLayer() : surface->Target();
state.texture_units[current_bindpoint].texture = handle;
state.texture_units[current_bindpoint].target = target;
state.texture_units[current_bindpoint].swizzle.r =
MaxwellToGL::SwizzleSource(texture.tic.x_source);
state.texture_units[current_bindpoint].swizzle.g =

View File

@@ -23,6 +23,7 @@
#include "video_core/rasterizer_cache.h"
#include "video_core/rasterizer_interface.h"
#include "video_core/renderer_opengl/gl_buffer_cache.h"
#include "video_core/renderer_opengl/gl_global_cache.h"
#include "video_core/renderer_opengl/gl_primitive_assembler.h"
#include "video_core/renderer_opengl/gl_rasterizer_cache.h"
#include "video_core/renderer_opengl/gl_resource_manager.h"
@@ -66,6 +67,10 @@ public:
static_assert(MaxConstbufferSize % sizeof(GLvec4) == 0,
"The maximum size of a constbuffer must be a multiple of the size of GLvec4");
static constexpr std::size_t MaxGlobalMemorySize = 0x10000;
static_assert(MaxGlobalMemorySize % sizeof(float) == 0,
"The maximum size of a global memory must be a multiple of the size of float");
private:
class SamplerInfo {
public:
@@ -94,6 +99,23 @@ private:
float max_anisotropic = 1.0f;
};
struct FramebufferConfigState {
bool using_color_fb{};
bool using_depth_fb{};
bool preserve_contents{};
std::optional<std::size_t> single_color_target;
bool operator==(const FramebufferConfigState& rhs) const {
return std::tie(using_color_fb, using_depth_fb, preserve_contents,
single_color_target) == std::tie(rhs.using_color_fb, rhs.using_depth_fb,
rhs.preserve_contents,
rhs.single_color_target);
}
bool operator!=(const FramebufferConfigState& rhs) const {
return !operator==(rhs);
}
};
/**
* Configures the color and depth framebuffer states.
* @param use_color_fb If true, configure color framebuffers.
@@ -105,7 +127,7 @@ private:
bool using_depth_fb = true, bool preserve_contents = true,
std::optional<std::size_t> single_color_target = {});
/*
/**
* Configures the current constbuffers to use for the draw command.
* @param stage The shader stage to configure buffers for.
* @param shader The shader object that contains the specified stage.
@@ -115,7 +137,7 @@ private:
u32 SetupConstBuffers(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage, Shader& shader,
GLenum primitive_mode, u32 current_bindpoint);
/*
/**
* Configures the current textures to use for the draw command.
* @param stage The shader stage to configure textures for.
* @param shader The shader object that contains the specified stage.
@@ -185,6 +207,7 @@ private:
RasterizerCacheOpenGL res_cache;
ShaderCacheOpenGL shader_cache;
GlobalRegionCacheOpenGL global_cache;
Core::Frontend::EmuWindow& emu_window;
@@ -197,6 +220,7 @@ private:
vertex_array_cache;
std::map<FramebufferCacheKey, OGLFramebuffer> framebuffer_cache;
FramebufferConfigState current_framebuffer_config_state;
std::array<SamplerInfo, Tegra::Engines::Maxwell3D::Regs::NumTextureSamplers> texture_samplers;
@@ -209,8 +233,10 @@ private:
std::size_t CalculateIndexBufferSize() const;
void SetupVertexFormat();
void SetupVertexBuffer();
/// Updates and returns a vertex array object representing current vertex format
GLuint SetupVertexFormat();
void SetupVertexBuffer(GLuint vao);
DrawParameters SetupDraw();

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