Compare commits

...

281 Commits

Author SHA1 Message Date
MerryMage
ef41983c84 dynarmic: Update to 0118ee0
0118ee0 emit_x64_vector: packusdw is SSE4.1
2018-08-10 11:15:04 +01:00
bunnei
6828c25498 Merge pull request #995 from bunnei/gl-buff-bounds
gl_rasterizer_cache: Add bounds checking for gl_buffer copies.
2018-08-09 20:23:30 -04:00
bunnei
5191c20b71 Merge pull request #997 from lioncash/const-func
core: Make function reference parameters const where applicable
2018-08-09 19:30:51 -04:00
bunnei
a91bb7080d Merge pull request #989 from lioncash/log
common/logging: Add missing service log categories
2018-08-09 19:30:14 -04:00
bunnei
69cd213fac Merge pull request #990 from lioncash/entry
fsp_srv: Emplace entries first when building index instead of emplacing last
2018-08-09 19:29:36 -04:00
bunnei
05c33d89a1 Merge pull request #1001 from lioncash/reserve
gl_shader_decompiler: Reserve element memory beforehand in BuildRegisterList()
2018-08-09 19:27:35 -04:00
bunnei
96ef22d3d0 Merge pull request #897 from DarkLordZach/vfs-accuracy-2
vfs: Add VfsFilesystem and fix RealVfs* implementations
2018-08-09 19:22:06 -04:00
bunnei
e8c52d4c89 gl_rasterizer_cache: Add bounds checking for gl_buffer copies. 2018-08-09 19:20:17 -04:00
bunnei
37e1ed3744 Merge pull request #991 from bunnei/ignore-mac
maxwell_3d: Ignore macros that have not been uploaded yet.
2018-08-09 19:16:28 -04:00
Khangaroo
75e12a33ae Implement SNORM for BC5/DXN2 (#998)
* Implement BC5/DXN2 (#996)

- Used by Kirby Star Allies.

* Implement BC5/DXN2 SNORM

UNORM for Kirby Star Allies
SNORM for Super Mario Odyssey
2018-08-09 19:15:32 -04:00
bunnei
e58f7ff843 Merge pull request #999 from lioncash/map
gl_rasterizer_cache: Avoid iterator invalidation issues within InvalidateRegion()
2018-08-09 19:13:27 -04:00
Lioncash
6ef027b958 gl_shader_decompiler: Reserve element memory beforehand in BuildRegisterList()
Avoids potentially perfoming multiple reallocations when we know the
total amount of memory we need beforehand.
2018-08-09 17:29:11 -04:00
Lioncash
59ea37daa7 gl_rasterizer_cache: Avoid iterator invalidation issues within InvalidateRegion()
A range-based for loop can't be used when the container being iterated
is also being erased from.
2018-08-09 15:30:20 -04:00
bunnei
0bfe974281 Merge pull request #992 from bunnei/declr-pred
gl_shader_decompiler: Declare predicates on use.
2018-08-09 14:36:52 -04:00
bunnei
88b18b9ba4 Merge pull request #994 from lioncash/const
gl_rasterizer_cache: Use std::vector::assign vs resize() then copy for the non-tiled case
2018-08-09 14:36:06 -04:00
bunnei
b125137493 Merge pull request #993 from bunnei/smo-vtx-pts
Implement VertexAttribute::Size::Size_16_16_16_16 and PrimitiveTopology::Points.
2018-08-09 13:28:14 -04:00
bunnei
f765a6b902 Merge pull request #984 from bunnei/rt-none
gl_rasterizer: Do not render when no render target is configured.
2018-08-09 13:12:28 -04:00
Khangaroo
5cb6eceecf Implement BC5/DXN2 (#996)
- Used by Kirby Star Allies.
2018-08-09 12:57:13 -04:00
Lioncash
b46a5c42ff buffer_queue: Make reference parameter of SetPreallocatedBuffer const
This is simply copied by value, so there's no need to make it a
modifiable reference.

While we're at it, make the names of the parameters match its
definition.
2018-08-09 03:08:14 -04:00
Lioncash
ff5024ee2a hle_ipc: Make WriteToOutgoingCommandBuffer()'s reference parameter const
This function doesn't modify anything within the reference Thread
instance.
2018-08-09 02:51:38 -04:00
bunnei
5c908c0373 Merge pull request #988 from lioncash/color
common/color: Minor cleanup
2018-08-09 00:37:32 -04:00
bunnei
c333bfc193 Merge pull request #977 from bunnei/bgr565
gl_rasterizer_cached: Implement RenderTargetFormat::B5G6R5_UNORM.
2018-08-08 23:43:04 -04:00
Lioncash
e831b80d69 gl_rasterizer_cache: Invert conditional in LoadGLBuffer()
It's generally easier to follow code using conditionals that operate in
terms of the true case followed by the false case (no chance of
overlooking the exclamation mark).
2018-08-08 23:34:57 -04:00
Lioncash
434f352eb3 gl_rasterizer_cache: Use std::vector::assign in LoadGLBuffer() for the non-tiled case
resize() causes the vector to expand and zero out the added members to
the vector, however we can avoid this zeroing by using assign().

Given we have the pointer to the data we want to copy, we can calculate
the end pointer and directly copy the range of data without the
need to perform the resize() beforehand.
2018-08-08 23:34:58 -04:00
bunnei
5b1b758326 Merge pull request #987 from lioncash/vec
vector_math: Use variable template version of is_signed in Vec classes
2018-08-08 23:30:40 -04:00
bunnei
dfc3eed0cb maxwell_to_gl: Implement VertexAttribute::Size::Size_16_16_16_16.
- Used by Super Mario Odyssey (in game).
2018-08-08 23:28:17 -04:00
bunnei
06d0b96ca9 maxwell_to_gl: Implement PrimitiveTopology::Points.
- Used by Super Mario Odyssey (in game).
2018-08-08 23:28:00 -04:00
bunnei
4283019aa0 gl_shader_decompiler: Declare predicates on use.
- Used by Super Mario Odyssey (when going in game).
2018-08-08 23:26:31 -04:00
bunnei
efe6b473c5 maxwell_3d: Ignore macros that have not been uploaded yet.
- Used by Super Mario Odyssey (in game).
2018-08-08 23:25:37 -04:00
Lioncash
557c466994 gl_rasterizer_cache: Make pointer const in LoadGLBuffer()
This is only ever read from, so we can make the data it's pointing to
const.
2018-08-08 23:14:57 -04:00
bunnei
25ba4d1b68 Merge pull request #982 from bunnei/stub-unk-63
gl_shader_decompiler: Stub input attribute Unknown_63.
2018-08-08 22:28:18 -04:00
Zach Hilman
668458525e vfs: Fix documentation 2018-08-08 21:45:04 -04:00
bunnei
2f4f4f147f Merge pull request #986 from mailwl/acc-loadimage
Service/Account: stub LoadImage function
2018-08-08 21:21:06 -04:00
Zach Hilman
94cf327e77 vfs: Fix typo in VfsFilesystem docs 2018-08-08 21:18:45 -04:00
Zach Hilman
2b6128fe0b file_util: Use enum instead of bool for specifing path behavior 2018-08-08 21:18:45 -04:00
Zach Hilman
dad2ae1ee0 loader: Remove unused IdentifyFile overload 2018-08-08 21:18:45 -04:00
Zach Hilman
656e97df16 vfs: Use RealVfsFilesystem for fs-operations in RealVfsDirectory 2018-08-08 21:18:45 -04:00
Zach Hilman
52a2e42cb9 file_sys: Add missing include in savedata_factory 2018-08-08 21:18:45 -04:00
Zach Hilman
4b471f0554 core: Port core to VfsFilesystem for file access 2018-08-08 21:18:45 -04:00
Zach Hilman
aaa8fdea52 vfs: Add unreachable assert to file permissions converter 2018-08-08 21:18:45 -04:00
Zach Hilman
2de2ec25d6 vfs: Add RealVfsFilesystem implementation 2018-08-08 21:18:45 -04:00
Zach Hilman
3f82dad1e4 file_util: Add platform-specific slash option to SanitizePath 2018-08-08 21:18:45 -04:00
Zach Hilman
3bf488ce52 vfs: Add VfsFilesystem interface and default implementation 2018-08-08 21:18:45 -04:00
Zach Hilman
b36dee364e filesystem: Remove unnecessary if conditions 2018-08-08 21:18:45 -04:00
bunnei
ddec200290 gl_rasterizer: Do not render when no render target is configured.
- Used by Super Mario Odyssey.
2018-08-08 19:29:45 -04:00
bunnei
cf917a5e93 Merge pull request #976 from bunnei/shader-imm
gl_shader_decompiler: Let OpenGL interpret floats.
2018-08-08 19:17:01 -04:00
bunnei
9ceceb212f Merge pull request #981 from bunnei/cbuf-corrupt
maxwell_3d: Use correct const buffer size and check bounds.
2018-08-08 19:16:34 -04:00
bunnei
9f48454ea9 Merge pull request #978 from bunnei/fixioctl
nvhost_gpu: Don't over copy IoctlSubmitGpfifo.
2018-08-08 19:16:14 -04:00
Lioncash
7353cfc781 fsp_srv: Use std::string_view's copy() function instead of strncpy()
Given elements inserted into a vector are zeroed out, we can just copy
MAX_LEN - 1 elements and the data will already be properly null
terminated.
2018-08-08 18:51:52 -04:00
Lioncash
4afb05d0cc fsp_srv: Emplace entries first when building index instead of emplacing last
The current way were doing it would require copying a 768 character
buffer (part of the Entry struct) to the new element in the vector.
Given it's a plain array, std::move won't eliminate that.

Instead, we can emplace an instance directly into the destination buffer
and then fill it out, avoiding the need to perform any unnecessary
copies.

Given this is done in a loop, we can request the destination to allocate
all of the necessary memory ahead of time, avoiding the need to
potentially keep reallocating over and over on every few insertions into
the vector.
2018-08-08 18:51:41 -04:00
bunnei
cc2526dd51 Merge pull request #985 from bunnei/rt-r11g11b10
gpu: Add R11G11B10_FLOAT to RenderTargetBytesPerPixel.
2018-08-08 18:21:34 -04:00
bunnei
096b04f1a4 Merge pull request #979 from bunnei/vtx88
maxwell_to_gl: Implement VertexAttribute::Size::Size_8_8.
2018-08-08 18:18:50 -04:00
bunnei
756e1e6f9b Merge pull request #975 from bunnei/am-stub
am: Stub SetScreenShotImageOrientation.
2018-08-08 16:46:45 -04:00
Lioncash
6e90f0bf6a common/logging: Add missing service log categories
These weren't added when the services were introduced.
2018-08-08 16:46:28 -04:00
bunnei
599c011f40 Merge pull request #980 from bunnei/fix-logs
renderer_opengl: Use trace log in a few places.
2018-08-08 16:45:42 -04:00
Lioncash
5a9c00ea04 common/color: Remove unnecessary const qualifiers on return types
These are just superfluous and not necessesary
2018-08-08 16:17:41 -04:00
Lioncash
76197a4be9 common/color: Get rid of undefined behavior
Gets rid of type punning via reinterpret_cast within functions. Instead,
we use memcpy to transfer the contents across types.
2018-08-08 16:14:37 -04:00
Lioncash
cc9d7bbf01 vector_math: Use variable template version of is_signed in Vec classes
Same behavior, less code
2018-08-08 15:53:42 -04:00
bunnei
d224eb7c39 Merge pull request #966 from lioncash/modernize
common: Convert type traits templates over to variable template versions where applicable
2018-08-08 15:28:34 -04:00
bunnei
fd9da4232b Merge pull request #850 from DarkLordZach/icon-meta
Add Icons and Metadata Support
2018-08-08 12:27:19 -04:00
bunnei
507e6ae100 Merge pull request #968 from lioncash/vec
vector_math: Minor cleanups
2018-08-08 12:00:13 -04:00
bunnei
6a2f415298 Merge pull request #969 from lioncash/lz4
externals/CMakeLists: Add EXCLUDE_FROM_ALL to lz4's add_subdirectory() command
2018-08-08 11:59:14 -04:00
bunnei
448264e719 Merge pull request #958 from lioncash/nv-global
nvdrv: Get rid of global std::weak_ptr
2018-08-08 11:58:45 -04:00
mailwl
c0d44d3b2a Service/Account: stub LoadImage function 2018-08-08 14:42:54 +03:00
bunnei
f156a45c01 Merge pull request #972 from lioncash/catch
externals: Update catch to 2.3.0
2018-08-08 03:00:57 -04:00
bunnei
6a5d032809 Merge pull request #965 from lioncash/unused-files
hle: Remove unused romfs.cpp/.h
2018-08-08 03:00:38 -04:00
bunnei
4941e3d412 Merge pull request #974 from lioncash/acc
acc: Add missing function table entries for GetUserCount
2018-08-08 02:56:00 -04:00
bunnei
7bf422d58c gpu: Add R11G11B10_FLOAT to RenderTargetBytesPerPixel.
- Used by Super Mario Odyssey.
2018-08-08 02:42:14 -04:00
Mat M
9fde7d739a Merge pull request #983 from mailwl/hid-fix
hid: fix IsSixAxisSensorAtRest() response
2018-08-08 02:37:32 -04:00
mailwl
3c498189b6 hid: fix IsSixAxisSensorAtRest() response 2018-08-08 09:36:23 +03:00
bunnei
7f0d0a93f7 gl_shader_decompiler: Stub input attribute Unknown_63. 2018-08-08 02:35:59 -04:00
bunnei
57982df105 maxwell_3d: Use correct const buffer size and check bounds.
- Fixes mem corruption with Super Mario Odyssey and Pokkén Tournament DX.
2018-08-08 02:10:25 -04:00
bunnei
8c6338b6f9 renderer_opengl: Use trace log in a few places. 2018-08-08 01:53:23 -04:00
bunnei
c120ed7d18 maxwell_to_gl: Implement VertexAttribute::Size::Size_8_8. 2018-08-08 01:50:53 -04:00
bunnei
0f834e2284 nvhost_gpu: Don't over copy IoctlSubmitGpfifo. 2018-08-08 01:49:47 -04:00
bunnei
aaf8d9ac2f gl_rasterizer_cached: Implement RenderTargetFormat::B5G6R5_UNORM.
- Used by Super Mario Odyssey.
2018-08-08 01:48:27 -04:00
bunnei
e542356d0c gl_shader_decompiler: Let OpenGL interpret floats.
- Accuracy is lost in translation to string, e.g. with NaN.
- Needed for Super Mario Odyssey.
2018-08-08 01:45:23 -04:00
bunnei
b7fb9f2071 am: Stub SetScreenShotImageOrientation.
- Used by Super Mario Odyssey.
2018-08-08 00:41:35 -04:00
Lioncash
934a2b9604 acc: Add missing function table entries for GetUserCount
Given this is stubbed within the common module in
5ac7b84, it should be added to the other relevant tables as well.
2018-08-07 22:50:45 -04:00
bunnei
2bc296801a acc: Stub GetUserCount. (#973)
- Used by Pokken Tournament DX.
2018-08-07 22:39:12 -04:00
bunnei
80cfd88e4e Merge pull request #967 from lioncash/sign
file_util: Avoid sign-conversions in WriteArray() and ReadArray()
2018-08-07 22:37:00 -04:00
Lioncash
d378d98e26 nvdrv: Get rid of global std::weak_ptr
Rather than use global state, we can simply pass the instance into the
NVFlinger instance directly.
2018-08-07 21:53:05 -04:00
Lioncash
766c1a2d50 vector_math: Remove unimplemented function prototypes 2018-08-07 21:33:48 -04:00
Lioncash
5c323d96e0 vector_math: Make functions constexpr where applicable 2018-08-07 21:32:05 -04:00
Lioncash
4e3bc37791 vector_math: Convert typedefs to type aliases 2018-08-07 21:15:10 -04:00
Lioncash
a7d6efc520 common: Convert type traits templates over to variable template versions where applicable
Uses the C++17 inline variable variants
2018-08-07 19:34:47 -04:00
Lioncash
cd1a96f389 hle: Remove unused romfs.cpp/.h
These files are no longer used, so we can get rid of them.
2018-08-07 19:34:12 -04:00
Lioncash
62b0b83fd8 externals: Update catch to 2.3.0
Updates the library from 2.2.3 to 2.3.0
2018-08-07 19:26:13 -04:00
bunnei
825e8cb925 Merge pull request #971 from DarkLordZach/mbedtls-2.12.0
externals/mbedtls: Update to mbedtls v2.12.0
2018-08-07 19:25:50 -04:00
Zach Hilman
1fbb7aff1e externals/mbedtls: Update to mbedtls v2.12.0 2018-08-07 19:22:49 -04:00
bunnei
4fa3511a63 Merge pull request #964 from Hexagon12/lower-logs
Lowered down the logging for command processor methods
2018-08-07 19:00:19 -04:00
Hexagon12
7139f05fc5 Fixed the sRGB pixel format (#963)
* Changed the sRGB pixel format return

* Add a message about SRGBA -> RGBA conversion
2018-08-07 18:59:50 -04:00
Lioncash
e530f82dd3 externals/CMakeLists: Add EXCLUDE_FROM_ALL to lz4's add_subdirectory() command
We don't need to build the lz4 CLI tool, or anything else. We just want
to build in the library statically, so we specify this to ensure that.
Now, we don't potentially build unnecessary targets.
2018-08-07 18:40:52 -04:00
Zach Hilman
1abfd4166e configure_gamelist: Use explicit QVariant constructor 2018-08-07 17:10:10 -04:00
bunnei
b9829a05be Merge pull request #920 from DarkLordZach/titlekey
content_archive: Add support for titlekey cryptography
2018-08-07 17:01:25 -04:00
bunnei
7ed8565978 Merge pull request #957 from lioncash/event
nvflinger: Correct typo in name of composition event
2018-08-07 15:56:51 -04:00
bunnei
6576bc8927 Merge pull request #954 from lioncash/hid
services/hid: Add ActivateNpadWithRevision() to the hid function info array
2018-08-07 15:56:34 -04:00
bunnei
2d57cbaec1 Merge pull request #960 from lioncash/apm
service/apm: Add the apm:sys service
2018-08-07 14:57:12 -04:00
bunnei
3242e7c164 Merge pull request #950 from lioncash/hotkey
qt/hotkey: Get rid of global hotkey map instance
2018-08-07 14:45:45 -04:00
bunnei
c707240685 Merge pull request #948 from hcorion/fix-mbedtls-installing-files
CMakeLists: Make mbedtls and cubeb not install headers and libraries
2018-08-07 14:27:43 -04:00
bunnei
573a66c23d Merge pull request #955 from lioncash/view
nvflinger: Use std::string_view in OpenDisplay()
2018-08-07 14:26:51 -04:00
bunnei
97c6f984dc Merge pull request #953 from lioncash/time
service/time: Amend command IDs of ToPosixTime() and ToPosixTimeWithMyRule()
2018-08-07 14:25:52 -04:00
bunnei
07595987ac Merge pull request #959 from KAMiKAZOW/cubeb-compilation
Make building cubeb optional
2018-08-07 14:25:17 -04:00
bunnei
b09c4f45c7 Merge pull request #956 from lioncash/nv
nvdrv: Get rid of indirect inclusions
2018-08-07 14:23:32 -04:00
Lioncash
0735a0c8a1 file_util: Avoid sign-conversions in WriteArray() and ReadArray()
Prevents compiler warnings.
2018-08-07 13:51:37 -04:00
Hexagon12
bc6d91a103 Lowered down the logging for methods 2018-08-07 19:51:40 +03:00
bunnei
c392650e21 Merge pull request #952 from lioncash/usb
service: Add usb services
2018-08-07 11:27:49 -04:00
bunnei
8f73f41824 Merge pull request #949 from lioncash/priv
client_port: Make all data members private
2018-08-07 11:20:26 -04:00
bunnei
c4397ec77e Merge pull request #951 from lioncash/glad
externals: Update glad to 0.1.26
2018-08-07 11:20:02 -04:00
bunnei
438d9aa407 Merge pull request #961 from DarkLordZach/nca-as-drd-scope
loader: Fix scope error in DeconstructedRomDirectory
2018-08-07 11:18:52 -04:00
Zach Hilman
3e81c09094 loader: Fix scope error in DeconstructedRomDirectory 2018-08-07 10:37:38 -04:00
Lioncash
12ab5a0547 service/apm: Add the apm:sys service
Adds the basic skeleton of the apm:sys service based off the information
on Switch Brew.
2018-08-07 10:05:26 -04:00
Lioncash
d3f64785d1 nvflinger: Correct typo in name of composition event 2018-08-07 09:03:52 -04:00
Lioncash
300ab211e8 nvdrv: Make Ioctl()'s definition match its prototype
The only reason this wasn't a compilation error is because we use
little-endian systems.
2018-08-07 08:57:11 -04:00
Lioncash
fa8017295b nvdrv: Get rid of indirect inclusions 2018-08-07 08:54:50 -04:00
Lioncash
e40b0cf437 nvflinger: Get rid of indirect inclusions 2018-08-07 08:32:05 -04:00
Lioncash
7e49881b7f nvflinger: Use std::string_view in OpenDisplay()
We don't need to use a std::string here, given all that's done is
comparing the character sequence against another. This allows passing
regular const char* without needing to heap allocate.
2018-08-07 08:32:06 -04:00
KAMiKAZOW
0f5c4615ae Make building cubeb optional 2018-08-07 13:21:56 +02:00
Lioncash
890e543304 services/hid: Add ActivateNpadWithRevision() to the hid function info array
Updated based off the information on Switch Brew.
2018-08-07 03:23:20 -04:00
Lioncash
20c976ff2a service/time: Amend command IDs of ToPosixTime() and ToPosixTimeWithMyRule()
Updates the ID of these based off the information on Switch Brew.
2018-08-07 03:18:07 -04:00
Lioncash
45bc449ff9 service: Add usb services
Adds basic skeleton for the usb services based off the information provided by Switch Brew.
2018-08-07 03:14:03 -04:00
Lioncash
c8f6754417 qt/hotkey: Get rid of global hotkey map instance
Instead, we make a proper registry class and house it within the main
window, then pass it to whatever needs access to the loaded hotkeys.

This way, we avoid a global variable, and don't need to initialize a
std::map instance before the program can do anything.
2018-08-07 02:28:17 -04:00
Lioncash
0db0e4c8f3 externals: Update glad to 0.1.26
Updates the library from 0.1.25. Mainly fixes issues related to macOS,
but we may as well update the library.
2018-08-07 02:24:34 -04:00
Zach Hilman
91cfe70301 loader: Add icon and title support to XCI 2018-08-06 23:13:42 -04:00
Zach Hilman
e4422b09b6 Fix missing qjpeg DLL 2018-08-06 23:06:33 -04:00
Zach Hilman
5927cf0e17 Use const where applicable 2018-08-06 23:06:33 -04:00
Zach Hilman
9e88f03e75 Avoid parsing RomFS to directory in NCA 2018-08-06 23:06:33 -04:00
Lioncash
da2f00ab7d client_port: Make all data members private
These members don't need to be entirely exposed, we can instead expose
an API to operate on them without directly needing to mutate them

We can also guard against overflow/API misuse this way as well, given
active_sessions is an unsigned value.
2018-08-06 23:05:17 -04:00
bunnei
826b1394e8 Merge pull request #931 from DarkLordZach/nca-as-drd
loader: Make AppLoader_NCA rely on directory loading code
2018-08-06 22:02:41 -04:00
bunnei
0c3c91e41c Merge pull request #947 from lioncash/encoding
game_list: Use QString::fromStdString() where applicable instead of c_str()
2018-08-06 22:02:01 -04:00
Hedges
e2b74f6354 GDBStub works with both Unicorn and Dynarmic now (#941)
* GDBStub works with both Unicorn and Dynarmic now

* Tidy up
2018-08-06 22:01:24 -04:00
bunnei
e218d79cc2 Merge pull request #943 from lioncash/decl
game_list: Join declarations and assignments in onTextChanged()
2018-08-06 22:00:49 -04:00
bunnei
75df8a3969 Merge pull request #946 from lioncash/compress
qt/main: Collapse if statement in UpdateRecentFiles()
2018-08-06 21:34:20 -04:00
bunnei
645d35ac32 Merge pull request #944 from lioncash/menu
qt: Don't show error dialog when canceling the Load Folder dialog
2018-08-06 21:33:23 -04:00
bunnei
168958f8e2 Merge pull request #942 from lioncash/default
qt: Minor cleanup-related changes
2018-08-06 21:32:25 -04:00
Zion Nimchuk
e3321c2e00 Make mbedtls and cubeb not install headers and libraries 2018-08-06 18:32:07 -07:00
bunnei
f179e87864 Merge pull request #940 from lioncash/private
kernel/event: Make data members private
2018-08-06 21:31:25 -04:00
bunnei
cf82358ee6 Merge pull request #936 from bunnei/avoid-copies
gl_rasterizer_cache: Avoid superfluous surface copies.
2018-08-06 21:29:29 -04:00
bunnei
83ef37ca37 Merge pull request #934 from lioncash/chrono
core_timing: Make GetGlobalTimeUs() return std::chrono::microseconds
2018-08-06 18:03:05 -04:00
James Rowe
bf51bbffcb Merge pull request #945 from lioncash/exist
qt/main: Better file-existence checking within OnMenuRecentFile() and UpdateUITheme()
2018-08-06 13:54:15 -06:00
Lioncash
96b6ad11c1 qt/main: Avoid sign conversions in UpdateRecentFiles()
This was intermixing signed and unsigned values when they could all just
be signed.
2018-08-06 15:42:44 -04:00
Lioncash
10d693b9c2 game_list: Remove unnecessary conversion to std::string in ValidateEntry()
We can just use the file interfaces that Qt provides to prevent needing
to convert to std::string.
2018-08-06 15:06:29 -04:00
Lioncash
a5ac53dd4c game_list: Use QString::fromStdString() where applicable instead of c_str()
The codec used by Qt for const char* and std::string don't necessarily
have to be the same depending on locale. Therefore, we should be using
the correct functions to do the conversions.
2018-08-06 15:06:30 -04:00
Lioncash
251e92513a game_list: Join declarations and assignments in onTextChanged()
There's no need to keep these separate from one another.
2018-08-06 14:35:40 -04:00
Lioncash
cf983888cc qt/main: Collapse if statement in UpdateRecentFiles()
Given the function accepts a boolean, we don't need to use an if
statement here and repeat ourselves.
2018-08-06 14:32:28 -04:00
Lioncash
2b2dc00bfd qt/main: Better file-existence checking within OnMenuRecentFile() and UpdateUITheme()
In OnMenuRecentFile() we don't need to construct a QFileInfo instance
just to check if a file exists, we can just use the static member
function to do that (which Qt's documentation also notes as quicker than
constructing an instance).

In UpdateUITheme(), we just want to try and open the file and check the
success of that operation. Technically speaking, between the existence
check and the open call, the file can be deleted or moved, but still
appear to succeed in code. i.e.

1. Existence check -> Returns true
2. File is moved/deleted
3. Open is called, the return value of which isn't checked
4. Nonsense behavior

This way we combine the existence check and the open into one.
2018-08-06 14:17:13 -04:00
Lioncash
d33f641912 qt: Don't show error dialog when canceling the Load Folder dialog
Previously, when canceling out of the Load Folder dialog, a user would
get an error dialog about the selected folder not containing a main
file, however, by canceling out of the dialog, no selection was actually
made.
2018-08-06 14:02:34 -04:00
Lioncash
9764b4ec0e qt/game_list_p: Remove redundant base class constructor invocations
These occur automatically without the need to call them. While we're at
it, also std::move the QString instance into its member variable.
2018-08-06 13:42:12 -04:00
Lioncash
7846295a8f qt: Add missing override specifiers where applicable 2018-08-06 13:29:14 -04:00
Lioncash
00a68c5eea qt: Default destructors where applicable
Makes code consistent with our style of defaulting special member
functions where applicable.
2018-08-06 13:27:08 -04:00
Lioncash
2feb1a8ba6 kernel/event: Make data members private
Instead we can simply provide accessors to the required data instead of
giving external read/write access to the variables directly.
2018-08-06 12:53:02 -04:00
bunnei
1ac45342dd Merge pull request #933 from lioncash/memory
memory: Correct prototype of ZeroBlock
2018-08-06 12:34:57 -04:00
Mat M
37adf04dcd Merge pull request #937 from mailwl/audout-fix
Service/Audio: audout_a.cpp: remove pragma once
2018-08-06 05:32:23 -04:00
mailwl
2ea0f0fd16 Service/Audio: audout_a.cpp: remove pragma once 2018-08-06 12:29:27 +03:00
bunnei
904d7eaa94 maxwell_3d: Remove outdated assert. 2018-08-05 23:57:19 -04:00
bunnei
57eb936200 gl_rasterizer_cache: Avoid superfluous surface copies. 2018-08-05 23:40:03 -04:00
bunnei
0b80bbeb95 Merge pull request #932 from lioncash/func
core_timing: Use transparent functors where applicable
2018-08-05 23:37:53 -04:00
bunnei
f1b93d63d1 Merge pull request #929 from lioncash/addr
gdbstub: Minor changes
2018-08-05 23:36:26 -04:00
bunnei
03b7ebbc08 Merge pull request #930 from lioncash/thread
address_arbiter: Return by value from GetThreadsWaitingOnAddress()
2018-08-05 23:35:59 -04:00
bunnei
bb21c2198a Merge pull request #925 from bunnei/audren
Implement audren audio output
2018-08-05 23:35:22 -04:00
Lioncash
6c56754322 perf_stats: Correct literal used for MAX_LAG_TIME_US
ms is shorthand for milliseconds, not microseconds, and given there's no
comment indicating that this was intentional, it probably wasn't.
2018-08-05 22:12:58 -04:00
Lioncash
a0c3a46aa9 core_timing: Make GetGlobalTimeUs() return std::chrono::microseconds
Enforces the time unit being returned and also allows using the standard
time utilities to manipulate it.
2018-08-05 22:07:30 -04:00
Lioncash
2a7a2b739b memory: Make prototype parameter names match their definitions
Keeps the code consistent.
2018-08-05 21:39:09 -04:00
Lioncash
4aa31b0618 memory: Correct prototype of ZeroBlock
Previously, the prototype wasn't matching the definition, which has a
Processor parameter before the destination address.
2018-08-05 21:39:06 -04:00
Lioncash
2fc5c783ed memory: Remove unnecessary const qualifiers in prototypes
These aren't necessary, as value-wise const only matters in the
definition.
2018-08-05 21:38:22 -04:00
Lioncash
6edd828101 core_timing: Convert typedef into a type alias
Makes the alias a little more readable from left-to-right.
2018-08-05 21:27:14 -04:00
Lioncash
d9815b523b core_timing: Use transparent functors where applicable
Gets rid of the need to hardcode the type in multiple places. This will
now be deduced automatically, based off the elements in the container
being provided to the algorithm.
2018-08-05 21:19:24 -04:00
Zach Hilman
7f9430f7ae loader: Make AppLoader_NCA rely on directory loading code
Eliminates duplicate code shared between their Load methods, after all the only difference is how the romfs is handled.
2018-08-05 18:28:15 -04:00
bunnei
c8e5c74092 Merge pull request #927 from bunnei/fix-texs
gl_shader_decompiler: Fix TEXS mask and dest.
2018-08-05 16:42:21 -04:00
Lioncash
00f7e584ce gdbstub: Use type alias for breakpoint maps
Rather than having to type out the full std::map type signature, we can
just use a straightforward alias. While we're at it, rename
GetBreakpointList to GetBreakpointMap, which makes the name more
accurate. We can also get rid of unnecessary u64 static_casts, since
VAddr is an alias for a u64.
2018-08-05 16:41:22 -04:00
Lioncash
89c076b4b1 gdbstub: Move all file-static variables into the GDBStub namespace
Keeps everything under the same namespace. While we're at it, enclose
them all within an inner anonymous namespace.
2018-08-05 16:41:18 -04:00
bunnei
c0af42d6eb Merge pull request #912 from lioncash/global-var
video_core: Eliminate the g_renderer global variable
2018-08-05 16:37:39 -04:00
Lioncash
7a77d0a71e address_arbiter: Return by value from GetThreadsWaitingOnAddress()
In all cases the vector being supplied is empty, so we can just return
by value in these instances.
2018-08-05 16:29:17 -04:00
Lioncash
ca96f8db4e gdbstub: Replace PAddr alias with VAddr
In all cases, a virtual address is being passed in, not a physical one.
2018-08-05 15:56:01 -04:00
Mat M
e06953626c Merge pull request #928 from MerryMage/dynarmic
externals: Update dynarmic to 4f96c63
2018-08-05 15:02:49 -04:00
MerryMage
e816745b19 externals: Update dynarmic to 4f96c63
4f96c63 emit_x64_vector_floating_point: Simplify FPVector{Min,Max}
e15fdfe emit_x64_vector_floating_point: Simplify Get*Vector functions
734a00b emit_x64_floating_point: Remove EmitProcessNaNs
fd45191 devirtualize: Replace DEVIRT macro with function template
67ba5d0 fuzz_with_unicorn: Remove FCVT_float from ignore list
66e6dd1 a32_emit_x64: std::move A32::UserConfig in the constructor
b4890b6 emit_x64_floating_point: Use EmitPostProcessNaNs in EmitFPMulX
18b2943 emit_x64_floating_point: Remove unnecessary DenormalsAreZero from EmitFPSingleToDouble and EmitFPDoubleToSingle
df1f81f emit_x64_floating_point: Simplify EmitFP{Min,Max}{,Numeric}{32,64}
21fb1c3 emit_x64_floating_point: Reduce NaN processing overhead
f5c9f0f A64: Implement FMULX, scalar single/double variant
8f47773 IR: Implement FPMulX IR instruction
79e6440 fuzz_with_unicorn: Randomize SP
33c80e3 fuzz_with_unicorn: Randomize PC
8d41024 testenv: Make code_mem mobile
a9fae0e emit_x64_vector: Vectorize 32-bit variants of paired min/max
8926a92 emit_x64_vector: Improve code emission of VectorGetElement* for index == 0
e20bd38 reg_alloc: Do a UseScratch if a Use destination is too small
a19fa0e fuzz_with_unicorn: Randomize FPCR.AHP and FPCR.FZ16
775f368 emit_x64_floating_point: AVX implementation of ForceToDefaultNaN
71018a1 emit_x64_vector_floating_point: Prefer blendvp{s,d} to vblendvp{s,d} where possible
137f4b3 backend_x64: Remove all use of xmm0
e73d67a emit_x64_vector_floating_point: AVX implementation of ForceToDefaultNaN
43cca54 emit_x64_vector_floating_point: Reduce codesize of ForceToDefaultNaN
5dc40f4 emit_x64_vector_floating_point: Reduce codesize of EmitTwoOpVectorOperation
07622ee emit_x64_vector_floating_point: Correct FMA in FTZ mode
621c85b emit_x64_floating_point: DenormalsAreZero is redundant as hardware already does DAZ
3d0ebaa emit_x64_floating_point: FlushToZero is redundant as hardware already does FTZ
f626ff8 backend_x64: Fix FPVectorMulAdd and FPMulAdd NaN handling with denormals
adeb9d9 a32/fuzz_arm: Disable vfp tests
19ea70d fuzz_with_unicorn: Randomize FPCR.FZ
895db36 backend_x64: Fix bugs when FPCR.FZ=1
d7e2de2 fuzz_with_unicorn: Extract RandomFpcr function
c858d6c fp/info: Deduplicate functions
5b88ec2 emit_x64_floating_point: Deduplicate EmitFPMulAdd implementation
2018-08-05 17:04:23 +01:00
bunnei
fd715e54a1 gl_shader_decompiler: Fix TEXS mask and dest. 2018-08-05 01:47:09 -04:00
bunnei
ce46fb27ca Merge pull request #926 from ogniK5377/vertex-attrib-format
gl_rasterizer: Fix glVertexAttribFormat for integers
2018-08-05 01:38:54 -04:00
bunnei
b46df98e93 audio_core: Implement audren_u audio playback. 2018-08-04 21:54:30 -04:00
David Marcec
b96010bfa9 added braces for conditions 2018-08-05 11:36:55 +10:00
David Marcec
6d1e30e041 fix the attrib format for ints 2018-08-05 11:29:21 +10:00
bunnei
a0a605df06 Merge pull request #924 from lioncash/arp
service: Add arp services
2018-08-04 21:20:26 -04:00
bunnei
cd96c04339 Merge pull request #921 from lioncash/view
core/crypto: Minor changes
2018-08-04 21:17:10 -04:00
bunnei
3f4fcd582e Merge pull request #923 from lioncash/pragma
service: Remove redundant #pragma once directives
2018-08-04 21:16:30 -04:00
bunnei
1dee8ceda1 audio_core: Use s16 where possible for audio samples. 2018-08-04 18:22:58 -04:00
bunnei
f1cb3903ac audio_core: Port codec code from Citra for ADPCM decoding. 2018-08-04 18:22:58 -04:00
Lioncash
de72956181 service: Add arp services
Adds the basic skeleton of the arp services based off the information
provided by Switch Brew.
2018-08-04 18:01:12 -04:00
Lioncash
df51207ed2 service: Remove redundant #pragma once directives
These don't do anything within .cpp files (we don't include cpp files,
so...)
2018-08-04 17:39:08 -04:00
Lioncash
0d04ee97dc aes_util: Add static assertion to Transcode() and XTSTranscode() to ensure well-defined behavior
These functions should only be given trivially-copyable types.
2018-08-04 17:30:52 -04:00
Lioncash
64c8212ae1 aes_util: Make CalculateNintendoTweak() an internally linked function
This function doesn't directly depend on class state, so it can be
hidden entirely from the interface in the cpp file.
2018-08-04 17:30:48 -04:00
Lioncash
b25468b498 aes_util: Make Transcode() a const member function
This doesn't modify member state, so it can be made const.
2018-08-04 16:49:42 -04:00
Lioncash
8da651ac4d core/crypto: Remove unnecessary includes 2018-08-04 16:44:07 -04:00
Lioncash
c1f76abfaf key_manager: Use regular std::string instead of std::string_view
The benefit of std::string_view comes from the idea of avoiding copies
(essentially acting as a non-owning view), however if we're just going
to copy into a local variable immediately, there's not much benefit
gained here.
2018-08-04 16:37:30 -04:00
bunnei
02fccc0940 cubeb_sink: Support variable sample_rate and num_channels. 2018-08-04 15:30:10 -04:00
Zach Hilman
2cc962e171 content_archive: Add support for titlekey cryptography 2018-08-04 14:57:21 -04:00
bunnei
34b3f83498 audio_core: Sinks need unique names as well. 2018-08-04 14:34:12 -04:00
bunnei
9f846d3aa4 audio_core: Streams need unique names for CoreTiming. 2018-08-04 14:34:12 -04:00
bunnei
2b06301dbf Merge pull request #849 from DarkLordZach/xci
XCI and Encrypted NCA Support
2018-08-04 14:33:11 -04:00
bunnei
13d6593753 Merge pull request #919 from lioncash/sign
gl_shader_manager: Amend sign differences in an assertion comparison in SetShaderUniformBlockBinding()
2018-08-04 14:29:59 -04:00
Lioncash
3b678b9e8e gl_shader_manager: Invert conditional in SetShaderUniformBlockBinding()
This lets us indent the majority of the code and places the error case
first.
2018-08-04 02:57:11 -04:00
Lioncash
dde5dce736 gl_shader_manager: Amend sign differences in an assertion comparison in SetShaderUniformBlockBinding()
Ensures both operands have the same sign in the comparison.

While we're at it, we can get rid of the redundant casting of ub_size to
an int. This type will always be trivial and alias a built-in type (not
doing so would break backwards compatibility at a standard level).
2018-08-04 02:55:03 -04:00
Lioncash
2665457f4a renderer_base: Make Rasterizer() return the rasterizer by reference
All calling code assumes that the rasterizer will be in a valid state,
which is a totally fine assumption. The only way the rasterizer wouldn't
be is if initialization is done incorrectly or fails, which is checked
against in System::Init().
2018-08-04 02:36:58 -04:00
Lioncash
6030c5ce41 video_core: Eliminate the g_renderer global variable
We move the initialization of the renderer to the core class, while
keeping the creation of it and any other specifics in video_core. This
way we can ensure that the renderer is initialized and doesn't give
unfettered access to the renderer. This also makes dependencies on types
more explicit.

For example, the GPU class doesn't need to depend on the
existence of a renderer, it only needs to care about whether or not it
has a rasterizer, but since it was accessing the global variable, it was
also making the renderer a part of its dependency chain. By adjusting
the interface, we can get rid of this dependency.
2018-08-04 02:36:57 -04:00
bunnei
762fcaf5de Merge pull request #911 from lioncash/prototype
video_core: Remove unimplemented Start() function prototype
2018-08-04 02:18:38 -04:00
bunnei
b0129489ea Merge pull request #913 from lioncash/unused-func
memory: Remove unused GetSpecialHandlers() function
2018-08-04 02:17:44 -04:00
bunnei
206f2e3436 Merge pull request #914 from lioncash/codeset
kernel/process: Use accessors instead of class members for referencing segment array
2018-08-04 02:17:25 -04:00
bunnei
d43dad001e Merge pull request #917 from lioncash/crash
kernel/thread: Fix potential crashes introduced in 26de4bb5
2018-08-04 01:19:01 -04:00
Lioncash
e93fa7f2cc kernel/thread: Fix potential crashes introduced in 26de4bb521
This amends cases where crashes can occur that were missed due to the
odd way the previous code was set up (using 3DS memory regions that
don't exist).
2018-08-03 23:49:10 -04:00
bunnei
29f31356d8 Merge pull request #910 from lioncash/unused
gl_shader_decompiler: Remove unused variable in GenerateDeclarations()
2018-08-03 15:54:11 -04:00
Lioncash
e649db8c6b kernel/process: Use std::array where applicable 2018-08-03 14:46:30 -04:00
Lioncash
2beda7c2b3 kernel/process: Use accessors instead of class members for referencing segment array
Using member variables for referencing the segments array increases the
size of the class in memory for little benefit. The same behavior can be
achieved through the use of accessors that just return the relevant
segment.
2018-08-03 14:45:45 -04:00
Lioncash
59b04c0df6 memory: Remove unused GetSpecialHandlers() function
This is just unused code, so we may as well get rid of it.
2018-08-03 14:20:50 -04:00
bunnei
40e63ede6d Merge pull request #908 from lioncash/memory
core/memory: Get rid of 3DS leftovers
2018-08-03 14:07:49 -04:00
bunnei
64806a8397 Merge pull request #909 from lioncash/const
gl_shader_manager: Make ProgramManager's GetCurrentProgramStage() a const member function
2018-08-03 14:07:30 -04:00
Lioncash
b4e050e6c4 video_core: Remove unimplemented Start() function prototype
Given this has no definition, we can just remove it entirely.
2018-08-03 12:48:14 -04:00
Lioncash
b45e5c2399 gl_shader_decompiler: Remove unused variable in GenerateDeclarations()
This variable was being incremented, but we were never actually using
it.
2018-08-03 12:18:31 -04:00
Lioncash
555d76d065 gl_shader_manager: Make ProgramManager's GetCurrentProgramStage() a const member function
This function doesn't modify class state, so it can be made const.
2018-08-03 12:08:17 -04:00
Lioncash
26de4bb521 core/memory: Get rid of 3DS leftovers
Removes leftover code from citra that isn't needed.
2018-08-03 11:22:47 -04:00
David
c1d54f4aea Added ability to change username & language code in the settings ui. Added IProfile::Get and SET::GetLanguageCode for libnx tests (#851) 2018-08-03 11:02:55 -04:00
bunnei
b6d61abd78 Merge pull request #895 from lioncash/sink
sink_details: std::move std::function instances
2018-08-03 11:00:56 -04:00
bunnei
40e78b9a89 Merge pull request #898 from lioncash/mig
service: Add migration services
2018-08-03 11:00:27 -04:00
bunnei
ef9433411d Merge pull request #900 from lioncash/init
math_util: Always initialize members of Rectangle
2018-08-03 11:00:10 -04:00
bunnei
00ba704a7f Merge pull request #892 from lioncash/global
video_core: Make global EmuWindow instance part of the base renderer …
2018-08-03 00:31:32 -04:00
bunnei
4c3c608d59 Merge pull request #894 from lioncash/object
kernel: Move object class to its own source files
2018-08-03 00:28:43 -04:00
bunnei
4b84d5bcec Merge pull request #904 from lioncash/static
kernel/thread: Minor changes
2018-08-03 00:19:29 -04:00
bunnei
9e48ca23b2 Merge pull request #906 from lioncash/override
input_common: minor changes
2018-08-03 00:18:52 -04:00
bunnei
49d817134a Merge pull request #907 from lioncash/slot
yuzu: Use Qt 5 signal/slots where applicable
2018-08-03 00:17:55 -04:00
bunnei
61ed68f3d0 Merge pull request #905 from lioncash/vma
kernel/vm_manager: Minor changes
2018-08-02 23:18:03 -04:00
bunnei
291ccf7257 Merge pull request #903 from lioncash/copy
vfs_vector: Minor changes
2018-08-02 23:01:19 -04:00
bunnei
52da0ce399 Merge pull request #901 from lioncash/ref
gl_shader_manager: Take ShaderSetup instances by const reference in UseProgrammableVertexShader() and UseProgrammableFragmentShader()
2018-08-02 23:00:56 -04:00
Lioncash
db340f6402 yuzu: Use Qt 5 signal/slots where applicable
Makes the signal/slot connections type-safe instead of string-based.
2018-08-02 22:18:33 -04:00
Lioncash
684fc2c320 input_common: Use std::move where applicable
Avoids unnecessary atomic reference count increments and decrements
2018-08-02 21:51:11 -04:00
Lioncash
29b6afb82f input_common: Add missing override specifiers 2018-08-02 21:44:25 -04:00
bunnei
4de18e054b Merge pull request #899 from lioncash/unused
hw: Remove unused files
2018-08-02 14:58:01 -04:00
bunnei
bae1822aed Merge pull request #902 from lioncash/array
gl_state: Make texture_units a std::array
2018-08-02 14:57:42 -04:00
bunnei
e79e967151 Merge pull request #891 from lioncash/ns
service/ns: Add missing ns services
2018-08-02 14:57:24 -04:00
greggameplayer
fe64e1d38e Implement RGB32F PixelFormat (#886) (used by Go Vacation) 2018-08-02 14:56:38 -04:00
bunnei
d0bd01146e Merge pull request #893 from lioncash/psc
service: Add the psc services
2018-08-02 14:53:55 -04:00
Lioncash
d94a173877 kernel/vm_manager: Convert loop into std::any_of() 2018-08-02 12:46:15 -04:00
Lioncash
c4e0c3d76c kernel/vm_manager: Use const where applicable
Makes our immutable state explicit.
2018-08-02 12:21:46 -04:00
Lioncash
ce5ad45278 kernel/vm_manager: Use the VAddr type alias in CarveVMA()
These two variables correspond to address ranges.
2018-08-02 12:15:50 -04:00
Lioncash
6058c84b79 kernel/thread: Make GetFreeThreadLocalSlot()'s loop indices size_t
Avoids using a u32 to compare against a range of size_t, which can be a
source of warnings. While we're at it, compress a std::tie into a
structured binding.
2018-08-02 12:01:25 -04:00
Lioncash
fac0e42b2f kernel/thread: Make GetFreeThreadLocalSlot() reference parameter a const reference
This function only reads the data being referenced, it doesn't modify
it, so we can turn the reference into a const reference.
2018-08-02 11:56:11 -04:00
Lioncash
9a50a4f2cc kernel/thread: Make GetFreeThreadLocalSlot() internally linked
This function isn't used outside of this translation unit, so we can
make it internally linked.
2018-08-02 11:54:23 -04:00
Lioncash
42a4c6b79e vfs_vector: Remove unused variable in FindAndRemoveVectorElement()
This wasn't being used for anything, so it can be removed.
2018-08-02 11:40:24 -04:00
Lioncash
cec9e9b811 vfs_vector: Avoid unnecessary copies where applicable
The lambda elements should be taken by const reference here, and we can
move the virtual directory passed to ReplaceFileWithSubdirectory()
2018-08-02 11:37:39 -04:00
Lioncash
6b32e24161 gl_state: Make texture_units a std::array
Gets rid of the use of a raw C array.
2018-08-02 11:19:58 -04:00
Lioncash
d92e8ab062 gl_shader_manager: Take ShaderSetup instances by const reference in UseProgrammableVertexShader() and UseProgrammableFragmentShader()
Avoids performing unnecessary copies of 65560 byte sized ShaderSetup
instances, considering it's only used as part of lookup and not
modified.

Given the parameters were already const, it's likely taking these
parameters by reference was intended but the ampersand was forgotten.
2018-08-02 11:09:46 -04:00
Lioncash
f2a03468b1 math_util: Always initialize members of Rectangle
Prevents potentially using the members uninitialized.
2018-08-02 10:47:34 -04:00
Lioncash
c6db1c390b hw: Remove unused files
None of these files are used in any meaningful way. They're just
leftovers from citra. Also has the benefit of getting rid of an unused
global variable.
2018-08-02 10:23:10 -04:00
Lioncash
7469e26e5e service: Add migration services
Adds the basic skeleton for the mig:usr service based off information
provided by Switch Brew.
2018-08-02 10:09:45 -04:00
bunnei
a03c644aed Merge pull request #896 from lioncash/audio-out
audio_out: Use Buffer::Tag alias in GetTagsAndReleaseBuffers()'s prototype
2018-08-02 09:51:47 -04:00
Lioncash
c1c397d37c audio_out: Use Buffer::Tag alias in GetTagsAndReleaseBuffers()'s prototype
This makes the Buffer::Tag usage consistent with the Stream class's
prototype of GetTagsAndReleaseBuffers().
2018-08-02 05:18:32 -04:00
Lioncash
2bc4ab3958 sink_details: Deduplicate long std::function repetition
We can just use type aliases to avoid needing to write the same long
type twice
2018-08-01 23:56:02 -04:00
Lioncash
89ebef6571 sink_details: std::move std::function instances
Given std::function is allowed to potentially allocate, these should be
std::move'd to prevent potential reallocation (should that ever happen).
2018-08-01 23:50:48 -04:00
Lioncash
bf45092c61 kernel: Move object class to its own source files
General moving to keep kernel object types separate from the direct
kernel code. Also essentially a preliminary cleanup before eliminating
global kernel state in the kernel code.
2018-08-01 23:34:42 -04:00
Lioncash
42c5171322 logging/log: Remove incorrect description in PCV doc comment
PCV isn't the parental control service.
2018-08-01 23:31:31 -04:00
Lioncash
5233040ab4 service: Add psc services
Adds the basic skeleton for the psc services based off the information
provided by Switch Brew.
2018-08-01 23:31:27 -04:00
Lioncash
0f2ac928f2 video_core: Make global EmuWindow instance part of the base renderer class
Makes the global a member of the RendererBase class. We also change this
to be a reference. Passing any form of null pointer to these functions
is incorrect entirely, especially given the code itself assumes that the
pointer would always be in a valid state.

This also makes it easier to follow the lifecycle of instances being
used, as we explicitly interact the renderer with the rasterizer, rather
than it just operating on a global pointer.
2018-08-01 21:40:30 -04:00
bunnei
746d7d4d28 Merge pull request #888 from lioncash/caps
service: Add capture services
2018-08-01 21:34:28 -04:00
bunnei
9bb8720289 Merge pull request #890 from lioncash/logger
lm: Amend name of ILogger
2018-08-01 21:33:11 -04:00
bunnei
16b2fd9fc8 Merge pull request #889 from lioncash/fsp
service/filesystem: Add fsp:ldr and fsp:pr services
2018-08-01 21:32:54 -04:00
bunnei
200c95db8a Merge pull request #887 from lioncash/pcv
service: Add bpc and pcv services
2018-08-01 21:32:36 -04:00
Lioncash
48acb764a4 service/ns: Add missing ns services
Implements the basic skeleton of ns:am2, ns:ec, ns:rid, ns:rt, ns:su,
ns:vm, and ns:web based off the information provided by Switch Brew and
SwIPC.
2018-08-01 18:02:18 -04:00
Lioncash
f77cfab516 lm: Amend name of ILogger
Previously this was being registered with the name "Logger". While we're
at it, also change the name of the class to match it.
2018-08-01 17:08:44 -04:00
Lioncash
208a457909 service/filesystem: Add fsp:ldr and fsp:pr services
Adds the basic skeleton for the remaining fsp services based off
information provided by Switch Brew.
2018-08-01 17:01:29 -04:00
Lioncash
e39294c267 service: Add capture services
Adds the basic skeleton for the capture services based off information
provided by Switch Brew.
2018-08-01 16:45:51 -04:00
Lioncash
d109279543 service: Add bpc and pcv services
Adds the basic skeleton for the remaining pcv-related services based off
information on Switch Brew.
2018-08-01 16:13:04 -04:00
Zach Hilman
13cdf1f159 Add missing parameter to files.push_back() 2018-08-01 00:16:54 -04:00
Zach Hilman
0497bb5528 Fix merge conflicts with opus and update docs 2018-08-01 00:16:54 -04:00
Zach Hilman
187d8e215f Use more descriptive error codes and messages 2018-08-01 00:16:54 -04:00
Zach Hilman
9d59b96ef9 Use static const instead of const static 2018-08-01 00:16:54 -04:00
Zach Hilman
a9c921a41d Use ErrorEncrypted where applicable and fix no keys crash 2018-08-01 00:16:54 -04:00
Zach Hilman
03149d3e4a Add missing includes and use const where applicable 2018-08-01 00:16:54 -04:00
Zach Hilman
150527ec19 Allow key loading from %YUZU_DIR%/keys in addition to ~/.switch 2018-08-01 00:16:54 -04:00
Zach Hilman
cc8234fa89 Use SHGetKnownFolderPath instead of SHGetFolderPathA 2018-08-01 00:16:54 -04:00
Zach Hilman
239a3113e4 Make XCI comply to review and style guidelines 2018-08-01 00:16:54 -04:00
Zach Hilman
22342487e8 Extract mbedtls to cpp file 2018-08-01 00:16:54 -04:00
Zach Hilman
83c3ae8be8 Add missing string.h include 2018-08-01 00:16:54 -04:00
Zach Hilman
c54a10cb4f Update mbedtls and fix compile error 2018-08-01 00:16:54 -04:00
Zach Hilman
df5b75694f Remove files that are not used 2018-08-01 00:16:54 -04:00
268 changed files with 9505 additions and 5947 deletions

7
.gitmodules vendored
View File

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

View File

@@ -4,8 +4,10 @@ function(copy_yuzu_Qt5_deps target_dir)
set(Qt5_DLL_DIR "${Qt5_DIR}/../../../bin")
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(PLATFORMS ${DLL_DEST}platforms/)
set(STYLES ${DLL_DEST}styles/)
set(IMAGEFORMATS ${DLL_DEST}imageformats/)
windows_copy_files(${target_dir} ${Qt5_DLL_DIR} ${DLL_DEST}
icudt*.dll
icuin*.dll
@@ -17,4 +19,5 @@ function(copy_yuzu_Qt5_deps target_dir)
)
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>.*)
endfunction(copy_yuzu_Qt5_deps)

View File

@@ -117,6 +117,7 @@ after_build:
mkdir $RELEASE_DIST
mkdir $RELEASE_DIST/platforms
mkdir $RELEASE_DIST/styles
mkdir $RELEASE_DIST/imageformats
# copy the compiled binaries and other release files to the release folder
Get-ChildItem "$CMAKE_BINARY_DIR" -Filter "yuzu*.exe" | Copy-Item -destination $RELEASE_DIST
@@ -140,6 +141,9 @@ after_build:
# copy the qt windows vista style dll to platforms
Copy-Item -path "C:/msys64/mingw64/share/qt5/plugins/styles/qwindowsvistastyle.dll" -force -destination "$RELEASE_DIST/styles"
# copy the qt jpeg imageformat dll to platforms
Copy-Item -path "C:/msys64/mingw64/share/qt5/plugins/imageformats/qjpeg.dll" -force -destination "$RELEASE_DIST/imageformats"
7z a -tzip $MINGW_BUILD_ZIP $RELEASE_DIST\*
7z a $MINGW_SEVENZIP $RELEASE_DIST
}

View File

@@ -32,9 +32,13 @@ add_subdirectory(inih)
# lz4
set(LZ4_BUNDLED_MODE ON)
add_subdirectory(lz4/contrib/cmake_unofficial)
add_subdirectory(lz4/contrib/cmake_unofficial EXCLUDE_FROM_ALL)
target_include_directories(lz4_static INTERFACE ./lz4/lib)
# mbedtls
add_subdirectory(mbedtls EXCLUDE_FROM_ALL)
target_include_directories(mbedtls PUBLIC ./mbedtls/include)
# MicroProfile
add_library(microprofile INTERFACE)
target_include_directories(microprofile INTERFACE ./microprofile)
@@ -58,5 +62,5 @@ target_include_directories(opus INTERFACE ./opus/include)
# Cubeb
if(ENABLE_CUBEB)
set(BUILD_TESTS OFF CACHE BOOL "")
add_subdirectory(cubeb)
add_subdirectory(cubeb EXCLUDE_FROM_ALL)
endif()

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1
externals/mbedtls vendored Submodule

Submodule externals/mbedtls added at d409b75a4c

View File

@@ -1,9 +1,11 @@
add_library(audio_core STATIC
audio_out.cpp
audio_out.h
audio_renderer.cpp
audio_renderer.h
buffer.h
cubeb_sink.cpp
cubeb_sink.h
codec.cpp
codec.h
null_sink.h
stream.cpp
stream.h
@@ -11,6 +13,8 @@ add_library(audio_core STATIC
sink_details.cpp
sink_details.h
sink_stream.h
$<$<BOOL:${ENABLE_CUBEB}>:cubeb_sink.cpp cubeb_sink.h>
)
create_target_directory_groups(audio_core)

View File

@@ -27,19 +27,19 @@ static Stream::Format ChannelsToStreamFormat(u32 num_channels) {
return {};
}
StreamPtr AudioOut::OpenStream(u32 sample_rate, u32 num_channels,
StreamPtr AudioOut::OpenStream(u32 sample_rate, u32 num_channels, std::string&& name,
Stream::ReleaseCallback&& release_callback) {
if (!sink) {
const SinkDetails& sink_details = GetSinkDetails(Settings::values.sink_id);
sink = sink_details.factory(Settings::values.audio_device_id);
}
return std::make_shared<Stream>(sample_rate, ChannelsToStreamFormat(num_channels),
std::move(release_callback),
sink->AcquireSinkStream(sample_rate, num_channels));
return std::make_shared<Stream>(
sample_rate, ChannelsToStreamFormat(num_channels), std::move(release_callback),
sink->AcquireSinkStream(sample_rate, num_channels, name), std::move(name));
}
std::vector<u64> AudioOut::GetTagsAndReleaseBuffers(StreamPtr stream, size_t max_count) {
std::vector<Buffer::Tag> AudioOut::GetTagsAndReleaseBuffers(StreamPtr stream, size_t max_count) {
return stream->GetTagsAndReleaseBuffers(max_count);
}
@@ -51,7 +51,7 @@ void AudioOut::StopStream(StreamPtr stream) {
stream->Stop();
}
bool AudioOut::QueueBuffer(StreamPtr stream, Buffer::Tag tag, std::vector<u8>&& data) {
bool AudioOut::QueueBuffer(StreamPtr stream, Buffer::Tag tag, std::vector<s16>&& data) {
return stream->QueueBuffer(std::make_shared<Buffer>(tag, std::move(data)));
}

View File

@@ -5,6 +5,7 @@
#pragma once
#include <memory>
#include <string>
#include <vector>
#include "audio_core/buffer.h"
@@ -20,11 +21,11 @@ namespace AudioCore {
class AudioOut {
public:
/// Opens a new audio stream
StreamPtr OpenStream(u32 sample_rate, u32 num_channels,
StreamPtr OpenStream(u32 sample_rate, u32 num_channels, std::string&& name,
Stream::ReleaseCallback&& release_callback);
/// Returns a vector of recently released buffers specified by tag for the specified stream
std::vector<u64> GetTagsAndReleaseBuffers(StreamPtr stream, size_t max_count);
std::vector<Buffer::Tag> GetTagsAndReleaseBuffers(StreamPtr stream, size_t max_count);
/// Starts an audio stream for playback
void StartStream(StreamPtr stream);
@@ -33,7 +34,7 @@ public:
void StopStream(StreamPtr stream);
/// Queues a buffer into the specified audio stream, returns true on success
bool QueueBuffer(StreamPtr stream, Buffer::Tag tag, std::vector<u8>&& data);
bool QueueBuffer(StreamPtr stream, Buffer::Tag tag, std::vector<s16>&& data);
private:
SinkPtr sink;

View File

@@ -0,0 +1,234 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "audio_core/audio_renderer.h"
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/memory.h"
namespace AudioCore {
constexpr u32 STREAM_SAMPLE_RATE{48000};
constexpr u32 STREAM_NUM_CHANNELS{2};
AudioRenderer::AudioRenderer(AudioRendererParameter params,
Kernel::SharedPtr<Kernel::Event> buffer_event)
: worker_params{params}, buffer_event{buffer_event}, voices(params.voice_count) {
audio_core = std::make_unique<AudioCore::AudioOut>();
stream = audio_core->OpenStream(STREAM_SAMPLE_RATE, STREAM_NUM_CHANNELS, "AudioRenderer",
[=]() { buffer_event->Signal(); });
audio_core->StartStream(stream);
QueueMixedBuffer(0);
QueueMixedBuffer(1);
QueueMixedBuffer(2);
}
std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_params) {
// Copy UpdateDataHeader struct
UpdateDataHeader config{};
std::memcpy(&config, input_params.data(), sizeof(UpdateDataHeader));
u32 memory_pool_count = worker_params.effect_count + (worker_params.voice_count * 4);
// Copy MemoryPoolInfo structs
std::vector<MemoryPoolInfo> mem_pool_info(memory_pool_count);
std::memcpy(mem_pool_info.data(),
input_params.data() + sizeof(UpdateDataHeader) + config.behavior_size,
memory_pool_count * sizeof(MemoryPoolInfo));
// Copy VoiceInfo structs
size_t offset{sizeof(UpdateDataHeader) + config.behavior_size + config.memory_pools_size +
config.voice_resource_size};
for (auto& voice : voices) {
std::memcpy(&voice.Info(), input_params.data() + offset, sizeof(VoiceInfo));
offset += sizeof(VoiceInfo);
}
// Update voices
for (auto& voice : voices) {
voice.UpdateState();
if (!voice.GetInfo().is_in_use) {
continue;
}
if (voice.GetInfo().is_new) {
voice.SetWaveIndex(voice.GetInfo().wave_buffer_head);
}
}
// Update memory pool state
std::vector<MemoryPoolEntry> memory_pool(memory_pool_count);
for (size_t index = 0; index < memory_pool.size(); ++index) {
if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestAttach) {
memory_pool[index].state = MemoryPoolStates::Attached;
} else if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestDetach) {
memory_pool[index].state = MemoryPoolStates::Detached;
}
}
// Release previous buffers and queue next ones for playback
ReleaseAndQueueBuffers();
// Copy output header
UpdateDataHeader response_data{worker_params};
std::vector<u8> output_params(response_data.total_size);
std::memcpy(output_params.data(), &response_data, sizeof(UpdateDataHeader));
// Copy output memory pool entries
std::memcpy(output_params.data() + sizeof(UpdateDataHeader), memory_pool.data(),
response_data.memory_pools_size);
// Copy output voice status
size_t voice_out_status_offset{sizeof(UpdateDataHeader) + response_data.memory_pools_size};
for (const auto& voice : voices) {
std::memcpy(output_params.data() + voice_out_status_offset, &voice.GetOutStatus(),
sizeof(VoiceOutStatus));
voice_out_status_offset += sizeof(VoiceOutStatus);
}
return output_params;
}
void AudioRenderer::VoiceState::SetWaveIndex(size_t index) {
wave_index = index & 3;
is_refresh_pending = true;
}
std::vector<s16> AudioRenderer::VoiceState::DequeueSamples(size_t sample_count) {
if (!IsPlaying()) {
return {};
}
if (is_refresh_pending) {
RefreshBuffer();
}
const size_t max_size{samples.size() - offset};
const size_t dequeue_offset{offset};
size_t size{sample_count * STREAM_NUM_CHANNELS};
if (size > max_size) {
size = max_size;
}
out_status.played_sample_count += size / STREAM_NUM_CHANNELS;
offset += size;
const auto& wave_buffer{info.wave_buffer[wave_index]};
if (offset == samples.size()) {
offset = 0;
if (!wave_buffer.is_looping) {
SetWaveIndex(wave_index + 1);
}
out_status.wave_buffer_consumed++;
if (wave_buffer.end_of_stream) {
info.play_state = PlayState::Paused;
}
}
return {samples.begin() + dequeue_offset, samples.begin() + dequeue_offset + size};
}
void AudioRenderer::VoiceState::UpdateState() {
if (is_in_use && !info.is_in_use) {
// No longer in use, reset state
is_refresh_pending = true;
wave_index = 0;
offset = 0;
out_status = {};
}
is_in_use = info.is_in_use;
}
void AudioRenderer::VoiceState::RefreshBuffer() {
std::vector<s16> new_samples(info.wave_buffer[wave_index].buffer_sz / sizeof(s16));
Memory::ReadBlock(info.wave_buffer[wave_index].buffer_addr, new_samples.data(),
info.wave_buffer[wave_index].buffer_sz);
switch (static_cast<Codec::PcmFormat>(info.sample_format)) {
case Codec::PcmFormat::Int16: {
// PCM16 is played as-is
break;
}
case Codec::PcmFormat::Adpcm: {
// Decode ADPCM to PCM16
Codec::ADPCM_Coeff coeffs;
Memory::ReadBlock(info.additional_params_addr, coeffs.data(), sizeof(Codec::ADPCM_Coeff));
new_samples = Codec::DecodeADPCM(reinterpret_cast<u8*>(new_samples.data()),
new_samples.size() * sizeof(s16), coeffs, adpcm_state);
break;
}
default:
LOG_CRITICAL(Audio, "Unimplemented sample_format={}", info.sample_format);
UNREACHABLE();
break;
}
switch (info.channel_count) {
case 1:
// 1 channel is upsampled to 2 channel
samples.resize(new_samples.size() * 2);
for (size_t index = 0; index < new_samples.size(); ++index) {
samples[index * 2] = new_samples[index];
samples[index * 2 + 1] = new_samples[index];
}
break;
case 2: {
// 2 channel is played as is
samples = std::move(new_samples);
break;
}
default:
LOG_CRITICAL(Audio, "Unimplemented channel_count={}", info.channel_count);
UNREACHABLE();
break;
}
is_refresh_pending = false;
}
static constexpr s16 ClampToS16(s32 value) {
return static_cast<s16>(std::clamp(value, -32768, 32767));
}
void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) {
constexpr size_t BUFFER_SIZE{512};
std::vector<s16> buffer(BUFFER_SIZE * stream->GetNumChannels());
for (auto& voice : voices) {
if (!voice.IsPlaying()) {
continue;
}
size_t offset{};
s64 samples_remaining{BUFFER_SIZE};
while (samples_remaining > 0) {
const std::vector<s16> samples{voice.DequeueSamples(samples_remaining)};
if (samples.empty()) {
break;
}
samples_remaining -= samples.size();
for (const auto& sample : samples) {
const s32 buffer_sample{buffer[offset]};
buffer[offset++] =
ClampToS16(buffer_sample + static_cast<s32>(sample * voice.GetInfo().volume));
}
}
}
audio_core->QueueBuffer(stream, tag, std::move(buffer));
}
void AudioRenderer::ReleaseAndQueueBuffers() {
const auto released_buffers{audio_core->GetTagsAndReleaseBuffers(stream, 2)};
for (const auto& tag : released_buffers) {
QueueMixedBuffer(tag);
}
}
} // namespace AudioCore

View File

@@ -0,0 +1,206 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include <memory>
#include <vector>
#include "audio_core/audio_out.h"
#include "audio_core/codec.h"
#include "audio_core/stream.h"
#include "common/common_types.h"
#include "common/swap.h"
#include "core/hle/kernel/event.h"
namespace AudioCore {
enum class PlayState : u8 {
Started = 0,
Stopped = 1,
Paused = 2,
};
struct AudioRendererParameter {
u32_le sample_rate;
u32_le sample_count;
u32_le unknown_8;
u32_le unknown_c;
u32_le voice_count;
u32_le sink_count;
u32_le effect_count;
u32_le unknown_1c;
u8 unknown_20;
INSERT_PADDING_BYTES(3);
u32_le splitter_count;
u32_le unknown_2c;
INSERT_PADDING_WORDS(1);
u32_le revision;
};
static_assert(sizeof(AudioRendererParameter) == 52, "AudioRendererParameter is an invalid size");
enum class MemoryPoolStates : u32 { // Should be LE
Invalid = 0x0,
Unknown = 0x1,
RequestDetach = 0x2,
Detached = 0x3,
RequestAttach = 0x4,
Attached = 0x5,
Released = 0x6,
};
struct MemoryPoolEntry {
MemoryPoolStates state;
u32_le unknown_4;
u32_le unknown_8;
u32_le unknown_c;
};
static_assert(sizeof(MemoryPoolEntry) == 0x10, "MemoryPoolEntry has wrong size");
struct MemoryPoolInfo {
u64_le pool_address;
u64_le pool_size;
MemoryPoolStates pool_state;
INSERT_PADDING_WORDS(3); // Unknown
};
static_assert(sizeof(MemoryPoolInfo) == 0x20, "MemoryPoolInfo has wrong size");
struct BiquadFilter {
u8 enable;
INSERT_PADDING_BYTES(1);
std::array<s16_le, 3> numerator;
std::array<s16_le, 2> denominator;
};
static_assert(sizeof(BiquadFilter) == 0xc, "BiquadFilter has wrong size");
struct WaveBuffer {
u64_le buffer_addr;
u64_le buffer_sz;
s32_le start_sample_offset;
s32_le end_sample_offset;
u8 is_looping;
u8 end_of_stream;
u8 sent_to_server;
INSERT_PADDING_BYTES(5);
u64 context_addr;
u64 context_sz;
INSERT_PADDING_BYTES(8);
};
static_assert(sizeof(WaveBuffer) == 0x38, "WaveBuffer has wrong size");
struct VoiceInfo {
u32_le id;
u32_le node_id;
u8 is_new;
u8 is_in_use;
PlayState play_state;
u8 sample_format;
u32_le sample_rate;
u32_le priority;
u32_le sorting_order;
u32_le channel_count;
float_le pitch;
float_le volume;
std::array<BiquadFilter, 2> biquad_filter;
u32_le wave_buffer_count;
u32_le wave_buffer_head;
INSERT_PADDING_WORDS(1);
u64_le additional_params_addr;
u64_le additional_params_sz;
u32_le mix_id;
u32_le splitter_info_id;
std::array<WaveBuffer, 4> wave_buffer;
std::array<u32_le, 6> voice_channel_resource_ids;
INSERT_PADDING_BYTES(24);
};
static_assert(sizeof(VoiceInfo) == 0x170, "VoiceInfo is wrong size");
struct VoiceOutStatus {
u64_le played_sample_count;
u32_le wave_buffer_consumed;
u32_le voice_drops_count;
};
static_assert(sizeof(VoiceOutStatus) == 0x10, "VoiceOutStatus has wrong size");
struct UpdateDataHeader {
UpdateDataHeader() {}
explicit UpdateDataHeader(const AudioRendererParameter& config) {
revision = Common::MakeMagic('R', 'E', 'V', '4'); // 5.1.0 Revision
behavior_size = 0xb0;
memory_pools_size = (config.effect_count + (config.voice_count * 4)) * 0x10;
voices_size = config.voice_count * 0x10;
voice_resource_size = 0x0;
effects_size = config.effect_count * 0x10;
mixes_size = 0x0;
sinks_size = config.sink_count * 0x20;
performance_manager_size = 0x10;
total_size = sizeof(UpdateDataHeader) + behavior_size + memory_pools_size + voices_size +
effects_size + sinks_size + performance_manager_size;
}
u32_le revision;
u32_le behavior_size;
u32_le memory_pools_size;
u32_le voices_size;
u32_le voice_resource_size;
u32_le effects_size;
u32_le mixes_size;
u32_le sinks_size;
u32_le performance_manager_size;
INSERT_PADDING_WORDS(6);
u32_le total_size;
};
static_assert(sizeof(UpdateDataHeader) == 0x40, "UpdateDataHeader has wrong size");
class AudioRenderer {
public:
AudioRenderer(AudioRendererParameter params, Kernel::SharedPtr<Kernel::Event> buffer_event);
std::vector<u8> UpdateAudioRenderer(const std::vector<u8>& input_params);
void QueueMixedBuffer(Buffer::Tag tag);
void ReleaseAndQueueBuffers();
private:
class VoiceState {
public:
bool IsPlaying() const {
return is_in_use && info.play_state == PlayState::Started;
}
const VoiceOutStatus& GetOutStatus() const {
return out_status;
}
const VoiceInfo& GetInfo() const {
return info;
}
VoiceInfo& Info() {
return info;
}
void SetWaveIndex(size_t index);
std::vector<s16> DequeueSamples(size_t sample_count);
void UpdateState();
void RefreshBuffer();
private:
bool is_in_use{};
bool is_refresh_pending{};
size_t wave_index{};
size_t offset{};
Codec::ADPCMState adpcm_state{};
std::vector<s16> samples;
VoiceOutStatus out_status{};
VoiceInfo info{};
};
AudioRendererParameter worker_params;
Kernel::SharedPtr<Kernel::Event> buffer_event;
std::vector<VoiceState> voices;
std::unique_ptr<AudioCore::AudioOut> audio_core;
AudioCore::StreamPtr stream;
};
} // namespace AudioCore

View File

@@ -18,11 +18,16 @@ class Buffer {
public:
using Tag = u64;
Buffer(Tag tag, std::vector<u8>&& data) : tag{tag}, data{std::move(data)} {}
Buffer(Tag tag, std::vector<s16>&& samples) : tag{tag}, samples{std::move(samples)} {}
/// Returns the raw audio data for the buffer
const std::vector<u8>& GetData() const {
return data;
std::vector<s16>& Samples() {
return samples;
}
/// Returns the raw audio data for the buffer
const std::vector<s16>& GetSamples() const {
return samples;
}
/// Returns the buffer tag, this is provided by the game to the audout service
@@ -32,7 +37,7 @@ public:
private:
Tag tag;
std::vector<u8> data;
std::vector<s16> samples;
};
using BufferPtr = std::shared_ptr<Buffer>;

77
src/audio_core/codec.cpp Normal file
View File

@@ -0,0 +1,77 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include "audio_core/codec.h"
namespace AudioCore::Codec {
std::vector<s16> DecodeADPCM(const u8* const data, size_t size, const ADPCM_Coeff& coeff,
ADPCMState& state) {
// GC-ADPCM with scale factor and variable coefficients.
// Frames are 8 bytes long containing 14 samples each.
// Samples are 4 bits (one nibble) long.
constexpr size_t FRAME_LEN = 8;
constexpr size_t SAMPLES_PER_FRAME = 14;
constexpr std::array<int, 16> SIGNED_NIBBLES = {
{0, 1, 2, 3, 4, 5, 6, 7, -8, -7, -6, -5, -4, -3, -2, -1}};
const size_t sample_count = (size / FRAME_LEN) * SAMPLES_PER_FRAME;
const size_t ret_size =
sample_count % 2 == 0 ? sample_count : sample_count + 1; // Ensure multiple of two.
std::vector<s16> ret(ret_size);
int yn1 = state.yn1, yn2 = state.yn2;
const size_t NUM_FRAMES =
(sample_count + (SAMPLES_PER_FRAME - 1)) / SAMPLES_PER_FRAME; // Round up.
for (size_t framei = 0; framei < NUM_FRAMES; framei++) {
const int frame_header = data[framei * FRAME_LEN];
const int scale = 1 << (frame_header & 0xF);
const int idx = (frame_header >> 4) & 0x7;
// Coefficients are fixed point with 11 bits fractional part.
const int coef1 = coeff[idx * 2 + 0];
const int coef2 = coeff[idx * 2 + 1];
// Decodes an audio sample. One nibble produces one sample.
const auto decode_sample = [&](const int nibble) -> s16 {
const int xn = nibble * scale;
// We first transform everything into 11 bit fixed point, perform the second order
// digital filter, then transform back.
// 0x400 == 0.5 in 11 bit fixed point.
// Filter: y[n] = x[n] + 0.5 + c1 * y[n-1] + c2 * y[n-2]
int val = ((xn << 11) + 0x400 + coef1 * yn1 + coef2 * yn2) >> 11;
// Clamp to output range.
val = std::clamp<s32>(val, -32768, 32767);
// Advance output feedback.
yn2 = yn1;
yn1 = val;
return static_cast<s16>(val);
};
size_t outputi = framei * SAMPLES_PER_FRAME;
size_t datai = framei * FRAME_LEN + 1;
for (size_t i = 0; i < SAMPLES_PER_FRAME && outputi < sample_count; i += 2) {
const s16 sample1 = decode_sample(SIGNED_NIBBLES[data[datai] >> 4]);
ret[outputi] = sample1;
outputi++;
const s16 sample2 = decode_sample(SIGNED_NIBBLES[data[datai] & 0xF]);
ret[outputi] = sample2;
outputi++;
datai++;
}
}
state.yn1 = yn1;
state.yn2 = yn2;
return ret;
}
} // namespace AudioCore::Codec

44
src/audio_core/codec.h Normal file
View File

@@ -0,0 +1,44 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include <vector>
#include "common/common_types.h"
namespace AudioCore::Codec {
enum class PcmFormat : u32 {
Invalid = 0,
Int8 = 1,
Int16 = 2,
Int24 = 3,
Int32 = 4,
PcmFloat = 5,
Adpcm = 6,
};
/// See: Codec::DecodeADPCM
struct ADPCMState {
// Two historical samples from previous processed buffer,
// required for ADPCM decoding
s16 yn1; ///< y[n-1]
s16 yn2; ///< y[n-2]
};
using ADPCM_Coeff = std::array<s16, 16>;
/**
* @param data Pointer to buffer that contains ADPCM data to decode
* @param size Size of buffer in bytes
* @param coeff ADPCM coefficients
* @param state ADPCM state, this is updated with new state
* @return Decoded stereo signed PCM16 data, sample_count in length
*/
std::vector<s16> DecodeADPCM(const u8* const data, size_t size, const ADPCM_Coeff& coeff,
ADPCMState& state);
}; // namespace AudioCore::Codec

View File

@@ -13,20 +13,30 @@ namespace AudioCore {
class SinkStreamImpl final : public SinkStream {
public:
SinkStreamImpl(cubeb* ctx, cubeb_devid output_device) : ctx{ctx} {
cubeb_stream_params params;
params.rate = 48000;
params.channels = GetNumChannels();
params.format = CUBEB_SAMPLE_S16NE;
params.layout = CUBEB_LAYOUT_STEREO;
SinkStreamImpl(cubeb* ctx, u32 sample_rate, u32 num_channels_, cubeb_devid output_device,
const std::string& name)
: ctx{ctx}, num_channels{num_channels_} {
u32 minimum_latency = 0;
if (num_channels == 6) {
// 6-channel audio does not seem to work with cubeb + SDL, so we downsample this to 2
// channel for now
is_6_channel = true;
num_channels = 2;
}
cubeb_stream_params params{};
params.rate = sample_rate;
params.channels = num_channels;
params.format = CUBEB_SAMPLE_S16NE;
params.layout = num_channels == 1 ? CUBEB_LAYOUT_MONO : CUBEB_LAYOUT_STEREO;
u32 minimum_latency{};
if (cubeb_get_min_latency(ctx, &params, &minimum_latency) != CUBEB_OK) {
LOG_CRITICAL(Audio_Sink, "Error getting minimum latency");
}
if (cubeb_stream_init(ctx, &stream_backend, "yuzu Audio Output", nullptr, nullptr,
output_device, &params, std::max(512u, minimum_latency),
if (cubeb_stream_init(ctx, &stream_backend, name.c_str(), nullptr, nullptr, output_device,
&params, std::max(512u, minimum_latency),
&SinkStreamImpl::DataCallback, &SinkStreamImpl::StateCallback,
this) != CUBEB_OK) {
LOG_CRITICAL(Audio_Sink, "Error initializing cubeb stream");
@@ -51,33 +61,29 @@ public:
cubeb_stream_destroy(stream_backend);
}
void EnqueueSamples(u32 num_channels, const s16* samples, size_t sample_count) override {
void EnqueueSamples(u32 num_channels, const std::vector<s16>& samples) override {
if (!ctx) {
return;
}
queue.reserve(queue.size() + sample_count * GetNumChannels());
queue.reserve(queue.size() + samples.size() * GetNumChannels());
if (num_channels == 2) {
// Copy as-is
std::copy(samples, samples + sample_count * GetNumChannels(),
std::back_inserter(queue));
} else if (num_channels == 6) {
if (is_6_channel) {
// Downsample 6 channels to 2
const size_t sample_count_copy_size = sample_count * num_channels * 2;
const size_t sample_count_copy_size = samples.size() * 2;
queue.reserve(sample_count_copy_size);
for (size_t i = 0; i < sample_count * num_channels; i += num_channels) {
for (size_t i = 0; i < samples.size(); i += num_channels) {
queue.push_back(samples[i]);
queue.push_back(samples[i + 1]);
}
} else {
ASSERT_MSG(false, "Unimplemented");
// Copy as-is
std::copy(samples.begin(), samples.end(), std::back_inserter(queue));
}
}
u32 GetNumChannels() const {
// Only support 2-channel stereo output for now
return 2;
return num_channels;
}
private:
@@ -85,6 +91,8 @@ private:
cubeb* ctx{};
cubeb_stream* stream_backend{};
u32 num_channels{};
bool is_6_channel{};
std::vector<s16> queue;
@@ -129,8 +137,10 @@ CubebSink::~CubebSink() {
cubeb_destroy(ctx);
}
SinkStream& CubebSink::AcquireSinkStream(u32 sample_rate, u32 num_channels) {
sink_streams.push_back(std::make_unique<SinkStreamImpl>(ctx, output_device));
SinkStream& CubebSink::AcquireSinkStream(u32 sample_rate, u32 num_channels,
const std::string& name) {
sink_streams.push_back(
std::make_unique<SinkStreamImpl>(ctx, sample_rate, num_channels, output_device, name));
return *sink_streams.back();
}

View File

@@ -18,7 +18,8 @@ public:
explicit CubebSink(std::string device_id);
~CubebSink() override;
SinkStream& AcquireSinkStream(u32 sample_rate, u32 num_channels) override;
SinkStream& AcquireSinkStream(u32 sample_rate, u32 num_channels,
const std::string& name) override;
private:
cubeb* ctx{};

View File

@@ -13,14 +13,14 @@ public:
explicit NullSink(std::string){};
~NullSink() override = default;
SinkStream& AcquireSinkStream(u32 /*sample_rate*/, u32 /*num_channels*/) override {
SinkStream& AcquireSinkStream(u32 /*sample_rate*/, u32 /*num_channels*/,
const std::string& /*name*/) override {
return null_sink_stream;
}
private:
struct NullSinkStreamImpl final : SinkStream {
void EnqueueSamples(u32 /*num_channels*/, const s16* /*samples*/,
size_t /*sample_count*/) override {}
void EnqueueSamples(u32 /*num_channels*/, const std::vector<s16>& /*samples*/) override {}
} null_sink_stream;
};

View File

@@ -5,6 +5,7 @@
#pragma once
#include <memory>
#include <string>
#include "audio_core/sink_stream.h"
#include "common/common_types.h"
@@ -21,7 +22,8 @@ constexpr char auto_device_name[] = "auto";
class Sink {
public:
virtual ~Sink() = default;
virtual SinkStream& AcquireSinkStream(u32 sample_rate, u32 num_channels) = 0;
virtual SinkStream& AcquireSinkStream(u32 sample_rate, u32 num_channels,
const std::string& name) = 0;
};
using SinkPtr = std::unique_ptr<Sink>;

View File

@@ -6,6 +6,7 @@
#include <functional>
#include <memory>
#include <utility>
#include <vector>
namespace AudioCore {
@@ -13,16 +14,18 @@ namespace AudioCore {
class Sink;
struct SinkDetails {
SinkDetails(const char* id_, std::function<std::unique_ptr<Sink>(std::string)> factory_,
std::function<std::vector<std::string>()> list_devices_)
: id(id_), factory(factory_), list_devices(list_devices_) {}
using FactoryFn = std::function<std::unique_ptr<Sink>(std::string)>;
using ListDevicesFn = std::function<std::vector<std::string>()>;
SinkDetails(const char* id_, FactoryFn factory_, ListDevicesFn list_devices_)
: id(id_), factory(std::move(factory_)), list_devices(std::move(list_devices_)) {}
/// Name for this sink.
const char* id;
/// A method to call to construct an instance of this type of sink.
std::function<std::unique_ptr<Sink>(std::string device_id)> factory;
FactoryFn factory;
/// A method to call to list available devices.
std::function<std::vector<std::string>()> list_devices;
ListDevicesFn list_devices;
};
extern const std::vector<SinkDetails> g_sink_details;

View File

@@ -5,6 +5,7 @@
#pragma once
#include <memory>
#include <vector>
#include "common/common_types.h"
@@ -22,9 +23,8 @@ public:
* Feed stereo samples to sink.
* @param num_channels Number of channels used.
* @param samples Samples in interleaved stereo PCM16 format.
* @param sample_count Number of samples.
*/
virtual void EnqueueSamples(u32 num_channels, const s16* samples, size_t sample_count) = 0;
virtual void EnqueueSamples(u32 num_channels, const std::vector<s16>& samples) = 0;
};
using SinkStreamPtr = std::unique_ptr<SinkStream>;

View File

@@ -32,17 +32,13 @@ u32 Stream::GetNumChannels() const {
return {};
}
u32 Stream::GetSampleSize() const {
return GetNumChannels() * 2;
}
Stream::Stream(u32 sample_rate, Format format, ReleaseCallback&& release_callback,
SinkStream& sink_stream)
SinkStream& sink_stream, std::string&& name_)
: sample_rate{sample_rate}, format{format}, release_callback{std::move(release_callback)},
sink_stream{sink_stream} {
sink_stream{sink_stream}, name{std::move(name_)} {
release_event = CoreTiming::RegisterEvent(
"Stream::Release", [this](u64 userdata, int cycles_late) { ReleaseActiveBuffer(); });
name, [this](u64 userdata, int cycles_late) { ReleaseActiveBuffer(); });
}
void Stream::Play() {
@@ -55,17 +51,15 @@ void Stream::Stop() {
}
s64 Stream::GetBufferReleaseCycles(const Buffer& buffer) const {
const size_t num_samples{buffer.GetData().size() / GetSampleSize()};
const size_t num_samples{buffer.GetSamples().size() / GetNumChannels()};
return CoreTiming::usToCycles((static_cast<u64>(num_samples) * 1000000) / sample_rate);
}
static std::vector<s16> GetVolumeAdjustedSamples(const std::vector<u8>& data) {
std::vector<s16> samples(data.size() / sizeof(s16));
std::memcpy(samples.data(), data.data(), data.size());
static void VolumeAdjustSamples(std::vector<s16>& samples) {
const float volume{std::clamp(Settings::values.volume, 0.0f, 1.0f)};
if (volume == 1.0f) {
return samples;
return;
}
// Implementation of a volume slider with a dynamic range of 60 dB
@@ -73,8 +67,6 @@ static std::vector<s16> GetVolumeAdjustedSamples(const std::vector<u8>& data) {
for (auto& sample : samples) {
sample = static_cast<s16>(sample * volume_scale_factor);
}
return samples;
}
void Stream::PlayNextBuffer() {
@@ -96,14 +88,14 @@ void Stream::PlayNextBuffer() {
active_buffer = queued_buffers.front();
queued_buffers.pop();
const size_t sample_count{active_buffer->GetData().size() / GetSampleSize()};
sink_stream.EnqueueSamples(
GetNumChannels(), GetVolumeAdjustedSamples(active_buffer->GetData()).data(), sample_count);
VolumeAdjustSamples(active_buffer->Samples());
sink_stream.EnqueueSamples(GetNumChannels(), active_buffer->GetSamples());
CoreTiming::ScheduleEventThreadsafe(GetBufferReleaseCycles(*active_buffer), release_event, {});
}
void Stream::ReleaseActiveBuffer() {
ASSERT(active_buffer);
released_buffers.push(std::move(active_buffer));
release_callback();
PlayNextBuffer();

View File

@@ -6,6 +6,7 @@
#include <functional>
#include <memory>
#include <string>
#include <vector>
#include <queue>
@@ -33,7 +34,7 @@ public:
using ReleaseCallback = std::function<void()>;
Stream(u32 sample_rate, Format format, ReleaseCallback&& release_callback,
SinkStream& sink_stream);
SinkStream& sink_stream, std::string&& name_);
/// Plays the audio stream
void Play();
@@ -68,9 +69,6 @@ public:
/// Gets the number of channels
u32 GetNumChannels() const;
/// Gets the sample size in bytes
u32 GetSampleSize() const;
private:
/// Current state of the stream
enum class State {
@@ -96,6 +94,7 @@ private:
std::queue<BufferPtr> queued_buffers; ///< Buffers queued to be played in the stream
std::queue<BufferPtr> released_buffers; ///< Buffers recently released from the stream
SinkStream& sink_stream; ///< Output sink for the stream
std::string name; ///< Name of the stream, must be unique
};
using StreamPtr = std::shared_ptr<Stream>;

View File

@@ -9,13 +9,13 @@ namespace Common {
template <typename T>
constexpr T AlignUp(T value, size_t size) {
static_assert(std::is_unsigned<T>::value, "T must be an unsigned value.");
static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
return static_cast<T>(value + (size - value % size) % size);
}
template <typename T>
constexpr T AlignDown(T value, size_t size) {
static_assert(std::is_unsigned<T>::value, "T must be an unsigned value.");
static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
return static_cast<T>(value - value % size);
}

View File

@@ -96,7 +96,7 @@ static inline int LeastSignificantSetBit(u64 val) {
template <typename IntTy>
class BitSet {
static_assert(!std::is_signed<IntTy>::value, "BitSet should not be used with signed types");
static_assert(!std::is_signed_v<IntTy>, "BitSet should not be used with signed types");
public:
// A reference to a particular bit, returned from operator[].

View File

@@ -4,6 +4,8 @@
#pragma once
#include <cstring>
#include "common/common_types.h"
#include "common/swap.h"
#include "common/vector_math.h"
@@ -55,7 +57,7 @@ constexpr u8 Convert8To6(u8 value) {
* @param bytes Pointer to encoded source color
* @return Result color decoded as Math::Vec4<u8>
*/
inline const Math::Vec4<u8> DecodeRGBA8(const u8* bytes) {
inline Math::Vec4<u8> DecodeRGBA8(const u8* bytes) {
return {bytes[3], bytes[2], bytes[1], bytes[0]};
}
@@ -64,7 +66,7 @@ inline const Math::Vec4<u8> DecodeRGBA8(const u8* bytes) {
* @param bytes Pointer to encoded source color
* @return Result color decoded as Math::Vec4<u8>
*/
inline const Math::Vec4<u8> DecodeRGB8(const u8* bytes) {
inline Math::Vec4<u8> DecodeRGB8(const u8* bytes) {
return {bytes[2], bytes[1], bytes[0], 255};
}
@@ -73,7 +75,7 @@ inline const Math::Vec4<u8> DecodeRGB8(const u8* bytes) {
* @param bytes Pointer to encoded source color
* @return Result color decoded as Math::Vec4<u8>
*/
inline const Math::Vec4<u8> DecodeRG8(const u8* bytes) {
inline Math::Vec4<u8> DecodeRG8(const u8* bytes) {
return {bytes[1], bytes[0], 0, 255};
}
@@ -82,8 +84,9 @@ inline const Math::Vec4<u8> DecodeRG8(const u8* bytes) {
* @param bytes Pointer to encoded source color
* @return Result color decoded as Math::Vec4<u8>
*/
inline const Math::Vec4<u8> DecodeRGB565(const u8* bytes) {
const u16_le pixel = *reinterpret_cast<const u16_le*>(bytes);
inline Math::Vec4<u8> DecodeRGB565(const u8* bytes) {
u16_le pixel;
std::memcpy(&pixel, bytes, sizeof(pixel));
return {Convert5To8((pixel >> 11) & 0x1F), Convert6To8((pixel >> 5) & 0x3F),
Convert5To8(pixel & 0x1F), 255};
}
@@ -93,8 +96,9 @@ inline const Math::Vec4<u8> DecodeRGB565(const u8* bytes) {
* @param bytes Pointer to encoded source color
* @return Result color decoded as Math::Vec4<u8>
*/
inline const Math::Vec4<u8> DecodeRGB5A1(const u8* bytes) {
const u16_le pixel = *reinterpret_cast<const u16_le*>(bytes);
inline Math::Vec4<u8> DecodeRGB5A1(const u8* bytes) {
u16_le pixel;
std::memcpy(&pixel, bytes, sizeof(pixel));
return {Convert5To8((pixel >> 11) & 0x1F), Convert5To8((pixel >> 6) & 0x1F),
Convert5To8((pixel >> 1) & 0x1F), Convert1To8(pixel & 0x1)};
}
@@ -104,8 +108,9 @@ inline const Math::Vec4<u8> DecodeRGB5A1(const u8* bytes) {
* @param bytes Pointer to encoded source color
* @return Result color decoded as Math::Vec4<u8>
*/
inline const Math::Vec4<u8> DecodeRGBA4(const u8* bytes) {
const u16_le pixel = *reinterpret_cast<const u16_le*>(bytes);
inline Math::Vec4<u8> DecodeRGBA4(const u8* bytes) {
u16_le pixel;
std::memcpy(&pixel, bytes, sizeof(pixel));
return {Convert4To8((pixel >> 12) & 0xF), Convert4To8((pixel >> 8) & 0xF),
Convert4To8((pixel >> 4) & 0xF), Convert4To8(pixel & 0xF)};
}
@@ -116,7 +121,9 @@ inline const Math::Vec4<u8> DecodeRGBA4(const u8* bytes) {
* @return Depth value as an u32
*/
inline u32 DecodeD16(const u8* bytes) {
return *reinterpret_cast<const u16_le*>(bytes);
u16_le data;
std::memcpy(&data, bytes, sizeof(data));
return data;
}
/**
@@ -133,7 +140,7 @@ inline u32 DecodeD24(const u8* bytes) {
* @param bytes Pointer to encoded source values
* @return Resulting values stored as a Math::Vec2
*/
inline const Math::Vec2<u32> DecodeD24S8(const u8* bytes) {
inline Math::Vec2<u32> DecodeD24S8(const u8* bytes) {
return {static_cast<u32>((bytes[2] << 16) | (bytes[1] << 8) | bytes[0]), bytes[3]};
}
@@ -175,8 +182,10 @@ inline void EncodeRG8(const Math::Vec4<u8>& color, u8* bytes) {
* @param bytes Destination pointer to store encoded color
*/
inline void EncodeRGB565(const Math::Vec4<u8>& color, u8* bytes) {
*reinterpret_cast<u16_le*>(bytes) =
const u16_le data =
(Convert8To5(color.r()) << 11) | (Convert8To6(color.g()) << 5) | Convert8To5(color.b());
std::memcpy(bytes, &data, sizeof(data));
}
/**
@@ -185,9 +194,10 @@ inline void EncodeRGB565(const Math::Vec4<u8>& color, u8* bytes) {
* @param bytes Destination pointer to store encoded color
*/
inline void EncodeRGB5A1(const Math::Vec4<u8>& color, u8* bytes) {
*reinterpret_cast<u16_le*>(bytes) = (Convert8To5(color.r()) << 11) |
(Convert8To5(color.g()) << 6) |
(Convert8To5(color.b()) << 1) | Convert8To1(color.a());
const u16_le data = (Convert8To5(color.r()) << 11) | (Convert8To5(color.g()) << 6) |
(Convert8To5(color.b()) << 1) | Convert8To1(color.a());
std::memcpy(bytes, &data, sizeof(data));
}
/**
@@ -196,9 +206,10 @@ inline void EncodeRGB5A1(const Math::Vec4<u8>& color, u8* bytes) {
* @param bytes Destination pointer to store encoded color
*/
inline void EncodeRGBA4(const Math::Vec4<u8>& color, u8* bytes) {
*reinterpret_cast<u16_le*>(bytes) = (Convert8To4(color.r()) << 12) |
(Convert8To4(color.g()) << 8) |
(Convert8To4(color.b()) << 4) | Convert8To4(color.a());
const u16 data = (Convert8To4(color.r()) << 12) | (Convert8To4(color.g()) << 8) |
(Convert8To4(color.b()) << 4) | Convert8To4(color.a());
std::memcpy(bytes, &data, sizeof(data));
}
/**
@@ -207,7 +218,8 @@ inline void EncodeRGBA4(const Math::Vec4<u8>& color, u8* bytes) {
* @param bytes Pointer where to store the encoded value
*/
inline void EncodeD16(u32 value, u8* bytes) {
*reinterpret_cast<u16_le*>(bytes) = value & 0xFFFF;
const u16_le data = static_cast<u16>(value);
std::memcpy(bytes, &data, sizeof(data));
}
/**

View File

@@ -32,6 +32,7 @@
#define SDMC_DIR "sdmc"
#define NAND_DIR "nand"
#define SYSDATA_DIR "sysdata"
#define KEYS_DIR "keys"
#define LOG_DIR "log"
// Filenames

View File

@@ -706,6 +706,7 @@ const std::string& GetUserPath(UserPath path, const std::string& new_path) {
paths.emplace(UserPath::SDMCDir, user_path + SDMC_DIR DIR_SEP);
paths.emplace(UserPath::NANDDir, user_path + NAND_DIR DIR_SEP);
paths.emplace(UserPath::SysDataDir, user_path + SYSDATA_DIR DIR_SEP);
paths.emplace(UserPath::KeysDir, user_path + KEYS_DIR DIR_SEP);
// TODO: Put the logs in a better location for each OS
paths.emplace(UserPath::LogDir, user_path + LOG_DIR DIR_SEP);
}
@@ -736,6 +737,19 @@ const std::string& GetUserPath(UserPath path, const std::string& new_path) {
return paths[path];
}
std::string GetHactoolConfigurationPath() {
#ifdef _WIN32
PWSTR pw_local_path = nullptr;
if (SHGetKnownFolderPath(FOLDERID_Profile, 0, nullptr, &pw_local_path) != S_OK)
return "";
std::string local_path = Common::UTF16ToUTF8(pw_local_path);
CoTaskMemFree(pw_local_path);
return local_path + "\\.switch";
#else
return GetHomeDirectory() + "/.switch";
#endif
}
size_t WriteStringToFile(bool text_file, const std::string& str, const char* filename) {
return FileUtil::IOFile(filename, text_file ? "w" : "wb").WriteBytes(str.data(), str.size());
}
@@ -870,11 +884,21 @@ std::string_view RemoveTrailingSlash(std::string_view path) {
return path;
}
std::string SanitizePath(std::string_view path_) {
std::string SanitizePath(std::string_view path_, DirectorySeparator directory_separator) {
std::string path(path_);
std::replace(path.begin(), path.end(), '\\', '/');
char type1 = directory_separator == DirectorySeparator::BackwardSlash ? '/' : '\\';
char type2 = directory_separator == DirectorySeparator::BackwardSlash ? '\\' : '/';
if (directory_separator == DirectorySeparator::PlatformDefault) {
#ifdef _WIN32
type1 = '/';
type2 = '\\';
#endif
}
std::replace(path.begin(), path.end(), type1, type2);
path.erase(std::unique(path.begin(), path.end(),
[](char c1, char c2) { return c1 == '/' && c2 == '/'; }),
[type2](char c1, char c2) { return c1 == type2 && c2 == type2; }),
path.end());
return std::string(RemoveTrailingSlash(path));
}

View File

@@ -8,6 +8,7 @@
#include <cstdio>
#include <fstream>
#include <functional>
#include <limits>
#include <string>
#include <string_view>
#include <type_traits>
@@ -23,6 +24,7 @@ namespace FileUtil {
enum class UserPath {
CacheDir,
ConfigDir,
KeysDir,
LogDir,
NANDDir,
RootDir,
@@ -125,6 +127,8 @@ bool SetCurrentDir(const std::string& directory);
// directory. To be used in "multi-user" mode (that is, installed).
const std::string& GetUserPath(UserPath path, const std::string& new_path = "");
std::string GetHactoolConfigurationPath();
// Returns the path to where the sys file are
std::string GetSysDirectory();
@@ -178,8 +182,12 @@ std::vector<T> SliceVector(const std::vector<T>& vector, size_t first, size_t la
return std::vector<T>(vector.begin() + first, vector.begin() + first + last);
}
// Removes trailing slash, makes all '\\' into '/', and removes duplicate '/'.
std::string SanitizePath(std::string_view path);
enum class DirectorySeparator { ForwardSlash, BackwardSlash, PlatformDefault };
// Removes trailing slash, makes all '\\' into '/', and removes duplicate '/'. Makes '/' into '\\'
// depending if directory_separator is BackwardSlash or PlatformDefault and running on windows
std::string SanitizePath(std::string_view path,
DirectorySeparator directory_separator = DirectorySeparator::ForwardSlash);
// simple wrapper for cstdlib file functions to
// hopefully will make error checking easier
@@ -204,39 +212,42 @@ public:
template <typename T>
size_t ReadArray(T* data, size_t length) const {
static_assert(std::is_trivially_copyable<T>(),
static_assert(std::is_trivially_copyable_v<T>,
"Given array does not consist of trivially copyable objects");
if (!IsOpen())
return -1;
if (!IsOpen()) {
return std::numeric_limits<size_t>::max();
}
return std::fread(data, sizeof(T), length, m_file);
}
template <typename T>
size_t WriteArray(const T* data, size_t length) {
static_assert(std::is_trivially_copyable<T>(),
static_assert(std::is_trivially_copyable_v<T>,
"Given array does not consist of trivially copyable objects");
if (!IsOpen())
return -1;
if (!IsOpen()) {
return std::numeric_limits<size_t>::max();
}
return std::fwrite(data, sizeof(T), length, m_file);
}
template <typename T>
size_t ReadBytes(T* data, size_t length) const {
static_assert(std::is_trivially_copyable<T>(), "T must be trivially copyable");
static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable");
return ReadArray(reinterpret_cast<char*>(data), length);
}
template <typename T>
size_t WriteBytes(const T* data, size_t length) {
static_assert(std::is_trivially_copyable<T>(), "T must be trivially copyable");
static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable");
return WriteArray(reinterpret_cast<const char*>(data), length);
}
template <typename T>
size_t WriteObject(const T& object) {
static_assert(!std::is_pointer<T>::value, "Given object is a pointer");
static_assert(!std::is_pointer_v<T>, "WriteObject arguments must not be a pointer");
return WriteArray(&object, 1);
}

View File

@@ -28,7 +28,7 @@ static inline u64 ComputeHash64(const void* data, size_t len) {
*/
template <typename T>
static inline u64 ComputeStructHash64(const T& data) {
static_assert(std::is_trivially_copyable<T>(),
static_assert(std::is_trivially_copyable_v<T>,
"Type passed to ComputeStructHash64 must be trivially copyable");
return ComputeHash64(&data, sizeof(data));
}
@@ -38,7 +38,7 @@ template <typename T>
struct HashableStruct {
// In addition to being trivially copyable, T must also have a trivial default constructor,
// because any member initialization would be overridden by memset
static_assert(std::is_trivial<T>(), "Type passed to HashableStruct must be trivial");
static_assert(std::is_trivial_v<T>, "Type passed to HashableStruct must be trivial");
/*
* We use a union because "implicitly-defined copy/move constructor for a union X copies the
* object representation of X." and "implicitly-defined copy assignment operator for a union X

View File

@@ -168,32 +168,47 @@ void FileBackend::Write(const Entry& entry) {
SUB(Service, AM) \
SUB(Service, AOC) \
SUB(Service, APM) \
SUB(Service, ARP) \
SUB(Service, BCAT) \
SUB(Service, BPC) \
SUB(Service, BTDRV) \
SUB(Service, BTM) \
SUB(Service, Capture) \
SUB(Service, ERPT) \
SUB(Service, ETicket) \
SUB(Service, EUPLD) \
SUB(Service, Fatal) \
SUB(Service, FGM) \
SUB(Service, Friend) \
SUB(Service, FS) \
SUB(Service, GRC) \
SUB(Service, HID) \
SUB(Service, LBL) \
SUB(Service, LDN) \
SUB(Service, LDR) \
SUB(Service, LM) \
SUB(Service, Migration) \
SUB(Service, Mii) \
SUB(Service, MM) \
SUB(Service, NCM) \
SUB(Service, NFC) \
SUB(Service, NFP) \
SUB(Service, NIFM) \
SUB(Service, NIM) \
SUB(Service, NS) \
SUB(Service, NVDRV) \
SUB(Service, PCIE) \
SUB(Service, PCTL) \
SUB(Service, PCV) \
SUB(Service, PM) \
SUB(Service, PREPO) \
SUB(Service, PSC) \
SUB(Service, SET) \
SUB(Service, SM) \
SUB(Service, SPL) \
SUB(Service, SSL) \
SUB(Service, Time) \
SUB(Service, USB) \
SUB(Service, VI) \
SUB(Service, WLAN) \
CLS(HW) \
@@ -212,6 +227,7 @@ void FileBackend::Write(const Entry& entry) {
CLS(Input) \
CLS(Network) \
CLS(Loader) \
CLS(Crypto) \
CLS(WebService)
// GetClassName is a macro defined by Windows.h, grrr...

View File

@@ -54,33 +54,48 @@ enum class Class : ClassType {
Service_AM, ///< The AM (Applet manager) service
Service_AOC, ///< The AOC (AddOn Content) service
Service_APM, ///< The APM (Performance) service
Service_ARP, ///< The ARP service
Service_Audio, ///< The Audio (Audio control) service
Service_BCAT, ///< The BCAT service
Service_BPC, ///< The BPC service
Service_BTDRV, ///< The Bluetooth driver service
Service_BTM, ///< The BTM service
Service_Capture, ///< The capture service
Service_ERPT, ///< The error reporting service
Service_ETicket, ///< The ETicket service
Service_EUPLD, ///< The error upload service
Service_Fatal, ///< The Fatal service
Service_FGM, ///< The FGM service
Service_Friend, ///< The friend service
Service_FS, ///< The FS (Filesystem) service
Service_GRC, ///< The game recording service
Service_HID, ///< The HID (Human interface device) service
Service_LBL, ///< The LBL (LCD backlight) service
Service_LDN, ///< The LDN (Local domain network) service
Service_LDR, ///< The loader service
Service_LM, ///< The LM (Logger) service
Service_Migration, ///< The migration service
Service_Mii, ///< The Mii service
Service_MM, ///< The MM (Multimedia) service
Service_NCM, ///< The NCM service
Service_NFC, ///< The NFC (Near-field communication) service
Service_NFP, ///< The NFP service
Service_NIFM, ///< The NIFM (Network interface) service
Service_NIM, ///< The NIM service
Service_NS, ///< The NS services
Service_NVDRV, ///< The NVDRV (Nvidia driver) service
Service_PCIE, ///< The PCIe service
Service_PCTL, ///< The PCTL (Parental control) service
Service_PCV, ///< The PCV service
Service_PM, ///< The PM service
Service_PREPO, ///< The PREPO (Play report) service
Service_PSC, ///< The PSC service
Service_SET, ///< The SET (Settings) service
Service_SM, ///< The SM (Service manager) service
Service_SPL, ///< The SPL service
Service_SSL, ///< The SSL service
Service_Time, ///< The time service
Service_USB, ///< The USB (Universal Serial Bus) service
Service_VI, ///< The VI (Video interface) service
Service_WLAN, ///< The WLAN (Wireless local area network) service
HW, ///< Low-level hardware emulation
@@ -97,6 +112,7 @@ enum class Class : ClassType {
Audio_DSP, ///< The HLE implementation of the DSP
Audio_Sink, ///< Emulator audio output backend
Loader, ///< ROM loader
Crypto, ///< Cryptographic engine/functions
Input, ///< Input emulation
Network, ///< Network emulation
WebService, ///< Interface to yuzu Web Services

View File

@@ -19,12 +19,12 @@ inline bool IntervalsIntersect(unsigned start0, unsigned length0, unsigned start
template <class T>
struct Rectangle {
T left;
T top;
T right;
T bottom;
T left{};
T top{};
T right{};
T bottom{};
Rectangle() {}
Rectangle() = default;
Rectangle(T left, T top, T right, T bottom)
: left(left), top(top), right(right), bottom(bottom) {}

View File

@@ -42,140 +42,136 @@ class Vec3;
template <typename T>
class Vec4;
template <typename T>
static inline Vec2<T> MakeVec(const T& x, const T& y);
template <typename T>
static inline Vec3<T> MakeVec(const T& x, const T& y, const T& z);
template <typename T>
static inline Vec4<T> MakeVec(const T& x, const T& y, const T& z, const T& w);
template <typename T>
class Vec2 {
public:
T x{};
T y{};
Vec2() = default;
Vec2(const T& _x, const T& _y) : x(_x), y(_y) {}
constexpr Vec2() = default;
constexpr Vec2(const T& x_, const T& y_) : x(x_), y(y_) {}
template <typename T2>
Vec2<T2> Cast() const {
return Vec2<T2>((T2)x, (T2)y);
constexpr Vec2<T2> Cast() const {
return Vec2<T2>(static_cast<T2>(x), static_cast<T2>(y));
}
static Vec2 AssignToAll(const T& f) {
return Vec2<T>(f, f);
static constexpr Vec2 AssignToAll(const T& f) {
return Vec2{f, f};
}
Vec2<decltype(T{} + T{})> operator+(const Vec2& other) const {
return MakeVec(x + other.x, y + other.y);
constexpr Vec2<decltype(T{} + T{})> operator+(const Vec2& other) const {
return {x + other.x, y + other.y};
}
void operator+=(const Vec2& other) {
constexpr Vec2& operator+=(const Vec2& other) {
x += other.x;
y += other.y;
return *this;
}
Vec2<decltype(T{} - T{})> operator-(const Vec2& other) const {
return MakeVec(x - other.x, y - other.y);
constexpr Vec2<decltype(T{} - T{})> operator-(const Vec2& other) const {
return {x - other.x, y - other.y};
}
void operator-=(const Vec2& other) {
constexpr Vec2& operator-=(const Vec2& other) {
x -= other.x;
y -= other.y;
return *this;
}
template <typename U = T>
Vec2<std::enable_if_t<std::is_signed<U>::value, U>> operator-() const {
return MakeVec(-x, -y);
constexpr Vec2<std::enable_if_t<std::is_signed_v<U>, U>> operator-() const {
return {-x, -y};
}
Vec2<decltype(T{} * T{})> operator*(const Vec2& other) const {
return MakeVec(x * other.x, y * other.y);
}
template <typename V>
Vec2<decltype(T{} * V{})> operator*(const V& f) const {
return MakeVec(x * f, y * f);
}
template <typename V>
void operator*=(const V& f) {
*this = *this * f;
}
template <typename V>
Vec2<decltype(T{} / V{})> operator/(const V& f) const {
return MakeVec(x / f, y / f);
}
template <typename V>
void operator/=(const V& f) {
*this = *this / f;
constexpr Vec2<decltype(T{} * T{})> operator*(const Vec2& other) const {
return {x * other.x, y * other.y};
}
T Length2() const {
template <typename V>
constexpr Vec2<decltype(T{} * V{})> operator*(const V& f) const {
return {x * f, y * f};
}
template <typename V>
constexpr Vec2& operator*=(const V& f) {
*this = *this * f;
return *this;
}
template <typename V>
constexpr Vec2<decltype(T{} / V{})> operator/(const V& f) const {
return {x / f, y / f};
}
template <typename V>
constexpr Vec2& operator/=(const V& f) {
*this = *this / f;
return *this;
}
constexpr T Length2() const {
return x * x + y * y;
}
// Only implemented for T=float
float Length() const;
void SetLength(const float l);
Vec2 WithLength(const float l) const;
float Distance2To(Vec2& other);
Vec2 Normalized() const;
float Normalize(); // returns the previous length, which is often useful
T& operator[](int i) // allow vector[1] = 3 (vector.y=3)
{
constexpr T& operator[](std::size_t i) {
return *((&x) + i);
}
T operator[](const int i) const {
constexpr const T& operator[](std::size_t i) const {
return *((&x) + i);
}
void SetZero() {
constexpr void SetZero() {
x = 0;
y = 0;
}
// Common aliases: UV (texel coordinates), ST (texture coordinates)
T& u() {
constexpr T& u() {
return x;
}
T& v() {
constexpr T& v() {
return y;
}
T& s() {
constexpr T& s() {
return x;
}
T& t() {
constexpr T& t() {
return y;
}
const T& u() const {
constexpr const T& u() const {
return x;
}
const T& v() const {
constexpr const T& v() const {
return y;
}
const T& s() const {
constexpr const T& s() const {
return x;
}
const T& t() const {
constexpr const T& t() const {
return y;
}
// swizzlers - create a subvector of specific components
const Vec2 yx() const {
constexpr Vec2 yx() const {
return Vec2(y, x);
}
const Vec2 vu() const {
constexpr Vec2 vu() const {
return Vec2(y, x);
}
const Vec2 ts() const {
constexpr Vec2 ts() const {
return Vec2(y, x);
}
};
template <typename T, typename V>
Vec2<T> operator*(const V& f, const Vec2<T>& vec) {
constexpr Vec2<T> operator*(const V& f, const Vec2<T>& vec) {
return Vec2<T>(f * vec.x, f * vec.y);
}
typedef Vec2<float> Vec2f;
using Vec2f = Vec2<float>;
template <>
inline float Vec2<float>::Length() const {
@@ -196,147 +192,151 @@ public:
T y{};
T z{};
Vec3() = default;
Vec3(const T& _x, const T& _y, const T& _z) : x(_x), y(_y), z(_z) {}
constexpr Vec3() = default;
constexpr Vec3(const T& x_, const T& y_, const T& z_) : x(x_), y(y_), z(z_) {}
template <typename T2>
Vec3<T2> Cast() const {
return MakeVec<T2>((T2)x, (T2)y, (T2)z);
constexpr Vec3<T2> Cast() const {
return Vec3<T2>(static_cast<T2>(x), static_cast<T2>(y), static_cast<T2>(z));
}
// Only implemented for T=int and T=float
static Vec3 FromRGB(unsigned int rgb);
unsigned int ToRGB() const; // alpha bits set to zero
static Vec3 AssignToAll(const T& f) {
return MakeVec(f, f, f);
static constexpr Vec3 AssignToAll(const T& f) {
return Vec3(f, f, f);
}
Vec3<decltype(T{} + T{})> operator+(const Vec3& other) const {
return MakeVec(x + other.x, y + other.y, z + other.z);
constexpr Vec3<decltype(T{} + T{})> operator+(const Vec3& other) const {
return {x + other.x, y + other.y, z + other.z};
}
void operator+=(const Vec3& other) {
constexpr Vec3& operator+=(const Vec3& other) {
x += other.x;
y += other.y;
z += other.z;
return *this;
}
Vec3<decltype(T{} - T{})> operator-(const Vec3& other) const {
return MakeVec(x - other.x, y - other.y, z - other.z);
constexpr Vec3<decltype(T{} - T{})> operator-(const Vec3& other) const {
return {x - other.x, y - other.y, z - other.z};
}
void operator-=(const Vec3& other) {
constexpr Vec3& operator-=(const Vec3& other) {
x -= other.x;
y -= other.y;
z -= other.z;
return *this;
}
template <typename U = T>
Vec3<std::enable_if_t<std::is_signed<U>::value, U>> operator-() const {
return MakeVec(-x, -y, -z);
}
Vec3<decltype(T{} * T{})> operator*(const Vec3& other) const {
return MakeVec(x * other.x, y * other.y, z * other.z);
}
template <typename V>
Vec3<decltype(T{} * V{})> operator*(const V& f) const {
return MakeVec(x * f, y * f, z * f);
}
template <typename V>
void operator*=(const V& f) {
*this = *this * f;
}
template <typename V>
Vec3<decltype(T{} / V{})> operator/(const V& f) const {
return MakeVec(x / f, y / f, z / f);
}
template <typename V>
void operator/=(const V& f) {
*this = *this / f;
constexpr Vec3<std::enable_if_t<std::is_signed_v<U>, U>> operator-() const {
return {-x, -y, -z};
}
T Length2() const {
constexpr Vec3<decltype(T{} * T{})> operator*(const Vec3& other) const {
return {x * other.x, y * other.y, z * other.z};
}
template <typename V>
constexpr Vec3<decltype(T{} * V{})> operator*(const V& f) const {
return {x * f, y * f, z * f};
}
template <typename V>
constexpr Vec3& operator*=(const V& f) {
*this = *this * f;
return *this;
}
template <typename V>
constexpr Vec3<decltype(T{} / V{})> operator/(const V& f) const {
return {x / f, y / f, z / f};
}
template <typename V>
constexpr Vec3& operator/=(const V& f) {
*this = *this / f;
return *this;
}
constexpr T Length2() const {
return x * x + y * y + z * z;
}
// Only implemented for T=float
float Length() const;
void SetLength(const float l);
Vec3 WithLength(const float l) const;
float Distance2To(Vec3& other);
Vec3 Normalized() const;
float Normalize(); // returns the previous length, which is often useful
T& operator[](int i) // allow vector[2] = 3 (vector.z=3)
{
return *((&x) + i);
}
T operator[](const int i) const {
constexpr T& operator[](std::size_t i) {
return *((&x) + i);
}
void SetZero() {
constexpr const T& operator[](std::size_t i) const {
return *((&x) + i);
}
constexpr void SetZero() {
x = 0;
y = 0;
z = 0;
}
// Common aliases: UVW (texel coordinates), RGB (colors), STQ (texture coordinates)
T& u() {
constexpr T& u() {
return x;
}
T& v() {
constexpr T& v() {
return y;
}
T& w() {
constexpr T& w() {
return z;
}
T& r() {
constexpr T& r() {
return x;
}
T& g() {
constexpr T& g() {
return y;
}
T& b() {
constexpr T& b() {
return z;
}
T& s() {
constexpr T& s() {
return x;
}
T& t() {
constexpr T& t() {
return y;
}
T& q() {
constexpr T& q() {
return z;
}
const T& u() const {
constexpr const T& u() const {
return x;
}
const T& v() const {
constexpr const T& v() const {
return y;
}
const T& w() const {
constexpr const T& w() const {
return z;
}
const T& r() const {
constexpr const T& r() const {
return x;
}
const T& g() const {
constexpr const T& g() const {
return y;
}
const T& b() const {
constexpr const T& b() const {
return z;
}
const T& s() const {
constexpr const T& s() const {
return x;
}
const T& t() const {
constexpr const T& t() const {
return y;
}
const T& q() const {
constexpr const T& q() const {
return z;
}
@@ -345,7 +345,7 @@ public:
// _DEFINE_SWIZZLER2 defines a single such function, DEFINE_SWIZZLER2 defines all of them for all
// component names (x<->r) and permutations (xy<->yx)
#define _DEFINE_SWIZZLER2(a, b, name) \
const Vec2<T> name() const { \
constexpr Vec2<T> name() const { \
return Vec2<T>(a, b); \
}
#define DEFINE_SWIZZLER2(a, b, a2, b2, a3, b3, a4, b4) \
@@ -366,7 +366,7 @@ public:
};
template <typename T, typename V>
Vec3<T> operator*(const V& f, const Vec3<T>& vec) {
constexpr Vec3<T> operator*(const V& f, const Vec3<T>& vec) {
return Vec3<T>(f * vec.x, f * vec.y, f * vec.z);
}
@@ -387,7 +387,7 @@ inline float Vec3<float>::Normalize() {
return length;
}
typedef Vec3<float> Vec3f;
using Vec3f = Vec3<float>;
template <typename T>
class Vec4 {
@@ -397,86 +397,88 @@ public:
T z{};
T w{};
Vec4() = default;
Vec4(const T& _x, const T& _y, const T& _z, const T& _w) : x(_x), y(_y), z(_z), w(_w) {}
constexpr Vec4() = default;
constexpr Vec4(const T& x_, const T& y_, const T& z_, const T& w_)
: x(x_), y(y_), z(z_), w(w_) {}
template <typename T2>
Vec4<T2> Cast() const {
return Vec4<T2>((T2)x, (T2)y, (T2)z, (T2)w);
constexpr Vec4<T2> Cast() const {
return Vec4<T2>(static_cast<T2>(x), static_cast<T2>(y), static_cast<T2>(z),
static_cast<T2>(w));
}
// Only implemented for T=int and T=float
static Vec4 FromRGBA(unsigned int rgba);
unsigned int ToRGBA() const;
static Vec4 AssignToAll(const T& f) {
return Vec4<T>(f, f, f, f);
static constexpr Vec4 AssignToAll(const T& f) {
return Vec4(f, f, f, f);
}
Vec4<decltype(T{} + T{})> operator+(const Vec4& other) const {
return MakeVec(x + other.x, y + other.y, z + other.z, w + other.w);
constexpr Vec4<decltype(T{} + T{})> operator+(const Vec4& other) const {
return {x + other.x, y + other.y, z + other.z, w + other.w};
}
void operator+=(const Vec4& other) {
constexpr Vec4& operator+=(const Vec4& other) {
x += other.x;
y += other.y;
z += other.z;
w += other.w;
return *this;
}
Vec4<decltype(T{} - T{})> operator-(const Vec4& other) const {
return MakeVec(x - other.x, y - other.y, z - other.z, w - other.w);
constexpr Vec4<decltype(T{} - T{})> operator-(const Vec4& other) const {
return {x - other.x, y - other.y, z - other.z, w - other.w};
}
void operator-=(const Vec4& other) {
constexpr Vec4& operator-=(const Vec4& other) {
x -= other.x;
y -= other.y;
z -= other.z;
w -= other.w;
return *this;
}
template <typename U = T>
Vec4<std::enable_if_t<std::is_signed<U>::value, U>> operator-() const {
return MakeVec(-x, -y, -z, -w);
}
Vec4<decltype(T{} * T{})> operator*(const Vec4& other) const {
return MakeVec(x * other.x, y * other.y, z * other.z, w * other.w);
}
template <typename V>
Vec4<decltype(T{} * V{})> operator*(const V& f) const {
return MakeVec(x * f, y * f, z * f, w * f);
}
template <typename V>
void operator*=(const V& f) {
*this = *this * f;
}
template <typename V>
Vec4<decltype(T{} / V{})> operator/(const V& f) const {
return MakeVec(x / f, y / f, z / f, w / f);
}
template <typename V>
void operator/=(const V& f) {
*this = *this / f;
constexpr Vec4<std::enable_if_t<std::is_signed_v<U>, U>> operator-() const {
return {-x, -y, -z, -w};
}
T Length2() const {
constexpr Vec4<decltype(T{} * T{})> operator*(const Vec4& other) const {
return {x * other.x, y * other.y, z * other.z, w * other.w};
}
template <typename V>
constexpr Vec4<decltype(T{} * V{})> operator*(const V& f) const {
return {x * f, y * f, z * f, w * f};
}
template <typename V>
constexpr Vec4& operator*=(const V& f) {
*this = *this * f;
return *this;
}
template <typename V>
constexpr Vec4<decltype(T{} / V{})> operator/(const V& f) const {
return {x / f, y / f, z / f, w / f};
}
template <typename V>
constexpr Vec4& operator/=(const V& f) {
*this = *this / f;
return *this;
}
constexpr T Length2() const {
return x * x + y * y + z * z + w * w;
}
// Only implemented for T=float
float Length() const;
void SetLength(const float l);
Vec4 WithLength(const float l) const;
float Distance2To(Vec4& other);
Vec4 Normalized() const;
float Normalize(); // returns the previous length, which is often useful
T& operator[](int i) // allow vector[2] = 3 (vector.z=3)
{
return *((&x) + i);
}
T operator[](const int i) const {
constexpr T& operator[](std::size_t i) {
return *((&x) + i);
}
void SetZero() {
constexpr const T& operator[](std::size_t i) const {
return *((&x) + i);
}
constexpr void SetZero() {
x = 0;
y = 0;
z = 0;
@@ -484,29 +486,29 @@ public:
}
// Common alias: RGBA (colors)
T& r() {
constexpr T& r() {
return x;
}
T& g() {
constexpr T& g() {
return y;
}
T& b() {
constexpr T& b() {
return z;
}
T& a() {
constexpr T& a() {
return w;
}
const T& r() const {
constexpr const T& r() const {
return x;
}
const T& g() const {
constexpr const T& g() const {
return y;
}
const T& b() const {
constexpr const T& b() const {
return z;
}
const T& a() const {
constexpr const T& a() const {
return w;
}
@@ -518,7 +520,7 @@ public:
// DEFINE_SWIZZLER2_COMP2 defines two component functions for all component names (x<->r) and
// permutations (xy<->yx)
#define _DEFINE_SWIZZLER2(a, b, name) \
const Vec2<T> name() const { \
constexpr Vec2<T> name() const { \
return Vec2<T>(a, b); \
}
#define DEFINE_SWIZZLER2_COMP1(a, a2) \
@@ -545,7 +547,7 @@ public:
#undef _DEFINE_SWIZZLER2
#define _DEFINE_SWIZZLER3(a, b, c, name) \
const Vec3<T> name() const { \
constexpr Vec3<T> name() const { \
return Vec3<T>(a, b, c); \
}
#define DEFINE_SWIZZLER3_COMP1(a, a2) \
@@ -579,51 +581,51 @@ public:
};
template <typename T, typename V>
Vec4<decltype(V{} * T{})> operator*(const V& f, const Vec4<T>& vec) {
return MakeVec(f * vec.x, f * vec.y, f * vec.z, f * vec.w);
constexpr Vec4<decltype(V{} * T{})> operator*(const V& f, const Vec4<T>& vec) {
return {f * vec.x, f * vec.y, f * vec.z, f * vec.w};
}
typedef Vec4<float> Vec4f;
using Vec4f = Vec4<float>;
template <typename T>
static inline decltype(T{} * T{} + T{} * T{}) Dot(const Vec2<T>& a, const Vec2<T>& b) {
constexpr decltype(T{} * T{} + T{} * T{}) Dot(const Vec2<T>& a, const Vec2<T>& b) {
return a.x * b.x + a.y * b.y;
}
template <typename T>
static inline decltype(T{} * T{} + T{} * T{}) Dot(const Vec3<T>& a, const Vec3<T>& b) {
constexpr decltype(T{} * T{} + T{} * T{}) Dot(const Vec3<T>& a, const Vec3<T>& b) {
return a.x * b.x + a.y * b.y + a.z * b.z;
}
template <typename T>
static inline decltype(T{} * T{} + T{} * T{}) Dot(const Vec4<T>& a, const Vec4<T>& b) {
constexpr decltype(T{} * T{} + T{} * T{}) Dot(const Vec4<T>& a, const Vec4<T>& b) {
return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w;
}
template <typename T>
static inline Vec3<decltype(T{} * T{} - T{} * T{})> Cross(const Vec3<T>& a, const Vec3<T>& b) {
return MakeVec(a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x);
constexpr Vec3<decltype(T{} * T{} - T{} * T{})> Cross(const Vec3<T>& a, const Vec3<T>& b) {
return {a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x};
}
// linear interpolation via float: 0.0=begin, 1.0=end
template <typename X>
static inline decltype(X{} * float{} + X{} * float{}) Lerp(const X& begin, const X& end,
const float t) {
constexpr decltype(X{} * float{} + X{} * float{}) Lerp(const X& begin, const X& end,
const float t) {
return begin * (1.f - t) + end * t;
}
// linear interpolation via int: 0=begin, base=end
template <typename X, int base>
static inline decltype((X{} * int{} + X{} * int{}) / base) LerpInt(const X& begin, const X& end,
const int t) {
constexpr decltype((X{} * int{} + X{} * int{}) / base) LerpInt(const X& begin, const X& end,
const int t) {
return (begin * (base - t) + end * t) / base;
}
// bilinear interpolation. s is for interpolating x00-x01 and x10-x11, and t is for the second
// interpolation.
template <typename X>
inline auto BilinearInterp(const X& x00, const X& x01, const X& x10, const X& x11, const float s,
const float t) {
constexpr auto BilinearInterp(const X& x00, const X& x01, const X& x10, const X& x11, const float s,
const float t) {
auto y0 = Lerp(x00, x01, s);
auto y1 = Lerp(x10, x11, s);
return Lerp(y0, y1, t);
@@ -631,42 +633,42 @@ inline auto BilinearInterp(const X& x00, const X& x01, const X& x10, const X& x1
// Utility vector factories
template <typename T>
static inline Vec2<T> MakeVec(const T& x, const T& y) {
constexpr Vec2<T> MakeVec(const T& x, const T& y) {
return Vec2<T>{x, y};
}
template <typename T>
static inline Vec3<T> MakeVec(const T& x, const T& y, const T& z) {
constexpr Vec3<T> MakeVec(const T& x, const T& y, const T& z) {
return Vec3<T>{x, y, z};
}
template <typename T>
static inline Vec4<T> MakeVec(const T& x, const T& y, const Vec2<T>& zw) {
constexpr Vec4<T> MakeVec(const T& x, const T& y, const Vec2<T>& zw) {
return MakeVec(x, y, zw[0], zw[1]);
}
template <typename T>
static inline Vec3<T> MakeVec(const Vec2<T>& xy, const T& z) {
constexpr Vec3<T> MakeVec(const Vec2<T>& xy, const T& z) {
return MakeVec(xy[0], xy[1], z);
}
template <typename T>
static inline Vec3<T> MakeVec(const T& x, const Vec2<T>& yz) {
constexpr Vec3<T> MakeVec(const T& x, const Vec2<T>& yz) {
return MakeVec(x, yz[0], yz[1]);
}
template <typename T>
static inline Vec4<T> MakeVec(const T& x, const T& y, const T& z, const T& w) {
constexpr Vec4<T> MakeVec(const T& x, const T& y, const T& z, const T& w) {
return Vec4<T>{x, y, z, w};
}
template <typename T>
static inline Vec4<T> MakeVec(const Vec2<T>& xy, const T& z, const T& w) {
constexpr Vec4<T> MakeVec(const Vec2<T>& xy, const T& z, const T& w) {
return MakeVec(xy[0], xy[1], z, w);
}
template <typename T>
static inline Vec4<T> MakeVec(const T& x, const Vec2<T>& yz, const T& w) {
constexpr Vec4<T> MakeVec(const T& x, const Vec2<T>& yz, const T& w) {
return MakeVec(x, yz[0], yz[1], w);
}
@@ -674,17 +676,17 @@ static inline Vec4<T> MakeVec(const T& x, const Vec2<T>& yz, const T& w) {
// Even if someone wanted to use an odd object like Vec2<Vec2<T>>, the compiler would error
// out soon enough due to misuse of the returned structure.
template <typename T>
static inline Vec4<T> MakeVec(const Vec2<T>& xy, const Vec2<T>& zw) {
constexpr Vec4<T> MakeVec(const Vec2<T>& xy, const Vec2<T>& zw) {
return MakeVec(xy[0], xy[1], zw[0], zw[1]);
}
template <typename T>
static inline Vec4<T> MakeVec(const Vec3<T>& xyz, const T& w) {
constexpr Vec4<T> MakeVec(const Vec3<T>& xyz, const T& w) {
return MakeVec(xyz[0], xyz[1], xyz[2], w);
}
template <typename T>
static inline Vec4<T> MakeVec(const T& x, const Vec3<T>& yzw) {
constexpr Vec4<T> MakeVec(const T& x, const Vec3<T>& yzw) {
return MakeVec(x, yzw[0], yzw[1], yzw[2]);
}

View File

@@ -34,7 +34,7 @@ inline bool IsWithin2G(const Xbyak::CodeGenerator& code, uintptr_t target) {
template <typename T>
inline void CallFarFunction(Xbyak::CodeGenerator& code, const T f) {
static_assert(std::is_pointer<T>(), "Argument must be a (function) pointer.");
static_assert(std::is_pointer_v<T>, "Argument must be a (function) pointer.");
size_t addr = reinterpret_cast<size_t>(f);
if (IsWithin2G(code, addr)) {
code.call(f);

View File

@@ -12,6 +12,16 @@ add_library(core STATIC
core_timing.h
core_timing_util.cpp
core_timing_util.h
crypto/aes_util.cpp
crypto/aes_util.h
crypto/encryption_layer.cpp
crypto/encryption_layer.h
crypto/key_manager.cpp
crypto/key_manager.h
crypto/ctr_encryption_layer.cpp
crypto/ctr_encryption_layer.h
file_sys/card_image.cpp
file_sys/card_image.h
file_sys/content_archive.cpp
file_sys/content_archive.h
file_sys/control_metadata.cpp
@@ -63,10 +73,10 @@ add_library(core STATIC
hle/kernel/hle_ipc.h
hle/kernel/kernel.cpp
hle/kernel/kernel.h
hle/kernel/memory.cpp
hle/kernel/memory.h
hle/kernel/mutex.cpp
hle/kernel/mutex.h
hle/kernel/object.cpp
hle/kernel/object.h
hle/kernel/process.cpp
hle/kernel/process.h
hle/kernel/resource_limit.cpp
@@ -94,8 +104,6 @@ add_library(core STATIC
hle/lock.cpp
hle/lock.h
hle/result.h
hle/romfs.cpp
hle/romfs.h
hle/service/acc/acc.cpp
hle/service/acc/acc.h
hle/service/acc/acc_aa.cpp
@@ -124,6 +132,8 @@ add_library(core STATIC
hle/service/apm/apm.h
hle/service/apm/interface.cpp
hle/service/apm/interface.h
hle/service/arp/arp.cpp
hle/service/arp/arp.h
hle/service/audio/audctl.cpp
hle/service/audio/audctl.h
hle/service/audio/auddbg.cpp
@@ -154,10 +164,14 @@ add_library(core STATIC
hle/service/bcat/bcat.h
hle/service/bcat/module.cpp
hle/service/bcat/module.h
hle/service/bpc/bpc.cpp
hle/service/bpc/bpc.h
hle/service/btdrv/btdrv.cpp
hle/service/btdrv/btdrv.h
hle/service/btm/btm.cpp
hle/service/btm/btm.h
hle/service/caps/caps.cpp
hle/service/caps/caps.h
hle/service/erpt/erpt.cpp
hle/service/erpt/erpt.h
hle/service/es/es.cpp
@@ -172,6 +186,10 @@ add_library(core STATIC
hle/service/fatal/fatal_u.h
hle/service/filesystem/filesystem.cpp
hle/service/filesystem/filesystem.h
hle/service/filesystem/fsp_ldr.cpp
hle/service/filesystem/fsp_ldr.h
hle/service/filesystem/fsp_pr.cpp
hle/service/filesystem/fsp_pr.h
hle/service/filesystem/fsp_srv.cpp
hle/service/filesystem/fsp_srv.h
hle/service/fgm/fgm.cpp
@@ -196,6 +214,8 @@ add_library(core STATIC
hle/service/ldr/ldr.h
hle/service/lm/lm.cpp
hle/service/lm/lm.h
hle/service/mig/mig.cpp
hle/service/mig/mig.h
hle/service/mii/mii.cpp
hle/service/mii/mii.h
hle/service/mm/mm_u.cpp
@@ -247,10 +267,14 @@ add_library(core STATIC
hle/service/pctl/module.h
hle/service/pctl/pctl.cpp
hle/service/pctl/pctl.h
hle/service/pcv/pcv.cpp
hle/service/pcv/pcv.h
hle/service/pm/pm.cpp
hle/service/pm/pm.h
hle/service/prepo/prepo.cpp
hle/service/prepo/prepo.h
hle/service/psc/psc.cpp
hle/service/psc/psc.h
hle/service/service.cpp
hle/service/service.h
hle/service/set/set.cpp
@@ -289,6 +313,8 @@ add_library(core STATIC
hle/service/time/interface.h
hle/service/time/time.cpp
hle/service/time/time.h
hle/service/usb/usb.cpp
hle/service/usb/usb.h
hle/service/vi/vi.cpp
hle/service/vi/vi.h
hle/service/vi/vi_m.cpp
@@ -299,10 +325,6 @@ add_library(core STATIC
hle/service/vi/vi_u.h
hle/service/wlan/wlan.cpp
hle/service/wlan/wlan.h
hw/hw.cpp
hw/hw.h
hw/lcd.cpp
hw/lcd.h
loader/deconstructed_rom_directory.cpp
loader/deconstructed_rom_directory.h
loader/elf.cpp
@@ -317,6 +339,8 @@ add_library(core STATIC
loader/nro.h
loader/nso.cpp
loader/nso.h
loader/xci.cpp
loader/xci.h
memory.cpp
memory.h
memory_hook.cpp
@@ -336,7 +360,7 @@ add_library(core STATIC
create_target_directory_groups(core)
target_link_libraries(core PUBLIC common PRIVATE audio_core video_core)
target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt lz4_static opus unicorn)
target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt lz4_static mbedtls opus unicorn)
if (ARCHITECTURE_x86_64)
target_sources(core PRIVATE

View File

@@ -203,7 +203,7 @@ void ARM_Unicorn::ExecuteInstructions(int num_instructions) {
}
Kernel::Thread* thread = Kernel::GetCurrentThread();
SaveContext(thread->context);
if (last_bkpt_hit || (num_instructions == 1)) {
if (last_bkpt_hit || GDBStub::GetCpuStepFlag()) {
last_bkpt_hit = false;
GDBStub::Break();
GDBStub::SendTrap(thread, 5);

View File

@@ -15,11 +15,10 @@
#include "core/hle/service/service.h"
#include "core/hle/service/sm/controller.h"
#include "core/hle/service/sm/sm.h"
#include "core/hw/hw.h"
#include "core/loader/loader.h"
#include "core/memory_setup.h"
#include "core/settings.h"
#include "file_sys/vfs_real.h"
#include "video_core/renderer_base.h"
#include "video_core/video_core.h"
namespace Core {
@@ -63,7 +62,6 @@ System::ResultStatus System::RunLoop(bool tight_loop) {
// execute. Otherwise, get out of the loop function.
if (GDBStub::GetCpuHaltFlag()) {
if (GDBStub::GetCpuStepFlag()) {
GDBStub::SetCpuStepFlag(false);
tight_loop = false;
} else {
return ResultStatus::Success;
@@ -79,6 +77,10 @@ System::ResultStatus System::RunLoop(bool tight_loop) {
}
}
if (GDBStub::IsServerEnabled()) {
GDBStub::SetCpuStepFlag(false);
}
return status;
}
@@ -86,8 +88,8 @@ System::ResultStatus System::SingleStep() {
return RunLoop(false);
}
System::ResultStatus System::Load(EmuWindow* emu_window, const std::string& filepath) {
app_loader = Loader::GetLoader(std::make_shared<FileSys::RealVfsFile>(filepath));
System::ResultStatus System::Load(EmuWindow& emu_window, const std::string& filepath) {
app_loader = Loader::GetLoader(virtual_filesystem->OpenFile(filepath, FileSys::Mode::Read));
if (!app_loader) {
LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath);
@@ -101,8 +103,10 @@ System::ResultStatus System::Load(EmuWindow* emu_window, const std::string& file
static_cast<int>(system_mode.second));
switch (system_mode.second) {
case Loader::ResultStatus::ErrorEncrypted:
return ResultStatus::ErrorLoader_ErrorEncrypted;
case Loader::ResultStatus::ErrorMissingKeys:
return ResultStatus::ErrorLoader_ErrorMissingKeys;
case Loader::ResultStatus::ErrorDecrypting:
return ResultStatus::ErrorLoader_ErrorDecrypting;
case Loader::ResultStatus::ErrorInvalidFormat:
return ResultStatus::ErrorLoader_ErrorInvalidFormat;
case Loader::ResultStatus::ErrorUnsupportedArch:
@@ -112,7 +116,7 @@ System::ResultStatus System::Load(EmuWindow* emu_window, const std::string& file
}
}
ResultStatus init_result{Init(emu_window, system_mode.first.get())};
ResultStatus init_result{Init(emu_window)};
if (init_result != ResultStatus::Success) {
LOG_CRITICAL(Core, "Failed to initialize system (Error {})!",
static_cast<int>(init_result));
@@ -126,8 +130,10 @@ System::ResultStatus System::Load(EmuWindow* emu_window, const std::string& file
System::Shutdown();
switch (load_result) {
case Loader::ResultStatus::ErrorEncrypted:
return ResultStatus::ErrorLoader_ErrorEncrypted;
case Loader::ResultStatus::ErrorMissingKeys:
return ResultStatus::ErrorLoader_ErrorMissingKeys;
case Loader::ResultStatus::ErrorDecrypting:
return ResultStatus::ErrorLoader_ErrorDecrypting;
case Loader::ResultStatus::ErrorInvalidFormat:
return ResultStatus::ErrorLoader_ErrorInvalidFormat;
case Loader::ResultStatus::ErrorUnsupportedArch:
@@ -163,11 +169,15 @@ Cpu& System::CpuCore(size_t core_index) {
return *cpu_cores[core_index];
}
System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) {
System::ResultStatus System::Init(EmuWindow& emu_window) {
LOG_DEBUG(HW_Memory, "initialized OK");
CoreTiming::Init();
// Create a default fs if one doesn't already exist.
if (virtual_filesystem == nullptr)
virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>();
current_process = Kernel::Process::Create("main");
cpu_barrier = std::make_shared<CpuBarrier>();
@@ -176,19 +186,20 @@ System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) {
cpu_cores[index] = std::make_shared<Cpu>(cpu_exclusive_monitor, cpu_barrier, index);
}
gpu_core = std::make_unique<Tegra::GPU>();
telemetry_session = std::make_unique<Core::TelemetrySession>();
service_manager = std::make_shared<Service::SM::ServiceManager>();
HW::Init();
Kernel::Init(system_mode);
Service::Init(service_manager);
Kernel::Init();
Service::Init(service_manager, virtual_filesystem);
GDBStub::Init();
if (!VideoCore::Init(emu_window)) {
renderer = VideoCore::CreateRenderer(emu_window);
if (!renderer->Init()) {
return ResultStatus::ErrorVideoCore;
}
gpu_core = std::make_unique<Tegra::GPU>(renderer->Rasterizer());
// Create threads for CPU cores 1-3, and build thread_to_cpu map
// CPU core 0 is run on the main thread
thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0];
@@ -220,11 +231,10 @@ void System::Shutdown() {
perf_results.frametime * 1000.0);
// Shutdown emulation session
VideoCore::Shutdown();
renderer.reset();
GDBStub::Shutdown();
Service::Shutdown();
Kernel::Shutdown();
HW::Shutdown();
service_manager.reset();
telemetry_session.reset();
gpu_core.reset();

View File

@@ -11,12 +11,14 @@
#include "common/common_types.h"
#include "core/arm/exclusive_monitor.h"
#include "core/core_cpu.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/scheduler.h"
#include "core/loader/loader.h"
#include "core/memory.h"
#include "core/perf_stats.h"
#include "core/telemetry_session.h"
#include "file_sys/vfs_real.h"
#include "hle/service/filesystem/filesystem.h"
#include "video_core/debug_utils/debug_utils.h"
#include "video_core/gpu.h"
@@ -27,6 +29,10 @@ namespace Service::SM {
class ServiceManager;
}
namespace VideoCore {
class RendererBase;
}
namespace Core {
class System {
@@ -43,12 +49,14 @@ public:
/// Enumeration representing the return values of the System Initialize and Load process.
enum class ResultStatus : u32 {
Success, ///< Succeeded
ErrorNotInitialized, ///< Error trying to use core prior to initialization
ErrorGetLoader, ///< Error finding the correct application loader
ErrorSystemMode, ///< Error determining the system mode
ErrorLoader, ///< Error loading the specified application
ErrorLoader_ErrorEncrypted, ///< Error loading the specified application due to encryption
Success, ///< Succeeded
ErrorNotInitialized, ///< Error trying to use core prior to initialization
ErrorGetLoader, ///< Error finding the correct application loader
ErrorSystemMode, ///< Error determining the system mode
ErrorLoader, ///< Error loading the specified application
ErrorLoader_ErrorMissingKeys, ///< Error because the key/keys needed to run could not be
///< found.
ErrorLoader_ErrorDecrypting, ///< Error loading the specified application due to encryption
ErrorLoader_ErrorInvalidFormat, ///< Error loading the specified application due to an
/// invalid format
ErrorSystemFiles, ///< Error in finding system files
@@ -76,16 +84,28 @@ public:
*/
ResultStatus SingleStep();
/**
* Invalidate the CPU instruction caches
* This function should only be used by GDB Stub to support breakpoints, memory updates and
* step/continue commands.
*/
void InvalidateCpuInstructionCaches() {
for (auto& cpu : cpu_cores) {
cpu->ArmInterface().ClearInstructionCache();
}
}
/// Shutdown the emulated system.
void Shutdown();
/**
* Load an executable application.
* @param emu_window Pointer to the host-system window used for video output and keyboard input.
* @param emu_window Reference to the host-system window used for video output and keyboard
* input.
* @param filepath String path to the executable application to load on the host file system.
* @returns ResultStatus code, indicating if the operation succeeded.
*/
ResultStatus Load(EmuWindow* emu_window, const std::string& filepath);
ResultStatus Load(EmuWindow& emu_window, const std::string& filepath);
/**
* Indicates if the emulated system is powered on (all subsystems initialized and able to run an
@@ -126,11 +146,26 @@ public:
/// Gets a CPU interface to the CPU core with the specified index
Cpu& CpuCore(size_t core_index);
/// Gets the GPU interface
/// Gets a mutable reference to the GPU interface
Tegra::GPU& GPU() {
return *gpu_core;
}
/// Gets an immutable reference to the GPU interface.
const Tegra::GPU& GPU() const {
return *gpu_core;
}
/// Gets a mutable reference to the renderer.
VideoCore::RendererBase& Renderer() {
return *renderer;
}
/// Gets an immutable reference to the renderer.
const VideoCore::RendererBase& Renderer() const {
return *renderer;
}
/// Gets the scheduler for the CPU core that is currently running
Kernel::Scheduler& CurrentScheduler() {
return *CurrentCpuCore().Scheduler();
@@ -178,6 +213,14 @@ public:
return debug_context;
}
void SetFilesystem(FileSys::VirtualFilesystem vfs) {
virtual_filesystem = std::move(vfs);
}
FileSys::VirtualFilesystem GetFilesystem() const {
return virtual_filesystem;
}
private:
System();
@@ -186,14 +229,17 @@ private:
/**
* Initialize the emulated system.
* @param emu_window Pointer to the host-system window used for video output and keyboard input.
* @param system_mode The system mode.
* @param emu_window Reference to the host-system window used for video output and keyboard
* input.
* @return ResultStatus code, indicating if the operation succeeded.
*/
ResultStatus Init(EmuWindow* emu_window, u32 system_mode);
ResultStatus Init(EmuWindow& emu_window);
/// RealVfsFilesystem instance
FileSys::VirtualFilesystem virtual_filesystem;
/// AppLoader used to load the current executing application
std::unique_ptr<Loader::AppLoader> app_loader;
std::unique_ptr<VideoCore::RendererBase> renderer;
std::unique_ptr<Tegra::GPU> gpu_core;
std::shared_ptr<Tegra::DebugContext> debug_context;
Kernel::SharedPtr<Kernel::Process> current_process;

View File

@@ -12,7 +12,6 @@
#include "core/arm/unicorn/arm_unicorn.h"
#include "core/core_cpu.h"
#include "core/core_timing.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/scheduler.h"
#include "core/hle/kernel/thread.h"
#include "core/settings.h"

View File

@@ -141,7 +141,7 @@ void ScheduleEvent(s64 cycles_into_future, const EventType* event_type, u64 user
ForceExceptionCheck(cycles_into_future);
event_queue.emplace_back(Event{timeout, event_fifo_id++, userdata, event_type});
std::push_heap(event_queue.begin(), event_queue.end(), std::greater<Event>());
std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>());
}
void ScheduleEventThreadsafe(s64 cycles_into_future, const EventType* event_type, u64 userdata) {
@@ -156,7 +156,7 @@ void UnscheduleEvent(const EventType* event_type, u64 userdata) {
// Removing random items breaks the invariant so we have to re-establish it.
if (itr != event_queue.end()) {
event_queue.erase(itr, event_queue.end());
std::make_heap(event_queue.begin(), event_queue.end(), std::greater<Event>());
std::make_heap(event_queue.begin(), event_queue.end(), std::greater<>());
}
}
@@ -167,7 +167,7 @@ void RemoveEvent(const EventType* event_type) {
// Removing random items breaks the invariant so we have to re-establish it.
if (itr != event_queue.end()) {
event_queue.erase(itr, event_queue.end());
std::make_heap(event_queue.begin(), event_queue.end(), std::greater<Event>());
std::make_heap(event_queue.begin(), event_queue.end(), std::greater<>());
}
}
@@ -190,7 +190,7 @@ void MoveEvents() {
for (Event ev; ts_queue.Pop(ev);) {
ev.fifo_order = event_fifo_id++;
event_queue.emplace_back(std::move(ev));
std::push_heap(event_queue.begin(), event_queue.end(), std::greater<Event>());
std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>());
}
}
@@ -205,7 +205,7 @@ void Advance() {
while (!event_queue.empty() && event_queue.front().time <= global_timer) {
Event evt = std::move(event_queue.front());
std::pop_heap(event_queue.begin(), event_queue.end(), std::greater<Event>());
std::pop_heap(event_queue.begin(), event_queue.end(), std::greater<>());
event_queue.pop_back();
evt.type->callback(evt.userdata, static_cast<int>(global_timer - evt.time));
}
@@ -226,8 +226,8 @@ void Idle() {
downcount = 0;
}
u64 GetGlobalTimeUs() {
return GetTicks() * 1000000 / BASE_CLOCK_RATE;
std::chrono::microseconds GetGlobalTimeUs() {
return std::chrono::microseconds{GetTicks() * 1000000 / BASE_CLOCK_RATE};
}
int GetDowncount() {

View File

@@ -17,12 +17,17 @@
* ScheduleEvent(periodInCycles - cyclesLate, callback, "whatever")
*/
#include <chrono>
#include <functional>
#include <string>
#include "common/common_types.h"
namespace CoreTiming {
struct EventType;
using TimedCallback = std::function<void(u64 userdata, int cycles_late)>;
/**
* CoreTiming begins at the boundary of timing slice -1. An initial call to Advance() is
* required to end slice -1 and start slice 0 before the first cycle of code is executed.
@@ -30,8 +35,6 @@ namespace CoreTiming {
void Init();
void Shutdown();
typedef std::function<void(u64 userdata, int cycles_late)> TimedCallback;
/**
* This should only be called from the emu thread, if you are calling it any other thread, you are
* doing something evil
@@ -40,8 +43,6 @@ u64 GetTicks();
u64 GetIdleTicks();
void AddTicks(u64 ticks);
struct EventType;
/**
* Returns the event_type identifier. if name is not unique, it will assert.
*/
@@ -86,7 +87,7 @@ void ClearPendingEvents();
void ForceExceptionCheck(s64 cycles);
u64 GetGlobalTimeUs();
std::chrono::microseconds GetGlobalTimeUs();
int GetDowncount();

View File

@@ -0,0 +1,115 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <mbedtls/cipher.h>
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/crypto/aes_util.h"
#include "core/crypto/key_manager.h"
namespace Core::Crypto {
namespace {
std::vector<u8> CalculateNintendoTweak(size_t sector_id) {
std::vector<u8> out(0x10);
for (size_t i = 0xF; i <= 0xF; --i) {
out[i] = sector_id & 0xFF;
sector_id >>= 8;
}
return out;
}
} // Anonymous namespace
static_assert(static_cast<size_t>(Mode::CTR) == static_cast<size_t>(MBEDTLS_CIPHER_AES_128_CTR),
"CTR has incorrect value.");
static_assert(static_cast<size_t>(Mode::ECB) == static_cast<size_t>(MBEDTLS_CIPHER_AES_128_ECB),
"ECB has incorrect value.");
static_assert(static_cast<size_t>(Mode::XTS) == static_cast<size_t>(MBEDTLS_CIPHER_AES_128_XTS),
"XTS has incorrect value.");
// Structure to hide mbedtls types from header file
struct CipherContext {
mbedtls_cipher_context_t encryption_context;
mbedtls_cipher_context_t decryption_context;
};
template <typename Key, size_t KeySize>
Crypto::AESCipher<Key, KeySize>::AESCipher(Key key, Mode mode)
: ctx(std::make_unique<CipherContext>()) {
mbedtls_cipher_init(&ctx->encryption_context);
mbedtls_cipher_init(&ctx->decryption_context);
ASSERT_MSG((mbedtls_cipher_setup(
&ctx->encryption_context,
mbedtls_cipher_info_from_type(static_cast<mbedtls_cipher_type_t>(mode))) ||
mbedtls_cipher_setup(
&ctx->decryption_context,
mbedtls_cipher_info_from_type(static_cast<mbedtls_cipher_type_t>(mode)))) == 0,
"Failed to initialize mbedtls ciphers.");
ASSERT(
!mbedtls_cipher_setkey(&ctx->encryption_context, key.data(), KeySize * 8, MBEDTLS_ENCRYPT));
ASSERT(
!mbedtls_cipher_setkey(&ctx->decryption_context, key.data(), KeySize * 8, MBEDTLS_DECRYPT));
//"Failed to set key on mbedtls ciphers.");
}
template <typename Key, size_t KeySize>
AESCipher<Key, KeySize>::~AESCipher() {
mbedtls_cipher_free(&ctx->encryption_context);
mbedtls_cipher_free(&ctx->decryption_context);
}
template <typename Key, size_t KeySize>
void AESCipher<Key, KeySize>::SetIV(std::vector<u8> iv) {
ASSERT_MSG((mbedtls_cipher_set_iv(&ctx->encryption_context, iv.data(), iv.size()) ||
mbedtls_cipher_set_iv(&ctx->decryption_context, iv.data(), iv.size())) == 0,
"Failed to set IV on mbedtls ciphers.");
}
template <typename Key, size_t KeySize>
void AESCipher<Key, KeySize>::Transcode(const u8* src, size_t size, u8* dest, Op op) const {
auto* const context = op == Op::Encrypt ? &ctx->encryption_context : &ctx->decryption_context;
mbedtls_cipher_reset(context);
size_t written = 0;
if (mbedtls_cipher_get_cipher_mode(context) == MBEDTLS_MODE_XTS) {
mbedtls_cipher_update(context, src, size, dest, &written);
if (written != size) {
LOG_WARNING(Crypto, "Not all data was decrypted requested={:016X}, actual={:016X}.",
size, written);
}
} else {
const auto block_size = mbedtls_cipher_get_block_size(context);
for (size_t offset = 0; offset < size; offset += block_size) {
auto length = std::min<size_t>(block_size, size - offset);
mbedtls_cipher_update(context, src + offset, length, dest + offset, &written);
if (written != length) {
LOG_WARNING(Crypto, "Not all data was decrypted requested={:016X}, actual={:016X}.",
length, written);
}
}
}
mbedtls_cipher_finish(context, nullptr, nullptr);
}
template <typename Key, size_t KeySize>
void AESCipher<Key, KeySize>::XTSTranscode(const u8* src, size_t size, u8* dest, size_t sector_id,
size_t sector_size, Op op) {
if (size % sector_size > 0) {
LOG_CRITICAL(Crypto, "Data size must be a multiple of sector size.");
return;
}
for (size_t i = 0; i < size; i += sector_size) {
SetIV(CalculateNintendoTweak(sector_id++));
Transcode<u8, u8>(src + i, sector_size, dest + i, op);
}
}
template class AESCipher<Key128>;
template class AESCipher<Key256>;
} // namespace Core::Crypto

View File

@@ -0,0 +1,64 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <memory>
#include <type_traits>
#include <vector>
#include "common/common_types.h"
#include "core/file_sys/vfs.h"
namespace Core::Crypto {
struct CipherContext;
enum class Mode {
CTR = 11,
ECB = 2,
XTS = 70,
};
enum class Op {
Encrypt,
Decrypt,
};
template <typename Key, size_t KeySize = sizeof(Key)>
class AESCipher {
static_assert(std::is_same_v<Key, std::array<u8, KeySize>>, "Key must be std::array of u8.");
static_assert(KeySize == 0x10 || KeySize == 0x20, "KeySize must be 128 or 256.");
public:
AESCipher(Key key, Mode mode);
~AESCipher();
void SetIV(std::vector<u8> iv);
template <typename Source, typename Dest>
void Transcode(const Source* src, size_t size, Dest* dest, Op op) const {
static_assert(std::is_trivially_copyable_v<Source> && std::is_trivially_copyable_v<Dest>,
"Transcode source and destination types must be trivially copyable.");
Transcode(reinterpret_cast<const u8*>(src), size, reinterpret_cast<u8*>(dest), op);
}
void Transcode(const u8* src, size_t size, u8* dest, Op op) const;
template <typename Source, typename Dest>
void XTSTranscode(const Source* src, size_t size, Dest* dest, size_t sector_id,
size_t sector_size, Op op) {
static_assert(std::is_trivially_copyable_v<Source> && std::is_trivially_copyable_v<Dest>,
"XTSTranscode source and destination types must be trivially copyable.");
XTSTranscode(reinterpret_cast<const u8*>(src), size, reinterpret_cast<u8*>(dest), sector_id,
sector_size, op);
}
void XTSTranscode(const u8* src, size_t size, u8* dest, size_t sector_id, size_t sector_size,
Op op);
private:
std::unique_ptr<CipherContext> ctx;
};
} // namespace Core::Crypto

View File

@@ -0,0 +1,56 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <cstring>
#include "common/assert.h"
#include "core/crypto/ctr_encryption_layer.h"
namespace Core::Crypto {
CTREncryptionLayer::CTREncryptionLayer(FileSys::VirtualFile base_, Key128 key_, size_t base_offset)
: EncryptionLayer(std::move(base_)), base_offset(base_offset), cipher(key_, Mode::CTR),
iv(16, 0) {}
size_t CTREncryptionLayer::Read(u8* data, size_t length, size_t offset) const {
if (length == 0)
return 0;
const auto sector_offset = offset & 0xF;
if (sector_offset == 0) {
UpdateIV(base_offset + offset);
std::vector<u8> raw = base->ReadBytes(length, offset);
if (raw.size() != length)
return Read(data, raw.size(), offset);
cipher.Transcode(raw.data(), length, data, Op::Decrypt);
return length;
}
// offset does not fall on block boundary (0x10)
std::vector<u8> block = base->ReadBytes(0x10, offset - sector_offset);
UpdateIV(base_offset + offset - sector_offset);
cipher.Transcode(block.data(), block.size(), block.data(), Op::Decrypt);
size_t read = 0x10 - sector_offset;
if (length + sector_offset < 0x10) {
std::memcpy(data, block.data() + sector_offset, std::min<u64>(length, read));
return read;
}
std::memcpy(data, block.data() + sector_offset, read);
return read + Read(data + read, length - read, offset + read);
}
void CTREncryptionLayer::SetIV(const std::vector<u8>& iv_) {
const auto length = std::min(iv_.size(), iv.size());
iv.assign(iv_.cbegin(), iv_.cbegin() + length);
}
void CTREncryptionLayer::UpdateIV(size_t offset) const {
offset >>= 4;
for (size_t i = 0; i < 8; ++i) {
iv[16 - i - 1] = offset & 0xFF;
offset >>= 8;
}
cipher.SetIV(iv);
}
} // namespace Core::Crypto

View File

@@ -0,0 +1,33 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <vector>
#include "core/crypto/aes_util.h"
#include "core/crypto/encryption_layer.h"
#include "core/crypto/key_manager.h"
namespace Core::Crypto {
// Sits on top of a VirtualFile and provides CTR-mode AES decription.
class CTREncryptionLayer : public EncryptionLayer {
public:
CTREncryptionLayer(FileSys::VirtualFile base, Key128 key, size_t base_offset);
size_t Read(u8* data, size_t length, size_t offset) const override;
void SetIV(const std::vector<u8>& iv);
private:
size_t base_offset;
// Must be mutable as operations modify cipher contexts.
mutable AESCipher<Key128> cipher;
mutable std::vector<u8> iv;
void UpdateIV(size_t offset) const;
};
} // namespace Core::Crypto

View File

@@ -0,0 +1,42 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/crypto/encryption_layer.h"
namespace Core::Crypto {
EncryptionLayer::EncryptionLayer(FileSys::VirtualFile base_) : base(std::move(base_)) {}
std::string EncryptionLayer::GetName() const {
return base->GetName();
}
size_t EncryptionLayer::GetSize() const {
return base->GetSize();
}
bool EncryptionLayer::Resize(size_t new_size) {
return false;
}
std::shared_ptr<FileSys::VfsDirectory> EncryptionLayer::GetContainingDirectory() const {
return base->GetContainingDirectory();
}
bool EncryptionLayer::IsWritable() const {
return false;
}
bool EncryptionLayer::IsReadable() const {
return true;
}
size_t EncryptionLayer::Write(const u8* data, size_t length, size_t offset) {
return 0;
}
bool EncryptionLayer::Rename(std::string_view name) {
return base->Rename(name);
}
} // namespace Core::Crypto

View File

@@ -0,0 +1,33 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "common/common_types.h"
#include "core/file_sys/vfs.h"
namespace Core::Crypto {
// Basically non-functional class that implements all of the methods that are irrelevant to an
// EncryptionLayer. Reduces duplicate code.
class EncryptionLayer : public FileSys::VfsFile {
public:
explicit EncryptionLayer(FileSys::VirtualFile base);
size_t Read(u8* data, size_t length, size_t offset) const override = 0;
std::string GetName() const override;
size_t GetSize() const override;
bool Resize(size_t new_size) override;
std::shared_ptr<FileSys::VfsDirectory> GetContainingDirectory() const override;
bool IsWritable() const override;
bool IsReadable() const override;
size_t Write(const u8* data, size_t length, size_t offset) override;
bool Rename(std::string_view name) override;
protected:
FileSys::VirtualFile base;
};
} // namespace Core::Crypto

View File

@@ -0,0 +1,208 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <array>
#include <fstream>
#include <locale>
#include <sstream>
#include <string_view>
#include "common/common_paths.h"
#include "common/file_util.h"
#include "core/crypto/key_manager.h"
#include "core/settings.h"
namespace Core::Crypto {
static u8 ToHexNibble(char c1) {
if (c1 >= 65 && c1 <= 70)
return c1 - 55;
if (c1 >= 97 && c1 <= 102)
return c1 - 87;
if (c1 >= 48 && c1 <= 57)
return c1 - 48;
throw std::logic_error("Invalid hex digit");
}
template <size_t Size>
static std::array<u8, Size> HexStringToArray(std::string_view str) {
std::array<u8, Size> out{};
for (size_t i = 0; i < 2 * Size; i += 2) {
auto d1 = str[i];
auto d2 = str[i + 1];
out[i / 2] = (ToHexNibble(d1) << 4) | ToHexNibble(d2);
}
return out;
}
std::array<u8, 16> operator""_array16(const char* str, size_t len) {
if (len != 32)
throw std::logic_error("Not of correct size.");
return HexStringToArray<16>(str);
}
std::array<u8, 32> operator""_array32(const char* str, size_t len) {
if (len != 64)
throw std::logic_error("Not of correct size.");
return HexStringToArray<32>(str);
}
KeyManager::KeyManager() {
// Initialize keys
const std::string hactool_keys_dir = FileUtil::GetHactoolConfigurationPath();
const std::string yuzu_keys_dir = FileUtil::GetUserPath(FileUtil::UserPath::KeysDir);
if (Settings::values.use_dev_keys) {
dev_mode = true;
AttemptLoadKeyFile(yuzu_keys_dir, hactool_keys_dir, "dev.keys", false);
} else {
dev_mode = false;
AttemptLoadKeyFile(yuzu_keys_dir, hactool_keys_dir, "prod.keys", false);
}
AttemptLoadKeyFile(yuzu_keys_dir, hactool_keys_dir, "title.keys", true);
}
void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) {
std::ifstream file(filename);
if (!file.is_open())
return;
std::string line;
while (std::getline(file, line)) {
std::vector<std::string> out;
std::stringstream stream(line);
std::string item;
while (std::getline(stream, item, '='))
out.push_back(std::move(item));
if (out.size() != 2)
continue;
out[0].erase(std::remove(out[0].begin(), out[0].end(), ' '), out[0].end());
out[1].erase(std::remove(out[1].begin(), out[1].end(), ' '), out[1].end());
if (is_title_keys) {
auto rights_id_raw = HexStringToArray<16>(out[0]);
u128 rights_id{};
std::memcpy(rights_id.data(), rights_id_raw.data(), rights_id_raw.size());
Key128 key = HexStringToArray<16>(out[1]);
SetKey(S128KeyType::Titlekey, key, rights_id[1], rights_id[0]);
} else {
std::transform(out[0].begin(), out[0].end(), out[0].begin(), ::tolower);
if (s128_file_id.find(out[0]) != s128_file_id.end()) {
const auto index = s128_file_id.at(out[0]);
Key128 key = HexStringToArray<16>(out[1]);
SetKey(index.type, key, index.field1, index.field2);
} else if (s256_file_id.find(out[0]) != s256_file_id.end()) {
const auto index = s256_file_id.at(out[0]);
Key256 key = HexStringToArray<32>(out[1]);
SetKey(index.type, key, index.field1, index.field2);
}
}
}
}
void KeyManager::AttemptLoadKeyFile(const std::string& dir1, const std::string& dir2,
const std::string& filename, bool title) {
if (FileUtil::Exists(dir1 + DIR_SEP + filename))
LoadFromFile(dir1 + DIR_SEP + filename, title);
else if (FileUtil::Exists(dir2 + DIR_SEP + filename))
LoadFromFile(dir2 + DIR_SEP + filename, title);
}
bool KeyManager::HasKey(S128KeyType id, u64 field1, u64 field2) const {
return s128_keys.find({id, field1, field2}) != s128_keys.end();
}
bool KeyManager::HasKey(S256KeyType id, u64 field1, u64 field2) const {
return s256_keys.find({id, field1, field2}) != s256_keys.end();
}
Key128 KeyManager::GetKey(S128KeyType id, u64 field1, u64 field2) const {
if (!HasKey(id, field1, field2))
return {};
return s128_keys.at({id, field1, field2});
}
Key256 KeyManager::GetKey(S256KeyType id, u64 field1, u64 field2) const {
if (!HasKey(id, field1, field2))
return {};
return s256_keys.at({id, field1, field2});
}
void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) {
s128_keys[{id, field1, field2}] = key;
}
void KeyManager::SetKey(S256KeyType id, Key256 key, u64 field1, u64 field2) {
s256_keys[{id, field1, field2}] = key;
}
bool KeyManager::KeyFileExists(bool title) {
const std::string hactool_keys_dir = FileUtil::GetHactoolConfigurationPath();
const std::string yuzu_keys_dir = FileUtil::GetUserPath(FileUtil::UserPath::KeysDir);
if (title) {
return FileUtil::Exists(hactool_keys_dir + DIR_SEP + "title.keys") ||
FileUtil::Exists(yuzu_keys_dir + DIR_SEP + "title.keys");
}
if (Settings::values.use_dev_keys) {
return FileUtil::Exists(hactool_keys_dir + DIR_SEP + "dev.keys") ||
FileUtil::Exists(yuzu_keys_dir + DIR_SEP + "dev.keys");
}
return FileUtil::Exists(hactool_keys_dir + DIR_SEP + "prod.keys") ||
FileUtil::Exists(yuzu_keys_dir + DIR_SEP + "prod.keys");
}
const std::unordered_map<std::string, KeyIndex<S128KeyType>> KeyManager::s128_file_id = {
{"master_key_00", {S128KeyType::Master, 0, 0}},
{"master_key_01", {S128KeyType::Master, 1, 0}},
{"master_key_02", {S128KeyType::Master, 2, 0}},
{"master_key_03", {S128KeyType::Master, 3, 0}},
{"master_key_04", {S128KeyType::Master, 4, 0}},
{"package1_key_00", {S128KeyType::Package1, 0, 0}},
{"package1_key_01", {S128KeyType::Package1, 1, 0}},
{"package1_key_02", {S128KeyType::Package1, 2, 0}},
{"package1_key_03", {S128KeyType::Package1, 3, 0}},
{"package1_key_04", {S128KeyType::Package1, 4, 0}},
{"package2_key_00", {S128KeyType::Package2, 0, 0}},
{"package2_key_01", {S128KeyType::Package2, 1, 0}},
{"package2_key_02", {S128KeyType::Package2, 2, 0}},
{"package2_key_03", {S128KeyType::Package2, 3, 0}},
{"package2_key_04", {S128KeyType::Package2, 4, 0}},
{"titlekek_00", {S128KeyType::Titlekek, 0, 0}},
{"titlekek_01", {S128KeyType::Titlekek, 1, 0}},
{"titlekek_02", {S128KeyType::Titlekek, 2, 0}},
{"titlekek_03", {S128KeyType::Titlekek, 3, 0}},
{"titlekek_04", {S128KeyType::Titlekek, 4, 0}},
{"eticket_rsa_kek", {S128KeyType::ETicketRSAKek, 0, 0}},
{"key_area_key_application_00",
{S128KeyType::KeyArea, 0, static_cast<u64>(KeyAreaKeyType::Application)}},
{"key_area_key_application_01",
{S128KeyType::KeyArea, 1, static_cast<u64>(KeyAreaKeyType::Application)}},
{"key_area_key_application_02",
{S128KeyType::KeyArea, 2, static_cast<u64>(KeyAreaKeyType::Application)}},
{"key_area_key_application_03",
{S128KeyType::KeyArea, 3, static_cast<u64>(KeyAreaKeyType::Application)}},
{"key_area_key_application_04",
{S128KeyType::KeyArea, 4, static_cast<u64>(KeyAreaKeyType::Application)}},
{"key_area_key_ocean_00", {S128KeyType::KeyArea, 0, static_cast<u64>(KeyAreaKeyType::Ocean)}},
{"key_area_key_ocean_01", {S128KeyType::KeyArea, 1, static_cast<u64>(KeyAreaKeyType::Ocean)}},
{"key_area_key_ocean_02", {S128KeyType::KeyArea, 2, static_cast<u64>(KeyAreaKeyType::Ocean)}},
{"key_area_key_ocean_03", {S128KeyType::KeyArea, 3, static_cast<u64>(KeyAreaKeyType::Ocean)}},
{"key_area_key_ocean_04", {S128KeyType::KeyArea, 4, static_cast<u64>(KeyAreaKeyType::Ocean)}},
{"key_area_key_system_00", {S128KeyType::KeyArea, 0, static_cast<u64>(KeyAreaKeyType::System)}},
{"key_area_key_system_01", {S128KeyType::KeyArea, 1, static_cast<u64>(KeyAreaKeyType::System)}},
{"key_area_key_system_02", {S128KeyType::KeyArea, 2, static_cast<u64>(KeyAreaKeyType::System)}},
{"key_area_key_system_03", {S128KeyType::KeyArea, 3, static_cast<u64>(KeyAreaKeyType::System)}},
{"key_area_key_system_04", {S128KeyType::KeyArea, 4, static_cast<u64>(KeyAreaKeyType::System)}},
};
const std::unordered_map<std::string, KeyIndex<S256KeyType>> KeyManager::s256_file_id = {
{"header_key", {S256KeyType::Header, 0, 0}},
{"sd_card_save_key", {S256KeyType::SDSave, 0, 0}},
{"sd_card_nca_key", {S256KeyType::SDNCA, 0, 0}},
};
} // namespace Core::Crypto

View File

@@ -0,0 +1,120 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include <string>
#include <type_traits>
#include <unordered_map>
#include <vector>
#include <fmt/format.h>
#include "common/common_types.h"
namespace Core::Crypto {
using Key128 = std::array<u8, 0x10>;
using Key256 = std::array<u8, 0x20>;
using SHA256Hash = std::array<u8, 0x20>;
static_assert(sizeof(Key128) == 16, "Key128 must be 128 bytes big.");
static_assert(sizeof(Key256) == 32, "Key128 must be 128 bytes big.");
enum class S256KeyType : u64 {
Header, //
SDSave, //
SDNCA, //
};
enum class S128KeyType : u64 {
Master, // f1=crypto revision
Package1, // f1=crypto revision
Package2, // f1=crypto revision
Titlekek, // f1=crypto revision
ETicketRSAKek, //
KeyArea, // f1=crypto revision f2=type {app, ocean, system}
SDSeed, //
Titlekey, // f1=rights id LSB f2=rights id MSB
};
enum class KeyAreaKeyType : u8 {
Application,
Ocean,
System,
};
template <typename KeyType>
struct KeyIndex {
KeyType type;
u64 field1;
u64 field2;
std::string DebugInfo() const {
u8 key_size = 16;
if constexpr (std::is_same_v<KeyType, S256KeyType>)
key_size = 32;
return fmt::format("key_size={:02X}, key={:02X}, field1={:016X}, field2={:016X}", key_size,
static_cast<u8>(type), field1, field2);
}
};
// The following two (== and hash) are so KeyIndex can be a key in unordered_map
template <typename KeyType>
bool operator==(const KeyIndex<KeyType>& lhs, const KeyIndex<KeyType>& rhs) {
return std::tie(lhs.type, lhs.field1, lhs.field2) == std::tie(rhs.type, rhs.field1, rhs.field2);
}
template <typename KeyType>
bool operator!=(const KeyIndex<KeyType>& lhs, const KeyIndex<KeyType>& rhs) {
return !operator==(lhs, rhs);
}
} // namespace Core::Crypto
namespace std {
template <typename KeyType>
struct hash<Core::Crypto::KeyIndex<KeyType>> {
size_t operator()(const Core::Crypto::KeyIndex<KeyType>& k) const {
using std::hash;
return ((hash<u64>()(static_cast<u64>(k.type)) ^ (hash<u64>()(k.field1) << 1)) >> 1) ^
(hash<u64>()(k.field2) << 1);
}
};
} // namespace std
namespace Core::Crypto {
std::array<u8, 0x10> operator"" _array16(const char* str, size_t len);
std::array<u8, 0x20> operator"" _array32(const char* str, size_t len);
class KeyManager {
public:
KeyManager();
bool HasKey(S128KeyType id, u64 field1 = 0, u64 field2 = 0) const;
bool HasKey(S256KeyType id, u64 field1 = 0, u64 field2 = 0) const;
Key128 GetKey(S128KeyType id, u64 field1 = 0, u64 field2 = 0) const;
Key256 GetKey(S256KeyType id, u64 field1 = 0, u64 field2 = 0) const;
void SetKey(S128KeyType id, Key128 key, u64 field1 = 0, u64 field2 = 0);
void SetKey(S256KeyType id, Key256 key, u64 field1 = 0, u64 field2 = 0);
static bool KeyFileExists(bool title);
private:
std::unordered_map<KeyIndex<S128KeyType>, Key128> s128_keys;
std::unordered_map<KeyIndex<S256KeyType>, Key256> s256_keys;
bool dev_mode;
void LoadFromFile(const std::string& filename, bool is_title_keys);
void AttemptLoadKeyFile(const std::string& dir1, const std::string& dir2,
const std::string& filename, bool title);
static const std::unordered_map<std::string, KeyIndex<S128KeyType>> s128_file_id;
static const std::unordered_map<std::string, KeyIndex<S256KeyType>> s256_file_id;
};
} // namespace Core::Crypto

View File

@@ -0,0 +1,5 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
namespace Crypto {} // namespace Crypto

View File

@@ -0,0 +1,20 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "common/assert.h"
#include "core/file_sys/vfs.h"
#include "key_manager.h"
#include "mbedtls/cipher.h"
namespace Crypto {
typedef std::array<u8, 0x20> SHA256Hash;
inline SHA256Hash operator"" _HASH(const char* data, size_t len) {
if (len != 0x40)
return {};
}
} // namespace Crypto

View File

@@ -0,0 +1,150 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <array>
#include <string>
#include <core/loader/loader.h>
#include "common/logging/log.h"
#include "core/file_sys/card_image.h"
#include "core/file_sys/partition_filesystem.h"
#include "core/file_sys/vfs_offset.h"
namespace FileSys {
XCI::XCI(VirtualFile file_) : file(std::move(file_)), partitions(0x4) {
if (file->ReadObject(&header) != sizeof(GamecardHeader)) {
status = Loader::ResultStatus::ErrorInvalidFormat;
return;
}
if (header.magic != Common::MakeMagic('H', 'E', 'A', 'D')) {
status = Loader::ResultStatus::ErrorInvalidFormat;
return;
}
PartitionFilesystem main_hfs(
std::make_shared<OffsetVfsFile>(file, header.hfs_size, header.hfs_offset));
if (main_hfs.GetStatus() != Loader::ResultStatus::Success) {
status = main_hfs.GetStatus();
return;
}
static constexpr std::array<const char*, 0x4> partition_names = {"update", "normal", "secure",
"logo"};
for (XCIPartition partition :
{XCIPartition::Update, XCIPartition::Normal, XCIPartition::Secure, XCIPartition::Logo}) {
auto raw = main_hfs.GetFile(partition_names[static_cast<size_t>(partition)]);
if (raw != nullptr)
partitions[static_cast<size_t>(partition)] = std::make_shared<PartitionFilesystem>(raw);
}
auto result = AddNCAFromPartition(XCIPartition::Secure);
if (result != Loader::ResultStatus::Success) {
status = result;
return;
}
result = AddNCAFromPartition(XCIPartition::Update);
if (result != Loader::ResultStatus::Success) {
status = result;
return;
}
result = AddNCAFromPartition(XCIPartition::Normal);
if (result != Loader::ResultStatus::Success) {
status = result;
return;
}
if (GetFormatVersion() >= 0x2) {
result = AddNCAFromPartition(XCIPartition::Logo);
if (result != Loader::ResultStatus::Success) {
status = result;
return;
}
}
status = Loader::ResultStatus::Success;
}
Loader::ResultStatus XCI::GetStatus() const {
return status;
}
VirtualDir XCI::GetPartition(XCIPartition partition) const {
return partitions[static_cast<size_t>(partition)];
}
VirtualDir XCI::GetSecurePartition() const {
return GetPartition(XCIPartition::Secure);
}
VirtualDir XCI::GetNormalPartition() const {
return GetPartition(XCIPartition::Normal);
}
VirtualDir XCI::GetUpdatePartition() const {
return GetPartition(XCIPartition::Update);
}
VirtualDir XCI::GetLogoPartition() const {
return GetPartition(XCIPartition::Logo);
}
std::shared_ptr<NCA> XCI::GetNCAByType(NCAContentType type) const {
const auto iter =
std::find_if(ncas.begin(), ncas.end(),
[type](const std::shared_ptr<NCA>& nca) { return nca->GetType() == type; });
return iter == ncas.end() ? nullptr : *iter;
}
VirtualFile XCI::GetNCAFileByType(NCAContentType type) const {
auto nca = GetNCAByType(type);
if (nca != nullptr)
return nca->GetBaseFile();
return nullptr;
}
std::vector<std::shared_ptr<VfsFile>> XCI::GetFiles() const {
return {};
}
std::vector<std::shared_ptr<VfsDirectory>> XCI::GetSubdirectories() const {
return std::vector<std::shared_ptr<VfsDirectory>>();
}
std::string XCI::GetName() const {
return file->GetName();
}
std::shared_ptr<VfsDirectory> XCI::GetParentDirectory() const {
return file->GetContainingDirectory();
}
bool XCI::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
return false;
}
Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) {
if (partitions[static_cast<size_t>(part)] == nullptr) {
return Loader::ResultStatus::ErrorInvalidFormat;
}
for (const VirtualFile& file : partitions[static_cast<size_t>(part)]->GetFiles()) {
if (file->GetExtension() != "nca")
continue;
auto nca = std::make_shared<NCA>(file);
if (nca->GetStatus() == Loader::ResultStatus::Success)
ncas.push_back(std::move(nca));
}
return Loader::ResultStatus::Success;
}
u8 XCI::GetFormatVersion() const {
return GetLogoPartition() == nullptr ? 0x1 : 0x2;
}
} // namespace FileSys

View File

@@ -0,0 +1,96 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include <vector>
#include "common/common_types.h"
#include "common/swap.h"
#include "core/file_sys/content_archive.h"
#include "core/file_sys/vfs.h"
#include "core/loader/loader.h"
namespace FileSys {
enum class GamecardSize : u8 {
S_1GB = 0xFA,
S_2GB = 0xF8,
S_4GB = 0xF0,
S_8GB = 0xE0,
S_16GB = 0xE1,
S_32GB = 0xE2,
};
struct GamecardInfo {
std::array<u8, 0x70> data;
};
static_assert(sizeof(GamecardInfo) == 0x70, "GamecardInfo has incorrect size.");
struct GamecardHeader {
std::array<u8, 0x100> signature;
u32_le magic;
u32_le secure_area_start;
u32_le backup_area_start;
u8 kek_index;
GamecardSize size;
u8 header_version;
u8 flags;
u64_le package_id;
u64_le valid_data_end;
u128 info_iv;
u64_le hfs_offset;
u64_le hfs_size;
std::array<u8, 0x20> hfs_header_hash;
std::array<u8, 0x20> initial_data_hash;
u32_le secure_mode_flag;
u32_le title_key_flag;
u32_le key_flag;
u32_le normal_area_end;
GamecardInfo info;
};
static_assert(sizeof(GamecardHeader) == 0x200, "GamecardHeader has incorrect size.");
enum class XCIPartition : u8 { Update, Normal, Secure, Logo };
class XCI : public ReadOnlyVfsDirectory {
public:
explicit XCI(VirtualFile file);
Loader::ResultStatus GetStatus() const;
u8 GetFormatVersion() const;
VirtualDir GetPartition(XCIPartition partition) const;
VirtualDir GetSecurePartition() const;
VirtualDir GetNormalPartition() const;
VirtualDir GetUpdatePartition() const;
VirtualDir GetLogoPartition() const;
std::shared_ptr<NCA> GetNCAByType(NCAContentType type) const;
VirtualFile GetNCAFileByType(NCAContentType type) const;
std::vector<std::shared_ptr<VfsFile>> GetFiles() const override;
std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override;
std::string GetName() const override;
std::shared_ptr<VfsDirectory> GetParentDirectory() const override;
protected:
bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
private:
Loader::ResultStatus AddNCAFromPartition(XCIPartition part);
VirtualFile file;
GamecardHeader header{};
Loader::ResultStatus status;
std::vector<VirtualDir> partitions;
std::vector<std::shared_ptr<NCA>> ncas;
};
} // namespace FileSys

View File

@@ -4,12 +4,14 @@
#include <algorithm>
#include <utility>
#include <boost/optional.hpp>
#include "common/logging/log.h"
#include "core/crypto/aes_util.h"
#include "core/crypto/ctr_encryption_layer.h"
#include "core/file_sys/content_archive.h"
#include "core/file_sys/romfs.h"
#include "core/file_sys/vfs_offset.h"
#include "core/loader/loader.h"
#include "romfs.h"
namespace FileSys {
@@ -29,11 +31,19 @@ enum class NCASectionFilesystemType : u8 {
struct NCASectionHeaderBlock {
INSERT_PADDING_BYTES(3);
NCASectionFilesystemType filesystem_type;
u8 crypto_type;
NCASectionCryptoType crypto_type;
INSERT_PADDING_BYTES(3);
};
static_assert(sizeof(NCASectionHeaderBlock) == 0x8, "NCASectionHeaderBlock has incorrect size.");
struct NCASectionRaw {
NCASectionHeaderBlock header;
std::array<u8, 0x138> block_data;
std::array<u8, 0x8> section_ctr;
INSERT_PADDING_BYTES(0xB8);
};
static_assert(sizeof(NCASectionRaw) == 0x200, "NCASectionRaw has incorrect size.");
struct PFS0Superblock {
NCASectionHeaderBlock header_block;
std::array<u8, 0x20> hash;
@@ -43,67 +53,204 @@ struct PFS0Superblock {
u64_le hash_table_size;
u64_le pfs0_header_offset;
u64_le pfs0_size;
INSERT_PADDING_BYTES(432);
INSERT_PADDING_BYTES(0x1B0);
};
static_assert(sizeof(PFS0Superblock) == 0x200, "PFS0Superblock has incorrect size.");
struct RomFSSuperblock {
NCASectionHeaderBlock header_block;
IVFCHeader ivfc;
INSERT_PADDING_BYTES(0x118);
};
static_assert(sizeof(RomFSSuperblock) == 0xE8, "RomFSSuperblock has incorrect size.");
static_assert(sizeof(RomFSSuperblock) == 0x200, "RomFSSuperblock has incorrect size.");
union NCASectionHeader {
NCASectionRaw raw;
PFS0Superblock pfs0;
RomFSSuperblock romfs;
};
static_assert(sizeof(NCASectionHeader) == 0x200, "NCASectionHeader has incorrect size.");
bool IsValidNCA(const NCAHeader& header) {
// TODO(DarkLordZach): Add NCA2/NCA0 support.
return header.magic == Common::MakeMagic('N', 'C', 'A', '3');
}
u8 NCA::GetCryptoRevision() const {
u8 master_key_id = header.crypto_type;
if (header.crypto_type_2 > master_key_id)
master_key_id = header.crypto_type_2;
if (master_key_id > 0)
--master_key_id;
return master_key_id;
}
boost::optional<Core::Crypto::Key128> NCA::GetKeyAreaKey(NCASectionCryptoType type) const {
const auto master_key_id = GetCryptoRevision();
if (!keys.HasKey(Core::Crypto::S128KeyType::KeyArea, master_key_id, header.key_index))
return boost::none;
std::vector<u8> key_area(header.key_area.begin(), header.key_area.end());
Core::Crypto::AESCipher<Core::Crypto::Key128> cipher(
keys.GetKey(Core::Crypto::S128KeyType::KeyArea, master_key_id, header.key_index),
Core::Crypto::Mode::ECB);
cipher.Transcode(key_area.data(), key_area.size(), key_area.data(), Core::Crypto::Op::Decrypt);
Core::Crypto::Key128 out;
if (type == NCASectionCryptoType::XTS)
std::copy(key_area.begin(), key_area.begin() + 0x10, out.begin());
else if (type == NCASectionCryptoType::CTR)
std::copy(key_area.begin() + 0x20, key_area.begin() + 0x30, out.begin());
else
LOG_CRITICAL(Crypto, "Called GetKeyAreaKey on invalid NCASectionCryptoType type={:02X}",
static_cast<u8>(type));
u128 out_128{};
memcpy(out_128.data(), out.data(), 16);
LOG_DEBUG(Crypto, "called with crypto_rev={:02X}, kak_index={:02X}, key={:016X}{:016X}",
master_key_id, header.key_index, out_128[1], out_128[0]);
return out;
}
boost::optional<Core::Crypto::Key128> NCA::GetTitlekey() const {
const auto master_key_id = GetCryptoRevision();
u128 rights_id{};
memcpy(rights_id.data(), header.rights_id.data(), 16);
if (rights_id == u128{})
return boost::none;
auto titlekey = keys.GetKey(Core::Crypto::S128KeyType::Titlekey, rights_id[1], rights_id[0]);
if (titlekey == Core::Crypto::Key128{})
return boost::none;
Core::Crypto::AESCipher<Core::Crypto::Key128> cipher(
keys.GetKey(Core::Crypto::S128KeyType::Titlekek, master_key_id), Core::Crypto::Mode::ECB);
cipher.Transcode(titlekey.data(), titlekey.size(), titlekey.data(), Core::Crypto::Op::Decrypt);
return titlekey;
}
VirtualFile NCA::Decrypt(NCASectionHeader s_header, VirtualFile in, u64 starting_offset) const {
if (!encrypted)
return in;
switch (s_header.raw.header.crypto_type) {
case NCASectionCryptoType::NONE:
LOG_DEBUG(Crypto, "called with mode=NONE");
return in;
case NCASectionCryptoType::CTR:
LOG_DEBUG(Crypto, "called with mode=CTR, starting_offset={:016X}", starting_offset);
{
boost::optional<Core::Crypto::Key128> key = boost::none;
if (std::find_if_not(header.rights_id.begin(), header.rights_id.end(),
[](char c) { return c == 0; }) == header.rights_id.end()) {
key = GetKeyAreaKey(NCASectionCryptoType::CTR);
} else {
key = GetTitlekey();
}
if (key == boost::none)
return nullptr;
auto out = std::make_shared<Core::Crypto::CTREncryptionLayer>(
std::move(in), key.value(), starting_offset);
std::vector<u8> iv(16);
for (u8 i = 0; i < 8; ++i)
iv[i] = s_header.raw.section_ctr[0x8 - i - 1];
out->SetIV(iv);
return std::static_pointer_cast<VfsFile>(out);
}
case NCASectionCryptoType::XTS:
// TODO(DarkLordZach): Implement XTSEncryptionLayer.
default:
LOG_ERROR(Crypto, "called with unhandled crypto type={:02X}",
static_cast<u8>(s_header.raw.header.crypto_type));
return nullptr;
}
}
NCA::NCA(VirtualFile file_) : file(std::move(file_)) {
if (sizeof(NCAHeader) != file->ReadObject(&header))
LOG_CRITICAL(Loader, "File reader errored out during header read.");
if (!IsValidNCA(header)) {
if (file == nullptr) {
status = Loader::ResultStatus::ErrorInvalidFormat;
return;
}
if (sizeof(NCAHeader) != file->ReadObject(&header))
LOG_ERROR(Loader, "File reader errored out during header read.");
std::ptrdiff_t number_sections =
encrypted = false;
if (!IsValidNCA(header)) {
NCAHeader dec_header{};
Core::Crypto::AESCipher<Core::Crypto::Key256> cipher(
keys.GetKey(Core::Crypto::S256KeyType::Header), Core::Crypto::Mode::XTS);
cipher.XTSTranscode(&header, sizeof(NCAHeader), &dec_header, 0, 0x200,
Core::Crypto::Op::Decrypt);
if (IsValidNCA(dec_header)) {
header = dec_header;
encrypted = true;
} else {
if (!keys.HasKey(Core::Crypto::S256KeyType::Header))
status = Loader::ResultStatus::ErrorMissingKeys;
else
status = Loader::ResultStatus::ErrorDecrypting;
return;
}
}
const std::ptrdiff_t number_sections =
std::count_if(std::begin(header.section_tables), std::end(header.section_tables),
[](NCASectionTableEntry entry) { return entry.media_offset > 0; });
std::vector<NCASectionHeader> sections(number_sections);
const auto length_sections = SECTION_HEADER_SIZE * number_sections;
if (encrypted) {
auto raw = file->ReadBytes(length_sections, SECTION_HEADER_OFFSET);
Core::Crypto::AESCipher<Core::Crypto::Key256> cipher(
keys.GetKey(Core::Crypto::S256KeyType::Header), Core::Crypto::Mode::XTS);
cipher.XTSTranscode(raw.data(), length_sections, sections.data(), 2, SECTION_HEADER_SIZE,
Core::Crypto::Op::Decrypt);
} else {
file->ReadBytes(sections.data(), length_sections, SECTION_HEADER_OFFSET);
}
for (std::ptrdiff_t i = 0; i < number_sections; ++i) {
// Seek to beginning of this section.
NCASectionHeaderBlock block{};
if (sizeof(NCASectionHeaderBlock) !=
file->ReadObject(&block, SECTION_HEADER_OFFSET + i * SECTION_HEADER_SIZE))
LOG_CRITICAL(Loader, "File reader errored out during header read.");
if (block.filesystem_type == NCASectionFilesystemType::ROMFS) {
RomFSSuperblock sb{};
if (sizeof(RomFSSuperblock) !=
file->ReadObject(&sb, SECTION_HEADER_OFFSET + i * SECTION_HEADER_SIZE))
LOG_CRITICAL(Loader, "File reader errored out during header read.");
auto section = sections[i];
if (section.raw.header.filesystem_type == NCASectionFilesystemType::ROMFS) {
const size_t romfs_offset =
header.section_tables[i].media_offset * MEDIA_OFFSET_MULTIPLIER +
sb.ivfc.levels[IVFC_MAX_LEVEL - 1].offset;
const size_t romfs_size = sb.ivfc.levels[IVFC_MAX_LEVEL - 1].size;
files.emplace_back(std::make_shared<OffsetVfsFile>(file, romfs_size, romfs_offset));
romfs = files.back();
} else if (block.filesystem_type == NCASectionFilesystemType::PFS0) {
PFS0Superblock sb{};
// Seek back to beginning of this section.
if (sizeof(PFS0Superblock) !=
file->ReadObject(&sb, SECTION_HEADER_OFFSET + i * SECTION_HEADER_SIZE))
LOG_CRITICAL(Loader, "File reader errored out during header read.");
section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset;
const size_t romfs_size = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].size;
auto dec =
Decrypt(section, std::make_shared<OffsetVfsFile>(file, romfs_size, romfs_offset),
romfs_offset);
if (dec != nullptr) {
files.push_back(std::move(dec));
romfs = files.back();
} else {
status = Loader::ResultStatus::ErrorMissingKeys;
return;
}
} else if (section.raw.header.filesystem_type == NCASectionFilesystemType::PFS0) {
u64 offset = (static_cast<u64>(header.section_tables[i].media_offset) *
MEDIA_OFFSET_MULTIPLIER) +
sb.pfs0_header_offset;
section.pfs0.pfs0_header_offset;
u64 size = MEDIA_OFFSET_MULTIPLIER * (header.section_tables[i].media_end_offset -
header.section_tables[i].media_offset);
auto npfs = std::make_shared<PartitionFilesystem>(
std::make_shared<OffsetVfsFile>(file, size, offset));
auto dec =
Decrypt(section, std::make_shared<OffsetVfsFile>(file, size, offset), offset);
if (dec != nullptr) {
auto npfs = std::make_shared<PartitionFilesystem>(std::move(dec));
if (npfs->GetStatus() == Loader::ResultStatus::Success) {
dirs.emplace_back(npfs);
if (IsDirectoryExeFS(dirs.back()))
exefs = dirs.back();
if (npfs->GetStatus() == Loader::ResultStatus::Success) {
dirs.push_back(std::move(npfs));
if (IsDirectoryExeFS(dirs.back()))
exefs = dirs.back();
}
} else {
status = Loader::ResultStatus::ErrorMissingKeys;
return;
}
}
}
@@ -153,6 +300,10 @@ VirtualDir NCA::GetExeFS() const {
return exefs;
}
VirtualFile NCA::GetBaseFile() const {
return file;
}
bool NCA::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
return false;
}

View File

@@ -8,14 +8,19 @@
#include <memory>
#include <string>
#include <vector>
#include <boost/optional.hpp>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
#include "control_metadata.h"
#include "core/crypto/key_manager.h"
#include "core/file_sys/partition_filesystem.h"
#include "core/loader/loader.h"
namespace FileSys {
union NCASectionHeader;
enum class NCAContentType : u8 {
Program = 0,
Meta = 1,
@@ -24,6 +29,13 @@ enum class NCAContentType : u8 {
Data = 4,
};
enum class NCASectionCryptoType : u8 {
NONE = 1,
XTS = 2,
CTR = 3,
BKTR = 4,
};
struct NCASectionTableEntry {
u32_le media_offset;
u32_le media_end_offset;
@@ -48,7 +60,7 @@ struct NCAHeader {
std::array<u8, 0x10> rights_id;
std::array<NCASectionTableEntry, 0x4> section_tables;
std::array<std::array<u8, 0x20>, 0x4> hash_tables;
std::array<std::array<u8, 0x10>, 0x4> key_area;
std::array<u8, 0x40> key_area;
INSERT_PADDING_BYTES(0xC0);
};
static_assert(sizeof(NCAHeader) == 0x400, "NCAHeader has incorrect size.");
@@ -58,10 +70,7 @@ inline bool IsDirectoryExeFS(const std::shared_ptr<VfsDirectory>& pfs) {
return pfs->GetFile("main") != nullptr && pfs->GetFile("main.npdm") != nullptr;
}
inline bool IsValidNCA(const NCAHeader& header) {
return header.magic == Common::MakeMagic('N', 'C', 'A', '2') ||
header.magic == Common::MakeMagic('N', 'C', 'A', '3');
}
bool IsValidNCA(const NCAHeader& header);
// 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.
@@ -81,10 +90,17 @@ public:
VirtualFile GetRomFS() const;
VirtualDir GetExeFS() const;
VirtualFile GetBaseFile() const;
protected:
bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
private:
u8 GetCryptoRevision() const;
boost::optional<Core::Crypto::Key128> GetKeyAreaKey(NCASectionCryptoType type) const;
boost::optional<Core::Crypto::Key128> GetTitlekey() const;
VirtualFile Decrypt(NCASectionHeader header, VirtualFile in, u64 starting_offset) const;
std::vector<VirtualDir> dirs;
std::vector<VirtualFile> files;
@@ -95,6 +111,10 @@ private:
NCAHeader header{};
Loader::ResultStatus status{};
bool encrypted;
Core::Crypto::KeyManager keys;
};
} // namespace FileSys

View File

@@ -62,6 +62,13 @@ enum class Language : u8 {
Chinese = 14,
};
static constexpr std::array<const char*, 15> LANGUAGE_NAMES = {
"AmericanEnglish", "BritishEnglish", "Japanese",
"French", "German", "LatinAmericanSpanish",
"Spanish", "Italian", "Dutch",
"CanadianFrench", "Portugese", "Russian",
"Korean", "Taiwanese", "Chinese"};
// A class representing the format used by NX metadata files, typically named Control.nacp.
// These store application name, dev name, title id, and other miscellaneous data.
class NACP {

View File

@@ -4,8 +4,9 @@
#pragma once
#include <array>
#include <cstddef>
#include <iterator>
#include <string_view>
#include "common/common_funcs.h"
#include "common/common_types.h"
@@ -21,9 +22,14 @@ enum EntryType : u8 {
// Structure of a directory entry, from
// http://switchbrew.org/index.php?title=Filesystem_services#DirectoryEntry
const size_t FILENAME_LENGTH = 0x300;
struct Entry {
char filename[FILENAME_LENGTH];
Entry(std::string_view view, EntryType entry_type, u64 entry_size)
: type{entry_type}, file_size{entry_size} {
const size_t copy_size = view.copy(filename, std::size(filename) - 1);
filename[copy_size] = '\0';
}
char filename[0x300];
INSERT_PADDING_BYTES(4);
EntryType type;
INSERT_PADDING_BYTES(3);

View File

@@ -7,6 +7,7 @@
#include <memory>
#include <string>
#include "common/common_types.h"
#include "common/swap.h"
#include "core/hle/result.h"
namespace FileSys {

View File

@@ -4,12 +4,160 @@
#include <algorithm>
#include <numeric>
#include <string>
#include "common/common_paths.h"
#include "common/file_util.h"
#include "common/logging/backend.h"
#include "core/file_sys/vfs.h"
namespace FileSys {
VfsFilesystem::VfsFilesystem(VirtualDir root_) : root(std::move(root_)) {}
VfsFilesystem::~VfsFilesystem() = default;
std::string VfsFilesystem::GetName() const {
return root->GetName();
}
bool VfsFilesystem::IsReadable() const {
return root->IsReadable();
}
bool VfsFilesystem::IsWritable() const {
return root->IsWritable();
}
VfsEntryType VfsFilesystem::GetEntryType(std::string_view path_) const {
const auto path = FileUtil::SanitizePath(path_);
if (root->GetFileRelative(path) != nullptr)
return VfsEntryType::File;
if (root->GetDirectoryRelative(path) != nullptr)
return VfsEntryType::Directory;
return VfsEntryType::None;
}
VirtualFile VfsFilesystem::OpenFile(std::string_view path_, Mode perms) {
const auto path = FileUtil::SanitizePath(path_);
return root->GetFileRelative(path);
}
VirtualFile VfsFilesystem::CreateFile(std::string_view path_, Mode perms) {
const auto path = FileUtil::SanitizePath(path_);
return root->CreateFileRelative(path);
}
VirtualFile VfsFilesystem::CopyFile(std::string_view old_path_, std::string_view new_path_) {
const auto old_path = FileUtil::SanitizePath(old_path_);
const auto new_path = FileUtil::SanitizePath(new_path_);
// VfsDirectory impls are only required to implement copy across the current directory.
if (FileUtil::GetParentPath(old_path) == FileUtil::GetParentPath(new_path)) {
if (!root->Copy(FileUtil::GetFilename(old_path), FileUtil::GetFilename(new_path)))
return nullptr;
return OpenFile(new_path, Mode::ReadWrite);
}
// Do it using RawCopy. Non-default impls are encouraged to optimize this.
const auto old_file = OpenFile(old_path, Mode::Read);
if (old_file == nullptr)
return nullptr;
auto new_file = OpenFile(new_path, Mode::Read);
if (new_file != nullptr)
return nullptr;
new_file = CreateFile(new_path, Mode::Write);
if (new_file == nullptr)
return nullptr;
if (!VfsRawCopy(old_file, new_file))
return nullptr;
return new_file;
}
VirtualFile VfsFilesystem::MoveFile(std::string_view old_path_, std::string_view new_path_) {
const auto old_path = FileUtil::SanitizePath(old_path_);
const auto new_path = FileUtil::SanitizePath(new_path_);
// Again, non-default impls are highly encouraged to provide a more optimized version of this.
auto out = CopyFile(old_path_, new_path_);
if (out == nullptr)
return nullptr;
if (DeleteFile(old_path))
return out;
return nullptr;
}
bool VfsFilesystem::DeleteFile(std::string_view path_) {
const auto path = FileUtil::SanitizePath(path_);
auto parent = OpenDirectory(FileUtil::GetParentPath(path), Mode::Write);
if (parent == nullptr)
return false;
return parent->DeleteFile(FileUtil::GetFilename(path));
}
VirtualDir VfsFilesystem::OpenDirectory(std::string_view path_, Mode perms) {
const auto path = FileUtil::SanitizePath(path_);
return root->GetDirectoryRelative(path);
}
VirtualDir VfsFilesystem::CreateDirectory(std::string_view path_, Mode perms) {
const auto path = FileUtil::SanitizePath(path_);
return root->CreateDirectoryRelative(path);
}
VirtualDir VfsFilesystem::CopyDirectory(std::string_view old_path_, std::string_view new_path_) {
const auto old_path = FileUtil::SanitizePath(old_path_);
const auto new_path = FileUtil::SanitizePath(new_path_);
// Non-default impls are highly encouraged to provide a more optimized version of this.
auto old_dir = OpenDirectory(old_path, Mode::Read);
if (old_dir == nullptr)
return nullptr;
auto new_dir = OpenDirectory(new_path, Mode::Read);
if (new_dir != nullptr)
return nullptr;
new_dir = CreateDirectory(new_path, Mode::Write);
if (new_dir == nullptr)
return nullptr;
for (const auto& file : old_dir->GetFiles()) {
const auto x =
CopyFile(old_path + DIR_SEP + file->GetName(), new_path + DIR_SEP + file->GetName());
if (x == nullptr)
return nullptr;
}
for (const auto& dir : old_dir->GetSubdirectories()) {
const auto x =
CopyDirectory(old_path + DIR_SEP + dir->GetName(), new_path + DIR_SEP + dir->GetName());
if (x == nullptr)
return nullptr;
}
return new_dir;
}
VirtualDir VfsFilesystem::MoveDirectory(std::string_view old_path_, std::string_view new_path_) {
const auto old_path = FileUtil::SanitizePath(old_path_);
const auto new_path = FileUtil::SanitizePath(new_path_);
// Non-default impls are highly encouraged to provide a more optimized version of this.
auto out = CopyDirectory(old_path_, new_path_);
if (out == nullptr)
return nullptr;
if (DeleteDirectory(old_path))
return out;
return nullptr;
}
bool VfsFilesystem::DeleteDirectory(std::string_view path_) {
const auto path = FileUtil::SanitizePath(path_);
auto parent = OpenDirectory(FileUtil::GetParentPath(path), Mode::Write);
if (parent == nullptr)
return false;
return parent->DeleteSubdirectoryRecursive(FileUtil::GetFilename(path));
}
VfsFile::~VfsFile() = default;
std::string VfsFile::GetExtension() const {
@@ -285,6 +433,26 @@ bool ReadOnlyVfsDirectory::Rename(std::string_view name) {
return false;
}
bool DeepEquals(const VirtualFile& file1, const VirtualFile& file2, size_t block_size) {
if (file1->GetSize() != file2->GetSize())
return false;
std::vector<u8> f1_v(block_size);
std::vector<u8> f2_v(block_size);
for (size_t i = 0; i < file1->GetSize(); i += block_size) {
auto f1_vs = file1->Read(f1_v.data(), block_size, i);
auto f2_vs = file2->Read(f2_v.data(), block_size, i);
if (f1_vs != f2_vs)
return false;
auto iters = std::mismatch(f1_v.begin(), f1_v.end(), f2_v.begin(), f2_v.end());
if (iters.first != f1_v.end() && iters.second != f2_v.end())
return false;
}
return true;
}
bool VfsRawCopy(VirtualFile src, VirtualFile dest) {
if (src == nullptr || dest == nullptr)
return false;

View File

@@ -11,14 +11,74 @@
#include <vector>
#include "boost/optional.hpp"
#include "common/common_types.h"
#include "core/file_sys/mode.h"
namespace FileSys {
struct VfsFilesystem;
struct VfsFile;
struct VfsDirectory;
// Convenience typedefs to use VfsDirectory and VfsFile
using VirtualDir = std::shared_ptr<FileSys::VfsDirectory>;
using VirtualFile = std::shared_ptr<FileSys::VfsFile>;
// Convenience typedefs to use Vfs* interfaces
using VirtualFilesystem = std::shared_ptr<VfsFilesystem>;
using VirtualDir = std::shared_ptr<VfsDirectory>;
using VirtualFile = std::shared_ptr<VfsFile>;
// An enumeration representing what can be at the end of a path in a VfsFilesystem
enum class VfsEntryType {
None,
File,
Directory,
};
// A class representing an abstract filesystem. A default implementation given the root VirtualDir
// is provided for convenience, but if the Vfs implementation has any additional state or
// functionality, they will need to override.
struct VfsFilesystem : NonCopyable {
VfsFilesystem(VirtualDir root);
virtual ~VfsFilesystem();
// Gets the friendly name for the filesystem.
virtual std::string GetName() const;
// Return whether or not the user has read permissions on this filesystem.
virtual bool IsReadable() const;
// Return whether or not the user has write permission on this filesystem.
virtual bool IsWritable() const;
// Determine if the entry at path is non-existant, a file, or a directory.
virtual VfsEntryType GetEntryType(std::string_view path) const;
// Opens the file with path relative to root. If it doesn't exist, returns nullptr.
virtual VirtualFile OpenFile(std::string_view path, Mode perms);
// Creates a new, empty file at path
virtual VirtualFile CreateFile(std::string_view path, Mode perms);
// Copies the file from old_path to new_path, returning the new file on success and nullptr on
// failure.
virtual VirtualFile CopyFile(std::string_view old_path, std::string_view new_path);
// Moves the file from old_path to new_path, returning the moved file on success and nullptr on
// failure.
virtual VirtualFile MoveFile(std::string_view old_path, std::string_view new_path);
// Deletes the file with path relative to root, returing true on success.
virtual bool DeleteFile(std::string_view path);
// Opens the directory with path relative to root. If it doesn't exist, returns nullptr.
virtual VirtualDir OpenDirectory(std::string_view path, Mode perms);
// Creates a new, empty directory at path
virtual VirtualDir CreateDirectory(std::string_view path, Mode perms);
// Copies the directory from old_path to new_path, returning the new directory on success and
// nullptr on failure.
virtual VirtualDir CopyDirectory(std::string_view old_path, std::string_view new_path);
// Moves the directory from old_path to new_path, returning the moved directory on success and
// nullptr on failure.
virtual VirtualDir MoveDirectory(std::string_view old_path, std::string_view new_path);
// Deletes the directory with path relative to root, returing true on success.
virtual bool DeleteDirectory(std::string_view path);
protected:
// Root directory in default implementation.
VirtualDir root;
};
// A class representing a file in an abstract filesystem.
struct VfsFile : NonCopyable {
@@ -245,6 +305,9 @@ struct ReadOnlyVfsDirectory : public VfsDirectory {
bool Rename(std::string_view name) override;
};
// Compare the two files, byte-for-byte, in increments specificed by block_size
bool DeepEquals(const VirtualFile& file1, const VirtualFile& file2, size_t block_size = 0x200);
// A method that copies the raw data between two different implementations of VirtualFile. If you
// are using the same implementation, it is probably better to use the Copy method in the parent
// directory of src/dest.

View File

@@ -6,7 +6,7 @@
#include <cstddef>
#include <iterator>
#include <utility>
#include "common/assert.h"
#include "common/common_paths.h"
#include "common/logging/log.h"
#include "core/file_sys/vfs_real.h"
@@ -29,6 +29,8 @@ static std::string ModeFlagsToString(Mode mode) {
mode_str = "a";
else if (mode & Mode::Write)
mode_str = "w";
else
UNREACHABLE_MSG("Invalid file open mode: {:02X}", static_cast<u8>(mode));
}
mode_str += "b";
@@ -36,8 +38,174 @@ static std::string ModeFlagsToString(Mode mode) {
return mode_str;
}
RealVfsFile::RealVfsFile(const std::string& path_, Mode perms_)
: backing(path_, ModeFlagsToString(perms_).c_str()), path(path_),
RealVfsFilesystem::RealVfsFilesystem() : VfsFilesystem(nullptr) {}
std::string RealVfsFilesystem::GetName() const {
return "Real";
}
bool RealVfsFilesystem::IsReadable() const {
return true;
}
bool RealVfsFilesystem::IsWritable() const {
return true;
}
VfsEntryType RealVfsFilesystem::GetEntryType(std::string_view path_) const {
const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault);
if (!FileUtil::Exists(path))
return VfsEntryType::None;
if (FileUtil::IsDirectory(path))
return VfsEntryType::Directory;
return VfsEntryType::File;
}
VirtualFile RealVfsFilesystem::OpenFile(std::string_view path_, Mode perms) {
const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault);
if (cache.find(path) != cache.end()) {
auto weak = cache[path];
if (!weak.expired()) {
return std::shared_ptr<RealVfsFile>(new RealVfsFile(*this, weak.lock(), path, perms));
}
}
if (!FileUtil::Exists(path) && (perms & Mode::WriteAppend) != 0)
FileUtil::CreateEmptyFile(path);
auto backing = std::make_shared<FileUtil::IOFile>(path, ModeFlagsToString(perms).c_str());
cache[path] = backing;
// Cannot use make_shared as RealVfsFile constructor is private
return std::shared_ptr<RealVfsFile>(new RealVfsFile(*this, backing, path, perms));
}
VirtualFile RealVfsFilesystem::CreateFile(std::string_view path_, Mode perms) {
const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault);
if (!FileUtil::Exists(path) && !FileUtil::CreateEmptyFile(path))
return nullptr;
return OpenFile(path, perms);
}
VirtualFile RealVfsFilesystem::CopyFile(std::string_view old_path_, std::string_view new_path_) {
const auto old_path =
FileUtil::SanitizePath(old_path_, FileUtil::DirectorySeparator::PlatformDefault);
const auto new_path =
FileUtil::SanitizePath(new_path_, FileUtil::DirectorySeparator::PlatformDefault);
if (!FileUtil::Exists(old_path) || FileUtil::Exists(new_path) ||
FileUtil::IsDirectory(old_path) || !FileUtil::Copy(old_path, new_path))
return nullptr;
return OpenFile(new_path, Mode::ReadWrite);
}
VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_view new_path_) {
const auto old_path =
FileUtil::SanitizePath(old_path_, FileUtil::DirectorySeparator::PlatformDefault);
const auto new_path =
FileUtil::SanitizePath(new_path_, FileUtil::DirectorySeparator::PlatformDefault);
if (!FileUtil::Exists(old_path) || FileUtil::Exists(new_path) ||
FileUtil::IsDirectory(old_path) || !FileUtil::Rename(old_path, new_path))
return nullptr;
if (cache.find(old_path) != cache.end()) {
auto cached = cache[old_path];
if (!cached.expired()) {
auto file = cached.lock();
file->Open(new_path, "r+b");
cache.erase(old_path);
cache[new_path] = file;
}
}
return OpenFile(new_path, Mode::ReadWrite);
}
bool RealVfsFilesystem::DeleteFile(std::string_view path_) {
const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault);
if (cache.find(path) != cache.end()) {
if (!cache[path].expired())
cache[path].lock()->Close();
cache.erase(path);
}
return FileUtil::Delete(path);
}
VirtualDir RealVfsFilesystem::OpenDirectory(std::string_view path_, Mode perms) {
const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault);
// Cannot use make_shared as RealVfsDirectory constructor is private
return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms));
}
VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, Mode perms) {
const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault);
if (!FileUtil::Exists(path) && !FileUtil::CreateDir(path))
return nullptr;
// Cannot use make_shared as RealVfsDirectory constructor is private
return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms));
}
VirtualDir RealVfsFilesystem::CopyDirectory(std::string_view old_path_,
std::string_view new_path_) {
const auto old_path =
FileUtil::SanitizePath(old_path_, FileUtil::DirectorySeparator::PlatformDefault);
const auto new_path =
FileUtil::SanitizePath(new_path_, FileUtil::DirectorySeparator::PlatformDefault);
if (!FileUtil::Exists(old_path) || FileUtil::Exists(new_path) ||
!FileUtil::IsDirectory(old_path))
return nullptr;
FileUtil::CopyDir(old_path, new_path);
return OpenDirectory(new_path, Mode::ReadWrite);
}
VirtualDir RealVfsFilesystem::MoveDirectory(std::string_view old_path_,
std::string_view new_path_) {
const auto old_path =
FileUtil::SanitizePath(old_path_, FileUtil::DirectorySeparator::PlatformDefault);
const auto new_path =
FileUtil::SanitizePath(new_path_, FileUtil::DirectorySeparator::PlatformDefault);
if (!FileUtil::Exists(old_path) || FileUtil::Exists(new_path) ||
FileUtil::IsDirectory(old_path) || !FileUtil::Rename(old_path, new_path))
return nullptr;
for (auto& kv : cache) {
// Path in cache starts with old_path
if (kv.first.rfind(old_path, 0) == 0) {
const auto file_old_path =
FileUtil::SanitizePath(kv.first, FileUtil::DirectorySeparator::PlatformDefault);
const auto file_new_path =
FileUtil::SanitizePath(new_path + DIR_SEP + kv.first.substr(old_path.size()),
FileUtil::DirectorySeparator::PlatformDefault);
auto cached = cache[file_old_path];
if (!cached.expired()) {
auto file = cached.lock();
file->Open(file_new_path, "r+b");
cache.erase(file_old_path);
cache[file_new_path] = file;
}
}
}
return OpenDirectory(new_path, Mode::ReadWrite);
}
bool RealVfsFilesystem::DeleteDirectory(std::string_view path_) {
const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault);
for (auto& kv : cache) {
// Path in cache starts with old_path
if (kv.first.rfind(path, 0) == 0) {
if (!cache[kv.first].expired())
cache[kv.first].lock()->Close();
cache.erase(kv.first);
}
}
return FileUtil::DeleteDirRecursively(path);
}
RealVfsFile::RealVfsFile(RealVfsFilesystem& base_, std::shared_ptr<FileUtil::IOFile> backing_,
const std::string& path_, Mode perms_)
: base(base_), backing(std::move(backing_)), path(path_),
parent_path(FileUtil::GetParentPath(path_)),
path_components(FileUtil::SplitPathComponents(path_)),
parent_components(FileUtil::SliceVector(path_components, 0, path_components.size() - 1)),
@@ -48,15 +216,15 @@ std::string RealVfsFile::GetName() const {
}
size_t RealVfsFile::GetSize() const {
return backing.GetSize();
return backing->GetSize();
}
bool RealVfsFile::Resize(size_t new_size) {
return backing.Resize(new_size);
return backing->Resize(new_size);
}
std::shared_ptr<VfsDirectory> RealVfsFile::GetContainingDirectory() const {
return std::make_shared<RealVfsDirectory>(parent_path, perms);
return base.OpenDirectory(parent_path, perms);
}
bool RealVfsFile::IsWritable() const {
@@ -68,62 +236,118 @@ bool RealVfsFile::IsReadable() const {
}
size_t RealVfsFile::Read(u8* data, size_t length, size_t offset) const {
if (!backing.Seek(offset, SEEK_SET))
if (!backing->Seek(offset, SEEK_SET))
return 0;
return backing.ReadBytes(data, length);
return backing->ReadBytes(data, length);
}
size_t RealVfsFile::Write(const u8* data, size_t length, size_t offset) {
if (!backing.Seek(offset, SEEK_SET))
if (!backing->Seek(offset, SEEK_SET))
return 0;
return backing.WriteBytes(data, length);
return backing->WriteBytes(data, length);
}
bool RealVfsFile::Rename(std::string_view name) {
std::string name_str(name.begin(), name.end());
const auto out = FileUtil::Rename(GetName(), name_str);
return base.MoveFile(path, parent_path + DIR_SEP + std::string(name)) != nullptr;
}
path = (parent_path + DIR_SEP).append(name);
path_components = parent_components;
path_components.push_back(std::move(name_str));
backing = FileUtil::IOFile(path, ModeFlagsToString(perms).c_str());
bool RealVfsFile::Close() {
return backing->Close();
}
// TODO(DarkLordZach): MSVC would not let me combine the following two functions using 'if
// constexpr' because there is a compile error in the branch not used.
template <>
std::vector<VirtualFile> RealVfsDirectory::IterateEntries<RealVfsFile, VfsFile>() const {
if (perms == Mode::Append)
return {};
std::vector<VirtualFile> out;
FileUtil::ForeachDirectoryEntry(
nullptr, path,
[&out, this](u64* entries_out, const std::string& directory, const std::string& filename) {
const std::string full_path = directory + DIR_SEP + filename;
if (!FileUtil::IsDirectory(full_path))
out.emplace_back(base.OpenFile(full_path, perms));
return true;
});
return out;
}
bool RealVfsFile::Close() {
return backing.Close();
template <>
std::vector<VirtualDir> RealVfsDirectory::IterateEntries<RealVfsDirectory, VfsDirectory>() const {
if (perms == Mode::Append)
return {};
std::vector<VirtualDir> out;
FileUtil::ForeachDirectoryEntry(
nullptr, path,
[&out, this](u64* entries_out, const std::string& directory, const std::string& filename) {
const std::string full_path = directory + DIR_SEP + filename;
if (FileUtil::IsDirectory(full_path))
out.emplace_back(base.OpenDirectory(full_path, perms));
return true;
});
return out;
}
RealVfsDirectory::RealVfsDirectory(const std::string& path_, Mode perms_)
: path(FileUtil::RemoveTrailingSlash(path_)), parent_path(FileUtil::GetParentPath(path)),
RealVfsDirectory::RealVfsDirectory(RealVfsFilesystem& base_, const std::string& path_, Mode perms_)
: base(base_), path(FileUtil::RemoveTrailingSlash(path_)),
parent_path(FileUtil::GetParentPath(path)),
path_components(FileUtil::SplitPathComponents(path)),
parent_components(FileUtil::SliceVector(path_components, 0, path_components.size() - 1)),
perms(perms_) {
if (!FileUtil::Exists(path) && perms & Mode::WriteAppend)
FileUtil::CreateDir(path);
}
if (perms == Mode::Append)
return;
std::shared_ptr<VfsFile> RealVfsDirectory::GetFileRelative(std::string_view path) const {
const auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(path));
if (!FileUtil::Exists(full_path))
return nullptr;
return base.OpenFile(full_path, perms);
}
FileUtil::ForeachDirectoryEntry(
nullptr, path,
[this](u64* entries_out, const std::string& directory, const std::string& filename) {
std::string full_path = directory + DIR_SEP + filename;
if (FileUtil::IsDirectory(full_path))
subdirectories.emplace_back(std::make_shared<RealVfsDirectory>(full_path, perms));
else
files.emplace_back(std::make_shared<RealVfsFile>(full_path, perms));
return true;
});
std::shared_ptr<VfsDirectory> RealVfsDirectory::GetDirectoryRelative(std::string_view path) const {
const auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(path));
if (!FileUtil::Exists(full_path))
return nullptr;
return base.OpenDirectory(full_path, perms);
}
std::shared_ptr<VfsFile> RealVfsDirectory::GetFile(std::string_view name) const {
return GetFileRelative(name);
}
std::shared_ptr<VfsDirectory> RealVfsDirectory::GetSubdirectory(std::string_view name) const {
return GetDirectoryRelative(name);
}
std::shared_ptr<VfsFile> RealVfsDirectory::CreateFileRelative(std::string_view path) {
const auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(path));
return base.CreateFile(full_path, perms);
}
std::shared_ptr<VfsDirectory> RealVfsDirectory::CreateDirectoryRelative(std::string_view path) {
const auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(path));
auto parent = std::string(FileUtil::GetParentPath(full_path));
return base.CreateDirectory(full_path, perms);
}
bool RealVfsDirectory::DeleteSubdirectoryRecursive(std::string_view name) {
auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(name));
return base.DeleteDirectory(full_path);
}
std::vector<std::shared_ptr<VfsFile>> RealVfsDirectory::GetFiles() const {
return files;
return IterateEntries<RealVfsFile, VfsFile>();
}
std::vector<std::shared_ptr<VfsDirectory>> RealVfsDirectory::GetSubdirectories() const {
return subdirectories;
return IterateEntries<RealVfsDirectory, VfsDirectory>();
}
bool RealVfsDirectory::IsWritable() const {
@@ -142,57 +366,32 @@ std::shared_ptr<VfsDirectory> RealVfsDirectory::GetParentDirectory() const {
if (path_components.size() <= 1)
return nullptr;
return std::make_shared<RealVfsDirectory>(parent_path, perms);
return base.OpenDirectory(parent_path, perms);
}
std::shared_ptr<VfsDirectory> RealVfsDirectory::CreateSubdirectory(std::string_view name) {
const std::string subdir_path = (path + DIR_SEP).append(name);
if (!FileUtil::CreateDir(subdir_path)) {
return nullptr;
}
subdirectories.emplace_back(std::make_shared<RealVfsDirectory>(subdir_path, perms));
return subdirectories.back();
return base.CreateDirectory(subdir_path, perms);
}
std::shared_ptr<VfsFile> RealVfsDirectory::CreateFile(std::string_view name) {
const std::string file_path = (path + DIR_SEP).append(name);
if (!FileUtil::CreateEmptyFile(file_path)) {
return nullptr;
}
files.emplace_back(std::make_shared<RealVfsFile>(file_path, perms));
return files.back();
return base.CreateFile(file_path, perms);
}
bool RealVfsDirectory::DeleteSubdirectory(std::string_view name) {
const std::string subdir_path = (path + DIR_SEP).append(name);
return FileUtil::DeleteDirRecursively(subdir_path);
return base.DeleteDirectory(subdir_path);
}
bool RealVfsDirectory::DeleteFile(std::string_view name) {
const auto file = GetFile(name);
if (file == nullptr) {
return false;
}
files.erase(std::find(files.begin(), files.end(), file));
auto real_file = std::static_pointer_cast<RealVfsFile>(file);
real_file->Close();
const std::string file_path = (path + DIR_SEP).append(name);
return FileUtil::Delete(file_path);
return base.DeleteFile(file_path);
}
bool RealVfsDirectory::Rename(std::string_view name) {
const std::string new_name = (parent_path + DIR_SEP).append(name);
return FileUtil::Rename(path, new_name);
return base.MoveFile(path, new_name) != nullptr;
}
std::string RealVfsDirectory::GetFullPath() const {
@@ -202,16 +401,6 @@ std::string RealVfsDirectory::GetFullPath() const {
}
bool RealVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
const auto iter = std::find(files.begin(), files.end(), file);
if (iter == files.end())
return false;
const std::ptrdiff_t offset = std::distance(files.begin(), iter);
files[offset] = files.back();
files.pop_back();
subdirectories.emplace_back(std::move(dir));
return true;
return false;
}
} // namespace FileSys

View File

@@ -6,18 +6,45 @@
#include <string_view>
#include <boost/container/flat_map.hpp>
#include "common/file_util.h"
#include "core/file_sys/mode.h"
#include "core/file_sys/vfs.h"
namespace FileSys {
class RealVfsFilesystem : public VfsFilesystem {
public:
RealVfsFilesystem();
std::string GetName() const override;
bool IsReadable() const override;
bool IsWritable() const override;
VfsEntryType GetEntryType(std::string_view path) const override;
VirtualFile OpenFile(std::string_view path, Mode perms = Mode::Read) override;
VirtualFile CreateFile(std::string_view path, Mode perms = Mode::ReadWrite) override;
VirtualFile CopyFile(std::string_view old_path, std::string_view new_path) override;
VirtualFile MoveFile(std::string_view old_path, std::string_view new_path) override;
bool DeleteFile(std::string_view path) override;
VirtualDir OpenDirectory(std::string_view path, Mode perms = Mode::Read) override;
VirtualDir CreateDirectory(std::string_view path, Mode perms = Mode::ReadWrite) override;
VirtualDir CopyDirectory(std::string_view old_path, std::string_view new_path) override;
VirtualDir MoveDirectory(std::string_view old_path, std::string_view new_path) override;
bool DeleteDirectory(std::string_view path) override;
private:
boost::container::flat_map<std::string, std::weak_ptr<FileUtil::IOFile>> cache;
};
// An implmentation of VfsFile that represents a file on the user's computer.
struct RealVfsFile : public VfsFile {
friend struct RealVfsDirectory;
class RealVfsFile : public VfsFile {
friend class RealVfsDirectory;
friend class RealVfsFilesystem;
RealVfsFile(const std::string& name, Mode perms = Mode::Read);
RealVfsFile(RealVfsFilesystem& base, std::shared_ptr<FileUtil::IOFile> backing,
const std::string& path, Mode perms = Mode::Read);
public:
std::string GetName() const override;
size_t GetSize() const override;
bool Resize(size_t new_size) override;
@@ -31,7 +58,8 @@ struct RealVfsFile : public VfsFile {
private:
bool Close();
FileUtil::IOFile backing;
RealVfsFilesystem& base;
std::shared_ptr<FileUtil::IOFile> backing;
std::string path;
std::string parent_path;
std::vector<std::string> path_components;
@@ -40,9 +68,19 @@ private:
};
// An implementation of VfsDirectory that represents a directory on the user's computer.
struct RealVfsDirectory : public VfsDirectory {
RealVfsDirectory(const std::string& path, Mode perms = Mode::Read);
class RealVfsDirectory : public VfsDirectory {
friend class RealVfsFilesystem;
RealVfsDirectory(RealVfsFilesystem& base, const std::string& path, Mode perms = Mode::Read);
public:
std::shared_ptr<VfsFile> GetFileRelative(std::string_view path) const override;
std::shared_ptr<VfsDirectory> GetDirectoryRelative(std::string_view path) const override;
std::shared_ptr<VfsFile> GetFile(std::string_view name) const override;
std::shared_ptr<VfsDirectory> GetSubdirectory(std::string_view name) const override;
std::shared_ptr<VfsFile> CreateFileRelative(std::string_view path) override;
std::shared_ptr<VfsDirectory> CreateDirectoryRelative(std::string_view path) override;
bool DeleteSubdirectoryRecursive(std::string_view name) override;
std::vector<std::shared_ptr<VfsFile>> GetFiles() const override;
std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override;
bool IsWritable() const override;
@@ -60,13 +98,15 @@ protected:
bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
private:
template <typename T, typename R>
std::vector<std::shared_ptr<R>> IterateEntries() const;
RealVfsFilesystem& base;
std::string path;
std::string parent_path;
std::vector<std::string> path_components;
std::vector<std::string> parent_components;
Mode perms;
std::vector<std::shared_ptr<VfsFile>> files;
std::vector<std::shared_ptr<VfsDirectory>> subdirectories;
};
} // namespace FileSys

View File

@@ -3,6 +3,7 @@
// Refer to the license.txt file included.
#include <algorithm>
#include <utility>
#include "core/file_sys/vfs_vector.h"
namespace FileSys {
@@ -31,16 +32,18 @@ bool VectorVfsDirectory::IsReadable() const {
std::string VectorVfsDirectory::GetName() const {
return name;
}
std::shared_ptr<VfsDirectory> VectorVfsDirectory::GetParentDirectory() const {
return parent;
}
template <typename T>
static bool FindAndRemoveVectorElement(std::vector<T>& vec, std::string_view name) {
auto iter = std::find_if(vec.begin(), vec.end(), [name](T e) { return e->GetName() == name; });
const auto iter =
std::find_if(vec.begin(), vec.end(), [name](const T& e) { return e->GetName() == name; });
if (iter == vec.end())
return false;
auto old_size = vec.size();
vec.erase(iter);
return true;
}
@@ -77,7 +80,7 @@ void VectorVfsDirectory::AddDirectory(VirtualDir dir) {
bool VectorVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
if (!DeleteFile(file->GetName()))
return false;
dirs.emplace_back(dir);
dirs.emplace_back(std::move(dir));
return true;
}
} // namespace FileSys

View File

@@ -37,45 +37,46 @@
#include "core/core.h"
#include "core/core_cpu.h"
#include "core/gdbstub/gdbstub.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/scheduler.h"
#include "core/loader/loader.h"
#include "core/memory.h"
const int GDB_BUFFER_SIZE = 10000;
namespace GDBStub {
namespace {
constexpr int GDB_BUFFER_SIZE = 10000;
const char GDB_STUB_START = '$';
const char GDB_STUB_END = '#';
const char GDB_STUB_ACK = '+';
const char GDB_STUB_NACK = '-';
constexpr char GDB_STUB_START = '$';
constexpr char GDB_STUB_END = '#';
constexpr char GDB_STUB_ACK = '+';
constexpr char GDB_STUB_NACK = '-';
#ifndef SIGTRAP
const u32 SIGTRAP = 5;
constexpr u32 SIGTRAP = 5;
#endif
#ifndef SIGTERM
const u32 SIGTERM = 15;
constexpr u32 SIGTERM = 15;
#endif
#ifndef MSG_WAITALL
const u32 MSG_WAITALL = 8;
constexpr u32 MSG_WAITALL = 8;
#endif
const u32 LR_REGISTER = 30;
const u32 SP_REGISTER = 31;
const u32 PC_REGISTER = 32;
const u32 CPSR_REGISTER = 33;
const u32 UC_ARM64_REG_Q0 = 34;
const u32 FPSCR_REGISTER = 66;
constexpr u32 LR_REGISTER = 30;
constexpr u32 SP_REGISTER = 31;
constexpr u32 PC_REGISTER = 32;
constexpr u32 CPSR_REGISTER = 33;
constexpr u32 UC_ARM64_REG_Q0 = 34;
constexpr u32 FPSCR_REGISTER = 66;
// TODO/WiP - Used while working on support for FPU
const u32 TODO_DUMMY_REG_997 = 997;
const u32 TODO_DUMMY_REG_998 = 998;
constexpr u32 TODO_DUMMY_REG_997 = 997;
constexpr u32 TODO_DUMMY_REG_998 = 998;
// For sample XML files see the GDB source /gdb/features
// GDB also wants the l character at the start
// This XML defines what the registers are for this specific ARM device
static const char* target_xml =
constexpr char target_xml[] =
R"(l<?xml version="1.0"?>
<!DOCTYPE target SYSTEM "gdb-target.dtd">
<target version="1.0">
@@ -141,30 +142,28 @@ static const char* target_xml =
</target>
)";
namespace GDBStub {
int gdbserver_socket = -1;
static int gdbserver_socket = -1;
u8 command_buffer[GDB_BUFFER_SIZE];
u32 command_length;
static u8 command_buffer[GDB_BUFFER_SIZE];
static u32 command_length;
u32 latest_signal = 0;
bool memory_break = false;
static u32 latest_signal = 0;
static bool memory_break = false;
static Kernel::Thread* current_thread = nullptr;
static u32 current_core = 0;
Kernel::Thread* current_thread = nullptr;
u32 current_core = 0;
// Binding to a port within the reserved ports range (0-1023) requires root permissions,
// so default to a port outside of that range.
static u16 gdbstub_port = 24689;
u16 gdbstub_port = 24689;
static bool halt_loop = true;
static bool step_loop = false;
static bool send_trap = false;
bool halt_loop = true;
bool step_loop = false;
bool send_trap = false;
// If set to false, the server will never be started and no
// gdbstub-related functions will be executed.
static std::atomic<bool> server_enabled(false);
std::atomic<bool> server_enabled(false);
#ifdef _WIN32
WSADATA InitData;
@@ -172,23 +171,26 @@ WSADATA InitData;
struct Breakpoint {
bool active;
PAddr addr;
VAddr addr;
u64 len;
std::array<u8, 4> inst;
};
static std::map<u64, Breakpoint> breakpoints_execute;
static std::map<u64, Breakpoint> breakpoints_read;
static std::map<u64, Breakpoint> breakpoints_write;
using BreakpointMap = std::map<VAddr, Breakpoint>;
BreakpointMap breakpoints_execute;
BreakpointMap breakpoints_read;
BreakpointMap breakpoints_write;
struct Module {
std::string name;
PAddr beg;
PAddr end;
VAddr beg;
VAddr end;
};
static std::vector<Module> modules;
std::vector<Module> modules;
} // Anonymous namespace
void RegisterModule(std::string name, PAddr beg, PAddr end, bool add_elf_ext) {
void RegisterModule(std::string name, VAddr beg, VAddr end, bool add_elf_ext) {
Module module;
if (add_elf_ext) {
Common::SplitPath(name, nullptr, &module.name, nullptr);
@@ -419,11 +421,11 @@ static u8 CalculateChecksum(const u8* buffer, size_t length) {
}
/**
* Get the list of breakpoints for a given breakpoint type.
* Get the map of breakpoints for a given breakpoint type.
*
* @param type Type of breakpoint list.
* @param type Type of breakpoint map.
*/
static std::map<u64, Breakpoint>& GetBreakpointList(BreakpointType type) {
static BreakpointMap& GetBreakpointMap(BreakpointType type) {
switch (type) {
case BreakpointType::Execute:
return breakpoints_execute;
@@ -442,20 +444,24 @@ static std::map<u64, Breakpoint>& GetBreakpointList(BreakpointType type) {
* @param type Type of breakpoint.
* @param addr Address of breakpoint.
*/
static void RemoveBreakpoint(BreakpointType type, PAddr addr) {
std::map<u64, Breakpoint>& p = GetBreakpointList(type);
static void RemoveBreakpoint(BreakpointType type, VAddr addr) {
BreakpointMap& p = GetBreakpointMap(type);
auto bp = p.find(static_cast<u64>(addr));
if (bp != p.end()) {
LOG_DEBUG(Debug_GDBStub, "gdb: removed a breakpoint: {:016X} bytes at {:016X} of type {}",
bp->second.len, bp->second.addr, static_cast<int>(type));
p.erase(static_cast<u64>(addr));
const auto bp = p.find(addr);
if (bp == p.end()) {
return;
}
LOG_DEBUG(Debug_GDBStub, "gdb: removed a breakpoint: {:016X} bytes at {:016X} of type {}",
bp->second.len, bp->second.addr, static_cast<int>(type));
Memory::WriteBlock(bp->second.addr, bp->second.inst.data(), bp->second.inst.size());
Core::System::GetInstance().InvalidateCpuInstructionCaches();
p.erase(addr);
}
BreakpointAddress GetNextBreakpointFromAddress(PAddr addr, BreakpointType type) {
std::map<u64, Breakpoint>& p = GetBreakpointList(type);
auto next_breakpoint = p.lower_bound(static_cast<u64>(addr));
BreakpointAddress GetNextBreakpointFromAddress(VAddr addr, BreakpointType type) {
const BreakpointMap& p = GetBreakpointMap(type);
const auto next_breakpoint = p.lower_bound(addr);
BreakpointAddress breakpoint;
if (next_breakpoint != p.end()) {
@@ -469,36 +475,38 @@ BreakpointAddress GetNextBreakpointFromAddress(PAddr addr, BreakpointType type)
return breakpoint;
}
bool CheckBreakpoint(PAddr addr, BreakpointType type) {
bool CheckBreakpoint(VAddr addr, BreakpointType type) {
if (!IsConnected()) {
return false;
}
std::map<u64, Breakpoint>& p = GetBreakpointList(type);
const BreakpointMap& p = GetBreakpointMap(type);
const auto bp = p.find(addr);
auto bp = p.find(static_cast<u64>(addr));
if (bp != p.end()) {
u64 len = bp->second.len;
if (bp == p.end()) {
return false;
}
// IDA Pro defaults to 4-byte breakpoints for all non-hardware breakpoints
// no matter if it's a 4-byte or 2-byte instruction. When you execute a
// Thumb instruction with a 4-byte breakpoint set, it will set a breakpoint on
// two instructions instead of the single instruction you placed the breakpoint
// on. So, as a way to make sure that execution breakpoints are only breaking
// on the instruction that was specified, set the length of an execution
// breakpoint to 1. This should be fine since the CPU should never begin executing
// an instruction anywhere except the beginning of the instruction.
if (type == BreakpointType::Execute) {
len = 1;
}
u64 len = bp->second.len;
if (bp->second.active && (addr >= bp->second.addr && addr < bp->second.addr + len)) {
LOG_DEBUG(Debug_GDBStub,
"Found breakpoint type {} @ {:016X}, range: {:016X}"
" - {:016X} ({:X} bytes)",
static_cast<int>(type), addr, bp->second.addr, bp->second.addr + len, len);
return true;
}
// IDA Pro defaults to 4-byte breakpoints for all non-hardware breakpoints
// no matter if it's a 4-byte or 2-byte instruction. When you execute a
// Thumb instruction with a 4-byte breakpoint set, it will set a breakpoint on
// two instructions instead of the single instruction you placed the breakpoint
// on. So, as a way to make sure that execution breakpoints are only breaking
// on the instruction that was specified, set the length of an execution
// breakpoint to 1. This should be fine since the CPU should never begin executing
// an instruction anywhere except the beginning of the instruction.
if (type == BreakpointType::Execute) {
len = 1;
}
if (bp->second.active && (addr >= bp->second.addr && addr < bp->second.addr + len)) {
LOG_DEBUG(Debug_GDBStub,
"Found breakpoint type {} @ {:016X}, range: {:016X}"
" - {:016X} ({:X} bytes)",
static_cast<int>(type), addr, bp->second.addr, bp->second.addr + len, len);
return true;
}
return false;
@@ -932,6 +940,7 @@ static void WriteMemory() {
GdbHexToMem(data.data(), len_pos + 1, len);
Memory::WriteBlock(addr, data.data(), len);
Core::System::GetInstance().InvalidateCpuInstructionCaches();
SendReply("OK");
}
@@ -951,6 +960,7 @@ static void Step() {
step_loop = true;
halt_loop = true;
send_trap = true;
Core::System::GetInstance().InvalidateCpuInstructionCaches();
}
/// Tell the CPU if we hit a memory breakpoint.
@@ -967,6 +977,7 @@ static void Continue() {
memory_break = false;
step_loop = false;
halt_loop = false;
Core::System::GetInstance().InvalidateCpuInstructionCaches();
}
/**
@@ -976,13 +987,17 @@ static void Continue() {
* @param addr Address of breakpoint.
* @param len Length of breakpoint.
*/
static bool CommitBreakpoint(BreakpointType type, PAddr addr, u64 len) {
std::map<u64, Breakpoint>& p = GetBreakpointList(type);
static bool CommitBreakpoint(BreakpointType type, VAddr addr, u64 len) {
BreakpointMap& p = GetBreakpointMap(type);
Breakpoint breakpoint;
breakpoint.active = true;
breakpoint.addr = addr;
breakpoint.len = len;
Memory::ReadBlock(addr, breakpoint.inst.data(), breakpoint.inst.size());
static constexpr std::array<u8, 4> btrap{{0xd4, 0x20, 0x7d, 0x0}};
Memory::WriteBlock(addr, btrap.data(), btrap.size());
Core::System::GetInstance().InvalidateCpuInstructionCaches();
p.insert({addr, breakpoint});
LOG_DEBUG(Debug_GDBStub, "gdb: added {} breakpoint: {:016X} bytes at {:016X}",
@@ -1016,7 +1031,7 @@ static void AddBreakpoint() {
auto start_offset = command_buffer + 3;
auto addr_pos = std::find(start_offset, command_buffer + command_length, ',');
PAddr addr = HexToLong(start_offset, static_cast<u64>(addr_pos - start_offset));
VAddr addr = HexToLong(start_offset, static_cast<u64>(addr_pos - start_offset));
start_offset = addr_pos + 1;
u64 len =
@@ -1065,7 +1080,7 @@ static void RemoveBreakpoint() {
auto start_offset = command_buffer + 3;
auto addr_pos = std::find(start_offset, command_buffer + command_length, ',');
PAddr addr = HexToLong(start_offset, static_cast<u64>(addr_pos - start_offset));
VAddr addr = HexToLong(start_offset, static_cast<u64>(addr_pos - start_offset));
if (type == BreakpointType::Access) {
// Access is made up of Read and Write types, so add both breakpoints

View File

@@ -22,7 +22,7 @@ enum class BreakpointType {
};
struct BreakpointAddress {
PAddr address;
VAddr address;
BreakpointType type;
};
@@ -53,7 +53,7 @@ bool IsServerEnabled();
bool IsConnected();
/// Register module.
void RegisterModule(std::string name, PAddr beg, PAddr end, bool add_elf_ext = true);
void RegisterModule(std::string name, VAddr beg, VAddr end, bool add_elf_ext = true);
/**
* Signal to the gdbstub server that it should halt CPU execution.
@@ -74,7 +74,7 @@ void HandlePacket();
* @param addr Address to search from.
* @param type Type of breakpoint.
*/
BreakpointAddress GetNextBreakpointFromAddress(PAddr addr, GDBStub::BreakpointType type);
BreakpointAddress GetNextBreakpointFromAddress(VAddr addr, GDBStub::BreakpointType type);
/**
* Check if a breakpoint of the specified type exists at the given address.
@@ -82,7 +82,7 @@ BreakpointAddress GetNextBreakpointFromAddress(PAddr addr, GDBStub::BreakpointTy
* @param addr Address of breakpoint.
* @param type Type of breakpoint.
*/
bool CheckBreakpoint(PAddr addr, GDBStub::BreakpointType type);
bool CheckBreakpoint(VAddr addr, GDBStub::BreakpointType type);
/// If set to true, the CPU will halt at the beginning of the next CPU loop.
bool GetCpuHaltFlag();

View File

@@ -5,15 +5,18 @@
#pragma once
#include <array>
#include <cstring>
#include <memory>
#include <tuple>
#include <type_traits>
#include <utility>
#include "common/assert.h"
#include "common/common_types.h"
#include "core/hle/ipc.h"
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/client_session.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/server_port.h"
namespace IPC {

View File

@@ -9,7 +9,7 @@
#include "common/common_types.h"
#include "core/core.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/result.h"
@@ -32,9 +32,8 @@ static ResultCode WaitForAddress(VAddr address, s64 timeout) {
}
// Gets the threads waiting on an address.
static void GetThreadsWaitingOnAddress(std::vector<SharedPtr<Thread>>& waiting_threads,
VAddr address) {
auto RetrieveWaitingThreads =
static std::vector<SharedPtr<Thread>> GetThreadsWaitingOnAddress(VAddr address) {
const auto RetrieveWaitingThreads =
[](size_t core_index, std::vector<SharedPtr<Thread>>& waiting_threads, VAddr arb_addr) {
const auto& scheduler = Core::System::GetInstance().Scheduler(core_index);
auto& thread_list = scheduler->GetThreadList();
@@ -45,16 +44,20 @@ static void GetThreadsWaitingOnAddress(std::vector<SharedPtr<Thread>>& waiting_t
}
};
// Retrieve a list of all threads that are waiting for this address.
RetrieveWaitingThreads(0, waiting_threads, address);
RetrieveWaitingThreads(1, waiting_threads, address);
RetrieveWaitingThreads(2, waiting_threads, address);
RetrieveWaitingThreads(3, waiting_threads, address);
// Retrieve all threads that are waiting for this address.
std::vector<SharedPtr<Thread>> threads;
RetrieveWaitingThreads(0, threads, address);
RetrieveWaitingThreads(1, threads, address);
RetrieveWaitingThreads(2, threads, address);
RetrieveWaitingThreads(3, threads, address);
// Sort them by priority, such that the highest priority ones come first.
std::sort(waiting_threads.begin(), waiting_threads.end(),
std::sort(threads.begin(), threads.end(),
[](const SharedPtr<Thread>& lhs, const SharedPtr<Thread>& rhs) {
return lhs->current_priority < rhs->current_priority;
});
return threads;
}
// Wake up num_to_wake (or all) threads in a vector.
@@ -76,9 +79,7 @@ static void WakeThreads(std::vector<SharedPtr<Thread>>& waiting_threads, s32 num
// Signals an address being waited on.
ResultCode SignalToAddress(VAddr address, s32 num_to_wake) {
// Get threads waiting on the address.
std::vector<SharedPtr<Thread>> waiting_threads;
GetThreadsWaitingOnAddress(waiting_threads, address);
std::vector<SharedPtr<Thread>> waiting_threads = GetThreadsWaitingOnAddress(address);
WakeThreads(waiting_threads, num_to_wake);
return RESULT_SUCCESS;
@@ -110,12 +111,11 @@ ResultCode ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 valu
}
// Get threads waiting on the address.
std::vector<SharedPtr<Thread>> waiting_threads;
GetThreadsWaitingOnAddress(waiting_threads, address);
std::vector<SharedPtr<Thread>> waiting_threads = GetThreadsWaitingOnAddress(address);
// Determine the modified value depending on the waiting count.
s32 updated_value;
if (waiting_threads.size() == 0) {
if (waiting_threads.empty()) {
updated_value = value - 1;
} else if (num_to_wake <= 0 || waiting_threads.size() <= static_cast<u32>(num_to_wake)) {
updated_value = value + 1;

View File

@@ -8,14 +8,14 @@
#include "core/hle/kernel/client_session.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/server_port.h"
#include "core/hle/kernel/server_session.h"
namespace Kernel {
ClientPort::ClientPort() {}
ClientPort::~ClientPort() {}
ClientPort::ClientPort() = default;
ClientPort::~ClientPort() = default;
ResultVal<SharedPtr<ClientSession>> ClientPort::Connect() {
// Note: Threads do not wait for the server endpoint to call
@@ -40,4 +40,12 @@ ResultVal<SharedPtr<ClientSession>> ClientPort::Connect() {
return MakeResult(std::get<SharedPtr<ClientSession>>(sessions));
}
void ClientPort::ConnectionClosed() {
if (active_sessions == 0) {
return;
}
--active_sessions;
}
} // namespace Kernel

View File

@@ -6,7 +6,7 @@
#include <string>
#include "common/common_types.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/object.h"
#include "core/hle/result.h"
namespace Kernel {
@@ -37,14 +37,20 @@ public:
*/
ResultVal<SharedPtr<ClientSession>> Connect();
SharedPtr<ServerPort> server_port; ///< ServerPort associated with this client port.
u32 max_sessions; ///< Maximum number of simultaneous sessions the port can have
u32 active_sessions; ///< Number of currently open sessions to this port
std::string name; ///< Name of client port (optional)
/**
* Signifies that a previously active connection has been closed,
* decreasing the total number of active connections to this port.
*/
void ConnectionClosed();
private:
ClientPort();
~ClientPort() override;
SharedPtr<ServerPort> server_port; ///< ServerPort associated with this client port.
u32 max_sessions = 0; ///< Maximum number of simultaneous sessions the port can have
u32 active_sessions = 0; ///< Number of currently open sessions to this port
std::string name; ///< Name of client port (optional)
};
} // namespace Kernel

View File

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

View File

@@ -5,7 +5,7 @@
#include <algorithm>
#include "common/assert.h"
#include "core/hle/kernel/event.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/thread.h"
namespace Kernel {

View File

@@ -5,7 +5,7 @@
#pragma once
#include "common/common_types.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/wait_object.h"
namespace Kernel {
@@ -31,10 +31,9 @@ public:
return HANDLE_TYPE;
}
ResetType reset_type; ///< Current ResetType
bool signaled; ///< Whether the event has already been signaled
std::string name; ///< Name of event (optional)
ResetType GetResetType() const {
return reset_type;
}
bool ShouldWait(Thread* thread) const override;
void Acquire(Thread* thread) override;
@@ -47,6 +46,11 @@ public:
private:
Event();
~Event() override;
ResetType reset_type; ///< Current ResetType
bool signaled; ///< Whether the event has already been signaled
std::string name; ///< Name of event (optional)
};
} // namespace Kernel

View File

@@ -8,7 +8,6 @@
#include "core/core.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/thread.h"

View File

@@ -7,7 +7,7 @@
#include <array>
#include <cstddef>
#include "common/common_types.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/object.h"
#include "core/hle/result.h"
namespace Kernel {

View File

@@ -3,17 +3,21 @@
// Refer to the license.txt file included.
#include <algorithm>
#include <array>
#include <sstream>
#include <utility>
#include <boost/range/algorithm_ext/erase.hpp>
#include "common/assert.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/logging/log.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/event.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/server_session.h"
#include "core/memory.h"
@@ -197,7 +201,7 @@ ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(u32_le* src_cmdb
return RESULT_SUCCESS;
}
ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) {
ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(const Thread& thread) {
std::array<u32, IPC::COMMAND_BUFFER_LENGTH> dst_cmdbuf;
Memory::ReadBlock(*thread.owner_process, thread.GetTLSAddress(), dst_cmdbuf.data(),
dst_cmdbuf.size() * sizeof(u32));

View File

@@ -13,7 +13,7 @@
#include "common/common_types.h"
#include "common/swap.h"
#include "core/hle/ipc.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/server_session.h"
#include "core/hle/kernel/thread.h"
@@ -132,7 +132,7 @@ public:
ResultCode PopulateFromIncomingCommandBuffer(u32_le* src_cmdbuf, Process& src_process,
HandleTable& src_table);
/// Writes data from this context back to the requesting process/thread.
ResultCode WriteToOutgoingCommandBuffer(Thread& thread);
ResultCode WriteToOutgoingCommandBuffer(const Thread& thread);
u32_le GetCommand() const {
return command;

View File

@@ -4,7 +4,6 @@
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/memory.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/resource_limit.h"
#include "core/hle/kernel/thread.h"
@@ -15,9 +14,7 @@ namespace Kernel {
unsigned int Object::next_object_id;
/// Initialize the kernel
void Init(u32 system_mode) {
Kernel::MemoryInit(system_mode);
void Init() {
Kernel::ResourceLimitsInit();
Kernel::ThreadingInit();
Kernel::TimersInit();
@@ -37,7 +34,6 @@ void Shutdown() {
Kernel::TimersShutdown();
Kernel::ResourceLimitsShutdown();
Kernel::MemoryShutdown();
}
} // namespace Kernel

View File

@@ -4,122 +4,12 @@
#pragma once
#include <cstddef>
#include <string>
#include <utility>
#include <boost/smart_ptr/intrusive_ptr.hpp>
#include "common/assert.h"
#include "common/common_types.h"
namespace Kernel {
using Handle = u32;
enum class HandleType : u32 {
Unknown,
Event,
SharedMemory,
Thread,
Process,
AddressArbiter,
Timer,
ResourceLimit,
CodeSet,
ClientPort,
ServerPort,
ClientSession,
ServerSession,
};
enum class ResetType {
OneShot,
Sticky,
Pulse,
};
class Object : NonCopyable {
public:
virtual ~Object() {}
/// Returns a unique identifier for the object. For debugging purposes only.
unsigned int GetObjectId() const {
return object_id;
}
virtual std::string GetTypeName() const {
return "[BAD KERNEL OBJECT TYPE]";
}
virtual std::string GetName() const {
return "[UNKNOWN KERNEL OBJECT]";
}
virtual Kernel::HandleType GetHandleType() const = 0;
/**
* Check if a thread can wait on the object
* @return True if a thread can wait on the object, otherwise false
*/
bool IsWaitable() const {
switch (GetHandleType()) {
case HandleType::Event:
case HandleType::Thread:
case HandleType::Timer:
case HandleType::ServerPort:
case HandleType::ServerSession:
return true;
case HandleType::Unknown:
case HandleType::SharedMemory:
case HandleType::Process:
case HandleType::AddressArbiter:
case HandleType::ResourceLimit:
case HandleType::CodeSet:
case HandleType::ClientPort:
case HandleType::ClientSession:
return false;
}
UNREACHABLE();
}
public:
static unsigned int next_object_id;
private:
friend void intrusive_ptr_add_ref(Object*);
friend void intrusive_ptr_release(Object*);
unsigned int ref_count = 0;
unsigned int object_id = next_object_id++;
};
// Special functions used by boost::instrusive_ptr to do automatic ref-counting
inline void intrusive_ptr_add_ref(Object* object) {
++object->ref_count;
}
inline void intrusive_ptr_release(Object* object) {
if (--object->ref_count == 0) {
delete object;
}
}
template <typename T>
using SharedPtr = boost::intrusive_ptr<T>;
/**
* Attempts to downcast the given Object pointer to a pointer to T.
* @return Derived pointer to the object, or `nullptr` if `object` isn't of type T.
*/
template <typename T>
inline SharedPtr<T> DynamicObjectCast(SharedPtr<Object> object) {
if (object != nullptr && object->GetHandleType() == T::HANDLE_TYPE) {
return boost::static_pointer_cast<T>(std::move(object));
}
return nullptr;
}
/// Initialize the kernel with the specified system mode.
void Init(u32 system_mode);
void Init();
/// Shutdown the kernel
void Shutdown();

View File

@@ -1,90 +0,0 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <cinttypes>
#include <memory>
#include <utility>
#include <vector>
#include "common/assert.h"
#include "common/common_types.h"
#include "common/logging/log.h"
#include "core/hle/kernel/memory.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/vm_manager.h"
#include "core/memory.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace Kernel {
MemoryRegionInfo memory_regions[3];
/// Size of the APPLICATION, SYSTEM and BASE memory regions (respectively) for each system
/// memory configuration type.
static const u32 memory_region_sizes[8][3] = {
// Old 3DS layouts
{0x04000000, 0x02C00000, 0x01400000}, // 0
{/* This appears to be unused. */}, // 1
{0x06000000, 0x00C00000, 0x01400000}, // 2
{0x05000000, 0x01C00000, 0x01400000}, // 3
{0x04800000, 0x02400000, 0x01400000}, // 4
{0x02000000, 0x04C00000, 0x01400000}, // 5
// New 3DS layouts
{0x07C00000, 0x06400000, 0x02000000}, // 6
{0x0B200000, 0x02E00000, 0x02000000}, // 7
};
void MemoryInit(u32 mem_type) {
// TODO(yuriks): On the n3DS, all o3DS configurations (<=5) are forced to 6 instead.
ASSERT_MSG(mem_type <= 5, "New 3DS memory configuration aren't supported yet!");
ASSERT(mem_type != 1);
// The kernel allocation regions (APPLICATION, SYSTEM and BASE) are laid out in sequence, with
// the sizes specified in the memory_region_sizes table.
VAddr base = 0;
for (int i = 0; i < 3; ++i) {
memory_regions[i].base = base;
memory_regions[i].size = memory_region_sizes[mem_type][i];
memory_regions[i].used = 0;
memory_regions[i].linear_heap_memory = std::make_shared<std::vector<u8>>();
// Reserve enough space for this region of FCRAM.
// We do not want this block of memory to be relocated when allocating from it.
memory_regions[i].linear_heap_memory->reserve(memory_regions[i].size);
base += memory_regions[i].size;
}
// We must've allocated the entire FCRAM by the end
ASSERT(base == Memory::FCRAM_SIZE);
}
void MemoryShutdown() {
for (auto& region : memory_regions) {
region.base = 0;
region.size = 0;
region.used = 0;
region.linear_heap_memory = nullptr;
}
}
MemoryRegionInfo* GetMemoryRegion(MemoryRegion region) {
switch (region) {
case MemoryRegion::APPLICATION:
return &memory_regions[0];
case MemoryRegion::SYSTEM:
return &memory_regions[1];
case MemoryRegion::BASE:
return &memory_regions[2];
default:
UNREACHABLE();
}
}
void HandleSpecialMapping(VMManager& address_space, const AddressMapping& mapping) {}
void MapSharedPages(VMManager& address_space) {}
} // namespace Kernel

View File

@@ -1,34 +0,0 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <memory>
#include <vector>
#include "common/common_types.h"
namespace Kernel {
class VMManager;
enum class MemoryRegion : u16;
struct AddressMapping;
struct MemoryRegionInfo {
u64 base; // Not an address, but offset from start of FCRAM
u64 size;
u64 used;
std::shared_ptr<std::vector<u8>> linear_heap_memory;
};
void MemoryInit(u32 mem_type);
void MemoryShutdown();
MemoryRegionInfo* GetMemoryRegion(MemoryRegion region);
void HandleSpecialMapping(VMManager& address_space, const AddressMapping& mapping);
void MapSharedPages(VMManager& address_space);
extern MemoryRegionInfo memory_regions[3];
} // namespace Kernel

View File

@@ -12,8 +12,8 @@
#include "core/core.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/mutex.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/result.h"

View File

@@ -5,7 +5,7 @@
#pragma once
#include "common/common_types.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/object.h"
union ResultCode;

View File

@@ -0,0 +1,35 @@
// Copyright 2018 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/assert.h"
#include "core/hle/kernel/object.h"
namespace Kernel {
Object::~Object() = default;
bool Object::IsWaitable() const {
switch (GetHandleType()) {
case HandleType::Event:
case HandleType::Thread:
case HandleType::Timer:
case HandleType::ServerPort:
case HandleType::ServerSession:
return true;
case HandleType::Unknown:
case HandleType::SharedMemory:
case HandleType::Process:
case HandleType::AddressArbiter:
case HandleType::ResourceLimit:
case HandleType::CodeSet:
case HandleType::ClientPort:
case HandleType::ClientSession:
return false;
}
UNREACHABLE();
}
} // namespace Kernel

View File

@@ -0,0 +1,100 @@
// Copyright 2018 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <string>
#include <utility>
#include <boost/smart_ptr/intrusive_ptr.hpp>
#include "common/common_types.h"
namespace Kernel {
using Handle = u32;
enum class HandleType : u32 {
Unknown,
Event,
SharedMemory,
Thread,
Process,
AddressArbiter,
Timer,
ResourceLimit,
CodeSet,
ClientPort,
ServerPort,
ClientSession,
ServerSession,
};
enum class ResetType {
OneShot,
Sticky,
Pulse,
};
class Object : NonCopyable {
public:
virtual ~Object();
/// Returns a unique identifier for the object. For debugging purposes only.
unsigned int GetObjectId() const {
return object_id;
}
virtual std::string GetTypeName() const {
return "[BAD KERNEL OBJECT TYPE]";
}
virtual std::string GetName() const {
return "[UNKNOWN KERNEL OBJECT]";
}
virtual HandleType GetHandleType() const = 0;
/**
* Check if a thread can wait on the object
* @return True if a thread can wait on the object, otherwise false
*/
bool IsWaitable() const;
public:
static unsigned int next_object_id;
private:
friend void intrusive_ptr_add_ref(Object*);
friend void intrusive_ptr_release(Object*);
unsigned int ref_count = 0;
unsigned int object_id = next_object_id++;
};
// Special functions used by boost::instrusive_ptr to do automatic ref-counting
inline void intrusive_ptr_add_ref(Object* object) {
++object->ref_count;
}
inline void intrusive_ptr_release(Object* object) {
if (--object->ref_count == 0) {
delete object;
}
}
template <typename T>
using SharedPtr = boost::intrusive_ptr<T>;
/**
* Attempts to downcast the given Object pointer to a pointer to T.
* @return Derived pointer to the object, or `nullptr` if `object` isn't of type T.
*/
template <typename T>
inline SharedPtr<T> DynamicObjectCast(SharedPtr<Object> object) {
if (object != nullptr && object->GetHandleType() == T::HANDLE_TYPE) {
return boost::static_pointer_cast<T>(std::move(object));
}
return nullptr;
}
} // namespace Kernel

View File

@@ -8,7 +8,6 @@
#include "common/common_funcs.h"
#include "common/logging/log.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/memory.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/resource_limit.h"
#include "core/hle/kernel/thread.h"
@@ -125,14 +124,6 @@ void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) {
std::make_shared<std::vector<u8>>(stack_size, 0), 0, stack_size,
MemoryState::Mapped)
.Unwrap();
misc_memory_used += stack_size;
memory_region->used += stack_size;
// Map special address mappings
MapSharedPages(vm_manager);
for (const auto& mapping : address_mappings) {
HandleSpecialMapping(vm_manager, mapping);
}
vm_manager.LogLayout();
status = ProcessStatus::Running;
@@ -141,37 +132,19 @@ void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) {
}
void Process::LoadModule(SharedPtr<CodeSet> module_, VAddr base_addr) {
memory_region = GetMemoryRegion(flags.memory_region);
auto MapSegment = [&](CodeSet::Segment& segment, VMAPermission permissions,
MemoryState memory_state) {
const auto MapSegment = [&](CodeSet::Segment& segment, VMAPermission permissions,
MemoryState memory_state) {
auto vma = vm_manager
.MapMemoryBlock(segment.addr + base_addr, module_->memory, segment.offset,
segment.size, memory_state)
.Unwrap();
vm_manager.Reprotect(vma, permissions);
misc_memory_used += segment.size;
memory_region->used += segment.size;
};
// Map CodeSet segments
MapSegment(module_->code, VMAPermission::ReadExecute, MemoryState::CodeStatic);
MapSegment(module_->rodata, VMAPermission::Read, MemoryState::CodeMutable);
MapSegment(module_->data, VMAPermission::ReadWrite, MemoryState::CodeMutable);
}
VAddr Process::GetLinearHeapAreaAddress() const {
// Starting from system version 8.0.0 a new linear heap layout is supported to allow usage of
// the extra RAM in the n3DS.
return kernel_version < 0x22C ? Memory::LINEAR_HEAP_VADDR : Memory::NEW_LINEAR_HEAP_VADDR;
}
VAddr Process::GetLinearHeapBase() const {
return GetLinearHeapAreaAddress() + memory_region->base;
}
VAddr Process::GetLinearHeapLimit() const {
return GetLinearHeapBase() + memory_region->size;
MapSegment(module_->CodeSegment(), VMAPermission::ReadExecute, MemoryState::CodeStatic);
MapSegment(module_->RODataSegment(), VMAPermission::Read, MemoryState::CodeMutable);
MapSegment(module_->DataSegment(), VMAPermission::ReadWrite, MemoryState::CodeMutable);
}
ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission perms) {
@@ -206,7 +179,6 @@ ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission per
vm_manager.Reprotect(vma, perms);
heap_used = size;
memory_region->used += size;
return MakeResult<VAddr>(heap_end - size);
}
@@ -226,52 +198,6 @@ ResultCode Process::HeapFree(VAddr target, u32 size) {
return result;
heap_used -= size;
memory_region->used -= size;
return RESULT_SUCCESS;
}
ResultVal<VAddr> Process::LinearAllocate(VAddr target, u32 size, VMAPermission perms) {
UNIMPLEMENTED();
return {};
}
ResultCode Process::LinearFree(VAddr target, u32 size) {
auto& linheap_memory = memory_region->linear_heap_memory;
if (target < GetLinearHeapBase() || target + size > GetLinearHeapLimit() ||
target + size < target) {
return ERR_INVALID_ADDRESS;
}
if (size == 0) {
return RESULT_SUCCESS;
}
VAddr heap_end = GetLinearHeapBase() + (u32)linheap_memory->size();
if (target + size > heap_end) {
return ERR_INVALID_ADDRESS_STATE;
}
ResultCode result = vm_manager.UnmapRange(target, size);
if (result.IsError())
return result;
linear_heap_used -= size;
memory_region->used -= size;
if (target + size == heap_end) {
// End of linear heap has been freed, so check what's the last allocated block in it and
// reduce the size.
auto vma = vm_manager.FindVMA(target);
ASSERT(vma != vm_manager.vma_map.end());
ASSERT(vma->second.type == VMAType::Free);
VAddr new_end = vma->second.base;
if (new_end >= GetLinearHeapBase()) {
linheap_memory->resize(new_end - GetLinearHeapBase());
}
}
return RESULT_SUCCESS;
}

View File

@@ -4,6 +4,7 @@
#pragma once
#include <array>
#include <bitset>
#include <cstddef>
#include <memory>
@@ -12,7 +13,7 @@
#include <boost/container/static_vector.hpp>
#include "common/bit_field.h"
#include "common/common_types.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/kernel/vm_manager.h"
@@ -53,9 +54,14 @@ union ProcessFlags {
enum class ProcessStatus { Created, Running, Exited };
class ResourceLimit;
struct MemoryRegionInfo;
struct CodeSet final : public Object {
struct Segment {
size_t offset = 0;
VAddr addr = 0;
u32 size = 0;
};
static SharedPtr<CodeSet> Create(std::string name);
std::string GetTypeName() const override {
@@ -70,24 +76,38 @@ struct CodeSet final : public Object {
return HANDLE_TYPE;
}
/// Name of the process
std::string name;
Segment& CodeSegment() {
return segments[0];
}
const Segment& CodeSegment() const {
return segments[0];
}
Segment& RODataSegment() {
return segments[1];
}
const Segment& RODataSegment() const {
return segments[1];
}
Segment& DataSegment() {
return segments[2];
}
const Segment& DataSegment() const {
return segments[2];
}
std::shared_ptr<std::vector<u8>> memory;
struct Segment {
size_t offset = 0;
VAddr addr = 0;
u32 size = 0;
};
Segment segments[3];
Segment& code = segments[0];
Segment& rodata = segments[1];
Segment& data = segments[2];
std::array<Segment, 3> segments;
VAddr entrypoint;
/// Name of the process
std::string name;
private:
CodeSet();
~CodeSet() override;
@@ -163,12 +183,11 @@ public:
// This makes deallocation and reallocation of holes fast and keeps process memory contiguous
// in the emulator address space, allowing Memory::GetPointer to be reasonably safe.
std::shared_ptr<std::vector<u8>> heap_memory;
// The left/right bounds of the address space covered by heap_memory.
VAddr heap_start = 0, heap_end = 0;
u64 heap_used = 0, linear_heap_used = 0, misc_memory_used = 0;
MemoryRegionInfo* memory_region = nullptr;
VAddr heap_start = 0;
VAddr heap_end = 0;
u64 heap_used = 0;
/// The Thread Local Storage area is allocated as processes create threads,
/// each TLS area is 0x200 bytes, so one page (0x1000) is split up in 8 parts, and each part
@@ -179,16 +198,9 @@ public:
std::string name;
VAddr GetLinearHeapAreaAddress() const;
VAddr GetLinearHeapBase() const;
VAddr GetLinearHeapLimit() const;
ResultVal<VAddr> HeapAllocate(VAddr target, u64 size, VMAPermission perms);
ResultCode HeapFree(VAddr target, u32 size);
ResultVal<VAddr> LinearAllocate(VAddr target, u32 size, VMAPermission perms);
ResultCode LinearFree(VAddr target, u32 size);
ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size);
ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size);

View File

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

View File

@@ -8,6 +8,7 @@
#include <vector>
#include "common/common_types.h"
#include "common/thread_queue_list.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/thread.h"
class ARM_Interface;

View File

@@ -6,7 +6,7 @@
#include "common/assert.h"
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/server_port.h"
#include "core/hle/kernel/server_session.h"
#include "core/hle/kernel/thread.h"

View File

@@ -9,7 +9,7 @@
#include <tuple>
#include <vector>
#include "common/common_types.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/wait_object.h"
namespace Kernel {

View File

@@ -27,7 +27,7 @@ ServerSession::~ServerSession() {
// Decrease the port's connection count.
if (parent->port)
parent->port->active_sessions--;
parent->port->ConnectionClosed();
// TODO(Subv): Wake up all the ClientSession's waiting threads and set
// the SendSyncRequest result to 0xC920181A.

View File

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

View File

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

View File

@@ -8,7 +8,6 @@
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/memory.h"
#include "core/hle/kernel/shared_memory.h"
#include "core/memory.h"
@@ -30,35 +29,17 @@ SharedPtr<SharedMemory> SharedMemory::Create(SharedPtr<Process> owner_process, u
shared_memory->other_permissions = other_permissions;
if (address == 0) {
// We need to allocate a block from the Linear Heap ourselves.
// We'll manually allocate some memory from the linear heap in the specified region.
MemoryRegionInfo* memory_region = GetMemoryRegion(region);
auto& linheap_memory = memory_region->linear_heap_memory;
ASSERT_MSG(linheap_memory->size() + size <= memory_region->size,
"Not enough space in region to allocate shared memory!");
shared_memory->backing_block = linheap_memory;
shared_memory->backing_block_offset = linheap_memory->size();
// Allocate some memory from the end of the linear heap for this region.
linheap_memory->insert(linheap_memory->end(), size, 0);
memory_region->used += size;
shared_memory->linear_heap_phys_address =
Memory::FCRAM_PADDR + memory_region->base +
static_cast<PAddr>(shared_memory->backing_block_offset);
// Increase the amount of used linear heap memory for the owner process.
if (shared_memory->owner_process != nullptr) {
shared_memory->owner_process->linear_heap_used += size;
}
shared_memory->backing_block = std::make_shared<std::vector<u8>>(size);
shared_memory->backing_block_offset = 0;
// Refresh the address mappings for the current process.
if (Core::CurrentProcess() != nullptr) {
Core::CurrentProcess()->vm_manager.RefreshMemoryBlockMappings(linheap_memory.get());
Core::CurrentProcess()->vm_manager.RefreshMemoryBlockMappings(
shared_memory->backing_block.get());
}
} else {
auto& vm_manager = shared_memory->owner_process->vm_manager;
// The memory is already available and mapped in the owner process.
auto vma = vm_manager.FindVMA(address);
ASSERT_MSG(vma != vm_manager.vma_map.end(), "Invalid memory address");
@@ -74,6 +55,7 @@ SharedPtr<SharedMemory> SharedMemory::Create(SharedPtr<Process> owner_process, u
}
shared_memory->base_address = address;
return shared_memory;
}
@@ -124,11 +106,6 @@ ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermi
VAddr target_address = address;
if (base_address == 0 && target_address == 0) {
// Calculate the address at which to map the memory block.
target_address = Memory::PhysicalToVirtualAddress(linear_heap_phys_address).value();
}
// Map the memory block into the target process
auto result = target_process->vm_manager.MapMemoryBlock(
target_address, backing_block, backing_block_offset, size, MemoryState::Shared);

View File

@@ -9,7 +9,7 @@
#include <vector>
#include "common/common_types.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/process.h"
#include "core/hle/result.h"
@@ -111,9 +111,6 @@ public:
SharedPtr<Process> owner_process;
/// Address of shared memory block in the owner process if specified.
VAddr base_address;
/// Physical address of the shared memory block in the linear heap if no address was specified
/// during creation.
PAddr linear_heap_phys_address;
/// Backing memory for this shared memory block.
std::shared_ptr<std::vector<u8>> backing_block;
/// Offset into the backing block for this shared memory.

View File

@@ -267,7 +267,7 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
LOG_TRACE(Kernel_SVC, "called info_id=0x{:X}, info_sub_id=0x{:X}, handle=0x{:08X}", info_id,
info_sub_id, handle);
auto& vm_manager = Core::CurrentProcess()->vm_manager;
const auto& vm_manager = Core::CurrentProcess()->vm_manager;
switch (static_cast<GetInfoType>(info_id)) {
case GetInfoType::AllowedCpuIdBitmask:

View File

@@ -20,8 +20,7 @@
#include "core/core_timing_util.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/memory.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/result.h"
@@ -81,8 +80,8 @@ void Thread::Stop() {
wait_objects.clear();
// Mark the TLS slot in the thread's page as free.
u64 tls_page = (tls_address - Memory::TLS_AREA_VADDR) / Memory::PAGE_SIZE;
u64 tls_slot =
const u64 tls_page = (tls_address - Memory::TLS_AREA_VADDR) / Memory::PAGE_SIZE;
const u64 tls_slot =
((tls_address - Memory::TLS_AREA_VADDR) % Memory::PAGE_SIZE) / Memory::TLS_ENTRY_SIZE;
Core::CurrentProcess()->tls_slots[tls_page].reset(tls_slot);
}
@@ -252,13 +251,14 @@ void Thread::ResumeFromWait() {
* slot: The index of the first free slot in the indicated page.
* alloc_needed: Whether there's a need to allocate a new TLS page (All pages are full).
*/
std::tuple<u32, u32, bool> GetFreeThreadLocalSlot(std::vector<std::bitset<8>>& tls_slots) {
static std::tuple<std::size_t, std::size_t, bool> GetFreeThreadLocalSlot(
const std::vector<std::bitset<8>>& tls_slots) {
// Iterate over all the allocated pages, and try to find one where not all slots are used.
for (unsigned page = 0; page < tls_slots.size(); ++page) {
for (std::size_t page = 0; page < tls_slots.size(); ++page) {
const auto& page_tls_slots = tls_slots[page];
if (!page_tls_slots.all()) {
// We found a page with at least one free slot, find which slot it is
for (unsigned slot = 0; slot < page_tls_slots.size(); ++slot) {
for (std::size_t slot = 0; slot < page_tls_slots.size(); ++slot) {
if (!page_tls_slots.test(slot)) {
return std::make_tuple(page, slot, false);
}
@@ -333,42 +333,22 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
// Find the next available TLS index, and mark it as used
auto& tls_slots = owner_process->tls_slots;
bool needs_allocation = true;
u32 available_page; // Which allocated page has free space
u32 available_slot; // Which slot within the page is free
std::tie(available_page, available_slot, needs_allocation) = GetFreeThreadLocalSlot(tls_slots);
auto [available_page, available_slot, needs_allocation] = GetFreeThreadLocalSlot(tls_slots);
if (needs_allocation) {
// There are no already-allocated pages with free slots, lets allocate a new one.
// TLS pages are allocated from the BASE region in the linear heap.
MemoryRegionInfo* memory_region = GetMemoryRegion(MemoryRegion::BASE);
auto& linheap_memory = memory_region->linear_heap_memory;
if (linheap_memory->size() + Memory::PAGE_SIZE > memory_region->size) {
LOG_ERROR(Kernel_SVC,
"Not enough space in region to allocate a new TLS page for thread");
return ERR_OUT_OF_MEMORY;
}
size_t offset = linheap_memory->size();
// Allocate some memory from the end of the linear heap for this region.
linheap_memory->insert(linheap_memory->end(), Memory::PAGE_SIZE, 0);
memory_region->used += Memory::PAGE_SIZE;
owner_process->linear_heap_used += Memory::PAGE_SIZE;
tls_slots.emplace_back(0); // The page is completely available at the start
available_page = static_cast<u32>(tls_slots.size() - 1);
available_page = tls_slots.size() - 1;
available_slot = 0; // Use the first slot in the new page
auto& vm_manager = owner_process->vm_manager;
vm_manager.RefreshMemoryBlockMappings(linheap_memory.get());
// Allocate some memory from the end of the linear heap for this region.
const size_t offset = thread->tls_memory->size();
thread->tls_memory->insert(thread->tls_memory->end(), Memory::PAGE_SIZE, 0);
auto& vm_manager = owner_process->vm_manager;
vm_manager.RefreshMemoryBlockMappings(thread->tls_memory.get());
// Map the page to the current process' address space.
// TODO(Subv): Find the correct MemoryState for this region.
vm_manager.MapMemoryBlock(Memory::TLS_AREA_VADDR + available_page * Memory::PAGE_SIZE,
linheap_memory, offset, Memory::PAGE_SIZE,
thread->tls_memory, 0, Memory::PAGE_SIZE,
MemoryState::ThreadLocal);
}

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