Compare commits

..

102 Commits

Author SHA1 Message Date
Lioncash
f3297d8cd1 loader/nso: Place translation unit specific functions into an anonymous namespace
Makes it impossible to indirectly violate the ODR in some other
translation unit due to these existing.
2019-03-22 15:25:53 -04:00
Lioncash
611f4666fd loader/nso: Clean up use of magic constants
Now that the NSO header has the proper size, we can just use sizeof on
it instead of having magic constants.
2019-03-22 14:39:17 -04:00
Lioncash
1cf90f4570 file_sys/patch_manager: Deduplicate NSO header
This source file was utilizing its own version of the NSO header.
Instead of keeping this around, we can have the patch manager also use
the version of the header that we have defined in loader/nso.h
2019-03-22 14:39:10 -04:00
Lioncash
90e27ea003 loader/nso: Fix definition of the NSO header struct
The total struct itself is 0x100 (256) bytes in size, so we should be
providing that amount of data.

Without the data, this can result in omitted data from the final loaded
NSO file.
2019-03-22 14:26:58 -04:00
Lioncash
ee49e1fcb6 file_sys/patch_manager: Remove two magic values
These correspond to the NSOBuildHeader.
2019-03-22 14:17:50 -04:00
bunnei
7b6d516faa Merge pull request #2234 from lioncash/mutex
core/hle/kernel: Make Mutex a per-process class.
2019-03-21 22:18:36 -04:00
bunnei
b78e7b3454 Merge pull request #2274 from lioncash/include
core/memory: Remove unnecessary includes
2019-03-21 22:14:27 -04:00
bunnei
4d95adcac5 Merge pull request #2275 from lioncash/memflags
kernel/vm_manager: Amend flag value for code data
2019-03-21 21:43:15 -04:00
bunnei
e703772c83 Merge pull request #2276 from lioncash/am
service/am: Add function table for IDebugFunctions
2019-03-21 21:42:17 -04:00
bunnei
639f0c524d Merge pull request #1933 from DarkLordZach/cheat-engine
file_sys: Implement parser and interpreter for game memory cheats
2019-03-21 21:41:59 -04:00
Lioncash
76f27d1f44 service/am: Add function table for IDebugFunctions
We already have the service related stuff set up for this, however, it's
missing the function table.
2019-03-21 15:58:03 -04:00
Lioncash
18918f5f2f kernel/vm_manager: Rename CodeStatic/CodeMutable to Code and CodeData respectively
Makes it more evident that one is for actual code and one is for actual
data. Mutable and static are less than ideal terms here, because
read-only data is technically not mutable, but we were mapping it with
that label.
2019-03-21 11:43:35 -04:00
Lioncash
56c80a2a21 kernel/vm_manager: Amend flag values for CodeMutable
This should actually be using the data flags, rather than the code
flags.
2019-03-21 11:23:14 -04:00
Lioncash
c221308a66 core/memory: Remove unnecessary includes
In 93da8e0abf, the page table construct
was moved to the common library (which utilized these inclusions). Since
the move, nothing requires these headers to be included within the
memory header.
2019-03-21 09:48:54 -04:00
bunnei
839c0f829b Merge pull request #2260 from lioncash/sdl
input_common/sdl: Correct return values within GetPollers implementations
2019-03-21 00:20:49 -04:00
Lioncash
109b78a6d6 common/bit_util: Fix bad merge duplicating the copy constructor
Introduced as a result of #2090, we already define the copy constructor
further down below, so this isn't needed.
2019-03-20 23:48:37 -04:00
bunnei
3e930304fe Merge pull request #2090 from FearlessTobi/port-4599
Port citra-emu/citra#4244 and citra-emu/citra#4599: Changes to BitField
2019-03-20 23:44:20 -04:00
bunnei
52f36ea1c7 Merge pull request #2262 from lioncash/enum
file_sys/content_archive: Amend name of Data_Unknown5 enum entry
2019-03-20 23:13:32 -04:00
bunnei
b72664abfd Merge pull request #2273 from lioncash/guard
common/uint128: Add missing header guard
2019-03-20 23:13:06 -04:00
Lioncash
f2c41ba256 common/uint128: Add missing header guard 2019-03-20 22:39:00 -04:00
Lioncash
b0d70096a1 common/uint128: Add missing top-file source text 2019-03-20 22:38:25 -04:00
bunnei
e76f442a0e Merge pull request #2268 from lioncash/codeset
core/kernel: Migrate CodeSet to its own source files
2019-03-20 22:37:58 -04:00
bunnei
43b83d6b6a Merge pull request #2272 from lioncash/boost
common/CMakeLists: Amend boost dependency
2019-03-20 22:35:36 -04:00
Lioncash
1b6adb5308 common/CMakeLists: Amend boost dependency
When #2247 was created, thread_queue_list.h was the only user of
boost-related code, however #2252 moved the page table struct into
common, which makes use of Boost.ICL, so we need to add the dependency
to the common library's link interface again.
2019-03-20 21:42:13 -04:00
bunnei
872a7bee72 Merge pull request #2267 from FernandoS27/fix-2238
Fix crash caused by #2238.
2019-03-20 21:36:55 -04:00
bunnei
e8ff8a66b0 Merge pull request #2247 from lioncash/include
common/thread_queue_list: Remove unnecessary dependency on boost
2019-03-20 21:34:12 -04:00
bunnei
723ad4512f Merge pull request #2224 from lioncash/opus
hwopus: Leverage multistream API for decoding regular Opus packets
2019-03-20 21:33:37 -04:00
bunnei
c1409602da Merge pull request #2239 from FearlessTobi/port-4684
Port citra-emu/citra#4684: "frontend: qt: fix a freeze where if you click on entry in the game list too fast, citra will hang"
2019-03-20 21:33:05 -04:00
Lioncash
8f454a5c68 kernel/process: Make MapSegment lambda reference parameter const
The segment itself isn't actually modified.
2019-03-20 13:07:09 -04:00
Lioncash
1b6bd9d6df kernel: Move CodeSet structure to its own source files
Given this is utilized by the loaders, this allows avoiding inclusion of
the kernel process definitions where avoidable.

This also keeps the loading format for all executable data separate from
the kernel objects.
2019-03-20 13:07:04 -04:00
bunnei
9d11303a36 Merge pull request #2264 from lioncash/linker
core/loader: Remove vestigial Linker class
2019-03-20 12:31:00 -04:00
bunnei
adf07cbe17 Merge pull request #2263 from FearlessTobi/port-4697
Port citra-emu/citra#4697: "Fix getopt on systems where char is unsigned by default"
2019-03-19 23:27:17 -04:00
Fernando Sahmkow
8a320a6ee2 Fix crash caused by 2238. 2019-03-19 22:45:34 -04:00
Lioncash
1342c53e27 loader: Remove Linker class
Given the class is now currently unused, it can be removed.
2019-03-19 21:32:02 -04:00
Lioncash
ab00552118 loader: Remove Linker inheritance from NRO and NSO loaders
Neither the NRO or NSO loaders actually make use of the functions or
members provided by the Linker interface, so we can just remove the
inheritance altogether.
2019-03-19 21:31:59 -04:00
xperia64
ec74a4fd4a Fix getopt on systems where char is unsigned by default 2019-03-19 23:53:40 +01:00
Lioncash
b8c7072206 file_sys/content_archive: Amend name of Data_Unknown5 enum entry
While we're at it, give each entry some documentation.
2019-03-19 15:58:38 -04:00
bunnei
746167f11a Merge pull request #2258 from lioncash/am
service/am: Supply remaining missing IAudioController functions
2019-03-18 22:20:36 -04:00
Lioncash
eb335f51ca input_common/sdl: Correct return values within implementations of GetPollers()
In both cases, we weren't actually returning anything, which is
undefined behavior.
2019-03-18 11:40:38 -04:00
Lioncash
874826b6dd input_common/sdl: Use a type alias to shorten declaration of GetPollers
Just makes the definitions a little bit more tidy.
2019-03-18 11:40:35 -04:00
bunnei
8dc2f01eae Merge pull request #2259 from lioncash/fsp
fsp_srv: Unstub SetCurrentProcess
2019-03-18 11:13:52 -04:00
bunnei
e05136f70b Merge pull request #2254 from lioncash/redundant
input_common/sdl_impl: Minor cleanup in SDLState constructor
2019-03-18 11:13:20 -04:00
Lioncash
9f092554c2 fsp_srv: Unstub SetCurrentProcess
This just acts as a basic setter for a given PID value and performs no
further checking, so we can just store the passed in value.
2019-03-18 10:38:01 -04:00
Lioncash
26b809549b service/am: Add basic implementation of ChangeMainAppletMasterVolume
All this does is supply a new volume level and a fade time in
nanoseconds for the volume transition to occur within.
2019-03-18 09:18:34 -04:00
Lioncash
c07ebeac19 service/am: Unstub SetTransparentVolumeRate()
Like the other volume setter, this mainly just sets a data member within
the service, nothing too special.
2019-03-18 09:18:34 -04:00
Lioncash
ecd3afdc8e service/am: Unstub SetExpectedMasterVolume()
This function passes in the desired main applet and library applet
volume levels. We can then just pass those values back within the
relevant volume getter functions, allowing us to unstub those as well.

The initial values for the library and main applet volumes differ. The
main applet volume is 0.25 by default, while the library applet volume
is initialized to 1.0 by default in the services themselves.
2019-03-18 09:18:34 -04:00
bunnei
30f228a8c9 Merge pull request #2238 from lioncash/thread
kernel/thread: Amend conditional test and assignment within UpdatePriority()
2019-03-17 22:27:33 -04:00
Mat M
c57d8eb66c Merge pull request #2257 from MerryMage/boost-1.66
CMakeLists: Raise minimum Boost requirement to 1.66.0
2019-03-17 20:21:11 -04:00
MerryMage
51f609fee7 CMakeLists: Raise minimum Boost requirement to 1.66.0
Required due to bugfix in boost for changed template resolving rules in GCC 7.3.0 in C++17 mode
2019-03-17 23:04:03 +00:00
bunnei
57ca1e3e69 Merge pull request #2252 from bunnei/move-page-table
core: Move PageTable struct into Common.
2019-03-17 14:42:57 -04:00
Lioncash
114060fd87 input_common/sdl_impl: Make lambda capture more specific in SDLState constructor
We don't need to universally capture by reference. We specifically just
need to capture the this pointer.
2019-03-17 04:02:52 -04:00
Lioncash
d74aa13bd3 input_common/sdl_impl: Remove unnecessary std::chrono::duration construction
Specifying the time unit itself is sufficient here.
2019-03-17 04:02:52 -04:00
Lioncash
834d3fe336 input_common/sdl_impl: Remove unused variable in SDLState constructor 2019-03-17 04:02:48 -04:00
bunnei
41566c615b Merge pull request #2251 from bunnei/skip-zero-flush
gl_rasterizer: Skip zero addr/sized regions on flush/invalidate.
2019-03-17 01:40:32 -04:00
bunnei
9ad3b01d30 Merge pull request #2249 from lioncash/ipc
ipc_helpers: Allow pushing and popping floating-point values
2019-03-16 22:22:03 -04:00
bunnei
fd0533ef4c Merge pull request #2246 from lioncash/opus-fork
externals: Update opus to latest master
2019-03-16 22:12:59 -04:00
bunnei
ed7a1e1443 Merge pull request #2245 from lioncash/unused-def
kernel/thread: Actually remove the definition of ExitCurrentThread()
2019-03-16 22:12:40 -04:00
bunnei
93da8e0abf core: Move PageTable struct into Common. 2019-03-16 22:05:40 -04:00
bunnei
032e4c4ca3 gl_rasterizer: Skip zero addr/sized regions on flush/invalidate. 2019-03-16 22:03:19 -04:00
bunnei
2392e146b0 Merge pull request #2244 from bunnei/gpu-mem-refactor
video_core: Refactor to use MemoryManager interface for all memory access.
2019-03-16 21:59:45 -04:00
bunnei
bf41132aa9 Merge pull request #2243 from bunnei/mem-simplify-cache
memory: Simplify rasterizer cache operations.
2019-03-16 21:59:30 -04:00
bunnei
059465d496 Merge pull request #2129 from FernandoS27/cntpct
Correct CNTPCT from using CPU Cycles to using Clock Cycles
2019-03-16 21:58:59 -04:00
Lioncash
64444ff481 ipc_helpers: Allow pushing and popping floating-point values
Certain values that are passed through the IPC buffer are actually
floating point values, not solely integral values.
2019-03-16 14:05:03 -04:00
Lioncash
f71c598907 common/thread_queue_list: Remove unnecessary dependency on boost
We really don't need to pull in several headers of boost related
machinery just to perform the erase-remove idiom (particularly with
C++20 around the corner, which adds universal container std::erase and
std::erase_if, which we can just use instead).

With this, we don't need to link in anything boost-related into common.
2019-03-16 05:01:39 -04:00
Lioncash
6abc56672c externals: Update opus to latest master
Prevents yuzu from getting 2000+ warnings in MSVC in a future change.
2019-03-16 04:10:41 -04:00
Lioncash
99f982dce2 kernel/thread: Actually remove the definition of ExitCurrentThread()
This was intended to be removed in
51d7f6bffc, but I guess I forgot to
actually save the file like a dingus.
2019-03-16 00:51:44 -04:00
bunnei
29c242721a Merge pull request #2241 from lioncash/compile-flags
CMakeLists: Remove now-unnecessary GCC special-casing
2019-03-16 00:43:29 -04:00
bunnei
bdf2da4ee8 Merge pull request #2242 from lioncash/thread-fn
kernel/thread: Remove WaitCurrentThread_Sleep() and ExitCurrentThread()
2019-03-16 00:43:09 -04:00
bunnei
10118c71e0 memory: Simplify rasterizer cache operations. 2019-03-16 00:41:08 -04:00
bunnei
574e89d924 video_core: Refactor to use MemoryManager interface for all memory access.
# Conflicts:
#	src/video_core/engines/kepler_memory.cpp
#	src/video_core/engines/maxwell_3d.cpp
#	src/video_core/morton.cpp
#	src/video_core/morton.h
#	src/video_core/renderer_opengl/gl_global_cache.cpp
#	src/video_core/renderer_opengl/gl_global_cache.h
#	src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
2019-03-16 00:38:48 -04:00
bunnei
47b622825c Merge pull request #2237 from bunnei/cache-host-addr
gpu: Use host address for caching instead of guest address.
2019-03-16 00:05:24 -04:00
Lioncash
51d7f6bffc kernel/thread: Move thread exiting logic from ExitCurrentThread to svcExitThread
Puts the operation on global state in the same places as the rest of the
svc calls.
2019-03-15 23:58:37 -04:00
Lioncash
c892cf01fa kernel/thread: Migrate WaitCurrentThread_Sleep into the Thread interface
Rather than make a global accessor for this sort of thing. We can make
it a part of the thread interface itself. This allows getting rid of a
hidden global accessor in the kernel code.
2019-03-15 23:58:31 -04:00
Lioncash
db47d7e471 kernel/thread: Expand documentation of nominal_priority and current_priority
Aims to disambiguate why each priority instance exists a little bit.
While we're at it, also add an explanatory comment to UpdatePriority().
2019-03-15 23:02:14 -04:00
Lioncash
e0d1f11968 kernel/thread: Make bracing consistent within UpdatePriority() 2019-03-15 23:02:10 -04:00
Lioncash
39483b92b7 kernel/thread: Amend condition within UpdatePriority()
This condition was checking against the nominal thread priority, whereas
the kernel itself checks against the current priority instead. We were
also assigning the nominal priority, when we should be assigning
current_priority, which takes priority inheritance into account.

This can lead to the incorrect priority being assigned to a thread.

Given we recursively update the relevant threads, we don't need to go
through the whole mutex waiter list. This matches what the kernel does
as well (only accessing the first entry within the waiting list).
2019-03-15 23:01:43 -04:00
Lioncash
0b78cfcc53 kernel/thread: Maintain priority ordering of added mutex waiting threads
The kernel keeps the internal waiting list ordered by priority. This is
trivial to do with std::find_if followed by an insertion.
2019-03-15 23:01:39 -04:00
bunnei
06ac6460d3 Merge pull request #2048 from FearlessTobi/port-3924
Port citra-emu/citra#3924: "citra_qt: Settings (configuration) rework"
2019-03-15 22:23:38 -04:00
Lioncash
e5b004e903 CMakeLists: Remove now-unnecessary GCC special-casing
This issue has since been fixed in newer versions of Boost, so we don't
need to worry about this anymore.
2019-03-15 20:49:58 -04:00
liushuyu
59f16f2e02 frontend: qt: fix a freeze where if you click on entry in the game list too fast, citra will hang 2019-03-15 16:10:21 +01:00
Lioncash
d71cad6ed0 core/hle/kernel/mutex: Remove usages of global system accessors
Removes the use of global system accessors, and instead uses the
explicit interface provided.
2019-03-14 20:55:52 -04:00
Lioncash
555cd26ec2 core/hle/kernel: Make Mutex a per-process class.
Makes it an instantiable class like it is in the actual kernel. This
will also allow removing reliance on global accessors in a following
change, now that we can encapsulate a reference to the system instance
in the class.
2019-03-14 20:55:52 -04:00
Lioncash
7ad3d4e49c hwopus: Leverage multistream API for decoding regular Opus packets
After doing a little more reading up on the Opus codec, it turns out
that the multistream API that is part of libopus can handle regular
packets. Regular packets are just a degenerate case of multistream Opus
packets, and all that's necessary is to pass the number of streams as 1
and  provide a basic channel mapping, then everything works fine for
that case.

This allows us to get rid of the need to use both APIs in the future
when implementing multistream variants in a follow-up PR, greatly
simplifying the code that needs to be written.
2019-03-11 07:06:18 -04:00
zhupengfei
39e895c5ff citra_qt: Settings (configuration) rework 2019-03-07 16:55:50 +01:00
Zach Hilman
52ac6419da vm_manager: Remove cheat-specific ranges from VMManager 2019-03-05 10:09:36 -05:00
Zach Hilman
7053546687 core: Add support for registering and controlling ownership of CheatEngine 2019-03-04 18:41:29 -05:00
Zach Hilman
769b346682 cheat_engine: Add parser and interpreter for game cheats 2019-03-04 18:39:58 -05:00
Zach Hilman
c100a4b8d4 loader/nso: Set main code region in VMManager
For rom directories (and by extension, XCI/NSP/NAX/NCA) this is for the NSO with name 'main', for regular NSOs, this is the NSO.
2019-03-04 18:39:58 -05:00
Zach Hilman
b952a30555 vm_manager: Add support for storing and getting main code region
Used as root for one region of cheats, set by loader
2019-03-04 18:39:58 -05:00
Zach Hilman
4495bf5706 patch_manager: Display cheats in game list add-ons 2019-03-04 18:39:57 -05:00
Zach Hilman
c5091bfe00 patch_manager: Add support for loading cheats lists
Uses load/<title_id>/<mod_name>/cheats as root dir, file name is all upper or lower hex first 8 bytes build ID.
2019-03-04 18:39:57 -05:00
Zach Hilman
9d1ab766a0 controllers/npad: Add accessor for current press state
Allows frontend/features to access pressed buttons conveniently as possible
2019-03-04 18:39:57 -05:00
Fernando Sahmkow
a8d4927e29 Corrections, documenting and fixes. 2019-02-16 16:52:24 -04:00
Fernando Sahmkow
ecccfe0337 Use u128 on Clock Cycles calculation. 2019-02-15 22:57:16 -04:00
Fernando Sahmkow
3ea48e8ebe Implement 128 bits Unsigned Integer Multiplication and Division. 2019-02-15 22:55:31 -04:00
Fernando Sahmkow
5b7ec71fb7 Correct CNTPCT to use Clock Cycles instead of Cpu Cycles. 2019-02-15 22:55:29 -04:00
fearlessTobi
efd83570bd Make bitfield assignment operator public
This change needs to be made to get the code compiling again. It was suggested after a conversation with Lioncash.

The conversation can be seen here: https://user-images.githubusercontent.com/20753089/45064197-b6107800-b0b2-11e8-9db8-f696299fb86a.PNG
2019-02-13 21:15:15 +01:00
Weiyi Wang
89abef3518 remove all occurance of specifying endianness inside BitField
This commit it automatically generated by command in zsh:
sed -i -- 's/BitField<\(.*\)_le>/BitField<\1>/g' **/*(D.)

BitField is now aware to endianness and default to little endian. It expects a value representation type without storage specification for its template parameter.
2019-02-06 18:13:45 +01:00
Weiyi Wang
6b81ceb060 common/bitfield: make it endianness-aware 2019-02-06 17:29:39 +01:00
Weiyi Wang
71530781f3 common/swap: remove default value for swap type internal storage
This is compromise for swap type being used in union. A union has deleted default constructor if it has at least one variant member with non-trivial default constructor, and no variant member of T has a default member initializer. In the use case of Bitfield, all variant members will be the swap type on endianness mismatch, which would all have non-trivial default constructor if default value is specified, and non of them can have member initializer
2019-02-06 17:24:27 +01:00
Weiyi Wang
6734c64976 common/swap: use template and tag for LE/BE specification
The tag can be useful for other type-generic templates like BitFields to forward the endianness specification
2019-02-06 17:24:13 +01:00
Weiyi Wang
94bc48dd78 common/swap: add swap template for enum 2019-02-06 17:21:15 +01:00
94 changed files with 2494 additions and 1277 deletions

View File

@@ -163,12 +163,6 @@ else()
set(CMAKE_EXE_LINKER_FLAGS_RELEASE "/DEBUG /MANIFEST:NO /INCREMENTAL:NO /OPT:REF,ICF" CACHE STRING "" FORCE)
endif()
# Fix GCC C++17 and Boost.ICL incompatibility (needed to build dynarmic)
# See https://bugzilla.redhat.com/show_bug.cgi?id=1485641#c1
if (CMAKE_COMPILER_IS_GNUCC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-new-ttp-matching")
endif()
# Set file offset size to 64 bits.
#
# On modern Unixes, this is typically already the case. The lone exception is
@@ -185,9 +179,9 @@ set_property(DIRECTORY APPEND PROPERTY
# System imported libraries
# ======================
find_package(Boost 1.63.0 QUIET)
find_package(Boost 1.66.0 QUIET)
if (NOT Boost_FOUND)
message(STATUS "Boost 1.63.0 or newer not found, falling back to externals")
message(STATUS "Boost 1.66.0 or newer not found, falling back to externals")
set(BOOST_ROOT "${PROJECT_SOURCE_DIR}/externals/boost")
set(Boost_NO_SYSTEM_PATHS OFF)

2
externals/opus vendored

View File

@@ -92,10 +92,14 @@ add_library(common STATIC
logging/text_formatter.cpp
logging/text_formatter.h
math_util.h
memory_hook.cpp
memory_hook.h
microprofile.cpp
microprofile.h
microprofileui.h
misc.cpp
page_table.cpp
page_table.h
param_package.cpp
param_package.h
quaternion.h
@@ -114,6 +118,8 @@ add_library(common STATIC
threadsafe_queue.h
timer.cpp
timer.h
uint128.cpp
uint128.h
vector_math.h
web_result.h
)

View File

@@ -34,6 +34,7 @@
#include <limits>
#include <type_traits>
#include "common/common_funcs.h"
#include "common/swap.h"
/*
* Abstract bitfield class
@@ -108,7 +109,7 @@
* symptoms.
*/
#pragma pack(1)
template <std::size_t Position, std::size_t Bits, typename T>
template <std::size_t Position, std::size_t Bits, typename T, typename EndianTag = LETag>
struct BitField {
private:
// UnderlyingType is T for non-enum types and the underlying type of T if
@@ -121,6 +122,8 @@ private:
// We store the value as the unsigned type to avoid undefined behaviour on value shifting
using StorageType = std::make_unsigned_t<UnderlyingType>;
using StorageTypeWithEndian = typename AddEndian<StorageType, EndianTag>::type;
public:
/// Constants to allow limited introspection of fields if needed
static constexpr std::size_t position = Position;
@@ -170,7 +173,7 @@ public:
}
constexpr FORCE_INLINE void Assign(const T& value) {
storage = (storage & ~mask) | FormatValue(value);
storage = (static_cast<StorageType>(storage) & ~mask) | FormatValue(value);
}
constexpr T Value() const {
@@ -182,7 +185,7 @@ public:
}
private:
StorageType storage;
StorageTypeWithEndian storage;
static_assert(bits + position <= 8 * sizeof(T), "Bitfield out of range");
@@ -193,3 +196,6 @@ private:
static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable in a BitField");
};
#pragma pack()
template <std::size_t Position, std::size_t Bits, typename T>
using BitFieldBE = BitField<Position, Bits, T, BETag>;

View File

@@ -2,10 +2,10 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/memory_hook.h"
#include "common/memory_hook.h"
namespace Memory {
namespace Common {
MemoryHook::~MemoryHook() = default;
} // namespace Memory
} // namespace Common

View File

@@ -9,7 +9,7 @@
#include "common/common_types.h"
namespace Memory {
namespace Common {
/**
* Memory hooks have two purposes:
@@ -44,4 +44,4 @@ public:
};
using MemoryHookPointer = std::shared_ptr<MemoryHook>;
} // namespace Memory
} // namespace Common

29
src/common/page_table.cpp Normal file
View File

@@ -0,0 +1,29 @@
// Copyright 2019 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/page_table.h"
namespace Common {
PageTable::PageTable(std::size_t page_size_in_bits) : page_size_in_bits{page_size_in_bits} {}
PageTable::~PageTable() = default;
void PageTable::Resize(std::size_t address_space_width_in_bits) {
const std::size_t num_page_table_entries = 1ULL
<< (address_space_width_in_bits - page_size_in_bits);
pointers.resize(num_page_table_entries);
attributes.resize(num_page_table_entries);
// The default is a 39-bit address space, which causes an initial 1GB allocation size. If the
// vector size is subsequently decreased (via resize), the vector might not automatically
// actually reallocate/resize its underlying allocation, which wastes up to ~800 MB for
// 36-bit titles. Call shrink_to_fit to reduce capacity to what's actually in use.
pointers.shrink_to_fit();
attributes.shrink_to_fit();
}
} // namespace Common

80
src/common/page_table.h Normal file
View File

@@ -0,0 +1,80 @@
// Copyright 2019 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <vector>
#include <boost/icl/interval_map.hpp>
#include "common/common_types.h"
#include "common/memory_hook.h"
namespace Common {
enum class PageType : u8 {
/// Page is unmapped and should cause an access error.
Unmapped,
/// Page is mapped to regular memory. This is the only type you can get pointers to.
Memory,
/// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and
/// invalidation
RasterizerCachedMemory,
/// Page is mapped to a I/O region. Writing and reading to this page is handled by functions.
Special,
};
struct SpecialRegion {
enum class Type {
DebugHook,
IODevice,
} type;
MemoryHookPointer handler;
bool operator<(const SpecialRegion& other) const {
return std::tie(type, handler) < std::tie(other.type, other.handler);
}
bool operator==(const SpecialRegion& other) const {
return std::tie(type, handler) == std::tie(other.type, other.handler);
}
};
/**
* A (reasonably) fast way of allowing switchable and remappable process address spaces. It loosely
* mimics the way a real CPU page table works.
*/
struct PageTable {
explicit PageTable(std::size_t page_size_in_bits);
~PageTable();
/**
* Resizes the page table to be able to accomodate enough pages within
* a given address space.
*
* @param address_space_width_in_bits The address size width in bits.
*/
void Resize(std::size_t address_space_width_in_bits);
/**
* Vector of memory pointers backing each page. An entry can only be non-null if the
* corresponding entry in the `attributes` vector is of type `Memory`.
*/
std::vector<u8*> pointers;
/**
* Contains MMIO handlers that back memory regions whose entries in the `attribute` vector is
* of type `Special`.
*/
boost::icl::interval_map<VAddr, std::set<SpecialRegion>> special_regions;
/**
* Vector of fine grained page attributes. If it is set to any value other than `Memory`, then
* the corresponding entry in `pointers` MUST be set to null.
*/
std::vector<PageType> attributes;
const std::size_t page_size_in_bits{};
};
} // namespace Common

View File

@@ -17,6 +17,8 @@
#pragma once
#include <type_traits>
#if defined(_MSC_VER)
#include <cstdlib>
#elif defined(__linux__)
@@ -170,7 +172,7 @@ struct swap_struct_t {
using swapped_t = swap_struct_t;
protected:
T value = T();
T value;
static T swap(T v) {
return F::swap(v);
@@ -605,52 +607,154 @@ struct swap_double_t {
}
};
template <typename T>
struct swap_enum_t {
static_assert(std::is_enum_v<T>);
using base = std::underlying_type_t<T>;
public:
swap_enum_t() = default;
swap_enum_t(const T& v) : value(swap(v)) {}
swap_enum_t& operator=(const T& v) {
value = swap(v);
return *this;
}
operator T() const {
return swap(value);
}
explicit operator base() const {
return static_cast<base>(swap(value));
}
protected:
T value{};
// clang-format off
using swap_t = std::conditional_t<
std::is_same_v<base, u16>, swap_16_t<u16>, std::conditional_t<
std::is_same_v<base, s16>, swap_16_t<s16>, std::conditional_t<
std::is_same_v<base, u32>, swap_32_t<u32>, std::conditional_t<
std::is_same_v<base, s32>, swap_32_t<s32>, std::conditional_t<
std::is_same_v<base, u64>, swap_64_t<u64>, std::conditional_t<
std::is_same_v<base, s64>, swap_64_t<s64>, void>>>>>>;
// clang-format on
static T swap(T x) {
return static_cast<T>(swap_t::swap(static_cast<base>(x)));
}
};
struct SwapTag {}; // Use the different endianness from the system
struct KeepTag {}; // Use the same endianness as the system
template <typename T, typename Tag>
struct AddEndian;
// KeepTag specializations
template <typename T>
struct AddEndian<T, KeepTag> {
using type = T;
};
// SwapTag specializations
template <>
struct AddEndian<u8, SwapTag> {
using type = u8;
};
template <>
struct AddEndian<u16, SwapTag> {
using type = swap_struct_t<u16, swap_16_t<u16>>;
};
template <>
struct AddEndian<u32, SwapTag> {
using type = swap_struct_t<u32, swap_32_t<u32>>;
};
template <>
struct AddEndian<u64, SwapTag> {
using type = swap_struct_t<u64, swap_64_t<u64>>;
};
template <>
struct AddEndian<s8, SwapTag> {
using type = s8;
};
template <>
struct AddEndian<s16, SwapTag> {
using type = swap_struct_t<s16, swap_16_t<s16>>;
};
template <>
struct AddEndian<s32, SwapTag> {
using type = swap_struct_t<s32, swap_32_t<s32>>;
};
template <>
struct AddEndian<s64, SwapTag> {
using type = swap_struct_t<s64, swap_64_t<s64>>;
};
template <>
struct AddEndian<float, SwapTag> {
using type = swap_struct_t<float, swap_float_t<float>>;
};
template <>
struct AddEndian<double, SwapTag> {
using type = swap_struct_t<double, swap_double_t<double>>;
};
template <typename T>
struct AddEndian<T, SwapTag> {
static_assert(std::is_enum_v<T>);
using type = swap_enum_t<T>;
};
// Alias LETag/BETag as KeepTag/SwapTag depending on the system
#if COMMON_LITTLE_ENDIAN
using u16_le = u16;
using u32_le = u32;
using u64_le = u64;
using s16_le = s16;
using s32_le = s32;
using s64_le = s64;
using LETag = KeepTag;
using BETag = SwapTag;
using float_le = float;
using double_le = double;
using u64_be = swap_struct_t<u64, swap_64_t<u64>>;
using s64_be = swap_struct_t<s64, swap_64_t<s64>>;
using u32_be = swap_struct_t<u32, swap_32_t<u32>>;
using s32_be = swap_struct_t<s32, swap_32_t<s32>>;
using u16_be = swap_struct_t<u16, swap_16_t<u16>>;
using s16_be = swap_struct_t<s16, swap_16_t<s16>>;
using float_be = swap_struct_t<float, swap_float_t<float>>;
using double_be = swap_struct_t<double, swap_double_t<double>>;
#else
using u64_le = swap_struct_t<u64, swap_64_t<u64>>;
using s64_le = swap_struct_t<s64, swap_64_t<s64>>;
using u32_le = swap_struct_t<u32, swap_32_t<u32>>;
using s32_le = swap_struct_t<s32, swap_32_t<s32>>;
using u16_le = swap_struct_t<u16, swap_16_t<u16>>;
using s16_le = swap_struct_t<s16, swap_16_t<s16>>;
using float_le = swap_struct_t<float, swap_float_t<float>>;
using double_le = swap_struct_t<double, swap_double_t<double>>;
using u16_be = u16;
using u32_be = u32;
using u64_be = u64;
using s16_be = s16;
using s32_be = s32;
using s64_be = s64;
using float_be = float;
using double_be = double;
using BETag = KeepTag;
using LETag = SwapTag;
#endif
// Aliases for LE types
using u16_le = AddEndian<u16, LETag>::type;
using u32_le = AddEndian<u32, LETag>::type;
using u64_le = AddEndian<u64, LETag>::type;
using s16_le = AddEndian<s16, LETag>::type;
using s32_le = AddEndian<s32, LETag>::type;
using s64_le = AddEndian<s64, LETag>::type;
template <typename T>
using enum_le = std::enable_if_t<std::is_enum_v<T>, typename AddEndian<T, LETag>::type>;
using float_le = AddEndian<float, LETag>::type;
using double_le = AddEndian<double, LETag>::type;
// Aliases for BE types
using u16_be = AddEndian<u16, BETag>::type;
using u32_be = AddEndian<u32, BETag>::type;
using u64_be = AddEndian<u64, BETag>::type;
using s16_be = AddEndian<s16, BETag>::type;
using s32_be = AddEndian<s32, BETag>::type;
using s64_be = AddEndian<s64, BETag>::type;
template <typename T>
using enum_be = std::enable_if_t<std::is_enum_v<T>, typename AddEndian<T, BETag>::type>;
using float_be = AddEndian<float, BETag>::type;
using double_be = AddEndian<double, BETag>::type;

View File

@@ -6,7 +6,6 @@
#include <array>
#include <deque>
#include <boost/range/algorithm_ext/erase.hpp>
namespace Common {
@@ -111,8 +110,9 @@ struct ThreadQueueList {
}
void remove(Priority priority, const T& thread_id) {
Queue* cur = &queues[priority];
boost::remove_erase(cur->data, thread_id);
Queue* const cur = &queues[priority];
const auto iter = std::remove(cur->data.begin(), cur->data.end(), thread_id);
cur->data.erase(iter, cur->data.end());
}
void rotate(Priority priority) {

45
src/common/uint128.cpp Normal file
View File

@@ -0,0 +1,45 @@
// Copyright 2019 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#ifdef _MSC_VER
#include <intrin.h>
#pragma intrinsic(_umul128)
#endif
#include <cstring>
#include "common/uint128.h"
namespace Common {
u128 Multiply64Into128(u64 a, u64 b) {
u128 result;
#ifdef _MSC_VER
result[0] = _umul128(a, b, &result[1]);
#else
unsigned __int128 tmp = a;
tmp *= b;
std::memcpy(&result, &tmp, sizeof(u128));
#endif
return result;
}
std::pair<u64, u64> Divide128On32(u128 dividend, u32 divisor) {
u64 remainder = dividend[0] % divisor;
u64 accum = dividend[0] / divisor;
if (dividend[1] == 0)
return {accum, remainder};
// We ignore dividend[1] / divisor as that overflows
const u64 first_segment = (dividend[1] % divisor) << 32;
accum += (first_segment / divisor) << 32;
const u64 second_segment = (first_segment % divisor) << 32;
accum += (second_segment / divisor);
remainder += second_segment % divisor;
if (remainder >= divisor) {
accum++;
remainder -= divisor;
}
return {accum, remainder};
}
} // namespace Common

19
src/common/uint128.h Normal file
View File

@@ -0,0 +1,19 @@
// Copyright 2019 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <utility>
#include "common/common_types.h"
namespace Common {
// This function multiplies 2 u64 values and produces a u128 value;
u128 Multiply64Into128(u64 a, u64 b);
// This function divides a u128 by a u32 value and produces two u64 values:
// the result of division and the remainder
std::pair<u64, u64> Divide128On32(u128 dividend, u32 divisor);
} // namespace Common

View File

@@ -31,6 +31,8 @@ add_library(core STATIC
file_sys/bis_factory.h
file_sys/card_image.cpp
file_sys/card_image.h
file_sys/cheat_engine.cpp
file_sys/cheat_engine.h
file_sys/content_archive.cpp
file_sys/content_archive.h
file_sys/control_metadata.cpp
@@ -107,6 +109,8 @@ add_library(core STATIC
hle/kernel/client_port.h
hle/kernel/client_session.cpp
hle/kernel/client_session.h
hle/kernel/code_set.cpp
hle/kernel/code_set.h
hle/kernel/errors.h
hle/kernel/handle_table.cpp
hle/kernel/handle_table.h
@@ -419,8 +423,6 @@ add_library(core STATIC
loader/deconstructed_rom_directory.h
loader/elf.cpp
loader/elf.h
loader/linker.cpp
loader/linker.h
loader/loader.cpp
loader/loader.h
loader/nax.cpp
@@ -437,8 +439,6 @@ add_library(core STATIC
loader/xci.h
memory.cpp
memory.h
memory_hook.cpp
memory_hook.h
memory_setup.h
perf_stats.cpp
perf_stats.h

View File

@@ -12,6 +12,7 @@
#include "core/core.h"
#include "core/core_cpu.h"
#include "core/core_timing.h"
#include "core/core_timing_util.h"
#include "core/gdbstub/gdbstub.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/svc.h"
@@ -119,7 +120,7 @@ public:
return std::max(parent.core_timing.GetDowncount(), 0);
}
u64 GetCNTPCT() override {
return parent.core_timing.GetTicks();
return Timing::CpuCyclesToClockCycles(parent.core_timing.GetTicks());
}
ARM_Dynarmic& parent;
@@ -151,7 +152,7 @@ std::unique_ptr<Dynarmic::A64::Jit> ARM_Dynarmic::MakeJit() const {
config.tpidr_el0 = &cb->tpidr_el0;
config.dczid_el0 = 4;
config.ctr_el0 = 0x8444c004;
config.cntfrq_el0 = 19200000; // Value from fusee.
config.cntfrq_el0 = Timing::CNTFREQ;
// Unpredictable instructions
config.define_unpredictable_behaviour = true;

View File

@@ -12,7 +12,7 @@
#include "core/arm/exclusive_monitor.h"
#include "core/arm/unicorn/arm_unicorn.h"
namespace Memory {
namespace Common {
struct PageTable;
}
@@ -70,7 +70,7 @@ private:
Timing::CoreTiming& core_timing;
DynarmicExclusiveMonitor& exclusive_monitor;
Memory::PageTable* current_page_table = nullptr;
Common::PageTable* current_page_table = nullptr;
};
class DynarmicExclusiveMonitor final : public ExclusiveMonitor {

View File

@@ -32,6 +32,7 @@
#include "core/perf_stats.h"
#include "core/settings.h"
#include "core/telemetry_session.h"
#include "file_sys/cheat_engine.h"
#include "frontend/applets/profile_select.h"
#include "frontend/applets/software_keyboard.h"
#include "frontend/applets/web_browser.h"
@@ -205,6 +206,7 @@ struct System::Impl {
GDBStub::Shutdown();
Service::Shutdown();
service_manager.reset();
cheat_engine.reset();
telemetry_session.reset();
gpu_core.reset();
@@ -255,6 +257,8 @@ struct System::Impl {
CpuCoreManager cpu_core_manager;
bool is_powered_on = false;
std::unique_ptr<FileSys::CheatEngine> cheat_engine;
/// Frontend applets
std::unique_ptr<Core::Frontend::ProfileSelectApplet> profile_selector;
std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> software_keyboard;
@@ -453,6 +457,13 @@ Tegra::DebugContext* System::GetGPUDebugContext() const {
return impl->debug_context.get();
}
void System::RegisterCheatList(const std::vector<FileSys::CheatList>& list,
const std::string& build_id, VAddr code_region_start,
VAddr code_region_end) {
impl->cheat_engine =
std::make_unique<FileSys::CheatEngine>(list, build_id, code_region_start, code_region_end);
}
void System::SetFilesystem(std::shared_ptr<FileSys::VfsFilesystem> vfs) {
impl->virtual_filesystem = std::move(vfs);
}

View File

@@ -20,6 +20,7 @@ class WebBrowserApplet;
} // namespace Core::Frontend
namespace FileSys {
class CheatList;
class VfsFilesystem;
} // namespace FileSys
@@ -253,6 +254,9 @@ public:
std::shared_ptr<FileSys::VfsFilesystem> GetFilesystem() const;
void RegisterCheatList(const std::vector<FileSys::CheatList>& list, const std::string& build_id,
VAddr code_region_start, VAddr code_region_end);
void SetProfileSelector(std::unique_ptr<Frontend::ProfileSelectApplet> applet);
const Frontend::ProfileSelectApplet& GetProfileSelector() const;

View File

@@ -7,6 +7,7 @@
#include <cinttypes>
#include <limits>
#include "common/logging/log.h"
#include "common/uint128.h"
namespace Core::Timing {
@@ -60,4 +61,9 @@ s64 nsToCycles(u64 ns) {
return (BASE_CLOCK_RATE * static_cast<s64>(ns)) / 1000000000;
}
u64 CpuCyclesToClockCycles(u64 ticks) {
const u128 temporal = Common::Multiply64Into128(ticks, CNTFREQ);
return Common::Divide128On32(temporal, static_cast<u32>(BASE_CLOCK_RATE)).first;
}
} // namespace Core::Timing

View File

@@ -11,6 +11,7 @@ namespace Core::Timing {
// The below clock rate is based on Switch's clockspeed being widely known as 1.020GHz
// The exact value used is of course unverified.
constexpr u64 BASE_CLOCK_RATE = 1019215872; // Switch clock speed is 1020MHz un/docked
constexpr u64 CNTFREQ = 19200000; // Value from fusee.
inline s64 msToCycles(int ms) {
// since ms is int there is no way to overflow
@@ -61,4 +62,6 @@ inline u64 cyclesToMs(s64 cycles) {
return cycles * 1000 / BASE_CLOCK_RATE;
}
u64 CpuCyclesToClockCycles(u64 ticks);
} // namespace Core::Timing

View File

@@ -0,0 +1,493 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <locale>
#include "common/hex_util.h"
#include "common/microprofile.h"
#include "common/swap.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/core_timing_util.h"
#include "core/file_sys/cheat_engine.h"
#include "core/hle/kernel/process.h"
#include "core/hle/service/hid/controllers/controller_base.h"
#include "core/hle/service/hid/controllers/npad.h"
#include "core/hle/service/hid/hid.h"
#include "core/hle/service/sm/sm.h"
namespace FileSys {
constexpr u64 CHEAT_ENGINE_TICKS = Core::Timing::BASE_CLOCK_RATE / 60;
constexpr u32 KEYPAD_BITMASK = 0x3FFFFFF;
u64 Cheat::Address() const {
u64 out;
std::memcpy(&out, raw.data(), sizeof(u64));
return Common::swap64(out) & 0xFFFFFFFFFF;
}
u64 Cheat::ValueWidth(u64 offset) const {
return Value(offset, width);
}
u64 Cheat::Value(u64 offset, u64 width) const {
u64 out;
std::memcpy(&out, raw.data() + offset, sizeof(u64));
out = Common::swap64(out);
if (width == 8)
return out;
return out & ((1ull << (width * CHAR_BIT)) - 1);
}
u32 Cheat::KeypadValue() const {
u32 out;
std::memcpy(&out, raw.data(), sizeof(u32));
return Common::swap32(out) & 0x0FFFFFFF;
}
void CheatList::SetMemoryParameters(VAddr main_begin, VAddr heap_begin, VAddr main_end,
VAddr heap_end, MemoryWriter writer, MemoryReader reader) {
this->main_region_begin = main_begin;
this->main_region_end = main_end;
this->heap_region_begin = heap_begin;
this->heap_region_end = heap_end;
this->writer = writer;
this->reader = reader;
}
MICROPROFILE_DEFINE(Cheat_Engine, "Add-Ons", "Cheat Engine", MP_RGB(70, 200, 70));
void CheatList::Execute() {
MICROPROFILE_SCOPE(Cheat_Engine);
std::fill(scratch.begin(), scratch.end(), 0);
in_standard = false;
for (std::size_t i = 0; i < master_list.size(); ++i) {
LOG_DEBUG(Common_Filesystem, "Executing block #{:08X} ({})", i, master_list[i].first);
current_block = i;
ExecuteBlock(master_list[i].second);
}
in_standard = true;
for (std::size_t i = 0; i < standard_list.size(); ++i) {
LOG_DEBUG(Common_Filesystem, "Executing block #{:08X} ({})", i, standard_list[i].first);
current_block = i;
ExecuteBlock(standard_list[i].second);
}
}
CheatList::CheatList(ProgramSegment master, ProgramSegment standard)
: master_list(master), standard_list(standard) {}
bool CheatList::EvaluateConditional(const Cheat& cheat) const {
using ComparisonFunction = bool (*)(u64, u64);
constexpr std::array<ComparisonFunction, 6> comparison_functions{
[](u64 a, u64 b) { return a > b; }, [](u64 a, u64 b) { return a >= b; },
[](u64 a, u64 b) { return a < b; }, [](u64 a, u64 b) { return a <= b; },
[](u64 a, u64 b) { return a == b; }, [](u64 a, u64 b) { return a != b; },
};
if (cheat.type == CodeType::ConditionalInput) {
const auto applet_resource = Core::System::GetInstance()
.ServiceManager()
.GetService<Service::HID::Hid>("hid")
->GetAppletResource();
if (applet_resource == nullptr) {
LOG_WARNING(
Common_Filesystem,
"Attempted to evaluate input conditional, but applet resource is not initialized!");
return false;
}
const auto press_state =
applet_resource
->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad)
.GetAndResetPressState();
return ((press_state & cheat.KeypadValue()) & KEYPAD_BITMASK) != 0;
}
ASSERT(cheat.type == CodeType::Conditional);
const auto offset =
cheat.memory_type == MemoryType::MainNSO ? main_region_begin : heap_region_begin;
ASSERT(static_cast<u8>(cheat.comparison_op.Value()) < 6);
auto* function = comparison_functions[static_cast<u8>(cheat.comparison_op.Value())];
const auto addr = cheat.Address() + offset;
return function(reader(cheat.width, SanitizeAddress(addr)), cheat.ValueWidth(8));
}
void CheatList::ProcessBlockPairs(const Block& block) {
block_pairs.clear();
u64 scope = 0;
std::map<u64, u64> pairs;
for (std::size_t i = 0; i < block.size(); ++i) {
const auto& cheat = block[i];
switch (cheat.type) {
case CodeType::Conditional:
case CodeType::ConditionalInput:
pairs.insert_or_assign(scope, i);
++scope;
break;
case CodeType::EndConditional: {
--scope;
const auto idx = pairs.at(scope);
block_pairs.insert_or_assign(idx, i);
break;
}
case CodeType::Loop: {
if (cheat.end_of_loop) {
--scope;
const auto idx = pairs.at(scope);
block_pairs.insert_or_assign(idx, i);
} else {
pairs.insert_or_assign(scope, i);
++scope;
}
break;
}
}
}
}
void CheatList::WriteImmediate(const Cheat& cheat) {
const auto offset =
cheat.memory_type == MemoryType::MainNSO ? main_region_begin : heap_region_begin;
const auto& register_3 = scratch.at(cheat.register_3);
const auto addr = cheat.Address() + offset + register_3;
LOG_DEBUG(Common_Filesystem, "writing value={:016X} to addr={:016X}", addr,
cheat.Value(8, cheat.width));
writer(cheat.width, SanitizeAddress(addr), cheat.ValueWidth(8));
}
void CheatList::BeginConditional(const Cheat& cheat) {
if (EvaluateConditional(cheat)) {
return;
}
const auto iter = block_pairs.find(current_index);
ASSERT(iter != block_pairs.end());
current_index = iter->second - 1;
}
void CheatList::EndConditional(const Cheat& cheat) {
LOG_DEBUG(Common_Filesystem, "Ending conditional block.");
}
void CheatList::Loop(const Cheat& cheat) {
if (cheat.end_of_loop.Value())
ASSERT(!cheat.end_of_loop.Value());
auto& register_3 = scratch.at(cheat.register_3);
const auto iter = block_pairs.find(current_index);
ASSERT(iter != block_pairs.end());
ASSERT(iter->first < iter->second);
for (int i = cheat.Value(4, 4); i >= 0; --i) {
register_3 = i;
for (std::size_t c = iter->first + 1; c < iter->second; ++c) {
current_index = c;
ExecuteSingleCheat(
(in_standard ? standard_list : master_list)[current_block].second[c]);
}
}
current_index = iter->second;
}
void CheatList::LoadImmediate(const Cheat& cheat) {
auto& register_3 = scratch.at(cheat.register_3);
LOG_DEBUG(Common_Filesystem, "setting register={:01X} equal to value={:016X}", cheat.register_3,
cheat.Value(4, 8));
register_3 = cheat.Value(4, 8);
}
void CheatList::LoadIndexed(const Cheat& cheat) {
const auto offset =
cheat.memory_type == MemoryType::MainNSO ? main_region_begin : heap_region_begin;
auto& register_3 = scratch.at(cheat.register_3);
const auto addr = (cheat.load_from_register.Value() ? register_3 : offset) + cheat.Address();
LOG_DEBUG(Common_Filesystem, "writing indexed value to register={:01X}, addr={:016X}",
cheat.register_3, addr);
register_3 = reader(cheat.width, SanitizeAddress(addr));
}
void CheatList::StoreIndexed(const Cheat& cheat) {
const auto& register_3 = scratch.at(cheat.register_3);
const auto addr =
register_3 + (cheat.add_additional_register.Value() ? scratch.at(cheat.register_6) : 0);
LOG_DEBUG(Common_Filesystem, "writing value={:016X} to addr={:016X}",
cheat.Value(4, cheat.width), addr);
writer(cheat.width, SanitizeAddress(addr), cheat.ValueWidth(4));
}
void CheatList::RegisterArithmetic(const Cheat& cheat) {
using ArithmeticFunction = u64 (*)(u64, u64);
constexpr std::array<ArithmeticFunction, 5> arithmetic_functions{
[](u64 a, u64 b) { return a + b; }, [](u64 a, u64 b) { return a - b; },
[](u64 a, u64 b) { return a * b; }, [](u64 a, u64 b) { return a << b; },
[](u64 a, u64 b) { return a >> b; },
};
using ArithmeticOverflowCheck = bool (*)(u64, u64);
constexpr std::array<ArithmeticOverflowCheck, 5> arithmetic_overflow_checks{
[](u64 a, u64 b) { return a > (std::numeric_limits<u64>::max() - b); }, // a + b
[](u64 a, u64 b) { return a > (std::numeric_limits<u64>::max() + b); }, // a - b
[](u64 a, u64 b) { return a > (std::numeric_limits<u64>::max() / b); }, // a * b
[](u64 a, u64 b) { return b >= 64 || (a & ~((1ull << (64 - b)) - 1)) != 0; }, // a << b
[](u64 a, u64 b) { return b >= 64 || (a & ((1ull << b) - 1)) != 0; }, // a >> b
};
static_assert(sizeof(arithmetic_functions) == sizeof(arithmetic_overflow_checks),
"Missing or have extra arithmetic overflow checks compared to functions!");
auto& register_3 = scratch.at(cheat.register_3);
ASSERT(static_cast<u8>(cheat.arithmetic_op.Value()) < 5);
auto* function = arithmetic_functions[static_cast<u8>(cheat.arithmetic_op.Value())];
auto* overflow_function =
arithmetic_overflow_checks[static_cast<u8>(cheat.arithmetic_op.Value())];
LOG_DEBUG(Common_Filesystem, "performing arithmetic with register={:01X}, value={:016X}",
cheat.register_3, cheat.ValueWidth(4));
if (overflow_function(register_3, cheat.ValueWidth(4))) {
LOG_WARNING(Common_Filesystem,
"overflow will occur when performing arithmetic operation={:02X} with operands "
"a={:016X}, b={:016X}!",
static_cast<u8>(cheat.arithmetic_op.Value()), register_3, cheat.ValueWidth(4));
}
register_3 = function(register_3, cheat.ValueWidth(4));
}
void CheatList::BeginConditionalInput(const Cheat& cheat) {
if (EvaluateConditional(cheat))
return;
const auto iter = block_pairs.find(current_index);
ASSERT(iter != block_pairs.end());
current_index = iter->second - 1;
}
VAddr CheatList::SanitizeAddress(VAddr in) const {
if ((in < main_region_begin || in >= main_region_end) &&
(in < heap_region_begin || in >= heap_region_end)) {
LOG_ERROR(Common_Filesystem,
"Cheat attempting to access memory at invalid address={:016X}, if this persists, "
"the cheat may be incorrect. However, this may be normal early in execution if "
"the game has not properly set up yet.",
in);
return 0; ///< Invalid addresses will hard crash
}
return in;
}
void CheatList::ExecuteSingleCheat(const Cheat& cheat) {
using CheatOperationFunction = void (CheatList::*)(const Cheat&);
constexpr std::array<CheatOperationFunction, 9> cheat_operation_functions{
&CheatList::WriteImmediate, &CheatList::BeginConditional,
&CheatList::EndConditional, &CheatList::Loop,
&CheatList::LoadImmediate, &CheatList::LoadIndexed,
&CheatList::StoreIndexed, &CheatList::RegisterArithmetic,
&CheatList::BeginConditionalInput,
};
const auto index = static_cast<u8>(cheat.type.Value());
ASSERT(index < sizeof(cheat_operation_functions));
const auto op = cheat_operation_functions[index];
(this->*op)(cheat);
}
void CheatList::ExecuteBlock(const Block& block) {
encountered_loops.clear();
ProcessBlockPairs(block);
for (std::size_t i = 0; i < block.size(); ++i) {
current_index = i;
ExecuteSingleCheat(block[i]);
i = current_index;
}
}
CheatParser::~CheatParser() = default;
CheatList CheatParser::MakeCheatList(CheatList::ProgramSegment master,
CheatList::ProgramSegment standard) const {
return {master, standard};
}
TextCheatParser::~TextCheatParser() = default;
CheatList TextCheatParser::Parse(const std::vector<u8>& data) const {
std::stringstream ss;
ss.write(reinterpret_cast<const char*>(data.data()), data.size());
std::vector<std::string> lines;
std::string stream_line;
while (std::getline(ss, stream_line)) {
// Remove a trailing \r
if (!stream_line.empty() && stream_line.back() == '\r')
stream_line.pop_back();
lines.push_back(std::move(stream_line));
}
CheatList::ProgramSegment master_list;
CheatList::ProgramSegment standard_list;
for (std::size_t i = 0; i < lines.size(); ++i) {
auto line = lines[i];
if (!line.empty() && (line[0] == '[' || line[0] == '{')) {
const auto master = line[0] == '{';
const auto begin = master ? line.find('{') : line.find('[');
const auto end = master ? line.rfind('}') : line.rfind(']');
ASSERT(begin != std::string::npos && end != std::string::npos);
const std::string patch_name{line.begin() + begin + 1, line.begin() + end};
CheatList::Block block{};
while (i < lines.size() - 1) {
line = lines[++i];
if (!line.empty() && (line[0] == '[' || line[0] == '{')) {
--i;
break;
}
if (line.size() < 8)
continue;
Cheat out{};
out.raw = ParseSingleLineCheat(line);
block.push_back(out);
}
(master ? master_list : standard_list).emplace_back(patch_name, block);
}
}
return MakeCheatList(master_list, standard_list);
}
std::array<u8, 16> TextCheatParser::ParseSingleLineCheat(const std::string& line) const {
std::array<u8, 16> out{};
if (line.size() < 8)
return out;
const auto word1 = Common::HexStringToArray<sizeof(u32)>(std::string_view{line.data(), 8});
std::memcpy(out.data(), word1.data(), sizeof(u32));
if (line.size() < 17 || line[8] != ' ')
return out;
const auto word2 = Common::HexStringToArray<sizeof(u32)>(std::string_view{line.data() + 9, 8});
std::memcpy(out.data() + sizeof(u32), word2.data(), sizeof(u32));
if (line.size() < 26 || line[17] != ' ') {
// Perform shifting in case value is truncated early.
const auto type = static_cast<CodeType>((out[0] & 0xF0) >> 4);
if (type == CodeType::Loop || type == CodeType::LoadImmediate ||
type == CodeType::StoreIndexed || type == CodeType::RegisterArithmetic) {
std::memcpy(out.data() + 8, out.data() + 4, sizeof(u32));
std::memset(out.data() + 4, 0, sizeof(u32));
}
return out;
}
const auto word3 = Common::HexStringToArray<sizeof(u32)>(std::string_view{line.data() + 18, 8});
std::memcpy(out.data() + 2 * sizeof(u32), word3.data(), sizeof(u32));
if (line.size() < 35 || line[26] != ' ') {
// Perform shifting in case value is truncated early.
const auto type = static_cast<CodeType>((out[0] & 0xF0) >> 4);
if (type == CodeType::WriteImmediate || type == CodeType::Conditional) {
std::memcpy(out.data() + 12, out.data() + 8, sizeof(u32));
std::memset(out.data() + 8, 0, sizeof(u32));
}
return out;
}
const auto word4 = Common::HexStringToArray<sizeof(u32)>(std::string_view{line.data() + 27, 8});
std::memcpy(out.data() + 3 * sizeof(u32), word4.data(), sizeof(u32));
return out;
}
u64 MemoryReadImpl(u32 width, VAddr addr) {
switch (width) {
case 1:
return Memory::Read8(addr);
case 2:
return Memory::Read16(addr);
case 4:
return Memory::Read32(addr);
case 8:
return Memory::Read64(addr);
default:
UNREACHABLE();
return 0;
}
}
void MemoryWriteImpl(u32 width, VAddr addr, u64 value) {
switch (width) {
case 1:
Memory::Write8(addr, static_cast<u8>(value));
break;
case 2:
Memory::Write16(addr, static_cast<u16>(value));
break;
case 4:
Memory::Write32(addr, static_cast<u32>(value));
break;
case 8:
Memory::Write64(addr, value);
break;
default:
UNREACHABLE();
}
}
CheatEngine::CheatEngine(std::vector<CheatList> cheats, const std::string& build_id,
VAddr code_region_start, VAddr code_region_end)
: cheats(std::move(cheats)) {
auto& core_timing{Core::System::GetInstance().CoreTiming()};
event = core_timing.RegisterEvent(
"CheatEngine::FrameCallback::" + build_id,
[this](u64 userdata, s64 cycles_late) { FrameCallback(userdata, cycles_late); });
core_timing.ScheduleEvent(CHEAT_ENGINE_TICKS, event);
const auto& vm_manager = Core::System::GetInstance().CurrentProcess()->VMManager();
for (auto& list : this->cheats) {
list.SetMemoryParameters(code_region_start, vm_manager.GetHeapRegionBaseAddress(),
code_region_end, vm_manager.GetHeapRegionEndAddress(),
&MemoryWriteImpl, &MemoryReadImpl);
}
}
CheatEngine::~CheatEngine() {
auto& core_timing{Core::System::GetInstance().CoreTiming()};
core_timing.UnscheduleEvent(event, 0);
}
void CheatEngine::FrameCallback(u64 userdata, int cycles_late) {
for (auto& list : cheats)
list.Execute();
auto& core_timing{Core::System::GetInstance().CoreTiming()};
core_timing.ScheduleEvent(CHEAT_ENGINE_TICKS - cycles_late, event);
}
} // namespace FileSys

View File

@@ -0,0 +1,227 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <map>
#include <set>
#include <vector>
#include <queue>
#include "common/bit_field.h"
#include "common/common_types.h"
namespace Core::Timing {
struct EventType;
}
namespace FileSys {
enum class CodeType : u32 {
// 0TMR00AA AAAAAAAA YYYYYYYY YYYYYYYY
// Writes a T sized value Y to the address A added to the value of register R in memory domain M
WriteImmediate = 0,
// 1TMC00AA AAAAAAAA YYYYYYYY YYYYYYYY
// Compares the T sized value Y to the value at address A in memory domain M using the
// conditional function C. If success, continues execution. If failure, jumps to the matching
// EndConditional statement.
Conditional = 1,
// 20000000
// Terminates a Conditional or ConditionalInput block.
EndConditional = 2,
// 300R0000 VVVVVVVV
// Starts looping V times, storing the current count in register R.
// Loop block is terminated with a matching 310R0000.
Loop = 3,
// 400R0000 VVVVVVVV VVVVVVVV
// Sets the value of register R to the value V.
LoadImmediate = 4,
// 5TMRI0AA AAAAAAAA
// Sets the value of register R to the value of width T at address A in memory domain M, with
// the current value of R added to the address if I == 1.
LoadIndexed = 5,
// 6T0RIFG0 VVVVVVVV VVVVVVVV
// Writes the value V of width T to the memory address stored in register R. Adds the value of
// register G to the final calculation if F is nonzero. Increments the value of register R by T
// after operation if I is nonzero.
StoreIndexed = 6,
// 7T0RA000 VVVVVVVV
// Performs the arithmetic operation A on the value in register R and the value V of width T,
// storing the result in register R.
RegisterArithmetic = 7,
// 8KKKKKKK
// Checks to see if any of the buttons defined by the bitmask K are pressed. If any are,
// execution continues. If none are, execution skips to the next EndConditional command.
ConditionalInput = 8,
};
enum class MemoryType : u32 {
// Addressed relative to start of main NSO
MainNSO = 0,
// Addressed relative to start of heap
Heap = 1,
};
enum class ArithmeticOp : u32 {
Add = 0,
Sub = 1,
Mult = 2,
LShift = 3,
RShift = 4,
};
enum class ComparisonOp : u32 {
GreaterThan = 1,
GreaterThanEqual = 2,
LessThan = 3,
LessThanEqual = 4,
Equal = 5,
Inequal = 6,
};
union Cheat {
std::array<u8, 16> raw;
BitField<4, 4, CodeType> type;
BitField<0, 4, u32> width; // Can be 1, 2, 4, or 8. Measured in bytes.
BitField<0, 4, u32> end_of_loop;
BitField<12, 4, MemoryType> memory_type;
BitField<8, 4, u32> register_3;
BitField<8, 4, ComparisonOp> comparison_op;
BitField<20, 4, u32> load_from_register;
BitField<20, 4, u32> increment_register;
BitField<20, 4, ArithmeticOp> arithmetic_op;
BitField<16, 4, u32> add_additional_register;
BitField<28, 4, u32> register_6;
u64 Address() const;
u64 ValueWidth(u64 offset) const;
u64 Value(u64 offset, u64 width) const;
u32 KeypadValue() const;
};
class CheatParser;
// Represents a full collection of cheats for a game. The Execute function should be called every
// interval that all cheats should be executed. Clients should not directly instantiate this class
// (hence private constructor), they should instead receive an instance from CheatParser, which
// guarantees the list is always in an acceptable state.
class CheatList {
public:
friend class CheatParser;
using Block = std::vector<Cheat>;
using ProgramSegment = std::vector<std::pair<std::string, Block>>;
// (width in bytes, address, value)
using MemoryWriter = void (*)(u32, VAddr, u64);
// (width in bytes, address) -> value
using MemoryReader = u64 (*)(u32, VAddr);
void SetMemoryParameters(VAddr main_begin, VAddr heap_begin, VAddr main_end, VAddr heap_end,
MemoryWriter writer, MemoryReader reader);
void Execute();
private:
CheatList(ProgramSegment master, ProgramSegment standard);
void ProcessBlockPairs(const Block& block);
void ExecuteSingleCheat(const Cheat& cheat);
void ExecuteBlock(const Block& block);
bool EvaluateConditional(const Cheat& cheat) const;
// Individual cheat operations
void WriteImmediate(const Cheat& cheat);
void BeginConditional(const Cheat& cheat);
void EndConditional(const Cheat& cheat);
void Loop(const Cheat& cheat);
void LoadImmediate(const Cheat& cheat);
void LoadIndexed(const Cheat& cheat);
void StoreIndexed(const Cheat& cheat);
void RegisterArithmetic(const Cheat& cheat);
void BeginConditionalInput(const Cheat& cheat);
VAddr SanitizeAddress(VAddr in) const;
// Master Codes are defined as codes that cannot be disabled and are run prior to all
// others.
ProgramSegment master_list;
// All other codes
ProgramSegment standard_list;
bool in_standard = false;
// 16 (0x0-0xF) scratch registers that can be used by cheats
std::array<u64, 16> scratch{};
MemoryWriter writer = nullptr;
MemoryReader reader = nullptr;
u64 main_region_begin{};
u64 heap_region_begin{};
u64 main_region_end{};
u64 heap_region_end{};
u64 current_block{};
// The current index of the cheat within the current Block
u64 current_index{};
// The 'stack' of the program. When a conditional or loop statement is encountered, its index is
// pushed onto this queue. When a end block is encountered, the condition is checked.
std::map<u64, u64> block_pairs;
std::set<u64> encountered_loops;
};
// Intermediary class that parses a text file or other disk format for storing cheats into a
// CheatList object, that can be used for execution.
class CheatParser {
public:
virtual ~CheatParser();
virtual CheatList Parse(const std::vector<u8>& data) const = 0;
protected:
CheatList MakeCheatList(CheatList::ProgramSegment master,
CheatList::ProgramSegment standard) const;
};
// CheatParser implementation that parses text files
class TextCheatParser final : public CheatParser {
public:
~TextCheatParser() override;
CheatList Parse(const std::vector<u8>& data) const override;
private:
std::array<u8, 16> ParseSingleLineCheat(const std::string& line) const;
};
// Class that encapsulates a CheatList and manages its interaction with memory and CoreTiming
class CheatEngine final {
public:
CheatEngine(std::vector<CheatList> cheats, const std::string& build_id, VAddr code_region_start,
VAddr code_region_end);
~CheatEngine();
private:
void FrameCallback(u64 userdata, int cycles_late);
Core::Timing::EventType* event;
std::vector<CheatList> cheats;
};
} // namespace FileSys

View File

@@ -24,13 +24,26 @@ namespace FileSys {
union NCASectionHeader;
/// Describes the type of content within an NCA archive.
enum class NCAContentType : u8 {
/// Executable-related data
Program = 0,
/// Metadata.
Meta = 1,
/// Access control data.
Control = 2,
/// Information related to the game manual
/// e.g. Legal information, etc.
Manual = 3,
/// System data.
Data = 4,
Data_Unknown5 = 5, ///< Seems to be used on some system archives
/// Data that can be accessed by applications.
PublicData = 5,
};
enum class NCASectionCryptoType : u8 {

View File

@@ -7,6 +7,7 @@
#include <cstddef>
#include <cstring>
#include "common/file_util.h"
#include "common/hex_util.h"
#include "common/logging/log.h"
#include "core/file_sys/content_archive.h"
@@ -19,6 +20,7 @@
#include "core/file_sys/vfs_vector.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/loader/loader.h"
#include "core/loader/nso.h"
#include "core/settings.h"
namespace FileSys {
@@ -31,14 +33,6 @@ constexpr std::array<const char*, 14> EXEFS_FILE_NAMES{
"subsdk3", "subsdk4", "subsdk5", "subsdk6", "subsdk7", "subsdk8", "subsdk9",
};
struct NSOBuildHeader {
u32_le magic;
INSERT_PADDING_BYTES(0x3C);
std::array<u8, 0x20> build_id;
INSERT_PADDING_BYTES(0xA0);
};
static_assert(sizeof(NSOBuildHeader) == 0x100, "NSOBuildHeader has incorrect size.");
std::string FormatTitleVersion(u32 version, TitleVersionFormat format) {
std::array<u8, sizeof(u32)> bytes{};
bytes[0] = version % SINGLE_BYTE_MODULUS;
@@ -162,14 +156,16 @@ std::vector<VirtualFile> PatchManager::CollectPatches(const std::vector<VirtualD
}
std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso) const {
if (nso.size() < 0x100)
if (nso.size() < sizeof(Loader::NSOHeader)) {
return nso;
}
NSOBuildHeader header;
std::memcpy(&header, nso.data(), sizeof(NSOBuildHeader));
Loader::NSOHeader header;
std::memcpy(&header, nso.data(), sizeof(header));
if (header.magic != Common::MakeMagic('N', 'S', 'O', '0'))
if (header.magic != Common::MakeMagic('N', 'S', 'O', '0')) {
return nso;
}
const auto build_id_raw = Common::HexArrayToString(header.build_id);
const auto build_id = build_id_raw.substr(0, build_id_raw.find_last_not_of('0') + 1);
@@ -212,9 +208,11 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso) const {
}
}
if (out.size() < 0x100)
if (out.size() < sizeof(Loader::NSOHeader)) {
return nso;
std::memcpy(out.data(), &header, sizeof(NSOBuildHeader));
}
std::memcpy(out.data(), &header, sizeof(header));
return out;
}
@@ -232,6 +230,57 @@ bool PatchManager::HasNSOPatch(const std::array<u8, 32>& build_id_) const {
return !CollectPatches(patch_dirs, build_id).empty();
}
static std::optional<CheatList> ReadCheatFileFromFolder(u64 title_id,
const std::array<u8, 0x20>& build_id_,
const VirtualDir& base_path, bool upper) {
const auto build_id_raw = Common::HexArrayToString(build_id_, upper);
const auto build_id = build_id_raw.substr(0, sizeof(u64) * 2);
const auto file = base_path->GetFile(fmt::format("{}.txt", build_id));
if (file == nullptr) {
LOG_INFO(Common_Filesystem, "No cheats file found for title_id={:016X}, build_id={}",
title_id, build_id);
return std::nullopt;
}
std::vector<u8> data(file->GetSize());
if (file->Read(data.data(), data.size()) != data.size()) {
LOG_INFO(Common_Filesystem, "Failed to read cheats file for title_id={:016X}, build_id={}",
title_id, build_id);
return std::nullopt;
}
TextCheatParser parser;
return parser.Parse(data);
}
std::vector<CheatList> PatchManager::CreateCheatList(const std::array<u8, 32>& build_id_) const {
std::vector<CheatList> out;
const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
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(); });
out.reserve(patch_dirs.size());
for (const auto& subdir : patch_dirs) {
auto cheats_dir = subdir->GetSubdirectory("cheats");
if (cheats_dir != nullptr) {
auto res = ReadCheatFileFromFolder(title_id, build_id_, cheats_dir, true);
if (res.has_value()) {
out.push_back(std::move(*res));
continue;
}
res = ReadCheatFileFromFolder(title_id, build_id_, cheats_dir, false);
if (res.has_value())
out.push_back(std::move(*res));
}
}
return out;
}
static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type) {
const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
if ((type != ContentRecordType::Program && type != ContentRecordType::Data) ||
@@ -403,6 +452,8 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam
}
if (IsDirValidAndNonEmpty(mod->GetSubdirectory("romfs")))
AppendCommaIfNotEmpty(types, "LayeredFS");
if (IsDirValidAndNonEmpty(mod->GetSubdirectory("cheats")))
AppendCommaIfNotEmpty(types, "Cheats");
if (types.empty())
continue;

View File

@@ -8,6 +8,7 @@
#include <memory>
#include <string>
#include "common/common_types.h"
#include "core/file_sys/cheat_engine.h"
#include "core/file_sys/nca_metadata.h"
#include "core/file_sys/vfs.h"
@@ -45,6 +46,9 @@ public:
// Used to prevent expensive copies in NSO loader.
bool HasNSOPatch(const std::array<u8, 0x20>& build_id) const;
// Creates a CheatList object with all
std::vector<CheatList> CreateCheatList(const std::array<u8, 0x20>& build_id) const;
// Currently tracked RomFS patches:
// - Game Updates
// - LayeredFS

View File

@@ -94,7 +94,7 @@ static ContentRecordType GetCRTypeFromNCAType(NCAContentType type) {
case NCAContentType::Control:
return ContentRecordType::Control;
case NCAContentType::Data:
case NCAContentType::Data_Unknown5:
case NCAContentType::PublicData:
return ContentRecordType::Data;
case NCAContentType::Manual:
// TODO(DarkLordZach): Peek at NCA contents to differentiate Manual and Legal.

View File

@@ -39,10 +39,10 @@ struct CommandHeader {
union {
u32_le raw_low;
BitField<0, 16, CommandType> type;
BitField<16, 4, u32_le> num_buf_x_descriptors;
BitField<20, 4, u32_le> num_buf_a_descriptors;
BitField<24, 4, u32_le> num_buf_b_descriptors;
BitField<28, 4, u32_le> num_buf_w_descriptors;
BitField<16, 4, u32> num_buf_x_descriptors;
BitField<20, 4, u32> num_buf_a_descriptors;
BitField<24, 4, u32> num_buf_b_descriptors;
BitField<28, 4, u32> num_buf_w_descriptors;
};
enum class BufferDescriptorCFlag : u32 {
@@ -53,28 +53,28 @@ struct CommandHeader {
union {
u32_le raw_high;
BitField<0, 10, u32_le> data_size;
BitField<0, 10, u32> data_size;
BitField<10, 4, BufferDescriptorCFlag> buf_c_descriptor_flags;
BitField<31, 1, u32_le> enable_handle_descriptor;
BitField<31, 1, u32> enable_handle_descriptor;
};
};
static_assert(sizeof(CommandHeader) == 8, "CommandHeader size is incorrect");
union HandleDescriptorHeader {
u32_le raw_high;
BitField<0, 1, u32_le> send_current_pid;
BitField<1, 4, u32_le> num_handles_to_copy;
BitField<5, 4, u32_le> num_handles_to_move;
BitField<0, 1, u32> send_current_pid;
BitField<1, 4, u32> num_handles_to_copy;
BitField<5, 4, u32> num_handles_to_move;
};
static_assert(sizeof(HandleDescriptorHeader) == 4, "HandleDescriptorHeader size is incorrect");
struct BufferDescriptorX {
union {
BitField<0, 6, u32_le> counter_bits_0_5;
BitField<6, 3, u32_le> address_bits_36_38;
BitField<9, 3, u32_le> counter_bits_9_11;
BitField<12, 4, u32_le> address_bits_32_35;
BitField<16, 16, u32_le> size;
BitField<0, 6, u32> counter_bits_0_5;
BitField<6, 3, u32> address_bits_36_38;
BitField<9, 3, u32> counter_bits_9_11;
BitField<12, 4, u32> address_bits_32_35;
BitField<16, 16, u32> size;
};
u32_le address_bits_0_31;
@@ -103,10 +103,10 @@ struct BufferDescriptorABW {
u32_le address_bits_0_31;
union {
BitField<0, 2, u32_le> flags;
BitField<2, 3, u32_le> address_bits_36_38;
BitField<24, 4, u32_le> size_bits_32_35;
BitField<28, 4, u32_le> address_bits_32_35;
BitField<0, 2, u32> flags;
BitField<2, 3, u32> address_bits_36_38;
BitField<24, 4, u32> size_bits_32_35;
BitField<28, 4, u32> address_bits_32_35;
};
VAddr Address() const {
@@ -128,8 +128,8 @@ struct BufferDescriptorC {
u32_le address_bits_0_31;
union {
BitField<0, 16, u32_le> address_bits_32_47;
BitField<16, 16, u32_le> size;
BitField<0, 16, u32> address_bits_32_47;
BitField<16, 16, u32> size;
};
VAddr Address() const {
@@ -167,8 +167,8 @@ struct DomainMessageHeader {
struct {
union {
BitField<0, 8, CommandType> command;
BitField<8, 8, u32_le> input_object_count;
BitField<16, 16, u32_le> size;
BitField<8, 8, u32> input_object_count;
BitField<16, 16, u32> size;
};
u32_le object_id;
INSERT_PADDING_WORDS(2);

View File

@@ -274,6 +274,20 @@ inline void ResponseBuilder::Push(u64 value) {
Push(static_cast<u32>(value >> 32));
}
template <>
inline void ResponseBuilder::Push(float value) {
u32 integral;
std::memcpy(&integral, &value, sizeof(u32));
Push(integral);
}
template <>
inline void ResponseBuilder::Push(double value) {
u64 integral;
std::memcpy(&integral, &value, sizeof(u64));
Push(integral);
}
template <>
inline void ResponseBuilder::Push(bool value) {
Push(static_cast<u8>(value));
@@ -415,6 +429,22 @@ inline s64 RequestParser::Pop() {
return static_cast<s64>(Pop<u64>());
}
template <>
inline float RequestParser::Pop() {
const u32 value = Pop<u32>();
float real;
std::memcpy(&real, &value, sizeof(real));
return real;
}
template <>
inline double RequestParser::Pop() {
const u64 value = Pop<u64>();
float real;
std::memcpy(&real, &value, sizeof(real));
return real;
}
template <>
inline bool RequestParser::Pop() {
return Pop<u8>() != 0;

View File

@@ -0,0 +1,12 @@
// Copyright 2019 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/hle/kernel/code_set.h"
namespace Kernel {
CodeSet::CodeSet() = default;
CodeSet::~CodeSet() = default;
} // namespace Kernel

View File

@@ -0,0 +1,90 @@
// Copyright 2019 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <cstddef>
#include <memory>
#include <vector>
#include "common/common_types.h"
namespace Kernel {
/**
* Represents executable data that may be loaded into a kernel process.
*
* A code set consists of three basic segments:
* - A code (AKA text) segment,
* - A read-only data segment (rodata)
* - A data segment
*
* The code segment is the portion of the object file that contains
* executable instructions.
*
* The read-only data segment in the portion of the object file that
* contains (as one would expect) read-only data, such as fixed constant
* values and data structures.
*
* The data segment is similar to the read-only data segment -- it contains
* variables and data structures that have predefined values, however,
* entities within this segment can be modified.
*/
struct CodeSet final {
/// A single segment within a code set.
struct Segment final {
/// The byte offset that this segment is located at.
std::size_t offset = 0;
/// The address to map this segment to.
VAddr addr = 0;
/// The size of this segment in bytes.
u32 size = 0;
};
explicit CodeSet();
~CodeSet();
CodeSet(const CodeSet&) = delete;
CodeSet& operator=(const CodeSet&) = delete;
CodeSet(CodeSet&&) = default;
CodeSet& operator=(CodeSet&&) = default;
Segment& CodeSegment() {
return segments[0];
}
const Segment& CodeSegment() const {
return segments[0];
}
Segment& RODataSegment() {
return segments[1];
}
const Segment& RODataSegment() const {
return segments[1];
}
Segment& DataSegment() {
return segments[2];
}
const Segment& DataSegment() const {
return segments[2];
}
/// The overall data that backs this code set.
std::shared_ptr<std::vector<u8>> memory;
/// The segments that comprise this code set.
std::array<Segment, 3> segments;
/// The entry point address for this code set.
VAddr entrypoint = 0;
};
} // namespace Kernel

View File

@@ -2,7 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <map>
#include <utility>
#include <vector>
@@ -10,8 +9,11 @@
#include "core/core.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/mutex.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/scheduler.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/result.h"
#include "core/memory.h"
@@ -57,41 +59,47 @@ static void TransferMutexOwnership(VAddr mutex_addr, SharedPtr<Thread> current_t
}
}
ResultCode Mutex::TryAcquire(HandleTable& handle_table, VAddr address, Handle holding_thread_handle,
Mutex::Mutex(Core::System& system) : system{system} {}
Mutex::~Mutex() = default;
ResultCode Mutex::TryAcquire(VAddr address, Handle holding_thread_handle,
Handle requesting_thread_handle) {
// The mutex address must be 4-byte aligned
if ((address % sizeof(u32)) != 0) {
return ERR_INVALID_ADDRESS;
}
const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
Thread* const current_thread = system.CurrentScheduler().GetCurrentThread();
SharedPtr<Thread> holding_thread = handle_table.Get<Thread>(holding_thread_handle);
SharedPtr<Thread> requesting_thread = handle_table.Get<Thread>(requesting_thread_handle);
// TODO(Subv): It is currently unknown if it is possible to lock a mutex in behalf of another
// thread.
ASSERT(requesting_thread == GetCurrentThread());
ASSERT(requesting_thread == current_thread);
u32 addr_value = Memory::Read32(address);
const u32 addr_value = Memory::Read32(address);
// If the mutex isn't being held, just return success.
if (addr_value != (holding_thread_handle | Mutex::MutexHasWaitersFlag)) {
return RESULT_SUCCESS;
}
if (holding_thread == nullptr)
if (holding_thread == nullptr) {
return ERR_INVALID_HANDLE;
}
// Wait until the mutex is released
GetCurrentThread()->SetMutexWaitAddress(address);
GetCurrentThread()->SetWaitHandle(requesting_thread_handle);
current_thread->SetMutexWaitAddress(address);
current_thread->SetWaitHandle(requesting_thread_handle);
GetCurrentThread()->SetStatus(ThreadStatus::WaitMutex);
GetCurrentThread()->InvalidateWakeupCallback();
current_thread->SetStatus(ThreadStatus::WaitMutex);
current_thread->InvalidateWakeupCallback();
// Update the lock holder thread's priority to prevent priority inversion.
holding_thread->AddMutexWaiter(GetCurrentThread());
holding_thread->AddMutexWaiter(current_thread);
Core::System::GetInstance().PrepareReschedule();
system.PrepareReschedule();
return RESULT_SUCCESS;
}
@@ -102,7 +110,8 @@ ResultCode Mutex::Release(VAddr address) {
return ERR_INVALID_ADDRESS;
}
auto [thread, num_waiters] = GetHighestPriorityMutexWaitingThread(GetCurrentThread(), address);
auto* const current_thread = system.CurrentScheduler().GetCurrentThread();
auto [thread, num_waiters] = GetHighestPriorityMutexWaitingThread(current_thread, address);
// There are no more threads waiting for the mutex, release it completely.
if (thread == nullptr) {
@@ -111,7 +120,7 @@ ResultCode Mutex::Release(VAddr address) {
}
// Transfer the ownership of the mutex from the previous owner to the new one.
TransferMutexOwnership(address, GetCurrentThread(), thread);
TransferMutexOwnership(address, current_thread, thread);
u32 mutex_value = thread->GetWaitHandle();

View File

@@ -5,32 +5,34 @@
#pragma once
#include "common/common_types.h"
#include "core/hle/kernel/object.h"
union ResultCode;
namespace Kernel {
namespace Core {
class System;
}
class HandleTable;
class Thread;
namespace Kernel {
class Mutex final {
public:
explicit Mutex(Core::System& system);
~Mutex();
/// Flag that indicates that a mutex still has threads waiting for it.
static constexpr u32 MutexHasWaitersFlag = 0x40000000;
/// Mask of the bits in a mutex address value that contain the mutex owner.
static constexpr u32 MutexOwnerMask = 0xBFFFFFFF;
/// Attempts to acquire a mutex at the specified address.
static ResultCode TryAcquire(HandleTable& handle_table, VAddr address,
Handle holding_thread_handle, Handle requesting_thread_handle);
ResultCode TryAcquire(VAddr address, Handle holding_thread_handle,
Handle requesting_thread_handle);
/// Releases the mutex at the specified address.
static ResultCode Release(VAddr address);
ResultCode Release(VAddr address);
private:
Mutex() = default;
~Mutex() = default;
Core::System& system;
};
} // namespace Kernel

View File

@@ -9,6 +9,7 @@
#include "common/logging/log.h"
#include "core/core.h"
#include "core/file_sys/program_metadata.h"
#include "core/hle/kernel/code_set.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
@@ -31,7 +32,7 @@ namespace {
*/
void SetupMainThread(Process& owner_process, KernelCore& kernel, VAddr entry_point, u32 priority) {
// Setup page table so we can write to memory
SetCurrentPageTable(&owner_process.VMManager().page_table);
Memory::SetCurrentPageTable(&owner_process.VMManager().page_table);
// Initialize new "main" thread
const VAddr stack_top = owner_process.VMManager().GetTLSIORegionEndAddress();
@@ -50,9 +51,6 @@ void SetupMainThread(Process& owner_process, KernelCore& kernel, VAddr entry_poi
}
} // Anonymous namespace
CodeSet::CodeSet() = default;
CodeSet::~CodeSet() = default;
SharedPtr<Process> Process::Create(Core::System& system, std::string&& name) {
auto& kernel = system.Kernel();
@@ -212,7 +210,7 @@ void Process::FreeTLSSlot(VAddr tls_address) {
}
void Process::LoadModule(CodeSet module_, VAddr base_addr) {
const auto MapSegment = [&](CodeSet::Segment& segment, VMAPermission permissions,
const auto MapSegment = [&](const CodeSet::Segment& segment, VMAPermission permissions,
MemoryState memory_state) {
const auto vma = vm_manager
.MapMemoryBlock(segment.addr + base_addr, module_.memory,
@@ -222,16 +220,17 @@ void Process::LoadModule(CodeSet module_, VAddr base_addr) {
};
// Map CodeSet segments
MapSegment(module_.CodeSegment(), VMAPermission::ReadExecute, MemoryState::CodeStatic);
MapSegment(module_.RODataSegment(), VMAPermission::Read, MemoryState::CodeMutable);
MapSegment(module_.DataSegment(), VMAPermission::ReadWrite, MemoryState::CodeMutable);
MapSegment(module_.CodeSegment(), VMAPermission::ReadExecute, MemoryState::Code);
MapSegment(module_.RODataSegment(), VMAPermission::Read, MemoryState::CodeData);
MapSegment(module_.DataSegment(), VMAPermission::ReadWrite, MemoryState::CodeData);
// Clear instruction cache in CPU JIT
system.InvalidateCpuInstructionCaches();
}
Process::Process(Core::System& system)
: WaitObject{system.Kernel()}, address_arbiter{system}, system{system} {}
: WaitObject{system.Kernel()}, address_arbiter{system}, mutex{system}, system{system} {}
Process::~Process() = default;
void Process::Acquire(Thread* thread) {

View File

@@ -7,13 +7,13 @@
#include <array>
#include <bitset>
#include <cstddef>
#include <memory>
#include <string>
#include <vector>
#include <boost/container/static_vector.hpp>
#include "common/common_types.h"
#include "core/hle/kernel/address_arbiter.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/mutex.h"
#include "core/hle/kernel/process_capability.h"
#include "core/hle/kernel/vm_manager.h"
#include "core/hle/kernel/wait_object.h"
@@ -33,6 +33,8 @@ class KernelCore;
class ResourceLimit;
class Thread;
struct CodeSet;
struct AddressMapping {
// Address and size must be page-aligned
VAddr address;
@@ -65,46 +67,6 @@ enum class ProcessStatus {
DebugBreak,
};
struct CodeSet final {
struct Segment {
std::size_t offset = 0;
VAddr addr = 0;
u32 size = 0;
};
explicit CodeSet();
~CodeSet();
Segment& CodeSegment() {
return segments[0];
}
const Segment& CodeSegment() const {
return segments[0];
}
Segment& RODataSegment() {
return segments[1];
}
const Segment& RODataSegment() const {
return segments[1];
}
Segment& DataSegment() {
return segments[2];
}
const Segment& DataSegment() const {
return segments[2];
}
std::shared_ptr<std::vector<u8>> memory;
std::array<Segment, 3> segments;
VAddr entrypoint = 0;
};
class Process final : public WaitObject {
public:
enum : u64 {
@@ -165,6 +127,16 @@ public:
return address_arbiter;
}
/// Gets a reference to the process' mutex lock.
Mutex& GetMutex() {
return mutex;
}
/// Gets a const reference to the process' mutex lock
const Mutex& GetMutex() const {
return mutex;
}
/// Gets the current status of the process
ProcessStatus GetStatus() const {
return status;
@@ -327,6 +299,11 @@ private:
/// Per-process address arbiter.
AddressArbiter address_arbiter;
/// The per-process mutex lock instance used for handling various
/// forms of services, such as lock arbitration, and condition
/// variable related facilities.
Mutex mutex;
/// Random values for svcGetInfo RandomEntropy
std::array<u64, RANDOM_ENTROPY_SIZE> random_entropy;

View File

@@ -96,7 +96,7 @@ void Scheduler::SwitchContext(Thread* new_thread) {
auto* const thread_owner_process = current_thread->GetOwnerProcess();
if (previous_process != thread_owner_process) {
system.Kernel().MakeCurrentProcess(thread_owner_process);
SetCurrentPageTable(&thread_owner_process->VMManager().page_table);
Memory::SetCurrentPageTable(&thread_owner_process->VMManager().page_table);
}
cpu_core.LoadContext(new_thread->GetContext());
@@ -199,8 +199,7 @@ void Scheduler::YieldWithoutLoadBalancing(Thread* thread) {
ASSERT(thread->GetPriority() < THREADPRIO_COUNT);
// Yield this thread -- sleep for zero time and force reschedule to different thread
WaitCurrentThread_Sleep();
GetCurrentThread()->WakeAfterDelay(0);
GetCurrentThread()->Sleep(0);
}
void Scheduler::YieldWithLoadBalancing(Thread* thread) {
@@ -215,8 +214,7 @@ void Scheduler::YieldWithLoadBalancing(Thread* thread) {
ASSERT(priority < THREADPRIO_COUNT);
// Sleep for zero time to be able to force reschedule to different thread
WaitCurrentThread_Sleep();
GetCurrentThread()->WakeAfterDelay(0);
GetCurrentThread()->Sleep(0);
Thread* suggested_thread = nullptr;

View File

@@ -551,9 +551,9 @@ static ResultCode ArbitrateLock(Handle holding_thread_handle, VAddr mutex_addr,
return ERR_INVALID_ADDRESS;
}
auto& handle_table = Core::CurrentProcess()->GetHandleTable();
return Mutex::TryAcquire(handle_table, mutex_addr, holding_thread_handle,
requesting_thread_handle);
auto* const current_process = Core::System::GetInstance().Kernel().CurrentProcess();
return current_process->GetMutex().TryAcquire(mutex_addr, holding_thread_handle,
requesting_thread_handle);
}
/// Unlock a mutex
@@ -571,7 +571,8 @@ static ResultCode ArbitrateUnlock(VAddr mutex_addr) {
return ERR_INVALID_ADDRESS;
}
return Mutex::Release(mutex_addr);
auto* const current_process = Core::System::GetInstance().Kernel().CurrentProcess();
return current_process->GetMutex().Release(mutex_addr);
}
enum class BreakType : u32 {
@@ -1284,10 +1285,14 @@ static ResultCode StartThread(Handle thread_handle) {
/// Called when a thread exits
static void ExitThread() {
LOG_TRACE(Kernel_SVC, "called, pc=0x{:08X}", Core::CurrentArmInterface().GetPC());
auto& system = Core::System::GetInstance();
ExitCurrentThread();
Core::System::GetInstance().PrepareReschedule();
LOG_TRACE(Kernel_SVC, "called, pc=0x{:08X}", system.CurrentArmInterface().GetPC());
auto* const current_thread = system.CurrentScheduler().GetCurrentThread();
current_thread->Stop();
system.CurrentScheduler().RemoveThread(current_thread);
system.PrepareReschedule();
}
/// Sleep the current thread
@@ -1300,32 +1305,32 @@ static void SleepThread(s64 nanoseconds) {
YieldAndWaitForLoadBalancing = -2,
};
auto& system = Core::System::GetInstance();
auto& scheduler = system.CurrentScheduler();
auto* const current_thread = scheduler.GetCurrentThread();
if (nanoseconds <= 0) {
auto& scheduler{Core::System::GetInstance().CurrentScheduler()};
switch (static_cast<SleepType>(nanoseconds)) {
case SleepType::YieldWithoutLoadBalancing:
scheduler.YieldWithoutLoadBalancing(GetCurrentThread());
scheduler.YieldWithoutLoadBalancing(current_thread);
break;
case SleepType::YieldWithLoadBalancing:
scheduler.YieldWithLoadBalancing(GetCurrentThread());
scheduler.YieldWithLoadBalancing(current_thread);
break;
case SleepType::YieldAndWaitForLoadBalancing:
scheduler.YieldAndWaitForLoadBalancing(GetCurrentThread());
scheduler.YieldAndWaitForLoadBalancing(current_thread);
break;
default:
UNREACHABLE_MSG("Unimplemented sleep yield type '{:016X}'!", nanoseconds);
}
} else {
// Sleep current thread and check for next thread to schedule
WaitCurrentThread_Sleep();
// Create an event to wake the thread up after the specified nanosecond delay has passed
GetCurrentThread()->WakeAfterDelay(nanoseconds);
current_thread->Sleep(nanoseconds);
}
// Reschedule all CPU cores
for (std::size_t i = 0; i < Core::NUM_CPU_CORES; ++i)
Core::System::GetInstance().CpuCore(i).PrepareReschedule();
for (std::size_t i = 0; i < Core::NUM_CPU_CORES; ++i) {
system.CpuCore(i).PrepareReschedule();
}
}
/// Wait process wide key atomic
@@ -1336,11 +1341,15 @@ static ResultCode WaitProcessWideKeyAtomic(VAddr mutex_addr, VAddr condition_var
"called mutex_addr={:X}, condition_variable_addr={:X}, thread_handle=0x{:08X}, timeout={}",
mutex_addr, condition_variable_addr, thread_handle, nano_seconds);
const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
auto* const current_process = Core::System::GetInstance().Kernel().CurrentProcess();
const auto& handle_table = current_process->GetHandleTable();
SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
ASSERT(thread);
CASCADE_CODE(Mutex::Release(mutex_addr));
const auto release_result = current_process->GetMutex().Release(mutex_addr);
if (release_result.IsError()) {
return release_result;
}
SharedPtr<Thread> current_thread = GetCurrentThread();
current_thread->SetCondVarWaitAddress(condition_variable_addr);

View File

@@ -7,8 +7,6 @@
#include <optional>
#include <vector>
#include <boost/range/algorithm_ext/erase.hpp>
#include "common/assert.h"
#include "common/common_types.h"
#include "common/logging/log.h"
@@ -68,17 +66,6 @@ void Thread::Stop() {
owner_process->FreeTLSSlot(tls_address);
}
void WaitCurrentThread_Sleep() {
Thread* thread = GetCurrentThread();
thread->SetStatus(ThreadStatus::WaitSleep);
}
void ExitCurrentThread() {
Thread* thread = GetCurrentThread();
thread->Stop();
Core::System::GetInstance().CurrentScheduler().RemoveThread(thread);
}
void Thread::WakeAfterDelay(s64 nanoseconds) {
// Don't schedule a wakeup if the thread wants to wait forever
if (nanoseconds == -1)
@@ -269,8 +256,8 @@ void Thread::AddMutexWaiter(SharedPtr<Thread> thread) {
if (thread->lock_owner == this) {
// If the thread is already waiting for this thread to release the mutex, ensure that the
// waiters list is consistent and return without doing anything.
auto itr = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread);
ASSERT(itr != wait_mutex_threads.end());
const auto iter = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread);
ASSERT(iter != wait_mutex_threads.end());
return;
}
@@ -278,11 +265,16 @@ void Thread::AddMutexWaiter(SharedPtr<Thread> thread) {
ASSERT(thread->lock_owner == nullptr);
// Ensure that the thread is not already in the list of mutex waiters
auto itr = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread);
ASSERT(itr == wait_mutex_threads.end());
const auto iter = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread);
ASSERT(iter == wait_mutex_threads.end());
// Keep the list in an ordered fashion
const auto insertion_point = std::find_if(
wait_mutex_threads.begin(), wait_mutex_threads.end(),
[&thread](const auto& entry) { return entry->GetPriority() > thread->GetPriority(); });
wait_mutex_threads.insert(insertion_point, thread);
thread->lock_owner = this;
wait_mutex_threads.emplace_back(std::move(thread));
UpdatePriority();
}
@@ -290,32 +282,44 @@ void Thread::RemoveMutexWaiter(SharedPtr<Thread> thread) {
ASSERT(thread->lock_owner == this);
// Ensure that the thread is in the list of mutex waiters
auto itr = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread);
ASSERT(itr != wait_mutex_threads.end());
const auto iter = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread);
ASSERT(iter != wait_mutex_threads.end());
wait_mutex_threads.erase(iter);
boost::remove_erase(wait_mutex_threads, thread);
thread->lock_owner = nullptr;
UpdatePriority();
}
void Thread::UpdatePriority() {
// Find the highest priority among all the threads that are waiting for this thread's lock
// If any of the threads waiting on the mutex have a higher priority
// (taking into account priority inheritance), then this thread inherits
// that thread's priority.
u32 new_priority = nominal_priority;
for (const auto& thread : wait_mutex_threads) {
if (thread->nominal_priority < new_priority)
new_priority = thread->nominal_priority;
if (!wait_mutex_threads.empty()) {
if (wait_mutex_threads.front()->current_priority < new_priority) {
new_priority = wait_mutex_threads.front()->current_priority;
}
}
if (new_priority == current_priority)
if (new_priority == current_priority) {
return;
}
scheduler->SetThreadPriority(this, new_priority);
current_priority = new_priority;
if (!lock_owner) {
return;
}
// Ensure that the thread is within the correct location in the waiting list.
auto old_owner = lock_owner;
lock_owner->RemoveMutexWaiter(this);
old_owner->AddMutexWaiter(this);
// Recursively update the priority of the thread that depends on the priority of this one.
if (lock_owner)
lock_owner->UpdatePriority();
lock_owner->UpdatePriority();
}
void Thread::ChangeCore(u32 core, u64 mask) {
@@ -391,6 +395,14 @@ void Thread::SetActivity(ThreadActivity value) {
}
}
void Thread::Sleep(s64 nanoseconds) {
// Sleep current thread and check for next thread to schedule
SetStatus(ThreadStatus::WaitSleep);
// Create an event to wake the thread up after the specified nanosecond delay has passed
WakeAfterDelay(nanoseconds);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
/**

View File

@@ -383,6 +383,9 @@ public:
void SetActivity(ThreadActivity value);
/// Sleeps this thread for the given amount of nanoseconds.
void Sleep(s64 nanoseconds);
private:
explicit Thread(KernelCore& kernel);
~Thread() override;
@@ -398,8 +401,14 @@ private:
VAddr entry_point = 0;
VAddr stack_top = 0;
u32 nominal_priority = 0; ///< Nominal thread priority, as set by the emulated application
u32 current_priority = 0; ///< Current thread priority, can be temporarily changed
/// Nominal thread priority, as set by the emulated application.
/// The nominal priority is the thread priority without priority
/// inheritance taken into account.
u32 nominal_priority = 0;
/// Current thread priority. This may change over the course of the
/// thread's lifetime in order to facilitate priority inheritance.
u32 current_priority = 0;
u64 total_cpu_time_ticks = 0; ///< Total CPU running ticks.
u64 last_running_ticks = 0; ///< CPU tick when thread was last running
@@ -460,14 +469,4 @@ private:
*/
Thread* GetCurrentThread();
/**
* Waits the current thread on a sleep
*/
void WaitCurrentThread_Sleep();
/**
* Stops the current thread and removes it from the thread_list
*/
void ExitCurrentThread();
} // namespace Kernel

View File

@@ -7,29 +7,29 @@
#include <utility>
#include "common/assert.h"
#include "common/logging/log.h"
#include "common/memory_hook.h"
#include "core/arm/arm_interface.h"
#include "core/core.h"
#include "core/file_sys/program_metadata.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/vm_manager.h"
#include "core/memory.h"
#include "core/memory_hook.h"
#include "core/memory_setup.h"
namespace Kernel {
namespace {
const char* GetMemoryStateName(MemoryState state) {
static constexpr const char* names[] = {
"Unmapped", "Io",
"Normal", "CodeStatic",
"CodeMutable", "Heap",
"Shared", "Unknown1",
"ModuleCodeStatic", "ModuleCodeMutable",
"IpcBuffer0", "Stack",
"ThreadLocal", "TransferMemoryIsolated",
"TransferMemory", "ProcessMemory",
"Inaccessible", "IpcBuffer1",
"IpcBuffer3", "KernelStack",
"Unmapped", "Io",
"Normal", "Code",
"CodeData", "Heap",
"Shared", "Unknown1",
"ModuleCode", "ModuleCodeData",
"IpcBuffer0", "Stack",
"ThreadLocal", "TransferMemoryIsolated",
"TransferMemory", "ProcessMemory",
"Inaccessible", "IpcBuffer1",
"IpcBuffer3", "KernelStack",
};
return names[ToSvcMemoryState(state)];
@@ -177,7 +177,7 @@ ResultVal<VAddr> VMManager::FindFreeRegion(u64 size) const {
ResultVal<VMManager::VMAHandle> VMManager::MapMMIO(VAddr target, PAddr paddr, u64 size,
MemoryState state,
Memory::MemoryHookPointer mmio_handler) {
Common::MemoryHookPointer mmio_handler) {
// This is the appropriately sized VMA that will turn into our allocation.
CASCADE_RESULT(VMAIter vma_handle, CarveVMA(target, size));
VirtualMemoryArea& final_vma = vma_handle->second;
@@ -624,7 +624,7 @@ void VMManager::ClearPageTable() {
std::fill(page_table.pointers.begin(), page_table.pointers.end(), nullptr);
page_table.special_regions.clear();
std::fill(page_table.attributes.begin(), page_table.attributes.end(),
Memory::PageType::Unmapped);
Common::PageType::Unmapped);
}
VMManager::CheckResults VMManager::CheckRangeState(VAddr address, u64 size, MemoryState state_mask,

View File

@@ -9,9 +9,10 @@
#include <tuple>
#include <vector>
#include "common/common_types.h"
#include "common/memory_hook.h"
#include "common/page_table.h"
#include "core/hle/result.h"
#include "core/memory.h"
#include "core/memory_hook.h"
namespace FileSys {
enum class ProgramAddressSpaceType : u8;
@@ -164,12 +165,12 @@ enum class MemoryState : u32 {
Unmapped = 0x00,
Io = 0x01 | FlagMapped,
Normal = 0x02 | FlagMapped | FlagQueryPhysicalAddressAllowed,
CodeStatic = 0x03 | CodeFlags | FlagMapProcess,
CodeMutable = 0x04 | CodeFlags | FlagMapProcess | FlagCodeMemory,
Code = 0x03 | CodeFlags | FlagMapProcess,
CodeData = 0x04 | DataFlags | FlagMapProcess | FlagCodeMemory,
Heap = 0x05 | DataFlags | FlagCodeMemory,
Shared = 0x06 | FlagMapped | FlagMemoryPoolAllocated,
ModuleCodeStatic = 0x08 | CodeFlags | FlagModule | FlagMapProcess,
ModuleCodeMutable = 0x09 | DataFlags | FlagModule | FlagMapProcess | FlagCodeMemory,
ModuleCode = 0x08 | CodeFlags | FlagModule | FlagMapProcess,
ModuleCodeData = 0x09 | DataFlags | FlagModule | FlagMapProcess | FlagCodeMemory,
IpcBuffer0 = 0x0A | FlagMapped | FlagQueryPhysicalAddressAllowed | FlagMemoryPoolAllocated |
IPCFlags | FlagSharedDevice | FlagSharedDeviceAligned,
@@ -290,7 +291,7 @@ struct VirtualMemoryArea {
// Settings for type = MMIO
/// Physical address of the register area this VMA maps to.
PAddr paddr = 0;
Memory::MemoryHookPointer mmio_handler = nullptr;
Common::MemoryHookPointer mmio_handler = nullptr;
/// Tests if this area can be merged to the right with `next`.
bool CanBeMergedWith(const VirtualMemoryArea& next) const;
@@ -368,7 +369,7 @@ public:
* @param mmio_handler The handler that will implement read and write for this MMIO region.
*/
ResultVal<VMAHandle> MapMMIO(VAddr target, PAddr paddr, u64 size, MemoryState state,
Memory::MemoryHookPointer mmio_handler);
Common::MemoryHookPointer mmio_handler);
/// Unmaps a range of addresses, splitting VMAs as necessary.
ResultCode UnmapRange(VAddr target, u64 size);
@@ -509,7 +510,7 @@ public:
/// Each VMManager has its own page table, which is set as the main one when the owning process
/// is scheduled.
Memory::PageTable page_table;
Common::PageTable page_table{Memory::PAGE_BITS};
private:
using VMAIter = VMAMap::iterator;
@@ -616,6 +617,9 @@ private:
VAddr new_map_region_base = 0;
VAddr new_map_region_end = 0;
VAddr main_code_region_base = 0;
VAddr main_code_region_end = 0;
VAddr tls_io_region_base = 0;
VAddr tls_io_region_end = 0;

View File

@@ -2,10 +2,10 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <array>
#include <cinttypes>
#include <cstring>
#include <stack>
#include "audio_core/audio_renderer.h"
#include "core/core.h"
#include "core/file_sys/savedata_factory.h"
@@ -93,38 +93,84 @@ void IWindowController::AcquireForegroundRights(Kernel::HLERequestContext& ctx)
}
IAudioController::IAudioController() : ServiceFramework("IAudioController") {
// clang-format off
static const FunctionInfo functions[] = {
{0, &IAudioController::SetExpectedMasterVolume, "SetExpectedMasterVolume"},
{1, &IAudioController::GetMainAppletExpectedMasterVolume,
"GetMainAppletExpectedMasterVolume"},
{2, &IAudioController::GetLibraryAppletExpectedMasterVolume,
"GetLibraryAppletExpectedMasterVolume"},
{3, nullptr, "ChangeMainAppletMasterVolume"},
{4, nullptr, "SetTransparentVolumeRate"},
{1, &IAudioController::GetMainAppletExpectedMasterVolume, "GetMainAppletExpectedMasterVolume"},
{2, &IAudioController::GetLibraryAppletExpectedMasterVolume, "GetLibraryAppletExpectedMasterVolume"},
{3, &IAudioController::ChangeMainAppletMasterVolume, "ChangeMainAppletMasterVolume"},
{4, &IAudioController::SetTransparentAudioRate, "SetTransparentVolumeRate"},
};
// clang-format on
RegisterHandlers(functions);
}
IAudioController::~IAudioController() = default;
void IAudioController::SetExpectedMasterVolume(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
IPC::RequestParser rp{ctx};
const float main_applet_volume_tmp = rp.Pop<float>();
const float library_applet_volume_tmp = rp.Pop<float>();
LOG_DEBUG(Service_AM, "called. main_applet_volume={}, library_applet_volume={}",
main_applet_volume_tmp, library_applet_volume_tmp);
// Ensure the volume values remain within the 0-100% range
main_applet_volume = std::clamp(main_applet_volume_tmp, min_allowed_volume, max_allowed_volume);
library_applet_volume =
std::clamp(library_applet_volume_tmp, min_allowed_volume, max_allowed_volume);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void IAudioController::GetMainAppletExpectedMasterVolume(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
LOG_DEBUG(Service_AM, "called. main_applet_volume={}", main_applet_volume);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push(volume);
rb.Push(main_applet_volume);
}
void IAudioController::GetLibraryAppletExpectedMasterVolume(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
LOG_DEBUG(Service_AM, "called. library_applet_volume={}", library_applet_volume);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push(volume);
rb.Push(library_applet_volume);
}
void IAudioController::ChangeMainAppletMasterVolume(Kernel::HLERequestContext& ctx) {
struct Parameters {
float volume;
s64 fade_time_ns;
};
static_assert(sizeof(Parameters) == 16);
IPC::RequestParser rp{ctx};
const auto parameters = rp.PopRaw<Parameters>();
LOG_DEBUG(Service_AM, "called. volume={}, fade_time_ns={}", parameters.volume,
parameters.fade_time_ns);
main_applet_volume = std::clamp(parameters.volume, min_allowed_volume, max_allowed_volume);
fade_time_ns = std::chrono::nanoseconds{parameters.fade_time_ns};
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void IAudioController::SetTransparentAudioRate(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const float transparent_volume_rate_tmp = rp.Pop<float>();
LOG_DEBUG(Service_AM, "called. transparent_volume_rate={}", transparent_volume_rate_tmp);
// Clamp volume range to 0-100%.
transparent_volume_rate =
std::clamp(transparent_volume_rate_tmp, min_allowed_volume, max_allowed_volume);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
IDisplayController::IDisplayController() : ServiceFramework("IDisplayController") {
@@ -169,7 +215,21 @@ IDisplayController::IDisplayController() : ServiceFramework("IDisplayController"
IDisplayController::~IDisplayController() = default;
IDebugFunctions::IDebugFunctions() : ServiceFramework("IDebugFunctions") {}
IDebugFunctions::IDebugFunctions() : ServiceFramework{"IDebugFunctions"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "NotifyMessageToHomeMenuForDebug"},
{1, nullptr, "OpenMainApplication"},
{10, nullptr, "EmulateButtonEvent"},
{20, nullptr, "InvalidateTransitionLayer"},
{30, nullptr, "RequestLaunchApplicationWithUserAndArgumentForDebug"},
{40, nullptr, "GetAppletResourceUsageInfo"},
};
// clang-format on
RegisterHandlers(functions);
}
IDebugFunctions::~IDebugFunctions() = default;
ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)

View File

@@ -4,6 +4,7 @@
#pragma once
#include <chrono>
#include <memory>
#include <queue>
#include "core/hle/kernel/writable_event.h"
@@ -81,8 +82,21 @@ private:
void SetExpectedMasterVolume(Kernel::HLERequestContext& ctx);
void GetMainAppletExpectedMasterVolume(Kernel::HLERequestContext& ctx);
void GetLibraryAppletExpectedMasterVolume(Kernel::HLERequestContext& ctx);
void ChangeMainAppletMasterVolume(Kernel::HLERequestContext& ctx);
void SetTransparentAudioRate(Kernel::HLERequestContext& ctx);
u32 volume{100};
static constexpr float min_allowed_volume = 0.0f;
static constexpr float max_allowed_volume = 1.0f;
float main_applet_volume{0.25f};
float library_applet_volume{max_allowed_volume};
float transparent_volume_rate{min_allowed_volume};
// Volume transition fade time in nanoseconds.
// e.g. If the main applet volume was 0% and was changed to 50%
// with a fade of 50ns, then over the course of 50ns,
// the volume will gradually fade up to 50%
std::chrono::nanoseconds fade_time_ns{0};
};
class IDisplayController final : public ServiceFramework<IDisplayController> {

View File

@@ -8,6 +8,7 @@
#include <vector>
#include <opus.h>
#include <opus_multistream.h>
#include "common/assert.h"
#include "common/logging/log.h"
@@ -18,12 +19,12 @@
namespace Service::Audio {
namespace {
struct OpusDeleter {
void operator()(void* ptr) const {
operator delete(ptr);
void operator()(OpusMSDecoder* ptr) const {
opus_multistream_decoder_destroy(ptr);
}
};
using OpusDecoderPtr = std::unique_ptr<OpusDecoder, OpusDeleter>;
using OpusDecoderPtr = std::unique_ptr<OpusMSDecoder, OpusDeleter>;
struct OpusPacketHeader {
// Packet size in bytes.
@@ -33,7 +34,7 @@ struct OpusPacketHeader {
};
static_assert(sizeof(OpusPacketHeader) == 0x8, "OpusHeader is an invalid size");
class OpusDecoderStateBase {
class OpusDecoderState {
public:
/// Describes extra behavior that may be asked of the decoding context.
enum class ExtraBehavior {
@@ -49,22 +50,13 @@ public:
Enabled,
};
virtual ~OpusDecoderStateBase() = default;
// Decodes interleaved Opus packets. Optionally allows reporting time taken to
// perform the decoding, as well as any relevant extra behavior.
virtual void DecodeInterleaved(Kernel::HLERequestContext& ctx, PerfTime perf_time,
ExtraBehavior extra_behavior) = 0;
};
// Represents the decoder state for a non-multistream decoder.
class OpusDecoderState final : public OpusDecoderStateBase {
public:
explicit OpusDecoderState(OpusDecoderPtr decoder, u32 sample_rate, u32 channel_count)
: decoder{std::move(decoder)}, sample_rate{sample_rate}, channel_count{channel_count} {}
// Decodes interleaved Opus packets. Optionally allows reporting time taken to
// perform the decoding, as well as any relevant extra behavior.
void DecodeInterleaved(Kernel::HLERequestContext& ctx, PerfTime perf_time,
ExtraBehavior extra_behavior) override {
ExtraBehavior extra_behavior) {
if (perf_time == PerfTime::Disabled) {
DecodeInterleavedHelper(ctx, nullptr, extra_behavior);
} else {
@@ -135,7 +127,7 @@ private:
const int frame_size = (static_cast<int>(raw_output_sz / sizeof(s16) / channel_count));
const auto out_sample_count =
opus_decode(decoder.get(), frame, hdr.size, output.data(), frame_size, 0);
opus_multistream_decode(decoder.get(), frame, hdr.size, output.data(), frame_size, 0);
if (out_sample_count < 0) {
LOG_ERROR(Audio,
"Incorrect sample count received from opus_decode, "
@@ -158,7 +150,7 @@ private:
void ResetDecoderContext() {
ASSERT(decoder != nullptr);
opus_decoder_ctl(decoder.get(), OPUS_RESET_STATE);
opus_multistream_decoder_ctl(decoder.get(), OPUS_RESET_STATE);
}
OpusDecoderPtr decoder;
@@ -168,7 +160,7 @@ private:
class IHardwareOpusDecoderManager final : public ServiceFramework<IHardwareOpusDecoderManager> {
public:
explicit IHardwareOpusDecoderManager(std::unique_ptr<OpusDecoderStateBase> decoder_state)
explicit IHardwareOpusDecoderManager(OpusDecoderState decoder_state)
: ServiceFramework("IHardwareOpusDecoderManager"), decoder_state{std::move(decoder_state)} {
// clang-format off
static const FunctionInfo functions[] = {
@@ -190,35 +182,51 @@ private:
void DecodeInterleavedOld(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Audio, "called");
decoder_state->DecodeInterleaved(ctx, OpusDecoderStateBase::PerfTime::Disabled,
OpusDecoderStateBase::ExtraBehavior::None);
decoder_state.DecodeInterleaved(ctx, OpusDecoderState::PerfTime::Disabled,
OpusDecoderState::ExtraBehavior::None);
}
void DecodeInterleavedWithPerfOld(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Audio, "called");
decoder_state->DecodeInterleaved(ctx, OpusDecoderStateBase::PerfTime::Enabled,
OpusDecoderStateBase::ExtraBehavior::None);
decoder_state.DecodeInterleaved(ctx, OpusDecoderState::PerfTime::Enabled,
OpusDecoderState::ExtraBehavior::None);
}
void DecodeInterleaved(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Audio, "called");
IPC::RequestParser rp{ctx};
const auto extra_behavior = rp.Pop<bool>()
? OpusDecoderStateBase::ExtraBehavior::ResetContext
: OpusDecoderStateBase::ExtraBehavior::None;
const auto extra_behavior = rp.Pop<bool>() ? OpusDecoderState::ExtraBehavior::ResetContext
: OpusDecoderState::ExtraBehavior::None;
decoder_state->DecodeInterleaved(ctx, OpusDecoderStateBase::PerfTime::Enabled,
extra_behavior);
decoder_state.DecodeInterleaved(ctx, OpusDecoderState::PerfTime::Enabled, extra_behavior);
}
std::unique_ptr<OpusDecoderStateBase> decoder_state;
OpusDecoderState decoder_state;
};
std::size_t WorkerBufferSize(u32 channel_count) {
ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count");
return opus_decoder_get_size(static_cast<int>(channel_count));
constexpr int num_streams = 1;
const int num_stereo_streams = channel_count == 2 ? 1 : 0;
return opus_multistream_decoder_get_size(num_streams, num_stereo_streams);
}
// Creates the mapping table that maps the input channels to the particular
// output channels. In the stereo case, we map the left and right input channels
// to the left and right output channels respectively.
//
// However, in the monophonic case, we only map the one available channel
// to the sole output channel. We specify 255 for the would-be right channel
// as this is a special value defined by Opus to indicate to the decoder to
// ignore that channel.
std::array<u8, 2> CreateMappingTable(u32 channel_count) {
if (channel_count == 2) {
return {{0, 1}};
}
return {{0, 255}};
}
} // Anonymous namespace
@@ -259,9 +267,15 @@ void HwOpus::OpenOpusDecoder(Kernel::HLERequestContext& ctx) {
const std::size_t worker_sz = WorkerBufferSize(channel_count);
ASSERT_MSG(buffer_sz >= worker_sz, "Worker buffer too large");
OpusDecoderPtr decoder{static_cast<OpusDecoder*>(operator new(worker_sz))};
if (const int err = opus_decoder_init(decoder.get(), sample_rate, channel_count)) {
LOG_ERROR(Audio, "Failed to init opus decoder with error={}", err);
const int num_stereo_streams = channel_count == 2 ? 1 : 0;
const auto mapping_table = CreateMappingTable(channel_count);
int error = 0;
OpusDecoderPtr decoder{
opus_multistream_decoder_create(sample_rate, static_cast<int>(channel_count), 1,
num_stereo_streams, mapping_table.data(), &error)};
if (error != OPUS_OK || decoder == nullptr) {
LOG_ERROR(Audio, "Failed to create Opus decoder (error={}).", error);
IPC::ResponseBuilder rb{ctx, 2};
// TODO(ogniK): Use correct error code
rb.Push(ResultCode(-1));
@@ -271,7 +285,7 @@ void HwOpus::OpenOpusDecoder(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IHardwareOpusDecoderManager>(
std::make_unique<OpusDecoderState>(std::move(decoder), sample_rate, channel_count));
OpusDecoderState{std::move(decoder), sample_rate, channel_count});
}
HwOpus::HwOpus() : ServiceFramework("hwopus") {

View File

@@ -733,7 +733,10 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
FSP_SRV::~FSP_SRV() = default;
void FSP_SRV::SetCurrentProcess(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_FS, "(STUBBED) called");
IPC::RequestParser rp{ctx};
current_process_id = rp.Pop<u64>();
LOG_DEBUG(Service_FS, "called. current_process_id=0x{:016X}", current_process_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);

View File

@@ -32,6 +32,7 @@ private:
void OpenPatchDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx);
FileSys::VirtualFile romfs;
u64 current_process_id = 0;
};
} // namespace Service::FileSystem

View File

@@ -41,20 +41,20 @@ private:
struct PadState {
union {
u32_le raw{};
BitField<0, 1, u32_le> a;
BitField<1, 1, u32_le> b;
BitField<2, 1, u32_le> x;
BitField<3, 1, u32_le> y;
BitField<4, 1, u32_le> l;
BitField<5, 1, u32_le> r;
BitField<6, 1, u32_le> zl;
BitField<7, 1, u32_le> zr;
BitField<8, 1, u32_le> plus;
BitField<9, 1, u32_le> minus;
BitField<10, 1, u32_le> d_left;
BitField<11, 1, u32_le> d_up;
BitField<12, 1, u32_le> d_right;
BitField<13, 1, u32_le> d_down;
BitField<0, 1, u32> a;
BitField<1, 1, u32> b;
BitField<2, 1, u32> x;
BitField<3, 1, u32> y;
BitField<4, 1, u32> l;
BitField<5, 1, u32> r;
BitField<6, 1, u32> zl;
BitField<7, 1, u32> zr;
BitField<8, 1, u32> plus;
BitField<9, 1, u32> minus;
BitField<10, 1, u32> d_left;
BitField<11, 1, u32> d_up;
BitField<12, 1, u32> d_right;
BitField<13, 1, u32> d_down;
};
};
static_assert(sizeof(PadState) == 0x4, "PadState is an invalid size");
@@ -62,7 +62,7 @@ private:
struct Attributes {
union {
u32_le raw{};
BitField<0, 1, u32_le> connected;
BitField<0, 1, u32> connected;
};
};
static_assert(sizeof(Attributes) == 0x4, "Attributes is an invalid size");

View File

@@ -39,13 +39,13 @@ public:
union {
u32_le raw{};
BitField<0, 1, u32_le> pro_controller;
BitField<1, 1, u32_le> handheld;
BitField<2, 1, u32_le> joycon_dual;
BitField<3, 1, u32_le> joycon_left;
BitField<4, 1, u32_le> joycon_right;
BitField<0, 1, u32> pro_controller;
BitField<1, 1, u32> handheld;
BitField<2, 1, u32> joycon_dual;
BitField<3, 1, u32> joycon_left;
BitField<4, 1, u32> joycon_right;
BitField<6, 1, u32_le> pokeball; // TODO(ogniK): Confirm when possible
BitField<6, 1, u32> pokeball; // TODO(ogniK): Confirm when possible
};
};
static_assert(sizeof(NPadType) == 4, "NPadType is an invalid size");
@@ -150,43 +150,43 @@ private:
union {
u64_le raw{};
// Button states
BitField<0, 1, u64_le> a;
BitField<1, 1, u64_le> b;
BitField<2, 1, u64_le> x;
BitField<3, 1, u64_le> y;
BitField<4, 1, u64_le> l_stick;
BitField<5, 1, u64_le> r_stick;
BitField<6, 1, u64_le> l;
BitField<7, 1, u64_le> r;
BitField<8, 1, u64_le> zl;
BitField<9, 1, u64_le> zr;
BitField<10, 1, u64_le> plus;
BitField<11, 1, u64_le> minus;
BitField<0, 1, u64> a;
BitField<1, 1, u64> b;
BitField<2, 1, u64> x;
BitField<3, 1, u64> y;
BitField<4, 1, u64> l_stick;
BitField<5, 1, u64> r_stick;
BitField<6, 1, u64> l;
BitField<7, 1, u64> r;
BitField<8, 1, u64> zl;
BitField<9, 1, u64> zr;
BitField<10, 1, u64> plus;
BitField<11, 1, u64> minus;
// D-Pad
BitField<12, 1, u64_le> d_left;
BitField<13, 1, u64_le> d_up;
BitField<14, 1, u64_le> d_right;
BitField<15, 1, u64_le> d_down;
BitField<12, 1, u64> d_left;
BitField<13, 1, u64> d_up;
BitField<14, 1, u64> d_right;
BitField<15, 1, u64> d_down;
// Left JoyStick
BitField<16, 1, u64_le> l_stick_left;
BitField<17, 1, u64_le> l_stick_up;
BitField<18, 1, u64_le> l_stick_right;
BitField<19, 1, u64_le> l_stick_down;
BitField<16, 1, u64> l_stick_left;
BitField<17, 1, u64> l_stick_up;
BitField<18, 1, u64> l_stick_right;
BitField<19, 1, u64> l_stick_down;
// Right JoyStick
BitField<20, 1, u64_le> r_stick_left;
BitField<21, 1, u64_le> r_stick_up;
BitField<22, 1, u64_le> r_stick_right;
BitField<23, 1, u64_le> r_stick_down;
BitField<20, 1, u64> r_stick_left;
BitField<21, 1, u64> r_stick_up;
BitField<22, 1, u64> r_stick_right;
BitField<23, 1, u64> r_stick_down;
// Not always active?
BitField<24, 1, u64_le> left_sl;
BitField<25, 1, u64_le> left_sr;
BitField<24, 1, u64> left_sl;
BitField<25, 1, u64> left_sr;
BitField<26, 1, u64_le> right_sl;
BitField<27, 1, u64_le> right_sr;
BitField<26, 1, u64> right_sl;
BitField<27, 1, u64> right_sr;
};
};
static_assert(sizeof(ControllerPadState) == 8, "ControllerPadState is an invalid size");
@@ -200,12 +200,12 @@ private:
struct ConnectionState {
union {
u32_le raw{};
BitField<0, 1, u32_le> IsConnected;
BitField<1, 1, u32_le> IsWired;
BitField<2, 1, u32_le> IsLeftJoyConnected;
BitField<3, 1, u32_le> IsLeftJoyWired;
BitField<4, 1, u32_le> IsRightJoyConnected;
BitField<5, 1, u32_le> IsRightJoyWired;
BitField<0, 1, u32> IsConnected;
BitField<1, 1, u32> IsWired;
BitField<2, 1, u32> IsLeftJoyConnected;
BitField<3, 1, u32> IsLeftJoyWired;
BitField<4, 1, u32> IsRightJoyConnected;
BitField<5, 1, u32> IsRightJoyWired;
};
};
static_assert(sizeof(ConnectionState) == 4, "ConnectionState is an invalid size");
@@ -240,23 +240,23 @@ private:
struct NPadProperties {
union {
s64_le raw{};
BitField<11, 1, s64_le> is_vertical;
BitField<12, 1, s64_le> is_horizontal;
BitField<13, 1, s64_le> use_plus;
BitField<14, 1, s64_le> use_minus;
BitField<11, 1, s64> is_vertical;
BitField<12, 1, s64> is_horizontal;
BitField<13, 1, s64> use_plus;
BitField<14, 1, s64> use_minus;
};
};
struct NPadDevice {
union {
u32_le raw{};
BitField<0, 1, s32_le> pro_controller;
BitField<1, 1, s32_le> handheld;
BitField<2, 1, s32_le> handheld_left;
BitField<3, 1, s32_le> handheld_right;
BitField<4, 1, s32_le> joycon_left;
BitField<5, 1, s32_le> joycon_right;
BitField<6, 1, s32_le> pokeball;
BitField<0, 1, s32> pro_controller;
BitField<1, 1, s32> handheld;
BitField<2, 1, s32> handheld_left;
BitField<3, 1, s32> handheld_right;
BitField<4, 1, s32> joycon_left;
BitField<5, 1, s32> joycon_right;
BitField<6, 1, s32> pokeball;
};
};

View File

@@ -33,8 +33,8 @@ private:
struct Attributes {
union {
u32 raw{};
BitField<0, 1, u32_le> start_touch;
BitField<1, 1, u32_le> end_touch;
BitField<0, 1, u32> start_touch;
BitField<1, 1, u32> end_touch;
};
};
static_assert(sizeof(Attributes) == 0x4, "Attributes is an invalid size");

View File

@@ -4,6 +4,9 @@
#pragma once
#include "core/hle/service/hid/controllers/controller_base.h"
#include "core/hle/service/service.h"
#include "controllers/controller_base.h"
#include "core/hle/service/service.h"

View File

@@ -319,15 +319,14 @@ public:
}
ASSERT(vm_manager
.MirrorMemory(*map_address, nro_addr, nro_size,
Kernel::MemoryState::ModuleCodeStatic)
.MirrorMemory(*map_address, nro_addr, nro_size, Kernel::MemoryState::ModuleCode)
.IsSuccess());
ASSERT(vm_manager.UnmapRange(nro_addr, nro_size).IsSuccess());
if (bss_size > 0) {
ASSERT(vm_manager
.MirrorMemory(*map_address + nro_size, bss_addr, bss_size,
Kernel::MemoryState::ModuleCodeStatic)
Kernel::MemoryState::ModuleCode)
.IsSuccess());
ASSERT(vm_manager.UnmapRange(bss_addr, bss_size).IsSuccess());
}
@@ -388,8 +387,7 @@ public:
const auto& nro_size = iter->second.size;
ASSERT(vm_manager
.MirrorMemory(heap_addr, mapped_addr, nro_size,
Kernel::MemoryState::ModuleCodeStatic)
.MirrorMemory(heap_addr, mapped_addr, nro_size, Kernel::MemoryState::ModuleCode)
.IsSuccess());
ASSERT(vm_manager.UnmapRange(mapped_addr, nro_size).IsSuccess());

View File

@@ -42,7 +42,7 @@ private:
union {
BitField<0, 16, Flags> flags;
BitField<16, 8, Severity> severity;
BitField<24, 8, u32_le> verbosity;
BitField<24, 8, u32> verbosity;
};
u32_le payload_size;

View File

@@ -19,11 +19,11 @@ public:
virtual ~nvdevice() = default;
union Ioctl {
u32_le raw;
BitField<0, 8, u32_le> cmd;
BitField<8, 8, u32_le> group;
BitField<16, 14, u32_le> length;
BitField<30, 1, u32_le> is_in;
BitField<31, 1, u32_le> is_out;
BitField<0, 8, u32> cmd;
BitField<8, 8, u32> group;
BitField<16, 14, u32> length;
BitField<30, 1, u32> is_in;
BitField<31, 1, u32> is_out;
};
/**

View File

@@ -9,6 +9,7 @@
#include "common/common_types.h"
#include "common/file_util.h"
#include "common/logging/log.h"
#include "core/hle/kernel/code_set.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/vm_manager.h"
#include "core/loader/elf.h"

View File

@@ -1,147 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <vector>
#include "common/common_funcs.h"
#include "common/logging/log.h"
#include "common/swap.h"
#include "core/loader/linker.h"
#include "core/memory.h"
namespace Loader {
enum class RelocationType : u32 { ABS64 = 257, GLOB_DAT = 1025, JUMP_SLOT = 1026, RELATIVE = 1027 };
enum DynamicType : u32 {
DT_NULL = 0,
DT_PLTRELSZ = 2,
DT_STRTAB = 5,
DT_SYMTAB = 6,
DT_RELA = 7,
DT_RELASZ = 8,
DT_STRSZ = 10,
DT_JMPREL = 23,
};
struct Elf64_Rela {
u64_le offset;
RelocationType type;
u32_le symbol;
s64_le addend;
};
static_assert(sizeof(Elf64_Rela) == 0x18, "Elf64_Rela has incorrect size.");
struct Elf64_Dyn {
u64_le tag;
u64_le value;
};
static_assert(sizeof(Elf64_Dyn) == 0x10, "Elf64_Dyn has incorrect size.");
struct Elf64_Sym {
u32_le name;
INSERT_PADDING_BYTES(0x2);
u16_le shndx;
u64_le value;
u64_le size;
};
static_assert(sizeof(Elf64_Sym) == 0x18, "Elf64_Sym has incorrect size.");
void Linker::WriteRelocations(std::vector<u8>& program_image, const std::vector<Symbol>& symbols,
u64 relocation_offset, u64 size, VAddr load_base) {
for (u64 i = 0; i < size; i += sizeof(Elf64_Rela)) {
Elf64_Rela rela;
std::memcpy(&rela, &program_image[relocation_offset + i], sizeof(Elf64_Rela));
const Symbol& symbol = symbols[rela.symbol];
switch (rela.type) {
case RelocationType::RELATIVE: {
const u64 value = load_base + rela.addend;
if (!symbol.name.empty()) {
exports[symbol.name] = value;
}
std::memcpy(&program_image[rela.offset], &value, sizeof(u64));
break;
}
case RelocationType::JUMP_SLOT:
case RelocationType::GLOB_DAT:
if (!symbol.value) {
imports[symbol.name] = {rela.offset + load_base, 0};
} else {
exports[symbol.name] = symbol.value;
std::memcpy(&program_image[rela.offset], &symbol.value, sizeof(u64));
}
break;
case RelocationType::ABS64:
if (!symbol.value) {
imports[symbol.name] = {rela.offset + load_base, rela.addend};
} else {
const u64 value = symbol.value + rela.addend;
exports[symbol.name] = value;
std::memcpy(&program_image[rela.offset], &value, sizeof(u64));
}
break;
default:
LOG_CRITICAL(Loader, "Unknown relocation type: {}", static_cast<int>(rela.type));
break;
}
}
}
void Linker::Relocate(std::vector<u8>& program_image, u32 dynamic_section_offset, VAddr load_base) {
std::map<u64, u64> dynamic;
while (dynamic_section_offset < program_image.size()) {
Elf64_Dyn dyn;
std::memcpy(&dyn, &program_image[dynamic_section_offset], sizeof(Elf64_Dyn));
dynamic_section_offset += sizeof(Elf64_Dyn);
if (dyn.tag == DT_NULL) {
break;
}
dynamic[dyn.tag] = dyn.value;
}
u64 offset = dynamic[DT_SYMTAB];
std::vector<Symbol> symbols;
while (offset < program_image.size()) {
Elf64_Sym sym;
std::memcpy(&sym, &program_image[offset], sizeof(Elf64_Sym));
offset += sizeof(Elf64_Sym);
if (sym.name >= dynamic[DT_STRSZ]) {
break;
}
std::string name = reinterpret_cast<char*>(&program_image[dynamic[DT_STRTAB] + sym.name]);
if (sym.value) {
exports[name] = load_base + sym.value;
symbols.emplace_back(std::move(name), load_base + sym.value);
} else {
symbols.emplace_back(std::move(name), 0);
}
}
if (dynamic.find(DT_RELA) != dynamic.end()) {
WriteRelocations(program_image, symbols, dynamic[DT_RELA], dynamic[DT_RELASZ], load_base);
}
if (dynamic.find(DT_JMPREL) != dynamic.end()) {
WriteRelocations(program_image, symbols, dynamic[DT_JMPREL], dynamic[DT_PLTRELSZ],
load_base);
}
}
void Linker::ResolveImports() {
// Resolve imports
for (const auto& import : imports) {
const auto& search = exports.find(import.first);
if (search != exports.end()) {
Memory::Write64(import.second.ea, search->second + import.second.addend);
} else {
LOG_ERROR(Loader, "Unresolved import: {}", import.first);
}
}
}
} // namespace Loader

View File

@@ -1,36 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <map>
#include <string>
#include "common/common_types.h"
namespace Loader {
class Linker {
protected:
struct Symbol {
Symbol(std::string&& name, u64 value) : name(std::move(name)), value(value) {}
std::string name;
u64 value;
};
struct Import {
VAddr ea;
s64 addend;
};
void WriteRelocations(std::vector<u8>& program_image, const std::vector<Symbol>& symbols,
u64 relocation_offset, u64 size, VAddr load_base);
void Relocate(std::vector<u8>& program_image, u32 dynamic_section_offset, VAddr load_base);
void ResolveImports();
std::map<std::string, Import> imports;
std::map<std::string, VAddr> exports;
};
} // namespace Loader

View File

@@ -14,6 +14,7 @@
#include "core/file_sys/romfs_factory.h"
#include "core/file_sys/vfs_offset.h"
#include "core/gdbstub/gdbstub.h"
#include "core/hle/kernel/code_set.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/vm_manager.h"
#include "core/hle/service/filesystem/filesystem.h"

View File

@@ -4,10 +4,10 @@
#pragma once
#include <memory>
#include <string>
#include <vector>
#include "common/common_types.h"
#include "core/loader/linker.h"
#include "core/loader/loader.h"
namespace FileSys {
@@ -21,7 +21,7 @@ class Process;
namespace Loader {
/// Loads an NRO file
class AppLoader_NRO final : public AppLoader, Linker {
class AppLoader_NRO final : public AppLoader {
public:
explicit AppLoader_NRO(FileSys::VirtualFile file);
~AppLoader_NRO() override;

View File

@@ -7,10 +7,13 @@
#include <lz4.h>
#include "common/common_funcs.h"
#include "common/file_util.h"
#include "common/hex_util.h"
#include "common/logging/log.h"
#include "common/swap.h"
#include "core/core.h"
#include "core/file_sys/patch_manager.h"
#include "core/gdbstub/gdbstub.h"
#include "core/hle/kernel/code_set.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/vm_manager.h"
#include "core/loader/nso.h"
@@ -18,36 +21,8 @@
#include "core/settings.h"
namespace Loader {
struct NsoSegmentHeader {
u32_le offset;
u32_le location;
u32_le size;
union {
u32_le alignment;
u32_le bss_size;
};
};
static_assert(sizeof(NsoSegmentHeader) == 0x10, "NsoSegmentHeader has incorrect size.");
struct NsoHeader {
u32_le magic;
u32_le version;
INSERT_PADDING_WORDS(1);
u8 flags;
std::array<NsoSegmentHeader, 3> segments; // Text, RoData, Data (in that order)
std::array<u8, 0x20> build_id;
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.");
struct ModHeader {
namespace {
struct MODHeader {
u32_le magic;
u32_le dynamic_offset;
u32_le bss_start_offset;
@@ -56,7 +31,32 @@ struct ModHeader {
u32_le eh_frame_hdr_end_offset;
u32_le module_offset; // Offset to runtime-generated module object. typically equal to .bss base
};
static_assert(sizeof(ModHeader) == 0x1c, "ModHeader has incorrect size.");
static_assert(sizeof(MODHeader) == 0x1c, "MODHeader has incorrect size.");
std::vector<u8> DecompressSegment(const std::vector<u8>& compressed_data,
const NSOSegmentHeader& header) {
std::vector<u8> uncompressed_data(header.size);
const int bytes_uncompressed =
LZ4_decompress_safe(reinterpret_cast<const char*>(compressed_data.data()),
reinterpret_cast<char*>(uncompressed_data.data()),
static_cast<int>(compressed_data.size()), header.size);
ASSERT_MSG(bytes_uncompressed == static_cast<int>(header.size) &&
bytes_uncompressed == static_cast<int>(uncompressed_data.size()),
"{} != {} != {}", bytes_uncompressed, header.size, uncompressed_data.size());
return uncompressed_data;
}
constexpr u32 PageAlignSize(u32 size) {
return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK;
}
} // Anonymous namespace
bool NSOHeader::IsSegmentCompressed(size_t segment_num) const {
ASSERT_MSG(segment_num < 3, "Invalid segment {}", segment_num);
return ((flags >> segment_num) & 1) != 0;
}
AppLoader_NSO::AppLoader_NSO(FileSys::VirtualFile file) : AppLoader(std::move(file)) {}
@@ -73,38 +73,22 @@ FileType AppLoader_NSO::IdentifyType(const FileSys::VirtualFile& file) {
return FileType::NSO;
}
static std::vector<u8> DecompressSegment(const std::vector<u8>& compressed_data,
const NsoSegmentHeader& header) {
std::vector<u8> uncompressed_data(header.size);
const int bytes_uncompressed =
LZ4_decompress_safe(reinterpret_cast<const char*>(compressed_data.data()),
reinterpret_cast<char*>(uncompressed_data.data()),
static_cast<int>(compressed_data.size()), header.size);
ASSERT_MSG(bytes_uncompressed == static_cast<int>(header.size) &&
bytes_uncompressed == static_cast<int>(uncompressed_data.size()),
"{} != {} != {}", bytes_uncompressed, header.size, uncompressed_data.size());
return uncompressed_data;
}
static constexpr u32 PageAlignSize(u32 size) {
return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK;
}
std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process,
const FileSys::VfsFile& file, VAddr load_base,
bool should_pass_arguments,
std::optional<FileSys::PatchManager> pm) {
if (file.GetSize() < sizeof(NsoHeader))
if (file.GetSize() < sizeof(NSOHeader)) {
return {};
}
NsoHeader nso_header{};
if (sizeof(NsoHeader) != file.ReadObject(&nso_header))
NSOHeader nso_header{};
if (sizeof(NSOHeader) != file.ReadObject(&nso_header)) {
return {};
}
if (nso_header.magic != Common::MakeMagic('N', 'S', 'O', '0'))
if (nso_header.magic != Common::MakeMagic('N', 'S', 'O', '0')) {
return {};
}
// Build program image
Kernel::CodeSet codeset;
@@ -140,10 +124,10 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process,
std::memcpy(&module_offset, program_image.data() + 4, sizeof(u32));
// Read MOD header
ModHeader mod_header{};
MODHeader mod_header{};
// Default .bss to size in segment header if MOD0 section doesn't exist
u32 bss_size{PageAlignSize(nso_header.segments[2].bss_size)};
std::memcpy(&mod_header, program_image.data() + module_offset, sizeof(ModHeader));
std::memcpy(&mod_header, program_image.data() + module_offset, sizeof(MODHeader));
const bool has_mod_header{mod_header.magic == Common::MakeMagic('M', 'O', 'D', '0')};
if (has_mod_header) {
// Resize program image to include .bss section and page align each section
@@ -155,13 +139,25 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process,
// Apply patches if necessary
if (pm && (pm->HasNSOPatch(nso_header.build_id) || Settings::values.dump_nso)) {
std::vector<u8> pi_header(program_image.size() + 0x100);
std::memcpy(pi_header.data(), &nso_header, sizeof(NsoHeader));
std::memcpy(pi_header.data() + 0x100, program_image.data(), program_image.size());
std::vector<u8> pi_header(sizeof(NSOHeader) + program_image.size());
pi_header.insert(pi_header.begin(), reinterpret_cast<u8*>(&nso_header),
reinterpret_cast<u8*>(&nso_header) + sizeof(NSOHeader));
pi_header.insert(pi_header.begin() + sizeof(NSOHeader), program_image.begin(),
program_image.end());
pi_header = pm->PatchNSO(pi_header);
std::memcpy(program_image.data(), pi_header.data() + 0x100, program_image.size());
std::copy(pi_header.begin() + sizeof(NSOHeader), pi_header.end(), program_image.begin());
}
// Apply cheats if they exist and the program has a valid title ID
if (pm) {
const auto cheats = pm->CreateCheatList(nso_header.build_id);
if (!cheats.empty()) {
Core::System::GetInstance().RegisterCheatList(
cheats, Common::HexArrayToString(nso_header.build_id), load_base,
load_base + program_image.size());
}
}
// Load codeset for current process

View File

@@ -4,10 +4,12 @@
#pragma once
#include <array>
#include <optional>
#include <type_traits>
#include "common/common_types.h"
#include "common/swap.h"
#include "core/file_sys/patch_manager.h"
#include "core/loader/linker.h"
#include "core/loader/loader.h"
namespace Kernel {
@@ -16,6 +18,43 @@ class Process;
namespace Loader {
struct NSOSegmentHeader {
u32_le offset;
u32_le location;
u32_le size;
union {
u32_le alignment;
u32_le bss_size;
};
};
static_assert(sizeof(NSOSegmentHeader) == 0x10, "NsoSegmentHeader has incorrect size.");
struct NSOHeader {
using SHA256Hash = std::array<u8, 0x20>;
struct RODataRelativeExtent {
u32_le data_offset;
u32_le size;
};
u32_le magic;
u32_le version;
u32 reserved;
u32_le flags;
std::array<NSOSegmentHeader, 3> segments; // Text, RoData, Data (in that order)
std::array<u8, 0x20> build_id;
std::array<u32_le, 3> segments_compressed_size;
std::array<u8, 0x1C> padding;
RODataRelativeExtent api_info_extent;
RODataRelativeExtent dynstr_extent;
RODataRelativeExtent dynsyn_extent;
std::array<SHA256Hash, 3> segment_hashes;
bool IsSegmentCompressed(size_t segment_num) const;
};
static_assert(sizeof(NSOHeader) == 0x100, "NSOHeader has incorrect size.");
static_assert(std::is_trivially_copyable_v<NSOHeader>, "NSOHeader must be trivially copyable.");
constexpr u64 NSO_ARGUMENT_DATA_ALLOCATION_SIZE = 0x9000;
struct NSOArgumentHeader {
@@ -26,7 +65,7 @@ struct NSOArgumentHeader {
static_assert(sizeof(NSOArgumentHeader) == 0x20, "NSOArgumentHeader has incorrect size.");
/// Loads an NSO file
class AppLoader_NSO final : public AppLoader, Linker {
class AppLoader_NSO final : public AppLoader {
public:
explicit AppLoader_NSO(FileSys::VirtualFile file);

View File

@@ -10,6 +10,7 @@
#include "common/assert.h"
#include "common/common_types.h"
#include "common/logging/log.h"
#include "common/page_table.h"
#include "common/swap.h"
#include "core/arm/arm_interface.h"
#include "core/core.h"
@@ -18,13 +19,14 @@
#include "core/hle/lock.h"
#include "core/memory.h"
#include "core/memory_setup.h"
#include "video_core/gpu.h"
#include "video_core/renderer_base.h"
namespace Memory {
static PageTable* current_page_table = nullptr;
static Common::PageTable* current_page_table = nullptr;
void SetCurrentPageTable(PageTable* page_table) {
void SetCurrentPageTable(Common::PageTable* page_table) {
current_page_table = page_table;
auto& system = Core::System::GetInstance();
@@ -36,41 +38,19 @@ void SetCurrentPageTable(PageTable* page_table) {
}
}
PageTable* GetCurrentPageTable() {
Common::PageTable* GetCurrentPageTable() {
return current_page_table;
}
PageTable::PageTable() = default;
PageTable::PageTable(std::size_t address_space_width_in_bits) {
Resize(address_space_width_in_bits);
}
PageTable::~PageTable() = default;
void PageTable::Resize(std::size_t address_space_width_in_bits) {
const std::size_t num_page_table_entries = 1ULL << (address_space_width_in_bits - PAGE_BITS);
pointers.resize(num_page_table_entries);
attributes.resize(num_page_table_entries);
// The default is a 39-bit address space, which causes an initial 1GB allocation size. If the
// vector size is subsequently decreased (via resize), the vector might not automatically
// actually reallocate/resize its underlying allocation, which wastes up to ~800 MB for
// 36-bit titles. Call shrink_to_fit to reduce capacity to what's actually in use.
pointers.shrink_to_fit();
attributes.shrink_to_fit();
}
static void MapPages(PageTable& page_table, VAddr base, u64 size, u8* memory, PageType type) {
static void MapPages(Common::PageTable& page_table, VAddr base, u64 size, u8* memory,
Common::PageType type) {
LOG_DEBUG(HW_Memory, "Mapping {} onto {:016X}-{:016X}", fmt::ptr(memory), base * PAGE_SIZE,
(base + size) * PAGE_SIZE);
// During boot, current_page_table might not be set yet, in which case we need not flush
if (current_page_table) {
RasterizerFlushVirtualRegion(base << PAGE_BITS, size * PAGE_SIZE,
FlushMode::FlushAndInvalidate);
Core::System::GetInstance().GPU().FlushAndInvalidateRegion(base << PAGE_BITS,
size * PAGE_SIZE);
}
VAddr end = base + size;
@@ -91,41 +71,47 @@ static void MapPages(PageTable& page_table, VAddr base, u64 size, u8* memory, Pa
}
}
void MapMemoryRegion(PageTable& page_table, VAddr base, u64 size, u8* target) {
void MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, u8* target) {
ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size);
ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base);
MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, target, PageType::Memory);
MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, target, Common::PageType::Memory);
}
void MapIoRegion(PageTable& page_table, VAddr base, u64 size, MemoryHookPointer mmio_handler) {
void MapIoRegion(Common::PageTable& page_table, VAddr base, u64 size,
Common::MemoryHookPointer mmio_handler) {
ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size);
ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base);
MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, nullptr, PageType::Special);
MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, nullptr, Common::PageType::Special);
auto interval = boost::icl::discrete_interval<VAddr>::closed(base, base + size - 1);
SpecialRegion region{SpecialRegion::Type::IODevice, std::move(mmio_handler)};
page_table.special_regions.add(std::make_pair(interval, std::set<SpecialRegion>{region}));
Common::SpecialRegion region{Common::SpecialRegion::Type::IODevice, std::move(mmio_handler)};
page_table.special_regions.add(
std::make_pair(interval, std::set<Common::SpecialRegion>{region}));
}
void UnmapRegion(PageTable& page_table, VAddr base, u64 size) {
void UnmapRegion(Common::PageTable& page_table, VAddr base, u64 size) {
ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size);
ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base);
MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, nullptr, PageType::Unmapped);
MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, nullptr, Common::PageType::Unmapped);
auto interval = boost::icl::discrete_interval<VAddr>::closed(base, base + size - 1);
page_table.special_regions.erase(interval);
}
void AddDebugHook(PageTable& page_table, VAddr base, u64 size, MemoryHookPointer hook) {
void AddDebugHook(Common::PageTable& page_table, VAddr base, u64 size,
Common::MemoryHookPointer hook) {
auto interval = boost::icl::discrete_interval<VAddr>::closed(base, base + size - 1);
SpecialRegion region{SpecialRegion::Type::DebugHook, std::move(hook)};
page_table.special_regions.add(std::make_pair(interval, std::set<SpecialRegion>{region}));
Common::SpecialRegion region{Common::SpecialRegion::Type::DebugHook, std::move(hook)};
page_table.special_regions.add(
std::make_pair(interval, std::set<Common::SpecialRegion>{region}));
}
void RemoveDebugHook(PageTable& page_table, VAddr base, u64 size, MemoryHookPointer hook) {
void RemoveDebugHook(Common::PageTable& page_table, VAddr base, u64 size,
Common::MemoryHookPointer hook) {
auto interval = boost::icl::discrete_interval<VAddr>::closed(base, base + size - 1);
SpecialRegion region{SpecialRegion::Type::DebugHook, std::move(hook)};
page_table.special_regions.subtract(std::make_pair(interval, std::set<SpecialRegion>{region}));
Common::SpecialRegion region{Common::SpecialRegion::Type::DebugHook, std::move(hook)};
page_table.special_regions.subtract(
std::make_pair(interval, std::set<Common::SpecialRegion>{region}));
}
/**
@@ -174,19 +160,19 @@ T Read(const VAddr vaddr) {
return value;
}
PageType type = current_page_table->attributes[vaddr >> PAGE_BITS];
Common::PageType type = current_page_table->attributes[vaddr >> PAGE_BITS];
switch (type) {
case PageType::Unmapped:
case Common::PageType::Unmapped:
LOG_ERROR(HW_Memory, "Unmapped Read{} @ 0x{:08X}", sizeof(T) * 8, vaddr);
return 0;
case PageType::Memory:
case Common::PageType::Memory:
ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr);
break;
case PageType::RasterizerCachedMemory: {
RasterizerFlushVirtualRegion(vaddr, sizeof(T), FlushMode::Flush);
case Common::PageType::RasterizerCachedMemory: {
auto host_ptr{GetPointerFromVMA(vaddr)};
Core::System::GetInstance().GPU().FlushRegion(ToCacheAddr(host_ptr), sizeof(T));
T value;
std::memcpy(&value, GetPointerFromVMA(vaddr), sizeof(T));
std::memcpy(&value, host_ptr, sizeof(T));
return value;
}
default:
@@ -204,18 +190,19 @@ void Write(const VAddr vaddr, const T data) {
return;
}
PageType type = current_page_table->attributes[vaddr >> PAGE_BITS];
Common::PageType type = current_page_table->attributes[vaddr >> PAGE_BITS];
switch (type) {
case PageType::Unmapped:
case Common::PageType::Unmapped:
LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8,
static_cast<u32>(data), vaddr);
return;
case PageType::Memory:
case Common::PageType::Memory:
ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr);
break;
case PageType::RasterizerCachedMemory: {
RasterizerFlushVirtualRegion(vaddr, sizeof(T), FlushMode::Invalidate);
std::memcpy(GetPointerFromVMA(vaddr), &data, sizeof(T));
case Common::PageType::RasterizerCachedMemory: {
auto host_ptr{GetPointerFromVMA(vaddr)};
Core::System::GetInstance().GPU().InvalidateRegion(ToCacheAddr(host_ptr), sizeof(T));
std::memcpy(host_ptr, &data, sizeof(T));
break;
}
default:
@@ -230,10 +217,10 @@ bool IsValidVirtualAddress(const Kernel::Process& process, const VAddr vaddr) {
if (page_pointer)
return true;
if (page_table.attributes[vaddr >> PAGE_BITS] == PageType::RasterizerCachedMemory)
if (page_table.attributes[vaddr >> PAGE_BITS] == Common::PageType::RasterizerCachedMemory)
return true;
if (page_table.attributes[vaddr >> PAGE_BITS] != PageType::Special)
if (page_table.attributes[vaddr >> PAGE_BITS] != Common::PageType::Special)
return false;
return false;
@@ -253,7 +240,8 @@ u8* GetPointer(const VAddr vaddr) {
return page_pointer + (vaddr & PAGE_MASK);
}
if (current_page_table->attributes[vaddr >> PAGE_BITS] == PageType::RasterizerCachedMemory) {
if (current_page_table->attributes[vaddr >> PAGE_BITS] ==
Common::PageType::RasterizerCachedMemory) {
return GetPointerFromVMA(vaddr);
}
@@ -287,20 +275,20 @@ void RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) {
u64 num_pages = ((vaddr + size - 1) >> PAGE_BITS) - (vaddr >> PAGE_BITS) + 1;
for (unsigned i = 0; i < num_pages; ++i, vaddr += PAGE_SIZE) {
PageType& page_type = current_page_table->attributes[vaddr >> PAGE_BITS];
Common::PageType& page_type = current_page_table->attributes[vaddr >> PAGE_BITS];
if (cached) {
// Switch page type to cached if now cached
switch (page_type) {
case PageType::Unmapped:
case Common::PageType::Unmapped:
// It is not necessary for a process to have this region mapped into its address
// space, for example, a system module need not have a VRAM mapping.
break;
case PageType::Memory:
page_type = PageType::RasterizerCachedMemory;
case Common::PageType::Memory:
page_type = Common::PageType::RasterizerCachedMemory;
current_page_table->pointers[vaddr >> PAGE_BITS] = nullptr;
break;
case PageType::RasterizerCachedMemory:
case Common::PageType::RasterizerCachedMemory:
// There can be more than one GPU region mapped per CPU region, so it's common that
// this area is already marked as cached.
break;
@@ -310,23 +298,23 @@ void RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) {
} else {
// Switch page type to uncached if now uncached
switch (page_type) {
case PageType::Unmapped:
case Common::PageType::Unmapped:
// It is not necessary for a process to have this region mapped into its address
// space, for example, a system module need not have a VRAM mapping.
break;
case PageType::Memory:
case Common::PageType::Memory:
// There can be more than one GPU region mapped per CPU region, so it's common that
// this area is already unmarked as cached.
break;
case PageType::RasterizerCachedMemory: {
case Common::PageType::RasterizerCachedMemory: {
u8* pointer = GetPointerFromVMA(vaddr & ~PAGE_MASK);
if (pointer == nullptr) {
// It's possible that this function has been called while updating the pagetable
// after unmapping a VMA. In that case the underlying VMA will no longer exist,
// and we should just leave the pagetable entry blank.
page_type = PageType::Unmapped;
page_type = Common::PageType::Unmapped;
} else {
page_type = PageType::Memory;
page_type = Common::PageType::Memory;
current_page_table->pointers[vaddr >> PAGE_BITS] = pointer;
}
break;
@@ -338,47 +326,6 @@ void RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) {
}
}
void RasterizerFlushVirtualRegion(VAddr start, u64 size, FlushMode mode) {
auto& system_instance = Core::System::GetInstance();
// Since pages are unmapped on shutdown after video core is shutdown, the renderer may be
// null here
if (!system_instance.IsPoweredOn()) {
return;
}
const VAddr end = start + size;
const auto CheckRegion = [&](VAddr region_start, VAddr region_end) {
if (start >= region_end || end <= region_start) {
// No overlap with region
return;
}
const VAddr overlap_start = std::max(start, region_start);
const VAddr overlap_end = std::min(end, region_end);
const VAddr overlap_size = overlap_end - overlap_start;
auto& gpu = system_instance.GPU();
switch (mode) {
case FlushMode::Flush:
gpu.FlushRegion(ToCacheAddr(GetPointer(overlap_start)), overlap_size);
break;
case FlushMode::Invalidate:
gpu.InvalidateRegion(ToCacheAddr(GetPointer(overlap_start)), overlap_size);
break;
case FlushMode::FlushAndInvalidate:
gpu.FlushAndInvalidateRegion(ToCacheAddr(GetPointer(overlap_start)), overlap_size);
break;
}
};
const auto& vm_manager = Core::CurrentProcess()->VMManager();
CheckRegion(vm_manager.GetCodeRegionBaseAddress(), vm_manager.GetCodeRegionEndAddress());
CheckRegion(vm_manager.GetHeapRegionBaseAddress(), vm_manager.GetHeapRegionEndAddress());
}
u8 Read8(const VAddr addr) {
return Read<u8>(addr);
}
@@ -409,24 +356,24 @@ void ReadBlock(const Kernel::Process& process, const VAddr src_addr, void* dest_
const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
switch (page_table.attributes[page_index]) {
case PageType::Unmapped: {
case Common::PageType::Unmapped: {
LOG_ERROR(HW_Memory,
"Unmapped ReadBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
current_vaddr, src_addr, size);
std::memset(dest_buffer, 0, copy_amount);
break;
}
case PageType::Memory: {
case Common::PageType::Memory: {
DEBUG_ASSERT(page_table.pointers[page_index]);
const u8* src_ptr = page_table.pointers[page_index] + page_offset;
std::memcpy(dest_buffer, src_ptr, copy_amount);
break;
}
case PageType::RasterizerCachedMemory: {
RasterizerFlushVirtualRegion(current_vaddr, static_cast<u32>(copy_amount),
FlushMode::Flush);
std::memcpy(dest_buffer, GetPointerFromVMA(process, current_vaddr), copy_amount);
case Common::PageType::RasterizerCachedMemory: {
const auto& host_ptr{GetPointerFromVMA(process, current_vaddr)};
Core::System::GetInstance().GPU().FlushRegion(ToCacheAddr(host_ptr), copy_amount);
std::memcpy(dest_buffer, host_ptr, copy_amount);
break;
}
default:
@@ -473,23 +420,23 @@ void WriteBlock(const Kernel::Process& process, const VAddr dest_addr, const voi
const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
switch (page_table.attributes[page_index]) {
case PageType::Unmapped: {
case Common::PageType::Unmapped: {
LOG_ERROR(HW_Memory,
"Unmapped WriteBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
current_vaddr, dest_addr, size);
break;
}
case PageType::Memory: {
case Common::PageType::Memory: {
DEBUG_ASSERT(page_table.pointers[page_index]);
u8* dest_ptr = page_table.pointers[page_index] + page_offset;
std::memcpy(dest_ptr, src_buffer, copy_amount);
break;
}
case PageType::RasterizerCachedMemory: {
RasterizerFlushVirtualRegion(current_vaddr, static_cast<u32>(copy_amount),
FlushMode::Invalidate);
std::memcpy(GetPointerFromVMA(process, current_vaddr), src_buffer, copy_amount);
case Common::PageType::RasterizerCachedMemory: {
const auto& host_ptr{GetPointerFromVMA(process, current_vaddr)};
Core::System::GetInstance().GPU().InvalidateRegion(ToCacheAddr(host_ptr), copy_amount);
std::memcpy(host_ptr, src_buffer, copy_amount);
break;
}
default:
@@ -519,23 +466,23 @@ void ZeroBlock(const Kernel::Process& process, const VAddr dest_addr, const std:
const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
switch (page_table.attributes[page_index]) {
case PageType::Unmapped: {
case Common::PageType::Unmapped: {
LOG_ERROR(HW_Memory,
"Unmapped ZeroBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
current_vaddr, dest_addr, size);
break;
}
case PageType::Memory: {
case Common::PageType::Memory: {
DEBUG_ASSERT(page_table.pointers[page_index]);
u8* dest_ptr = page_table.pointers[page_index] + page_offset;
std::memset(dest_ptr, 0, copy_amount);
break;
}
case PageType::RasterizerCachedMemory: {
RasterizerFlushVirtualRegion(current_vaddr, static_cast<u32>(copy_amount),
FlushMode::Invalidate);
std::memset(GetPointerFromVMA(process, current_vaddr), 0, copy_amount);
case Common::PageType::RasterizerCachedMemory: {
const auto& host_ptr{GetPointerFromVMA(process, current_vaddr)};
Core::System::GetInstance().GPU().InvalidateRegion(ToCacheAddr(host_ptr), copy_amount);
std::memset(host_ptr, 0, copy_amount);
break;
}
default:
@@ -561,23 +508,23 @@ void CopyBlock(const Kernel::Process& process, VAddr dest_addr, VAddr src_addr,
const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
switch (page_table.attributes[page_index]) {
case PageType::Unmapped: {
case Common::PageType::Unmapped: {
LOG_ERROR(HW_Memory,
"Unmapped CopyBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
current_vaddr, src_addr, size);
ZeroBlock(process, dest_addr, copy_amount);
break;
}
case PageType::Memory: {
case Common::PageType::Memory: {
DEBUG_ASSERT(page_table.pointers[page_index]);
const u8* src_ptr = page_table.pointers[page_index] + page_offset;
WriteBlock(process, dest_addr, src_ptr, copy_amount);
break;
}
case PageType::RasterizerCachedMemory: {
RasterizerFlushVirtualRegion(current_vaddr, static_cast<u32>(copy_amount),
FlushMode::Flush);
WriteBlock(process, dest_addr, GetPointerFromVMA(process, current_vaddr), copy_amount);
case Common::PageType::RasterizerCachedMemory: {
const auto& host_ptr{GetPointerFromVMA(process, current_vaddr)};
Core::System::GetInstance().GPU().FlushRegion(ToCacheAddr(host_ptr), copy_amount);
WriteBlock(process, dest_addr, host_ptr, copy_amount);
break;
}
default:

View File

@@ -6,11 +6,11 @@
#include <cstddef>
#include <string>
#include <tuple>
#include <vector>
#include <boost/icl/interval_map.hpp>
#include "common/common_types.h"
#include "core/memory_hook.h"
namespace Common {
struct PageTable;
}
namespace Kernel {
class Process;
@@ -26,71 +26,6 @@ constexpr std::size_t PAGE_BITS = 12;
constexpr u64 PAGE_SIZE = 1ULL << PAGE_BITS;
constexpr u64 PAGE_MASK = PAGE_SIZE - 1;
enum class PageType : u8 {
/// Page is unmapped and should cause an access error.
Unmapped,
/// Page is mapped to regular memory. This is the only type you can get pointers to.
Memory,
/// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and
/// invalidation
RasterizerCachedMemory,
/// Page is mapped to a I/O region. Writing and reading to this page is handled by functions.
Special,
};
struct SpecialRegion {
enum class Type {
DebugHook,
IODevice,
} type;
MemoryHookPointer handler;
bool operator<(const SpecialRegion& other) const {
return std::tie(type, handler) < std::tie(other.type, other.handler);
}
bool operator==(const SpecialRegion& other) const {
return std::tie(type, handler) == std::tie(other.type, other.handler);
}
};
/**
* A (reasonably) fast way of allowing switchable and remappable process address spaces. It loosely
* mimics the way a real CPU page table works.
*/
struct PageTable {
explicit PageTable();
explicit PageTable(std::size_t address_space_width_in_bits);
~PageTable();
/**
* Resizes the page table to be able to accomodate enough pages within
* a given address space.
*
* @param address_space_width_in_bits The address size width in bits.
*/
void Resize(std::size_t address_space_width_in_bits);
/**
* Vector of memory pointers backing each page. An entry can only be non-null if the
* corresponding entry in the `attributes` vector is of type `Memory`.
*/
std::vector<u8*> pointers;
/**
* Contains MMIO handlers that back memory regions whose entries in the `attribute` vector is
* of type `Special`.
*/
boost::icl::interval_map<VAddr, std::set<SpecialRegion>> special_regions;
/**
* Vector of fine grained page attributes. If it is set to any value other than `Memory`, then
* the corresponding entry in `pointers` MUST be set to null.
*/
std::vector<PageType> attributes;
};
/// Virtual user-space memory regions
enum : VAddr {
/// Read-only page containing kernel and system configuration values.
@@ -116,8 +51,8 @@ enum : VAddr {
};
/// Currently active page table
void SetCurrentPageTable(PageTable* page_table);
PageTable* GetCurrentPageTable();
void SetCurrentPageTable(Common::PageTable* page_table);
Common::PageTable* GetCurrentPageTable();
/// Determines if the given VAddr is valid for the specified process.
bool IsValidVirtualAddress(const Kernel::Process& process, VAddr vaddr);
@@ -161,10 +96,4 @@ enum class FlushMode {
*/
void RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached);
/**
* Flushes and invalidates any externally cached rasterizer resources touching the given virtual
* address region.
*/
void RasterizerFlushVirtualRegion(VAddr start, u64 size, FlushMode mode);
} // namespace Memory

View File

@@ -5,7 +5,11 @@
#pragma once
#include "common/common_types.h"
#include "core/memory_hook.h"
#include "common/memory_hook.h"
namespace Common {
struct PageTable;
}
namespace Memory {
@@ -17,7 +21,7 @@ namespace Memory {
* @param size The amount of bytes to map. Must be page-aligned.
* @param target Buffer with the memory backing the mapping. Must be of length at least `size`.
*/
void MapMemoryRegion(PageTable& page_table, VAddr base, u64 size, u8* target);
void MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, u8* target);
/**
* Maps a region of the emulated process address space as a IO region.
@@ -26,11 +30,14 @@ void MapMemoryRegion(PageTable& page_table, VAddr base, u64 size, u8* target);
* @param size The amount of bytes to map. Must be page-aligned.
* @param mmio_handler The handler that backs the mapping.
*/
void MapIoRegion(PageTable& page_table, VAddr base, u64 size, MemoryHookPointer mmio_handler);
void MapIoRegion(Common::PageTable& page_table, VAddr base, u64 size,
Common::MemoryHookPointer mmio_handler);
void UnmapRegion(PageTable& page_table, VAddr base, u64 size);
void UnmapRegion(Common::PageTable& page_table, VAddr base, u64 size);
void AddDebugHook(PageTable& page_table, VAddr base, u64 size, MemoryHookPointer hook);
void RemoveDebugHook(PageTable& page_table, VAddr base, u64 size, MemoryHookPointer hook);
void AddDebugHook(Common::PageTable& page_table, VAddr base, u64 size,
Common::MemoryHookPointer hook);
void RemoveDebugHook(Common::PageTable& page_table, VAddr base, u64 size,
Common::MemoryHookPointer hook);
} // namespace Memory

View File

@@ -24,17 +24,19 @@ namespace InputCommon::SDL {
class State {
public:
/// Unresisters SDL device factories and shut them down.
using Pollers = std::vector<std::unique_ptr<Polling::DevicePoller>>;
/// Unregisters SDL device factories and shut them down.
virtual ~State() = default;
virtual std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> GetPollers(
InputCommon::Polling::DeviceType type) = 0;
virtual Pollers GetPollers(Polling::DeviceType type) = 0;
};
class NullState : public State {
public:
std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> GetPollers(
InputCommon::Polling::DeviceType type) override {}
Pollers GetPollers(Polling::DeviceType type) override {
return {};
}
};
std::unique_ptr<State> Init();

View File

@@ -475,12 +475,11 @@ SDLState::SDLState() {
initialized = true;
if (start_thread) {
poll_thread = std::thread([&] {
poll_thread = std::thread([this] {
using namespace std::chrono_literals;
SDL_Event event;
while (initialized) {
SDL_PumpEvents();
std::this_thread::sleep_for(std::chrono::duration(10ms));
std::this_thread::sleep_for(10ms);
}
});
}
@@ -651,9 +650,9 @@ private:
};
} // namespace Polling
std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> SDLState::GetPollers(
InputCommon::Polling::DeviceType type) {
std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> pollers;
SDLState::Pollers SDLState::GetPollers(InputCommon::Polling::DeviceType type) {
Pollers pollers;
switch (type) {
case InputCommon::Polling::DeviceType::Analog:
pollers.emplace_back(std::make_unique<Polling::SDLAnalogPoller>(*this));
@@ -661,8 +660,9 @@ std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> SDLState::GetPo
case InputCommon::Polling::DeviceType::Button:
pollers.emplace_back(std::make_unique<Polling::SDLButtonPoller>(*this));
break;
return pollers;
}
return pollers;
}
} // namespace SDL

View File

@@ -25,7 +25,7 @@ public:
/// Initializes and registers SDL device factories
SDLState();
/// Unresisters SDL device factories and shut them down.
/// Unregisters SDL device factories and shut them down.
~SDLState() override;
/// Handle SDL_Events for joysticks from SDL_PollEvent
@@ -35,8 +35,7 @@ public:
std::shared_ptr<SDLJoystick> GetSDLJoystickByGUID(const std::string& guid, int port);
/// Get all DevicePoller that use the SDL backend for a specific device type
std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> GetPollers(
InputCommon::Polling::DeviceType type) override;
Pollers GetPollers(Polling::DeviceType type) override;
/// Used by the Pollers during config
std::atomic<bool> polling = false;

View File

@@ -1,4 +1,5 @@
add_executable(tests
common/bit_field.cpp
common/param_package.cpp
common/ring_buffer.cpp
core/arm/arm_test_common.cpp

View File

@@ -0,0 +1,90 @@
// Copyright 2019 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <array>
#include <cstring>
#include <type_traits>
#include <catch2/catch.hpp>
#include "common/bit_field.h"
TEST_CASE("BitField", "[common]") {
enum class TestEnum : u32 {
A = 0b10111101,
B = 0b10101110,
C = 0b00001111,
};
union LEBitField {
u32_le raw;
BitField<0, 6, u32> a;
BitField<6, 4, s32> b;
BitField<10, 8, TestEnum> c;
BitField<18, 14, u32> d;
} le_bitfield;
union BEBitField {
u32_be raw;
BitFieldBE<0, 6, u32> a;
BitFieldBE<6, 4, s32> b;
BitFieldBE<10, 8, TestEnum> c;
BitFieldBE<18, 14, u32> d;
} be_bitfield;
static_assert(sizeof(LEBitField) == sizeof(u32));
static_assert(sizeof(BEBitField) == sizeof(u32));
static_assert(std::is_trivially_copyable_v<LEBitField>);
static_assert(std::is_trivially_copyable_v<BEBitField>);
std::array<u8, 4> raw{{
0b01101100,
0b11110110,
0b10111010,
0b11101100,
}};
std::memcpy(&le_bitfield, &raw, sizeof(raw));
std::memcpy(&be_bitfield, &raw, sizeof(raw));
// bit fields: 11101100101110'10111101'1001'101100
REQUIRE(le_bitfield.raw == 0b11101100'10111010'11110110'01101100);
REQUIRE(le_bitfield.a == 0b101100);
REQUIRE(le_bitfield.b == -7); // 1001 as two's complement
REQUIRE(le_bitfield.c == TestEnum::A);
REQUIRE(le_bitfield.d == 0b11101100101110);
le_bitfield.a.Assign(0b000111);
le_bitfield.b.Assign(-1);
le_bitfield.c.Assign(TestEnum::C);
le_bitfield.d.Assign(0b01010101010101);
std::memcpy(&raw, &le_bitfield, sizeof(raw));
// bit fields: 01010101010101'00001111'1111'000111
REQUIRE(le_bitfield.raw == 0b01010101'01010100'00111111'11000111);
REQUIRE(raw == std::array<u8, 4>{{
0b11000111,
0b00111111,
0b01010100,
0b01010101,
}});
// bit fields: 01101100111101'10101110'1011'101100
REQUIRE(be_bitfield.raw == 0b01101100'11110110'10111010'11101100);
REQUIRE(be_bitfield.a == 0b101100);
REQUIRE(be_bitfield.b == -5); // 1011 as two's complement
REQUIRE(be_bitfield.c == TestEnum::B);
REQUIRE(be_bitfield.d == 0b01101100111101);
be_bitfield.a.Assign(0b000111);
be_bitfield.b.Assign(-1);
be_bitfield.c.Assign(TestEnum::C);
be_bitfield.d.Assign(0b01010101010101);
std::memcpy(&raw, &be_bitfield, sizeof(raw));
// bit fields: 01010101010101'00001111'1111'000111
REQUIRE(be_bitfield.raw == 0b01010101'01010100'00111111'11000111);
REQUIRE(raw == std::array<u8, 4>{{
0b01010101,
0b01010100,
0b00111111,
0b11000111,
}});
}

View File

@@ -4,6 +4,7 @@
#include <algorithm>
#include "common/page_table.h"
#include "core/core.h"
#include "core/hle/kernel/process.h"
#include "core/memory.h"
@@ -22,7 +23,7 @@ TestEnvironment::TestEnvironment(bool mutable_memory_)
std::fill(page_table->pointers.begin(), page_table->pointers.end(), nullptr);
page_table->special_regions.clear();
std::fill(page_table->attributes.begin(), page_table->attributes.end(),
Memory::PageType::Unmapped);
Common::PageType::Unmapped);
Memory::MapIoRegion(*page_table, 0x00000000, 0x80000000, test_memory);
Memory::MapIoRegion(*page_table, 0x80000000, 0x80000000, test_memory);

View File

@@ -9,10 +9,10 @@
#include <vector>
#include "common/common_types.h"
#include "common/memory_hook.h"
#include "core/hle/kernel/kernel.h"
#include "core/memory_hook.h"
namespace Memory {
namespace Common {
struct PageTable;
}
@@ -58,7 +58,7 @@ public:
private:
friend struct TestMemory;
struct TestMemory final : Memory::MemoryHook {
struct TestMemory final : Common::MemoryHook {
explicit TestMemory(TestEnvironment* env_) : env(env_) {}
TestEnvironment* env;
@@ -86,7 +86,7 @@ private:
bool mutable_memory;
std::shared_ptr<TestMemory> test_memory;
std::vector<WriteRecord> write_records;
Memory::PageTable* page_table = nullptr;
Common::PageTable* page_table = nullptr;
Kernel::KernelCore kernel;
};

View File

@@ -55,12 +55,9 @@ bool DmaPusher::Step() {
}
// Push buffer non-empty, read a word
const auto address = gpu.MemoryManager().GpuToCpuAddress(dma_get);
ASSERT_MSG(address, "Invalid GPU address");
command_headers.resize(command_list_header.size);
Memory::ReadBlock(*address, command_headers.data(), command_list_header.size * sizeof(u32));
gpu.MemoryManager().ReadBlock(dma_get, command_headers.data(),
command_list_header.size * sizeof(u32));
for (const CommandHeader& command_header : command_headers) {

View File

@@ -41,18 +41,13 @@ void KeplerMemory::ProcessData(u32 data) {
ASSERT_MSG(regs.exec.linear, "Non-linear uploads are not supported");
ASSERT(regs.dest.x == 0 && regs.dest.y == 0 && regs.dest.z == 0);
const GPUVAddr address = regs.dest.Address();
const auto dest_address =
memory_manager.GpuToCpuAddress(address + state.write_offset * sizeof(u32));
ASSERT_MSG(dest_address, "Invalid GPU address");
// We have to invalidate the destination region to evict any outdated surfaces from the cache.
// We do this before actually writing the new data because the destination address might contain
// a dirty surface that will have to be written back to memory.
system.Renderer().Rasterizer().InvalidateRegion(ToCacheAddr(Memory::GetPointer(*dest_address)),
sizeof(u32));
// We do this before actually writing the new data because the destination address might
// contain a dirty surface that will have to be written back to memory.
const GPUVAddr address{regs.dest.Address() + state.write_offset * sizeof(u32)};
rasterizer.InvalidateRegion(ToCacheAddr(memory_manager.GetPointer(address)), sizeof(u32));
memory_manager.Write32(address, data);
Memory::Write32(*dest_address, data);
system.GPU().Maxwell3D().dirty_flags.OnMemoryWrite();
state.write_offset++;

View File

@@ -270,11 +270,9 @@ void Maxwell3D::ProcessMacroBind(u32 data) {
}
void Maxwell3D::ProcessQueryGet() {
GPUVAddr sequence_address = regs.query.QueryAddress();
const GPUVAddr sequence_address{regs.query.QueryAddress()};
// Since the sequence address is given as a GPU VAddr, we have to convert it to an application
// VAddr before writing.
const auto address = memory_manager.GpuToCpuAddress(sequence_address);
ASSERT_MSG(address, "Invalid GPU address");
// TODO(Subv): Support the other query units.
ASSERT_MSG(regs.query.query_get.unit == Regs::QueryUnit::Crop,
@@ -309,7 +307,7 @@ void Maxwell3D::ProcessQueryGet() {
// Write the current query sequence to the sequence address.
// TODO(Subv): Find out what happens if you use a long query type but mark it as a short
// query.
Memory::Write32(*address, sequence);
memory_manager.Write32(sequence_address, sequence);
} else {
// Write the 128-bit result structure in long mode. Note: We emulate an infinitely fast
// GPU, this command may actually take a while to complete in real hardware due to GPU
@@ -318,7 +316,7 @@ void Maxwell3D::ProcessQueryGet() {
query_result.value = result;
// TODO(Subv): Generate a real GPU timestamp and write it here instead of CoreTiming
query_result.timestamp = system.CoreTiming().GetTicks();
Memory::WriteBlock(*address, &query_result, sizeof(query_result));
memory_manager.WriteBlock(sequence_address, &query_result, sizeof(query_result));
}
dirty_flags.OnMemoryWrite();
break;
@@ -393,12 +391,11 @@ void Maxwell3D::ProcessCBData(u32 value) {
// Don't allow writing past the end of the buffer.
ASSERT(regs.const_buffer.cb_pos + sizeof(u32) <= regs.const_buffer.cb_size);
const auto address = memory_manager.GpuToCpuAddress(buffer_address + regs.const_buffer.cb_pos);
ASSERT_MSG(address, "Invalid GPU address");
const GPUVAddr address{buffer_address + regs.const_buffer.cb_pos};
u8* ptr{Memory::GetPointer(*address)};
u8* ptr{memory_manager.GetPointer(address)};
rasterizer.InvalidateRegion(ToCacheAddr(ptr), sizeof(u32));
std::memcpy(ptr, &value, sizeof(u32));
memory_manager.Write32(address, value);
dirty_flags.OnMemoryWrite();
@@ -407,14 +404,10 @@ void Maxwell3D::ProcessCBData(u32 value) {
}
Texture::TICEntry Maxwell3D::GetTICEntry(u32 tic_index) const {
const GPUVAddr tic_base_address = regs.tic.TICAddress();
const GPUVAddr tic_address_gpu = tic_base_address + tic_index * sizeof(Texture::TICEntry);
const auto tic_address_cpu = memory_manager.GpuToCpuAddress(tic_address_gpu);
ASSERT_MSG(tic_address_cpu, "Invalid GPU address");
const GPUVAddr tic_address_gpu{regs.tic.TICAddress() + tic_index * sizeof(Texture::TICEntry)};
Texture::TICEntry tic_entry;
Memory::ReadBlock(*tic_address_cpu, &tic_entry, sizeof(Texture::TICEntry));
memory_manager.ReadBlock(tic_address_gpu, &tic_entry, sizeof(Texture::TICEntry));
ASSERT_MSG(tic_entry.header_version == Texture::TICHeaderVersion::BlockLinear ||
tic_entry.header_version == Texture::TICHeaderVersion::Pitch,
@@ -432,14 +425,10 @@ Texture::TICEntry Maxwell3D::GetTICEntry(u32 tic_index) const {
}
Texture::TSCEntry Maxwell3D::GetTSCEntry(u32 tsc_index) const {
const GPUVAddr tsc_base_address = regs.tsc.TSCAddress();
const GPUVAddr tsc_address_gpu = tsc_base_address + tsc_index * sizeof(Texture::TSCEntry);
const auto tsc_address_cpu = memory_manager.GpuToCpuAddress(tsc_address_gpu);
ASSERT_MSG(tsc_address_cpu, "Invalid GPU address");
const GPUVAddr tsc_address_gpu{regs.tsc.TSCAddress() + tsc_index * sizeof(Texture::TSCEntry)};
Texture::TSCEntry tsc_entry;
Memory::ReadBlock(*tsc_address_cpu, &tsc_entry, sizeof(Texture::TSCEntry));
memory_manager.ReadBlock(tsc_address_gpu, &tsc_entry, sizeof(Texture::TSCEntry));
return tsc_entry;
}
@@ -458,10 +447,7 @@ std::vector<Texture::FullTextureInfo> Maxwell3D::GetStageTextures(Regs::ShaderSt
for (GPUVAddr current_texture = tex_info_buffer.address + TextureInfoOffset;
current_texture < tex_info_buffer_end; current_texture += sizeof(Texture::TextureHandle)) {
const auto address = memory_manager.GpuToCpuAddress(current_texture);
ASSERT_MSG(address, "Invalid GPU address");
const Texture::TextureHandle tex_handle{Memory::Read32(*address)};
const Texture::TextureHandle tex_handle{memory_manager.Read32(current_texture)};
Texture::FullTextureInfo tex_info{};
// TODO(Subv): Use the shader to determine which textures are actually accessed.
@@ -496,10 +482,7 @@ Texture::FullTextureInfo Maxwell3D::GetStageTexture(Regs::ShaderStage stage,
ASSERT(tex_info_address < tex_info_buffer.address + tex_info_buffer.size);
const auto tex_address_cpu = memory_manager.GpuToCpuAddress(tex_info_address);
ASSERT_MSG(tex_address_cpu, "Invalid GPU address");
const Texture::TextureHandle tex_handle{Memory::Read32(*tex_address_cpu)};
const Texture::TextureHandle tex_handle{memory_manager.Read32(tex_info_address)};
Texture::FullTextureInfo tex_info{};
tex_info.index = static_cast<u32>(offset);

View File

@@ -43,11 +43,6 @@ void MaxwellDMA::HandleCopy() {
const GPUVAddr source = regs.src_address.Address();
const GPUVAddr dest = regs.dst_address.Address();
const auto source_cpu = memory_manager.GpuToCpuAddress(source);
const auto dest_cpu = memory_manager.GpuToCpuAddress(dest);
ASSERT_MSG(source_cpu, "Invalid source GPU address");
ASSERT_MSG(dest_cpu, "Invalid destination GPU address");
// TODO(Subv): Perform more research and implement all features of this engine.
ASSERT(regs.exec.enable_swizzle == 0);
ASSERT(regs.exec.query_mode == Regs::QueryMode::None);
@@ -70,7 +65,7 @@ void MaxwellDMA::HandleCopy() {
// buffer of length `x_count`, otherwise we copy a 2D image of dimensions (x_count,
// y_count).
if (!regs.exec.enable_2d) {
Memory::CopyBlock(*dest_cpu, *source_cpu, regs.x_count);
memory_manager.CopyBlock(dest, source, regs.x_count);
return;
}
@@ -79,9 +74,9 @@ void MaxwellDMA::HandleCopy() {
// rectangle. There is no need to manually flush/invalidate the regions because
// CopyBlock does that for us.
for (u32 line = 0; line < regs.y_count; ++line) {
const VAddr source_line = *source_cpu + line * regs.src_pitch;
const VAddr dest_line = *dest_cpu + line * regs.dst_pitch;
Memory::CopyBlock(dest_line, source_line, regs.x_count);
const GPUVAddr source_line = source + line * regs.src_pitch;
const GPUVAddr dest_line = dest + line * regs.dst_pitch;
memory_manager.CopyBlock(dest_line, source_line, regs.x_count);
}
return;
}
@@ -90,17 +85,18 @@ void MaxwellDMA::HandleCopy() {
const std::size_t copy_size = regs.x_count * regs.y_count;
auto source_ptr{memory_manager.GetPointer(source)};
auto dst_ptr{memory_manager.GetPointer(dest)};
const auto FlushAndInvalidate = [&](u32 src_size, u64 dst_size) {
// TODO(Subv): For now, manually flush the regions until we implement GPU-accelerated
// copying.
Core::System::GetInstance().Renderer().Rasterizer().FlushRegion(
ToCacheAddr(Memory::GetPointer(*source_cpu)), src_size);
rasterizer.FlushRegion(ToCacheAddr(source_ptr), src_size);
// We have to invalidate the destination region to evict any outdated surfaces from the
// cache. We do this before actually writing the new data because the destination address
// might contain a dirty surface that will have to be written back to memory.
Core::System::GetInstance().Renderer().Rasterizer().InvalidateRegion(
ToCacheAddr(Memory::GetPointer(*dest_cpu)), dst_size);
rasterizer.InvalidateRegion(ToCacheAddr(dst_ptr), dst_size);
};
if (regs.exec.is_dst_linear && !regs.exec.is_src_linear) {
@@ -113,8 +109,8 @@ void MaxwellDMA::HandleCopy() {
copy_size * src_bytes_per_pixel);
Texture::UnswizzleSubrect(regs.x_count, regs.y_count, regs.dst_pitch,
regs.src_params.size_x, src_bytes_per_pixel, *source_cpu,
*dest_cpu, regs.src_params.BlockHeight(), regs.src_params.pos_x,
regs.src_params.size_x, src_bytes_per_pixel, source_ptr, dst_ptr,
regs.src_params.BlockHeight(), regs.src_params.pos_x,
regs.src_params.pos_y);
} else {
ASSERT(regs.dst_params.size_z == 1);
@@ -127,7 +123,7 @@ void MaxwellDMA::HandleCopy() {
// If the input is linear and the output is tiled, swizzle the input and copy it over.
Texture::SwizzleSubrect(regs.x_count, regs.y_count, regs.src_pitch, regs.dst_params.size_x,
src_bpp, *dest_cpu, *source_cpu, regs.dst_params.BlockHeight());
src_bpp, dst_ptr, source_ptr, regs.dst_params.BlockHeight());
}
}

View File

@@ -274,7 +274,6 @@ void GPU::ProcessSemaphoreTriggerMethod() {
const auto op =
static_cast<GpuSemaphoreOperation>(regs.semaphore_trigger & semaphoreOperationMask);
if (op == GpuSemaphoreOperation::WriteLong) {
auto address = memory_manager->GpuToCpuAddress(regs.smaphore_address.SmaphoreAddress());
struct Block {
u32 sequence;
u32 zeros = 0;
@@ -286,11 +285,9 @@ void GPU::ProcessSemaphoreTriggerMethod() {
// TODO(Kmather73): Generate a real GPU timestamp and write it here instead of
// CoreTiming
block.timestamp = Core::System::GetInstance().CoreTiming().GetTicks();
Memory::WriteBlock(*address, &block, sizeof(block));
memory_manager->WriteBlock(regs.smaphore_address.SmaphoreAddress(), &block, sizeof(block));
} else {
const auto address =
memory_manager->GpuToCpuAddress(regs.smaphore_address.SmaphoreAddress());
const u32 word = Memory::Read32(*address);
const u32 word{memory_manager->Read32(regs.smaphore_address.SmaphoreAddress())};
if ((op == GpuSemaphoreOperation::AcquireEqual && word == regs.semaphore_sequence) ||
(op == GpuSemaphoreOperation::AcquireGequal &&
static_cast<s32>(word - regs.semaphore_sequence) > 0) ||
@@ -317,13 +314,11 @@ void GPU::ProcessSemaphoreTriggerMethod() {
}
void GPU::ProcessSemaphoreRelease() {
const auto address = memory_manager->GpuToCpuAddress(regs.smaphore_address.SmaphoreAddress());
Memory::Write32(*address, regs.semaphore_release);
memory_manager->Write32(regs.smaphore_address.SmaphoreAddress(), regs.semaphore_release);
}
void GPU::ProcessSemaphoreAcquire() {
const auto address = memory_manager->GpuToCpuAddress(regs.smaphore_address.SmaphoreAddress());
const u32 word = Memory::Read32(*address);
const u32 word = memory_manager->Read32(regs.smaphore_address.SmaphoreAddress());
const auto value = regs.semaphore_acquire;
if (word != value) {
regs.acquire_active = true;

View File

@@ -5,6 +5,7 @@
#include "common/alignment.h"
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/memory.h"
#include "video_core/memory_manager.h"
namespace Tegra {
@@ -162,15 +163,51 @@ std::optional<VAddr> MemoryManager::GpuToCpuAddress(GPUVAddr gpu_addr) {
return base_addr + (gpu_addr & PAGE_MASK);
}
std::vector<GPUVAddr> MemoryManager::CpuToGpuAddress(VAddr cpu_addr) const {
std::vector<GPUVAddr> results;
for (const auto& region : mapped_regions) {
if (cpu_addr >= region.cpu_addr && cpu_addr < (region.cpu_addr + region.size)) {
const u64 offset{cpu_addr - region.cpu_addr};
results.push_back(region.gpu_addr + offset);
}
}
return results;
u8 MemoryManager::Read8(GPUVAddr addr) {
return Memory::Read8(*GpuToCpuAddress(addr));
}
u16 MemoryManager::Read16(GPUVAddr addr) {
return Memory::Read16(*GpuToCpuAddress(addr));
}
u32 MemoryManager::Read32(GPUVAddr addr) {
return Memory::Read32(*GpuToCpuAddress(addr));
}
u64 MemoryManager::Read64(GPUVAddr addr) {
return Memory::Read64(*GpuToCpuAddress(addr));
}
void MemoryManager::Write8(GPUVAddr addr, u8 data) {
Memory::Write8(*GpuToCpuAddress(addr), data);
}
void MemoryManager::Write16(GPUVAddr addr, u16 data) {
Memory::Write16(*GpuToCpuAddress(addr), data);
}
void MemoryManager::Write32(GPUVAddr addr, u32 data) {
Memory::Write32(*GpuToCpuAddress(addr), data);
}
void MemoryManager::Write64(GPUVAddr addr, u64 data) {
Memory::Write64(*GpuToCpuAddress(addr), data);
}
u8* MemoryManager::GetPointer(GPUVAddr addr) {
return Memory::GetPointer(*GpuToCpuAddress(addr));
}
void MemoryManager::ReadBlock(GPUVAddr src_addr, void* dest_buffer, std::size_t size) {
std::memcpy(dest_buffer, GetPointer(src_addr), size);
}
void MemoryManager::WriteBlock(GPUVAddr dest_addr, const void* src_buffer, std::size_t size) {
std::memcpy(GetPointer(dest_addr), src_buffer, size);
}
void MemoryManager::CopyBlock(GPUVAddr dest_addr, GPUVAddr src_addr, std::size_t size) {
std::memcpy(GetPointer(dest_addr), GetPointer(src_addr), size);
}
VAddr& MemoryManager::PageSlot(GPUVAddr gpu_addr) {

View File

@@ -27,12 +27,27 @@ public:
GPUVAddr UnmapBuffer(GPUVAddr gpu_addr, u64 size);
GPUVAddr GetRegionEnd(GPUVAddr region_start) const;
std::optional<VAddr> GpuToCpuAddress(GPUVAddr gpu_addr);
std::vector<GPUVAddr> CpuToGpuAddress(VAddr cpu_addr) const;
static constexpr u64 PAGE_BITS = 16;
static constexpr u64 PAGE_SIZE = 1 << PAGE_BITS;
static constexpr u64 PAGE_MASK = PAGE_SIZE - 1;
u8 Read8(GPUVAddr addr);
u16 Read16(GPUVAddr addr);
u32 Read32(GPUVAddr addr);
u64 Read64(GPUVAddr addr);
void Write8(GPUVAddr addr, u8 data);
void Write16(GPUVAddr addr, u16 data);
void Write32(GPUVAddr addr, u32 data);
void Write64(GPUVAddr addr, u64 data);
u8* GetPointer(GPUVAddr vaddr);
void ReadBlock(GPUVAddr src_addr, void* dest_buffer, std::size_t size);
void WriteBlock(GPUVAddr dest_addr, const void* src_buffer, std::size_t size);
void CopyBlock(VAddr dest_addr, VAddr src_addr, std::size_t size);
private:
enum class PageStatus : u64 {
Unmapped = 0xFFFFFFFFFFFFFFFFULL,

View File

@@ -6,7 +6,6 @@
#include <cstring>
#include "common/assert.h"
#include "common/common_types.h"
#include "core/memory.h"
#include "video_core/morton.h"
#include "video_core/surface.h"
#include "video_core/textures/decoders.h"
@@ -16,12 +15,12 @@ namespace VideoCore {
using Surface::GetBytesPerPixel;
using Surface::PixelFormat;
using MortonCopyFn = void (*)(u32, u32, u32, u32, u32, u32, u8*, VAddr);
using MortonCopyFn = void (*)(u32, u32, u32, u32, u32, u32, u8*, u8*);
using ConversionArray = std::array<MortonCopyFn, Surface::MaxPixelFormat>;
template <bool morton_to_linear, PixelFormat format>
static void MortonCopy(u32 stride, u32 block_height, u32 height, u32 block_depth, u32 depth,
u32 tile_width_spacing, u8* buffer, VAddr addr) {
u32 tile_width_spacing, u8* buffer, u8* addr) {
constexpr u32 bytes_per_pixel = GetBytesPerPixel(format);
// With the BCn formats (DXT and DXN), each 4x4 tile is swizzled instead of just individual
@@ -34,10 +33,10 @@ static void MortonCopy(u32 stride, u32 block_height, u32 height, u32 block_depth
stride, height, depth, block_height, block_depth,
tile_width_spacing);
} else {
Tegra::Texture::CopySwizzledData(
(stride + tile_size_x - 1) / tile_size_x, (height + tile_size_y - 1) / tile_size_y,
depth, bytes_per_pixel, bytes_per_pixel, Memory::GetPointer(addr), buffer, false,
block_height, block_depth, tile_width_spacing);
Tegra::Texture::CopySwizzledData((stride + tile_size_x - 1) / tile_size_x,
(height + tile_size_y - 1) / tile_size_y, depth,
bytes_per_pixel, bytes_per_pixel, addr, buffer, false,
block_height, block_depth, tile_width_spacing);
}
}
@@ -282,7 +281,7 @@ static u32 GetMortonOffset128(u32 x, u32 y, u32 bytes_per_pixel) {
void MortonSwizzle(MortonSwizzleMode mode, Surface::PixelFormat format, u32 stride,
u32 block_height, u32 height, u32 block_depth, u32 depth, u32 tile_width_spacing,
u8* buffer, VAddr addr) {
u8* buffer, u8* addr) {
GetSwizzleFunction(mode, format)(stride, block_height, height, block_depth, depth,
tile_width_spacing, buffer, addr);
}

View File

@@ -13,7 +13,7 @@ enum class MortonSwizzleMode { MortonToLinear, LinearToMorton };
void MortonSwizzle(MortonSwizzleMode mode, VideoCore::Surface::PixelFormat format, u32 stride,
u32 block_height, u32 height, u32 block_depth, u32 depth, u32 tile_width_spacing,
u8* buffer, VAddr addr);
u8* buffer, u8* addr);
void MortonCopyPixels128(MortonSwizzleMode mode, u32 width, u32 height, u32 bytes_per_pixel,
u32 linear_bytes_per_pixel, u8* morton_data, u8* linear_data);

View File

@@ -24,14 +24,12 @@ OGLBufferCache::OGLBufferCache(RasterizerOpenGL& rasterizer, std::size_t size)
GLintptr OGLBufferCache::UploadMemory(Tegra::GPUVAddr gpu_addr, std::size_t size,
std::size_t alignment, bool cache) {
auto& memory_manager = Core::System::GetInstance().GPU().MemoryManager();
const auto cpu_addr{memory_manager.GpuToCpuAddress(gpu_addr)};
ASSERT_MSG(cpu_addr, "Invalid GPU address");
// Cache management is a big overhead, so only cache entries with a given size.
// TODO: Figure out which size is the best for given games.
cache &= size >= 2048;
const auto& host_ptr{Memory::GetPointer(*cpu_addr)};
const auto& host_ptr{memory_manager.GetPointer(gpu_addr)};
if (cache) {
auto entry = TryGet(host_ptr);
if (entry) {
@@ -54,8 +52,8 @@ GLintptr OGLBufferCache::UploadMemory(Tegra::GPUVAddr gpu_addr, std::size_t size
buffer_offset += size;
if (cache) {
auto entry = std::make_shared<CachedBufferEntry>(*cpu_addr, size, uploaded_offset,
alignment, host_ptr);
auto entry = std::make_shared<CachedBufferEntry>(
*memory_manager.GpuToCpuAddress(gpu_addr), size, uploaded_offset, alignment, host_ptr);
Register(entry);
}

View File

@@ -7,7 +7,6 @@
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/core.h"
#include "core/memory.h"
#include "video_core/renderer_opengl/gl_global_cache.h"
#include "video_core/renderer_opengl/gl_rasterizer.h"
#include "video_core/renderer_opengl/gl_shader_decompiler.h"
@@ -39,7 +38,7 @@ void CachedGlobalRegion::Reload(u32 size_) {
glBufferData(GL_SHADER_STORAGE_BUFFER, size, GetHostPtr(), GL_DYNAMIC_DRAW);
}
GlobalRegion GlobalRegionCacheOpenGL::TryGetReservedGlobalRegion(VAddr addr, u32 size) const {
GlobalRegion GlobalRegionCacheOpenGL::TryGetReservedGlobalRegion(CacheAddr addr, u32 size) const {
const auto search{reserve.find(addr)};
if (search == reserve.end()) {
return {};
@@ -47,11 +46,14 @@ GlobalRegion GlobalRegionCacheOpenGL::TryGetReservedGlobalRegion(VAddr addr, u32
return search->second;
}
GlobalRegion GlobalRegionCacheOpenGL::GetUncachedGlobalRegion(VAddr addr, u32 size, u8* host_ptr) {
GlobalRegion region{TryGetReservedGlobalRegion(addr, size)};
GlobalRegion GlobalRegionCacheOpenGL::GetUncachedGlobalRegion(Tegra::GPUVAddr addr, u32 size,
u8* host_ptr) {
GlobalRegion region{TryGetReservedGlobalRegion(ToCacheAddr(host_ptr), size)};
if (!region) {
// No reserved surface available, create a new one and reserve it
region = std::make_shared<CachedGlobalRegion>(addr, size, host_ptr);
auto& memory_manager{Core::System::GetInstance().GPU().MemoryManager()};
const auto cpu_addr = *memory_manager.GpuToCpuAddress(addr);
region = std::make_shared<CachedGlobalRegion>(cpu_addr, size, host_ptr);
ReserveGlobalRegion(region);
}
region->Reload(size);
@@ -59,7 +61,7 @@ GlobalRegion GlobalRegionCacheOpenGL::GetUncachedGlobalRegion(VAddr addr, u32 si
}
void GlobalRegionCacheOpenGL::ReserveGlobalRegion(GlobalRegion region) {
reserve.insert_or_assign(region->GetCpuAddr(), std::move(region));
reserve.insert_or_assign(region->GetCacheAddr(), std::move(region));
}
GlobalRegionCacheOpenGL::GlobalRegionCacheOpenGL(RasterizerOpenGL& rasterizer)
@@ -70,23 +72,20 @@ GlobalRegion GlobalRegionCacheOpenGL::GetGlobalRegion(
Tegra::Engines::Maxwell3D::Regs::ShaderStage stage) {
auto& gpu{Core::System::GetInstance().GPU()};
const auto cbufs = gpu.Maxwell3D().state.shader_stages[static_cast<u64>(stage)];
const auto cbuf_addr = gpu.MemoryManager().GpuToCpuAddress(
cbufs.const_buffers[global_region.GetCbufIndex()].address + global_region.GetCbufOffset());
ASSERT(cbuf_addr);
const auto actual_addr_gpu = Memory::Read64(*cbuf_addr);
const auto size = Memory::Read32(*cbuf_addr + 8);
const auto actual_addr = gpu.MemoryManager().GpuToCpuAddress(actual_addr_gpu);
ASSERT(actual_addr);
auto& memory_manager{gpu.MemoryManager()};
const auto cbufs{gpu.Maxwell3D().state.shader_stages[static_cast<u64>(stage)]};
const auto addr{cbufs.const_buffers[global_region.GetCbufIndex()].address +
global_region.GetCbufOffset()};
const auto actual_addr{memory_manager.Read64(addr)};
const auto size{memory_manager.Read32(addr + 8)};
// Look up global region in the cache based on address
const auto& host_ptr{Memory::GetPointer(*actual_addr)};
const auto& host_ptr{memory_manager.GetPointer(actual_addr)};
GlobalRegion region{TryGet(host_ptr)};
if (!region) {
// No global region found - create a new one
region = GetUncachedGlobalRegion(*actual_addr, size, host_ptr);
region = GetUncachedGlobalRegion(actual_addr, size, host_ptr);
Register(region);
}

View File

@@ -65,11 +65,11 @@ public:
Tegra::Engines::Maxwell3D::Regs::ShaderStage stage);
private:
GlobalRegion TryGetReservedGlobalRegion(VAddr addr, u32 size) const;
GlobalRegion GetUncachedGlobalRegion(VAddr addr, u32 size, u8* host_ptr);
GlobalRegion TryGetReservedGlobalRegion(CacheAddr addr, u32 size) const;
GlobalRegion GetUncachedGlobalRegion(Tegra::GPUVAddr addr, u32 size, u8* host_ptr);
void ReserveGlobalRegion(GlobalRegion region);
std::unordered_map<VAddr, GlobalRegion> reserve;
std::unordered_map<CacheAddr, GlobalRegion> reserve;
};
} // namespace OpenGL

View File

@@ -46,10 +46,7 @@ GLintptr PrimitiveAssembler::MakeQuadIndexed(Tegra::GPUVAddr gpu_addr, std::size
auto [dst_pointer, index_offset] = buffer_cache.ReserveMemory(map_size);
auto& memory_manager = Core::System::GetInstance().GPU().MemoryManager();
const auto cpu_addr{memory_manager.GpuToCpuAddress(gpu_addr)};
ASSERT_MSG(cpu_addr, "Invalid GPU address");
const u8* source{Memory::GetPointer(*cpu_addr)};
const u8* source{memory_manager.GetPointer(gpu_addr)};
for (u32 primitive = 0; primitive < count / 4; ++primitive) {
for (std::size_t i = 0; i < TRIANGLES_PER_QUAD; ++i) {
@@ -64,4 +61,4 @@ GLintptr PrimitiveAssembler::MakeQuadIndexed(Tegra::GPUVAddr gpu_addr, std::size
return index_offset;
}
} // namespace OpenGL
} // namespace OpenGL

View File

@@ -749,11 +749,17 @@ void RasterizerOpenGL::FlushAll() {}
void RasterizerOpenGL::FlushRegion(CacheAddr addr, u64 size) {
MICROPROFILE_SCOPE(OpenGL_CacheManagement);
if (!addr || !size) {
return;
}
res_cache.FlushRegion(addr, size);
}
void RasterizerOpenGL::InvalidateRegion(CacheAddr addr, u64 size) {
MICROPROFILE_SCOPE(OpenGL_CacheManagement);
if (!addr || !size) {
return;
}
res_cache.InvalidateRegion(addr, size);
shader_cache.InvalidateRegion(addr, size);
global_cache.InvalidateRegion(addr, size);

View File

@@ -57,11 +57,9 @@ static void ApplyTextureDefaults(GLuint texture, u32 max_mip_level) {
void SurfaceParams::InitCacheParameters(Tegra::GPUVAddr gpu_addr_) {
auto& memory_manager{Core::System::GetInstance().GPU().MemoryManager()};
const auto cpu_addr{memory_manager.GpuToCpuAddress(gpu_addr_)};
addr = cpu_addr ? *cpu_addr : 0;
gpu_addr = gpu_addr_;
host_ptr = Memory::GetPointer(addr);
host_ptr = memory_manager.GetPointer(gpu_addr_);
size_in_bytes = SizeInBytesRaw();
if (IsPixelFormatASTC(pixel_format)) {
@@ -447,7 +445,7 @@ void SwizzleFunc(const MortonSwizzleMode& mode, const SurfaceParams& params,
MortonSwizzle(mode, params.pixel_format, params.MipWidth(mip_level),
params.MipBlockHeight(mip_level), params.MipHeight(mip_level),
params.MipBlockDepth(mip_level), 1, params.tile_width_spacing,
gl_buffer.data() + offset_gl, params.addr + offset);
gl_buffer.data() + offset_gl, params.host_ptr + offset);
offset += layer_size;
offset_gl += gl_size;
}
@@ -456,7 +454,7 @@ void SwizzleFunc(const MortonSwizzleMode& mode, const SurfaceParams& params,
MortonSwizzle(mode, params.pixel_format, params.MipWidth(mip_level),
params.MipBlockHeight(mip_level), params.MipHeight(mip_level),
params.MipBlockDepth(mip_level), depth, params.tile_width_spacing,
gl_buffer.data(), params.addr + offset);
gl_buffer.data(), params.host_ptr + offset);
}
}
@@ -514,9 +512,9 @@ void RasterizerCacheOpenGL::CopySurface(const Surface& src_surface, const Surfac
"reinterpretation but the texture is tiled.");
}
const std::size_t remaining_size = dst_params.size_in_bytes - src_params.size_in_bytes;
auto& memory_manager{Core::System::GetInstance().GPU().MemoryManager()};
glBufferSubData(GL_PIXEL_PACK_BUFFER, src_params.size_in_bytes, remaining_size,
Memory::GetPointer(dst_params.addr + src_params.size_in_bytes));
memory_manager.GetPointer(dst_params.gpu_addr + src_params.size_in_bytes));
}
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
@@ -604,7 +602,7 @@ CachedSurface::CachedSurface(const SurfaceParams& params)
ApplyTextureDefaults(texture.handle, params.max_mip_level);
OpenGL::LabelGLObject(GL_TEXTURE, texture.handle, params.addr, params.IdentityString());
OpenGL::LabelGLObject(GL_TEXTURE, texture.handle, params.gpu_addr, params.IdentityString());
// Clamp size to mapped GPU memory region
// TODO(bunnei): Super Mario Odyssey maps a 0x40000 byte region and then uses it for a 0x80000
@@ -617,6 +615,8 @@ CachedSurface::CachedSurface(const SurfaceParams& params)
LOG_ERROR(HW_GPU, "Surface size {} exceeds region size {}", params.size_in_bytes, max_size);
cached_size_in_bytes = max_size;
}
cpu_addr = *memory_manager.GpuToCpuAddress(params.gpu_addr);
}
MICROPROFILE_DEFINE(OpenGL_SurfaceLoad, "OpenGL", "Surface Load", MP_RGB(128, 192, 64));
@@ -925,7 +925,7 @@ void RasterizerCacheOpenGL::LoadSurface(const Surface& surface) {
}
Surface RasterizerCacheOpenGL::GetSurface(const SurfaceParams& params, bool preserve_contents) {
if (params.addr == 0 || params.height * params.width == 0) {
if (params.gpu_addr == 0 || params.height * params.width == 0) {
return {};
}
@@ -979,14 +979,16 @@ void RasterizerCacheOpenGL::FastLayeredCopySurface(const Surface& src_surface,
const Surface& dst_surface) {
const auto& init_params{src_surface->GetSurfaceParams()};
const auto& dst_params{dst_surface->GetSurfaceParams()};
VAddr address = init_params.addr;
const std::size_t layer_size = dst_params.LayerMemorySize();
auto& memory_manager{Core::System::GetInstance().GPU().MemoryManager()};
Tegra::GPUVAddr address{init_params.gpu_addr};
const std::size_t layer_size{dst_params.LayerMemorySize()};
for (u32 layer = 0; layer < dst_params.depth; layer++) {
for (u32 mipmap = 0; mipmap < dst_params.max_mip_level; mipmap++) {
const VAddr sub_address = address + dst_params.GetMipmapLevelOffset(mipmap);
const Surface& copy = TryGet(Memory::GetPointer(sub_address));
if (!copy)
const Tegra::GPUVAddr sub_address{address + dst_params.GetMipmapLevelOffset(mipmap)};
const Surface& copy{TryGet(memory_manager.GetPointer(sub_address))};
if (!copy) {
continue;
}
const auto& src_params{copy->GetSurfaceParams()};
const u32 width{std::min(src_params.width, dst_params.MipWidth(mipmap))};
const u32 height{std::min(src_params.height, dst_params.MipHeight(mipmap))};
@@ -1242,9 +1244,10 @@ static std::optional<u32> TryFindBestMipMap(std::size_t memory, const SurfacePar
return {};
}
static std::optional<u32> TryFindBestLayer(VAddr addr, const SurfaceParams params, u32 mipmap) {
const std::size_t size = params.LayerMemorySize();
VAddr start = params.addr + params.GetMipmapLevelOffset(mipmap);
static std::optional<u32> TryFindBestLayer(Tegra::GPUVAddr addr, const SurfaceParams params,
u32 mipmap) {
const std::size_t size{params.LayerMemorySize()};
Tegra::GPUVAddr start{params.gpu_addr + params.GetMipmapLevelOffset(mipmap)};
for (u32 i = 0; i < params.depth; i++) {
if (start == addr) {
return {i};
@@ -1266,7 +1269,7 @@ static bool LayerFitReinterpretSurface(RasterizerCacheOpenGL& cache, const Surfa
src_params.height == dst_params.MipHeight(*level) &&
src_params.block_height >= dst_params.MipBlockHeight(*level)) {
const std::optional<u32> slot =
TryFindBestLayer(render_surface->GetCpuAddr(), dst_params, *level);
TryFindBestLayer(render_surface->GetSurfaceParams().gpu_addr, dst_params, *level);
if (slot.has_value()) {
glCopyImageSubData(render_surface->Texture().handle,
SurfaceTargetToGL(src_params.target), 0, 0, 0, 0,

View File

@@ -296,7 +296,6 @@ struct SurfaceParams {
bool is_array;
bool srgb_conversion;
// Parameters used for caching
VAddr addr;
u8* host_ptr;
Tegra::GPUVAddr gpu_addr;
std::size_t size_in_bytes;
@@ -349,7 +348,7 @@ public:
explicit CachedSurface(const SurfaceParams& params);
VAddr GetCpuAddr() const override {
return params.addr;
return cpu_addr;
}
std::size_t GetSizeInBytes() const override {
@@ -433,6 +432,7 @@ private:
std::size_t memory_size;
bool reinterpreted = false;
bool must_reload = false;
VAddr cpu_addr{};
};
class RasterizerCacheOpenGL final : public RasterizerCache<Surface> {

View File

@@ -32,13 +32,10 @@ struct UnspecializedShader {
namespace {
/// Gets the address for the specified shader stage program
VAddr GetShaderAddress(Maxwell::ShaderProgram program) {
const auto& gpu = Core::System::GetInstance().GPU().Maxwell3D();
const auto& shader_config = gpu.regs.shader_config[static_cast<std::size_t>(program)];
const auto address = gpu.memory_manager.GpuToCpuAddress(gpu.regs.code_address.CodeAddress() +
shader_config.offset);
ASSERT_MSG(address, "Invalid GPU address");
return *address;
Tegra::GPUVAddr GetShaderAddress(Maxwell::ShaderProgram program) {
const auto& gpu{Core::System::GetInstance().GPU().Maxwell3D()};
const auto& shader_config{gpu.regs.shader_config[static_cast<std::size_t>(program)]};
return gpu.regs.code_address.CodeAddress() + shader_config.offset;
}
/// Gets the shader program code from memory for the specified address
@@ -214,11 +211,11 @@ std::set<GLenum> GetSupportedFormats() {
} // namespace
CachedShader::CachedShader(VAddr guest_addr, u64 unique_identifier,
CachedShader::CachedShader(VAddr cpu_addr, u64 unique_identifier,
Maxwell::ShaderProgram program_type, ShaderDiskCacheOpenGL& disk_cache,
const PrecompiledPrograms& precompiled_programs,
ProgramCode&& program_code, ProgramCode&& program_code_b, u8* host_ptr)
: host_ptr{host_ptr}, guest_addr{guest_addr}, unique_identifier{unique_identifier},
: host_ptr{host_ptr}, cpu_addr{cpu_addr}, unique_identifier{unique_identifier},
program_type{program_type}, disk_cache{disk_cache},
precompiled_programs{precompiled_programs}, RasterizerCacheObject{host_ptr} {
@@ -244,11 +241,11 @@ CachedShader::CachedShader(VAddr guest_addr, u64 unique_identifier,
disk_cache.SaveRaw(raw);
}
CachedShader::CachedShader(VAddr guest_addr, u64 unique_identifier,
CachedShader::CachedShader(VAddr cpu_addr, u64 unique_identifier,
Maxwell::ShaderProgram program_type, ShaderDiskCacheOpenGL& disk_cache,
const PrecompiledPrograms& precompiled_programs,
GLShader::ProgramResult result, u8* host_ptr)
: guest_addr{guest_addr}, unique_identifier{unique_identifier}, program_type{program_type},
: cpu_addr{cpu_addr}, unique_identifier{unique_identifier}, program_type{program_type},
disk_cache{disk_cache}, precompiled_programs{precompiled_programs}, RasterizerCacheObject{
host_ptr} {
@@ -273,7 +270,7 @@ std::tuple<GLuint, BaseBindings> CachedShader::GetProgramHandle(GLenum primitive
disk_cache.SaveUsage(GetUsage(primitive_mode, base_bindings));
}
LabelGLObject(GL_PROGRAM, program->handle, guest_addr);
LabelGLObject(GL_PROGRAM, program->handle, cpu_addr);
}
handle = program->handle;
@@ -325,7 +322,7 @@ GLuint CachedShader::LazyGeometryProgram(CachedProgram& target_program, BaseBind
disk_cache.SaveUsage(GetUsage(primitive_mode, base_bindings));
}
LabelGLObject(GL_PROGRAM, target_program->handle, guest_addr, debug_name);
LabelGLObject(GL_PROGRAM, target_program->handle, cpu_addr, debug_name);
return target_program->handle;
};
@@ -488,31 +485,31 @@ Shader ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) {
return last_shaders[static_cast<u32>(program)];
}
const VAddr program_addr{GetShaderAddress(program)};
auto& memory_manager{Core::System::GetInstance().GPU().MemoryManager()};
const Tegra::GPUVAddr program_addr{GetShaderAddress(program)};
// Look up shader in the cache based on address
const auto& host_ptr{Memory::GetPointer(program_addr)};
const auto& host_ptr{memory_manager.GetPointer(program_addr)};
Shader shader{TryGet(host_ptr)};
if (!shader) {
// No shader found - create a new one
const auto& host_ptr{Memory::GetPointer(program_addr)};
ProgramCode program_code{GetShaderCode(host_ptr)};
ProgramCode program_code_b;
if (program == Maxwell::ShaderProgram::VertexA) {
program_code_b = GetShaderCode(
Memory::GetPointer(GetShaderAddress(Maxwell::ShaderProgram::VertexB)));
memory_manager.GetPointer(GetShaderAddress(Maxwell::ShaderProgram::VertexB)));
}
const u64 unique_identifier = GetUniqueIdentifier(program, program_code, program_code_b);
const VAddr cpu_addr{*memory_manager.GpuToCpuAddress(program_addr)};
const auto found = precompiled_shaders.find(unique_identifier);
if (found != precompiled_shaders.end()) {
shader =
std::make_shared<CachedShader>(program_addr, unique_identifier, program, disk_cache,
std::make_shared<CachedShader>(cpu_addr, unique_identifier, program, disk_cache,
precompiled_programs, found->second, host_ptr);
} else {
shader = std::make_shared<CachedShader>(
program_addr, unique_identifier, program, disk_cache, precompiled_programs,
cpu_addr, unique_identifier, program, disk_cache, precompiled_programs,
std::move(program_code), std::move(program_code_b), host_ptr);
}
Register(shader);

View File

@@ -39,18 +39,18 @@ using PrecompiledShaders = std::unordered_map<u64, GLShader::ProgramResult>;
class CachedShader final : public RasterizerCacheObject {
public:
explicit CachedShader(VAddr guest_addr, u64 unique_identifier,
explicit CachedShader(VAddr cpu_addr, u64 unique_identifier,
Maxwell::ShaderProgram program_type, ShaderDiskCacheOpenGL& disk_cache,
const PrecompiledPrograms& precompiled_programs,
ProgramCode&& program_code, ProgramCode&& program_code_b, u8* host_ptr);
explicit CachedShader(VAddr guest_addr, u64 unique_identifier,
explicit CachedShader(VAddr cpu_addr, u64 unique_identifier,
Maxwell::ShaderProgram program_type, ShaderDiskCacheOpenGL& disk_cache,
const PrecompiledPrograms& precompiled_programs,
GLShader::ProgramResult result, u8* host_ptr);
VAddr GetCpuAddr() const override {
return guest_addr;
return cpu_addr;
}
std::size_t GetSizeInBytes() const override {
@@ -92,7 +92,7 @@ private:
ShaderDiskCacheUsage GetUsage(GLenum primitive_mode, BaseBindings base_bindings) const;
u8* host_ptr{};
VAddr guest_addr{};
VAddr cpu_addr{};
u64 unique_identifier{};
Maxwell::ShaderProgram program_type{};
ShaderDiskCacheOpenGL& disk_cache;

View File

@@ -164,8 +164,7 @@ void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuf
// Reset the screen info's display texture to its own permanent texture
screen_info.display_texture = screen_info.texture.resource.handle;
Memory::RasterizerFlushVirtualRegion(framebuffer_addr, size_in_bytes,
Memory::FlushMode::Flush);
rasterizer->FlushRegion(ToCacheAddr(Memory::GetPointer(framebuffer_addr)), size_in_bytes);
constexpr u32 linear_bpp = 4;
VideoCore::MortonCopyPixels128(VideoCore::MortonSwizzleMode::MortonToLinear,

View File

@@ -6,7 +6,6 @@
#include <cstring>
#include "common/alignment.h"
#include "common/assert.h"
#include "core/memory.h"
#include "video_core/gpu.h"
#include "video_core/textures/decoders.h"
#include "video_core/textures/texture.h"
@@ -230,18 +229,18 @@ u32 BytesPerPixel(TextureFormat format) {
}
}
void UnswizzleTexture(u8* const unswizzled_data, VAddr address, u32 tile_size_x, u32 tile_size_y,
void UnswizzleTexture(u8* const unswizzled_data, u8* address, u32 tile_size_x, u32 tile_size_y,
u32 bytes_per_pixel, u32 width, u32 height, u32 depth, u32 block_height,
u32 block_depth, u32 width_spacing) {
CopySwizzledData((width + tile_size_x - 1) / tile_size_x,
(height + tile_size_y - 1) / tile_size_y, depth, bytes_per_pixel,
bytes_per_pixel, Memory::GetPointer(address), unswizzled_data, true,
block_height, block_depth, width_spacing);
bytes_per_pixel, address, unswizzled_data, true, block_height, block_depth,
width_spacing);
}
std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size_x, u32 tile_size_y,
u32 bytes_per_pixel, u32 width, u32 height, u32 depth,
u32 block_height, u32 block_depth, u32 width_spacing) {
std::vector<u8> UnswizzleTexture(u8* address, u32 tile_size_x, u32 tile_size_y, u32 bytes_per_pixel,
u32 width, u32 height, u32 depth, u32 block_height,
u32 block_depth, u32 width_spacing) {
std::vector<u8> unswizzled_data(width * height * depth * bytes_per_pixel);
UnswizzleTexture(unswizzled_data.data(), address, tile_size_x, tile_size_y, bytes_per_pixel,
width, height, depth, block_height, block_depth, width_spacing);
@@ -249,8 +248,7 @@ std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size_x, u32 tile_size_y
}
void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32 swizzled_width,
u32 bytes_per_pixel, VAddr swizzled_data, VAddr unswizzled_data,
u32 block_height) {
u32 bytes_per_pixel, u8* swizzled_data, u8* unswizzled_data, u32 block_height) {
const u32 image_width_in_gobs{(swizzled_width * bytes_per_pixel + (gob_size_x - 1)) /
gob_size_x};
for (u32 line = 0; line < subrect_height; ++line) {
@@ -262,17 +260,17 @@ void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32
const u32 gob_address =
gob_address_y + (x * bytes_per_pixel / gob_size_x) * gob_size * block_height;
const u32 swizzled_offset = gob_address + table[(x * bytes_per_pixel) % gob_size_x];
const VAddr source_line = unswizzled_data + line * source_pitch + x * bytes_per_pixel;
const VAddr dest_addr = swizzled_data + swizzled_offset;
u8* source_line = unswizzled_data + line * source_pitch + x * bytes_per_pixel;
u8* dest_addr = swizzled_data + swizzled_offset;
Memory::CopyBlock(dest_addr, source_line, bytes_per_pixel);
std::memcpy(dest_addr, source_line, bytes_per_pixel);
}
}
}
void UnswizzleSubrect(u32 subrect_width, u32 subrect_height, u32 dest_pitch, u32 swizzled_width,
u32 bytes_per_pixel, VAddr swizzled_data, VAddr unswizzled_data,
u32 block_height, u32 offset_x, u32 offset_y) {
u32 bytes_per_pixel, u8* swizzled_data, u8* unswizzled_data, u32 block_height,
u32 offset_x, u32 offset_y) {
for (u32 line = 0; line < subrect_height; ++line) {
const u32 y2 = line + offset_y;
const u32 gob_address_y = (y2 / (gob_size_y * block_height)) * gob_size * block_height +
@@ -282,10 +280,10 @@ void UnswizzleSubrect(u32 subrect_width, u32 subrect_height, u32 dest_pitch, u32
const u32 x2 = (x + offset_x) * bytes_per_pixel;
const u32 gob_address = gob_address_y + (x2 / gob_size_x) * gob_size * block_height;
const u32 swizzled_offset = gob_address + table[x2 % gob_size_x];
const VAddr dest_line = unswizzled_data + line * dest_pitch + x * bytes_per_pixel;
const VAddr source_addr = swizzled_data + swizzled_offset;
u8* dest_line = unswizzled_data + line * dest_pitch + x * bytes_per_pixel;
u8* source_addr = swizzled_data + swizzled_offset;
Memory::CopyBlock(dest_line, source_addr, bytes_per_pixel);
std::memcpy(dest_line, source_addr, bytes_per_pixel);
}
}
}

View File

@@ -17,14 +17,14 @@ inline std::size_t GetGOBSize() {
}
/// Unswizzles a swizzled texture without changing its format.
void UnswizzleTexture(u8* unswizzled_data, VAddr address, u32 tile_size_x, u32 tile_size_y,
void UnswizzleTexture(u8* unswizzled_data, u8* address, u32 tile_size_x, u32 tile_size_y,
u32 bytes_per_pixel, u32 width, u32 height, u32 depth,
u32 block_height = TICEntry::DefaultBlockHeight,
u32 block_depth = TICEntry::DefaultBlockHeight, u32 width_spacing = 0);
/// Unswizzles a swizzled texture without changing its format.
std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size_x, u32 tile_size_y,
u32 bytes_per_pixel, u32 width, u32 height, u32 depth,
std::vector<u8> UnswizzleTexture(u8* address, u32 tile_size_x, u32 tile_size_y, u32 bytes_per_pixel,
u32 width, u32 height, u32 depth,
u32 block_height = TICEntry::DefaultBlockHeight,
u32 block_depth = TICEntry::DefaultBlockHeight,
u32 width_spacing = 0);
@@ -44,12 +44,11 @@ std::size_t CalculateSize(bool tiled, u32 bytes_per_pixel, u32 width, u32 height
/// Copies an untiled subrectangle into a tiled surface.
void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32 swizzled_width,
u32 bytes_per_pixel, VAddr swizzled_data, VAddr unswizzled_data,
u32 block_height);
u32 bytes_per_pixel, u8* swizzled_data, u8* unswizzled_data, u32 block_height);
/// Copies a tiled subrectangle into a linear surface.
void UnswizzleSubrect(u32 subrect_width, u32 subrect_height, u32 dest_pitch, u32 swizzled_width,
u32 bytes_per_pixel, VAddr swizzled_data, VAddr unswizzled_data,
u32 block_height, u32 offset_x, u32 offset_y);
u32 bytes_per_pixel, u8* swizzled_data, u8* unswizzled_data, u32 block_height,
u32 offset_x, u32 offset_y);
} // namespace Tegra::Texture

View File

@@ -24,8 +24,6 @@ void EmuThread::run() {
MicroProfileOnThreadCreate("EmuThread");
stop_run = false;
emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0);
Core::System::GetInstance().Renderer().Rasterizer().LoadDiskResources(
@@ -40,7 +38,7 @@ void EmuThread::run() {
render_window->DoneCurrent();
}
// holds whether the cpu was running during the last iteration,
// Holds whether the cpu was running during the last iteration,
// so that the DebugModeLeft signal can be emitted before the
// next execution step
bool was_active = false;

View File

@@ -209,7 +209,7 @@ void Config::ReadPlayerValues() {
for (std::size_t p = 0; p < Settings::values.players.size(); ++p) {
auto& player = Settings::values.players[p];
player.connected = qt_config->value(QString("player_%1_connected").arg(p), false).toBool();
player.connected = ReadSetting(QString("player_%1_connected").arg(p), false).toBool();
player.type = static_cast<Settings::ControllerType>(
qt_config
@@ -269,7 +269,7 @@ void Config::ReadPlayerValues() {
}
void Config::ReadDebugValues() {
Settings::values.debug_pad_enabled = qt_config->value("debug_pad_enabled", false).toBool();
Settings::values.debug_pad_enabled = ReadSetting("debug_pad_enabled", false).toBool();
for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
Settings::values.debug_pad_buttons[i] =
@@ -298,7 +298,7 @@ void Config::ReadDebugValues() {
}
void Config::ReadKeyboardValues() {
Settings::values.keyboard_enabled = qt_config->value("keyboard_enabled", false).toBool();
Settings::values.keyboard_enabled = ReadSetting("keyboard_enabled", false).toBool();
std::transform(default_keyboard_keys.begin(), default_keyboard_keys.end(),
Settings::values.keyboard_keys.begin(), InputCommon::GenerateKeyboardParam);
@@ -311,7 +311,7 @@ void Config::ReadKeyboardValues() {
}
void Config::ReadMouseValues() {
Settings::values.mouse_enabled = qt_config->value("mouse_enabled", false).toBool();
Settings::values.mouse_enabled = ReadSetting("mouse_enabled", false).toBool();
for (int i = 0; i < Settings::NativeMouseButton::NumMouseButtons; ++i) {
std::string default_param = InputCommon::GenerateKeyboardParam(default_mouse_buttons[i]);
@@ -327,16 +327,14 @@ void Config::ReadMouseValues() {
}
void Config::ReadTouchscreenValues() {
Settings::values.touchscreen.enabled = qt_config->value("touchscreen_enabled", true).toBool();
Settings::values.touchscreen.enabled = ReadSetting("touchscreen_enabled", true).toBool();
Settings::values.touchscreen.device =
qt_config->value("touchscreen_device", "engine:emu_window").toString().toStdString();
ReadSetting("touchscreen_device", "engine:emu_window").toString().toStdString();
Settings::values.touchscreen.finger = qt_config->value("touchscreen_finger", 0).toUInt();
Settings::values.touchscreen.rotation_angle = qt_config->value("touchscreen_angle", 0).toUInt();
Settings::values.touchscreen.diameter_x =
qt_config->value("touchscreen_diameter_x", 15).toUInt();
Settings::values.touchscreen.diameter_y =
qt_config->value("touchscreen_diameter_y", 15).toUInt();
Settings::values.touchscreen.finger = ReadSetting("touchscreen_finger", 0).toUInt();
Settings::values.touchscreen.rotation_angle = ReadSetting("touchscreen_angle", 0).toUInt();
Settings::values.touchscreen.diameter_x = ReadSetting("touchscreen_diameter_x", 15).toUInt();
Settings::values.touchscreen.diameter_y = ReadSetting("touchscreen_diameter_y", 15).toUInt();
qt_config->endGroup();
}
@@ -357,42 +355,41 @@ void Config::ReadValues() {
ReadTouchscreenValues();
Settings::values.motion_device =
qt_config->value("motion_device", "engine:motion_emu,update_period:100,sensitivity:0.01")
ReadSetting("motion_device", "engine:motion_emu,update_period:100,sensitivity:0.01")
.toString()
.toStdString();
qt_config->beginGroup("Core");
Settings::values.use_cpu_jit = qt_config->value("use_cpu_jit", true).toBool();
Settings::values.use_multi_core = qt_config->value("use_multi_core", false).toBool();
Settings::values.use_cpu_jit = ReadSetting("use_cpu_jit", true).toBool();
Settings::values.use_multi_core = ReadSetting("use_multi_core", false).toBool();
qt_config->endGroup();
qt_config->beginGroup("Renderer");
Settings::values.resolution_factor = qt_config->value("resolution_factor", 1.0).toFloat();
Settings::values.use_frame_limit = qt_config->value("use_frame_limit", true).toBool();
Settings::values.frame_limit = qt_config->value("frame_limit", 100).toInt();
Settings::values.use_disk_shader_cache =
qt_config->value("use_disk_shader_cache", false).toBool();
Settings::values.resolution_factor = ReadSetting("resolution_factor", 1.0).toFloat();
Settings::values.use_frame_limit = ReadSetting("use_frame_limit", true).toBool();
Settings::values.frame_limit = ReadSetting("frame_limit", 100).toInt();
Settings::values.use_disk_shader_cache = ReadSetting("use_disk_shader_cache", true).toBool();
Settings::values.use_accurate_gpu_emulation =
qt_config->value("use_accurate_gpu_emulation", false).toBool();
ReadSetting("use_accurate_gpu_emulation", false).toBool();
Settings::values.use_asynchronous_gpu_emulation =
qt_config->value("use_asynchronous_gpu_emulation", false).toBool();
ReadSetting("use_asynchronous_gpu_emulation", false).toBool();
Settings::values.bg_red = qt_config->value("bg_red", 0.0).toFloat();
Settings::values.bg_green = qt_config->value("bg_green", 0.0).toFloat();
Settings::values.bg_blue = qt_config->value("bg_blue", 0.0).toFloat();
Settings::values.bg_red = ReadSetting("bg_red", 0.0).toFloat();
Settings::values.bg_green = ReadSetting("bg_green", 0.0).toFloat();
Settings::values.bg_blue = ReadSetting("bg_blue", 0.0).toFloat();
qt_config->endGroup();
qt_config->beginGroup("Audio");
Settings::values.sink_id = qt_config->value("output_engine", "auto").toString().toStdString();
Settings::values.sink_id = ReadSetting("output_engine", "auto").toString().toStdString();
Settings::values.enable_audio_stretching =
qt_config->value("enable_audio_stretching", true).toBool();
ReadSetting("enable_audio_stretching", true).toBool();
Settings::values.audio_device_id =
qt_config->value("output_device", "auto").toString().toStdString();
Settings::values.volume = qt_config->value("volume", 1).toFloat();
ReadSetting("output_device", "auto").toString().toStdString();
Settings::values.volume = ReadSetting("volume", 1).toFloat();
qt_config->endGroup();
qt_config->beginGroup("Data Storage");
Settings::values.use_virtual_sd = qt_config->value("use_virtual_sd", true).toBool();
Settings::values.use_virtual_sd = ReadSetting("use_virtual_sd", true).toBool();
FileUtil::GetUserPath(
FileUtil::UserPath::NANDDir,
qt_config
@@ -410,30 +407,30 @@ void Config::ReadValues() {
qt_config->endGroup();
qt_config->beginGroup("Core");
Settings::values.use_cpu_jit = qt_config->value("use_cpu_jit", true).toBool();
Settings::values.use_multi_core = qt_config->value("use_multi_core", false).toBool();
Settings::values.use_cpu_jit = ReadSetting("use_cpu_jit", true).toBool();
Settings::values.use_multi_core = ReadSetting("use_multi_core", false).toBool();
qt_config->endGroup();
qt_config->beginGroup("System");
Settings::values.use_docked_mode = qt_config->value("use_docked_mode", false).toBool();
Settings::values.enable_nfc = qt_config->value("enable_nfc", true).toBool();
Settings::values.use_docked_mode = ReadSetting("use_docked_mode", false).toBool();
Settings::values.enable_nfc = ReadSetting("enable_nfc", true).toBool();
Settings::values.current_user = std::clamp<int>(qt_config->value("current_user", 0).toInt(), 0,
Service::Account::MAX_USERS - 1);
Settings::values.current_user =
std::clamp<int>(ReadSetting("current_user", 0).toInt(), 0, Service::Account::MAX_USERS - 1);
Settings::values.language_index = qt_config->value("language_index", 1).toInt();
Settings::values.language_index = ReadSetting("language_index", 1).toInt();
const auto rng_seed_enabled = qt_config->value("rng_seed_enabled", false).toBool();
const auto rng_seed_enabled = ReadSetting("rng_seed_enabled", false).toBool();
if (rng_seed_enabled) {
Settings::values.rng_seed = qt_config->value("rng_seed", 0).toULongLong();
Settings::values.rng_seed = ReadSetting("rng_seed", 0).toULongLong();
} else {
Settings::values.rng_seed = std::nullopt;
}
const auto custom_rtc_enabled = qt_config->value("custom_rtc_enabled", false).toBool();
const auto custom_rtc_enabled = ReadSetting("custom_rtc_enabled", false).toBool();
if (custom_rtc_enabled) {
Settings::values.custom_rtc =
std::chrono::seconds(qt_config->value("custom_rtc", 0).toULongLong());
std::chrono::seconds(ReadSetting("custom_rtc", 0).toULongLong());
} else {
Settings::values.custom_rtc = std::nullopt;
}
@@ -441,35 +438,35 @@ void Config::ReadValues() {
qt_config->endGroup();
qt_config->beginGroup("Miscellaneous");
Settings::values.log_filter = qt_config->value("log_filter", "*:Info").toString().toStdString();
Settings::values.use_dev_keys = qt_config->value("use_dev_keys", false).toBool();
Settings::values.log_filter = ReadSetting("log_filter", "*:Info").toString().toStdString();
Settings::values.use_dev_keys = ReadSetting("use_dev_keys", false).toBool();
qt_config->endGroup();
qt_config->beginGroup("Debugging");
Settings::values.use_gdbstub = qt_config->value("use_gdbstub", false).toBool();
Settings::values.gdbstub_port = qt_config->value("gdbstub_port", 24689).toInt();
Settings::values.program_args = qt_config->value("program_args", "").toString().toStdString();
Settings::values.dump_exefs = qt_config->value("dump_exefs", false).toBool();
Settings::values.dump_nso = qt_config->value("dump_nso", false).toBool();
Settings::values.use_gdbstub = ReadSetting("use_gdbstub", false).toBool();
Settings::values.gdbstub_port = ReadSetting("gdbstub_port", 24689).toInt();
Settings::values.program_args = ReadSetting("program_args", "").toString().toStdString();
Settings::values.dump_exefs = ReadSetting("dump_exefs", false).toBool();
Settings::values.dump_nso = ReadSetting("dump_nso", false).toBool();
qt_config->endGroup();
qt_config->beginGroup("WebService");
Settings::values.enable_telemetry = qt_config->value("enable_telemetry", true).toBool();
Settings::values.enable_telemetry = ReadSetting("enable_telemetry", true).toBool();
Settings::values.web_api_url =
qt_config->value("web_api_url", "https://api.yuzu-emu.org").toString().toStdString();
Settings::values.yuzu_username = qt_config->value("yuzu_username").toString().toStdString();
Settings::values.yuzu_token = qt_config->value("yuzu_token").toString().toStdString();
ReadSetting("web_api_url", "https://api.yuzu-emu.org").toString().toStdString();
Settings::values.yuzu_username = ReadSetting("yuzu_username").toString().toStdString();
Settings::values.yuzu_token = ReadSetting("yuzu_token").toString().toStdString();
qt_config->endGroup();
const auto size = qt_config->beginReadArray("DisabledAddOns");
for (int i = 0; i < size; ++i) {
qt_config->setArrayIndex(i);
const auto title_id = qt_config->value("title_id", 0).toULongLong();
const auto title_id = ReadSetting("title_id", 0).toULongLong();
std::vector<std::string> out;
const auto d_size = qt_config->beginReadArray("disabled");
for (int j = 0; j < d_size; ++j) {
qt_config->setArrayIndex(j);
out.push_back(qt_config->value("d", "").toString().toStdString());
out.push_back(ReadSetting("d", "").toString().toStdString());
}
qt_config->endArray();
Settings::values.disabled_addons.insert_or_assign(title_id, out);
@@ -477,41 +474,38 @@ void Config::ReadValues() {
qt_config->endArray();
qt_config->beginGroup("UI");
UISettings::values.theme = qt_config->value("theme", UISettings::themes[0].second).toString();
UISettings::values.theme = ReadSetting("theme", UISettings::themes[0].second).toString();
UISettings::values.enable_discord_presence =
qt_config->value("enable_discord_presence", true).toBool();
ReadSetting("enable_discord_presence", true).toBool();
UISettings::values.screenshot_resolution_factor =
static_cast<u16>(qt_config->value("screenshot_resolution_factor", 0).toUInt());
UISettings::values.select_user_on_boot =
qt_config->value("select_user_on_boot", false).toBool();
static_cast<u16>(ReadSetting("screenshot_resolution_factor", 0).toUInt());
UISettings::values.select_user_on_boot = ReadSetting("select_user_on_boot", false).toBool();
qt_config->beginGroup("UIGameList");
UISettings::values.show_unknown = qt_config->value("show_unknown", true).toBool();
UISettings::values.show_add_ons = qt_config->value("show_add_ons", true).toBool();
UISettings::values.icon_size = qt_config->value("icon_size", 64).toUInt();
UISettings::values.row_1_text_id = qt_config->value("row_1_text_id", 3).toUInt();
UISettings::values.row_2_text_id = qt_config->value("row_2_text_id", 2).toUInt();
UISettings::values.show_unknown = ReadSetting("show_unknown", true).toBool();
UISettings::values.show_add_ons = ReadSetting("show_add_ons", true).toBool();
UISettings::values.icon_size = ReadSetting("icon_size", 64).toUInt();
UISettings::values.row_1_text_id = ReadSetting("row_1_text_id", 3).toUInt();
UISettings::values.row_2_text_id = ReadSetting("row_2_text_id", 2).toUInt();
qt_config->endGroup();
qt_config->beginGroup("UILayout");
UISettings::values.geometry = qt_config->value("geometry").toByteArray();
UISettings::values.state = qt_config->value("state").toByteArray();
UISettings::values.renderwindow_geometry =
qt_config->value("geometryRenderWindow").toByteArray();
UISettings::values.gamelist_header_state =
qt_config->value("gameListHeaderState").toByteArray();
UISettings::values.geometry = ReadSetting("geometry").toByteArray();
UISettings::values.state = ReadSetting("state").toByteArray();
UISettings::values.renderwindow_geometry = ReadSetting("geometryRenderWindow").toByteArray();
UISettings::values.gamelist_header_state = ReadSetting("gameListHeaderState").toByteArray();
UISettings::values.microprofile_geometry =
qt_config->value("microProfileDialogGeometry").toByteArray();
ReadSetting("microProfileDialogGeometry").toByteArray();
UISettings::values.microprofile_visible =
qt_config->value("microProfileDialogVisible", false).toBool();
ReadSetting("microProfileDialogVisible", false).toBool();
qt_config->endGroup();
qt_config->beginGroup("Paths");
UISettings::values.roms_path = qt_config->value("romsPath").toString();
UISettings::values.symbols_path = qt_config->value("symbolsPath").toString();
UISettings::values.gamedir = qt_config->value("gameListRootDir", ".").toString();
UISettings::values.gamedir_deepscan = qt_config->value("gameListDeepScan", false).toBool();
UISettings::values.recent_files = qt_config->value("recentFiles").toStringList();
UISettings::values.roms_path = ReadSetting("romsPath").toString();
UISettings::values.symbols_path = ReadSetting("symbolsPath").toString();
UISettings::values.gamedir = ReadSetting("gameListRootDir", ".").toString();
UISettings::values.gamedir_deepscan = ReadSetting("gameListDeepScan", false).toBool();
UISettings::values.recent_files = ReadSetting("recentFiles").toStringList();
qt_config->endGroup();
qt_config->beginGroup("Shortcuts");
@@ -524,8 +518,8 @@ void Config::ReadValues() {
qt_config->beginGroup(hotkey);
UISettings::values.shortcuts.emplace_back(UISettings::Shortcut(
group + "/" + hotkey,
UISettings::ContextualShortcut(qt_config->value("KeySeq").toString(),
qt_config->value("Context").toInt())));
UISettings::ContextualShortcut(ReadSetting("KeySeq").toString(),
ReadSetting("Context").toInt())));
qt_config->endGroup();
}
@@ -533,16 +527,16 @@ void Config::ReadValues() {
}
qt_config->endGroup();
UISettings::values.single_window_mode = qt_config->value("singleWindowMode", true).toBool();
UISettings::values.fullscreen = qt_config->value("fullscreen", false).toBool();
UISettings::values.display_titlebar = qt_config->value("displayTitleBars", true).toBool();
UISettings::values.show_filter_bar = qt_config->value("showFilterBar", true).toBool();
UISettings::values.show_status_bar = qt_config->value("showStatusBar", true).toBool();
UISettings::values.confirm_before_closing = qt_config->value("confirmClose", true).toBool();
UISettings::values.first_start = qt_config->value("firstStart", true).toBool();
UISettings::values.callout_flags = qt_config->value("calloutFlags", 0).toUInt();
UISettings::values.show_console = qt_config->value("showConsole", false).toBool();
UISettings::values.profile_index = qt_config->value("profileIndex", 0).toUInt();
UISettings::values.single_window_mode = ReadSetting("singleWindowMode", true).toBool();
UISettings::values.fullscreen = ReadSetting("fullscreen", false).toBool();
UISettings::values.display_titlebar = ReadSetting("displayTitleBars", true).toBool();
UISettings::values.show_filter_bar = ReadSetting("showFilterBar", true).toBool();
UISettings::values.show_status_bar = ReadSetting("showStatusBar", true).toBool();
UISettings::values.confirm_before_closing = ReadSetting("confirmClose", true).toBool();
UISettings::values.first_start = ReadSetting("firstStart", true).toBool();
UISettings::values.callout_flags = ReadSetting("calloutFlags", 0).toUInt();
UISettings::values.show_console = ReadSetting("showConsole", false).toBool();
UISettings::values.profile_index = ReadSetting("profileIndex", 0).toUInt();
ApplyDefaultProfileIfInputInvalid();
@@ -553,62 +547,79 @@ void Config::SavePlayerValues() {
for (std::size_t p = 0; p < Settings::values.players.size(); ++p) {
const auto& player = Settings::values.players[p];
qt_config->setValue(QString("player_%1_connected").arg(p), player.connected);
qt_config->setValue(QString("player_%1_type").arg(p), static_cast<u8>(player.type));
WriteSetting(QString("player_%1_connected").arg(p), player.connected, false);
WriteSetting(QString("player_%1_type").arg(p), static_cast<u8>(player.type),
static_cast<u8>(Settings::ControllerType::DualJoycon));
qt_config->setValue(QString("player_%1_body_color_left").arg(p), player.body_color_left);
qt_config->setValue(QString("player_%1_body_color_right").arg(p), player.body_color_right);
qt_config->setValue(QString("player_%1_button_color_left").arg(p),
player.button_color_left);
qt_config->setValue(QString("player_%1_button_color_right").arg(p),
player.button_color_right);
WriteSetting(QString("player_%1_body_color_left").arg(p), player.body_color_left,
Settings::JOYCON_BODY_NEON_BLUE);
WriteSetting(QString("player_%1_body_color_right").arg(p), player.body_color_right,
Settings::JOYCON_BODY_NEON_RED);
WriteSetting(QString("player_%1_button_color_left").arg(p), player.button_color_left,
Settings::JOYCON_BUTTONS_NEON_BLUE);
WriteSetting(QString("player_%1_button_color_right").arg(p), player.button_color_right,
Settings::JOYCON_BUTTONS_NEON_RED);
for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
qt_config->setValue(QString("player_%1_").arg(p) +
QString::fromStdString(Settings::NativeButton::mapping[i]),
QString::fromStdString(player.buttons[i]));
std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
WriteSetting(QString("player_%1_").arg(p) +
QString::fromStdString(Settings::NativeButton::mapping[i]),
QString::fromStdString(player.buttons[i]),
QString::fromStdString(default_param));
}
for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
qt_config->setValue(QString("player_%1_").arg(p) +
QString::fromStdString(Settings::NativeAnalog::mapping[i]),
QString::fromStdString(player.analogs[i]));
std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
default_analogs[i][3], default_analogs[i][4], 0.5f);
WriteSetting(QString("player_%1_").arg(p) +
QString::fromStdString(Settings::NativeAnalog::mapping[i]),
QString::fromStdString(player.analogs[i]),
QString::fromStdString(default_param));
}
}
}
void Config::SaveDebugValues() {
qt_config->setValue("debug_pad_enabled", Settings::values.debug_pad_enabled);
WriteSetting("debug_pad_enabled", Settings::values.debug_pad_enabled, false);
for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
qt_config->setValue(QString("debug_pad_") +
QString::fromStdString(Settings::NativeButton::mapping[i]),
QString::fromStdString(Settings::values.debug_pad_buttons[i]));
std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
WriteSetting(QString("debug_pad_") +
QString::fromStdString(Settings::NativeButton::mapping[i]),
QString::fromStdString(Settings::values.debug_pad_buttons[i]),
QString::fromStdString(default_param));
}
for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
qt_config->setValue(QString("debug_pad_") +
QString::fromStdString(Settings::NativeAnalog::mapping[i]),
QString::fromStdString(Settings::values.debug_pad_analogs[i]));
std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
default_analogs[i][3], default_analogs[i][4], 0.5f);
WriteSetting(QString("debug_pad_") +
QString::fromStdString(Settings::NativeAnalog::mapping[i]),
QString::fromStdString(Settings::values.debug_pad_analogs[i]),
QString::fromStdString(default_param));
}
}
void Config::SaveMouseValues() {
qt_config->setValue("mouse_enabled", Settings::values.mouse_enabled);
WriteSetting("mouse_enabled", Settings::values.mouse_enabled, false);
for (int i = 0; i < Settings::NativeMouseButton::NumMouseButtons; ++i) {
qt_config->setValue(QString("mouse_") +
QString::fromStdString(Settings::NativeMouseButton::mapping[i]),
QString::fromStdString(Settings::values.mouse_buttons[i]));
std::string default_param = InputCommon::GenerateKeyboardParam(default_mouse_buttons[i]);
WriteSetting(QString("mouse_") +
QString::fromStdString(Settings::NativeMouseButton::mapping[i]),
QString::fromStdString(Settings::values.mouse_buttons[i]),
QString::fromStdString(default_param));
}
}
void Config::SaveTouchscreenValues() {
qt_config->setValue("touchscreen_enabled", Settings::values.touchscreen.enabled);
qt_config->setValue("touchscreen_device",
QString::fromStdString(Settings::values.touchscreen.device));
WriteSetting("touchscreen_enabled", Settings::values.touchscreen.enabled, true);
WriteSetting("touchscreen_device", QString::fromStdString(Settings::values.touchscreen.device),
"engine:emu_window");
qt_config->setValue("touchscreen_finger", Settings::values.touchscreen.finger);
qt_config->setValue("touchscreen_angle", Settings::values.touchscreen.rotation_angle);
qt_config->setValue("touchscreen_diameter_x", Settings::values.touchscreen.diameter_x);
qt_config->setValue("touchscreen_diameter_y", Settings::values.touchscreen.diameter_y);
WriteSetting("touchscreen_finger", Settings::values.touchscreen.finger, 0);
WriteSetting("touchscreen_angle", Settings::values.touchscreen.rotation_angle, 0);
WriteSetting("touchscreen_diameter_x", Settings::values.touchscreen.diameter_x, 15);
WriteSetting("touchscreen_diameter_y", Settings::values.touchscreen.diameter_y, 15);
}
void Config::SaveValues() {
@@ -619,91 +630,96 @@ void Config::SaveValues() {
SaveMouseValues();
SaveTouchscreenValues();
qt_config->setValue("motion_device", QString::fromStdString(Settings::values.motion_device));
qt_config->setValue("keyboard_enabled", Settings::values.keyboard_enabled);
WriteSetting("motion_device", QString::fromStdString(Settings::values.motion_device),
"engine:motion_emu,update_period:100,sensitivity:0.01");
WriteSetting("keyboard_enabled", Settings::values.keyboard_enabled, false);
qt_config->endGroup();
qt_config->beginGroup("Core");
qt_config->setValue("use_cpu_jit", Settings::values.use_cpu_jit);
qt_config->setValue("use_multi_core", Settings::values.use_multi_core);
WriteSetting("use_cpu_jit", Settings::values.use_cpu_jit, true);
WriteSetting("use_multi_core", Settings::values.use_multi_core, false);
qt_config->endGroup();
qt_config->beginGroup("Renderer");
qt_config->setValue("resolution_factor", (double)Settings::values.resolution_factor);
qt_config->setValue("use_frame_limit", Settings::values.use_frame_limit);
qt_config->setValue("frame_limit", Settings::values.frame_limit);
qt_config->setValue("use_disk_shader_cache", Settings::values.use_disk_shader_cache);
qt_config->setValue("use_accurate_gpu_emulation", Settings::values.use_accurate_gpu_emulation);
qt_config->setValue("use_asynchronous_gpu_emulation",
Settings::values.use_asynchronous_gpu_emulation);
WriteSetting("resolution_factor", (double)Settings::values.resolution_factor, 1.0);
WriteSetting("use_frame_limit", Settings::values.use_frame_limit, true);
WriteSetting("frame_limit", Settings::values.frame_limit, 100);
WriteSetting("use_disk_shader_cache", Settings::values.use_disk_shader_cache, true);
WriteSetting("use_accurate_gpu_emulation", Settings::values.use_accurate_gpu_emulation, false);
WriteSetting("use_asynchronous_gpu_emulation", Settings::values.use_asynchronous_gpu_emulation,
false);
// Cast to double because Qt's written float values are not human-readable
qt_config->setValue("bg_red", (double)Settings::values.bg_red);
qt_config->setValue("bg_green", (double)Settings::values.bg_green);
qt_config->setValue("bg_blue", (double)Settings::values.bg_blue);
WriteSetting("bg_red", (double)Settings::values.bg_red, 0.0);
WriteSetting("bg_green", (double)Settings::values.bg_green, 0.0);
WriteSetting("bg_blue", (double)Settings::values.bg_blue, 0.0);
qt_config->endGroup();
qt_config->beginGroup("Audio");
qt_config->setValue("output_engine", QString::fromStdString(Settings::values.sink_id));
qt_config->setValue("enable_audio_stretching", Settings::values.enable_audio_stretching);
qt_config->setValue("output_device", QString::fromStdString(Settings::values.audio_device_id));
qt_config->setValue("volume", Settings::values.volume);
WriteSetting("output_engine", QString::fromStdString(Settings::values.sink_id), "auto");
WriteSetting("enable_audio_stretching", Settings::values.enable_audio_stretching, true);
WriteSetting("output_device", QString::fromStdString(Settings::values.audio_device_id), "auto");
WriteSetting("volume", Settings::values.volume, 1.0f);
qt_config->endGroup();
qt_config->beginGroup("Data Storage");
qt_config->setValue("use_virtual_sd", Settings::values.use_virtual_sd);
qt_config->setValue("nand_directory",
QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)));
qt_config->setValue("sdmc_directory",
QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)));
WriteSetting("use_virtual_sd", Settings::values.use_virtual_sd, true);
WriteSetting("nand_directory",
QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)),
QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)));
WriteSetting("sdmc_directory",
QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)),
QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)));
qt_config->endGroup();
qt_config->beginGroup("System");
qt_config->setValue("use_docked_mode", Settings::values.use_docked_mode);
qt_config->setValue("enable_nfc", Settings::values.enable_nfc);
qt_config->setValue("current_user", Settings::values.current_user);
qt_config->setValue("language_index", Settings::values.language_index);
WriteSetting("use_docked_mode", Settings::values.use_docked_mode, false);
WriteSetting("enable_nfc", Settings::values.enable_nfc, true);
WriteSetting("current_user", Settings::values.current_user, 0);
WriteSetting("language_index", Settings::values.language_index, 1);
qt_config->setValue("rng_seed_enabled", Settings::values.rng_seed.has_value());
qt_config->setValue("rng_seed", Settings::values.rng_seed.value_or(0));
WriteSetting("rng_seed_enabled", Settings::values.rng_seed.has_value(), false);
WriteSetting("rng_seed", Settings::values.rng_seed.value_or(0), 0);
qt_config->setValue("custom_rtc_enabled", Settings::values.custom_rtc.has_value());
qt_config->setValue("custom_rtc",
QVariant::fromValue<long long>(
Settings::values.custom_rtc.value_or(std::chrono::seconds{}).count()));
WriteSetting("custom_rtc_enabled", Settings::values.custom_rtc.has_value(), false);
WriteSetting("custom_rtc",
QVariant::fromValue<long long>(
Settings::values.custom_rtc.value_or(std::chrono::seconds{}).count()),
0);
qt_config->endGroup();
qt_config->beginGroup("Miscellaneous");
qt_config->setValue("log_filter", QString::fromStdString(Settings::values.log_filter));
qt_config->setValue("use_dev_keys", Settings::values.use_dev_keys);
WriteSetting("log_filter", QString::fromStdString(Settings::values.log_filter), "*:Info");
WriteSetting("use_dev_keys", Settings::values.use_dev_keys, false);
qt_config->endGroup();
qt_config->beginGroup("Debugging");
qt_config->setValue("use_gdbstub", Settings::values.use_gdbstub);
qt_config->setValue("gdbstub_port", Settings::values.gdbstub_port);
qt_config->setValue("program_args", QString::fromStdString(Settings::values.program_args));
qt_config->setValue("dump_exefs", Settings::values.dump_exefs);
qt_config->setValue("dump_nso", Settings::values.dump_nso);
WriteSetting("use_gdbstub", Settings::values.use_gdbstub, false);
WriteSetting("gdbstub_port", Settings::values.gdbstub_port, 24689);
WriteSetting("program_args", QString::fromStdString(Settings::values.program_args), "");
WriteSetting("dump_exefs", Settings::values.dump_exefs, false);
WriteSetting("dump_nso", Settings::values.dump_nso, false);
qt_config->endGroup();
qt_config->beginGroup("WebService");
qt_config->setValue("enable_telemetry", Settings::values.enable_telemetry);
qt_config->setValue("web_api_url", QString::fromStdString(Settings::values.web_api_url));
qt_config->setValue("yuzu_username", QString::fromStdString(Settings::values.yuzu_username));
qt_config->setValue("yuzu_token", QString::fromStdString(Settings::values.yuzu_token));
WriteSetting("enable_telemetry", Settings::values.enable_telemetry, true);
WriteSetting("web_api_url", QString::fromStdString(Settings::values.web_api_url),
"https://api.yuzu-emu.org");
WriteSetting("yuzu_username", QString::fromStdString(Settings::values.yuzu_username));
WriteSetting("yuzu_token", QString::fromStdString(Settings::values.yuzu_token));
qt_config->endGroup();
qt_config->beginWriteArray("DisabledAddOns");
int i = 0;
for (const auto& elem : Settings::values.disabled_addons) {
qt_config->setArrayIndex(i);
qt_config->setValue("title_id", QVariant::fromValue<u64>(elem.first));
WriteSetting("title_id", QVariant::fromValue<u64>(elem.first), 0);
qt_config->beginWriteArray("disabled");
for (std::size_t j = 0; j < elem.second.size(); ++j) {
qt_config->setArrayIndex(static_cast<int>(j));
qt_config->setValue("d", QString::fromStdString(elem.second[j]));
WriteSetting("d", QString::fromStdString(elem.second[j]), "");
}
qt_config->endArray();
++i;
@@ -711,60 +727,86 @@ void Config::SaveValues() {
qt_config->endArray();
qt_config->beginGroup("UI");
qt_config->setValue("theme", UISettings::values.theme);
qt_config->setValue("enable_discord_presence", UISettings::values.enable_discord_presence);
qt_config->setValue("screenshot_resolution_factor",
UISettings::values.screenshot_resolution_factor);
qt_config->setValue("select_user_on_boot", UISettings::values.select_user_on_boot);
WriteSetting("theme", UISettings::values.theme, UISettings::themes[0].second);
WriteSetting("enable_discord_presence", UISettings::values.enable_discord_presence, true);
WriteSetting("screenshot_resolution_factor", UISettings::values.screenshot_resolution_factor,
0);
WriteSetting("select_user_on_boot", UISettings::values.select_user_on_boot, false);
qt_config->beginGroup("UIGameList");
qt_config->setValue("show_unknown", UISettings::values.show_unknown);
qt_config->setValue("show_add_ons", UISettings::values.show_add_ons);
qt_config->setValue("icon_size", UISettings::values.icon_size);
qt_config->setValue("row_1_text_id", UISettings::values.row_1_text_id);
qt_config->setValue("row_2_text_id", UISettings::values.row_2_text_id);
WriteSetting("show_unknown", UISettings::values.show_unknown, true);
WriteSetting("show_add_ons", UISettings::values.show_add_ons, true);
WriteSetting("icon_size", UISettings::values.icon_size, 64);
WriteSetting("row_1_text_id", UISettings::values.row_1_text_id, 3);
WriteSetting("row_2_text_id", UISettings::values.row_2_text_id, 2);
qt_config->endGroup();
qt_config->beginGroup("UILayout");
qt_config->setValue("geometry", UISettings::values.geometry);
qt_config->setValue("state", UISettings::values.state);
qt_config->setValue("geometryRenderWindow", UISettings::values.renderwindow_geometry);
qt_config->setValue("gameListHeaderState", UISettings::values.gamelist_header_state);
qt_config->setValue("microProfileDialogGeometry", UISettings::values.microprofile_geometry);
qt_config->setValue("microProfileDialogVisible", UISettings::values.microprofile_visible);
WriteSetting("geometry", UISettings::values.geometry);
WriteSetting("state", UISettings::values.state);
WriteSetting("geometryRenderWindow", UISettings::values.renderwindow_geometry);
WriteSetting("gameListHeaderState", UISettings::values.gamelist_header_state);
WriteSetting("microProfileDialogGeometry", UISettings::values.microprofile_geometry);
WriteSetting("microProfileDialogVisible", UISettings::values.microprofile_visible, false);
qt_config->endGroup();
qt_config->beginGroup("Paths");
qt_config->setValue("romsPath", UISettings::values.roms_path);
qt_config->setValue("symbolsPath", UISettings::values.symbols_path);
qt_config->setValue("screenshotPath", UISettings::values.screenshot_path);
qt_config->setValue("gameListRootDir", UISettings::values.gamedir);
qt_config->setValue("gameListDeepScan", UISettings::values.gamedir_deepscan);
qt_config->setValue("recentFiles", UISettings::values.recent_files);
WriteSetting("romsPath", UISettings::values.roms_path);
WriteSetting("symbolsPath", UISettings::values.symbols_path);
WriteSetting("screenshotPath", UISettings::values.screenshot_path);
WriteSetting("gameListRootDir", UISettings::values.gamedir, ".");
WriteSetting("gameListDeepScan", UISettings::values.gamedir_deepscan, false);
WriteSetting("recentFiles", UISettings::values.recent_files);
qt_config->endGroup();
qt_config->beginGroup("Shortcuts");
for (auto shortcut : UISettings::values.shortcuts) {
qt_config->setValue(shortcut.first + "/KeySeq", shortcut.second.first);
qt_config->setValue(shortcut.first + "/Context", shortcut.second.second);
WriteSetting(shortcut.first + "/KeySeq", shortcut.second.first);
WriteSetting(shortcut.first + "/Context", shortcut.second.second);
}
qt_config->endGroup();
qt_config->setValue("singleWindowMode", UISettings::values.single_window_mode);
qt_config->setValue("fullscreen", UISettings::values.fullscreen);
qt_config->setValue("displayTitleBars", UISettings::values.display_titlebar);
qt_config->setValue("showFilterBar", UISettings::values.show_filter_bar);
qt_config->setValue("showStatusBar", UISettings::values.show_status_bar);
qt_config->setValue("confirmClose", UISettings::values.confirm_before_closing);
qt_config->setValue("firstStart", UISettings::values.first_start);
qt_config->setValue("calloutFlags", UISettings::values.callout_flags);
qt_config->setValue("showConsole", UISettings::values.show_console);
qt_config->setValue("profileIndex", UISettings::values.profile_index);
WriteSetting("singleWindowMode", UISettings::values.single_window_mode, true);
WriteSetting("fullscreen", UISettings::values.fullscreen, false);
WriteSetting("displayTitleBars", UISettings::values.display_titlebar, true);
WriteSetting("showFilterBar", UISettings::values.show_filter_bar, true);
WriteSetting("showStatusBar", UISettings::values.show_status_bar, true);
WriteSetting("confirmClose", UISettings::values.confirm_before_closing, true);
WriteSetting("firstStart", UISettings::values.first_start, true);
WriteSetting("calloutFlags", UISettings::values.callout_flags, 0);
WriteSetting("showConsole", UISettings::values.show_console, false);
WriteSetting("profileIndex", UISettings::values.profile_index, 0);
qt_config->endGroup();
}
QVariant Config::ReadSetting(const QString& name) const {
return qt_config->value(name);
}
QVariant Config::ReadSetting(const QString& name, const QVariant& default_value) const {
QVariant result;
if (qt_config->value(name + "/default", false).toBool()) {
result = default_value;
} else {
result = qt_config->value(name, default_value);
}
return result;
}
void Config::WriteSetting(const QString& name, const QVariant& value) {
qt_config->setValue(name, value);
}
void Config::WriteSetting(const QString& name, const QVariant& value,
const QVariant& default_value) {
qt_config->setValue(name + "/default", value == default_value);
qt_config->setValue(name, value);
}
void Config::Reload() {
ReadValues();
// To apply default value changes
SaveValues();
Settings::Apply();
}

View File

@@ -42,6 +42,11 @@ private:
void SaveMouseValues();
void SaveTouchscreenValues();
QVariant ReadSetting(const QString& name) const;
QVariant ReadSetting(const QString& name, const QVariant& default_value) const;
void WriteSetting(const QString& name, const QVariant& value);
void WriteSetting(const QString& name, const QVariant& value, const QVariant& default_value);
std::unique_ptr<QSettings> qt_config;
std::string qt_config_loc;
};

View File

@@ -383,13 +383,12 @@ void GraphicsSurfaceWidget::OnUpdate() {
// TODO: Implement a good way to visualize alpha components!
QImage decoded_image(surface_width, surface_height, QImage::Format_ARGB32);
std::optional<VAddr> address = gpu.MemoryManager().GpuToCpuAddress(surface_address);
// TODO(bunnei): Will not work with BCn formats that swizzle 4x4 tiles.
// Needs to be fixed if we plan to use this feature more, otherwise we may remove it.
auto unswizzled_data = Tegra::Texture::UnswizzleTexture(
*address, 1, 1, Tegra::Texture::BytesPerPixel(surface_format), surface_width,
surface_height, 1U);
gpu.MemoryManager().GetPointer(surface_address), 1, 1,
Tegra::Texture::BytesPerPixel(surface_format), surface_width, surface_height, 1U);
auto texture_data = Tegra::Texture::DecodeTexture(unswizzled_data, surface_format,
surface_width, surface_height);

View File

@@ -114,9 +114,9 @@ int main(int argc, char** argv) {
};
while (optind < argc) {
char arg = getopt_long(argc, argv, "g:fhvp::", long_options, &option_index);
int arg = getopt_long(argc, argv, "g:fhvp::", long_options, &option_index);
if (arg != -1) {
switch (arg) {
switch (static_cast<char>(arg)) {
case 'g':
errno = 0;
gdb_port = strtoul(optarg, &endarg, 0);