Compare commits

..

100 Commits

Author SHA1 Message Date
Zach Hilman 70e86248fd qt: Install System TitleTypes to System NAND
Fixes an issue where installed system archive NCAs would be installed to user NAND and not recognized by games.
2018-09-27 09:20:06 -04:00
bunnei 7b81e1e525 Merge pull request #1365 from DarkLordZach/lfs
file_sys: Add support for LayeredFS mods
2018-09-25 16:59:44 -04:00
bunnei b67c1fdf38 Merge pull request #1393 from tech4me/svc
svc: Updated svc names
2018-09-25 10:47:12 -04:00
David 367c52ff0d Implemented fatal:u properly (#1347)
* Implemented fatal:u properly

fatal:u now is properly implemented with all the ipc cmds. Error reports/Crash reports are also now implemented for fatal:u. Crash reports save to yuzu/logs/crash_reports/
The register dump is currently known as sysmodules send all zeros. If there are any non zero values for the "registers" or the unknown values, let me know!

* Fatal:U fixups

* Made fatal:u execution break more clear

* Fatal fixups
2018-09-23 22:34:11 -04:00
David 2513e086ab Stubbed IRS (#1349)
* Stubbed IRS

Currently we have no ideal way of implementing IRS. For the time being we should have the functions stubbed until we come up with a way to emulate IRS properly.

* Added IRS to logging backend

* Forward declared shared memory for irs
2018-09-23 22:33:29 -04:00
bunnei f2c1fd08f9 Merge pull request #1354 from ogniK5377/ssl-version
Corrected SSL::SetInterfaceVersion
2018-09-23 22:32:14 -04:00
Zach Hilman b3c2ec362b fsmitm: Cleanup and modernize fsmitm port 2018-09-23 21:50:20 -04:00
tech4me d42424ace0 svc: Updated svc names 2018-09-23 17:03:38 -07:00
David 9f3fc067bf Added glObjectLabels for renderdoc for textures and shader programs (#1384)
* Added glObjectLabels for renderdoc for textures and shader programs

* Changed hardcoded "Texture" name to reflect the texture type instead

* Removed string initialize
2018-09-23 17:55:41 -04:00
bunnei 6b05f71b67 Merge pull request #1387 from FearlessTobi/port-4245
Port citra-emu/citra#4245: "common/thread: remove YieldCPU()"
2018-09-23 17:51:53 -04:00
bunnei 5d9f001bb5 Merge pull request #1385 from FearlessTobi/port-4214
Port citra-emu/citra#4214: "Set citra-qt project as default StartUp Project in Visual Studio"
2018-09-23 17:49:27 -04:00
bunnei d373a65d26 Merge pull request #1391 from ogniK5377/GetAudioRendererState
Added audren:u#GetAudioRendererState
2018-09-23 16:25:12 -04:00
bunnei c6f5c7d291 Merge pull request #1392 from greggameplayer/correct-BC6H
Correction of the ComponentType of BC6H_UF16 & BC6H_SF16
2018-09-23 16:23:56 -04:00
greggameplayer b91e2d55f3 correct BC6H 2018-09-23 19:17:22 +02:00
David Marcec c461188f51 Added audren:u#GetAudioRendererState 2018-09-23 22:32:01 +10:00
bunnei 93fea4e179 Merge pull request #1378 from lioncash/thread
process: Make a few member variables private where applicable
2018-09-22 19:46:53 -04:00
Weiyi Wang e0ce07aa7d common/thread: remove YieldCPU()
simply use the standard library yield()
2018-09-22 23:27:42 +02:00
James Rowe 5659d5d258 Merge pull request #1386 from jroweboy/oops
Build: Reintroduce Appveyor deploy
2018-09-22 13:15:20 -06:00
James Rowe b23b30f9ef Build: Reintroduce Appveyor deploy
This was erroneously removed when we added travis mingw builds. We still
want to push MSVC builds to github for the time being
2018-09-22 13:09:32 -06:00
fearlessTobi f90b75cc4c Port citra-emu/citra#4214: "Set citra-qt project as default StartUp Project in Visual Studio" 2018-09-22 12:24:02 +02:00
bunnei fb9f273e90 Merge pull request #1380 from lioncash/const
shader_bytecode: Make operator== and operator!= of IpaMode const qualified
2018-09-22 01:37:36 -04:00
bunnei 73eea61614 Merge pull request #1382 from lioncash/inc
gl_state: Remove unused type alias
2018-09-22 01:36:21 -04:00
bunnei 0246db9ea2 Merge pull request #1376 from Subv/timestretch_trace
Logging: Change the TimeStretch::Process log from debug to trace level.
2018-09-22 01:30:28 -04:00
bunnei 2d5f51bb9b Merge pull request #1381 from valentinvanelslande/patch-1
yuzu_cmd: fix sd card directory set to nand directory
2018-09-22 01:29:28 -04:00
James Rowe 37bb2c45ee Merge pull request #1383 from DarkLordZach/game-list-interpolation
game_list: Add Qt SmoothTransformation to picture scaling
2018-09-21 20:55:49 -06:00
Zach Hilman 4f183123f5 game_list: Add Qt SmoothTransformation to picture scaling 2018-09-21 21:34:46 -04:00
Zach Hilman ba0873d33c qt: Add UI elements for LayeredFS and related tools 2018-09-21 19:53:33 -04:00
Zach Hilman 050547b801 romfs: Implement CreateRomFS 2018-09-21 19:53:33 -04:00
Zach Hilman 6eada3c57d file_sys: Port Atmosphere-NX fs_mitm implementation 2018-09-21 19:53:33 -04:00
Zach Hilman 940a711caf filesystem: Add LayeredFS VFS directory getter 2018-09-21 19:53:33 -04:00
Zach Hilman 50a470eab8 bis_factory: Add mod directory VFS getter 2018-09-21 19:53:33 -04:00
Zach Hilman 16188acb50 patch_manager: Add LayeredFS mods support 2018-09-21 19:53:33 -04:00
Zach Hilman 44fdac334c vfs_concat: Rewrite and fix ConcatenatedVfsFile 2018-09-21 19:53:33 -04:00
Zach Hilman 3e5c3d0f16 vfs_layered: Add LayeredVfsDirectory
Reads multiple dirs through as if a waterfall.
2018-09-21 19:53:05 -04:00
Zach Hilman b52343a428 vfs_vector: Add VectorVfsFile
Maps a vector into the VFS interface.
2018-09-21 19:53:05 -04:00
Zach Hilman c65d4d119f vfs_static: Add StaticVfsFile
Always returns the template argument byte for all reads. Doesn't support writes.
2018-09-21 19:53:05 -04:00
Zach Hilman f68e324672 vfs: Add and rewite VfsRawCopy functions 2018-09-21 19:53:05 -04:00
Zach Hilman d6cbb3a3e0 vfs: Add GetEntries method
Maps name string to directory or file.
2018-09-21 19:53:05 -04:00
Zach Hilman bd8db3f7f8 common_paths: Add Load and Dump dirs 2018-09-21 19:53:05 -04:00
Lioncash 90746c33c7 gl_state: Remove unused type alias
This isn't used anywhere within the header, so we can remove it, along
with the include that was previously necessary. This also uncovers an
indirect include in the cpp file for the assertion macros.
2018-09-21 18:22:43 -04:00
Valentin Vanelslande 19d9b0778a Update config.cpp 2018-09-21 16:16:51 -05:00
Lioncash a8f5fd787f shader_bytecode: Lay out the Ipa-related enums better
This is more consistent with the surrounding enums.
2018-09-21 16:17:31 -04:00
Lioncash 272517cf7e shader_bytecode: Make operator== and operator!= of IpaMode const qualified
These don't affect the state of the struct and can be const member
functions.
2018-09-21 16:17:27 -04:00
bunnei 4f186de069 Merge pull request #1379 from lioncash/bitwise
gl_stream_buffer: Fix use of bitwise OR instead of logical OR in Map()
2018-09-21 14:02:00 -04:00
Lioncash ba02dd9ebc gl_stream_buffer: Fix use of bitwise OR instead of logical OR in Map()
This was very likely intended to be a logical OR based off the
conditioning and testing of inversion in one case.

Even if this was intentional, this is the kind of non-obvious thing one
should be clarifying with a comment.
2018-09-21 07:59:03 -04:00
Lioncash 48b2eda492 svc: Move most process termination code to its own function within Process
Reduces the use of Process class members externally and keeps most code
related to tearing down a process with the rest of the process code.
2018-09-21 06:07:41 -04:00
Lioncash acfc801d14 thread/process: Move TLS slot marking/freeing to the process class
Allows making several members of the process class private, it also
avoids going through Core::CurrentProcess() just to retrieve the owning
process.
2018-09-21 03:50:12 -04:00
David 1db1e013e0 Added support for uncompressed NSOs (#1374)
* Added support for uncompressed NSOs

* Moved compressed section check to NsoHeader
2018-09-21 00:39:30 -04:00
James Rowe 8606995515 Merge pull request #1225 from tech4me/travis-windows
travis: running mingw build on travis ci
2018-09-20 21:39:15 -06:00
bunnei 000da01c1d Merge pull request #1337 from DarkLordZach/create-fs-cmd
yuzu-cmd: Add filesystem service initializer call
2018-09-20 23:36:11 -04:00
bunnei 0285ddfbd4 Merge pull request #1372 from lioncash/thread
kernel/thread: Use owner_process when setting the page table in SetupMainThread()
2018-09-20 23:35:29 -04:00
bunnei 072053ab95 Merge pull request #1371 from lioncash/fwd-arm
arm_interface: Replace kernel vm_manager include with a forward declaration
2018-09-20 23:35:06 -04:00
bunnei a373dddbd9 Merge pull request #1375 from Subv/gl_clear
RasterizerGL: Use the correct framebuffer when clearing via the CLEAR_BUFFERS register.
2018-09-20 23:34:39 -04:00
Subv 8ba21e28cf Logging: Change the TimeStretch::Process log from debug to trace level.
This function is called too many times and makes the debug logging basically unusable due to the spam.
2018-09-20 22:33:54 -05:00
Subv 9cd5c61fcf RasterizerGL: Use the correct framebuffer when clearing via the CLEAR_BUFFERS register.
Previously we were clearing the default backbuffer framebuffer.

Found thanks to a Piglit test :)
2018-09-20 22:31:53 -05:00
bunnei c1c59617ad Merge pull request #1364 from lioncash/content
file-sys: Default heavy-weight class destructors in the cpp file
2018-09-20 23:31:31 -04:00
bunnei cf97d00eb6 Merge pull request #1367 from lioncash/plural
game_list: Handle plurals within setFilterResult() better
2018-09-20 23:30:32 -04:00
bunnei b0b57c21e6 Merge pull request #1368 from ogniK5377/nifm-fix
Added IRequest::Submit
2018-09-20 23:30:11 -04:00
bunnei a0db7e2cf9 Merge pull request #1352 from lioncash/sharing
ring_buffer: Use std::hardware_destructive_interference_size to determine alignment size for avoiding false sharing
2018-09-20 23:29:10 -04:00
bunnei 9f92533cc2 Merge pull request #1373 from ogniK5377/revert-nifm
Revert GetRequestState
2018-09-20 22:19:58 -04:00
David Marcec b918925bd5 Revert GetRequestState
Even though setting this value to 3 is more correct. We break more games than we fix due to missing implementations. We should keep this as 0 for the time being
2018-09-21 12:15:49 +10:00
Lioncash 05aa4aa01a kernel/thread: Use owner_process when setting the page table in SetupMainThread()
The owning process of a thread is required to exist before the thread,
so we can enforce this API-wise by using a reference. We can also avoid
the reliance on the system instance by using that parameter to access
the page table that needs to be set.
2018-09-20 21:10:00 -04:00
Lioncash 9b8fc2b689 arm_interface: Replace kernel vm_manager include with a forward declaration
Avoids an unnecessary inclusion and also uncovers three places where
indirect inclusions were relied upon, which allows us to also resolve
those.
2018-09-20 19:35:36 -04:00
Mat M f237265187 Merge pull request #1370 from Hedges/GDBClean
Correct endianness of BRK
2018-09-20 17:25:56 -04:00
Mat M 1e35d8fa8f Merge pull request #1362 from MerryMage/dynarmic
externals: Update dynarmic to 171d116
2018-09-20 17:22:42 -04:00
Jarek Syrylak 74f966404d Correct endianness of BKPT 2018-09-20 22:03:57 +01:00
MerryMage ef31487624 arm_dynarmic: Halt when BRK encountered 2018-09-20 19:12:42 +01:00
David Marcec 3f49725a51 Fixed submit 2018-09-21 00:51:13 +10:00
David Marcec cfc9fe4460 Added IRequest::Submit
This fixes updated versions of SMO. Currently unable to test as I don't have an updated version
2018-09-21 00:47:30 +10:00
bunnei 8fe9572271 Merge pull request #1358 from DarkLordZach/temp-storage
savedata_factory: Add TemporaryStorage SaveDataType
2018-09-20 10:13:38 -04:00
bunnei 0931a711de Merge pull request #1363 from lioncash/control
control_metadata: Move language name array definition to the cpp file
2018-09-20 10:13:23 -04:00
bunnei b02a1e38fa Merge pull request #1361 from lioncash/nax
xts_archive/nax: Minor interface changes
2018-09-20 10:13:10 -04:00
bunnei e57aa62b11 Merge pull request #1366 from ogniK5377/splat-fix
Implemented NTC & IEnsureNetworkClockAvailabilityService
2018-09-20 10:12:35 -04:00
Lioncash e980e90d6e game_list: Handle plurals within setFilterResult() better
Qt provides an overload of tr() that operates on quantities in relation
to pluralization. This also allows the translation to adapt based on the
target language rules better.

For example, the previous code would result in an incorrect translation
for the French language (which doesn't use the pluralized version of
"result" in the case of a total of zero. While in English it's
correct to use the pluralized version of "result", that is, "results"

---

For example:

English: "0 results"

French: "0 résultat" (uses the singular form)

In French, the noun being counted is singular if the quantity is 0 or 1.
In English, on the other hand, if the noun being counted has a quantity
of 0 or N > 1, then the noun is pluralized.

---

For another example in a language that has different counting methods
than the above, consider English and Irish. Irish has a special form of
of a grammatical number called a dual. Which alters how a word is
written when N of something is 2. This won't appear in this case with a
direct number "2", but it would change if we ever used "Two" to refer to
two of something. For example:

English: "Zero results"

Irish: "Toradh ar bith"

English: "One result"

Irish: "Toradh amháin"

English: "Two results"

Irish: "Dhá thorthaí" <- Dual case

Which is an important distinction to make between singular and plural,
because in other situations, "two" on its own would be written as "dó"
in Irish. There's also a few other cases where the order the words are
placed *and* whether or not the plural or singular variant of the word
is used *and* whether or not the word is placed after or between a set
of numbers can vary. Counting in Irish also differs depending on whether or not
you're counting things (like above) or counting people, in which case an
entirely different set of numbers are used.

It's not important for this case, but it's provided as an example as to why one
should never assume the placement of values in text will be like that of
English or other languages. Some languages have very different ways to
represent counting, and breaking up the translated string like this
isn't advisable because it makes it extremely difficult to get right
depending on what language a translator is translating text into due to
the ambiguity of the strings being presented for translation.

In this case a translator would see three fragmented strings on
Transifex (and not necessarily grouped beside one another, but even
then, it would still be annoying to decipher):

- "of"
- "result"
- "results"

There is no way a translator is going to know what those sets of words
are actually used for unless they look at the code to see what is being
done with them (which they shouldn't have to do).
2018-09-20 01:35:50 -04:00
David Marcec 583137709f Removed unneeded event clear 2018-09-20 15:16:08 +10:00
David Marcec 335e9d18ae Implemented NTC & IEnsureNetworkClockAvailabilityService
Needed because of the recent nim fixes
2018-09-20 15:14:07 +10:00
Lioncash 05ef9dfc10 file-sys: Default heavy-weight class destructors in the cpp file
Several classes have a lot of non-trivial members within them, or don't
but likely should have the destructor defaulted in the cpp file for
future-proofing/being more friendly to forward declarations.

Leaving the destructor unspecified allows the compiler to inline the
destruction code all over the place, which is generally undesirable from
a code bloat perspective.
2018-09-19 19:34:08 -04:00
Lioncash 3146502a12 control_metadata: Remove unnecessary else within GetLanguageEntry()
There's no need to indent the code here, given the if case contains a
return statement at the end of it.
2018-09-19 19:02:06 -04:00
Lioncash 05d49962b6 control_metadata: Move language name array definition to the cpp file
This was used in two different translation units
(deconstructed_rom_directory and patch_manager). This means we'd be
pointlessly duplicating the whole array twice due to it being defined
within the header.
2018-09-19 18:57:26 -04:00
MerryMage 3b4a158230 arm_dynarmic: Support BKPT instruction 2018-09-19 21:00:38 +01:00
MerryMage 321eb0b6b0 externals: Update dynarmic to 171d116
171d116 A64: Implement SCVTF, UCVTF (vector, fixed-point), scalar variant
f221bb0 emit_x64_floating_point: Reduce fallback LUT code in EmitFPToFixed
eb123e2 A64: Implement FCVTZS, FCVTZU, UCVTF, SCVTF (vector, fixed-point), vector variant
487d37a A64: Implement UQSHL's vector immediate and register variants
f698933 ir: Add opcodes for unsigned saturating left shifts
7148e66 A64/translate/impl: Make signatures consistent for unimplemented by-element SIMD variants
fdde4ca A64: Implement BRK
b1490db A64/imm: Add full range of comparison operators to Imm template
1ec40ef IR: Add fbits argument to FPVectorFrom{Signed,Unsigned}Fixed
d6d5e98 A64: Implement SCVTF, UCVTF (scalar, fixed-point)
6513595 opcodes.inc: Align columns to a tabstop of 4
6b0d2b5 IR: Add fbits argument to FixedToFP-related opcodes
c4b3831 A64: Implement SQSHL's vector immediate variant
e0d8d2d A64: Implement SQSHL's vector register variant
5327625 ir: Add opcodes for left signed saturated shifts
9705252 branch: Make variables const where applicable
650946e move_wide: Make variables const where applicable
62b3a6d load_store_register_unprivileged: Make variables const where applicable
3add1c7 load_store_register_immediate: Place conditional bodies on their own line
2fc4088 load_store_load_literal: Make variables const where applicable
b2c1462 data_processing_logical: Move datasize declarations after early-exit conditionals
028028f data_processing_conditional_select: Make variables const where applicable
c66042d data_processing_addsub: Move datasize declarations after early-exit conditionals
6bc546e data_processing_bitfield: Move datasize variables after early-exit conditionals
2aad5fa A64: Implement CLS's vector variant
6c877ff emit_x64_vector: Make EmitVectorUnsignedSaturatedAccumulateSigned() internally linked
4b5926d perf_map: Use std::string_view instead of std::string for PerfMapRegister()
7445947 A64: Implement SQRDMULH (vector), vector variant
03b80f2 A64: Implement SQDMULL (vector), vector variant
4a2c596 IR: Add VectorSignedSaturatedDoublingMultiplyLong
59dc33e emit_x64_vector: Changes to VectorSignedSaturatedDoublingMultiply
bbaebeb IR: Implement Vector{Signed,Unsigned}Multiply{16,32}
baac5a8 backend_x64/a64_interface: Re-enable the constant folding pass
e78ca19 emit_x64_vector_floating_point: Hardware FMA implementation for RSqrtStepFused
8a5ae9a emit_x64_vector_floating_point: Hardware FMA implementation of FPVectorRecipStepFused
39818f9 emit_x64_floating_point: Hardware FMA implementation of FPRSqrtStepFused
3d0a0b4 emit_x64_floating_point: Hardware FMA implementation of FPRecipStepFused{32,64}
2293dff emit_x64_vector: SSE implementation of VectorSignedSaturatedAccumulateUnsigned{8,16,32}
2047683 emit_x64_vector: Correct static asserts for < 64-bit type checks in saturated accumulate fallbacks
55e9e40 emit_x64_vector: EmitVectorSignedSaturatedAccumulateUnsigned64: SSE implementation
1076651 emit_x64_vector: Simplify fpsr_qc related code
4039030 A64: Implement CLZ's vector variant
0bb908f ir: Add opcodes for vector CLZ operations
3b13259 A64/translate: VectorZeroUpper for V(64) stores
1931d44 simd_two_register_misc: FNEG (vector) with Q == 0 had dirty upper
a0790f0 emit_x64_vector: Remove unnecessary [[maybe_unused]] attributes
b0e1eb5 A64: Implement USQADD's scalar and vector variants
28424c7 ir: Add opcodes form unsigned saturated accumulations of signed values
9923ea0 A64: Implement SUQADD's scalar and vector variants
4c0adbb ir: Add opcodes for signed saturated accumulations of unsigned values
799bfed A64: Implement SMLAL{2}, SMLSL{2}, UMLAL{2}, and UMLSL{2}'s vector by-element variants
94451ec A64: Implement UMULL{2}'s vector by-element variant
45867de A64: Implement SMULL{2}'s vector by-element variant
0235793 ir/value: Replace includes with forward declarations
450f721 ir/cond: Migrate to C++17 nested namespace specifiers
e649988 CMakeLists: Add missing cond.h header to file listing
d20e769 A64: Implement URSQRTE
4f3bde5 ir: Add opcodes for performing unsigned reciprocal square root estimates
cfeeaec A64: Implement URECPE
622b60e ir: Add opcodes for unsigned reciprocal estimate
d17599a Update Xbyak to 5.71
f7c26e9 Squashed 'externals/xbyak/' changes from 671fc805..1de435ed
8782b69 travis: Make macOS build with Xcode 9.4.1
b575b23 A64: Implement SQNEG's scalar and vector variant
06062a9 A64: Add opcodes for signed saturating negations
1c40579 emit_x64_vector: Simplify "position == 0" case for EmitVectorExtract()
e335050 emit_x64_vector: Simplify "position == 0" case for EmitVectorExtractLower()
8b13421 A64: Implement SQDMULH's by-element scalar variant
9122a6e A64: Implement SQDMULH's by-element vector variant
176e60e backend/x64: Do not clear fast_dispatch_table if not enabled
2018-09-19 20:31:39 +01:00
Lioncash 2e5f0e5024 xts_archive: Remove unused variables from CalculateHMAC256()
These variables aren't used, which still has an impact, as std::vector
cannot be optimized away by the compiler (it's constructor and
destructor are both non-trivial), so this was just wasting memory.
2018-09-19 14:23:13 -04:00
Lioncash 2752183883 xts_archive: Make AsNCA() return a std::unique_ptr instead of a std::shared_ptr
std::shared_ptr isn't strictly necessary here and is only ever used in
contexts where the object doesn't depend on being shared. This also
makes the interface more flexible, as it's possible to create a
std::shared_ptr from a std::unique_ptr (std::shared_ptr has a
constructor that accepts a std::unique_ptr), but not the other way
around.
2018-09-19 14:22:37 -04:00
Lioncash 45195a51a7 nax: Avoid re-parsing NAX data with GetFileType()
An instance of the NAX apploader already has an existing NAX instance in
memory. Calling directly into IdentifyType() directly would re-parse the
whole file again into yet another NAX instance, only to toss it away
again.

This gets rid of unnecessary/redundant file parsing and allocations.
2018-09-19 14:22:37 -04:00
Lioncash c8c4105659 nax: Avoid unnecessary calls to AsNCA() in IdentifyType()
AsNCA() allocates an NCA instance every time it's called. In the current
manner it's used, it's quite inefficient as it's making a redundant
allocation.

We can just amend the order of the conditionals to make it easier to
just call it once.
2018-09-19 14:22:37 -04:00
Lioncash f272261c21 xts_archive: Ensure NAX's type member is always initialized
Ensures that the member always has a deterministic value.
2018-09-19 14:22:37 -04:00
Lioncash 92a98a8b19 xts_archive: Amend initializer order of NAX's constructor
Orders the initializer list in the same order the members would be
initialized. Avoids compiler warnings.
2018-09-19 14:22:30 -04:00
tech4me 189cf0da7b Update MinGWCross.cmake to lowercase 2018-09-19 14:22:14 -04:00
tech4me a8f54f96fc travis: running mingw build on travis ci
This commit also fixed a broken cmake dependency with unicorn
2018-09-19 14:22:14 -04:00
David 0432af5ad1 Reworked incorrect nifm stubs (#1355)
* Reworked incorrect nifm stubs

Need confirmation on `CreateTemporaryNetworkProfile`, unsure which game uses it but according to reversing. It should return a uuid which we currently don't do.

Any 0 client id is considered an invalid client id.

GetRequestState 0 is considered invalid.

* Fixups for nifm
2018-09-19 11:59:01 -04:00
bunnei 52fb86c65c Merge pull request #1356 from degasus/hotfix
gl_rasterizer: Fix StartAddress handling with indexed draw calls.
2018-09-19 11:58:22 -04:00
bunnei 8dff92c5f6 Merge pull request #1359 from ogniK5377/nes
Fixed GetAccountId stub, Added error code for OpenDirectory and added ActivateNpadWithRevision
2018-09-19 10:01:36 -04:00
bunnei 5fbc354261 Merge pull request #1353 from ogniK5377/remove-MakeBuilder
Removed the use of rp.MakeBuilder in the yuzu codebase
2018-09-19 10:00:23 -04:00
David Marcec cbc7ad8f6d Fixed GetAccountId stub, Added error code for OpenDirectory and added ActivateNpadWithRevision
With these, `Nintendo Entertainment System - Nintendo Switch Online` loads
2018-09-19 23:25:00 +10:00
Zach Hilman 9bba103791 yuzu-cmd: Add call to CreateFactories
Ensures all relevant filesystem objects are initialized and eliminates a crash related to the RegisteredCache.
2018-09-19 09:09:11 -04:00
Zach Hilman dfaea55be5 savedata_factory: Add TemporaryStorage SaveDataType
Seems to be used by NSO NES Emulator
2018-09-19 09:06:22 -04:00
Markus Wick f465e4aaf2 gl_rasterizer: Fix StartAddress handling with indexed draw calls.
We uploaded the wrong data before. So the offset on the host GPU pointer may work for the first vertices, the last ones run out bounds.
Let's just offset the upload instead.
2018-09-19 09:22:30 +02:00
David Marcec d06f4cfc63 Corrected SSL::SetInterfaceVersion
Should be a single u32
2018-09-19 16:46:11 +10:00
Lioncash ab6dfa4fa5 ring_buffer: Use std::atomic_size_t in a static assert
Avoids the need to repeat "std::" twice
2018-09-18 23:36:04 -04:00
Lioncash c51f8563a6 ring_buffer: Use std::hardware_destructive_interference_size to determine alignment size for avoiding false sharing
MSVC 19.11 (A.K.A. VS 15.3)'s C++ standard library implements P0154R1
(http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0154r1.html)
which defines two new constants within the <new> header, std::hardware_destructive_interference_size
and std::hardware_constructive_interference_size.

std::hardware_destructive_interference_size defines the minimum
recommended offset between two concurrently-accessed objects to avoid
performance degradation due to contention introduced by the
implementation (with the lower-bound being at least alignof(max_align_t)).
In other words, the minimum offset between objects necessary to avoid
false-sharing.

std::hardware_constructive_interference_size on the other hand defines
the maximum recommended size of contiguous memory occupied by two
objects accessed wth temporal locality by concurrent threads (also
defined to be at least alignof(max_align_t)). In other words the maximum
size to promote true-sharing.

So we can simply use this facility to determine the ideal alignment
size. Unfortunately, only MSVC supports this right now, so we need to
enclose it within an ifdef for the time being.
2018-09-18 23:35:57 -04:00
108 changed files with 2342 additions and 289 deletions
+13
View File
@@ -29,6 +29,19 @@ matrix:
script: "./.travis/macos/build.sh"
after_success: "./.travis/macos/upload.sh"
cache: ccache
- os: linux
env: NAME="MinGW build"
sudo: required
dist: trusty
services: docker
addons:
apt:
packages:
- p7zip-full
install: "./.travis/linux-mingw/deps.sh"
script: "./.travis/linux-mingw/build.sh"
after_success: "./.travis/linux-mingw/upload.sh"
cache: ccache
deploy:
provider: releases
+3
View File
@@ -11,6 +11,9 @@ if [ -z $TRAVIS_TAG ]; then
RELEASE_NAME=head
else
RELEASE_NAME=$(echo $TRAVIS_TAG | cut -d- -f1)
if [ "$NAME" = "MinGW build" ]; then
RELEASE_NAME="${RELEASE_NAME}-mingw"
fi
fi
mv "$REV_NAME" $RELEASE_NAME
+3
View File
@@ -0,0 +1,3 @@
#!/bin/bash -ex
mkdir "$HOME/.ccache" || true
docker run --env-file .travis/common/travis-ci.env -v $(pwd):/yuzu -v "$HOME/.ccache":/root/.ccache ubuntu:18.04 /bin/bash -ex /yuzu/.travis/linux-mingw/docker.sh
+3
View File
@@ -0,0 +1,3 @@
#!/bin/sh -ex
docker pull ubuntu:18.04
+59
View File
@@ -0,0 +1,59 @@
#!/bin/bash -ex
cd /yuzu
MINGW_PACKAGES="sdl2-mingw-w64 qt5base-mingw-w64 qt5tools-mingw-w64 libsamplerate-mingw-w64 qt5multimedia-mingw-w64"
apt-get update
apt-get install -y gpg wget git python3-pip python ccache g++-mingw-w64-x86-64 gcc-mingw-w64-x86-64 mingw-w64-tools cmake
echo 'deb http://ppa.launchpad.net/tobydox/mingw-w64/ubuntu bionic main ' > /etc/apt/sources.list.d/extras.list
apt-key adv --keyserver keyserver.ubuntu.com --recv '72931B477E22FEFD47F8DECE02FE5F12ADDE29B2'
apt-get update
apt-get install -y ${MINGW_PACKAGES}
# fix a problem in current MinGW headers
wget -q https://raw.githubusercontent.com/Alexpux/mingw-w64/d0d7f784833bbb0b2d279310ddc6afb52fe47a46/mingw-w64-headers/crt/errno.h -O /usr/x86_64-w64-mingw32/include/errno.h
# override Travis CI unreasonable ccache size
echo 'max_size = 3.0G' > "$HOME/.ccache/ccache.conf"
# Dirty hack to trick unicorn makefile into believing we are in a MINGW system
mv /bin/uname /bin/uname1 && echo -e '#!/bin/sh\necho MINGW64' >> /bin/uname
chmod +x /bin/uname
# Dirty hack to trick unicorn makefile into believing we have cmd
echo '' >> /bin/cmd
chmod +x /bin/cmd
mkdir build && cd build
cmake .. -DCMAKE_TOOLCHAIN_FILE="$(pwd)/../CMakeModules/MinGWCross.cmake" -DUSE_CCACHE=ON -DYUZU_USE_BUNDLED_UNICORN=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DCMAKE_BUILD_TYPE=Release
make -j4
# Clean up the dirty hacks
rm /bin/uname && mv /bin/uname1 /bin/uname
rm /bin/cmd
ccache -s
echo "Tests skipped"
#ctest -VV -C Release
echo 'Prepare binaries...'
cd ..
mkdir package
QT_PLATFORM_DLL_PATH='/usr/x86_64-w64-mingw32/lib/qt5/plugins/platforms/'
find build/ -name "yuzu*.exe" -exec cp {} 'package' \;
# copy Qt plugins
mkdir package/platforms
cp "${QT_PLATFORM_DLL_PATH}/qwindows.dll" package/platforms/
cp -rv "${QT_PLATFORM_DLL_PATH}/../mediaservice/" package/
cp -rv "${QT_PLATFORM_DLL_PATH}/../imageformats/" package/
rm -f package/mediaservice/*d.dll
for i in package/*.exe; do
# we need to process pdb here, however, cv2pdb
# does not work here, so we just simply strip all the debug symbols
x86_64-w64-mingw32-strip "${i}"
done
pip3 install pefile
python3 .travis/linux-mingw/scan_dll.py package/*.exe "package/"
+106
View File
@@ -0,0 +1,106 @@
import pefile
import sys
import re
import os
import queue
import shutil
# constant definitions
KNOWN_SYS_DLLS = ['WINMM.DLL', 'MSVCRT.DLL', 'VERSION.DLL', 'MPR.DLL',
'DWMAPI.DLL', 'UXTHEME.DLL', 'DNSAPI.DLL', 'IPHLPAPI.DLL']
# below is for Ubuntu 18.04 with specified PPA enabled, if you are using
# other distro or different repositories, change the following accordingly
DLL_PATH = [
'/usr/x86_64-w64-mingw32/bin/',
'/usr/x86_64-w64-mingw32/lib/',
'/usr/lib/gcc/x86_64-w64-mingw32/7.3-posix/'
]
missing = []
def parse_imports(file_name):
results = []
pe = pefile.PE(file_name, fast_load=True)
pe.parse_data_directories()
for entry in pe.DIRECTORY_ENTRY_IMPORT:
current = entry.dll.decode()
current_u = current.upper() # b/c Windows is often case insensitive
# here we filter out system dlls
# dll w/ names like *32.dll are likely to be system dlls
if current_u.upper() not in KNOWN_SYS_DLLS and not re.match(string=current_u, pattern=r'.*32\.DLL'):
results.append(current)
return results
def parse_imports_recursive(file_name, path_list=[]):
q = queue.Queue() # create a FIFO queue
# file_name can be a string or a list for the convience
if isinstance(file_name, str):
q.put(file_name)
elif isinstance(file_name, list):
for i in file_name:
q.put(i)
full_list = []
while q.qsize():
current = q.get_nowait()
print('> %s' % current)
deps = parse_imports(current)
# if this dll does not have any import, ignore it
if not deps:
continue
for dep in deps:
# the dependency already included in the list, skip
if dep in full_list:
continue
# find the requested dll in the provided paths
full_path = find_dll(dep)
if not full_path:
missing.append(dep)
continue
full_list.append(dep)
q.put(full_path)
path_list.append(full_path)
return full_list
def find_dll(name):
for path in DLL_PATH:
for root, _, files in os.walk(path):
for f in files:
if name.lower() == f.lower():
return os.path.join(root, f)
def deploy(name, dst, dry_run=False):
dlls_path = []
parse_imports_recursive(name, dlls_path)
for dll_entry in dlls_path:
if not dry_run:
shutil.copy(dll_entry, dst)
else:
print('[Dry-Run] Copy %s to %s' % (dll_entry, dst))
print('Deploy completed.')
return dlls_path
def main():
if len(sys.argv) < 3:
print('Usage: %s [files to examine ...] [target deploy directory]')
return 1
to_deploy = sys.argv[1:-1]
tgt_dir = sys.argv[-1]
if not os.path.isdir(tgt_dir):
print('%s is not a directory.' % tgt_dir)
return 1
print('Scanning dependencies...')
deploy(to_deploy, tgt_dir)
if missing:
print('Following DLLs are not found: %s' % ('\n'.join(missing)))
return 0
if __name__ == '__main__':
main()
+13
View File
@@ -0,0 +1,13 @@
#!/bin/bash -ex
. .travis/common/pre-upload.sh
REV_NAME="yuzu-windows-mingw-${GITDATE}-${GITREV}"
ARCHIVE_NAME="${REV_NAME}.tar.gz"
COMPRESSION_FLAGS="-czvf"
mkdir "$REV_NAME"
# get around the permission issues
cp -r package/* "$REV_NAME"
. .travis/common/post-upload.sh
+3 -1
View File
@@ -6,7 +6,9 @@ apt-get install --no-install-recommends -y build-essential git libqt5opengl5-dev
cd /yuzu
mkdir build && cd build
cmake .. -DYUZU_BUILD_UNICORN=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/lib/ccache/gcc -DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -G Ninja
cmake .. -DYUZU_USE_BUNDLED_UNICORN=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/lib/ccache/gcc -DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -G Ninja
ninja
ccache -s
ctest -VV -C Release
+3 -1
View File
@@ -9,7 +9,9 @@ export PATH="/usr/local/opt/ccache/libexec:$PATH"
mkdir build && cd build
cmake --version
cmake .. -DYUZU_BUILD_UNICORN=ON -DCMAKE_BUILD_TYPE=Release -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON
cmake .. -DYUZU_USE_BUNDLED_UNICORN=ON -DCMAKE_BUILD_TYPE=Release -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON
make -j4
ccache -s
ctest -VV -C Release
+19 -6
View File
@@ -269,10 +269,18 @@ if (YUZU_USE_BUNDLED_UNICORN)
find_package(PythonInterp 2.7 REQUIRED)
add_custom_command(OUTPUT ${LIBUNICORN_LIBRARY}
COMMAND ${CMAKE_COMMAND} -E env UNICORN_ARCHS="aarch64" PYTHON="${PYTHON_EXECUTABLE}" /bin/sh make.sh macos-universal-no
WORKING_DIRECTORY ${UNICORN_PREFIX}
)
if (MINGW)
add_custom_command(OUTPUT ${LIBUNICORN_LIBRARY}
COMMAND ${CMAKE_COMMAND} -E env UNICORN_ARCHS="aarch64" PYTHON="${PYTHON_EXECUTABLE}" /bin/sh make.sh cross-win64
WORKING_DIRECTORY ${UNICORN_PREFIX}
)
else()
add_custom_command(OUTPUT ${LIBUNICORN_LIBRARY}
COMMAND ${CMAKE_COMMAND} -E env UNICORN_ARCHS="aarch64" PYTHON="${PYTHON_EXECUTABLE}" /bin/sh make.sh macos-universal-no
WORKING_DIRECTORY ${UNICORN_PREFIX}
)
endif()
# ALL makes this custom target build every time
# but it won't actually build if LIBUNICORN_LIBRARY is up to date
add_custom_target(unicorn-build ALL
@@ -286,6 +294,7 @@ endif()
if (UNICORN_FOUND)
add_library(unicorn INTERFACE)
add_dependencies(unicorn unicorn-build)
target_link_libraries(unicorn INTERFACE "${LIBUNICORN_LIBRARY}")
target_include_directories(unicorn INTERFACE "${LIBUNICORN_INCLUDE_DIR}")
else()
@@ -431,8 +440,12 @@ enable_testing()
add_subdirectory(externals)
add_subdirectory(src)
# Set yuzu project as default StartUp Project in Visual Studio
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT yuzu)
# Set yuzu project or yuzu-cmd project as default StartUp Project in Visual Studio depending on whether QT is enabled or not
if(ENABLE_QT)
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT yuzu)
else()
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT yuzu-cmd)
endif()
# Installation instructions
+54
View File
@@ -0,0 +1,54 @@
set(MINGW_PREFIX /usr/x86_64-w64-mingw32/)
set(CMAKE_SYSTEM_NAME Windows)
set(CMAKE_SYSTEM_PROCESSOR x86_64)
# Actually a hack, w/o this will cause some strange errors
set(CMAKE_HOST_WIN32 TRUE)
set(CMAKE_FIND_ROOT_PATH ${MINGW_PREFIX})
set(SDL2_PATH ${MINGW_PREFIX})
set(MINGW_TOOL_PREFIX ${CMAKE_SYSTEM_PROCESSOR}-w64-mingw32-)
# Specify the cross compiler
set(CMAKE_C_COMPILER ${MINGW_TOOL_PREFIX}gcc-posix)
set(CMAKE_CXX_COMPILER ${MINGW_TOOL_PREFIX}g++-posix)
set(CMAKE_RC_COMPILER ${MINGW_TOOL_PREFIX}windres)
# Mingw tools
set(STRIP ${MINGW_TOOL_PREFIX}strip)
set(WINDRES ${MINGW_TOOL_PREFIX}windres)
set(ENV{PKG_CONFIG} ${MINGW_TOOL_PREFIX}pkg-config)
# ccache wrapper
option(USE_CCACHE "Use ccache for compilation" OFF)
if(USE_CCACHE)
find_program(CCACHE ccache)
if(CCACHE)
message(STATUS "Using ccache found in PATH")
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ${CCACHE})
set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ${CCACHE})
else(CCACHE)
message(WARNING "USE_CCACHE enabled, but no ccache found")
endif(CCACHE)
endif(USE_CCACHE)
# Search for programs in the build host directories
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
# Echo modified cmake vars to screen for debugging purposes
if(NOT DEFINED ENV{MINGW_DEBUG_INFO})
message("")
message("Custom cmake vars: (blank = system default)")
message("-----------------------------------------")
message("* CMAKE_C_COMPILER : ${CMAKE_C_COMPILER}")
message("* CMAKE_CXX_COMPILER : ${CMAKE_CXX_COMPILER}")
message("* CMAKE_RC_COMPILER : ${CMAKE_RC_COMPILER}")
message("* WINDRES : ${WINDRES}")
message("* ENV{PKG_CONFIG} : $ENV{PKG_CONFIG}")
message("* STRIP : ${STRIP}")
message("* USE_CCACHE : ${USE_CCACHE}")
message("")
# So that the debug info only appears once
set(ENV{MINGW_DEBUG_INFO} SHOWN)
endif()
+4
View File
@@ -79,6 +79,10 @@ u32 AudioRenderer::GetMixBufferCount() const {
return worker_params.mix_buffer_count;
}
u32 AudioRenderer::GetState() const {
return stream->GetState();
}
std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_params) {
// Copy UpdateDataHeader struct
UpdateDataHeader config{};
+1
View File
@@ -170,6 +170,7 @@ public:
u32 GetSampleRate() const;
u32 GetSampleCount() const;
u32 GetMixBufferCount() const;
u32 GetState() const;
private:
class VoiceState;
+5
View File
@@ -49,9 +49,14 @@ void Stream::Play() {
}
void Stream::Stop() {
state = State::Stopped;
ASSERT_MSG(false, "Unimplemented");
}
u32 Stream::GetState() const {
return static_cast<u32>(state);
}
s64 Stream::GetBufferReleaseCycles(const Buffer& buffer) const {
const std::size_t num_samples{buffer.GetSamples().size() / GetNumChannels()};
return CoreTiming::usToCycles((static_cast<u64>(num_samples) * 1000000) / sample_rate);
+3
View File
@@ -72,6 +72,9 @@ public:
/// Gets the number of channels
u32 GetNumChannels() const;
/// Get the state
u32 GetState() const;
private:
/// Current state of the stream
enum class State {
+1 -1
View File
@@ -59,7 +59,7 @@ std::size_t TimeStretcher::Process(const s16* in, std::size_t num_in, s16* out,
m_stretch_ratio = std::max(m_stretch_ratio, 0.05);
m_sound_touch.setTempo(m_stretch_ratio);
LOG_DEBUG(Audio, "{:5}/{:5} ratio:{:0.6f} backlog:{:0.6f}", num_in, num_out, m_stretch_ratio,
LOG_TRACE(Audio, "{:5}/{:5} ratio:{:0.6f} backlog:{:0.6f}", num_in, num_out, m_stretch_ratio,
backlog_fullness);
m_sound_touch.putSamples(in, static_cast<u32>(num_in));
+2
View File
@@ -33,6 +33,8 @@
#define NAND_DIR "nand"
#define SYSDATA_DIR "sysdata"
#define KEYS_DIR "keys"
#define LOAD_DIR "load"
#define DUMP_DIR "dump"
#define LOG_DIR "log"
// Filenames
+2
View File
@@ -705,6 +705,8 @@ const std::string& GetUserPath(UserPath path, const std::string& new_path) {
#endif
paths.emplace(UserPath::SDMCDir, user_path + SDMC_DIR DIR_SEP);
paths.emplace(UserPath::NANDDir, user_path + NAND_DIR DIR_SEP);
paths.emplace(UserPath::LoadDir, user_path + LOAD_DIR DIR_SEP);
paths.emplace(UserPath::DumpDir, user_path + DUMP_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
+2
View File
@@ -29,6 +29,8 @@ enum class UserPath {
NANDDir,
RootDir,
SDMCDir,
LoadDir,
DumpDir,
SysDataDir,
UserDir,
};
+1
View File
@@ -183,6 +183,7 @@ void FileBackend::Write(const Entry& entry) {
SUB(Service, FS) \
SUB(Service, GRC) \
SUB(Service, HID) \
SUB(Service, IRS) \
SUB(Service, LBL) \
SUB(Service, LDN) \
SUB(Service, LDR) \
+1
View File
@@ -70,6 +70,7 @@ enum class Class : ClassType {
Service_FS, ///< The FS (Filesystem) service
Service_GRC, ///< The game recording service
Service_HID, ///< The HID (Human interface device) service
Service_IRS, ///< The IRS service
Service_LBL, ///< The LBL (LCD backlight) service
Service_LDN, ///< The LDN (Local domain network) service
Service_LDR, ///< The loader service
+11 -3
View File
@@ -9,6 +9,7 @@
#include <atomic>
#include <cstddef>
#include <cstring>
#include <new>
#include <type_traits>
#include <vector>
#include "common/common_types.h"
@@ -29,7 +30,7 @@ class RingBuffer {
static_assert(capacity < std::numeric_limits<std::size_t>::max() / 2 / granularity);
static_assert((capacity & (capacity - 1)) == 0, "capacity must be a power of two");
// Ensure lock-free.
static_assert(std::atomic<std::size_t>::is_always_lock_free);
static_assert(std::atomic_size_t::is_always_lock_free);
public:
/// Pushes slots into the ring buffer
@@ -102,8 +103,15 @@ public:
private:
// It is important to align the below variables for performance reasons:
// Having them on the same cache-line would result in false-sharing between them.
alignas(128) std::atomic<std::size_t> m_read_index{0};
alignas(128) std::atomic<std::size_t> m_write_index{0};
// TODO: Remove this ifdef whenever clang and GCC support
// std::hardware_destructive_interference_size.
#if defined(_MSC_VER) && _MSC_VER >= 1911
alignas(std::hardware_destructive_interference_size) std::atomic_size_t m_read_index{0};
alignas(std::hardware_destructive_interference_size) std::atomic_size_t m_write_index{0};
#else
alignas(128) std::atomic_size_t m_read_index{0};
alignas(128) std::atomic_size_t m_write_index{0};
#endif
std::array<T, granularity * capacity> m_data;
};
-8
View File
@@ -87,14 +87,6 @@ private:
void SleepCurrentThread(int ms);
void SwitchCurrentThread(); // On Linux, this is equal to sleep 1ms
// Use this function during a spin-wait to make the current thread
// relax while another thread is working. This may be more efficient
// than using events because event functions use kernel calls.
inline void YieldCPU() {
std::this_thread::yield();
}
void SetCurrentThreadName(const char* name);
} // namespace Common
+5
View File
@@ -32,6 +32,8 @@ add_library(core STATIC
file_sys/control_metadata.h
file_sys/directory.h
file_sys/errors.h
file_sys/fsmitm_romfsbuild.cpp
file_sys/fsmitm_romfsbuild.h
file_sys/mode.h
file_sys/nca_metadata.cpp
file_sys/nca_metadata.h
@@ -59,10 +61,13 @@ add_library(core STATIC
file_sys/vfs.h
file_sys/vfs_concat.cpp
file_sys/vfs_concat.h
file_sys/vfs_layered.cpp
file_sys/vfs_layered.h
file_sys/vfs_offset.cpp
file_sys/vfs_offset.h
file_sys/vfs_real.cpp
file_sys/vfs_real.h
file_sys/vfs_static.h
file_sys/vfs_vector.cpp
file_sys/vfs_vector.h
file_sys/xts_archive.cpp
+4 -1
View File
@@ -6,7 +6,10 @@
#include <array>
#include "common/common_types.h"
#include "core/hle/kernel/vm_manager.h"
namespace Kernel {
enum class VMAPermission : u8;
}
namespace Core {
+13
View File
@@ -12,8 +12,10 @@
#include "core/core.h"
#include "core/core_cpu.h"
#include "core/core_timing.h"
#include "core/gdbstub/gdbstub.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/svc.h"
#include "core/hle/kernel/vm_manager.h"
#include "core/memory.h"
namespace Core {
@@ -79,6 +81,17 @@ public:
case Dynarmic::A64::Exception::SendEventLocal:
case Dynarmic::A64::Exception::Yield:
return;
case Dynarmic::A64::Exception::Breakpoint:
if (GDBStub::IsServerEnabled()) {
parent.jit->HaltExecution();
parent.SetPC(pc);
Kernel::Thread* thread = Kernel::GetCurrentThread();
parent.SaveContext(thread->context);
GDBStub::Break();
GDBStub::SendTrap(thread, 5);
return;
}
[[fallthrough]];
default:
ASSERT_MSG(false, "ExceptionRaised(exception = {}, pc = {:X})",
static_cast<std::size_t>(exception), pc);
+4
View File
@@ -12,6 +12,10 @@
#include "core/arm/exclusive_monitor.h"
#include "core/arm/unicorn/arm_unicorn.h"
namespace Memory {
struct PageTable;
}
namespace Core {
class ARM_Dynarmic_Callbacks;
+10 -2
View File
@@ -2,13 +2,14 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <fmt/format.h>
#include "core/file_sys/bis_factory.h"
#include "core/file_sys/registered_cache.h"
namespace FileSys {
BISFactory::BISFactory(VirtualDir nand_root_)
: nand_root(std::move(nand_root_)),
BISFactory::BISFactory(VirtualDir nand_root_, VirtualDir load_root_)
: nand_root(std::move(nand_root_)), load_root(std::move(load_root_)),
sysnand_cache(std::make_shared<RegisteredCache>(
GetOrCreateDirectoryRelative(nand_root, "/system/Contents/registered"))),
usrnand_cache(std::make_shared<RegisteredCache>(
@@ -24,4 +25,11 @@ std::shared_ptr<RegisteredCache> BISFactory::GetUserNANDContents() const {
return usrnand_cache;
}
VirtualDir BISFactory::GetModificationLoadRoot(u64 title_id) const {
// LayeredFS doesn't work on updates and title id-less homebrew
if (title_id == 0 || (title_id & 0x800) > 0)
return nullptr;
return GetOrCreateDirectoryRelative(load_root, fmt::format("/{:016X}", title_id));
}
} // namespace FileSys
+4 -1
View File
@@ -17,14 +17,17 @@ class RegisteredCache;
/// registered caches.
class BISFactory {
public:
explicit BISFactory(VirtualDir nand_root);
explicit BISFactory(VirtualDir nand_root, VirtualDir load_root);
~BISFactory();
std::shared_ptr<RegisteredCache> GetSystemNANDContents() const;
std::shared_ptr<RegisteredCache> GetUserNANDContents() const;
VirtualDir GetModificationLoadRoot(u64 title_id) const;
private:
VirtualDir nand_root;
VirtualDir load_root;
std::shared_ptr<RegisteredCache> sysnand_cache;
std::shared_ptr<RegisteredCache> usrnand_cache;
+2
View File
@@ -463,6 +463,8 @@ NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_off
status = Loader::ResultStatus::Success;
}
NCA::~NCA() = default;
Loader::ResultStatus NCA::GetStatus() const {
return status;
}
+2
View File
@@ -81,6 +81,8 @@ class NCA : public ReadOnlyVfsDirectory {
public:
explicit NCA(VirtualFile file, VirtualFile bktr_base_romfs = nullptr,
u64 bktr_base_ivfc_offset = 0);
~NCA() override;
Loader::ResultStatus GetStatus() const;
std::vector<std::shared_ptr<VfsFile>> GetFiles() const override;
+18 -8
View File
@@ -8,6 +8,14 @@
namespace FileSys {
const std::array<const char*, 15> LANGUAGE_NAMES = {
"AmericanEnglish", "BritishEnglish", "Japanese",
"French", "German", "LatinAmericanSpanish",
"Spanish", "Italian", "Dutch",
"CanadianFrench", "Portugese", "Russian",
"Korean", "Taiwanese", "Chinese",
};
std::string LanguageEntry::GetApplicationName() const {
return Common::StringFromFixedZeroTerminatedBuffer(application_name.data(), 0x200);
}
@@ -20,18 +28,20 @@ NACP::NACP(VirtualFile file) : raw(std::make_unique<RawNACP>()) {
file->ReadObject(raw.get());
}
NACP::~NACP() = default;
const LanguageEntry& NACP::GetLanguageEntry(Language language) const {
if (language != Language::Default) {
return raw->language_entries.at(static_cast<u8>(language));
} else {
for (const auto& language_entry : raw->language_entries) {
if (!language_entry.GetApplicationName().empty())
return language_entry;
}
// Fallback to English
return GetLanguageEntry(Language::AmericanEnglish);
}
for (const auto& language_entry : raw->language_entries) {
if (!language_entry.GetApplicationName().empty())
return language_entry;
}
// Fallback to English
return GetLanguageEntry(Language::AmericanEnglish);
}
std::string NACP::GetApplicationName(Language language) const {
+3 -6
View File
@@ -66,18 +66,15 @@ enum class Language : u8 {
Default = 255,
};
static constexpr std::array<const char*, 15> LANGUAGE_NAMES = {
"AmericanEnglish", "BritishEnglish", "Japanese",
"French", "German", "LatinAmericanSpanish",
"Spanish", "Italian", "Dutch",
"CanadianFrench", "Portugese", "Russian",
"Korean", "Taiwanese", "Chinese"};
extern const std::array<const char*, 15> LANGUAGE_NAMES;
// 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 {
public:
explicit NACP(VirtualFile file);
~NACP();
const LanguageEntry& GetLanguageEntry(Language language = Language::Default) const;
std::string GetApplicationName(Language language = Language::Default) const;
std::string GetDeveloperName(Language language = Language::Default) const;
+372
View File
@@ -0,0 +1,372 @@
/*
* Copyright (c) 2018 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* Adapted by DarkLordZach for use/interaction with yuzu
*
* Modifications 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/file_sys/fsmitm_romfsbuild.h"
#include "core/file_sys/vfs.h"
#include "core/file_sys/vfs_vector.h"
namespace FileSys {
constexpr u64 FS_MAX_PATH = 0x301;
constexpr u32 ROMFS_ENTRY_EMPTY = 0xFFFFFFFF;
constexpr u32 ROMFS_FILEPARTITION_OFS = 0x200;
// Types for building a RomFS.
struct RomFSHeader {
u64 header_size;
u64 dir_hash_table_ofs;
u64 dir_hash_table_size;
u64 dir_table_ofs;
u64 dir_table_size;
u64 file_hash_table_ofs;
u64 file_hash_table_size;
u64 file_table_ofs;
u64 file_table_size;
u64 file_partition_ofs;
};
static_assert(sizeof(RomFSHeader) == 0x50, "RomFSHeader has incorrect size.");
struct RomFSDirectoryEntry {
u32 parent;
u32 sibling;
u32 child;
u32 file;
u32 hash;
u32 name_size;
};
static_assert(sizeof(RomFSDirectoryEntry) == 0x18, "RomFSDirectoryEntry has incorrect size.");
struct RomFSFileEntry {
u32 parent;
u32 sibling;
u64 offset;
u64 size;
u32 hash;
u32 name_size;
};
static_assert(sizeof(RomFSFileEntry) == 0x20, "RomFSFileEntry has incorrect size.");
struct RomFSBuildFileContext;
struct RomFSBuildDirectoryContext {
std::string path = "";
u32 cur_path_ofs = 0;
u32 path_len = 0;
u32 entry_offset = 0;
std::shared_ptr<RomFSBuildDirectoryContext> parent;
std::shared_ptr<RomFSBuildDirectoryContext> child;
std::shared_ptr<RomFSBuildDirectoryContext> sibling;
std::shared_ptr<RomFSBuildFileContext> file;
};
struct RomFSBuildFileContext {
std::string path = "";
u32 cur_path_ofs = 0;
u32 path_len = 0;
u32 entry_offset = 0;
u64 offset = 0;
u64 size = 0;
std::shared_ptr<RomFSBuildDirectoryContext> parent;
std::shared_ptr<RomFSBuildFileContext> sibling;
VirtualFile source = nullptr;
RomFSBuildFileContext() : path(""), cur_path_ofs(0), path_len(0) {}
};
static u32 romfs_calc_path_hash(u32 parent, std::string path, u32 start, size_t path_len) {
u32 hash = parent ^ 123456789;
for (u32 i = 0; i < path_len; i++) {
hash = (hash >> 5) | (hash << 27);
hash ^= path[start + i];
}
return hash;
}
static u32 romfs_get_hash_table_count(u32 num_entries) {
if (num_entries < 3) {
return 3;
} else if (num_entries < 19) {
return num_entries | 1;
}
u32 count = num_entries;
while (count % 2 == 0 || count % 3 == 0 || count % 5 == 0 || count % 7 == 0 ||
count % 11 == 0 || count % 13 == 0 || count % 17 == 0) {
count++;
}
return count;
}
void RomFSBuildContext::VisitDirectory(VirtualDir root_romfs,
std::shared_ptr<RomFSBuildDirectoryContext> parent) {
std::vector<std::shared_ptr<RomFSBuildDirectoryContext>> child_dirs;
VirtualDir dir;
if (parent->path_len == 0)
dir = root_romfs;
else
dir = root_romfs->GetDirectoryRelative(parent->path);
const auto entries = dir->GetEntries();
for (const auto& kv : entries) {
if (kv.second == VfsEntryType::Directory) {
const auto child = std::make_shared<RomFSBuildDirectoryContext>();
// Set child's path.
child->cur_path_ofs = parent->path_len + 1;
child->path_len = child->cur_path_ofs + kv.first.size();
child->path = parent->path + "/" + kv.first;
// Sanity check on path_len
ASSERT(child->path_len < FS_MAX_PATH);
if (AddDirectory(parent, child)) {
child_dirs.push_back(child);
}
} else {
const auto child = std::make_shared<RomFSBuildFileContext>();
// Set child's path.
child->cur_path_ofs = parent->path_len + 1;
child->path_len = child->cur_path_ofs + kv.first.size();
child->path = parent->path + "/" + kv.first;
// Sanity check on path_len
ASSERT(child->path_len < FS_MAX_PATH);
child->source = root_romfs->GetFileRelative(child->path);
child->size = child->source->GetSize();
AddFile(parent, child);
}
}
for (auto& child : child_dirs) {
this->VisitDirectory(root_romfs, child);
}
}
bool RomFSBuildContext::AddDirectory(std::shared_ptr<RomFSBuildDirectoryContext> parent_dir_ctx,
std::shared_ptr<RomFSBuildDirectoryContext> dir_ctx) {
// Check whether it's already in the known directories.
const auto existing = directories.find(dir_ctx->path);
if (existing != directories.end())
return false;
// Add a new directory.
num_dirs++;
dir_table_size +=
sizeof(RomFSDirectoryEntry) + ((dir_ctx->path_len - dir_ctx->cur_path_ofs + 3) & ~3);
dir_ctx->parent = parent_dir_ctx;
directories.emplace(dir_ctx->path, dir_ctx);
return true;
}
bool RomFSBuildContext::AddFile(std::shared_ptr<RomFSBuildDirectoryContext> parent_dir_ctx,
std::shared_ptr<RomFSBuildFileContext> file_ctx) {
// Check whether it's already in the known files.
const auto existing = files.find(file_ctx->path);
if (existing != files.end()) {
return false;
}
// Add a new file.
num_files++;
file_table_size +=
sizeof(RomFSFileEntry) + ((file_ctx->path_len - file_ctx->cur_path_ofs + 3) & ~3);
file_ctx->parent = parent_dir_ctx;
files.emplace(file_ctx->path, file_ctx);
return true;
}
RomFSBuildContext::RomFSBuildContext(VirtualDir base_) : base(std::move(base_)) {
root = std::make_shared<RomFSBuildDirectoryContext>();
root->path = "\0";
directories.emplace(root->path, root);
num_dirs = 1;
dir_table_size = 0x18;
VisitDirectory(base, root);
}
RomFSBuildContext::~RomFSBuildContext() = default;
std::map<u64, VirtualFile> RomFSBuildContext::Build() {
const auto dir_hash_table_entry_count = romfs_get_hash_table_count(num_dirs);
const auto file_hash_table_entry_count = romfs_get_hash_table_count(num_files);
dir_hash_table_size = 4 * dir_hash_table_entry_count;
file_hash_table_size = 4 * file_hash_table_entry_count;
// Assign metadata pointers
RomFSHeader header{};
std::vector<u32> dir_hash_table(dir_hash_table_entry_count, ROMFS_ENTRY_EMPTY);
std::vector<u32> file_hash_table(file_hash_table_entry_count, ROMFS_ENTRY_EMPTY);
std::vector<u8> dir_table(dir_table_size);
std::vector<u8> file_table(file_table_size);
// Clear out hash tables.
for (u32 i = 0; i < dir_hash_table_entry_count; i++)
dir_hash_table[i] = ROMFS_ENTRY_EMPTY;
for (u32 i = 0; i < file_hash_table_entry_count; i++)
file_hash_table[i] = ROMFS_ENTRY_EMPTY;
std::shared_ptr<RomFSBuildFileContext> cur_file;
// Determine file offsets.
u32 entry_offset = 0;
std::shared_ptr<RomFSBuildFileContext> prev_file = nullptr;
for (const auto& it : files) {
cur_file = it.second;
file_partition_size = (file_partition_size + 0xFULL) & ~0xFULL;
cur_file->offset = file_partition_size;
file_partition_size += cur_file->size;
cur_file->entry_offset = entry_offset;
entry_offset +=
sizeof(RomFSFileEntry) + ((cur_file->path_len - cur_file->cur_path_ofs + 3) & ~3);
prev_file = cur_file;
}
// Assign deferred parent/sibling ownership.
for (auto it = files.rbegin(); it != files.rend(); ++it) {
cur_file = it->second;
cur_file->sibling = cur_file->parent->file;
cur_file->parent->file = cur_file;
}
std::shared_ptr<RomFSBuildDirectoryContext> cur_dir;
// Determine directory offsets.
entry_offset = 0;
for (const auto& it : directories) {
cur_dir = it.second;
cur_dir->entry_offset = entry_offset;
entry_offset +=
sizeof(RomFSDirectoryEntry) + ((cur_dir->path_len - cur_dir->cur_path_ofs + 3) & ~3);
}
// Assign deferred parent/sibling ownership.
for (auto it = directories.rbegin(); it->second != root; ++it) {
cur_dir = it->second;
cur_dir->sibling = cur_dir->parent->child;
cur_dir->parent->child = cur_dir;
}
std::map<u64, VirtualFile> out;
// Populate file tables.
for (const auto& it : files) {
cur_file = it.second;
RomFSFileEntry cur_entry{};
cur_entry.parent = cur_file->parent->entry_offset;
cur_entry.sibling =
cur_file->sibling == nullptr ? ROMFS_ENTRY_EMPTY : cur_file->sibling->entry_offset;
cur_entry.offset = cur_file->offset;
cur_entry.size = cur_file->size;
const auto name_size = cur_file->path_len - cur_file->cur_path_ofs;
const auto hash = romfs_calc_path_hash(cur_file->parent->entry_offset, cur_file->path,
cur_file->cur_path_ofs, name_size);
cur_entry.hash = file_hash_table[hash % file_hash_table_entry_count];
file_hash_table[hash % file_hash_table_entry_count] = cur_file->entry_offset;
cur_entry.name_size = name_size;
out.emplace(cur_file->offset + ROMFS_FILEPARTITION_OFS, cur_file->source);
std::memcpy(file_table.data() + cur_file->entry_offset, &cur_entry, sizeof(RomFSFileEntry));
std::memset(file_table.data() + cur_file->entry_offset + sizeof(RomFSFileEntry), 0,
(cur_entry.name_size + 3) & ~3);
std::memcpy(file_table.data() + cur_file->entry_offset + sizeof(RomFSFileEntry),
cur_file->path.data() + cur_file->cur_path_ofs, name_size);
}
// Populate dir tables.
for (const auto& it : directories) {
cur_dir = it.second;
RomFSDirectoryEntry cur_entry{};
cur_entry.parent = cur_dir == root ? 0 : cur_dir->parent->entry_offset;
cur_entry.sibling =
cur_dir->sibling == nullptr ? ROMFS_ENTRY_EMPTY : cur_dir->sibling->entry_offset;
cur_entry.child =
cur_dir->child == nullptr ? ROMFS_ENTRY_EMPTY : cur_dir->child->entry_offset;
cur_entry.file = cur_dir->file == nullptr ? ROMFS_ENTRY_EMPTY : cur_dir->file->entry_offset;
const auto name_size = cur_dir->path_len - cur_dir->cur_path_ofs;
const auto hash = romfs_calc_path_hash(cur_dir == root ? 0 : cur_dir->parent->entry_offset,
cur_dir->path, cur_dir->cur_path_ofs, name_size);
cur_entry.hash = dir_hash_table[hash % dir_hash_table_entry_count];
dir_hash_table[hash % dir_hash_table_entry_count] = cur_dir->entry_offset;
cur_entry.name_size = name_size;
std::memcpy(dir_table.data() + cur_dir->entry_offset, &cur_entry,
sizeof(RomFSDirectoryEntry));
std::memcpy(dir_table.data() + cur_dir->entry_offset, &cur_entry,
sizeof(RomFSDirectoryEntry));
std::memset(dir_table.data() + cur_dir->entry_offset + sizeof(RomFSDirectoryEntry), 0,
(cur_entry.name_size + 3) & ~3);
std::memcpy(dir_table.data() + cur_dir->entry_offset + sizeof(RomFSDirectoryEntry),
cur_dir->path.data() + cur_dir->cur_path_ofs, name_size);
}
// Set header fields.
header.header_size = sizeof(RomFSHeader);
header.file_hash_table_size = file_hash_table_size;
header.file_table_size = file_table_size;
header.dir_hash_table_size = dir_hash_table_size;
header.dir_table_size = dir_table_size;
header.file_partition_ofs = ROMFS_FILEPARTITION_OFS;
header.dir_hash_table_ofs = (header.file_partition_ofs + file_partition_size + 3ULL) & ~3ULL;
header.dir_table_ofs = header.dir_hash_table_ofs + header.dir_hash_table_size;
header.file_hash_table_ofs = header.dir_table_ofs + header.dir_table_size;
header.file_table_ofs = header.file_hash_table_ofs + header.file_hash_table_size;
std::vector<u8> header_data(sizeof(RomFSHeader));
std::memcpy(header_data.data(), &header, header_data.size());
out.emplace(0, std::make_shared<VectorVfsFile>(header_data));
std::vector<u8> metadata(file_hash_table_size + file_table_size + dir_hash_table_size +
dir_table_size);
auto index = 0;
std::memcpy(metadata.data(), dir_hash_table.data(), dir_hash_table.size() * sizeof(u32));
index += dir_hash_table.size() * sizeof(u32);
std::memcpy(metadata.data() + index, dir_table.data(), dir_table.size());
index += dir_table.size();
std::memcpy(metadata.data() + index, file_hash_table.data(),
file_hash_table.size() * sizeof(u32));
index += file_hash_table.size() * sizeof(u32);
std::memcpy(metadata.data() + index, file_table.data(), file_table.size());
out.emplace(header.dir_hash_table_ofs, std::make_shared<VectorVfsFile>(metadata));
return out;
}
} // namespace FileSys
+70
View File
@@ -0,0 +1,70 @@
/*
* Copyright (c) 2018 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* Adapted by DarkLordZach for use/interaction with yuzu
*
* Modifications Copyright 2018 yuzu emulator team
* Licensed under GPLv2 or any later version
* Refer to the license.txt file included.
*/
#pragma once
#include <map>
#include <memory>
#include <string>
#include <boost/detail/container_fwd.hpp>
#include "common/common_types.h"
#include "core/file_sys/vfs.h"
namespace FileSys {
struct RomFSBuildDirectoryContext;
struct RomFSBuildFileContext;
struct RomFSDirectoryEntry;
struct RomFSFileEntry;
class RomFSBuildContext {
public:
explicit RomFSBuildContext(VirtualDir base);
~RomFSBuildContext();
// This finalizes the context.
std::map<u64, VirtualFile> Build();
private:
VirtualDir base;
std::shared_ptr<RomFSBuildDirectoryContext> root;
std::map<std::string, std::shared_ptr<RomFSBuildDirectoryContext>, std::less<>> directories;
std::map<std::string, std::shared_ptr<RomFSBuildFileContext>, std::less<>> files;
u64 num_dirs = 0;
u64 num_files = 0;
u64 dir_table_size = 0;
u64 file_table_size = 0;
u64 dir_hash_table_size = 0;
u64 file_hash_table_size = 0;
u64 file_partition_size = 0;
void VisitDirectory(VirtualDir filesys, std::shared_ptr<RomFSBuildDirectoryContext> parent);
bool AddDirectory(std::shared_ptr<RomFSBuildDirectoryContext> parent_dir_ctx,
std::shared_ptr<RomFSBuildDirectoryContext> dir_ctx);
bool AddFile(std::shared_ptr<RomFSBuildDirectoryContext> parent_dir_ctx,
std::shared_ptr<RomFSBuildFileContext> file_ctx);
};
} // namespace FileSys
+2
View File
@@ -51,6 +51,8 @@ CNMT::CNMT(CNMTHeader header, OptionalHeader opt_header, std::vector<ContentReco
: header(std::move(header)), opt_header(std::move(opt_header)),
content_records(std::move(content_records)), meta_records(std::move(meta_records)) {}
CNMT::~CNMT() = default;
u64 CNMT::GetTitleID() const {
return header.title_id;
}
+1
View File
@@ -87,6 +87,7 @@ public:
explicit CNMT(VirtualFile file);
CNMT(CNMTHeader header, OptionalHeader opt_header, std::vector<ContentRecord> content_records,
std::vector<MetaRecord> meta_records);
~CNMT();
u64 GetTitleID() const;
u32 GetTitleVersion() const;
@@ -72,6 +72,8 @@ PartitionFilesystem::PartitionFilesystem(std::shared_ptr<VfsFile> file) {
status = Loader::ResultStatus::Success;
}
PartitionFilesystem::~PartitionFilesystem() = default;
Loader::ResultStatus PartitionFilesystem::GetStatus() const {
return status;
}
+2
View File
@@ -25,6 +25,8 @@ namespace FileSys {
class PartitionFilesystem : public ReadOnlyVfsDirectory {
public:
explicit PartitionFilesystem(std::shared_ptr<VfsFile> file);
~PartitionFilesystem() override;
Loader::ResultStatus GetStatus() const;
std::vector<std::shared_ptr<VfsFile>> GetFiles() const override;
+48 -1
View File
@@ -11,6 +11,7 @@
#include "core/file_sys/patch_manager.h"
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/romfs.h"
#include "core/file_sys/vfs_layered.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/loader/loader.h"
@@ -31,8 +32,9 @@ std::string FormatTitleVersion(u32 version, TitleVersionFormat format) {
return fmt::format("v{}.{}.{}", bytes[3], bytes[2], bytes[1]);
}
constexpr std::array<const char*, 1> PATCH_TYPE_NAMES{
constexpr std::array<const char*, 2> PATCH_TYPE_NAMES{
"Update",
"LayeredFS",
};
std::string FormatPatchTypeName(PatchType type) {
@@ -41,6 +43,8 @@ std::string FormatPatchTypeName(PatchType type) {
PatchManager::PatchManager(u64 title_id) : title_id(title_id) {}
PatchManager::~PatchManager() = default;
VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
LOG_INFO(Loader, "Patching ExeFS for title_id={:016X}", title_id);
@@ -64,6 +68,42 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
return exefs;
}
static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type) {
const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
if (type == ContentRecordType::Program && load_dir != nullptr && load_dir->GetSize() > 0) {
auto extracted = ExtractRomFS(romfs);
if (extracted != nullptr) {
auto patch_dirs = load_dir->GetSubdirectories();
std::sort(patch_dirs.begin(), patch_dirs.end(),
[](const VirtualDir& l, const VirtualDir& r) {
return l->GetName() < r->GetName();
});
std::vector<VirtualDir> layers;
layers.reserve(patch_dirs.size() + 1);
for (const auto& subdir : patch_dirs) {
auto romfs_dir = subdir->GetSubdirectory("romfs");
if (romfs_dir != nullptr)
layers.push_back(std::move(romfs_dir));
}
layers.push_back(std::move(extracted));
const auto layered = LayerDirectories(layers);
if (layered != nullptr) {
auto packed = CreateRomFS(layered);
if (packed != nullptr) {
LOG_INFO(Loader, " RomFS: LayeredFS patches applied successfully");
romfs = std::move(packed);
}
}
}
}
}
VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset,
ContentRecordType type) const {
LOG_INFO(Loader, "Patching RomFS for title_id={:016X}, type={:02X}", title_id,
@@ -87,6 +127,9 @@ VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset,
}
}
// LayeredFS
ApplyLayeredFS(romfs, title_id, type);
return romfs;
}
@@ -112,6 +155,10 @@ std::map<PatchType, std::string> PatchManager::GetPatchVersionNames() const {
}
}
const auto lfs_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
if (lfs_dir != nullptr && lfs_dir->GetSize() > 0)
out.insert_or_assign(PatchType::LayeredFS, "");
return out;
}
+3
View File
@@ -26,6 +26,7 @@ std::string FormatTitleVersion(u32 version,
enum class PatchType {
Update,
LayeredFS,
};
std::string FormatPatchTypeName(PatchType type);
@@ -34,6 +35,7 @@ std::string FormatPatchTypeName(PatchType type);
class PatchManager {
public:
explicit PatchManager(u64 title_id);
~PatchManager();
// Currently tracked ExeFS patches:
// - Game Updates
@@ -41,6 +43,7 @@ public:
// Currently tracked RomFS patches:
// - Game Updates
// - LayeredFS
VirtualFile PatchRomFS(VirtualFile base, u64 ivfc_offset,
ContentRecordType type = ContentRecordType::Program) const;
+4
View File
@@ -12,6 +12,10 @@
namespace FileSys {
ProgramMetadata::ProgramMetadata() = default;
ProgramMetadata::~ProgramMetadata() = default;
Loader::ResultStatus ProgramMetadata::Load(VirtualFile file) {
std::size_t total_size = static_cast<std::size_t>(file->GetSize());
if (total_size < sizeof(Header))
+3
View File
@@ -36,6 +36,9 @@ enum class ProgramFilePermission : u64 {
*/
class ProgramMetadata {
public:
ProgramMetadata();
~ProgramMetadata();
Loader::ResultStatus Load(VirtualFile file);
bool Is64BitProgram() const;
+7 -2
View File
@@ -18,6 +18,10 @@
#include "core/loader/loader.h"
namespace FileSys {
// The size of blocks to use when vfs raw copying into nand.
constexpr size_t VFS_RC_LARGE_COPY_BLOCK = 0x400000;
std::string RegisteredCacheEntry::DebugInfo() const {
return fmt::format("title_id={:016X}, content_type={:02X}", title_id, static_cast<u8>(type));
}
@@ -121,7 +125,7 @@ VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& dir,
if (concat.empty())
return nullptr;
file = FileSys::ConcatenateFiles(concat);
file = FileSys::ConcatenateFiles(concat, concat.front()->GetName());
}
return file;
@@ -480,7 +484,8 @@ InstallResult RegisteredCache::RawInstallNCA(std::shared_ptr<NCA> nca, const Vfs
auto out = dir->CreateFileRelative(path);
if (out == nullptr)
return InstallResult::ErrorCopyFailed;
return copy(in, out) ? InstallResult::Success : InstallResult::ErrorCopyFailed;
return copy(in, out, VFS_RC_LARGE_COPY_BLOCK) ? InstallResult::Success
: InstallResult::ErrorCopyFailed;
}
bool RegisteredCache::RawInstallYuzuMeta(const CNMT& cnmt) {
+1 -1
View File
@@ -27,7 +27,7 @@ struct ContentRecord;
using NcaID = std::array<u8, 0x10>;
using RegisteredCacheParsingFunction = std::function<VirtualFile(const VirtualFile&, const NcaID&)>;
using VfsCopyFunction = std::function<bool(VirtualFile, VirtualFile)>;
using VfsCopyFunction = std::function<bool(const VirtualFile&, const VirtualFile&, size_t)>;
enum class InstallResult {
Success,
+18 -3
View File
@@ -4,8 +4,10 @@
#include "common/common_types.h"
#include "common/swap.h"
#include "core/file_sys/fsmitm_romfsbuild.h"
#include "core/file_sys/romfs.h"
#include "core/file_sys/vfs.h"
#include "core/file_sys/vfs_concat.h"
#include "core/file_sys/vfs_offset.h"
#include "core/file_sys/vfs_vector.h"
@@ -98,7 +100,7 @@ void ProcessDirectory(VirtualFile file, std::size_t dir_offset, std::size_t file
}
}
VirtualDir ExtractRomFS(VirtualFile file) {
VirtualDir ExtractRomFS(VirtualFile file, RomFSExtractionType type) {
RomFSHeader header{};
if (file->ReadObject(&header) != sizeof(RomFSHeader))
return nullptr;
@@ -117,9 +119,22 @@ VirtualDir ExtractRomFS(VirtualFile file) {
VirtualDir out = std::move(root);
while (out->GetSubdirectory("") != nullptr)
out = out->GetSubdirectory("");
while (out->GetSubdirectories().size() == 1 && out->GetFiles().empty()) {
if (out->GetSubdirectories().front()->GetName() == "data" &&
type == RomFSExtractionType::Truncated)
break;
out = out->GetSubdirectories().front();
}
return out;
}
VirtualFile CreateRomFS(VirtualDir dir) {
if (dir == nullptr)
return nullptr;
RomFSBuildContext ctx{dir};
return ConcatenateFiles<0>(ctx.Build(), dir->GetName());
}
} // namespace FileSys
+14 -1
View File
@@ -5,6 +5,7 @@
#pragma once
#include <array>
#include <map>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
@@ -12,6 +13,8 @@
namespace FileSys {
struct RomFSHeader;
struct IVFCLevel {
u64_le offset;
u64_le size;
@@ -29,8 +32,18 @@ struct IVFCHeader {
};
static_assert(sizeof(IVFCHeader) == 0xE0, "IVFCHeader has incorrect size.");
enum class RomFSExtractionType {
Full, // Includes data directory
Truncated, // Traverses into data directory
};
// Converts a RomFS binary blob to VFS Filesystem
// Returns nullptr on failure
VirtualDir ExtractRomFS(VirtualFile file);
VirtualDir ExtractRomFS(VirtualFile file,
RomFSExtractionType type = RomFSExtractionType::Truncated);
// Converts a VFS filesystem into a RomFS binary
// Returns nullptr on failure
VirtualFile CreateRomFS(VirtualDir dir);
} // namespace FileSys
+2
View File
@@ -28,6 +28,8 @@ RomFSFactory::RomFSFactory(Loader::AppLoader& app_loader) {
ivfc_offset = app_loader.ReadRomFSIVFCOffset();
}
RomFSFactory::~RomFSFactory() = default;
ResultVal<VirtualFile> RomFSFactory::OpenCurrentProcess() {
if (!updatable)
return MakeResult<VirtualFile>(file);
+1
View File
@@ -30,6 +30,7 @@ enum class StorageId : u8 {
class RomFSFactory {
public:
explicit RomFSFactory(Loader::AppLoader& app_loader);
~RomFSFactory();
ResultVal<VirtualFile> OpenCurrentProcess();
ResultVal<VirtualFile> Open(u64 title_id, StorageId storage, ContentRecordType type);
+9 -4
View File
@@ -20,6 +20,8 @@ std::string SaveDataDescriptor::DebugInfo() const {
SaveDataFactory::SaveDataFactory(VirtualDir save_directory) : dir(std::move(save_directory)) {}
SaveDataFactory::~SaveDataFactory() = default;
ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space, SaveDataDescriptor meta) {
if (meta.type == SaveDataType::SystemSaveData || meta.type == SaveDataType::SaveData) {
if (meta.zero_1 != 0) {
@@ -85,10 +87,10 @@ std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType typ
switch (space) {
case SaveDataSpaceId::NandSystem:
out = "/system/save/";
out = "/system/";
break;
case SaveDataSpaceId::NandUser:
out = "/user/save/";
out = "/user/";
break;
default:
ASSERT_MSG(false, "Unrecognized SaveDataSpaceId: {:02X}", static_cast<u8>(space));
@@ -96,9 +98,12 @@ std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType typ
switch (type) {
case SaveDataType::SystemSaveData:
return fmt::format("{}{:016X}/{:016X}{:016X}", out, save_id, user_id[1], user_id[0]);
return fmt::format("{}save/{:016X}/{:016X}{:016X}", out, save_id, user_id[1], user_id[0]);
case SaveDataType::SaveData:
return fmt::format("{}{:016X}/{:016X}{:016X}/{:016X}", out, 0, user_id[1], user_id[0],
return fmt::format("{}save/{:016X}/{:016X}{:016X}/{:016X}", out, 0, user_id[1], user_id[0],
title_id);
case SaveDataType::TemporaryStorage:
return fmt::format("{}temp/{:016X}/{:016X}{:016X}/{:016X}", out, 0, user_id[1], user_id[0],
title_id);
default:
ASSERT_MSG(false, "Unrecognized SaveDataType: {:02X}", static_cast<u8>(type));
+1
View File
@@ -48,6 +48,7 @@ static_assert(sizeof(SaveDataDescriptor) == 0x40, "SaveDataDescriptor has incorr
class SaveDataFactory {
public:
explicit SaveDataFactory(VirtualDir dir);
~SaveDataFactory();
ResultVal<VirtualDir> Open(SaveDataSpaceId space, SaveDataDescriptor meta);
+1 -1
View File
@@ -24,7 +24,7 @@ enum class ContentRecordType : u8;
class NSP : public ReadOnlyVfsDirectory {
public:
explicit NSP(VirtualFile file);
~NSP();
~NSP() override;
Loader::ResultStatus GetStatus() const;
Loader::ResultStatus GetProgramStatus(u64 title_id) const;
+41 -4
View File
@@ -399,6 +399,15 @@ bool VfsDirectory::Copy(std::string_view src, std::string_view dest) {
return f2->WriteBytes(f1->ReadAllBytes()) == f1->GetSize();
}
std::map<std::string, VfsEntryType, std::less<>> VfsDirectory::GetEntries() const {
std::map<std::string, VfsEntryType, std::less<>> out;
for (const auto& dir : GetSubdirectories())
out.emplace(dir->GetName(), VfsEntryType::Directory);
for (const auto& file : GetFiles())
out.emplace(file->GetName(), VfsEntryType::File);
return out;
}
std::string VfsDirectory::GetFullPath() const {
if (IsRoot())
return GetName();
@@ -454,13 +463,41 @@ bool DeepEquals(const VirtualFile& file1, const VirtualFile& file2, std::size_t
return true;
}
bool VfsRawCopy(VirtualFile src, VirtualFile dest) {
if (src == nullptr || dest == nullptr)
bool VfsRawCopy(const VirtualFile& src, const VirtualFile& dest, size_t block_size) {
if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable())
return false;
if (!dest->Resize(src->GetSize()))
return false;
std::vector<u8> data = src->ReadAllBytes();
return dest->WriteBytes(data, 0) == data.size();
std::vector<u8> temp(std::min(block_size, src->GetSize()));
for (size_t i = 0; i < src->GetSize(); i += block_size) {
const auto read = std::min(block_size, src->GetSize() - i);
const auto block = src->Read(temp.data(), read, i);
if (dest->Write(temp.data(), read, i) != read)
return false;
}
return true;
}
bool VfsRawCopyD(const VirtualDir& src, const VirtualDir& dest, size_t block_size) {
if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable())
return false;
for (const auto& file : src->GetFiles()) {
const auto out = dest->CreateFile(file->GetName());
if (!VfsRawCopy(file, out, block_size))
return false;
}
for (const auto& dir : src->GetSubdirectories()) {
const auto out = dest->CreateSubdirectory(dir->GetName());
if (!VfsRawCopyD(dir, out, block_size))
return false;
}
return true;
}
VirtualDir GetOrCreateDirectoryRelative(const VirtualDir& rel, std::string_view path) {
+12 -2
View File
@@ -4,6 +4,7 @@
#pragma once
#include <map>
#include <memory>
#include <string>
#include <string_view>
@@ -265,6 +266,10 @@ public:
// dest.
virtual bool Copy(std::string_view src, std::string_view dest);
// Gets all of the entries directly in the directory (files and dirs), returning a map between
// item name -> type.
virtual std::map<std::string, VfsEntryType, std::less<>> GetEntries() const;
// Interprets the file with name file instead as a directory of type directory.
// The directory must have a constructor that takes a single argument of type
// std::shared_ptr<VfsFile>. Allows to reinterpret container files (i.e NCA, zip, XCI, etc) as a
@@ -311,12 +316,17 @@ public:
};
// Compare the two files, byte-for-byte, in increments specificed by block_size
bool DeepEquals(const VirtualFile& file1, const VirtualFile& file2, std::size_t block_size = 0x200);
bool DeepEquals(const VirtualFile& file1, const VirtualFile& file2, size_t block_size = 0x1000);
// 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.
bool VfsRawCopy(VirtualFile src, VirtualFile dest);
bool VfsRawCopy(const VirtualFile& src, const VirtualFile& dest, size_t block_size = 0x1000);
// A method that performs a similar function to VfsRawCopy above, but instead copies entire
// directories. It suffers the same performance penalties as above and an implementation-specific
// Copy should always be preferred.
bool VfsRawCopyD(const VirtualDir& src, const VirtualDir& dest, size_t block_size = 0x1000);
// Checks if the directory at path relative to rel exists. If it does, returns that. If it does not
// it attempts to create it and returns the new dir or nullptr on failure.
+29 -11
View File
@@ -5,10 +5,23 @@
#include <algorithm>
#include <utility>
#include "common/assert.h"
#include "core/file_sys/vfs_concat.h"
namespace FileSys {
static bool VerifyConcatenationMapContinuity(const std::map<u64, VirtualFile>& map) {
const auto last_valid = --map.end();
for (auto iter = map.begin(); iter != last_valid;) {
const auto old = iter++;
if (old->first + old->second->GetSize() != iter->first) {
return false;
}
}
return map.begin()->first == 0;
}
VirtualFile ConcatenateFiles(std::vector<VirtualFile> files, std::string name) {
if (files.empty())
return nullptr;
@@ -27,6 +40,13 @@ ConcatenatedVfsFile::ConcatenatedVfsFile(std::vector<VirtualFile> files_, std::s
}
}
ConcatenatedVfsFile::ConcatenatedVfsFile(std::map<u64, VirtualFile> files_, std::string name)
: files(std::move(files_)), name(std::move(name)) {
ASSERT(VerifyConcatenationMapContinuity(files));
}
ConcatenatedVfsFile::~ConcatenatedVfsFile() = default;
std::string ConcatenatedVfsFile::GetName() const {
if (files.empty())
return "";
@@ -60,7 +80,7 @@ bool ConcatenatedVfsFile::IsReadable() const {
}
std::size_t ConcatenatedVfsFile::Read(u8* data, std::size_t length, std::size_t offset) const {
auto entry = files.end();
auto entry = --files.end();
for (auto iter = files.begin(); iter != files.end(); ++iter) {
if (iter->first > offset) {
entry = --iter;
@@ -68,20 +88,17 @@ std::size_t ConcatenatedVfsFile::Read(u8* data, std::size_t length, std::size_t
}
}
// Check if the entry should be the last one. The loop above will make it end().
if (entry == files.end() && offset < files.rbegin()->first + files.rbegin()->second->GetSize())
--entry;
if (entry == files.end())
if (entry->first + entry->second->GetSize() <= offset)
return 0;
const auto remaining = entry->second->GetSize() + offset - entry->first;
if (length > remaining) {
return entry->second->Read(data, remaining, offset - entry->first) +
Read(data + remaining, length - remaining, offset + remaining);
const auto read_in =
std::min<u64>(entry->first + entry->second->GetSize() - offset, entry->second->GetSize());
if (length > read_in) {
return entry->second->Read(data, read_in, offset - entry->first) +
Read(data + read_in, length - read_in, offset + read_in);
}
return entry->second->Read(data, length, offset - entry->first);
return entry->second->Read(data, std::min<u64>(read_in, length), offset - entry->first);
}
std::size_t ConcatenatedVfsFile::Write(const u8* data, std::size_t length, std::size_t offset) {
@@ -91,4 +108,5 @@ std::size_t ConcatenatedVfsFile::Write(const u8* data, std::size_t length, std::
bool ConcatenatedVfsFile::Rename(std::string_view name) {
return false;
}
} // namespace FileSys
+38 -4
View File
@@ -4,24 +4,29 @@
#pragma once
#include <map>
#include <memory>
#include <string_view>
#include <boost/container/flat_map.hpp>
#include "core/file_sys/vfs.h"
#include "core/file_sys/vfs_static.h"
namespace FileSys {
// Wrapper function to allow for more efficient handling of files.size() == 0, 1 cases.
VirtualFile ConcatenateFiles(std::vector<VirtualFile> files, std::string name = "");
// Class that wraps multiple vfs files and concatenates them, making reads seamless. Currently
// read-only.
class ConcatenatedVfsFile : public VfsFile {
friend VirtualFile ConcatenateFiles(std::vector<VirtualFile> files, std::string name);
template <u8 filler_byte>
friend VirtualFile ConcatenateFiles(std::map<u64, VirtualFile> files, std::string name);
ConcatenatedVfsFile(std::vector<VirtualFile> files, std::string name);
ConcatenatedVfsFile(std::map<u64, VirtualFile> files, std::string name);
public:
~ConcatenatedVfsFile() override;
std::string GetName() const override;
std::size_t GetSize() const override;
bool Resize(std::size_t new_size) override;
@@ -34,8 +39,37 @@ public:
private:
// Maps starting offset to file -- more efficient.
boost::container::flat_map<u64, VirtualFile> files;
std::map<u64, VirtualFile> files;
std::string name;
};
// Wrapper function to allow for more efficient handling of files.size() == 0, 1 cases.
VirtualFile ConcatenateFiles(std::vector<VirtualFile> files, std::string name);
// Convenience function that turns a map of offsets to files into a concatenated file, filling gaps
// with template parameter.
template <u8 filler_byte>
VirtualFile ConcatenateFiles(std::map<u64, VirtualFile> files, std::string name) {
if (files.empty())
return nullptr;
if (files.size() == 1)
return files.begin()->second;
const auto last_valid = --files.end();
for (auto iter = files.begin(); iter != last_valid;) {
const auto old = iter++;
if (old->first + old->second->GetSize() != iter->first) {
files.emplace(old->first + old->second->GetSize(),
std::make_shared<StaticVfsFile<filler_byte>>(iter->first - old->first -
old->second->GetSize()));
}
}
// Ensure the map starts at offset 0 (start of file), otherwise pad to fill.
if (files.begin()->first != 0)
files.emplace(0, std::make_shared<StaticVfsFile<filler_byte>>(files.begin()->first));
return std::shared_ptr<VfsFile>(new ConcatenatedVfsFile(std::move(files), std::move(name)));
}
} // namespace FileSys
+131
View File
@@ -0,0 +1,131 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <utility>
#include "core/file_sys/vfs_layered.h"
namespace FileSys {
VirtualDir LayerDirectories(std::vector<VirtualDir> dirs, std::string name) {
if (dirs.empty())
return nullptr;
if (dirs.size() == 1)
return dirs[0];
return std::shared_ptr<VfsDirectory>(new LayeredVfsDirectory(std::move(dirs), std::move(name)));
}
LayeredVfsDirectory::LayeredVfsDirectory(std::vector<VirtualDir> dirs, std::string name)
: dirs(std::move(dirs)), name(std::move(name)) {}
LayeredVfsDirectory::~LayeredVfsDirectory() = default;
std::shared_ptr<VfsFile> LayeredVfsDirectory::GetFileRelative(std::string_view path) const {
for (const auto& layer : dirs) {
const auto file = layer->GetFileRelative(path);
if (file != nullptr)
return file;
}
return nullptr;
}
std::shared_ptr<VfsDirectory> LayeredVfsDirectory::GetDirectoryRelative(
std::string_view path) const {
std::vector<VirtualDir> out;
for (const auto& layer : dirs) {
auto dir = layer->GetDirectoryRelative(path);
if (dir != nullptr)
out.push_back(std::move(dir));
}
return LayerDirectories(std::move(out));
}
std::shared_ptr<VfsFile> LayeredVfsDirectory::GetFile(std::string_view name) const {
return GetFileRelative(name);
}
std::shared_ptr<VfsDirectory> LayeredVfsDirectory::GetSubdirectory(std::string_view name) const {
return GetDirectoryRelative(name);
}
std::string LayeredVfsDirectory::GetFullPath() const {
return dirs[0]->GetFullPath();
}
std::vector<std::shared_ptr<VfsFile>> LayeredVfsDirectory::GetFiles() const {
std::vector<VirtualFile> out;
for (const auto& layer : dirs) {
for (const auto& file : layer->GetFiles()) {
if (std::find_if(out.begin(), out.end(), [&file](const VirtualFile& comp) {
return comp->GetName() == file->GetName();
}) == out.end()) {
out.push_back(file);
}
}
}
return out;
}
std::vector<std::shared_ptr<VfsDirectory>> LayeredVfsDirectory::GetSubdirectories() const {
std::vector<std::string> names;
for (const auto& layer : dirs) {
for (const auto& sd : layer->GetSubdirectories()) {
if (std::find(names.begin(), names.end(), sd->GetName()) == names.end())
names.push_back(sd->GetName());
}
}
std::vector<VirtualDir> out;
out.reserve(names.size());
for (const auto& subdir : names)
out.push_back(GetSubdirectory(subdir));
return out;
}
bool LayeredVfsDirectory::IsWritable() const {
return false;
}
bool LayeredVfsDirectory::IsReadable() const {
return true;
}
std::string LayeredVfsDirectory::GetName() const {
return name.empty() ? dirs[0]->GetName() : name;
}
std::shared_ptr<VfsDirectory> LayeredVfsDirectory::GetParentDirectory() const {
return dirs[0]->GetParentDirectory();
}
std::shared_ptr<VfsDirectory> LayeredVfsDirectory::CreateSubdirectory(std::string_view name) {
return nullptr;
}
std::shared_ptr<VfsFile> LayeredVfsDirectory::CreateFile(std::string_view name) {
return nullptr;
}
bool LayeredVfsDirectory::DeleteSubdirectory(std::string_view name) {
return false;
}
bool LayeredVfsDirectory::DeleteFile(std::string_view name) {
return false;
}
bool LayeredVfsDirectory::Rename(std::string_view name_) {
name = name_;
return true;
}
bool LayeredVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
return false;
}
} // namespace FileSys
+52
View File
@@ -0,0 +1,52 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <memory>
#include "core/file_sys/vfs.h"
namespace FileSys {
// Wrapper function to allow for more efficient handling of dirs.size() == 0, 1 cases.
VirtualDir LayerDirectories(std::vector<VirtualDir> dirs, std::string name = "");
// Class that stacks multiple VfsDirectories on top of each other, attempting to read from the first
// one and falling back to the one after. The highest priority directory (overwrites all others)
// should be element 0 in the dirs vector.
class LayeredVfsDirectory : public VfsDirectory {
friend VirtualDir LayerDirectories(std::vector<VirtualDir> dirs, std::string name);
LayeredVfsDirectory(std::vector<VirtualDir> dirs, std::string name);
public:
~LayeredVfsDirectory() override;
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::string GetFullPath() const override;
std::vector<std::shared_ptr<VfsFile>> GetFiles() const override;
std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override;
bool IsWritable() const override;
bool IsReadable() const override;
std::string GetName() const override;
std::shared_ptr<VfsDirectory> GetParentDirectory() const override;
std::shared_ptr<VfsDirectory> CreateSubdirectory(std::string_view name) override;
std::shared_ptr<VfsFile> CreateFile(std::string_view name) override;
bool DeleteSubdirectory(std::string_view name) override;
bool DeleteFile(std::string_view name) override;
bool Rename(std::string_view name) override;
protected:
bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
private:
std::vector<VirtualDir> dirs;
std::string name;
};
} // namespace FileSys
+2
View File
@@ -14,6 +14,8 @@ OffsetVfsFile::OffsetVfsFile(std::shared_ptr<VfsFile> file_, std::size_t size_,
: file(file_), offset(offset_), size(size_), name(std::move(name_)),
parent(parent_ == nullptr ? file->GetContainingDirectory() : std::move(parent_)) {}
OffsetVfsFile::~OffsetVfsFile() = default;
std::string OffsetVfsFile::GetName() const {
return name.empty() ? file->GetName() : name;
}
+1
View File
@@ -19,6 +19,7 @@ class OffsetVfsFile : public VfsFile {
public:
OffsetVfsFile(std::shared_ptr<VfsFile> file, std::size_t size, std::size_t offset = 0,
std::string new_name = "", VirtualDir new_parent = nullptr);
~OffsetVfsFile() override;
std::string GetName() const override;
std::size_t GetSize() const override;
+17
View File
@@ -413,6 +413,23 @@ std::string RealVfsDirectory::GetFullPath() const {
return out;
}
std::map<std::string, VfsEntryType, std::less<>> RealVfsDirectory::GetEntries() const {
if (perms == Mode::Append)
return {};
std::map<std::string, VfsEntryType, std::less<>> out;
FileUtil::ForeachDirectoryEntry(
nullptr, path,
[&out](u64* entries_out, const std::string& directory, const std::string& filename) {
const std::string full_path = directory + DIR_SEP + filename;
out.emplace(filename, FileUtil::IsDirectory(full_path) ? VfsEntryType::Directory
: VfsEntryType::File);
return true;
});
return out;
}
bool RealVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
return false;
}
+1
View File
@@ -98,6 +98,7 @@ public:
bool DeleteFile(std::string_view name) override;
bool Rename(std::string_view name) override;
std::string GetFullPath() const override;
std::map<std::string, VfsEntryType, std::less<>> GetEntries() const override;
protected:
bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
+78
View File
@@ -0,0 +1,78 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <algorithm>
#include <memory>
#include <string_view>
#include "core/file_sys/vfs.h"
namespace FileSys {
template <u8 value>
class StaticVfsFile : public VfsFile {
public:
explicit StaticVfsFile(size_t size = 0, std::string name = "", VirtualDir parent = nullptr)
: size(size), name(std::move(name)), parent(std::move(parent)) {}
std::string GetName() const override {
return name;
}
size_t GetSize() const override {
return size;
}
bool Resize(size_t new_size) override {
size = new_size;
return true;
}
std::shared_ptr<VfsDirectory> GetContainingDirectory() const override {
return parent;
}
bool IsWritable() const override {
return false;
}
bool IsReadable() const override {
return true;
}
size_t Read(u8* data, size_t length, size_t offset) const override {
const auto read = std::min(length, size - offset);
std::fill(data, data + read, value);
return read;
}
size_t Write(const u8* data, size_t length, size_t offset) override {
return 0;
}
boost::optional<u8> ReadByte(size_t offset) const override {
if (offset < size)
return value;
return boost::none;
}
std::vector<u8> ReadBytes(size_t length, size_t offset) const override {
const auto read = std::min(length, size - offset);
return std::vector<u8>(read, value);
}
bool Rename(std::string_view new_name) override {
name = new_name;
return true;
}
private:
size_t size;
std::string name;
VirtualDir parent;
};
} // namespace FileSys
+56
View File
@@ -3,16 +3,72 @@
// Refer to the license.txt file included.
#include <algorithm>
#include <cstring>
#include <utility>
#include "core/file_sys/vfs_vector.h"
namespace FileSys {
VectorVfsFile::VectorVfsFile(std::vector<u8> initial_data, std::string name, VirtualDir parent)
: data(std::move(initial_data)), name(std::move(name)), parent(std::move(parent)) {}
VectorVfsFile::~VectorVfsFile() = default;
std::string VectorVfsFile::GetName() const {
return name;
}
size_t VectorVfsFile::GetSize() const {
return data.size();
}
bool VectorVfsFile::Resize(size_t new_size) {
data.resize(new_size);
return true;
}
std::shared_ptr<VfsDirectory> VectorVfsFile::GetContainingDirectory() const {
return parent;
}
bool VectorVfsFile::IsWritable() const {
return true;
}
bool VectorVfsFile::IsReadable() const {
return true;
}
size_t VectorVfsFile::Read(u8* data_, size_t length, size_t offset) const {
const auto read = std::min(length, data.size() - offset);
std::memcpy(data_, data.data() + offset, read);
return read;
}
size_t VectorVfsFile::Write(const u8* data_, size_t length, size_t offset) {
if (offset + length > data.size())
data.resize(offset + length);
const auto write = std::min(length, data.size() - offset);
std::memcpy(data.data(), data_, write);
return write;
}
bool VectorVfsFile::Rename(std::string_view name_) {
name = name_;
return true;
}
void VectorVfsFile::Assign(std::vector<u8> new_data) {
data = std::move(new_data);
}
VectorVfsDirectory::VectorVfsDirectory(std::vector<VirtualFile> files_,
std::vector<VirtualDir> dirs_, std::string name_,
VirtualDir parent_)
: files(std::move(files_)), dirs(std::move(dirs_)), parent(std::move(parent_)),
name(std::move(name_)) {}
VectorVfsDirectory::~VectorVfsDirectory() = default;
std::vector<std::shared_ptr<VfsFile>> VectorVfsDirectory::GetFiles() const {
return files;
}
+26
View File
@@ -8,6 +8,31 @@
namespace FileSys {
// An implementation of VfsFile that is backed by a vector optionally supplied upon construction
class VectorVfsFile : public VfsFile {
public:
explicit VectorVfsFile(std::vector<u8> initial_data = {}, std::string name = "",
VirtualDir parent = nullptr);
~VectorVfsFile() override;
std::string GetName() const override;
size_t GetSize() const override;
bool Resize(size_t new_size) override;
std::shared_ptr<VfsDirectory> GetContainingDirectory() const override;
bool IsWritable() const override;
bool IsReadable() const override;
size_t Read(u8* data, size_t length, size_t offset) const override;
size_t Write(const u8* data, size_t length, size_t offset) override;
bool Rename(std::string_view name) override;
virtual void Assign(std::vector<u8> new_data);
private:
std::vector<u8> data;
VirtualDir parent;
std::string name;
};
// An implementation of VfsDirectory that maintains two vectors for subdirectories and files.
// Vector data is supplied upon construction.
class VectorVfsDirectory : public VfsDirectory {
@@ -15,6 +40,7 @@ public:
explicit VectorVfsDirectory(std::vector<VirtualFile> files = {},
std::vector<VirtualDir> dirs = {}, std::string name = "",
VirtualDir parent = nullptr);
~VectorVfsDirectory() override;
std::vector<std::shared_ptr<VfsFile>> GetFiles() const override;
std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override;
+6 -7
View File
@@ -30,9 +30,6 @@ static bool CalculateHMAC256(Destination* out, const SourceKey* key, std::size_t
mbedtls_md_context_t context;
mbedtls_md_init(&context);
const auto key_f = reinterpret_cast<const u8*>(key);
const std::vector<u8> key_v(key_f, key_f + key_length);
if (mbedtls_md_setup(&context, mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), 1) ||
mbedtls_md_hmac_starts(&context, reinterpret_cast<const u8*>(key), key_length) ||
mbedtls_md_hmac_update(&context, reinterpret_cast<const u8*>(data), data_length) ||
@@ -45,7 +42,7 @@ static bool CalculateHMAC256(Destination* out, const SourceKey* key, std::size_t
return true;
}
NAX::NAX(VirtualFile file_) : file(std::move(file_)), header(std::make_unique<NAXHeader>()) {
NAX::NAX(VirtualFile file_) : header(std::make_unique<NAXHeader>()), file(std::move(file_)) {
std::string path = FileUtil::SanitizePath(file->GetFullPath());
static const std::regex nax_path_regex("/registered/(000000[0-9A-F]{2})/([0-9A-F]{32})\\.nca",
std::regex_constants::ECMAScript |
@@ -65,13 +62,15 @@ NAX::NAX(VirtualFile file_) : file(std::move(file_)), header(std::make_unique<NA
}
NAX::NAX(VirtualFile file_, std::array<u8, 0x10> nca_id)
: file(std::move(file_)), header(std::make_unique<NAXHeader>()) {
: header(std::make_unique<NAXHeader>()), file(std::move(file_)) {
Core::Crypto::SHA256Hash hash{};
mbedtls_sha256(nca_id.data(), nca_id.size(), hash.data(), 0);
status = Parse(fmt::format("/registered/000000{:02X}/{}.nca", hash[0],
Common::HexArrayToString(nca_id, false)));
}
NAX::~NAX() = default;
Loader::ResultStatus NAX::Parse(std::string_view path) {
if (file->ReadObject(header.get()) != sizeof(NAXHeader))
return Loader::ResultStatus::ErrorBadNAXHeader;
@@ -138,9 +137,9 @@ VirtualFile NAX::GetDecrypted() const {
return dec_file;
}
std::shared_ptr<NCA> NAX::AsNCA() const {
std::unique_ptr<NCA> NAX::AsNCA() const {
if (type == NAXContentType::NCA)
return std::make_shared<NCA>(GetDecrypted());
return std::make_unique<NCA>(GetDecrypted());
return nullptr;
}
+3 -2
View File
@@ -33,12 +33,13 @@ class NAX : public ReadOnlyVfsDirectory {
public:
explicit NAX(VirtualFile file);
explicit NAX(VirtualFile file, std::array<u8, 0x10> nca_id);
~NAX() override;
Loader::ResultStatus GetStatus() const;
VirtualFile GetDecrypted() const;
std::shared_ptr<NCA> AsNCA() const;
std::unique_ptr<NCA> AsNCA() const;
NAXContentType GetContentType() const;
@@ -60,7 +61,7 @@ private:
VirtualFile file;
Loader::ResultStatus status;
NAXContentType type;
NAXContentType type{};
VirtualFile dec_file;
+1 -1
View File
@@ -995,7 +995,7 @@ static bool CommitBreakpoint(BreakpointType type, VAddr addr, u64 len) {
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}};
static constexpr std::array<u8, 4> btrap{{0x00, 0x7d, 0x20, 0xd4}};
Memory::WriteBlock(addr, btrap.data(), btrap.size());
Core::System::GetInstance().InvalidateCpuInstructionCaches();
p.insert({addr, breakpoint});
+1
View File
@@ -16,6 +16,7 @@
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/result.h"
#include "core/memory.h"
namespace Kernel {
+88 -1
View File
@@ -7,10 +7,12 @@
#include "common/assert.h"
#include "common/common_funcs.h"
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/resource_limit.h"
#include "core/hle/kernel/scheduler.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/kernel/vm_manager.h"
#include "core/memory.h"
@@ -125,7 +127,92 @@ void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) {
vm_manager.LogLayout();
status = ProcessStatus::Running;
Kernel::SetupMainThread(kernel, entry_point, main_thread_priority, this);
Kernel::SetupMainThread(kernel, entry_point, main_thread_priority, *this);
}
void Process::PrepareForTermination() {
status = ProcessStatus::Exited;
const auto stop_threads = [this](const std::vector<SharedPtr<Thread>>& thread_list) {
for (auto& thread : thread_list) {
if (thread->owner_process != this)
continue;
if (thread == GetCurrentThread())
continue;
// TODO(Subv): When are the other running/ready threads terminated?
ASSERT_MSG(thread->status == ThreadStatus::WaitSynchAny ||
thread->status == ThreadStatus::WaitSynchAll,
"Exiting processes with non-waiting threads is currently unimplemented");
thread->Stop();
}
};
auto& system = Core::System::GetInstance();
stop_threads(system.Scheduler(0)->GetThreadList());
stop_threads(system.Scheduler(1)->GetThreadList());
stop_threads(system.Scheduler(2)->GetThreadList());
stop_threads(system.Scheduler(3)->GetThreadList());
}
/**
* Finds a free location for the TLS section of a thread.
* @param tls_slots The TLS page array of the thread's owner process.
* Returns a tuple of (page, slot, alloc_needed) where:
* page: The index of the first allocated TLS page that has free slots.
* 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).
*/
static std::tuple<std::size_t, std::size_t, bool> FindFreeThreadLocalSlot(
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 (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 (std::size_t slot = 0; slot < page_tls_slots.size(); ++slot) {
if (!page_tls_slots.test(slot)) {
return std::make_tuple(page, slot, false);
}
}
}
}
return std::make_tuple(0, 0, true);
}
VAddr Process::MarkNextAvailableTLSSlotAsUsed(Thread& thread) {
auto [available_page, available_slot, needs_allocation] = FindFreeThreadLocalSlot(tls_slots);
if (needs_allocation) {
tls_slots.emplace_back(0); // The page is completely available at the start
available_page = tls_slots.size() - 1;
available_slot = 0; // Use the first slot in the new page
// Allocate some memory from the end of the linear heap for this region.
auto& tls_memory = thread.GetTLSMemory();
tls_memory->insert(tls_memory->end(), Memory::PAGE_SIZE, 0);
vm_manager.RefreshMemoryBlockMappings(tls_memory.get());
vm_manager.MapMemoryBlock(Memory::TLS_AREA_VADDR + available_page * Memory::PAGE_SIZE,
tls_memory, 0, Memory::PAGE_SIZE, MemoryState::ThreadLocal);
}
tls_slots[available_page].set(available_slot);
return Memory::TLS_AREA_VADDR + available_page * Memory::PAGE_SIZE +
available_slot * Memory::TLS_ENTRY_SIZE;
}
void Process::FreeTLSSlot(VAddr tls_address) {
const VAddr tls_base = tls_address - Memory::TLS_AREA_VADDR;
const VAddr tls_page = tls_base / Memory::PAGE_SIZE;
const VAddr tls_slot = (tls_base % Memory::PAGE_SIZE) / Memory::TLS_ENTRY_SIZE;
tls_slots[tls_page].reset(tls_slot);
}
void Process::LoadModule(SharedPtr<CodeSet> module_, VAddr base_addr) {
+39 -16
View File
@@ -131,6 +131,16 @@ public:
return HANDLE_TYPE;
}
/// Gets the current status of the process
ProcessStatus GetStatus() const {
return status;
}
/// Gets the unique ID that identifies this particular process.
u32 GetProcessID() const {
return process_id;
}
/// Title ID corresponding to the process
u64 program_id;
@@ -154,11 +164,6 @@ public:
u32 allowed_processor_mask = THREADPROCESSORID_DEFAULT_MASK;
u32 allowed_thread_priority_mask = 0xFFFFFFFF;
u32 is_virtual_address_memory_enabled = 0;
/// Current status of the process
ProcessStatus status;
/// The ID of this process
u32 process_id = 0;
/**
* Parses a list of kernel capability descriptors (as found in the ExHeader) and applies them
@@ -171,13 +176,42 @@ public:
*/
void Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size);
/**
* Prepares a process for termination by stopping all of its threads
* and clearing any other resources.
*/
void PrepareForTermination();
void LoadModule(SharedPtr<CodeSet> module_, VAddr base_addr);
///////////////////////////////////////////////////////////////////////////////////////////////
// Memory Management
// Marks the next available region as used and returns the address of the slot.
VAddr MarkNextAvailableTLSSlotAsUsed(Thread& thread);
// Frees a used TLS slot identified by the given address
void FreeTLSSlot(VAddr tls_address);
ResultVal<VAddr> HeapAllocate(VAddr target, u64 size, VMAPermission perms);
ResultCode HeapFree(VAddr target, u32 size);
ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size);
ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size);
VMManager vm_manager;
private:
explicit Process(KernelCore& kernel);
~Process() override;
/// Current status of the process
ProcessStatus status;
/// The ID of this process
u32 process_id = 0;
// Memory used to back the allocations in the regular heap. A single vector is used to cover
// the entire virtual address space extents that bound the allocations, including any holes.
// This makes deallocation and reallocation of holes fast and keeps process memory contiguous
@@ -197,17 +231,6 @@ public:
std::vector<std::bitset<8>> tls_slots;
std::string name;
ResultVal<VAddr> HeapAllocate(VAddr target, u64 size, VMAPermission perms);
ResultCode HeapFree(VAddr target, u32 size);
ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size);
ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size);
private:
explicit Process(KernelCore& kernel);
~Process() override;
};
} // namespace Kernel
+12 -34
View File
@@ -169,7 +169,7 @@ static ResultCode GetProcessId(u32* process_id, Handle process_handle) {
return ERR_INVALID_HANDLE;
}
*process_id = process->process_id;
*process_id = process->GetProcessID();
return RESULT_SUCCESS;
}
@@ -530,35 +530,13 @@ static ResultCode QueryMemory(MemoryInfo* memory_info, PageInfo* page_info, VAdd
/// Exits the current process
static void ExitProcess() {
LOG_INFO(Kernel_SVC, "Process {} exiting", Core::CurrentProcess()->process_id);
auto& current_process = Core::CurrentProcess();
ASSERT_MSG(Core::CurrentProcess()->status == ProcessStatus::Running,
LOG_INFO(Kernel_SVC, "Process {} exiting", current_process->GetProcessID());
ASSERT_MSG(current_process->GetStatus() == ProcessStatus::Running,
"Process has already exited");
Core::CurrentProcess()->status = ProcessStatus::Exited;
auto stop_threads = [](const std::vector<SharedPtr<Thread>>& thread_list) {
for (auto& thread : thread_list) {
if (thread->owner_process != Core::CurrentProcess())
continue;
if (thread == GetCurrentThread())
continue;
// TODO(Subv): When are the other running/ready threads terminated?
ASSERT_MSG(thread->status == ThreadStatus::WaitSynchAny ||
thread->status == ThreadStatus::WaitSynchAll,
"Exiting processes with non-waiting threads is currently unimplemented");
thread->Stop();
}
};
auto& system = Core::System::GetInstance();
stop_threads(system.Scheduler(0)->GetThreadList());
stop_threads(system.Scheduler(1)->GetThreadList());
stop_threads(system.Scheduler(2)->GetThreadList());
stop_threads(system.Scheduler(3)->GetThreadList());
current_process->PrepareForTermination();
// Kill the current thread
GetCurrentThread()->Stop();
@@ -1039,7 +1017,7 @@ static const FunctionDef SVC_Table[] = {
{0x2B, nullptr, "FlushDataCache"},
{0x2C, nullptr, "MapPhysicalMemory"},
{0x2D, nullptr, "UnmapPhysicalMemory"},
{0x2E, nullptr, "GetNextThreadInfo"},
{0x2E, nullptr, "GetFutureThreadInfo"},
{0x2F, nullptr, "GetLastThreadInfo"},
{0x30, nullptr, "GetResourceLimitLimitValue"},
{0x31, nullptr, "GetResourceLimitCurrentValue"},
@@ -1065,11 +1043,11 @@ static const FunctionDef SVC_Table[] = {
{0x45, nullptr, "CreateEvent"},
{0x46, nullptr, "Unknown"},
{0x47, nullptr, "Unknown"},
{0x48, nullptr, "AllocateUnsafeMemory"},
{0x49, nullptr, "FreeUnsafeMemory"},
{0x4A, nullptr, "SetUnsafeAllocationLimit"},
{0x4B, nullptr, "CreateJitMemory"},
{0x4C, nullptr, "MapJitMemory"},
{0x48, nullptr, "MapPhysicalMemoryUnsafe"},
{0x49, nullptr, "UnmapPhysicalMemoryUnsafe"},
{0x4A, nullptr, "SetUnsafeLimit"},
{0x4B, nullptr, "CreateCodeMemory"},
{0x4C, nullptr, "ControlCodeMemory"},
{0x4D, nullptr, "SleepSystem"},
{0x4E, nullptr, "ReadWriteRegister"},
{0x4F, nullptr, "SetProcessActivity"},
@@ -1104,7 +1082,7 @@ static const FunctionDef SVC_Table[] = {
{0x6C, nullptr, "SetHardwareBreakPoint"},
{0x6D, nullptr, "GetDebugThreadParam"},
{0x6E, nullptr, "Unknown"},
{0x6F, nullptr, "GetMemoryInfo"},
{0x6F, nullptr, "GetSystemInfo"},
{0x70, nullptr, "CreatePort"},
{0x71, nullptr, "ManageNamedPort"},
{0x72, nullptr, "ConnectToPort"},
+5 -59
View File
@@ -65,10 +65,7 @@ void Thread::Stop() {
wait_objects.clear();
// Mark the TLS slot in the thread's page as free.
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);
owner_process->FreeTLSSlot(tls_address);
}
void WaitCurrentThread_Sleep() {
@@ -177,32 +174,6 @@ void Thread::ResumeFromWait() {
Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule();
}
/**
* Finds a free location for the TLS section of a thread.
* @param tls_slots The TLS page array of the thread's owner process.
* Returns a tuple of (page, slot, alloc_needed) where:
* page: The index of the first allocated TLS page that has free slots.
* 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).
*/
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 (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 (std::size_t slot = 0; slot < page_tls_slots.size(); ++slot) {
if (!page_tls_slots.test(slot)) {
return std::make_tuple(page, slot, false);
}
}
}
}
return std::make_tuple(0, 0, true);
}
/**
* Resets a thread context, making it ready to be scheduled and run by the CPU
* @param context Thread context to reset
@@ -264,32 +235,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name
thread->owner_process = owner_process;
thread->scheduler = Core::System::GetInstance().Scheduler(processor_id);
thread->scheduler->AddThread(thread, priority);
// Find the next available TLS index, and mark it as used
auto& tls_slots = owner_process->tls_slots;
auto [available_page, available_slot, needs_allocation] = GetFreeThreadLocalSlot(tls_slots);
if (needs_allocation) {
tls_slots.emplace_back(0); // The page is completely available at the start
available_page = tls_slots.size() - 1;
available_slot = 0; // Use the first slot in the new page
// Allocate some memory from the end of the linear heap for this region.
const std::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());
vm_manager.MapMemoryBlock(Memory::TLS_AREA_VADDR + available_page * Memory::PAGE_SIZE,
thread->tls_memory, 0, Memory::PAGE_SIZE,
MemoryState::ThreadLocal);
}
// Mark the slot as used
tls_slots[available_page].set(available_slot);
thread->tls_address = Memory::TLS_AREA_VADDR + available_page * Memory::PAGE_SIZE +
available_slot * Memory::TLS_ENTRY_SIZE;
thread->tls_address = thread->owner_process->MarkNextAvailableTLSSlotAsUsed(*thread);
// TODO(peachum): move to ScheduleThread() when scheduler is added so selected core is used
// to initialize the context
@@ -311,13 +257,13 @@ void Thread::BoostPriority(u32 priority) {
}
SharedPtr<Thread> SetupMainThread(KernelCore& kernel, VAddr entry_point, u32 priority,
SharedPtr<Process> owner_process) {
Process& owner_process) {
// Setup page table so we can write to memory
SetCurrentPageTable(&Core::CurrentProcess()->vm_manager.page_table);
SetCurrentPageTable(&owner_process.vm_manager.page_table);
// Initialize new "main" thread
auto thread_res = Thread::Create(kernel, "main", entry_point, priority, 0, THREADPROCESSORID_0,
Memory::STACK_AREA_VADDR_END, std::move(owner_process));
Memory::STACK_AREA_VADDR_END, &owner_process);
SharedPtr<Thread> thread = std::move(thread_res).Unwrap();
+13 -2
View File
@@ -62,6 +62,9 @@ enum class ThreadWakeupReason {
class Thread final : public WaitObject {
public:
using TLSMemory = std::vector<u8>;
using TLSMemoryPtr = std::shared_ptr<TLSMemory>;
/**
* Creates and returns a new thread. The new thread is immediately scheduled
* @param kernel The kernel instance this thread will be created under.
@@ -134,6 +137,14 @@ public:
return thread_id;
}
TLSMemoryPtr& GetTLSMemory() {
return tls_memory;
}
const TLSMemoryPtr& GetTLSMemory() const {
return tls_memory;
}
/**
* Resumes a thread from waiting
*/
@@ -269,7 +280,7 @@ private:
explicit Thread(KernelCore& kernel);
~Thread() override;
std::shared_ptr<std::vector<u8>> tls_memory = std::make_shared<std::vector<u8>>();
TLSMemoryPtr tls_memory = std::make_shared<TLSMemory>();
};
/**
@@ -281,7 +292,7 @@ private:
* @return A shared pointer to the main thread
*/
SharedPtr<Thread> SetupMainThread(KernelCore& kernel, VAddr entry_point, u32 priority,
SharedPtr<Process> owner_process);
Process& owner_process);
/**
* Gets the current thread
+4 -5
View File
@@ -130,11 +130,10 @@ private:
void GetAccountId(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_ACC, "(STUBBED) called");
// TODO(Subv): Find out what this actually does and implement it. Stub it as an error for
// now since we do not implement NNID. Returning a bogus id here will cause games to send
// invalid IPC requests after ListOpenUsers is called.
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultCode(-1));
// Should return a nintendo account ID
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.PushRaw<u64>(1);
}
};
+8 -1
View File
@@ -25,7 +25,7 @@ public:
{0, &IAudioRenderer::GetAudioRendererSampleRate, "GetAudioRendererSampleRate"},
{1, &IAudioRenderer::GetAudioRendererSampleCount, "GetAudioRendererSampleCount"},
{2, &IAudioRenderer::GetAudioRendererMixBufferCount, "GetAudioRendererMixBufferCount"},
{3, nullptr, "GetAudioRendererState"},
{3, &IAudioRenderer::GetAudioRendererState, "GetAudioRendererState"},
{4, &IAudioRenderer::RequestUpdateAudioRenderer, "RequestUpdateAudioRenderer"},
{5, &IAudioRenderer::StartAudioRenderer, "StartAudioRenderer"},
{6, &IAudioRenderer::StopAudioRenderer, "StopAudioRenderer"},
@@ -62,6 +62,13 @@ private:
LOG_DEBUG(Service_Audio, "called");
}
void GetAudioRendererState(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(renderer->GetState());
LOG_DEBUG(Service_Audio, "called");
}
void GetAudioRendererMixBufferCount(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
+138 -3
View File
@@ -2,8 +2,17 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <array>
#include <cstring>
#include <ctime>
#include <fmt/time.h>
#include "common/common_paths.h"
#include "common/file_util.h"
#include "common/logging/log.h"
#include "common/scm_rev.h"
#include "common/swap.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/process.h"
#include "core/hle/service/fatal/fatal.h"
#include "core/hle/service/fatal/fatal_p.h"
#include "core/hle/service/fatal/fatal_u.h"
@@ -15,16 +24,142 @@ Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
Module::Interface::~Interface() = default;
struct FatalInfo {
std::array<u64_le, 31> registers{}; // TODO(ogniK): See if this actually is registers or
// not(find a game which has non zero valeus)
u64_le unk0{};
u64_le unk1{};
u64_le unk2{};
u64_le unk3{};
u64_le unk4{};
u64_le unk5{};
u64_le unk6{};
std::array<u64_le, 32> backtrace{};
u64_le unk7{};
u64_le unk8{};
u32_le backtrace_size{};
u32_le unk9{};
u32_le unk10{}; // TODO(ogniK): Is this even used or is it just padding?
};
static_assert(sizeof(FatalInfo) == 0x250, "FatalInfo is an invalid size");
enum class FatalType : u32 {
ErrorReportAndScreen = 0,
ErrorReport = 1,
ErrorScreen = 2,
};
static void GenerateErrorReport(ResultCode error_code, const FatalInfo& info) {
const auto title_id = Core::CurrentProcess()->program_id;
std::string crash_report =
fmt::format("Yuzu {}-{} crash report\n"
"Title ID: {:016x}\n"
"Result: 0x{:X} ({:04}-{:04d})\n"
"\n",
Common::g_scm_branch, Common::g_scm_desc, title_id, error_code.raw,
2000 + static_cast<u32>(error_code.module.Value()),
static_cast<u32>(error_code.description.Value()), info.unk8, info.unk7);
if (info.backtrace_size != 0x0) {
crash_report += "Registers:\n";
// TODO(ogniK): This is just a guess, find a game which actually has non zero values
for (size_t i = 0; i < info.registers.size(); i++) {
crash_report +=
fmt::format(" X[{:02d}]: {:016x}\n", i, info.registers[i]);
}
crash_report += fmt::format(" Unknown 0: {:016x}\n", info.unk0);
crash_report += fmt::format(" Unknown 1: {:016x}\n", info.unk1);
crash_report += fmt::format(" Unknown 2: {:016x}\n", info.unk2);
crash_report += fmt::format(" Unknown 3: {:016x}\n", info.unk3);
crash_report += fmt::format(" Unknown 4: {:016x}\n", info.unk4);
crash_report += fmt::format(" Unknown 5: {:016x}\n", info.unk5);
crash_report += fmt::format(" Unknown 6: {:016x}\n", info.unk6);
crash_report += "\nBacktrace:\n";
for (size_t i = 0; i < info.backtrace_size; i++) {
crash_report +=
fmt::format(" Backtrace[{:02d}]: {:016x}\n", i, info.backtrace[i]);
}
crash_report += fmt::format("\nUnknown 7: 0x{:016x}\n", info.unk7);
crash_report += fmt::format("Unknown 8: 0x{:016x}\n", info.unk8);
crash_report += fmt::format("Unknown 9: 0x{:016x}\n", info.unk9);
crash_report += fmt::format("Unknown 10: 0x{:016x}\n", info.unk10);
}
LOG_ERROR(Service_Fatal, "{}", crash_report);
const std::string crashreport_dir =
FileUtil::GetUserPath(FileUtil::UserPath::LogDir) + "crash_logs";
if (!FileUtil::CreateFullPath(crashreport_dir)) {
LOG_ERROR(
Service_Fatal,
"Unable to create crash report directory. Possible log directory permissions issue.");
return;
}
const std::time_t t = std::time(nullptr);
const std::string crashreport_filename =
fmt::format("{}/{:016x}-{:%F-%H%M%S}.log", crashreport_dir, title_id, *std::localtime(&t));
auto file = FileUtil::IOFile(crashreport_filename, "wb");
if (file.IsOpen()) {
file.WriteString(crash_report);
LOG_ERROR(Service_Fatal, "Saving error report to {}", crashreport_filename);
} else {
LOG_ERROR(Service_Fatal, "Failed to save error report to {}", crashreport_filename);
}
}
static void ThrowFatalError(ResultCode error_code, FatalType fatal_type, const FatalInfo& info) {
LOG_ERROR(Service_Fatal, "Threw fatal error type {}", static_cast<u32>(fatal_type));
switch (fatal_type) {
case FatalType::ErrorReportAndScreen:
GenerateErrorReport(error_code, info);
[[fallthrough]];
case FatalType::ErrorScreen:
// Since we have no fatal:u error screen. We should just kill execution instead
ASSERT(false);
break;
// Should not throw a fatal screen but should generate an error report
case FatalType::ErrorReport:
GenerateErrorReport(error_code, info);
break;
};
}
void Module::Interface::ThrowFatal(Kernel::HLERequestContext& ctx) {
LOG_ERROR(Service_Fatal, "called");
IPC::RequestParser rp{ctx};
auto error_code = rp.Pop<ResultCode>();
ThrowFatalError(error_code, FatalType::ErrorScreen, {});
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void Module::Interface::ThrowFatalWithPolicy(Kernel::HLERequestContext& ctx) {
LOG_ERROR(Service_Fatal, "called");
IPC::RequestParser rp(ctx);
u32 error_code = rp.Pop<u32>();
LOG_WARNING(Service_Fatal, "(STUBBED) called, error_code=0x{:X}", error_code);
auto error_code = rp.Pop<ResultCode>();
auto fatal_type = rp.PopEnum<FatalType>();
ThrowFatalError(error_code, fatal_type, {}); // No info is passed with ThrowFatalWithPolicy
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void Module::Interface::ThrowFatalWithCpuContext(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_Fatal, "(STUBBED) called");
LOG_ERROR(Service_Fatal, "called");
IPC::RequestParser rp(ctx);
auto error_code = rp.Pop<ResultCode>();
auto fatal_type = rp.PopEnum<FatalType>();
auto fatal_info = ctx.ReadBuffer();
FatalInfo info{};
ASSERT_MSG(fatal_info.size() == sizeof(FatalInfo), "Invalid fatal info buffer size!");
std::memcpy(&info, fatal_info.data(), sizeof(FatalInfo));
ThrowFatalError(error_code, fatal_type, info);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
+1
View File
@@ -15,6 +15,7 @@ public:
explicit Interface(std::shared_ptr<Module> module, const char* name);
~Interface() override;
void ThrowFatal(Kernel::HLERequestContext& ctx);
void ThrowFatalWithPolicy(Kernel::HLERequestContext& ctx);
void ThrowFatalWithCpuContext(Kernel::HLERequestContext& ctx);
+1 -1
View File
@@ -8,7 +8,7 @@ namespace Service::Fatal {
Fatal_U::Fatal_U(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "fatal:u") {
static const FunctionInfo functions[] = {
{0, nullptr, "ThrowFatal"},
{0, &Fatal_U::ThrowFatal, "ThrowFatal"},
{1, &Fatal_U::ThrowFatalWithPolicy, "ThrowFatalWithPolicy"},
{2, &Fatal_U::ThrowFatalWithCpuContext, "ThrowFatalWithCpuContext"},
};
+13 -2
View File
@@ -197,7 +197,7 @@ ResultVal<FileSys::VirtualDir> VfsDirectoryServiceWrapper::OpenDirectory(const s
auto dir = GetDirectoryRelativeWrapped(backing, path);
if (dir == nullptr) {
// TODO(DarkLordZach): Find a better error code for this
return ResultCode(-1);
return FileSys::ERROR_PATH_NOT_FOUND;
}
return MakeResult(dir);
}
@@ -343,6 +343,15 @@ std::shared_ptr<FileSys::RegisteredCache> GetSDMCContents() {
return sdmc_factory->GetSDMCContents();
}
FileSys::VirtualDir GetModificationLoadRoot(u64 title_id) {
LOG_TRACE(Service_FS, "Opening mod load root for tid={:016X}", title_id);
if (bis_factory == nullptr)
return nullptr;
return bis_factory->GetModificationLoadRoot(title_id);
}
void CreateFactories(const FileSys::VirtualFilesystem& vfs, bool overwrite) {
if (overwrite) {
bis_factory = nullptr;
@@ -354,9 +363,11 @@ void CreateFactories(const FileSys::VirtualFilesystem& vfs, bool overwrite) {
FileSys::Mode::ReadWrite);
auto sd_directory = vfs->OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir),
FileSys::Mode::ReadWrite);
auto load_directory = vfs->OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::LoadDir),
FileSys::Mode::ReadWrite);
if (bis_factory == nullptr)
bis_factory = std::make_unique<FileSys::BISFactory>(nand_directory);
bis_factory = std::make_unique<FileSys::BISFactory>(nand_directory, load_directory);
if (save_data_factory == nullptr)
save_data_factory = std::make_unique<FileSys::SaveDataFactory>(std::move(nand_directory));
if (sdmc_factory == nullptr)
@@ -52,6 +52,8 @@ std::shared_ptr<FileSys::RegisteredCache> GetSystemNANDContents();
std::shared_ptr<FileSys::RegisteredCache> GetUserNANDContents();
std::shared_ptr<FileSys::RegisteredCache> GetSDMCContents();
FileSys::VirtualDir GetModificationLoadRoot(u64 title_id);
// Creates the SaveData, SDMC, and BIS Factories. Should be called once and before any function
// above is called.
void CreateFactories(const FileSys::VirtualFilesystem& vfs, bool overwrite = true);
+7 -1
View File
@@ -338,7 +338,7 @@ public:
{106, &Hid::AcquireNpadStyleSetUpdateEventHandle, "AcquireNpadStyleSetUpdateEventHandle"},
{107, &Hid::DisconnectNpad, "DisconnectNpad"},
{108, &Hid::GetPlayerLedPattern, "GetPlayerLedPattern"},
{109, nullptr, "ActivateNpadWithRevision"},
{109, &Hid::ActivateNpadWithRevision, "ActivateNpadWithRevision"},
{120, &Hid::SetNpadJoyHoldType, "SetNpadJoyHoldType"},
{121, &Hid::GetNpadJoyHoldType, "GetNpadJoyHoldType"},
{122, &Hid::SetNpadJoyAssignmentModeSingleByDefault, "SetNpadJoyAssignmentModeSingleByDefault"},
@@ -603,6 +603,12 @@ private:
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_HID, "(STUBBED) called");
}
void ActivateNpadWithRevision(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_HID, "(STUBBED) called");
}
};
class HidDbg final : public ServiceFramework<HidDbg> {
+140 -18
View File
@@ -2,6 +2,11 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/swap.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/shared_memory.h"
#include "core/hle/service/hid/irs.h"
namespace Service::HID {
@@ -9,28 +14,145 @@ namespace Service::HID {
IRS::IRS() : ServiceFramework{"irs"} {
// clang-format off
static const FunctionInfo functions[] = {
{302, nullptr, "ActivateIrsensor"},
{303, nullptr, "DeactivateIrsensor"},
{304, nullptr, "GetIrsensorSharedMemoryHandle"},
{305, nullptr, "StopImageProcessor"},
{306, nullptr, "RunMomentProcessor"},
{307, nullptr, "RunClusteringProcessor"},
{308, nullptr, "RunImageTransferProcessor"},
{309, nullptr, "GetImageTransferProcessorState"},
{310, nullptr, "RunTeraPluginProcessor"},
{311, nullptr, "GetNpadIrCameraHandle"},
{312, nullptr, "RunPointingProcessor"},
{313, nullptr, "SuspendImageProcessor"},
{314, nullptr, "CheckFirmwareVersion"},
{315, nullptr, "SetFunctionLevel"},
{316, nullptr, "RunImageTransferExProcessor"},
{317, nullptr, "RunIrLedProcessor"},
{318, nullptr, "StopImageProcessorAsync"},
{319, nullptr, "ActivateIrsensorWithFunctionLevel"},
{302, &IRS::ActivateIrsensor, "ActivateIrsensor"},
{303, &IRS::DeactivateIrsensor, "DeactivateIrsensor"},
{304, &IRS::GetIrsensorSharedMemoryHandle, "GetIrsensorSharedMemoryHandle"},
{305, &IRS::StopImageProcessor, "StopImageProcessor"},
{306, &IRS::RunMomentProcessor, "RunMomentProcessor"},
{307, &IRS::RunClusteringProcessor, "RunClusteringProcessor"},
{308, &IRS::RunImageTransferProcessor, "RunImageTransferProcessor"},
{309, &IRS::GetImageTransferProcessorState, "GetImageTransferProcessorState"},
{310, &IRS::RunTeraPluginProcessor, "RunTeraPluginProcessor"},
{311, &IRS::GetNpadIrCameraHandle, "GetNpadIrCameraHandle"},
{312, &IRS::RunPointingProcessor, "RunPointingProcessor"},
{313, &IRS::SuspendImageProcessor, "SuspendImageProcessor"},
{314, &IRS::CheckFirmwareVersion, "CheckFirmwareVersion"},
{315, &IRS::SetFunctionLevel, "SetFunctionLevel"},
{316, &IRS::RunImageTransferExProcessor, "RunImageTransferExProcessor"},
{317, &IRS::RunIrLedProcessor, "RunIrLedProcessor"},
{318, &IRS::StopImageProcessorAsync, "StopImageProcessorAsync"},
{319, &IRS::ActivateIrsensorWithFunctionLevel, "ActivateIrsensorWithFunctionLevel"},
};
// clang-format on
RegisterHandlers(functions);
auto& kernel = Core::System::GetInstance().Kernel();
shared_mem = Kernel::SharedMemory::Create(
kernel, nullptr, 0x8000, Kernel::MemoryPermission::ReadWrite,
Kernel::MemoryPermission::Read, 0, Kernel::MemoryRegion::BASE, "IRS:SharedMemory");
}
void IRS::ActivateIrsensor(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_IRS, "(STUBBED) called");
}
void IRS::DeactivateIrsensor(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_IRS, "(STUBBED) called");
}
void IRS::GetIrsensorSharedMemoryHandle(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
rb.PushCopyObjects(shared_mem);
LOG_DEBUG(Service_IRS, "called");
}
void IRS::StopImageProcessor(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_IRS, "(STUBBED) called");
}
void IRS::RunMomentProcessor(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_IRS, "(STUBBED) called");
}
void IRS::RunClusteringProcessor(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_IRS, "(STUBBED) called");
}
void IRS::RunImageTransferProcessor(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_IRS, "(STUBBED) called");
}
void IRS::GetImageTransferProcessorState(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 5};
rb.Push(RESULT_SUCCESS);
rb.PushRaw<u64>(CoreTiming::GetTicks());
rb.PushRaw<u32>(0);
LOG_WARNING(Service_IRS, "(STUBBED) called");
}
void IRS::RunTeraPluginProcessor(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_IRS, "(STUBBED) called");
}
void IRS::GetNpadIrCameraHandle(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.PushRaw<u32>(device_handle);
LOG_WARNING(Service_IRS, "(STUBBED) called");
}
void IRS::RunPointingProcessor(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_IRS, "(STUBBED) called");
}
void IRS::SuspendImageProcessor(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_IRS, "(STUBBED) called");
}
void IRS::CheckFirmwareVersion(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_IRS, "(STUBBED) called");
}
void IRS::SetFunctionLevel(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_IRS, "(STUBBED) called");
}
void IRS::RunImageTransferExProcessor(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_IRS, "(STUBBED) called");
}
void IRS::RunIrLedProcessor(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_IRS, "(STUBBED) called");
}
void IRS::StopImageProcessorAsync(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_IRS, "(STUBBED) called");
}
void IRS::ActivateIrsensorWithFunctionLevel(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_IRS, "(STUBBED) called");
}
IRS::~IRS() = default;
+27
View File
@@ -4,14 +4,41 @@
#pragma once
#include "core/hle/kernel/object.h"
#include "core/hle/service/service.h"
namespace Kernel {
class SharedMemory;
}
namespace Service::HID {
class IRS final : public ServiceFramework<IRS> {
public:
explicit IRS();
~IRS() override;
private:
void ActivateIrsensor(Kernel::HLERequestContext& ctx);
void DeactivateIrsensor(Kernel::HLERequestContext& ctx);
void GetIrsensorSharedMemoryHandle(Kernel::HLERequestContext& ctx);
void StopImageProcessor(Kernel::HLERequestContext& ctx);
void RunMomentProcessor(Kernel::HLERequestContext& ctx);
void RunClusteringProcessor(Kernel::HLERequestContext& ctx);
void RunImageTransferProcessor(Kernel::HLERequestContext& ctx);
void GetImageTransferProcessorState(Kernel::HLERequestContext& ctx);
void RunTeraPluginProcessor(Kernel::HLERequestContext& ctx);
void GetNpadIrCameraHandle(Kernel::HLERequestContext& ctx);
void RunPointingProcessor(Kernel::HLERequestContext& ctx);
void SuspendImageProcessor(Kernel::HLERequestContext& ctx);
void CheckFirmwareVersion(Kernel::HLERequestContext& ctx);
void SetFunctionLevel(Kernel::HLERequestContext& ctx);
void RunImageTransferExProcessor(Kernel::HLERequestContext& ctx);
void RunIrLedProcessor(Kernel::HLERequestContext& ctx);
void StopImageProcessorAsync(Kernel::HLERequestContext& ctx);
void ActivateIrsensorWithFunctionLevel(Kernel::HLERequestContext& ctx);
Kernel::SharedPtr<Kernel::SharedMemory> shared_mem;
const u32 device_handle{0xABCD};
};
class IRS_SYS final : public ServiceFramework<IRS_SYS> {
+16 -3
View File
@@ -31,7 +31,7 @@ public:
{1, &IRequest::GetResult, "GetResult"},
{2, &IRequest::GetSystemEventReadableHandles, "GetSystemEventReadableHandles"},
{3, &IRequest::Cancel, "Cancel"},
{4, nullptr, "Submit"},
{4, &IRequest::Submit, "Submit"},
{5, nullptr, "SetRequirement"},
{6, nullptr, "SetRequirementPreset"},
{8, nullptr, "SetPriority"},
@@ -61,6 +61,12 @@ public:
}
private:
void Submit(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void GetRequestState(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
@@ -114,10 +120,11 @@ public:
private:
void GetClientId(Kernel::HLERequestContext& ctx) {
static constexpr u32 client_id = 1;
LOG_WARNING(Service_NIFM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push<u64>(0);
rb.Push<u64>(client_id); // Client ID needs to be non zero otherwise it's considered invalid
}
void CreateScanRequest(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
@@ -141,10 +148,16 @@ private:
rb.Push(RESULT_SUCCESS);
}
void CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
ASSERT_MSG(ctx.GetReadBufferSize() == 0x17c, "NetworkProfileData is not the correct size");
u128 uuid{};
auto buffer = ctx.ReadBuffer();
std::memcpy(&uuid, buffer.data() + 8, sizeof(u128));
IPC::ResponseBuilder rb{ctx, 6, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<INetworkProfile>();
rb.PushRaw<u128>(uuid);
LOG_DEBUG(Service_NIFM, "called");
}
+99 -3
View File
@@ -2,6 +2,10 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <chrono>
#include <ctime>
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/event.h"
#include "core/hle/service/nim/nim.h"
#include "core/hle/service/service.h"
#include "core/hle/service/sm/sm.h"
@@ -100,19 +104,111 @@ public:
}
};
class IEnsureNetworkClockAvailabilityService final
: public ServiceFramework<IEnsureNetworkClockAvailabilityService> {
public:
IEnsureNetworkClockAvailabilityService()
: ServiceFramework("IEnsureNetworkClockAvailabilityService") {
static const FunctionInfo functions[] = {
{0, &IEnsureNetworkClockAvailabilityService::StartTask, "StartTask"},
{1, &IEnsureNetworkClockAvailabilityService::GetFinishNotificationEvent,
"GetFinishNotificationEvent"},
{2, &IEnsureNetworkClockAvailabilityService::GetResult, "GetResult"},
{3, &IEnsureNetworkClockAvailabilityService::Cancel, "Cancel"},
{4, &IEnsureNetworkClockAvailabilityService::IsProcessing, "IsProcessing"},
{5, &IEnsureNetworkClockAvailabilityService::GetServerTime, "GetServerTime"},
};
RegisterHandlers(functions);
auto& kernel = Core::System::GetInstance().Kernel();
finished_event =
Kernel::Event::Create(kernel, Kernel::ResetType::OneShot,
"IEnsureNetworkClockAvailabilityService:FinishEvent");
}
private:
Kernel::SharedPtr<Kernel::Event> finished_event;
void StartTask(Kernel::HLERequestContext& ctx) {
// No need to connect to the internet, just finish the task straight away.
finished_event->Signal();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_DEBUG(Service_NIM, "called");
}
void GetFinishNotificationEvent(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
rb.PushCopyObjects(finished_event);
LOG_DEBUG(Service_NIM, "called");
}
void GetResult(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_DEBUG(Service_NIM, "called");
}
void Cancel(Kernel::HLERequestContext& ctx) {
finished_event->Clear();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_DEBUG(Service_NIM, "called");
}
void IsProcessing(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.PushRaw<u32>(0); // We instantly process the request
LOG_DEBUG(Service_NIM, "called");
}
void GetServerTime(Kernel::HLERequestContext& ctx) {
const s64 server_time{std::chrono::duration_cast<std::chrono::seconds>(
std::chrono::system_clock::now().time_since_epoch())
.count()};
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.PushRaw<s64>(server_time);
LOG_DEBUG(Service_NIM, "called");
}
};
class NTC final : public ServiceFramework<NTC> {
public:
explicit NTC() : ServiceFramework{"ntc"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "OpenEnsureNetworkClockAvailabilityService"},
{100, nullptr, "SuspendAutonomicTimeCorrection"},
{101, nullptr, "ResumeAutonomicTimeCorrection"},
{0, &NTC::OpenEnsureNetworkClockAvailabilityService, "OpenEnsureNetworkClockAvailabilityService"},
{100, &NTC::SuspendAutonomicTimeCorrection, "SuspendAutonomicTimeCorrection"},
{101, &NTC::ResumeAutonomicTimeCorrection, "ResumeAutonomicTimeCorrection"},
};
// clang-format on
RegisterHandlers(functions);
}
private:
void OpenEnsureNetworkClockAvailabilityService(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IEnsureNetworkClockAvailabilityService>();
LOG_DEBUG(Service_NIM, "called");
}
// TODO(ogniK): Do we need these?
void SuspendAutonomicTimeCorrection(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_NIM, "(STUBBED) called");
}
void ResumeAutonomicTimeCorrection(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_NIM, "(STUBBED) called");
}
};
void InstallInterfaces(SM::ServiceManager& sm) {
+3 -3
View File
@@ -103,6 +103,7 @@ public:
}
private:
u32 ssl_version{};
void CreateContext(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_SSL, "(STUBBED) called");
@@ -112,10 +113,9 @@ private:
}
void SetInterfaceVersion(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_SSL, "(STUBBED) called");
LOG_DEBUG(Service_SSL, "called");
IPC::RequestParser rp{ctx};
u32 unk1 = rp.Pop<u32>(); // Probably minor/major?
u32 unk2 = rp.Pop<u32>(); // TODO(ogniK): Figure out what this does
ssl_version = rp.Pop<u32>();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
+19 -7
View File
@@ -11,6 +11,20 @@
#include "core/loader/nca.h"
namespace Loader {
namespace {
FileType IdentifyTypeImpl(const FileSys::NAX& nax) {
if (nax.GetStatus() != ResultStatus::Success) {
return FileType::Error;
}
const auto nca = nax.AsNCA();
if (nca == nullptr || nca->GetStatus() != ResultStatus::Success) {
return FileType::Error;
}
return FileType::NAX;
}
} // Anonymous namespace
AppLoader_NAX::AppLoader_NAX(FileSys::VirtualFile file)
: AppLoader(file), nax(std::make_unique<FileSys::NAX>(file)),
@@ -19,14 +33,12 @@ AppLoader_NAX::AppLoader_NAX(FileSys::VirtualFile file)
AppLoader_NAX::~AppLoader_NAX() = default;
FileType AppLoader_NAX::IdentifyType(const FileSys::VirtualFile& file) {
FileSys::NAX nax(file);
const FileSys::NAX nax(file);
return IdentifyTypeImpl(nax);
}
if (nax.GetStatus() == ResultStatus::Success && nax.AsNCA() != nullptr &&
nax.AsNCA()->GetStatus() == ResultStatus::Success) {
return FileType::NAX;
}
return FileType::Error;
FileType AppLoader_NAX::GetFileType() {
return IdentifyTypeImpl(*nax);
}
ResultStatus AppLoader_NAX::Load(Kernel::SharedPtr<Kernel::Process>& process) {
+1 -3
View File
@@ -31,9 +31,7 @@ public:
*/
static FileType IdentifyType(const FileSys::VirtualFile& file);
FileType GetFileType() override {
return IdentifyType(file);
}
FileType GetFileType() override;
ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override;
+12 -3
View File
@@ -32,11 +32,18 @@ static_assert(sizeof(NsoSegmentHeader) == 0x10, "NsoSegmentHeader has incorrect
struct NsoHeader {
u32_le magic;
INSERT_PADDING_BYTES(0xc);
u32_le version;
INSERT_PADDING_WORDS(1);
u8 flags;
std::array<NsoSegmentHeader, 3> segments; // Text, RoData, Data (in that order)
u32_le bss_size;
INSERT_PADDING_BYTES(0x1c);
std::array<u32_le, 3> segments_compressed_size;
bool IsSegmentCompressed(size_t segment_num) const {
ASSERT_MSG(segment_num < 3, "Invalid segment {}", segment_num);
return ((flags >> segment_num) & 1);
}
};
static_assert(sizeof(NsoHeader) == 0x6c, "NsoHeader has incorrect size.");
static_assert(std::is_trivially_copyable_v<NsoHeader>, "NsoHeader isn't trivially copyable.");
@@ -105,9 +112,11 @@ VAddr AppLoader_NSO::LoadModule(FileSys::VirtualFile file, VAddr load_base) {
Kernel::SharedPtr<Kernel::CodeSet> codeset = Kernel::CodeSet::Create(kernel, "");
std::vector<u8> program_image;
for (std::size_t i = 0; i < nso_header.segments.size(); ++i) {
const std::vector<u8> compressed_data =
std::vector<u8> data =
file->ReadBytes(nso_header.segments_compressed_size[i], nso_header.segments[i].offset);
std::vector<u8> data = DecompressSegment(compressed_data, nso_header.segments[i]);
if (nso_header.IsSegmentCompressed(i)) {
data = DecompressSegment(data, nso_header.segments[i]);
}
program_image.resize(nso_header.segments[i].location);
program_image.insert(program_image.end(), data.begin(), data.end());
codeset->segments[i].addr = nso_header.segments[i].location;
+19 -8
View File
@@ -5,9 +5,8 @@
#pragma once
#include <bitset>
#include <cstring>
#include <map>
#include <string>
#include <tuple>
#include <vector>
#include <boost/optional.hpp>
@@ -315,17 +314,29 @@ enum class TextureMiscMode : u64 {
PTP,
};
enum class IpaInterpMode : u64 { Linear = 0, Perspective = 1, Flat = 2, Sc = 3 };
enum class IpaSampleMode : u64 { Default = 0, Centroid = 1, Offset = 2 };
enum class IpaInterpMode : u64 {
Linear = 0,
Perspective = 1,
Flat = 2,
Sc = 3,
};
enum class IpaSampleMode : u64 {
Default = 0,
Centroid = 1,
Offset = 2,
};
struct IpaMode {
IpaInterpMode interpolation_mode;
IpaSampleMode sampling_mode;
inline bool operator==(const IpaMode& a) {
return (a.interpolation_mode == interpolation_mode) && (a.sampling_mode == sampling_mode);
bool operator==(const IpaMode& a) const {
return std::tie(interpolation_mode, sampling_mode) ==
std::tie(a.interpolation_mode, a.sampling_mode);
}
inline bool operator!=(const IpaMode& a) {
return !((*this) == a);
bool operator!=(const IpaMode& a) const {
return !operator==(a);
}
};
@@ -383,7 +383,7 @@ void RasterizerOpenGL::Clear() {
bool use_stencil{};
OpenGLState clear_state;
clear_state.draw.draw_framebuffer = state.draw.draw_framebuffer;
clear_state.draw.draw_framebuffer = framebuffer.handle;
clear_state.color_mask.red_enabled = regs.clear_buffers.R ? GL_TRUE : GL_FALSE;
clear_state.color_mask.green_enabled = regs.clear_buffers.G ? GL_TRUE : GL_FALSE;
clear_state.color_mask.blue_enabled = regs.clear_buffers.B ? GL_TRUE : GL_FALSE;
@@ -484,8 +484,13 @@ void RasterizerOpenGL::DrawArrays() {
GLintptr index_buffer_offset = 0;
if (is_indexed) {
MICROPROFILE_SCOPE(OpenGL_Index);
index_buffer_offset =
buffer_cache.UploadMemory(regs.index_array.StartAddress(), index_buffer_size);
// Adjust the index buffer offset so it points to the first desired index.
auto index_start = regs.index_array.StartAddress();
index_start += static_cast<size_t>(regs.index_array.first) *
static_cast<size_t>(regs.index_array.FormatSizeInBytes());
index_buffer_offset = buffer_cache.UploadMemory(index_start, index_buffer_size);
}
SetupShaders();
@@ -499,10 +504,6 @@ void RasterizerOpenGL::DrawArrays() {
if (is_indexed) {
const GLint base_vertex{static_cast<GLint>(regs.vb_element_base)};
// Adjust the index buffer offset so it points to the first desired index.
index_buffer_offset += static_cast<GLintptr>(regs.index_array.first) *
static_cast<GLintptr>(regs.index_array.FormatSizeInBytes());
if (gpu.state.current_instance > 0) {
glDrawElementsInstancedBaseVertexBaseInstance(
primitive_mode, regs.index_array.count,
@@ -141,8 +141,8 @@ static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_form
{GL_COMPRESSED_RGBA_BPTC_UNORM_ARB, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm,
true}, // BC7U
{GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB, GL_RGB, GL_UNSIGNED_INT_8_8_8_8,
ComponentType::UNorm, true}, // BC6H_UF16
{GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB, GL_RGB, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm,
ComponentType::Float, true}, // BC6H_UF16
{GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB, GL_RGB, GL_UNSIGNED_INT_8_8_8_8, ComponentType::Float,
true}, // BC6H_SF16
{GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_4X4
{GL_RG8, GL_RG, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // G8R8U
@@ -501,6 +501,9 @@ CachedSurface::CachedSurface(const SurfaceParams& params)
glTexParameteri(SurfaceTargetToGL(params.target), GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(SurfaceTargetToGL(params.target), GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(SurfaceTargetToGL(params.target), GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
VideoCore::LabelGLObject(GL_TEXTURE, texture.handle, params.addr,
SurfaceParams::SurfaceTargetName(params.target));
}
static void ConvertS8Z24ToZ24S8(std::vector<u8>& data, u32 width, u32 height) {
@@ -137,6 +137,27 @@ struct SurfaceParams {
}
}
static std::string SurfaceTargetName(SurfaceTarget target) {
switch (target) {
case SurfaceTarget::Texture1D:
return "Texture1D";
case SurfaceTarget::Texture2D:
return "Texture2D";
case SurfaceTarget::Texture3D:
return "Texture3D";
case SurfaceTarget::Texture1DArray:
return "Texture1DArray";
case SurfaceTarget::Texture2DArray:
return "Texture2DArray";
case SurfaceTarget::TextureCubemap:
return "TextureCubemap";
default:
LOG_CRITICAL(HW_GPU, "Unimplemented surface_target={}", static_cast<u32>(target));
UNREACHABLE();
return fmt::format("TextureUnknown({})", static_cast<u32>(target));
}
}
/**
* Gets the compression factor for the specified PixelFormat. This applies to just the
* "compressed width" and "compressed height", not the overall compression factor of a
@@ -8,6 +8,7 @@
#include "video_core/engines/maxwell_3d.h"
#include "video_core/renderer_opengl/gl_shader_cache.h"
#include "video_core/renderer_opengl/gl_shader_manager.h"
#include "video_core/utils.h"
namespace OpenGL {
@@ -83,6 +84,7 @@ CachedShader::CachedShader(VAddr addr, Maxwell::ShaderProgram program_type)
shader.Create(program_result.first.c_str(), gl_type);
program.Create(true, shader.handle);
SetShaderUniformBlockBindings(program.handle);
VideoCore::LabelGLObject(GL_PROGRAM, program.handle, addr);
}
GLuint CachedShader::GetProgramResourceIndex(const GLShader::ConstBufferEntry& buffer) {
@@ -4,6 +4,7 @@
#include <iterator>
#include <glad/glad.h>
#include "common/assert.h"
#include "common/logging/log.h"
#include "video_core/renderer_opengl/gl_state.h"
@@ -7,12 +7,8 @@
#include <array>
#include <glad/glad.h>
#include "video_core/engines/maxwell_3d.h"
namespace OpenGL {
using Regs = Tegra::Engines::Maxwell3D::Regs;
namespace TextureUnits {
struct TextureUnit {
@@ -74,7 +74,7 @@ std::tuple<u8*, GLintptr, bool> OGLStreamBuffer::Map(GLsizeiptr size, GLintptr a
}
}
if (invalidate | !persistent) {
if (invalidate || !persistent) {
GLbitfield flags = GL_MAP_WRITE_BIT | (persistent ? GL_MAP_PERSISTENT_BIT : 0) |
(coherent ? GL_MAP_COHERENT_BIT : GL_MAP_FLUSH_EXPLICIT_BIT) |
(invalidate ? GL_MAP_INVALIDATE_BUFFER_BIT : GL_MAP_UNSYNCHRONIZED_BIT);
+22
View File
@@ -161,4 +161,26 @@ static inline void MortonCopyPixels128(u32 width, u32 height, u32 bytes_per_pixe
}
}
static void LabelGLObject(GLenum identifier, GLuint handle, VAddr addr,
std::string extra_info = "") {
if (!GLAD_GL_KHR_debug) {
return; // We don't need to throw an error as this is just for debugging
}
const std::string nice_addr = fmt::format("0x{:016x}", addr);
std::string object_label;
switch (identifier) {
case GL_TEXTURE:
object_label = extra_info + "@" + nice_addr;
break;
case GL_PROGRAM:
object_label = "ShaderProgram@" + nice_addr;
break;
default:
object_label = fmt::format("Object(0x{:x})@{}", identifier, nice_addr);
break;
}
glObjectLabel(identifier, handle, -1, static_cast<const GLchar*>(object_label.c_str()));
}
} // namespace VideoCore

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