Compare commits

..

64 Commits

Author SHA1 Message Date
Lioncash
12ab5a0547 service/apm: Add the apm:sys service
Adds the basic skeleton of the apm:sys service based off the information
on Switch Brew.
2018-08-07 10:05:26 -04:00
bunnei
826b1394e8 Merge pull request #931 from DarkLordZach/nca-as-drd
loader: Make AppLoader_NCA rely on directory loading code
2018-08-06 22:02:41 -04:00
bunnei
0c3c91e41c Merge pull request #947 from lioncash/encoding
game_list: Use QString::fromStdString() where applicable instead of c_str()
2018-08-06 22:02:01 -04:00
Hedges
e2b74f6354 GDBStub works with both Unicorn and Dynarmic now (#941)
* GDBStub works with both Unicorn and Dynarmic now

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

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

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

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

For example, the GPU class doesn't need to depend on the
existence of a renderer, it only needs to care about whether or not it
has a rasterizer, but since it was accessing the global variable, it was
also making the renderer a part of its dependency chain. By adjusting
the interface, we can get rid of this dependency.
2018-08-04 02:36:57 -04:00
74 changed files with 583 additions and 389 deletions

View File

@@ -168,6 +168,7 @@ void FileBackend::Write(const Entry& entry) {
SUB(Service, AM) \
SUB(Service, AOC) \
SUB(Service, APM) \
SUB(Service, ARP) \
SUB(Service, BCAT) \
SUB(Service, BPC) \
SUB(Service, BTM) \

View File

@@ -54,6 +54,7 @@ enum class Class : ClassType {
Service_AM, ///< The AM (Applet manager) service
Service_AOC, ///< The AOC (AddOn Content) service
Service_APM, ///< The APM (Performance) service
Service_ARP, ///< The ARP service
Service_Audio, ///< The Audio (Audio control) service
Service_BCAT, ///< The BCAT service
Service_BPC, ///< The BPC service

View File

@@ -134,6 +134,8 @@ add_library(core STATIC
hle/service/apm/apm.h
hle/service/apm/interface.cpp
hle/service/apm/interface.h
hle/service/arp/arp.cpp
hle/service/arp/arp.h
hle/service/audio/audctl.cpp
hle/service/audio/audctl.h
hle/service/audio/auddbg.cpp

View File

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

View File

@@ -18,6 +18,7 @@
#include "core/loader/loader.h"
#include "core/settings.h"
#include "file_sys/vfs_real.h"
#include "video_core/renderer_base.h"
#include "video_core/video_core.h"
namespace Core {
@@ -61,7 +62,6 @@ System::ResultStatus System::RunLoop(bool tight_loop) {
// execute. Otherwise, get out of the loop function.
if (GDBStub::GetCpuHaltFlag()) {
if (GDBStub::GetCpuStepFlag()) {
GDBStub::SetCpuStepFlag(false);
tight_loop = false;
} else {
return ResultStatus::Success;
@@ -77,6 +77,10 @@ System::ResultStatus System::RunLoop(bool tight_loop) {
}
}
if (GDBStub::IsServerEnabled()) {
GDBStub::SetCpuStepFlag(false);
}
return status;
}
@@ -178,7 +182,6 @@ System::ResultStatus System::Init(EmuWindow& emu_window) {
cpu_cores[index] = std::make_shared<Cpu>(cpu_exclusive_monitor, cpu_barrier, index);
}
gpu_core = std::make_unique<Tegra::GPU>();
telemetry_session = std::make_unique<Core::TelemetrySession>();
service_manager = std::make_shared<Service::SM::ServiceManager>();
@@ -186,10 +189,13 @@ System::ResultStatus System::Init(EmuWindow& emu_window) {
Service::Init(service_manager);
GDBStub::Init();
if (!VideoCore::Init(emu_window)) {
renderer = VideoCore::CreateRenderer(emu_window);
if (!renderer->Init()) {
return ResultStatus::ErrorVideoCore;
}
gpu_core = std::make_unique<Tegra::GPU>(renderer->Rasterizer());
// Create threads for CPU cores 1-3, and build thread_to_cpu map
// CPU core 0 is run on the main thread
thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0];
@@ -221,7 +227,7 @@ void System::Shutdown() {
perf_results.frametime * 1000.0);
// Shutdown emulation session
VideoCore::Shutdown();
renderer.reset();
GDBStub::Shutdown();
Service::Shutdown();
Kernel::Shutdown();

View File

@@ -27,6 +27,10 @@ namespace Service::SM {
class ServiceManager;
}
namespace VideoCore {
class RendererBase;
}
namespace Core {
class System {
@@ -78,6 +82,17 @@ public:
*/
ResultStatus SingleStep();
/**
* Invalidate the CPU instruction caches
* This function should only be used by GDB Stub to support breakpoints, memory updates and
* step/continue commands.
*/
void InvalidateCpuInstructionCaches() {
for (auto& cpu : cpu_cores) {
cpu->ArmInterface().ClearInstructionCache();
}
}
/// Shutdown the emulated system.
void Shutdown();
@@ -129,11 +144,26 @@ public:
/// Gets a CPU interface to the CPU core with the specified index
Cpu& CpuCore(size_t core_index);
/// Gets the GPU interface
/// Gets a mutable reference to the GPU interface
Tegra::GPU& GPU() {
return *gpu_core;
}
/// Gets an immutable reference to the GPU interface.
const Tegra::GPU& GPU() const {
return *gpu_core;
}
/// Gets a mutable reference to the renderer.
VideoCore::RendererBase& Renderer() {
return *renderer;
}
/// Gets an immutable reference to the renderer.
const VideoCore::RendererBase& Renderer() const {
return *renderer;
}
/// Gets the scheduler for the CPU core that is currently running
Kernel::Scheduler& CurrentScheduler() {
return *CurrentCpuCore().Scheduler();
@@ -197,6 +227,7 @@ private:
/// AppLoader used to load the current executing application
std::unique_ptr<Loader::AppLoader> app_loader;
std::unique_ptr<VideoCore::RendererBase> renderer;
std::unique_ptr<Tegra::GPU> gpu_core;
std::shared_ptr<Tegra::DebugContext> debug_context;
Kernel::SharedPtr<Kernel::Process> current_process;

View File

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

View File

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

View File

@@ -3,10 +3,22 @@
// Refer to the license.txt file included.
#include <mbedtls/cipher.h>
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/crypto/aes_util.h"
#include "core/crypto/key_manager.h"
namespace Core::Crypto {
namespace {
std::vector<u8> CalculateNintendoTweak(size_t sector_id) {
std::vector<u8> out(0x10);
for (size_t i = 0xF; i <= 0xF; --i) {
out[i] = sector_id & 0xFF;
sector_id >>= 8;
}
return out;
}
} // Anonymous namespace
static_assert(static_cast<size_t>(Mode::CTR) == static_cast<size_t>(MBEDTLS_CIPHER_AES_128_CTR),
"CTR has incorrect value.");
@@ -56,27 +68,28 @@ void AESCipher<Key, KeySize>::SetIV(std::vector<u8> iv) {
}
template <typename Key, size_t KeySize>
void AESCipher<Key, KeySize>::Transcode(const u8* src, size_t size, u8* dest, Op op) {
size_t written = 0;
const auto context = op == Op::Encrypt ? &ctx->encryption_context : &ctx->decryption_context;
void AESCipher<Key, KeySize>::Transcode(const u8* src, size_t size, u8* dest, Op op) const {
auto* const context = op == Op::Encrypt ? &ctx->encryption_context : &ctx->decryption_context;
mbedtls_cipher_reset(context);
size_t written = 0;
if (mbedtls_cipher_get_cipher_mode(context) == MBEDTLS_MODE_XTS) {
mbedtls_cipher_update(context, src, size, dest, &written);
if (written != size)
if (written != size) {
LOG_WARNING(Crypto, "Not all data was decrypted requested={:016X}, actual={:016X}.",
size, written);
}
} else {
const auto block_size = mbedtls_cipher_get_block_size(context);
for (size_t offset = 0; offset < size; offset += block_size) {
auto length = std::min<size_t>(block_size, size - offset);
mbedtls_cipher_update(context, src + offset, length, dest + offset, &written);
if (written != length)
if (written != length) {
LOG_WARNING(Crypto, "Not all data was decrypted requested={:016X}, actual={:016X}.",
length, written);
}
}
}
@@ -97,16 +110,6 @@ void AESCipher<Key, KeySize>::XTSTranscode(const u8* src, size_t size, u8* dest,
}
}
template <typename Key, size_t KeySize>
std::vector<u8> AESCipher<Key, KeySize>::CalculateNintendoTweak(size_t sector_id) {
std::vector<u8> out(0x10);
for (size_t i = 0xF; i <= 0xF; --i) {
out[i] = sector_id & 0xFF;
sector_id >>= 8;
}
return out;
}
template class AESCipher<Key128>;
template class AESCipher<Key256>;
} // namespace Core::Crypto

View File

@@ -7,7 +7,7 @@
#include <memory>
#include <type_traits>
#include <vector>
#include "common/assert.h"
#include "common/common_types.h"
#include "core/file_sys/vfs.h"
namespace Core::Crypto {
@@ -38,15 +38,19 @@ public:
void SetIV(std::vector<u8> iv);
template <typename Source, typename Dest>
void Transcode(const Source* src, size_t size, Dest* dest, Op op) {
void Transcode(const Source* src, size_t size, Dest* dest, Op op) const {
static_assert(std::is_trivially_copyable_v<Source> && std::is_trivially_copyable_v<Dest>,
"Transcode source and destination types must be trivially copyable.");
Transcode(reinterpret_cast<const u8*>(src), size, reinterpret_cast<u8*>(dest), op);
}
void Transcode(const u8* src, size_t size, u8* dest, Op op);
void Transcode(const u8* src, size_t size, u8* dest, Op op) const;
template <typename Source, typename Dest>
void XTSTranscode(const Source* src, size_t size, Dest* dest, size_t sector_id,
size_t sector_size, Op op) {
static_assert(std::is_trivially_copyable_v<Source> && std::is_trivially_copyable_v<Dest>,
"XTSTranscode source and destination types must be trivially copyable.");
XTSTranscode(reinterpret_cast<const u8*>(src), size, reinterpret_cast<u8*>(dest), sector_id,
sector_size, op);
}
@@ -56,7 +60,5 @@ public:
private:
std::unique_ptr<CipherContext> ctx;
static std::vector<u8> CalculateNintendoTweak(size_t sector_id);
};
} // namespace Core::Crypto

View File

@@ -4,6 +4,7 @@
#pragma once
#include "common/common_types.h"
#include "core/file_sys/vfs.h"
namespace Core::Crypto {

View File

@@ -2,19 +2,16 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <array>
#include <fstream>
#include <locale>
#include <sstream>
#include <string_view>
#include <mbedtls/sha256.h>
#include "common/assert.h"
#include "common/common_paths.h"
#include "common/file_util.h"
#include "common/logging/log.h"
#include "core/crypto/key_manager.h"
#include "core/settings.h"
#include "key_manager.h"
namespace Core::Crypto {
@@ -66,8 +63,7 @@ KeyManager::KeyManager() {
AttemptLoadKeyFile(yuzu_keys_dir, hactool_keys_dir, "title.keys", true);
}
void KeyManager::LoadFromFile(std::string_view filename_, bool is_title_keys) {
const auto filename = std::string(filename_);
void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) {
std::ifstream file(filename);
if (!file.is_open())
return;
@@ -107,11 +103,8 @@ void KeyManager::LoadFromFile(std::string_view filename_, bool is_title_keys) {
}
}
void KeyManager::AttemptLoadKeyFile(std::string_view dir1_, std::string_view dir2_,
std::string_view filename_, bool title) {
const std::string dir1(dir1_);
const std::string dir2(dir2_);
const std::string filename(filename_);
void KeyManager::AttemptLoadKeyFile(const std::string& dir1, const std::string& dir2,
const std::string& filename, bool title) {
if (FileUtil::Exists(dir1 + DIR_SEP + filename))
LoadFromFile(dir1 + DIR_SEP + filename, title);
else if (FileUtil::Exists(dir2 + DIR_SEP + filename))

View File

@@ -5,6 +5,7 @@
#pragma once
#include <array>
#include <string>
#include <type_traits>
#include <unordered_map>
#include <vector>
@@ -109,9 +110,9 @@ private:
std::unordered_map<KeyIndex<S256KeyType>, Key256> s256_keys;
bool dev_mode;
void LoadFromFile(std::string_view filename, bool is_title_keys);
void AttemptLoadKeyFile(std::string_view dir1, std::string_view dir2, std::string_view filename,
bool title);
void LoadFromFile(const std::string& filename, bool is_title_keys);
void AttemptLoadKeyFile(const std::string& dir1, const std::string& dir2,
const std::string& filename, bool title);
static const std::unordered_map<std::string, KeyIndex<S128KeyType>> s128_file_id;
static const std::unordered_map<std::string, KeyIndex<S256KeyType>> s256_file_id;

View File

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

View File

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

View File

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

View File

@@ -31,10 +31,9 @@ public:
return HANDLE_TYPE;
}
ResetType reset_type; ///< Current ResetType
bool signaled; ///< Whether the event has already been signaled
std::string name; ///< Name of event (optional)
ResetType GetResetType() const {
return reset_type;
}
bool ShouldWait(Thread* thread) const override;
void Acquire(Thread* thread) override;
@@ -47,6 +46,11 @@ public:
private:
Event();
~Event() override;
ResetType reset_type; ///< Current ResetType
bool signaled; ///< Whether the event has already been signaled
std::string name; ///< Name of event (optional)
};
} // namespace Kernel

View File

@@ -13,6 +13,7 @@ void InstallInterfaces(SM::ServiceManager& service_manager) {
auto module_ = std::make_shared<Module>();
std::make_shared<APM>(module_, "apm")->InstallAsService(service_manager);
std::make_shared<APM>(module_, "apm:p")->InstallAsService(service_manager);
std::make_shared<APM_Sys>()->InstallAsService(service_manager);
}
} // namespace Service::APM

View File

@@ -74,6 +74,31 @@ void APM::OpenSession(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<ISession>();
LOG_DEBUG(Service_APM, "called");
}
APM_Sys::APM_Sys() : ServiceFramework{"apm:sys"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "RequestPerformanceMode"},
{1, &APM_Sys::GetPerformanceEvent, "GetPerformanceEvent"},
{2, nullptr, "GetThrottlingState"},
{3, nullptr, "GetLastThrottlingState"},
{4, nullptr, "ClearLastThrottlingState"},
{5, nullptr, "LoadAndApplySettings"},
};
// clang-format on
RegisterHandlers(functions);
}
void APM_Sys::GetPerformanceEvent(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<ISession>();
LOG_DEBUG(Service_APM, "called");
}
} // namespace Service::APM

View File

@@ -19,4 +19,12 @@ private:
std::shared_ptr<Module> apm;
};
class APM_Sys final : public ServiceFramework<APM_Sys> {
public:
explicit APM_Sys();
private:
void GetPerformanceEvent(Kernel::HLERequestContext& ctx);
};
} // namespace Service::APM

View File

@@ -0,0 +1,75 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <memory>
#include "common/logging/log.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/service/arp/arp.h"
#include "core/hle/service/service.h"
#include "core/hle/service/sm/sm.h"
namespace Service::ARP {
class ARP_R final : public ServiceFramework<ARP_R> {
public:
explicit ARP_R() : ServiceFramework{"arp:r"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "GetApplicationLaunchProperty"},
{1, nullptr, "GetApplicationLaunchPropertyWithApplicationId"},
{2, nullptr, "GetApplicationControlProperty"},
{3, nullptr, "GetApplicationControlPropertyWithApplicationId"},
};
// clang-format on
RegisterHandlers(functions);
}
};
class IRegistrar final : public ServiceFramework<IRegistrar> {
public:
explicit IRegistrar() : ServiceFramework{"IRegistrar"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "Issue"},
{1, nullptr, "SetApplicationLaunchProperty"},
{2, nullptr, "SetApplicationControlProperty"},
};
// clang-format on
RegisterHandlers(functions);
}
};
class ARP_W final : public ServiceFramework<ARP_W> {
public:
explicit ARP_W() : ServiceFramework{"arp:w"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &ARP_W::AcquireRegistrar, "AcquireRegistrar"},
{1, nullptr, "DeleteProperties"},
};
// clang-format on
RegisterHandlers(functions);
}
private:
void AcquireRegistrar(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IRegistrar>();
LOG_DEBUG(Service_ARP, "called");
}
};
void InstallInterfaces(SM::ServiceManager& sm) {
std::make_shared<ARP_R>()->InstallAsService(sm);
std::make_shared<ARP_W>()->InstallAsService(sm);
}
} // namespace Service::ARP

View File

@@ -0,0 +1,16 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
namespace Service::SM {
class ServiceManager;
}
namespace Service::ARP {
/// Registers all ARP services with the specified service manager.
void InstallInterfaces(SM::ServiceManager& sm);
} // namespace Service::ARP

View File

@@ -2,8 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/service/audio/audin_a.h"
namespace Service::Audio {

View File

@@ -2,8 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/service/audio/audout_a.h"
namespace Service::Audio {

View File

@@ -2,8 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/service/audio/audrec_a.h"
namespace Service::Audio {

View File

@@ -2,8 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/service/audio/audren_a.h"
namespace Service::Audio {

View File

@@ -2,8 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/service/filesystem/fsp_ldr.h"
#include "core/hle/service/service.h"

View File

@@ -2,8 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/service/filesystem/fsp_pr.h"
#include "core/hle/service/service.h"

View File

@@ -30,9 +30,9 @@ void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u3
addr, offset, width, height, stride, static_cast<PixelFormat>(format),
transform, crop_rect};
Core::System::GetInstance().perf_stats.EndGameFrame();
VideoCore::g_renderer->SwapBuffers(framebuffer);
auto& instance = Core::System::GetInstance();
instance.perf_stats.EndGameFrame();
instance.Renderer().SwapBuffers(framebuffer);
}
} // namespace Service::Nvidia::Devices

View File

@@ -150,15 +150,16 @@ u32 nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& ou
LOG_DEBUG(Service_NVDRV, "called, offset=0x{:X}", params.offset);
auto& gpu = Core::System::GetInstance().GPU();
auto itr = buffer_mappings.find(params.offset);
const auto itr = buffer_mappings.find(params.offset);
ASSERT_MSG(itr != buffer_mappings.end(), "Tried to unmap invalid mapping");
// Remove this memory region from the rasterizer cache.
VideoCore::g_renderer->Rasterizer()->FlushAndInvalidateRegion(params.offset, itr->second.size);
auto& system_instance = Core::System::GetInstance();
// Remove this memory region from the rasterizer cache.
system_instance.Renderer().Rasterizer().FlushAndInvalidateRegion(params.offset,
itr->second.size);
auto& gpu = system_instance.GPU();
params.offset = gpu.memory_manager->UnmapBuffer(params.offset, itr->second.size);
buffer_mappings.erase(itr->second.offset);

View File

@@ -127,9 +127,11 @@ void NVFlinger::Compose() {
MicroProfileFlip();
if (buffer == boost::none) {
auto& system_instance = Core::System::GetInstance();
// There was no queued buffer to draw, render previous frame
Core::System::GetInstance().perf_stats.EndGameFrame();
VideoCore::g_renderer->SwapBuffers({});
system_instance.perf_stats.EndGameFrame();
system_instance.Renderer().SwapBuffers({});
continue;
}

View File

@@ -19,6 +19,7 @@
#include "core/hle/service/am/am.h"
#include "core/hle/service/aoc/aoc_u.h"
#include "core/hle/service/apm/apm.h"
#include "core/hle/service/arp/arp.h"
#include "core/hle/service/audio/audio.h"
#include "core/hle/service/bcat/bcat.h"
#include "core/hle/service/bpc/bpc.h"
@@ -207,6 +208,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm) {
AM::InstallInterfaces(*sm, nv_flinger);
AOC::InstallInterfaces(*sm);
APM::InstallInterfaces(*sm);
ARP::InstallInterfaces(*sm);
Audio::InstallInterfaces(*sm);
BCAT::InstallInterfaces(*sm);
BPC::InstallInterfaces(*sm);

View File

@@ -20,6 +20,10 @@ namespace Loader {
AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile file)
: AppLoader(std::move(file)) {}
AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(
FileSys::VirtualDir directory)
: AppLoader(directory->GetFile("main")), dir(std::move(directory)) {}
FileType AppLoader_DeconstructedRomDirectory::IdentifyType(const FileSys::VirtualFile& file) {
if (FileSys::IsDirectoryExeFS(file->GetContainingDirectory())) {
return FileType::DeconstructedRomDirectory;
@@ -34,7 +38,12 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(
return ResultStatus::ErrorAlreadyLoaded;
}
const FileSys::VirtualDir dir = file->GetContainingDirectory();
if (dir == nullptr) {
if (file == nullptr)
return ResultStatus::ErrorInvalidFormat;
const FileSys::VirtualDir dir = file->GetContainingDirectory();
}
const FileSys::VirtualFile npdm = dir->GetFile("main.npdm");
if (npdm == nullptr)
return ResultStatus::ErrorInvalidFormat;

View File

@@ -22,6 +22,9 @@ class AppLoader_DeconstructedRomDirectory final : public AppLoader {
public:
explicit AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile main_file);
// Overload to accept exefs directory. Must contain 'main' and 'main.npdm'
explicit AppLoader_DeconstructedRomDirectory(FileSys::VirtualDir directory);
/**
* Returns the type of the file
* @param file std::shared_ptr<VfsFile> open file
@@ -40,6 +43,7 @@ public:
private:
FileSys::ProgramMetadata metadata;
FileSys::VirtualFile romfs;
FileSys::VirtualDir dir;
};
} // namespace Loader

View File

@@ -22,7 +22,8 @@
namespace Loader {
AppLoader_NCA::AppLoader_NCA(FileSys::VirtualFile file) : AppLoader(std::move(file)) {}
AppLoader_NCA::AppLoader_NCA(FileSys::VirtualFile file_)
: AppLoader(std::move(file_)), nca(std::make_unique<FileSys::NCA>(file)) {}
FileType AppLoader_NCA::IdentifyType(const FileSys::VirtualFile& file) {
FileSys::NCA nca(file);
@@ -39,8 +40,7 @@ ResultStatus AppLoader_NCA::Load(Kernel::SharedPtr<Kernel::Process>& process) {
return ResultStatus::ErrorAlreadyLoaded;
}
nca = std::make_unique<FileSys::NCA>(file);
ResultStatus result = nca->GetStatus();
const auto result = nca->GetStatus();
if (result != ResultStatus::Success) {
return result;
}
@@ -48,44 +48,16 @@ ResultStatus AppLoader_NCA::Load(Kernel::SharedPtr<Kernel::Process>& process) {
if (nca->GetType() != FileSys::NCAContentType::Program)
return ResultStatus::ErrorInvalidFormat;
auto exefs = nca->GetExeFS();
const auto exefs = nca->GetExeFS();
if (exefs == nullptr)
return ResultStatus::ErrorInvalidFormat;
result = metadata.Load(exefs->GetFile("main.npdm"));
if (result != ResultStatus::Success) {
return result;
}
metadata.Print();
directory_loader = std::make_unique<AppLoader_DeconstructedRomDirectory>(exefs);
const FileSys::ProgramAddressSpaceType arch_bits{metadata.GetAddressSpaceType()};
if (arch_bits == FileSys::ProgramAddressSpaceType::Is32Bit) {
return ResultStatus::ErrorUnsupportedArch;
}
VAddr next_load_addr{Memory::PROCESS_IMAGE_VADDR};
for (const auto& module : {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", "subsdk3",
"subsdk4", "subsdk5", "subsdk6", "subsdk7", "sdk"}) {
const VAddr load_addr = next_load_addr;
next_load_addr = AppLoader_NSO::LoadModule(exefs->GetFile(module), load_addr);
if (next_load_addr) {
LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr);
// Register module with GDBStub
GDBStub::RegisterModule(module, load_addr, next_load_addr - 1, false);
} else {
next_load_addr = load_addr;
}
}
process->program_id = metadata.GetTitleID();
process->svc_access_mask.set();
process->address_mappings = default_address_mappings;
process->resource_limit =
Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION);
process->Run(Memory::PROCESS_IMAGE_VADDR, metadata.GetMainThreadPriority(),
metadata.GetMainThreadStackSize());
const auto load_result = directory_loader->Load(process);
if (load_result != ResultStatus::Success)
return load_result;
if (nca->GetRomFS() != nullptr && nca->GetRomFS()->GetSize() > 0)
Service::FileSystem::RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(*this));

View File

@@ -10,6 +10,7 @@
#include "core/file_sys/program_metadata.h"
#include "core/hle/kernel/object.h"
#include "core/loader/loader.h"
#include "deconstructed_rom_directory.h"
namespace Loader {
@@ -41,6 +42,7 @@ private:
FileSys::ProgramMetadata metadata;
std::unique_ptr<FileSys::NCA> nca;
std::unique_ptr<AppLoader_DeconstructedRomDirectory> directory_loader;
};
} // namespace Loader

View File

@@ -326,43 +326,45 @@ void RasterizerMarkRegionCached(Tegra::GPUVAddr gpu_addr, 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 (VideoCore::g_renderer == nullptr) {
if (!system_instance.IsPoweredOn()) {
return;
}
VAddr end = start + size;
auto CheckRegion = [&](VAddr region_start, VAddr region_end) {
const auto CheckRegion = [&](VAddr region_start, VAddr region_end) {
if (start >= region_end || end <= region_start) {
// No overlap with region
return;
}
VAddr overlap_start = std::max(start, region_start);
VAddr overlap_end = std::min(end, region_end);
const VAddr overlap_start = std::max(start, region_start);
const VAddr overlap_end = std::min(end, region_end);
std::vector<Tegra::GPUVAddr> gpu_addresses =
Core::System::GetInstance().GPU().memory_manager->CpuToGpuAddress(overlap_start);
const std::vector<Tegra::GPUVAddr> gpu_addresses =
system_instance.GPU().memory_manager->CpuToGpuAddress(overlap_start);
if (gpu_addresses.empty()) {
return;
}
u64 overlap_size = overlap_end - overlap_start;
const u64 overlap_size = overlap_end - overlap_start;
for (const auto& gpu_address : gpu_addresses) {
auto* rasterizer = VideoCore::g_renderer->Rasterizer();
auto& rasterizer = system_instance.Renderer().Rasterizer();
switch (mode) {
case FlushMode::Flush:
rasterizer->FlushRegion(gpu_address, overlap_size);
rasterizer.FlushRegion(gpu_address, overlap_size);
break;
case FlushMode::Invalidate:
rasterizer->InvalidateRegion(gpu_address, overlap_size);
rasterizer.InvalidateRegion(gpu_address, overlap_size);
break;
case FlushMode::FlushAndInvalidate:
rasterizer->FlushAndInvalidateRegion(gpu_address, overlap_size);
rasterizer.FlushAndInvalidateRegion(gpu_address, overlap_size);
break;
}
}

View File

@@ -140,10 +140,10 @@ void SetCurrentPageTable(PageTable* page_table);
PageTable* GetCurrentPageTable();
/// Determines if the given VAddr is valid for the specified process.
bool IsValidVirtualAddress(const Kernel::Process& process, const VAddr vaddr);
bool IsValidVirtualAddress(const VAddr addr);
bool IsValidVirtualAddress(const Kernel::Process& process, VAddr vaddr);
bool IsValidVirtualAddress(VAddr vaddr);
/// Determines if the given VAddr is a kernel address
bool IsKernelVirtualAddress(const VAddr addr);
bool IsKernelVirtualAddress(VAddr vaddr);
u8 Read8(VAddr addr);
u16 Read16(VAddr addr);
@@ -155,18 +155,17 @@ void Write16(VAddr addr, u16 data);
void Write32(VAddr addr, u32 data);
void Write64(VAddr addr, u64 data);
void ReadBlock(const Kernel::Process& process, const VAddr src_addr, void* dest_buffer,
size_t size);
void ReadBlock(const VAddr src_addr, void* dest_buffer, size_t size);
void WriteBlock(const Kernel::Process& process, const VAddr dest_addr, const void* src_buffer,
void ReadBlock(const Kernel::Process& process, VAddr src_addr, void* dest_buffer, size_t size);
void ReadBlock(VAddr src_addr, void* dest_buffer, size_t size);
void WriteBlock(const Kernel::Process& process, VAddr dest_addr, const void* src_buffer,
size_t size);
void WriteBlock(const VAddr dest_addr, const void* src_buffer, size_t size);
void ZeroBlock(const VAddr dest_addr, const size_t size);
void WriteBlock(VAddr dest_addr, const void* src_buffer, size_t size);
void ZeroBlock(const Kernel::Process& process, VAddr dest_addr, size_t size);
void CopyBlock(VAddr dest_addr, VAddr src_addr, size_t size);
u8* GetPointer(VAddr virtual_address);
u8* GetPointer(VAddr vaddr);
std::string ReadCString(VAddr virtual_address, std::size_t max_length);
std::string ReadCString(VAddr vaddr, std::size_t max_length);
enum class FlushMode {
/// Write back modified surfaces to RAM
@@ -180,7 +179,7 @@ enum class FlushMode {
/**
* Mark each page touching the region as cached.
*/
void RasterizerMarkRegionCached(Tegra::GPUVAddr start, u64 size, bool cached);
void RasterizerMarkRegionCached(Tegra::GPUVAddr gpu_addr, u64 size, bool cached);
/**
* Flushes and invalidates any externally cached rasterizer resources touching the given virtual

View File

@@ -40,22 +40,21 @@ void PerfStats::EndGameFrame() {
game_frames += 1;
}
PerfStats::Results PerfStats::GetAndResetStats(u64 current_system_time_us) {
PerfStats::Results PerfStats::GetAndResetStats(microseconds current_system_time_us) {
std::lock_guard<std::mutex> lock(object_mutex);
auto now = Clock::now();
const auto now = Clock::now();
// Walltime elapsed since stats were reset
auto interval = duration_cast<DoubleSecs>(now - reset_point).count();
const auto interval = duration_cast<DoubleSecs>(now - reset_point).count();
auto system_us_per_second =
static_cast<double>(current_system_time_us - reset_point_system_us) / interval;
const auto system_us_per_second = (current_system_time_us - reset_point_system_us) / interval;
Results results{};
results.system_fps = static_cast<double>(system_frames) / interval;
results.game_fps = static_cast<double>(game_frames) / interval;
results.frametime = duration_cast<DoubleSecs>(accumulated_frametime).count() /
static_cast<double>(system_frames);
results.emulation_speed = system_us_per_second / 1'000'000.0;
results.emulation_speed = system_us_per_second.count() / 1'000'000.0;
// Reset counters
reset_point = now;
@@ -74,10 +73,10 @@ double PerfStats::GetLastFrameTimeScale() {
return duration_cast<DoubleSecs>(previous_frame_length).count() / FRAME_LENGTH;
}
void FrameLimiter::DoFrameLimiting(u64 current_system_time_us) {
void FrameLimiter::DoFrameLimiting(microseconds current_system_time_us) {
// Max lag caused by slow frames. Can be adjusted to compensate for too many slow frames. Higher
// values increase the time needed to recover and limit framerate again after spikes.
constexpr microseconds MAX_LAG_TIME_US = 25ms;
constexpr microseconds MAX_LAG_TIME_US = 25us;
if (!Settings::values.toggle_framelimit) {
return;
@@ -85,7 +84,7 @@ void FrameLimiter::DoFrameLimiting(u64 current_system_time_us) {
auto now = Clock::now();
frame_limiting_delta_err += microseconds(current_system_time_us - previous_system_time_us);
frame_limiting_delta_err += current_system_time_us - previous_system_time_us;
frame_limiting_delta_err -= duration_cast<microseconds>(now - previous_walltime);
frame_limiting_delta_err =
std::clamp(frame_limiting_delta_err, -MAX_LAG_TIME_US, MAX_LAG_TIME_US);

View File

@@ -33,7 +33,7 @@ public:
void EndSystemFrame();
void EndGameFrame();
Results GetAndResetStats(u64 current_system_time_us);
Results GetAndResetStats(std::chrono::microseconds current_system_time_us);
/**
* Gets the ratio between walltime and the emulated time of the previous system frame. This is
@@ -47,7 +47,7 @@ private:
/// Point when the cumulative counters were reset
Clock::time_point reset_point = Clock::now();
/// System time when the cumulative counters were reset
u64 reset_point_system_us = 0;
std::chrono::microseconds reset_point_system_us{0};
/// Cumulative duration (excluding v-sync/frame-limiting) of frames since last reset
Clock::duration accumulated_frametime = Clock::duration::zero();
@@ -68,11 +68,11 @@ class FrameLimiter {
public:
using Clock = std::chrono::high_resolution_clock;
void DoFrameLimiting(u64 current_system_time_us);
void DoFrameLimiting(std::chrono::microseconds current_system_time_us);
private:
/// Emulated system time (in microseconds) at the last limiter invocation
u64 previous_system_time_us = 0;
std::chrono::microseconds previous_system_time_us{0};
/// Walltime at the last limiter invocation
Clock::time_point previous_walltime = Clock::now();

View File

@@ -2,6 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/core.h"
#include "core/gdbstub/gdbstub.h"
#include "core/hle/service/hid/hid.h"
#include "core/settings.h"
@@ -19,8 +20,9 @@ void Apply() {
VideoCore::g_toggle_framelimit_enabled = values.toggle_framelimit;
if (VideoCore::g_renderer) {
VideoCore::g_renderer->UpdateCurrentFramebufferLayout();
auto& system_instance = Core::System::GetInstance();
if (system_instance.IsPoweredOn()) {
system_instance.Renderer().UpdateCurrentFramebufferLayout();
}
Service::HID::ReloadInputDevices();

View File

@@ -19,8 +19,8 @@ namespace Engines {
/// First register id that is actually a Macro call.
constexpr u32 MacroRegistersStart = 0xE00;
Maxwell3D::Maxwell3D(MemoryManager& memory_manager)
: memory_manager(memory_manager), macro_interpreter(*this) {}
Maxwell3D::Maxwell3D(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager)
: memory_manager(memory_manager), rasterizer{rasterizer}, macro_interpreter(*this) {}
void Maxwell3D::CallMacroMethod(u32 method, std::vector<u32> parameters) {
auto macro_code = uploaded_macros.find(method);
@@ -130,7 +130,7 @@ void Maxwell3D::WriteReg(u32 method, u32 value, u32 remaining_params) {
break;
}
VideoCore::g_renderer->Rasterizer()->NotifyMaxwellRegisterChanged(method);
rasterizer.NotifyMaxwellRegisterChanged(method);
if (debug_context) {
debug_context->OnEvent(Tegra::DebugContext::Event::MaxwellCommandProcessed, nullptr);
@@ -218,7 +218,7 @@ void Maxwell3D::DrawArrays() {
}
const bool is_indexed{regs.index_array.count && !regs.vertex_buffer.count};
VideoCore::g_renderer->Rasterizer()->AccelerateDrawBatch(is_indexed);
rasterizer.AccelerateDrawBatch(is_indexed);
// TODO(bunnei): Below, we reset vertex count so that we can use these registers to determine if
// the game is trying to draw indexed or direct mode. This needs to be verified on HW still -
@@ -285,8 +285,6 @@ Texture::TICEntry Maxwell3D::GetTICEntry(u32 tic_index) const {
// TODO(Subv): Different data types for separate components are not supported
ASSERT(r_type == g_type && r_type == b_type && r_type == a_type);
// TODO(Subv): Only UNORM formats are supported for now.
ASSERT(r_type == Texture::ComponentType::UNORM);
return tic_entry;
}
@@ -393,7 +391,7 @@ void Maxwell3D::ProcessClearBuffers() {
regs.clear_buffers.R == regs.clear_buffers.B &&
regs.clear_buffers.R == regs.clear_buffers.A);
VideoCore::g_renderer->Rasterizer()->Clear();
rasterizer.Clear();
}
} // namespace Engines

View File

@@ -17,6 +17,10 @@
#include "video_core/memory_manager.h"
#include "video_core/textures/texture.h"
namespace VideoCore {
class RasterizerInterface;
}
namespace Tegra::Engines {
#define MAXWELL3D_REG_INDEX(field_name) \
@@ -24,7 +28,7 @@ namespace Tegra::Engines {
class Maxwell3D final {
public:
explicit Maxwell3D(MemoryManager& memory_manager);
explicit Maxwell3D(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager);
~Maxwell3D() = default;
/// Register structure of the Maxwell3D engine.
@@ -818,6 +822,8 @@ public:
Texture::FullTextureInfo GetStageTexture(Regs::ShaderStage stage, size_t offset) const;
private:
VideoCore::RasterizerInterface& rasterizer;
std::unordered_map<u32, std::vector<u32>> uploaded_macros;
/// Macro method that is currently being executed / being fed parameters.

View File

@@ -7,12 +7,13 @@
#include "video_core/engines/maxwell_compute.h"
#include "video_core/engines/maxwell_dma.h"
#include "video_core/gpu.h"
#include "video_core/rasterizer_interface.h"
namespace Tegra {
GPU::GPU() {
GPU::GPU(VideoCore::RasterizerInterface& rasterizer) {
memory_manager = std::make_unique<MemoryManager>();
maxwell_3d = std::make_unique<Engines::Maxwell3D>(*memory_manager);
maxwell_3d = std::make_unique<Engines::Maxwell3D>(rasterizer, *memory_manager);
fermi_2d = std::make_unique<Engines::Fermi2D>(*memory_manager);
maxwell_compute = std::make_unique<Engines::MaxwellCompute>();
maxwell_dma = std::make_unique<Engines::MaxwellDMA>(*memory_manager);

View File

@@ -11,6 +11,10 @@
#include "core/hle/service/nvflinger/buffer_queue.h"
#include "video_core/memory_manager.h"
namespace VideoCore {
class RasterizerInterface;
}
namespace Tegra {
enum class RenderTargetFormat : u32 {
@@ -98,7 +102,7 @@ enum class EngineID {
class GPU final {
public:
GPU();
explicit GPU(VideoCore::RasterizerInterface& rasterizer);
~GPU();
/// Processes a command list stored at the specified address in GPU memory.

View File

@@ -7,6 +7,8 @@
#include "video_core/renderer_base.h"
#include "video_core/renderer_opengl/gl_rasterizer.h"
namespace VideoCore {
RendererBase::RendererBase(EmuWindow& window) : render_window{window} {}
RendererBase::~RendererBase() = default;
@@ -21,3 +23,5 @@ void RendererBase::RefreshRasterizerSetting() {
rasterizer = std::make_unique<RasterizerOpenGL>(render_window);
}
}
} // namespace VideoCore

View File

@@ -13,6 +13,8 @@
class EmuWindow;
namespace VideoCore {
class RendererBase : NonCopyable {
public:
/// Used to reference a framebuffer
@@ -44,15 +46,21 @@ public:
return m_current_frame;
}
VideoCore::RasterizerInterface* Rasterizer() const {
return rasterizer.get();
RasterizerInterface& Rasterizer() {
return *rasterizer;
}
const RasterizerInterface& Rasterizer() const {
return *rasterizer;
}
void RefreshRasterizerSetting();
protected:
EmuWindow& render_window; ///< Reference to the render window handle.
std::unique_ptr<VideoCore::RasterizerInterface> rasterizer;
std::unique_ptr<RasterizerInterface> rasterizer;
f32 m_current_fps = 0.0f; ///< Current framerate, should be set by the renderer
int m_current_frame = 0; ///< Current frame, should be set by the renderer
};
} // namespace VideoCore

View File

@@ -169,8 +169,14 @@ std::pair<u8*, GLintptr> RasterizerOpenGL::SetupVertexArrays(u8* array_ptr,
ASSERT(buffer.IsEnabled());
glEnableVertexAttribArray(index);
glVertexAttribFormat(index, attrib.ComponentCount(), MaxwellToGL::VertexType(attrib),
attrib.IsNormalized() ? GL_TRUE : GL_FALSE, attrib.offset);
if (attrib.type == Tegra::Engines::Maxwell3D::Regs::VertexAttribute::Type::SignedInt ||
attrib.type == Tegra::Engines::Maxwell3D::Regs::VertexAttribute::Type::UnsignedInt) {
glVertexAttribIFormat(index, attrib.ComponentCount(), MaxwellToGL::VertexType(attrib),
attrib.offset);
} else {
glVertexAttribFormat(index, attrib.ComponentCount(), MaxwellToGL::VertexType(attrib),
attrib.IsNormalized() ? GL_TRUE : GL_FALSE, attrib.offset);
}
glVertexAttribBinding(index, attrib.buffer);
}

View File

@@ -46,6 +46,8 @@ struct FormatTuple {
params.height = Common::AlignUp(config.tic.Height(), GetCompressionFactor(params.pixel_format));
params.unaligned_height = config.tic.Height();
params.size_in_bytes = params.SizeInBytes();
params.cache_width = Common::AlignUp(params.width, 16);
params.cache_height = Common::AlignUp(params.height, 16);
return params;
}
@@ -63,6 +65,8 @@ struct FormatTuple {
params.height = config.height;
params.unaligned_height = config.height;
params.size_in_bytes = params.SizeInBytes();
params.cache_width = Common::AlignUp(params.width, 16);
params.cache_height = Common::AlignUp(params.height, 16);
return params;
}
@@ -82,6 +86,8 @@ struct FormatTuple {
params.height = zeta_height;
params.unaligned_height = zeta_height;
params.size_in_bytes = params.SizeInBytes();
params.cache_width = Common::AlignUp(params.width, 16);
params.cache_height = Common::AlignUp(params.height, 16);
return params;
}
@@ -680,12 +686,12 @@ Surface RasterizerCacheOpenGL::GetSurface(const SurfaceParams& params) {
// If use_accurate_framebuffers is enabled, always load from memory
FlushSurface(surface);
UnregisterSurface(surface);
} else if (surface->GetSurfaceParams() != params) {
// If surface parameters changed, recreate the surface from the old one
return RecreateSurface(surface, params);
} else {
} else if (surface->GetSurfaceParams().IsCompatibleSurface(params)) {
// Use the cached surface as-is
return surface;
} else {
// If surface parameters changed, recreate the surface from the old one
return RecreateSurface(surface, params);
}
}

View File

@@ -9,6 +9,7 @@
#include <memory>
#include <vector>
#include <boost/icl/interval_map.hpp>
#include "common/common_types.h"
#include "common/math_util.h"
#include "video_core/engines/maxwell_3d.h"
@@ -546,6 +547,12 @@ struct SurfaceParams {
return !operator==(other);
}
/// Checks if surfaces are compatible for caching
bool IsCompatibleSurface(const SurfaceParams& other) const {
return std::tie(pixel_format, type, cache_width, cache_height) ==
std::tie(other.pixel_format, other.type, other.cache_width, other.cache_height);
}
Tegra::GPUVAddr addr;
bool is_tiled;
u32 block_height;
@@ -556,6 +563,10 @@ struct SurfaceParams {
u32 height;
u32 unaligned_height;
size_t size_in_bytes;
// Parameters used for caching only
u32 cache_width;
u32 cache_height;
};
class CachedSurface final {

View File

@@ -766,13 +766,16 @@ private:
// goes into gpr28+0 and gpr28+1
size_t texs_offset{};
size_t src_elem{};
for (const auto& dest : {instr.gpr0.Value(), instr.gpr28.Value()}) {
size_t dest_elem{};
for (unsigned elem = 0; elem < 2; ++elem) {
if (!instr.texs.IsComponentEnabled(elem)) {
if (!instr.texs.IsComponentEnabled(src_elem++)) {
// Skip disabled components
continue;
}
regs.SetRegisterToFloat(dest, elem + texs_offset, texture, 1, 4, false, elem);
regs.SetRegisterToFloat(dest, elem + texs_offset, texture, 1, 4, false,
dest_elem++);
}
if (!instr.texs.HasTwoDestinations()) {

View File

@@ -103,7 +103,7 @@ ScopeAcquireGLContext::~ScopeAcquireGLContext() {
}
}
RendererOpenGL::RendererOpenGL(EmuWindow& window) : RendererBase{window} {}
RendererOpenGL::RendererOpenGL(EmuWindow& window) : VideoCore::RendererBase{window} {}
RendererOpenGL::~RendererOpenGL() = default;
/// Swap buffers (render frame)
@@ -160,8 +160,8 @@ void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuf
// only allows rows to have a memory alignement of 4.
ASSERT(framebuffer.stride % 4 == 0);
if (!Rasterizer()->AccelerateDisplay(framebuffer, framebuffer_addr, framebuffer.stride,
screen_info)) {
if (!rasterizer->AccelerateDisplay(framebuffer, framebuffer_addr, framebuffer.stride,
screen_info)) {
// Reset the screen info's display texture to its own permanent texture
screen_info.display_texture = screen_info.texture.resource.handle;

View File

@@ -41,7 +41,7 @@ private:
EmuWindow& emu_window;
};
class RendererOpenGL : public RendererBase {
class RendererOpenGL : public VideoCore::RendererBase {
public:
explicit RendererOpenGL(EmuWindow& window);
~RendererOpenGL() override;

View File

@@ -3,37 +3,16 @@
// Refer to the license.txt file included.
#include <memory>
#include "common/logging/log.h"
#include "video_core/renderer_base.h"
#include "video_core/renderer_opengl/renderer_opengl.h"
#include "video_core/video_core.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// Video Core namespace
namespace VideoCore {
std::unique_ptr<RendererBase> g_renderer; ///< Renderer plugin
std::atomic<bool> g_toggle_framelimit_enabled;
/// Initialize the video core
bool Init(EmuWindow& emu_window) {
g_renderer = std::make_unique<RendererOpenGL>(emu_window);
if (g_renderer->Init()) {
LOG_DEBUG(Render, "initialized OK");
} else {
LOG_CRITICAL(Render, "initialization failed !");
return false;
}
return true;
}
/// Shutdown the video core
void Shutdown() {
g_renderer.reset();
LOG_DEBUG(Render, "shutdown OK");
std::unique_ptr<RendererBase> CreateRenderer(EmuWindow& emu_window) {
return std::make_unique<RendererOpenGL>(emu_window);
}
} // namespace VideoCore

View File

@@ -8,25 +8,23 @@
#include <memory>
class EmuWindow;
class RendererBase;
////////////////////////////////////////////////////////////////////////////////////////////////////
// Video Core namespace
namespace VideoCore {
enum class Renderer { Software, OpenGL };
class RendererBase;
extern std::unique_ptr<RendererBase> g_renderer; ///< Renderer plugin
enum class Renderer { Software, OpenGL };
// TODO: Wrap these in a user settings struct along with any other graphics settings (often set from
// qt ui)
extern std::atomic<bool> g_toggle_framelimit_enabled;
/// Initialize the video core
bool Init(EmuWindow& emu_window);
/// Shutdown the video core
void Shutdown();
/**
* Creates a renderer instance.
*
* @note The returned renderer instance is simply allocated. Its Init()
* function still needs to be called to fully complete its setup.
*/
std::unique_ptr<RendererBase> CreateRenderer(EmuWindow& emu_window);
} // namespace VideoCore

View File

@@ -15,4 +15,4 @@ AboutDialog::AboutDialog(QWidget* parent) : QDialog(parent), ui(new Ui::AboutDia
Common::g_scm_desc, QString(Common::g_build_date).left(10)));
}
AboutDialog::~AboutDialog() {}
AboutDialog::~AboutDialog() = default;

View File

@@ -16,7 +16,7 @@ class AboutDialog : public QDialog {
public:
explicit AboutDialog(QWidget* parent);
~AboutDialog();
~AboutDialog() override;
private:
std::unique_ptr<Ui::AboutDialog> ui;

View File

@@ -106,7 +106,7 @@ class GRenderWindow : public QWidget, public EmuWindow {
public:
GRenderWindow(QWidget* parent, EmuThread* emu_thread);
~GRenderWindow();
~GRenderWindow() override;
// EmuWindow implementation
void SwapBuffers() override;

View File

@@ -24,7 +24,7 @@ ConfigureDebug::ConfigureDebug(QWidget* parent) : QWidget(parent), ui(new Ui::Co
});
}
ConfigureDebug::~ConfigureDebug() {}
ConfigureDebug::~ConfigureDebug() = default;
void ConfigureDebug::setConfiguration() {
ui->toggle_gdbstub->setChecked(Settings::values.use_gdbstub);

View File

@@ -22,13 +22,6 @@
<string>GDB</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QLabel" name="label_1">
<property name="text">
<string>The GDB Stub only works correctly when the CPU JIT is off.</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_1">
<item>

View File

@@ -12,7 +12,7 @@ ConfigureDialog::ConfigureDialog(QWidget* parent) : QDialog(parent), ui(new Ui::
this->setConfiguration();
}
ConfigureDialog::~ConfigureDialog() {}
ConfigureDialog::~ConfigureDialog() = default;
void ConfigureDialog::setConfiguration() {}

View File

@@ -24,7 +24,7 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent)
ui->use_docked_mode->setEnabled(!Core::System::GetInstance().IsPoweredOn());
}
ConfigureGeneral::~ConfigureGeneral() {}
ConfigureGeneral::~ConfigureGeneral() = default;
void ConfigureGeneral::setConfiguration() {
ui->toggle_deepscan->setChecked(UISettings::values.gamedir_deepscan);

View File

@@ -14,7 +14,7 @@ ConfigureGraphics::ConfigureGraphics(QWidget* parent)
this->setConfiguration();
}
ConfigureGraphics::~ConfigureGraphics() {}
ConfigureGraphics::~ConfigureGraphics() = default;
enum class Resolution : int {
Auto,

View File

@@ -35,7 +35,7 @@ ConfigureSystem::ConfigureSystem(QWidget* parent) : QWidget(parent), ui(new Ui::
this->setConfiguration();
}
ConfigureSystem::~ConfigureSystem() {}
ConfigureSystem::~ConfigureSystem() = default;
void ConfigureSystem::setConfiguration() {
enabled = !Core::System::GetInstance().IsPoweredOn();

View File

@@ -34,7 +34,8 @@ static Tegra::Texture::TextureFormat ConvertToTextureFormat(
SurfacePicture::SurfacePicture(QWidget* parent, GraphicsSurfaceWidget* surface_widget_)
: QLabel(parent), surface_widget(surface_widget_) {}
SurfacePicture::~SurfacePicture() {}
SurfacePicture::~SurfacePicture() = default;
void SurfacePicture::mousePressEvent(QMouseEvent* event) {
// Only do something while the left mouse button is held down

View File

@@ -22,11 +22,11 @@ class SurfacePicture : public QLabel {
public:
explicit SurfacePicture(QWidget* parent = nullptr,
GraphicsSurfaceWidget* surface_widget = nullptr);
~SurfacePicture();
~SurfacePicture() override;
protected slots:
virtual void mouseMoveEvent(QMouseEvent* event);
virtual void mousePressEvent(QMouseEvent* event);
void mouseMoveEvent(QMouseEvent* event) override;
void mousePressEvent(QMouseEvent* event) override;
private:
GraphicsSurfaceWidget* surface_widget;

View File

@@ -14,7 +14,7 @@
#include "core/hle/kernel/timer.h"
#include "core/hle/kernel/wait_object.h"
WaitTreeItem::~WaitTreeItem() {}
WaitTreeItem::~WaitTreeItem() = default;
QColor WaitTreeItem::GetColor() const {
return QColor(Qt::GlobalColor::black);
@@ -316,7 +316,7 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeEvent::GetChildren() const {
list.push_back(std::make_unique<WaitTreeText>(
tr("reset type = %1")
.arg(GetResetTypeQString(static_cast<const Kernel::Event&>(object).reset_type))));
.arg(GetResetTypeQString(static_cast<const Kernel::Event&>(object).GetResetType()))));
return list;
}

View File

@@ -25,11 +25,13 @@ class WaitTreeThread;
class WaitTreeItem : public QObject {
Q_OBJECT
public:
~WaitTreeItem() override;
virtual bool IsExpandable() const;
virtual std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const;
virtual QString GetText() const = 0;
virtual QColor GetColor() const;
virtual ~WaitTreeItem();
void Expand();
WaitTreeItem* Parent() const;
const std::vector<std::unique_ptr<WaitTreeItem>>& Children() const;

View File

@@ -162,15 +162,15 @@ void GameList::onTextChanged(const QString& newText) {
}
search_field->setFilterResult(rowCount, rowCount);
} else {
QStandardItem* child_file;
QString file_path, file_name, file_title, file_programmid;
int result_count = 0;
for (int i = 0; i < rowCount; ++i) {
child_file = item_model->item(i, 0);
file_path = child_file->data(GameListItemPath::FullPathRole).toString().toLower();
file_name = file_path.mid(file_path.lastIndexOf("/") + 1);
file_title = child_file->data(GameListItemPath::TitleRole).toString().toLower();
file_programmid =
const QStandardItem* child_file = item_model->item(i, 0);
const QString file_path =
child_file->data(GameListItemPath::FullPathRole).toString().toLower();
QString file_name = file_path.mid(file_path.lastIndexOf('/') + 1);
const QString file_title =
child_file->data(GameListItemPath::TitleRole).toString().toLower();
const QString file_programmid =
child_file->data(GameListItemPath::ProgramIdRole).toString().toLower();
// Only items which filename in combination with its title contains all words
@@ -258,18 +258,20 @@ void GameList::AddEntry(const QList<QStandardItem*>& entry_items) {
void GameList::ValidateEntry(const QModelIndex& item) {
// We don't care about the individual QStandardItem that was selected, but its row.
int row = item_model->itemFromIndex(item)->row();
QStandardItem* child_file = item_model->invisibleRootItem()->child(row, COLUMN_NAME);
QString file_path = child_file->data(GameListItemPath::FullPathRole).toString();
const int row = item_model->itemFromIndex(item)->row();
const QStandardItem* child_file = item_model->invisibleRootItem()->child(row, COLUMN_NAME);
const QString file_path = child_file->data(GameListItemPath::FullPathRole).toString();
if (file_path.isEmpty())
return;
std::string std_file_path(file_path.toStdString());
if (!FileUtil::Exists(std_file_path))
if (!QFileInfo::exists(file_path))
return;
if (FileUtil::IsDirectory(std_file_path)) {
QDir dir(std_file_path.c_str());
QStringList matching_main = dir.entryList(QStringList("main"), QDir::Files);
const QFileInfo file_info{file_path};
if (file_info.isDir()) {
const QDir dir{file_path};
const QStringList matching_main = dir.entryList(QStringList("main"), QDir::Files);
if (matching_main.size() == 1) {
emit GameChosen(dir.path() + DIR_SEP + matching_main[0]);
}
@@ -368,21 +370,23 @@ void GameList::LoadInterfaceLayout() {
const QStringList GameList::supported_file_extensions = {"nso", "nro", "nca", "xci"};
static bool HasSupportedFileExtension(const std::string& file_name) {
QFileInfo file = QFileInfo(file_name.c_str());
const QFileInfo file = QFileInfo(QString::fromStdString(file_name));
return GameList::supported_file_extensions.contains(file.suffix(), Qt::CaseInsensitive);
}
static bool IsExtractedNCAMain(const std::string& file_name) {
return QFileInfo(file_name.c_str()).fileName() == "main";
return QFileInfo(QString::fromStdString(file_name)).fileName() == "main";
}
static QString FormatGameName(const std::string& physical_name) {
QFileInfo file_info(physical_name.c_str());
const QString physical_name_as_qstring = QString::fromStdString(physical_name);
const QFileInfo file_info(physical_name_as_qstring);
if (IsExtractedNCAMain(physical_name)) {
return file_info.dir().path();
} else {
return QString::fromStdString(physical_name);
}
return physical_name_as_qstring;
}
void GameList::RefreshGameDirectory() {

View File

@@ -5,6 +5,7 @@
#pragma once
#include <atomic>
#include <utility>
#include <QImage>
#include <QRunnable>
#include <QStandardItem>
@@ -27,9 +28,8 @@ static QPixmap GetDefaultIcon(bool large) {
class GameListItem : public QStandardItem {
public:
GameListItem() : QStandardItem() {}
GameListItem(const QString& string) : QStandardItem(string) {}
virtual ~GameListItem() override {}
GameListItem() = default;
explicit GameListItem(const QString& string) : QStandardItem(string) {}
};
/**
@@ -45,9 +45,8 @@ public:
static const int TitleRole = Qt::UserRole + 2;
static const int ProgramIdRole = Qt::UserRole + 3;
GameListItemPath() : GameListItem() {}
GameListItemPath(const QString& game_path, const std::vector<u8>& smdh_data, u64 program_id)
: GameListItem() {
GameListItemPath() = default;
GameListItemPath(const QString& game_path, const std::vector<u8>& smdh_data, u64 program_id) {
setData(game_path, FullPathRole);
setData(qulonglong(program_id), ProgramIdRole);
}
@@ -75,8 +74,8 @@ class GameListItemSize : public GameListItem {
public:
static const int SizeRole = Qt::UserRole + 1;
GameListItemSize() : GameListItem() {}
GameListItemSize(const qulonglong size_bytes) : GameListItem() {
GameListItemSize() = default;
explicit GameListItemSize(const qulonglong size_bytes) {
setData(size_bytes, SizeRole);
}
@@ -111,7 +110,7 @@ class GameListWorker : public QObject, public QRunnable {
public:
GameListWorker(QString dir_path, bool deep_scan)
: QObject(), QRunnable(), dir_path(dir_path), deep_scan(deep_scan) {}
: dir_path(std::move(dir_path)), deep_scan(deep_scan) {}
public slots:
/// Starts the processing of directory tree information.

View File

@@ -81,6 +81,8 @@ static void ShowCalloutMessage(const QString& message, CalloutFlag flag) {
void GMainWindow::ShowCallouts() {}
const int GMainWindow::max_recent_files_item;
GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) {
debug_context = Tegra::DebugContext::Construct();
@@ -477,7 +479,7 @@ bool GMainWindow::LoadROM(const QString& filename) {
case Core::System::ResultStatus::ErrorVideoCore:
QMessageBox::critical(
this, tr("An error occured in the video core."),
this, tr("An error occurred initializing the video core."),
tr("yuzu has encountered an error while running the video core, please see the "
"log for more details."
"For more information on accessing the log, please see the following page: "
@@ -491,7 +493,7 @@ bool GMainWindow::LoadROM(const QString& filename) {
default:
QMessageBox::critical(
this, tr("Error while loading ROM!"),
tr("An unknown error occured. Please see the log for more details."));
tr("An unknown error occurred. Please see the log for more details."));
break;
}
return false;
@@ -579,11 +581,11 @@ void GMainWindow::StoreRecentFile(const QString& filename) {
}
void GMainWindow::UpdateRecentFiles() {
unsigned int num_recent_files =
std::min(UISettings::values.recent_files.size(), static_cast<int>(max_recent_files_item));
const int num_recent_files =
std::min(UISettings::values.recent_files.size(), max_recent_files_item);
for (unsigned int i = 0; i < num_recent_files; i++) {
QString text = QString("&%1. %2").arg(i + 1).arg(
for (int i = 0; i < num_recent_files; i++) {
const QString text = QString("&%1. %2").arg(i + 1).arg(
QFileInfo(UISettings::values.recent_files[i]).fileName());
actions_recent_files[i]->setText(text);
actions_recent_files[i]->setData(UISettings::values.recent_files[i]);
@@ -595,12 +597,8 @@ void GMainWindow::UpdateRecentFiles() {
actions_recent_files[j]->setVisible(false);
}
// Grey out the recent files menu if the list is empty
if (num_recent_files == 0) {
ui.menu_recent_files->setEnabled(false);
} else {
ui.menu_recent_files->setEnabled(true);
}
// Enable the recent files menu if the list isn't empty
ui.menu_recent_files->setEnabled(num_recent_files != 0);
}
void GMainWindow::OnGameListLoadFile(QString game_path) {
@@ -631,9 +629,15 @@ void GMainWindow::OnMenuLoadFile() {
}
void GMainWindow::OnMenuLoadFolder() {
QDir dir = QFileDialog::getExistingDirectory(this, tr("Open Extracted ROM Directory"));
const QString dir_path =
QFileDialog::getExistingDirectory(this, tr("Open Extracted ROM Directory"));
QStringList matching_main = dir.entryList(QStringList("main"), QDir::Files);
if (dir_path.isNull()) {
return;
}
const QDir dir{dir_path};
const QStringList matching_main = dir.entryList(QStringList("main"), QDir::Files);
if (matching_main.size() == 1) {
BootGame(dir.path() + DIR_SEP + matching_main[0]);
} else {
@@ -654,9 +658,8 @@ void GMainWindow::OnMenuRecentFile() {
QAction* action = qobject_cast<QAction*>(sender());
assert(action);
QString filename = action->data().toString();
QFileInfo file_info(filename);
if (file_info.exists()) {
const QString filename = action->data().toString();
if (QFileInfo::exists(filename)) {
BootGame(filename);
} else {
// Display an error message and remove the file from the list.
@@ -947,15 +950,14 @@ void GMainWindow::UpdateUITheme() {
QStringList theme_paths(default_theme_paths);
if (UISettings::values.theme != UISettings::themes[0].second &&
!UISettings::values.theme.isEmpty()) {
QString theme_uri(":" + UISettings::values.theme + "/style.qss");
const QString theme_uri(":" + UISettings::values.theme + "/style.qss");
QFile f(theme_uri);
if (!f.exists()) {
LOG_ERROR(Frontend, "Unable to set style, stylesheet file not found");
} else {
f.open(QFile::ReadOnly | QFile::Text);
if (f.open(QFile::ReadOnly | QFile::Text)) {
QTextStream ts(&f);
qApp->setStyleSheet(ts.readAll());
GMainWindow::setStyleSheet(ts.readAll());
} else {
LOG_ERROR(Frontend, "Unable to set style, stylesheet file not found");
}
theme_paths.append(QStringList{":/icons/default", ":/icons/" + UISettings::values.theme});
QIcon::setThemeName(":/icons/" + UISettings::values.theme);

View File

@@ -43,7 +43,7 @@ public:
void filterBarSetChecked(bool state);
void UpdateUITheme();
GMainWindow();
~GMainWindow();
~GMainWindow() override;
signals:

View File

@@ -193,7 +193,7 @@ int main(int argc, char** argv) {
LOG_CRITICAL(Frontend, "Failed to determine system mode!");
return -1;
case Core::System::ResultStatus::ErrorVideoCore:
LOG_CRITICAL(Frontend, "VideoCore not initialized");
LOG_CRITICAL(Frontend, "Failed to initialize VideoCore!");
return -1;
case Core::System::ResultStatus::Success:
break; // Expected case