summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/core/file_sys/control_metadata.cpp24
-rw-r--r--src/core/file_sys/registered_cache.cpp44
-rw-r--r--src/core/file_sys/registered_cache.h10
-rw-r--r--src/core/gdbstub/gdbstub.cpp2
-rw-r--r--src/core/hle/kernel/resource_limit.h6
-rw-r--r--src/core/hle/kernel/svc.cpp121
-rw-r--r--src/core/hle/kernel/svc_wrap.h8
-rw-r--r--src/core/hle/service/acc/acc.cpp17
-rw-r--r--src/core/hle/service/acc/profile_manager.cpp5
-rw-r--r--src/core/hle/service/acc/profile_manager.h14
-rw-r--r--src/core/hle/service/hid/controllers/npad.h6
-rw-r--r--src/video_core/command_processor.cpp3
-rw-r--r--src/video_core/engines/fermi_2d.cpp5
-rw-r--r--src/video_core/engines/kepler_memory.cpp3
-rw-r--r--src/video_core/engines/maxwell_3d.cpp16
-rw-r--r--src/video_core/engines/maxwell_3d.h46
-rw-r--r--src/video_core/engines/maxwell_dma.cpp5
-rw-r--r--src/video_core/morton.cpp3
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache.cpp3
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache.h2
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp66
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h3
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.cpp28
-rw-r--r--src/video_core/renderer_opengl/gl_shader_gen.cpp14
-rw-r--r--src/video_core/renderer_opengl/gl_shader_manager.cpp10
-rw-r--r--src/video_core/renderer_opengl/gl_shader_manager.h9
-rw-r--r--src/video_core/renderer_opengl/gl_state.cpp57
-rw-r--r--src/video_core/renderer_opengl/gl_state.h10
-rw-r--r--src/yuzu/configuration/configure_input.cpp44
-rw-r--r--src/yuzu/configuration/configure_input.h11
-rw-r--r--src/yuzu/configuration/configure_input_player.cpp18
-rw-r--r--src/yuzu/configuration/configure_input_player.h53
-rw-r--r--src/yuzu/configuration/configure_mouse_advanced.cpp4
-rw-r--r--src/yuzu/configuration/configure_mouse_advanced.h36
-rw-r--r--src/yuzu/main.cpp10
35 files changed, 515 insertions, 201 deletions
diff --git a/src/core/file_sys/control_metadata.cpp b/src/core/file_sys/control_metadata.cpp
index c8fa912bf..e065e592f 100644
--- a/src/core/file_sys/control_metadata.cpp
+++ b/src/core/file_sys/control_metadata.cpp
@@ -8,13 +8,23 @@
namespace FileSys {
-const std::array<const char*, 15> LANGUAGE_NAMES = {
- "AmericanEnglish", "BritishEnglish", "Japanese",
- "French", "German", "LatinAmericanSpanish",
- "Spanish", "Italian", "Dutch",
- "CanadianFrench", "Portugese", "Russian",
- "Korean", "Taiwanese", "Chinese",
-};
+const std::array<const char*, 15> LANGUAGE_NAMES{{
+ "AmericanEnglish",
+ "BritishEnglish",
+ "Japanese",
+ "French",
+ "German",
+ "LatinAmericanSpanish",
+ "Spanish",
+ "Italian",
+ "Dutch",
+ "CanadianFrench",
+ "Portuguese",
+ "Russian",
+ "Korean",
+ "Taiwanese",
+ "Chinese",
+}};
std::string LanguageEntry::GetApplicationName() const {
return Common::StringFromFixedZeroTerminatedBuffer(application_name.data(),
diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp
index a3f8f2f73..07c3af64a 100644
--- a/src/core/file_sys/registered_cache.cpp
+++ b/src/core/file_sys/registered_cache.cpp
@@ -381,22 +381,22 @@ std::vector<RegisteredCacheEntry> RegisteredCache::ListEntriesFilter(
return out;
}
-static std::shared_ptr<NCA> GetNCAFromNSPForID(std::shared_ptr<NSP> nsp, const NcaID& id) {
- const auto file = nsp->GetFile(fmt::format("{}.nca", Common::HexArrayToString(id, false)));
+static std::shared_ptr<NCA> GetNCAFromNSPForID(const NSP& nsp, const NcaID& id) {
+ const auto file = nsp.GetFile(fmt::format("{}.nca", Common::HexArrayToString(id, false)));
if (file == nullptr)
return nullptr;
return std::make_shared<NCA>(file);
}
-InstallResult RegisteredCache::InstallEntry(std::shared_ptr<XCI> xci, bool overwrite_if_exists,
+InstallResult RegisteredCache::InstallEntry(const XCI& xci, bool overwrite_if_exists,
const VfsCopyFunction& copy) {
- return InstallEntry(xci->GetSecurePartitionNSP(), overwrite_if_exists, copy);
+ return InstallEntry(*xci.GetSecurePartitionNSP(), overwrite_if_exists, copy);
}
-InstallResult RegisteredCache::InstallEntry(std::shared_ptr<NSP> nsp, bool overwrite_if_exists,
+InstallResult RegisteredCache::InstallEntry(const NSP& nsp, bool overwrite_if_exists,
const VfsCopyFunction& copy) {
- const auto& ncas = nsp->GetNCAsCollapsed();
- const auto& meta_iter = std::find_if(ncas.begin(), ncas.end(), [](std::shared_ptr<NCA> nca) {
+ const auto ncas = nsp.GetNCAsCollapsed();
+ const auto meta_iter = std::find_if(ncas.begin(), ncas.end(), [](const auto& nca) {
return nca->GetType() == NCAContentType::Meta;
});
@@ -410,7 +410,7 @@ InstallResult RegisteredCache::InstallEntry(std::shared_ptr<NSP> nsp, bool overw
const auto meta_id_raw = (*meta_iter)->GetName().substr(0, 32);
const auto meta_id = Common::HexStringToArray<16>(meta_id_raw);
- const auto res = RawInstallNCA(*meta_iter, copy, overwrite_if_exists, meta_id);
+ const auto res = RawInstallNCA(**meta_iter, copy, overwrite_if_exists, meta_id);
if (res != InstallResult::Success)
return res;
@@ -422,7 +422,7 @@ InstallResult RegisteredCache::InstallEntry(std::shared_ptr<NSP> nsp, bool overw
const auto nca = GetNCAFromNSPForID(nsp, record.nca_id);
if (nca == nullptr)
return InstallResult::ErrorCopyFailed;
- const auto res2 = RawInstallNCA(nca, copy, overwrite_if_exists, record.nca_id);
+ const auto res2 = RawInstallNCA(*nca, copy, overwrite_if_exists, record.nca_id);
if (res2 != InstallResult::Success)
return res2;
}
@@ -431,21 +431,21 @@ InstallResult RegisteredCache::InstallEntry(std::shared_ptr<NSP> nsp, bool overw
return InstallResult::Success;
}
-InstallResult RegisteredCache::InstallEntry(std::shared_ptr<NCA> nca, TitleType type,
+InstallResult RegisteredCache::InstallEntry(const NCA& nca, TitleType type,
bool overwrite_if_exists, const VfsCopyFunction& copy) {
CNMTHeader header{
- nca->GetTitleId(), ///< Title ID
- 0, ///< Ignore/Default title version
- type, ///< Type
- {}, ///< Padding
- 0x10, ///< Default table offset
- 1, ///< 1 Content Entry
- 0, ///< No Meta Entries
- {}, ///< Padding
+ nca.GetTitleId(), ///< Title ID
+ 0, ///< Ignore/Default title version
+ type, ///< Type
+ {}, ///< Padding
+ 0x10, ///< Default table offset
+ 1, ///< 1 Content Entry
+ 0, ///< No Meta Entries
+ {}, ///< Padding
};
OptionalHeader opt_header{0, 0};
- ContentRecord c_rec{{}, {}, {}, GetCRTypeFromNCAType(nca->GetType()), {}};
- const auto& data = nca->GetBaseFile()->ReadBytes(0x100000);
+ ContentRecord c_rec{{}, {}, {}, GetCRTypeFromNCAType(nca.GetType()), {}};
+ const auto& data = nca.GetBaseFile()->ReadBytes(0x100000);
mbedtls_sha256(data.data(), data.size(), c_rec.hash.data(), 0);
memcpy(&c_rec.nca_id, &c_rec.hash, 16);
const CNMT new_cnmt(header, opt_header, {c_rec}, {});
@@ -454,10 +454,10 @@ InstallResult RegisteredCache::InstallEntry(std::shared_ptr<NCA> nca, TitleType
return RawInstallNCA(nca, copy, overwrite_if_exists, c_rec.nca_id);
}
-InstallResult RegisteredCache::RawInstallNCA(std::shared_ptr<NCA> nca, const VfsCopyFunction& copy,
+InstallResult RegisteredCache::RawInstallNCA(const NCA& nca, const VfsCopyFunction& copy,
bool overwrite_if_exists,
std::optional<NcaID> override_id) {
- const auto in = nca->GetBaseFile();
+ const auto in = nca.GetBaseFile();
Core::Crypto::SHA256Hash hash{};
// Calculate NcaID
diff --git a/src/core/file_sys/registered_cache.h b/src/core/file_sys/registered_cache.h
index 6b89db8de..3b77af4e0 100644
--- a/src/core/file_sys/registered_cache.h
+++ b/src/core/file_sys/registered_cache.h
@@ -6,7 +6,6 @@
#include <array>
#include <functional>
-#include <map>
#include <memory>
#include <string>
#include <vector>
@@ -104,17 +103,16 @@ public:
// Raw copies all the ncas from the xci/nsp to the csache. Does some quick checks to make sure
// there is a meta NCA and all of them are accessible.
- InstallResult InstallEntry(std::shared_ptr<XCI> xci, bool overwrite_if_exists = false,
+ InstallResult InstallEntry(const XCI& xci, bool overwrite_if_exists = false,
const VfsCopyFunction& copy = &VfsRawCopy);
- InstallResult InstallEntry(std::shared_ptr<NSP> nsp, bool overwrite_if_exists = false,
+ InstallResult InstallEntry(const NSP& nsp, bool overwrite_if_exists = false,
const VfsCopyFunction& copy = &VfsRawCopy);
// Due to the fact that we must use Meta-type NCAs to determine the existance of files, this
// poses quite a challenge. Instead of creating a new meta NCA for this file, yuzu will create a
// dir inside the NAND called 'yuzu_meta' and store the raw CNMT there.
// TODO(DarkLordZach): Author real meta-type NCAs and install those.
- InstallResult InstallEntry(std::shared_ptr<NCA> nca, TitleType type,
- bool overwrite_if_exists = false,
+ InstallResult InstallEntry(const NCA& nca, TitleType type, bool overwrite_if_exists = false,
const VfsCopyFunction& copy = &VfsRawCopy);
private:
@@ -128,7 +126,7 @@ private:
std::optional<NcaID> GetNcaIDFromMetadata(u64 title_id, ContentRecordType type) const;
VirtualFile GetFileAtID(NcaID id) const;
VirtualFile OpenFileOrDirectoryConcat(const VirtualDir& dir, std::string_view path) const;
- InstallResult RawInstallNCA(std::shared_ptr<NCA> nca, const VfsCopyFunction& copy,
+ InstallResult RawInstallNCA(const NCA& nca, const VfsCopyFunction& copy,
bool overwrite_if_exists, std::optional<NcaID> override_id = {});
bool RawInstallYuzuMeta(const CNMT& cnmt);
diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp
index 687dea409..e6b5171ee 100644
--- a/src/core/gdbstub/gdbstub.cpp
+++ b/src/core/gdbstub/gdbstub.cpp
@@ -282,7 +282,7 @@ static void FpuWrite(std::size_t id, u128 val, Kernel::Thread* thread = nullptr)
if (id >= UC_ARM64_REG_Q0 && id < FPCR_REGISTER) {
thread_context.vector_registers[id - UC_ARM64_REG_Q0] = val;
} else if (id == FPCR_REGISTER) {
- thread_context.fpcr = val[0];
+ thread_context.fpcr = static_cast<u32>(val[0]);
}
}
diff --git a/src/core/hle/kernel/resource_limit.h b/src/core/hle/kernel/resource_limit.h
index bec065543..59dc11c22 100644
--- a/src/core/hle/kernel/resource_limit.h
+++ b/src/core/hle/kernel/resource_limit.h
@@ -14,7 +14,7 @@ namespace Kernel {
class KernelCore;
-enum class ResourceType {
+enum class ResourceType : u32 {
PhysicalMemory,
Threads,
Events,
@@ -25,6 +25,10 @@ enum class ResourceType {
ResourceTypeCount
};
+constexpr bool IsValidResourceType(ResourceType type) {
+ return type < ResourceType::ResourceTypeCount;
+}
+
class ResourceLimit final : public Object {
public:
/**
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index f287f7c97..1f19d5576 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -105,6 +105,38 @@ ResultCode MapUnmapMemorySanityChecks(const VMManager& vm_manager, VAddr dst_add
return RESULT_SUCCESS;
}
+
+enum class ResourceLimitValueType {
+ CurrentValue,
+ LimitValue,
+};
+
+ResultVal<s64> RetrieveResourceLimitValue(Handle resource_limit, u32 resource_type,
+ ResourceLimitValueType value_type) {
+ const auto type = static_cast<ResourceType>(resource_type);
+ if (!IsValidResourceType(type)) {
+ LOG_ERROR(Kernel_SVC, "Invalid resource limit type: '{}'", resource_type);
+ return ERR_INVALID_ENUM_VALUE;
+ }
+
+ const auto& kernel = Core::System::GetInstance().Kernel();
+ const auto* const current_process = kernel.CurrentProcess();
+ ASSERT(current_process != nullptr);
+
+ const auto resource_limit_object =
+ current_process->GetHandleTable().Get<ResourceLimit>(resource_limit);
+ if (!resource_limit_object) {
+ LOG_ERROR(Kernel_SVC, "Handle to non-existent resource limit instance used. Handle={:08X}",
+ resource_limit);
+ return ERR_INVALID_HANDLE;
+ }
+
+ if (value_type == ResourceLimitValueType::CurrentValue) {
+ return MakeResult(resource_limit_object->GetCurrentResourceValue(type));
+ }
+
+ return MakeResult(resource_limit_object->GetMaxResourceValue(type));
+}
} // Anonymous namespace
/// Set the process heap to a given Size. It can both extend and shrink the heap.
@@ -1346,6 +1378,87 @@ static ResultCode GetProcessInfo(u64* out, Handle process_handle, u32 type) {
return RESULT_SUCCESS;
}
+static ResultCode CreateResourceLimit(Handle* out_handle) {
+ LOG_DEBUG(Kernel_SVC, "called");
+
+ auto& kernel = Core::System::GetInstance().Kernel();
+ auto resource_limit = ResourceLimit::Create(kernel);
+
+ auto* const current_process = kernel.CurrentProcess();
+ ASSERT(current_process != nullptr);
+
+ const auto handle = current_process->GetHandleTable().Create(std::move(resource_limit));
+ if (handle.Failed()) {
+ return handle.Code();
+ }
+
+ *out_handle = *handle;
+ return RESULT_SUCCESS;
+}
+
+static ResultCode GetResourceLimitLimitValue(u64* out_value, Handle resource_limit,
+ u32 resource_type) {
+ LOG_DEBUG(Kernel_SVC, "called. Handle={:08X}, Resource type={}", resource_limit, resource_type);
+
+ const auto limit_value = RetrieveResourceLimitValue(resource_limit, resource_type,
+ ResourceLimitValueType::LimitValue);
+ if (limit_value.Failed()) {
+ return limit_value.Code();
+ }
+
+ *out_value = static_cast<u64>(*limit_value);
+ return RESULT_SUCCESS;
+}
+
+static ResultCode GetResourceLimitCurrentValue(u64* out_value, Handle resource_limit,
+ u32 resource_type) {
+ LOG_DEBUG(Kernel_SVC, "called. Handle={:08X}, Resource type={}", resource_limit, resource_type);
+
+ const auto current_value = RetrieveResourceLimitValue(resource_limit, resource_type,
+ ResourceLimitValueType::CurrentValue);
+ if (current_value.Failed()) {
+ return current_value.Code();
+ }
+
+ *out_value = static_cast<u64>(*current_value);
+ return RESULT_SUCCESS;
+}
+
+static ResultCode SetResourceLimitLimitValue(Handle resource_limit, u32 resource_type, u64 value) {
+ LOG_DEBUG(Kernel_SVC, "called. Handle={:08X}, Resource type={}, Value={}", resource_limit,
+ resource_type, value);
+
+ const auto type = static_cast<ResourceType>(resource_type);
+ if (!IsValidResourceType(type)) {
+ LOG_ERROR(Kernel_SVC, "Invalid resource limit type: '{}'", resource_type);
+ return ERR_INVALID_ENUM_VALUE;
+ }
+
+ auto& kernel = Core::System::GetInstance().Kernel();
+ auto* const current_process = kernel.CurrentProcess();
+ ASSERT(current_process != nullptr);
+
+ auto resource_limit_object =
+ current_process->GetHandleTable().Get<ResourceLimit>(resource_limit);
+ if (!resource_limit_object) {
+ LOG_ERROR(Kernel_SVC, "Handle to non-existent resource limit instance used. Handle={:08X}",
+ resource_limit);
+ return ERR_INVALID_HANDLE;
+ }
+
+ const auto set_result = resource_limit_object->SetLimitValue(type, static_cast<s64>(value));
+ if (set_result.IsError()) {
+ LOG_ERROR(
+ Kernel_SVC,
+ "Attempted to lower resource limit ({}) for category '{}' below its current value ({})",
+ resource_limit_object->GetMaxResourceValue(type), resource_type,
+ resource_limit_object->GetCurrentResourceValue(type));
+ return set_result;
+ }
+
+ return RESULT_SUCCESS;
+}
+
namespace {
struct FunctionDef {
using Func = void();
@@ -1405,8 +1518,8 @@ static const FunctionDef SVC_Table[] = {
{0x2D, nullptr, "UnmapPhysicalMemory"},
{0x2E, nullptr, "GetFutureThreadInfo"},
{0x2F, nullptr, "GetLastThreadInfo"},
- {0x30, nullptr, "GetResourceLimitLimitValue"},
- {0x31, nullptr, "GetResourceLimitCurrentValue"},
+ {0x30, SvcWrap<GetResourceLimitLimitValue>, "GetResourceLimitLimitValue"},
+ {0x31, SvcWrap<GetResourceLimitCurrentValue>, "GetResourceLimitCurrentValue"},
{0x32, SvcWrap<SetThreadActivity>, "SetThreadActivity"},
{0x33, SvcWrap<GetThreadContext>, "GetThreadContext"},
{0x34, SvcWrap<WaitForAddress>, "WaitForAddress"},
@@ -1482,8 +1595,8 @@ static const FunctionDef SVC_Table[] = {
{0x7A, nullptr, "StartProcess"},
{0x7B, nullptr, "TerminateProcess"},
{0x7C, SvcWrap<GetProcessInfo>, "GetProcessInfo"},
- {0x7D, nullptr, "CreateResourceLimit"},
- {0x7E, nullptr, "SetResourceLimitLimitValue"},
+ {0x7D, SvcWrap<CreateResourceLimit>, "CreateResourceLimit"},
+ {0x7E, SvcWrap<SetResourceLimitLimitValue>, "SetResourceLimitLimitValue"},
{0x7F, nullptr, "CallSecureMonitor"},
};
diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h
index 233a99fb0..fa1116624 100644
--- a/src/core/hle/kernel/svc_wrap.h
+++ b/src/core/hle/kernel/svc_wrap.h
@@ -43,6 +43,14 @@ void SvcWrap() {
FuncReturn(func(static_cast<u32>(Param(0)), static_cast<u32>(Param(1))).raw);
}
+template <ResultCode func(u32*)>
+void SvcWrap() {
+ u32 param = 0;
+ const u32 retval = func(&param).raw;
+ Core::CurrentArmInterface().SetReg(1, param);
+ FuncReturn(retval);
+}
+
template <ResultCode func(u32*, u32)>
void SvcWrap() {
u32 param_1 = 0;
diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp
index c629f9357..9521431bb 100644
--- a/src/core/hle/service/acc/acc.cpp
+++ b/src/core/hle/service/acc/acc.cpp
@@ -21,17 +21,6 @@
namespace Service::Account {
-// TODO: RE this structure
-struct UserData {
- INSERT_PADDING_WORDS(1);
- u32 icon_id;
- u8 bg_color_id;
- INSERT_PADDING_BYTES(0x7);
- INSERT_PADDING_BYTES(0x10);
- INSERT_PADDING_BYTES(0x60);
-};
-static_assert(sizeof(UserData) == 0x80, "UserData structure has incorrect size");
-
// Smallest JPEG https://github.com/mathiasbynens/small/blob/master/jpeg.jpg
// used as a backup should the one on disk not exist
constexpr u32 backup_jpeg_size = 107;
@@ -72,9 +61,11 @@ private:
void Get(Kernel::HLERequestContext& ctx) {
LOG_INFO(Service_ACC, "called user_id={}", user_id.Format());
ProfileBase profile_base{};
- std::array<u8, MAX_DATA> data{};
+ ProfileData data{};
if (profile_manager.GetProfileBaseAndData(user_id, profile_base, data)) {
- ctx.WriteBuffer(data);
+ std::array<u8, sizeof(ProfileData)> raw_data;
+ std::memcpy(raw_data.data(), &data, sizeof(ProfileData));
+ ctx.WriteBuffer(raw_data);
IPC::ResponseBuilder rb{ctx, 16};
rb.Push(RESULT_SUCCESS);
rb.PushRaw(profile_base);
diff --git a/src/core/hle/service/acc/profile_manager.cpp b/src/core/hle/service/acc/profile_manager.cpp
index 968263846..1316d0b07 100644
--- a/src/core/hle/service/acc/profile_manager.cpp
+++ b/src/core/hle/service/acc/profile_manager.cpp
@@ -18,7 +18,7 @@ struct UserRaw {
UUID uuid2;
u64 timestamp;
ProfileUsername username;
- INSERT_PADDING_BYTES(0x80);
+ ProfileData extra_data;
};
static_assert(sizeof(UserRaw) == 0xC8, "UserRaw has incorrect size.");
@@ -346,7 +346,7 @@ void ProfileManager::ParseUserSaveFile() {
continue;
}
- AddUser({user.uuid, user.username, user.timestamp, {}, false});
+ AddUser({user.uuid, user.username, user.timestamp, user.extra_data, false});
}
std::stable_partition(profiles.begin(), profiles.end(),
@@ -361,6 +361,7 @@ void ProfileManager::WriteUserSaveFile() {
raw.users[i].uuid2 = profiles[i].user_uuid;
raw.users[i].uuid = profiles[i].user_uuid;
raw.users[i].timestamp = profiles[i].creation_time;
+ raw.users[i].extra_data = profiles[i].data;
}
const auto raw_path =
diff --git a/src/core/hle/service/acc/profile_manager.h b/src/core/hle/service/acc/profile_manager.h
index d2d8e6c6b..c4ce2e0b3 100644
--- a/src/core/hle/service/acc/profile_manager.h
+++ b/src/core/hle/service/acc/profile_manager.h
@@ -13,7 +13,6 @@
namespace Service::Account {
constexpr std::size_t MAX_USERS = 8;
-constexpr std::size_t MAX_DATA = 128;
constexpr u128 INVALID_UUID{{0, 0}};
struct UUID {
@@ -50,9 +49,20 @@ static_assert(sizeof(UUID) == 16, "UUID is an invalid size!");
constexpr std::size_t profile_username_size = 32;
using ProfileUsername = std::array<u8, profile_username_size>;
-using ProfileData = std::array<u8, MAX_DATA>;
using UserIDArray = std::array<UUID, MAX_USERS>;
+/// Contains extra data related to a user.
+/// TODO: RE this structure
+struct ProfileData {
+ INSERT_PADDING_WORDS(1);
+ u32 icon_id;
+ u8 bg_color_id;
+ INSERT_PADDING_BYTES(0x7);
+ INSERT_PADDING_BYTES(0x10);
+ INSERT_PADDING_BYTES(0x60);
+};
+static_assert(sizeof(ProfileData) == 0x80, "ProfileData structure has incorrect size");
+
/// This holds general information about a users profile. This is where we store all the information
/// based on a specific user
struct ProfileInfo {
diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h
index ea8057b80..abff6544d 100644
--- a/src/core/hle/service/hid/controllers/npad.h
+++ b/src/core/hle/service/hid/controllers/npad.h
@@ -80,9 +80,9 @@ public:
struct LedPattern {
explicit LedPattern(u64 light1, u64 light2, u64 light3, u64 light4) {
position1.Assign(light1);
- position1.Assign(light2);
- position1.Assign(light3);
- position1.Assign(light4);
+ position2.Assign(light2);
+ position3.Assign(light3);
+ position4.Assign(light4);
}
union {
u64 raw{};
diff --git a/src/video_core/command_processor.cpp b/src/video_core/command_processor.cpp
index 28e8c13aa..8b9c548cc 100644
--- a/src/video_core/command_processor.cpp
+++ b/src/video_core/command_processor.cpp
@@ -34,6 +34,9 @@ MICROPROFILE_DEFINE(ProcessCommandLists, "GPU", "Execute command buffer", MP_RGB
void GPU::ProcessCommandLists(const std::vector<CommandListHeader>& commands) {
MICROPROFILE_SCOPE(ProcessCommandLists);
+ // On entering GPU code, assume all memory may be touched by the ARM core.
+ maxwell_3d->dirty_flags.OnMemoryWrite();
+
auto WriteReg = [this](u32 method, u32 subchannel, u32 value, u32 remaining_params) {
LOG_TRACE(HW_GPU,
"Processing method {:08X} on subchannel {} value "
diff --git a/src/video_core/engines/fermi_2d.cpp b/src/video_core/engines/fermi_2d.cpp
index ee8ebf678..e7721a2be 100644
--- a/src/video_core/engines/fermi_2d.cpp
+++ b/src/video_core/engines/fermi_2d.cpp
@@ -2,8 +2,10 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include "core/core.h"
#include "core/memory.h"
#include "video_core/engines/fermi_2d.h"
+#include "video_core/engines/maxwell_3d.h"
#include "video_core/rasterizer_interface.h"
#include "video_core/textures/decoders.h"
@@ -47,6 +49,9 @@ void Fermi2D::HandleSurfaceCopy() {
u32 dst_bytes_per_pixel = RenderTargetBytesPerPixel(regs.dst.format);
if (!rasterizer.AccelerateSurfaceCopy(regs.src, regs.dst)) {
+ // All copies here update the main memory, so mark all rasterizer states as invalid.
+ Core::System::GetInstance().GPU().Maxwell3D().dirty_flags.OnMemoryWrite();
+
rasterizer.FlushRegion(source_cpu, src_bytes_per_pixel * regs.src.width * regs.src.height);
// 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
diff --git a/src/video_core/engines/kepler_memory.cpp b/src/video_core/engines/kepler_memory.cpp
index 585290d9f..2adbc9eaf 100644
--- a/src/video_core/engines/kepler_memory.cpp
+++ b/src/video_core/engines/kepler_memory.cpp
@@ -3,8 +3,10 @@
// Refer to the license.txt file included.
#include "common/logging/log.h"
+#include "core/core.h"
#include "core/memory.h"
#include "video_core/engines/kepler_memory.h"
+#include "video_core/engines/maxwell_3d.h"
#include "video_core/rasterizer_interface.h"
namespace Tegra::Engines {
@@ -47,6 +49,7 @@ void KeplerMemory::ProcessData(u32 data) {
rasterizer.InvalidateRegion(dest_address, sizeof(u32));
Memory::Write32(dest_address, data);
+ Core::System::GetInstance().GPU().Maxwell3D().dirty_flags.OnMemoryWrite();
state.write_offset++;
}
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index 2bc534be3..f0a5470b9 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -135,10 +135,24 @@ void Maxwell3D::WriteReg(u32 method, u32 value, u32 remaining_params) {
if (regs.reg_array[method] != value) {
regs.reg_array[method] = value;
+ // Vertex format
if (method >= MAXWELL3D_REG_INDEX(vertex_attrib_format) &&
method < MAXWELL3D_REG_INDEX(vertex_attrib_format) + regs.vertex_attrib_format.size()) {
dirty_flags.vertex_attrib_format = true;
}
+
+ // Vertex buffer
+ if (method >= MAXWELL3D_REG_INDEX(vertex_array) &&
+ method < MAXWELL3D_REG_INDEX(vertex_array) + 4 * 32) {
+ dirty_flags.vertex_array |= 1u << ((method - MAXWELL3D_REG_INDEX(vertex_array)) >> 2);
+ } else if (method >= MAXWELL3D_REG_INDEX(vertex_array_limit) &&
+ method < MAXWELL3D_REG_INDEX(vertex_array_limit) + 2 * 32) {
+ dirty_flags.vertex_array |=
+ 1u << ((method - MAXWELL3D_REG_INDEX(vertex_array_limit)) >> 1);
+ } else if (method >= MAXWELL3D_REG_INDEX(instanced_arrays) &&
+ method < MAXWELL3D_REG_INDEX(instanced_arrays) + 32) {
+ dirty_flags.vertex_array |= 1u << (method - MAXWELL3D_REG_INDEX(instanced_arrays));
+ }
}
switch (method) {
@@ -270,6 +284,7 @@ void Maxwell3D::ProcessQueryGet() {
query_result.timestamp = CoreTiming::GetTicks();
Memory::WriteBlock(*address, &query_result, sizeof(query_result));
}
+ dirty_flags.OnMemoryWrite();
break;
}
default:
@@ -346,6 +361,7 @@ void Maxwell3D::ProcessCBData(u32 value) {
memory_manager.GpuToCpuAddress(buffer_address + regs.const_buffer.cb_pos);
Memory::Write32(*address, value);
+ dirty_flags.OnMemoryWrite();
// Increment the current buffer position.
regs.const_buffer.cb_pos = regs.const_buffer.cb_pos + 4;
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index 4f137e693..9324d9710 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -590,10 +590,18 @@ public:
float clear_color[4];
float clear_depth;
+
INSERT_PADDING_WORDS(0x3);
+
s32 clear_stencil;
- INSERT_PADDING_WORDS(0x17);
+ INSERT_PADDING_WORDS(0x7);
+
+ u32 polygon_offset_point_enable;
+ u32 polygon_offset_line_enable;
+ u32 polygon_offset_fill_enable;
+
+ INSERT_PADDING_WORDS(0xD);
std::array<ScissorTest, NumViewports> scissor_test;
@@ -728,6 +736,7 @@ public:
u32 frag_color_clamp;
union {
+ BitField<0, 1, u32> y_negate;
BitField<4, 1, u32> triangle_rast_flip;
} screen_y_control;
@@ -761,7 +770,11 @@ public:
}
} tsc;
- INSERT_PADDING_WORDS(0x3);
+ INSERT_PADDING_WORDS(0x1);
+
+ float polygon_offset_factor;
+
+ INSERT_PADDING_WORDS(0x1);
struct {
u32 tic_address_high;
@@ -786,7 +799,9 @@ public:
u32 framebuffer_srgb;
- INSERT_PADDING_WORDS(0x12);
+ float polygon_offset_units;
+
+ INSERT_PADDING_WORDS(0x11);
union {
BitField<2, 1, u32> coord_origin;
@@ -863,7 +878,9 @@ public:
INSERT_PADDING_WORDS(0x7);
- INSERT_PADDING_WORDS(0x20);
+ INSERT_PADDING_WORDS(0x1F);
+
+ float polygon_offset_clamp;
struct {
u32 is_instanced[NumVertexArrays];
@@ -879,7 +896,13 @@ public:
Cull cull;
- INSERT_PADDING_WORDS(0x28);
+ u32 pixel_center_integer;
+
+ INSERT_PADDING_WORDS(0x1);
+
+ u32 viewport_transform_enabled;
+
+ INSERT_PADDING_WORDS(0x25);
struct {
u32 enable;
@@ -1044,6 +1067,11 @@ public:
struct DirtyFlags {
bool vertex_attrib_format = true;
+ u32 vertex_array = 0xFFFFFFFF;
+
+ void OnMemoryWrite() {
+ vertex_array = 0xFFFFFFFF;
+ }
};
DirtyFlags dirty_flags;
@@ -1136,6 +1164,9 @@ ASSERT_REG_POSITION(vertex_buffer, 0x35D);
ASSERT_REG_POSITION(clear_color[0], 0x360);
ASSERT_REG_POSITION(clear_depth, 0x364);
ASSERT_REG_POSITION(clear_stencil, 0x368);
+ASSERT_REG_POSITION(polygon_offset_point_enable, 0x370);
+ASSERT_REG_POSITION(polygon_offset_line_enable, 0x371);
+ASSERT_REG_POSITION(polygon_offset_fill_enable, 0x372);
ASSERT_REG_POSITION(scissor_test, 0x380);
ASSERT_REG_POSITION(stencil_back_func_ref, 0x3D5);
ASSERT_REG_POSITION(stencil_back_mask, 0x3D6);
@@ -1174,6 +1205,7 @@ ASSERT_REG_POSITION(point_size, 0x546);
ASSERT_REG_POSITION(zeta_enable, 0x54E);
ASSERT_REG_POSITION(multisample_control, 0x54F);
ASSERT_REG_POSITION(tsc, 0x557);
+ASSERT_REG_POSITION(polygon_offset_factor, 0x55b);
ASSERT_REG_POSITION(tic, 0x55D);
ASSERT_REG_POSITION(stencil_two_side_enable, 0x565);
ASSERT_REG_POSITION(stencil_back_op_fail, 0x566);
@@ -1181,13 +1213,17 @@ ASSERT_REG_POSITION(stencil_back_op_zfail, 0x567);
ASSERT_REG_POSITION(stencil_back_op_zpass, 0x568);
ASSERT_REG_POSITION(stencil_back_func_func, 0x569);
ASSERT_REG_POSITION(framebuffer_srgb, 0x56E);
+ASSERT_REG_POSITION(polygon_offset_units, 0x56F);
ASSERT_REG_POSITION(point_coord_replace, 0x581);
ASSERT_REG_POSITION(code_address, 0x582);
ASSERT_REG_POSITION(draw, 0x585);
ASSERT_REG_POSITION(primitive_restart, 0x591);
ASSERT_REG_POSITION(index_array, 0x5F2);
+ASSERT_REG_POSITION(polygon_offset_clamp, 0x61F);
ASSERT_REG_POSITION(instanced_arrays, 0x620);
ASSERT_REG_POSITION(cull, 0x646);
+ASSERT_REG_POSITION(pixel_center_integer, 0x649);
+ASSERT_REG_POSITION(viewport_transform_enabled, 0x64B);
ASSERT_REG_POSITION(logic_op, 0x671);
ASSERT_REG_POSITION(clear_buffers, 0x674);
ASSERT_REG_POSITION(color_mask, 0x680);
diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp
index b8a78cf82..a34e884fe 100644
--- a/src/video_core/engines/maxwell_dma.cpp
+++ b/src/video_core/engines/maxwell_dma.cpp
@@ -2,7 +2,9 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include "core/core.h"
#include "core/memory.h"
+#include "video_core/engines/maxwell_3d.h"
#include "video_core/engines/maxwell_dma.h"
#include "video_core/rasterizer_interface.h"
#include "video_core/textures/decoders.h"
@@ -54,6 +56,9 @@ void MaxwellDMA::HandleCopy() {
return;
}
+ // All copies here update the main memory, so mark all rasterizer states as invalid.
+ Core::System::GetInstance().GPU().Maxwell3D().dirty_flags.OnMemoryWrite();
+
if (regs.exec.is_dst_linear && regs.exec.is_src_linear) {
// When the enable_2d bit is disabled, the copy is performed as if we were copying a 1D
// buffer of length `x_count`, otherwise we copy a 2D image of dimensions (x_count,
diff --git a/src/video_core/morton.cpp b/src/video_core/morton.cpp
index 1cf27ee25..a310491a8 100644
--- a/src/video_core/morton.cpp
+++ b/src/video_core/morton.cpp
@@ -184,13 +184,14 @@ static constexpr ConversionArray linear_to_morton_fns = {
// clang-format on
};
-constexpr MortonCopyFn GetSwizzleFunction(MortonSwizzleMode mode, Surface::PixelFormat format) {
+static MortonCopyFn GetSwizzleFunction(MortonSwizzleMode mode, Surface::PixelFormat format) {
switch (mode) {
case MortonSwizzleMode::MortonToLinear:
return morton_to_linear_fns[static_cast<std::size_t>(format)];
case MortonSwizzleMode::LinearToMorton:
return linear_to_morton_fns[static_cast<std::size_t>(format)];
}
+ UNREACHABLE();
}
/// 8x8 Z-Order coordinate from 2D coordinates
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.cpp b/src/video_core/renderer_opengl/gl_buffer_cache.cpp
index 075192c3f..46a6c0308 100644
--- a/src/video_core/renderer_opengl/gl_buffer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_buffer_cache.cpp
@@ -76,7 +76,7 @@ std::tuple<u8*, GLintptr> OGLBufferCache::ReserveMemory(std::size_t size, std::s
return std::make_tuple(uploaded_ptr, uploaded_offset);
}
-void OGLBufferCache::Map(std::size_t max_size) {
+bool OGLBufferCache::Map(std::size_t max_size) {
bool invalidate;
std::tie(buffer_ptr, buffer_offset_base, invalidate) =
stream_buffer.Map(static_cast<GLsizeiptr>(max_size), 4);
@@ -85,6 +85,7 @@ void OGLBufferCache::Map(std::size_t max_size) {
if (invalidate) {
InvalidateAll();
}
+ return invalidate;
}
void OGLBufferCache::Unmap() {
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.h b/src/video_core/renderer_opengl/gl_buffer_cache.h
index 91fca3f6c..c11acfb79 100644
--- a/src/video_core/renderer_opengl/gl_buffer_cache.h
+++ b/src/video_core/renderer_opengl/gl_buffer_cache.h
@@ -50,7 +50,7 @@ public:
/// Reserves memory to be used by host's CPU. Returns mapped address and offset.
std::tuple<u8*, GLintptr> ReserveMemory(std::size_t size, std::size_t alignment = 4);
- void Map(std::size_t max_size);
+ bool Map(std::size_t max_size);
void Unmap();
GLuint GetHandle() const;
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 630a58e49..82b7a0649 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -176,15 +176,25 @@ void RasterizerOpenGL::SetupVertexFormat() {
}
state.draw.vertex_array = VAO.handle;
state.ApplyVertexBufferState();
+
+ // Rebinding the VAO invalidates the vertex buffer bindings.
+ gpu.dirty_flags.vertex_array = 0xFFFFFFFF;
}
void RasterizerOpenGL::SetupVertexBuffer() {
- MICROPROFILE_SCOPE(OpenGL_VB);
- const auto& gpu = Core::System::GetInstance().GPU().Maxwell3D();
+ auto& gpu = Core::System::GetInstance().GPU().Maxwell3D();
const auto& regs = gpu.regs;
+ if (!gpu.dirty_flags.vertex_array)
+ return;
+
+ MICROPROFILE_SCOPE(OpenGL_VB);
+
// Upload all guest vertex arrays sequentially to our buffer
for (u32 index = 0; index < Maxwell::NumVertexArrays; ++index) {
+ if (~gpu.dirty_flags.vertex_array & (1u << index))
+ continue;
+
const auto& vertex_array = regs.vertex_array[index];
if (!vertex_array.IsEnabled())
continue;
@@ -211,6 +221,8 @@ void RasterizerOpenGL::SetupVertexBuffer() {
// Implicit set by glBindVertexBuffer. Stupid glstate handling...
state.draw.vertex_buffer = buffer_cache.GetHandle();
+
+ gpu.dirty_flags.vertex_array = 0;
}
DrawParameters RasterizerOpenGL::SetupDraw() {
@@ -600,7 +612,7 @@ void RasterizerOpenGL::DrawArrays() {
return;
MICROPROFILE_SCOPE(OpenGL_Drawing);
- const auto& gpu = Core::System::GetInstance().GPU().Maxwell3D();
+ auto& gpu = Core::System::GetInstance().GPU().Maxwell3D();
const auto& regs = gpu.regs;
ScopeAcquireGLContext acquire_context{emu_window};
@@ -620,7 +632,7 @@ void RasterizerOpenGL::DrawArrays() {
SyncTransformFeedback();
SyncPointState();
CheckAlphaTests();
-
+ SyncPolygonOffset();
// TODO(bunnei): Sync framebuffer_scale uniform here
// TODO(bunnei): Sync scissorbox uniform(s) here
@@ -653,7 +665,11 @@ void RasterizerOpenGL::DrawArrays() {
// Add space for at least 18 constant buffers
buffer_size += Maxwell::MaxConstBuffers * (MaxConstbufferSize + uniform_buffer_alignment);
- buffer_cache.Map(buffer_size);
+ bool invalidate = buffer_cache.Map(buffer_size);
+ if (invalidate) {
+ // As all cached buffers are invalidated, we need to recheck their state.
+ gpu.dirty_flags.vertex_array = 0xFFFFFFFF;
+ }
SetupVertexFormat();
SetupVertexBuffer();
@@ -969,13 +985,25 @@ u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, Shader& shader,
void RasterizerOpenGL::SyncViewport(OpenGLState& current_state) {
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
- for (std::size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumViewports; i++) {
- const MathUtil::Rectangle<s32> viewport_rect{regs.viewport_transform[i].GetRect()};
+ const bool geometry_shaders_enabled =
+ regs.IsShaderConfigEnabled(static_cast<size_t>(Maxwell::ShaderProgram::Geometry));
+ const std::size_t viewport_count =
+ geometry_shaders_enabled ? Tegra::Engines::Maxwell3D::Regs::NumViewports : 1;
+ for (std::size_t i = 0; i < viewport_count; i++) {
auto& viewport = current_state.viewports[i];
- viewport.x = viewport_rect.left;
- viewport.y = viewport_rect.bottom;
- viewport.width = viewport_rect.GetWidth();
- viewport.height = viewport_rect.GetHeight();
+ const auto& src = regs.viewports[i];
+ if (regs.viewport_transform_enabled) {
+ const MathUtil::Rectangle<s32> viewport_rect{regs.viewport_transform[i].GetRect()};
+ viewport.x = viewport_rect.left;
+ viewport.y = viewport_rect.bottom;
+ viewport.width = viewport_rect.GetWidth();
+ viewport.height = viewport_rect.GetHeight();
+ } else {
+ viewport.x = src.x;
+ viewport.y = src.y;
+ viewport.width = src.width;
+ viewport.height = src.height;
+ }
viewport.depth_range_far = regs.viewports[i].depth_range_far;
viewport.depth_range_near = regs.viewports[i].depth_range_near;
}
@@ -1149,7 +1177,11 @@ void RasterizerOpenGL::SyncLogicOpState() {
void RasterizerOpenGL::SyncScissorTest(OpenGLState& current_state) {
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
- for (std::size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumViewports; i++) {
+ const bool geometry_shaders_enabled =
+ regs.IsShaderConfigEnabled(static_cast<size_t>(Maxwell::ShaderProgram::Geometry));
+ const std::size_t viewport_count =
+ geometry_shaders_enabled ? Tegra::Engines::Maxwell3D::Regs::NumViewports : 1;
+ for (std::size_t i = 0; i < viewport_count; i++) {
const auto& src = regs.scissor_test[i];
auto& dst = current_state.viewports[i].scissor;
dst.enabled = (src.enable != 0);
@@ -1179,6 +1211,16 @@ void RasterizerOpenGL::SyncPointState() {
state.point.size = regs.point_size;
}
+void RasterizerOpenGL::SyncPolygonOffset() {
+ const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
+ state.polygon_offset.fill_enable = regs.polygon_offset_fill_enable != 0;
+ state.polygon_offset.line_enable = regs.polygon_offset_line_enable != 0;
+ state.polygon_offset.point_enable = regs.polygon_offset_point_enable != 0;
+ state.polygon_offset.units = regs.polygon_offset_units;
+ state.polygon_offset.factor = regs.polygon_offset_factor;
+ state.polygon_offset.clamp = regs.polygon_offset_clamp;
+}
+
void RasterizerOpenGL::CheckAlphaTests() {
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index f4354289c..dfb4616f2 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -183,6 +183,9 @@ private:
/// Syncs Color Mask
void SyncColorMask();
+ /// Syncs the polygon offsets
+ void SyncPolygonOffset();
+
/// Check asserts for alpha testing.
void CheckAlphaTests();
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index 05fe2d370..0c4524d5c 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -520,7 +520,7 @@ public:
switch (attribute) {
case Attribute::Index::ClipDistances0123:
case Attribute::Index::ClipDistances4567: {
- const u64 index = attribute == Attribute::Index::ClipDistances4567 ? 4 : 0 + elem;
+ const u64 index = (attribute == Attribute::Index::ClipDistances4567 ? 4 : 0) + elem;
UNIMPLEMENTED_IF_MSG(
((header.vtg.clip_distances >> index) & 1) == 0,
"Shader is setting gl_ClipDistance{} without enabling it in the header", index);
@@ -867,7 +867,8 @@ private:
// vertex shader, and what's the value of the fourth element when inside a Tess Eval
// shader.
ASSERT(stage == Maxwell3D::Regs::ShaderStage::Vertex);
- return "vec4(0, 0, uintBitsToFloat(instance_id.x), uintBitsToFloat(gl_VertexID))";
+ // Config pack's first value is instance_id.
+ return "vec4(0, 0, uintBitsToFloat(config_pack[0]), uintBitsToFloat(gl_VertexID))";
case Attribute::Index::FrontFacing:
// TODO(Subv): Find out what the values are for the other elements.
ASSERT(stage == Maxwell3D::Regs::ShaderStage::Fragment);
@@ -3400,6 +3401,10 @@ private:
regs.SetRegisterToInteger(instr.gpr0, false, 0, predicate + " ? 0xFFFFFFFF : 0", 1,
1);
}
+ if (instr.generates_cc.Value() != 0) {
+ regs.SetInternalFlag(InternalFlag::ZeroFlag, predicate);
+ LOG_WARNING(HW_GPU, "FSET Condition Code is incomplete");
+ }
break;
}
case OpCode::Type::IntegerSet: {
@@ -3653,6 +3658,11 @@ private:
regs.SetRegisterToInteger(instr.gpr0, false, 0, "0u", 1, 1);
break;
}
+ case Tegra::Shader::SystemVariable::Ydirection: {
+ // Config pack's third value is Y_NEGATE's state.
+ regs.SetRegisterToFloat(instr.gpr0, 0, "uintBitsToFloat(config_pack[2])", 1, 1);
+ break;
+ }
default: {
UNIMPLEMENTED_MSG("Unhandled system move: {}",
static_cast<u32>(instr.sys20.Value()));
@@ -3676,11 +3686,17 @@ private:
"BRA with constant buffers are not implemented");
const Tegra::Shader::ConditionCode cc = instr.flow_condition_code;
- UNIMPLEMENTED_IF_MSG(cc != Tegra::Shader::ConditionCode::T,
- "BRA condition code used: {}", static_cast<u32>(cc));
-
const u32 target = offset + instr.bra.GetBranchTarget();
- shader.AddLine("{ jmp_to = " + std::to_string(target) + "u; break; }");
+ if (cc != Tegra::Shader::ConditionCode::T) {
+ const std::string condition_code = regs.GetConditionCode(cc);
+ shader.AddLine("if (" + condition_code + "){");
+ shader.scope++;
+ shader.AddLine("{ jmp_to = " + std::to_string(target) + "u; break; }");
+ shader.scope--;
+ shader.AddLine('}');
+ } else {
+ shader.AddLine("{ jmp_to = " + std::to_string(target) + "u; break; }");
+ }
break;
}
case OpCode::Id::IPA: {
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp
index eea090e52..23ed91e27 100644
--- a/src/video_core/renderer_opengl/gl_shader_gen.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp
@@ -24,8 +24,7 @@ layout (location = 0) out vec4 position;
layout(std140) uniform vs_config {
vec4 viewport_flip;
- uvec4 instance_id;
- uvec4 flip_stage;
+ uvec4 config_pack; // instance_id, flip_stage, y_direction, padding
uvec4 alpha_test;
};
)";
@@ -63,7 +62,8 @@ void main() {
out += R"(
// Check if the flip stage is VertexB
- if (flip_stage[0] == 1) {
+ // Config pack's second value is flip_stage
+ if (config_pack[1] == 1) {
// Viewport can be flipped, which is unsupported by glViewport
position.xy *= viewport_flip.xy;
}
@@ -71,7 +71,7 @@ void main() {
// TODO(bunnei): This is likely a hack, position.w should be interpolated as 1.0
// For now, this is here to bring order in lieu of proper emulation
- if (flip_stage[0] == 1) {
+ if (config_pack[1] == 1) {
position.w = 1.0;
}
}
@@ -101,8 +101,7 @@ layout (location = 0) out vec4 position;
layout (std140) uniform gs_config {
vec4 viewport_flip;
- uvec4 instance_id;
- uvec4 flip_stage;
+ uvec4 config_pack; // instance_id, flip_stage, y_direction, padding
uvec4 alpha_test;
};
@@ -139,8 +138,7 @@ layout (location = 0) in vec4 position;
layout (std140) uniform fs_config {
vec4 viewport_flip;
- uvec4 instance_id;
- uvec4 flip_stage;
+ uvec4 config_pack; // instance_id, flip_stage, y_direction, padding
uvec4 alpha_test;
};
diff --git a/src/video_core/renderer_opengl/gl_shader_manager.cpp b/src/video_core/renderer_opengl/gl_shader_manager.cpp
index 8b8869ecb..6a30c28d2 100644
--- a/src/video_core/renderer_opengl/gl_shader_manager.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_manager.cpp
@@ -27,16 +27,18 @@ void MaxwellUniformData::SetFromRegs(const Maxwell3D::State::ShaderStageInfo& sh
alpha_test.func = func;
alpha_test.ref = regs.alpha_test_ref;
- // We only assign the instance to the first component of the vector, the rest is just padding.
- instance_id[0] = state.current_instance;
+ instance_id = state.current_instance;
// Assign in which stage the position has to be flipped
// (the last stage before the fragment shader).
if (gpu.regs.shader_config[static_cast<u32>(Maxwell3D::Regs::ShaderProgram::Geometry)].enable) {
- flip_stage[0] = static_cast<u32>(Maxwell3D::Regs::ShaderProgram::Geometry);
+ flip_stage = static_cast<u32>(Maxwell3D::Regs::ShaderProgram::Geometry);
} else {
- flip_stage[0] = static_cast<u32>(Maxwell3D::Regs::ShaderProgram::VertexB);
+ flip_stage = static_cast<u32>(Maxwell3D::Regs::ShaderProgram::VertexB);
}
+
+ // Y_NEGATE controls what value S2R returns for the Y_DIRECTION system value.
+ y_direction = regs.screen_y_control.y_negate == 0 ? 1.f : -1.f;
}
} // namespace OpenGL::GLShader
diff --git a/src/video_core/renderer_opengl/gl_shader_manager.h b/src/video_core/renderer_opengl/gl_shader_manager.h
index 9a5d7e289..b757f5f44 100644
--- a/src/video_core/renderer_opengl/gl_shader_manager.h
+++ b/src/video_core/renderer_opengl/gl_shader_manager.h
@@ -21,8 +21,11 @@ using Tegra::Engines::Maxwell3D;
struct MaxwellUniformData {
void SetFromRegs(const Maxwell3D::State::ShaderStageInfo& shader_stage);
alignas(16) GLvec4 viewport_flip;
- alignas(16) GLuvec4 instance_id;
- alignas(16) GLuvec4 flip_stage;
+ struct alignas(16) {
+ GLuint instance_id;
+ GLuint flip_stage;
+ GLfloat y_direction;
+ };
struct alignas(16) {
GLuint enabled;
GLuint func;
@@ -30,7 +33,7 @@ struct MaxwellUniformData {
GLuint padding;
} alpha_test;
};
-static_assert(sizeof(MaxwellUniformData) == 64, "MaxwellUniformData structure size is incorrect");
+static_assert(sizeof(MaxwellUniformData) == 48, "MaxwellUniformData structure size is incorrect");
static_assert(sizeof(MaxwellUniformData) < 16384,
"MaxwellUniformData structure must be less than 16kb as per the OpenGL spec");
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp
index 934f4db78..b3bfad6a0 100644
--- a/src/video_core/renderer_opengl/gl_state.cpp
+++ b/src/video_core/renderer_opengl/gl_state.cpp
@@ -92,6 +92,13 @@ OpenGLState::OpenGLState() {
point.size = 1;
fragment_color_clamp.enabled = false;
+
+ polygon_offset.fill_enable = false;
+ polygon_offset.line_enable = false;
+ polygon_offset.point_enable = false;
+ polygon_offset.factor = 0.0f;
+ polygon_offset.units = 0.0f;
+ polygon_offset.clamp = 0.0f;
}
void OpenGLState::ApplyDefaultState() {
@@ -406,6 +413,55 @@ void OpenGLState::ApplyLogicOp() const {
}
}
+void OpenGLState::ApplyPolygonOffset() const {
+
+ const bool fill_enable_changed =
+ polygon_offset.fill_enable != cur_state.polygon_offset.fill_enable;
+ const bool line_enable_changed =
+ polygon_offset.line_enable != cur_state.polygon_offset.line_enable;
+ const bool point_enable_changed =
+ polygon_offset.point_enable != cur_state.polygon_offset.point_enable;
+ const bool factor_changed = polygon_offset.factor != cur_state.polygon_offset.factor;
+ const bool units_changed = polygon_offset.units != cur_state.polygon_offset.units;
+ const bool clamp_changed = polygon_offset.clamp != cur_state.polygon_offset.clamp;
+
+ if (fill_enable_changed) {
+ if (polygon_offset.fill_enable) {
+ glEnable(GL_POLYGON_OFFSET_FILL);
+ } else {
+ glDisable(GL_POLYGON_OFFSET_FILL);
+ }
+ }
+
+ if (line_enable_changed) {
+ if (polygon_offset.line_enable) {
+ glEnable(GL_POLYGON_OFFSET_LINE);
+ } else {
+ glDisable(GL_POLYGON_OFFSET_LINE);
+ }
+ }
+
+ if (point_enable_changed) {
+ if (polygon_offset.point_enable) {
+ glEnable(GL_POLYGON_OFFSET_POINT);
+ } else {
+ glDisable(GL_POLYGON_OFFSET_POINT);
+ }
+ }
+
+ if ((polygon_offset.fill_enable || polygon_offset.line_enable || polygon_offset.point_enable) &&
+ (factor_changed || units_changed || clamp_changed)) {
+
+ if (GLAD_GL_EXT_polygon_offset_clamp && polygon_offset.clamp != 0) {
+ glPolygonOffsetClamp(polygon_offset.factor, polygon_offset.units, polygon_offset.clamp);
+ } else {
+ glPolygonOffset(polygon_offset.factor, polygon_offset.units);
+ UNIMPLEMENTED_IF_MSG(polygon_offset.clamp != 0,
+ "Unimplemented Depth polygon offset clamp.");
+ }
+ }
+}
+
void OpenGLState::ApplyTextures() const {
for (std::size_t i = 0; i < std::size(texture_units); ++i) {
const auto& texture_unit = texture_units[i];
@@ -532,6 +588,7 @@ void OpenGLState::Apply() const {
ApplyLogicOp();
ApplyTextures();
ApplySamplers();
+ ApplyPolygonOffset();
cur_state = *this;
}
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h
index 032fc43f0..0bf19ed07 100644
--- a/src/video_core/renderer_opengl/gl_state.h
+++ b/src/video_core/renderer_opengl/gl_state.h
@@ -176,6 +176,15 @@ public:
float size; // GL_POINT_SIZE
} point;
+ struct {
+ bool point_enable;
+ bool line_enable;
+ bool fill_enable;
+ GLfloat units;
+ GLfloat factor;
+ GLfloat clamp;
+ } polygon_offset;
+
std::array<bool, 2> clip_distance; // GL_CLIP_DISTANCE
OpenGLState();
@@ -226,6 +235,7 @@ private:
void ApplyLogicOp() const;
void ApplyTextures() const;
void ApplySamplers() const;
+ void ApplyPolygonOffset() const;
};
} // namespace OpenGL
diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp
index 7ee572761..ec46dc4e3 100644
--- a/src/yuzu/configuration/configure_input.cpp
+++ b/src/yuzu/configuration/configure_input.cpp
@@ -4,11 +4,9 @@
#include <algorithm>
#include <memory>
-#include <utility>
-#include <QMenu>
-#include <QMessageBox>
+
#include <QTimer>
-#include "common/param_package.h"
+
#include "configuration/configure_touchscreen_advanced.h"
#include "core/core.h"
#include "core/hle/service/am/am.h"
@@ -16,16 +14,25 @@
#include "core/hle/service/am/applet_oe.h"
#include "core/hle/service/hid/controllers/npad.h"
#include "core/hle/service/sm/sm.h"
-#include "input_common/main.h"
#include "ui_configure_input.h"
#include "ui_configure_input_player.h"
-#include "ui_configure_mouse_advanced.h"
-#include "ui_configure_touchscreen_advanced.h"
-#include "yuzu/configuration/config.h"
#include "yuzu/configuration/configure_input.h"
#include "yuzu/configuration/configure_input_player.h"
#include "yuzu/configuration/configure_mouse_advanced.h"
+namespace {
+template <typename Dialog, typename... Args>
+void CallConfigureDialog(ConfigureInput& parent, Args&&... args) {
+ parent.applyConfiguration();
+ Dialog dialog(&parent, std::forward<Args>(args)...);
+
+ const auto res = dialog.exec();
+ if (res == QDialog::Accepted) {
+ dialog.applyConfiguration();
+ }
+}
+} // Anonymous namespace
+
ConfigureInput::ConfigureInput(QWidget* parent)
: QWidget(parent), ui(std::make_unique<Ui::ConfigureInput>()) {
ui->setupUi(this);
@@ -65,31 +72,20 @@ ConfigureInput::ConfigureInput(QWidget* parent)
for (std::size_t i = 0; i < players_configure.size(); ++i) {
connect(players_configure[i], &QPushButton::pressed, this,
- [this, i]() { CallConfigureDialog<ConfigureInputPlayer>(i, false); });
+ [this, i] { CallConfigureDialog<ConfigureInputPlayer>(*this, i, false); });
}
connect(ui->handheld_configure, &QPushButton::pressed, this,
- [this]() { CallConfigureDialog<ConfigureInputPlayer>(8, false); });
+ [this] { CallConfigureDialog<ConfigureInputPlayer>(*this, 8, false); });
connect(ui->debug_configure, &QPushButton::pressed, this,
- [this]() { CallConfigureDialog<ConfigureInputPlayer>(9, true); });
+ [this] { CallConfigureDialog<ConfigureInputPlayer>(*this, 9, true); });
connect(ui->mouse_advanced, &QPushButton::pressed, this,
- [this]() { CallConfigureDialog<ConfigureMouseAdvanced>(); });
+ [this] { CallConfigureDialog<ConfigureMouseAdvanced>(*this); });
connect(ui->touchscreen_advanced, &QPushButton::pressed, this,
- [this]() { CallConfigureDialog<ConfigureTouchscreenAdvanced>(); });
-}
-
-template <typename Dialog, typename... Args>
-void ConfigureInput::CallConfigureDialog(Args&&... args) {
- this->applyConfiguration();
- Dialog dialog(this, std::forward<Args>(args)...);
-
- const auto res = dialog.exec();
- if (res == QDialog::Accepted) {
- dialog.applyConfiguration();
- }
+ [this] { CallConfigureDialog<ConfigureTouchscreenAdvanced>(*this); });
}
void ConfigureInput::OnDockedModeChanged(bool last_state, bool new_state) {
diff --git a/src/yuzu/configuration/configure_input.h b/src/yuzu/configuration/configure_input.h
index 29a8a03f8..e8723dfcb 100644
--- a/src/yuzu/configuration/configure_input.h
+++ b/src/yuzu/configuration/configure_input.h
@@ -5,20 +5,12 @@
#pragma once
#include <array>
-#include <functional>
#include <memory>
-#include <optional>
-#include <string>
-#include <unordered_map>
#include <QKeyEvent>
#include <QWidget>
-#include "common/param_package.h"
-#include "core/settings.h"
-#include "input_common/main.h"
#include "ui_configure_input.h"
-#include "yuzu/configuration/config.h"
class QPushButton;
class QString;
@@ -40,9 +32,6 @@ public:
private:
void updateUIEnabled();
- template <typename Dialog, typename... Args>
- void CallConfigureDialog(Args&&... args);
-
void OnDockedModeChanged(bool last_state, bool new_state);
/// Load configuration settings.
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp
index ba6e09368..7dadd83c1 100644
--- a/src/yuzu/configuration/configure_input_player.cpp
+++ b/src/yuzu/configuration/configure_input_player.cpp
@@ -25,13 +25,6 @@ const std::array<std::string, ConfigureInputPlayer::ANALOG_SUB_BUTTONS_NUM>
"modifier",
}};
-static void MoveGridElement(QGridLayout* grid, int row_old, int column_old, int row_new,
- int column_new) {
- const auto item = grid->itemAtPosition(row_old, column_old);
- // grid->removeItem(item);
- grid->addItem(item, row_new, column_new);
-}
-
static void LayerGridElements(QGridLayout* grid, QWidget* item, QWidget* onTopOf) {
const int index1 = grid->indexOf(item);
const int index2 = grid->indexOf(onTopOf);
@@ -111,11 +104,10 @@ static QString AnalogToText(const Common::ParamPackage& param, const std::string
}
};
-ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, u8 player_index, bool debug)
- : QDialog(parent), ui(std::make_unique<Ui::ConfigureInputPlayer>()),
- timeout_timer(std::make_unique<QTimer>()), poll_timer(std::make_unique<QTimer>()),
- player_index(player_index), debug(debug) {
-
+ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_index, bool debug)
+ : QDialog(parent), ui(std::make_unique<Ui::ConfigureInputPlayer>()), player_index(player_index),
+ debug(debug), timeout_timer(std::make_unique<QTimer>()),
+ poll_timer(std::make_unique<QTimer>()) {
ui->setupUi(this);
setFocusPolicy(Qt::ClickFocus);
@@ -315,7 +307,7 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, u8 player_index, boo
for (std::size_t i = 0; i < controller_color_buttons.size(); ++i) {
connect(controller_color_buttons[i], &QPushButton::clicked, this,
- std::bind(&ConfigureInputPlayer::OnControllerButtonClick, this, i));
+ [this, i] { OnControllerButtonClick(static_cast<int>(i)); });
}
this->loadConfiguration();
diff --git a/src/yuzu/configuration/configure_input_player.h b/src/yuzu/configuration/configure_input_player.h
index b0e5550c5..7a53f6715 100644
--- a/src/yuzu/configuration/configure_input_player.h
+++ b/src/yuzu/configuration/configure_input_player.h
@@ -9,9 +9,10 @@
#include <memory>
#include <optional>
#include <string>
-#include <unordered_map>
+
#include <QDialog>
#include <QKeyEvent>
+
#include "common/param_package.h"
#include "core/settings.h"
#include "input_common/main.h"
@@ -29,16 +30,39 @@ class ConfigureInputPlayer : public QDialog {
Q_OBJECT
public:
- explicit ConfigureInputPlayer(QWidget* parent, u8 player_index, bool debug = false);
+ explicit ConfigureInputPlayer(QWidget* parent, std::size_t player_index, bool debug = false);
~ConfigureInputPlayer() override;
/// Save all button configurations to settings file
void applyConfiguration();
private:
+ void OnControllerButtonClick(int i);
+
+ /// Load configuration settings.
+ void loadConfiguration();
+ /// Restore all buttons to their default values.
+ void restoreDefaults();
+ /// Clear all input configuration
+ void ClearAll();
+
+ /// Update UI to reflect current configuration.
+ void updateButtonLabels();
+
+ /// Called when the button was pressed.
+ void handleClick(QPushButton* button,
+ std::function<void(const Common::ParamPackage&)> new_input_setter,
+ InputCommon::Polling::DeviceType type);
+
+ /// Finish polling and configure input using the input_setter
+ void setPollingResult(const Common::ParamPackage& params, bool abort);
+
+ /// Handle key press events.
+ void keyPressEvent(QKeyEvent* event) override;
+
std::unique_ptr<Ui::ConfigureInputPlayer> ui;
- u8 player_index;
+ std::size_t player_index;
bool debug;
std::unique_ptr<QTimer> timeout_timer;
@@ -77,27 +101,4 @@ private:
std::array<QPushButton*, 4> controller_color_buttons;
std::array<QColor, 4> controller_colors;
-
- void OnControllerButtonClick(int i);
-
- /// Load configuration settings.
- void loadConfiguration();
- /// Restore all buttons to their default values.
- void restoreDefaults();
- /// Clear all input configuration
- void ClearAll();
-
- /// Update UI to reflect current configuration.
- void updateButtonLabels();
-
- /// Called when the button was pressed.
- void handleClick(QPushButton* button,
- std::function<void(const Common::ParamPackage&)> new_input_setter,
- InputCommon::Polling::DeviceType type);
-
- /// Finish polling and configure input using the input_setter
- void setPollingResult(const Common::ParamPackage& params, bool abort);
-
- /// Handle key press events.
- void keyPressEvent(QKeyEvent* event) override;
};
diff --git a/src/yuzu/configuration/configure_mouse_advanced.cpp b/src/yuzu/configuration/configure_mouse_advanced.cpp
index dab58fbaa..ef857035e 100644
--- a/src/yuzu/configuration/configure_mouse_advanced.cpp
+++ b/src/yuzu/configuration/configure_mouse_advanced.cpp
@@ -4,11 +4,11 @@
#include <algorithm>
#include <memory>
-#include <utility>
+
#include <QKeyEvent>
#include <QMenu>
-#include <QMessageBox>
#include <QTimer>
+
#include "common/assert.h"
#include "common/param_package.h"
#include "input_common/main.h"
diff --git a/src/yuzu/configuration/configure_mouse_advanced.h b/src/yuzu/configuration/configure_mouse_advanced.h
index 218df2bda..e04da4bf2 100644
--- a/src/yuzu/configuration/configure_mouse_advanced.h
+++ b/src/yuzu/configuration/configure_mouse_advanced.h
@@ -7,7 +7,7 @@
#include <memory>
#include <optional>
#include <QDialog>
-#include <QWidget>
+
#include "core/settings.h"
class QCheckBox;
@@ -28,23 +28,6 @@ public:
void applyConfiguration();
private:
- std::unique_ptr<Ui::ConfigureMouseAdvanced> ui;
-
- /// This will be the the setting function when an input is awaiting configuration.
- std::optional<std::function<void(const Common::ParamPackage&)>> input_setter;
-
- std::array<QPushButton*, Settings::NativeMouseButton::NumMouseButtons> button_map;
- std::array<Common::ParamPackage, Settings::NativeMouseButton::NumMouseButtons> buttons_param;
-
- std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> device_pollers;
-
- std::unique_ptr<QTimer> timeout_timer;
- std::unique_ptr<QTimer> poll_timer;
-
- /// A flag to indicate if keyboard keys are okay when configuring an input. If this is false,
- /// keyboard events are ignored.
- bool want_keyboard_keys = false;
-
/// Load configuration settings.
void loadConfiguration();
/// Restore all buttons to their default values.
@@ -65,4 +48,21 @@ private:
/// Handle key press events.
void keyPressEvent(QKeyEvent* event) override;
+
+ std::unique_ptr<Ui::ConfigureMouseAdvanced> ui;
+
+ /// This will be the the setting function when an input is awaiting configuration.
+ std::optional<std::function<void(const Common::ParamPackage&)>> input_setter;
+
+ std::array<QPushButton*, Settings::NativeMouseButton::NumMouseButtons> button_map;
+ std::array<Common::ParamPackage, Settings::NativeMouseButton::NumMouseButtons> buttons_param;
+
+ std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> device_pollers;
+
+ std::unique_ptr<QTimer> timeout_timer;
+ std::unique_ptr<QTimer> poll_timer;
+
+ /// A flag to indicate if keyboard keys are okay when configuring an input. If this is false,
+ /// keyboard events are ignored.
+ bool want_keyboard_keys = false;
};
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 9c6d150a5..93bf117c8 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -1105,14 +1105,14 @@ void GMainWindow::OnMenuInstallToNAND() {
return;
}
const auto res =
- Service::FileSystem::GetUserNANDContents()->InstallEntry(nsp, false, qt_raw_copy);
+ Service::FileSystem::GetUserNANDContents()->InstallEntry(*nsp, false, qt_raw_copy);
if (res == FileSys::InstallResult::Success) {
success();
} else {
if (res == FileSys::InstallResult::ErrorAlreadyExists) {
if (overwrite()) {
const auto res2 = Service::FileSystem::GetUserNANDContents()->InstallEntry(
- nsp, true, qt_raw_copy);
+ *nsp, true, qt_raw_copy);
if (res2 == FileSys::InstallResult::Success) {
success();
} else {
@@ -1167,10 +1167,10 @@ void GMainWindow::OnMenuInstallToNAND() {
FileSys::InstallResult res;
if (index >= static_cast<size_t>(FileSys::TitleType::Application)) {
res = Service::FileSystem::GetUserNANDContents()->InstallEntry(
- nca, static_cast<FileSys::TitleType>(index), false, qt_raw_copy);
+ *nca, static_cast<FileSys::TitleType>(index), false, qt_raw_copy);
} else {
res = Service::FileSystem::GetSystemNANDContents()->InstallEntry(
- nca, static_cast<FileSys::TitleType>(index), false, qt_raw_copy);
+ *nca, static_cast<FileSys::TitleType>(index), false, qt_raw_copy);
}
if (res == FileSys::InstallResult::Success) {
@@ -1178,7 +1178,7 @@ void GMainWindow::OnMenuInstallToNAND() {
} else if (res == FileSys::InstallResult::ErrorAlreadyExists) {
if (overwrite()) {
const auto res2 = Service::FileSystem::GetUserNANDContents()->InstallEntry(
- nca, static_cast<FileSys::TitleType>(index), true, qt_raw_copy);
+ *nca, static_cast<FileSys::TitleType>(index), true, qt_raw_copy);
if (res2 == FileSys::InstallResult::Success) {
success();
} else {