Compare commits

...

26 Commits

Author SHA1 Message Date
4ff5edbdd8 Android #81 2023-09-25 00:58:26 +00:00
37a4a6751a Merge pull request #11569 from german77/lle_applet
service: am: Add support for LLE Mii Edit Applet
2023-09-24 10:50:38 -04:00
93a1cd75fe Merge pull request #11562 from GPUCode/srgb-madness
vk_texture_cache: Limit srgb block to transcoding only
2023-09-24 10:50:28 -04:00
b356909212 Merge pull request #11165 from Morph1984/ds_blit
vulkan_device: Return true if either depth/stencil format supports blit
2023-09-24 10:50:04 -04:00
bb28f4a0c4 service: mii: Limit checks to string size 2023-09-23 20:14:37 -06:00
0993c71335 service: hid: Set last connected controller as active 2023-09-23 20:14:37 -06:00
6e1b113c89 service: am: Stub to exit applet cleanly 2023-09-23 20:14:37 -06:00
c46f54b091 service: am: Implement stuff needed for Mii Edit 2023-09-23 20:14:33 -06:00
3983ce9b5c service: fsp: Implement CreateSaveDataFileSystemBySystemSaveDataId and OpenSaveDataFileSystemBySystemSaveDataId 2023-09-23 20:13:36 -06:00
d2cd08e3e1 service: ns: Implement GetSharedFontInOrderOfPriorityForSystem 2023-09-23 20:13:36 -06:00
bb4ae5ee53 yuzu: Add button to boot mii edit from firmware 2023-09-23 20:13:36 -06:00
ace91dd0c0 Merge pull request #11567 from liamwhite/fixing-my-error
emit_spirv: fix incorrect use of descriptor index in image atomics
2023-09-23 13:00:31 +02:00
2921a24268 Merge pull request #11573 from t895/import-fix
android: Use smaller read buffer size for exporting user data
2023-09-22 22:02:06 -04:00
5269a46399 android: Use smaller read buffer size for exporting user data
The File.readBytes() extension attempts to load an entire file into a byte array. This would cause crashes when loading huge files into memory.
2023-09-22 16:51:48 -04:00
33e2dce715 Merge pull request #11572 from t895/import-heuristic
android: Adjust valid user data check
2023-09-22 14:53:39 -04:00
f3bc7354b1 android: Adjust valid user data check 2023-09-22 12:05:44 -04:00
bd5ae33153 Merge pull request #11561 from german77/hle_applet
am: mii_edit: Implement DB operations
2023-09-22 09:56:14 -04:00
16f1592e50 Merge pull request #11557 from GPUCode/brr-format
renderer_vulkan: Correct component order for A4B4G4R4_UNORM
2023-09-22 09:56:04 -04:00
fda08cbbb0 Merge pull request #11563 from Kelebek1/dma_regs
Fix DMA engine register offsets
2023-09-22 09:55:54 -04:00
1e24d02434 emit_spirv: fix incorrect use of descriptor index in image atomics 2023-09-22 00:39:09 -04:00
a57ca3fb66 am: mii_edit: Implement DB operations 2023-09-21 18:21:39 -06:00
4f69be8169 Fix DMA engine register offsets 2023-09-21 20:21:00 +01:00
b6ad7e263b vk_texture_cache: Limit srgb block to transcoding only 2023-09-21 21:46:35 +03:00
4a59dc2947 renderer_vulkan: Correct component order for A4B4G4R4_UNORM 2023-09-21 15:33:44 +03:00
d31676935e vulkan_device: Test depth stencil blit support by format 2023-07-31 19:14:20 -04:00
26658c2e93 vulkan_device: Return true if either depth/stencil format supports blit
On devices that don't support D24S8 but supports D32S8, this should still return true if D32S8 supports src and dst blit
2023-07-26 20:21:37 -04:00
29 changed files with 639 additions and 198 deletions

View File

@ -1,3 +1,11 @@
| Pull Request | Commit | Title | Author | Merged? |
|----|----|----|----|----|
End of merge log. You can find the original README.md below the break.
-----
<!--
SPDX-FileCopyrightText: 2018 yuzu Emulator Project
SPDX-License-Identifier: GPL-2.0-or-later

View File

@ -50,6 +50,7 @@ import org.yuzu.yuzu_emu.model.TaskViewModel
import org.yuzu.yuzu_emu.utils.*
import java.io.BufferedInputStream
import java.io.BufferedOutputStream
import java.io.FileInputStream
import java.io.FileOutputStream
import java.util.zip.ZipEntry
import java.util.zip.ZipInputStream
@ -639,7 +640,15 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
file.path.length
)
stream.putNextEntry(ZipEntry(newPath))
stream.write(file.readBytes())
val buffer = ByteArray(8096)
var read: Int
FileInputStream(file).use { fis ->
while (fis.read(buffer).also { read = it } != -1) {
stream.write(buffer, 0, read)
}
}
stream.closeEntry()
}
}
@ -664,7 +673,8 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
checkStream.use { stream ->
var ze: ZipEntry? = null
while (stream.nextEntry?.also { ze = it } != null) {
if (ze!!.name.trim() == "/config/config.ini") {
val itemName = ze!!.name.trim()
if (itemName == "/config/config.ini" || itemName == "config/config.ini") {
isYuzuBackup = true
return@use
}

View File

@ -19,6 +19,7 @@
#include "core/hle/service/am/am.h"
#include "core/hle/service/am/applet_ae.h"
#include "core/hle/service/am/applet_oe.h"
#include "core/hle/service/am/applets/applet_mii_edit_types.h"
#include "core/hle/service/am/applets/applet_profile_select.h"
#include "core/hle/service/am/applets/applet_web_browser.h"
#include "core/hle/service/am/applets/applets.h"
@ -190,7 +191,7 @@ IDisplayController::IDisplayController(Core::System& system_)
{5, nullptr, "GetLastForegroundCaptureImageEx"},
{6, nullptr, "GetLastApplicationCaptureImageEx"},
{7, nullptr, "GetCallerAppletCaptureImageEx"},
{8, nullptr, "TakeScreenShotOfOwnLayer"},
{8, &IDisplayController::TakeScreenShotOfOwnLayer, "TakeScreenShotOfOwnLayer"},
{9, nullptr, "CopyBetweenCaptureBuffers"},
{10, nullptr, "AcquireLastApplicationCaptureBuffer"},
{11, nullptr, "ReleaseLastApplicationCaptureBuffer"},
@ -218,6 +219,13 @@ IDisplayController::IDisplayController(Core::System& system_)
IDisplayController::~IDisplayController() = default;
void IDisplayController::TakeScreenShotOfOwnLayer(HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
IDebugFunctions::IDebugFunctions(Core::System& system_)
: ServiceFramework{system_, "IDebugFunctions"} {
// clang-format off
@ -724,7 +732,7 @@ ICommonStateGetter::ICommonStateGetter(Core::System& system_,
{110, nullptr, "OpenMyGpuErrorHandler"},
{120, nullptr, "GetAppletLaunchedHistory"},
{200, nullptr, "GetOperationModeSystemInfo"},
{300, nullptr, "GetSettingsPlatformRegion"},
{300, &ICommonStateGetter::GetSettingsPlatformRegion, "GetSettingsPlatformRegion"},
{400, nullptr, "ActivateMigrationService"},
{401, nullptr, "DeactivateMigrationService"},
{500, nullptr, "DisableSleepTillShutdown"},
@ -736,6 +744,10 @@ ICommonStateGetter::ICommonStateGetter(Core::System& system_,
// clang-format on
RegisterHandlers(functions);
// Configure applets to be in foreground state
msg_queue->PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged);
msg_queue->PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoForeground);
}
ICommonStateGetter::~ICommonStateGetter() = default;
@ -867,6 +879,14 @@ void ICommonStateGetter::PerformSystemButtonPressingIfInFocus(HLERequestContext&
rb.Push(ResultSuccess);
}
void ICommonStateGetter::GetSettingsPlatformRegion(HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushEnum(SysPlatformRegion::Global);
}
void ICommonStateGetter::SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled(
HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
@ -1324,18 +1344,19 @@ void ILibraryAppletCreator::CreateHandleStorage(HLERequestContext& ctx) {
ILibraryAppletSelfAccessor::ILibraryAppletSelfAccessor(Core::System& system_)
: ServiceFramework{system_, "ILibraryAppletSelfAccessor"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "PopInData"},
{1, nullptr, "PushOutData"},
{0, &ILibraryAppletSelfAccessor::PopInData, "PopInData"},
{1, &ILibraryAppletSelfAccessor::PushOutData, "PushOutData"},
{2, nullptr, "PopInteractiveInData"},
{3, nullptr, "PushInteractiveOutData"},
{5, nullptr, "GetPopInDataEvent"},
{6, nullptr, "GetPopInteractiveInDataEvent"},
{10, nullptr, "ExitProcessAndReturn"},
{11, nullptr, "GetLibraryAppletInfo"},
{10, &ILibraryAppletSelfAccessor::ExitProcessAndReturn, "ExitProcessAndReturn"},
{11, &ILibraryAppletSelfAccessor::GetLibraryAppletInfo, "GetLibraryAppletInfo"},
{12, nullptr, "GetMainAppletIdentityInfo"},
{13, nullptr, "CanUseApplicationCore"},
{14, nullptr, "GetCallerAppletIdentityInfo"},
{14, &ILibraryAppletSelfAccessor::GetCallerAppletIdentityInfo, "GetCallerAppletIdentityInfo"},
{15, nullptr, "GetMainAppletApplicationControlProperty"},
{16, nullptr, "GetMainAppletStorageId"},
{17, nullptr, "GetCallerAppletIdentityInfoStack"},
@ -1361,10 +1382,142 @@ ILibraryAppletSelfAccessor::ILibraryAppletSelfAccessor(Core::System& system_)
{140, nullptr, "SetApplicationMemoryReservation"},
{150, nullptr, "ShouldSetGpuTimeSliceManually"},
};
// clang-format on
RegisterHandlers(functions);
PushInShowMiiEditData();
}
ILibraryAppletSelfAccessor::~ILibraryAppletSelfAccessor() = default;
void ILibraryAppletSelfAccessor::PopInData(HLERequestContext& ctx) {
LOG_INFO(Service_AM, "called");
if (queue_data.empty()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultNoDataInChannel);
return;
}
auto data = queue_data.front();
queue_data.pop_front();
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IStorage>(system, std::move(data));
}
void ILibraryAppletSelfAccessor::PushOutData(HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void ILibraryAppletSelfAccessor::ExitProcessAndReturn(HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
system.Exit();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void ILibraryAppletSelfAccessor::GetLibraryAppletInfo(HLERequestContext& ctx) {
struct LibraryAppletInfo {
Applets::AppletId applet_id;
Applets::LibraryAppletMode library_applet_mode;
};
LOG_WARNING(Service_AM, "(STUBBED) called");
const LibraryAppletInfo applet_info{
.applet_id = Applets::AppletId::MiiEdit,
.library_applet_mode = Applets::LibraryAppletMode::AllForeground,
};
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.PushRaw(applet_info);
}
void ILibraryAppletSelfAccessor::GetCallerAppletIdentityInfo(HLERequestContext& ctx) {
struct AppletIdentityInfo {
Applets::AppletId applet_id;
INSERT_PADDING_BYTES(0x4);
u64 application_id;
};
LOG_WARNING(Service_AM, "(STUBBED) called");
const AppletIdentityInfo applet_info{
.applet_id = Applets::AppletId::QLaunch,
.application_id = 0x0100000000001000ull,
};
IPC::ResponseBuilder rb{ctx, 6};
rb.Push(ResultSuccess);
rb.PushRaw(applet_info);
}
void ILibraryAppletSelfAccessor::PushInShowMiiEditData() {
struct MiiEditV3 {
Applets::MiiEditAppletInputCommon common;
Applets::MiiEditAppletInputV3 input;
};
static_assert(sizeof(MiiEditV3) == 0x100, "MiiEditV3 has incorrect size.");
MiiEditV3 mii_arguments{
.common =
{
.version = Applets::MiiEditAppletVersion::Version3,
.applet_mode = Applets::MiiEditAppletMode::ShowMiiEdit,
},
.input{},
};
std::vector<u8> argument_data(sizeof(mii_arguments));
std::memcpy(argument_data.data(), &mii_arguments, sizeof(mii_arguments));
queue_data.emplace_back(std::move(argument_data));
}
IAppletCommonFunctions::IAppletCommonFunctions(Core::System& system_)
: ServiceFramework{system_, "IAppletCommonFunctions"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "SetTerminateResult"},
{10, nullptr, "ReadThemeStorage"},
{11, nullptr, "WriteThemeStorage"},
{20, nullptr, "PushToAppletBoundChannel"},
{21, nullptr, "TryPopFromAppletBoundChannel"},
{40, nullptr, "GetDisplayLogicalResolution"},
{42, nullptr, "SetDisplayMagnification"},
{50, nullptr, "SetHomeButtonDoubleClickEnabled"},
{51, nullptr, "GetHomeButtonDoubleClickEnabled"},
{52, nullptr, "IsHomeButtonShortPressedBlocked"},
{60, nullptr, "IsVrModeCurtainRequired"},
{61, nullptr, "IsSleepRequiredByHighTemperature"},
{62, nullptr, "IsSleepRequiredByLowBattery"},
{70, &IAppletCommonFunctions::SetCpuBoostRequestPriority, "SetCpuBoostRequestPriority"},
{80, nullptr, "SetHandlingCaptureButtonShortPressedMessageEnabledForApplet"},
{81, nullptr, "SetHandlingCaptureButtonLongPressedMessageEnabledForApplet"},
{90, nullptr, "OpenNamedChannelAsParent"},
{91, nullptr, "OpenNamedChannelAsChild"},
{100, nullptr, "SetApplicationCoreUsageMode"},
};
// clang-format on
RegisterHandlers(functions);
}
IAppletCommonFunctions::~IAppletCommonFunctions() = default;
void IAppletCommonFunctions::SetCpuBoostRequestPriority(HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
IApplicationFunctions::IApplicationFunctions(Core::System& system_)
: ServiceFramework{system_, "IApplicationFunctions"}, service_context{system,
@ -2049,8 +2202,8 @@ IProcessWindingController::IProcessWindingController(Core::System& system_)
: ServiceFramework{system_, "IProcessWindingController"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "GetLaunchReason"},
{11, nullptr, "OpenCallingLibraryApplet"},
{0, &IProcessWindingController::GetLaunchReason, "GetLaunchReason"},
{11, &IProcessWindingController::OpenCallingLibraryApplet, "OpenCallingLibraryApplet"},
{21, nullptr, "PushContext"},
{22, nullptr, "PopContext"},
{23, nullptr, "CancelWindingReservation"},
@ -2064,4 +2217,46 @@ IProcessWindingController::IProcessWindingController(Core::System& system_)
}
IProcessWindingController::~IProcessWindingController() = default;
void IProcessWindingController::GetLaunchReason(HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
struct AppletProcessLaunchReason {
u8 flag;
INSERT_PADDING_BYTES(3);
};
static_assert(sizeof(AppletProcessLaunchReason) == 0x4,
"AppletProcessLaunchReason is an invalid size");
AppletProcessLaunchReason reason{
.flag = 0,
};
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushRaw(reason);
}
void IProcessWindingController::OpenCallingLibraryApplet(HLERequestContext& ctx) {
const auto applet_id = Applets::AppletId::MiiEdit;
const auto applet_mode = Applets::LibraryAppletMode::AllForeground;
LOG_WARNING(Service_AM, "(STUBBED) called with applet_id={:08X}, applet_mode={:08X}", applet_id,
applet_mode);
const auto& applet_manager{system.GetAppletManager()};
const auto applet = applet_manager.GetApplet(applet_id, applet_mode);
if (applet == nullptr) {
LOG_ERROR(Service_AM, "Applet doesn't exist! applet_id={}", applet_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
}
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<ILibraryAppletAccessor>(system, applet);
}
} // namespace Service::AM

View File

@ -120,6 +120,9 @@ class IDisplayController final : public ServiceFramework<IDisplayController> {
public:
explicit IDisplayController(Core::System& system_);
~IDisplayController() override;
private:
void TakeScreenShotOfOwnLayer(HLERequestContext& ctx);
};
class IDebugFunctions final : public ServiceFramework<IDebugFunctions> {
@ -212,6 +215,11 @@ private:
CaptureButtonLongPressing,
};
enum class SysPlatformRegion : s32 {
Global = 1,
Terra = 2,
};
void GetEventHandle(HLERequestContext& ctx);
void ReceiveMessage(HLERequestContext& ctx);
void GetCurrentFocusState(HLERequestContext& ctx);
@ -227,6 +235,7 @@ private:
void GetDefaultDisplayResolution(HLERequestContext& ctx);
void SetCpuBoostMode(HLERequestContext& ctx);
void PerformSystemButtonPressingIfInFocus(HLERequestContext& ctx);
void GetSettingsPlatformRegion(HLERequestContext& ctx);
void SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled(HLERequestContext& ctx);
std::shared_ptr<AppletMessageQueue> msg_queue;
@ -294,6 +303,26 @@ class ILibraryAppletSelfAccessor final : public ServiceFramework<ILibraryAppletS
public:
explicit ILibraryAppletSelfAccessor(Core::System& system_);
~ILibraryAppletSelfAccessor() override;
private:
void PopInData(HLERequestContext& ctx);
void PushOutData(HLERequestContext& ctx);
void GetLibraryAppletInfo(HLERequestContext& ctx);
void ExitProcessAndReturn(HLERequestContext& ctx);
void GetCallerAppletIdentityInfo(HLERequestContext& ctx);
void PushInShowMiiEditData();
std::deque<std::vector<u8>> queue_data;
};
class IAppletCommonFunctions final : public ServiceFramework<IAppletCommonFunctions> {
public:
explicit IAppletCommonFunctions(Core::System& system_);
~IAppletCommonFunctions() override;
private:
void SetCpuBoostRequestPriority(HLERequestContext& ctx);
};
class IApplicationFunctions final : public ServiceFramework<IApplicationFunctions> {
@ -378,6 +407,10 @@ class IProcessWindingController final : public ServiceFramework<IProcessWindingC
public:
explicit IProcessWindingController(Core::System& system_);
~IProcessWindingController() override;
private:
void GetLaunchReason(HLERequestContext& ctx);
void OpenCallingLibraryApplet(HLERequestContext& ctx);
};
void LoopProcess(Nvnflinger::Nvnflinger& nvnflinger, Core::System& system);

View File

@ -27,7 +27,7 @@ public:
{10, &ILibraryAppletProxy::GetProcessWindingController, "GetProcessWindingController"},
{11, &ILibraryAppletProxy::GetLibraryAppletCreator, "GetLibraryAppletCreator"},
{20, &ILibraryAppletProxy::OpenLibraryAppletSelfAccessor, "OpenLibraryAppletSelfAccessor"},
{21, nullptr, "GetAppletCommonFunctions"},
{21, &ILibraryAppletProxy::GetAppletCommonFunctions, "GetAppletCommonFunctions"},
{22, nullptr, "GetHomeMenuFunctions"},
{23, nullptr, "GetGlobalStateController"},
{1000, &ILibraryAppletProxy::GetDebugFunctions, "GetDebugFunctions"},
@ -86,14 +86,6 @@ private:
rb.PushIpcInterface<IProcessWindingController>(system);
}
void GetDebugFunctions(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IDebugFunctions>(system);
}
void GetLibraryAppletCreator(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
@ -110,6 +102,22 @@ private:
rb.PushIpcInterface<ILibraryAppletSelfAccessor>(system);
}
void GetAppletCommonFunctions(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IAppletCommonFunctions>(system);
}
void GetDebugFunctions(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IDebugFunctions>(system);
}
Nvnflinger::Nvnflinger& nvnflinger;
std::shared_ptr<AppletMessageQueue> msg_queue;
};
@ -133,7 +141,7 @@ public:
{20, &ISystemAppletProxy::GetHomeMenuFunctions, "GetHomeMenuFunctions"},
{21, &ISystemAppletProxy::GetGlobalStateController, "GetGlobalStateController"},
{22, &ISystemAppletProxy::GetApplicationCreator, "GetApplicationCreator"},
{23, nullptr, "GetAppletCommonFunctions"},
{23, &ISystemAppletProxy::GetAppletCommonFunctions, "GetAppletCommonFunctions"},
{1000, &ISystemAppletProxy::GetDebugFunctions, "GetDebugFunctions"},
};
// clang-format on
@ -182,14 +190,6 @@ private:
rb.PushIpcInterface<IDisplayController>(system);
}
void GetDebugFunctions(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IDebugFunctions>(system);
}
void GetLibraryAppletCreator(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
@ -222,6 +222,22 @@ private:
rb.PushIpcInterface<IApplicationCreator>(system);
}
void GetAppletCommonFunctions(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IAppletCommonFunctions>(system);
}
void GetDebugFunctions(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IDebugFunctions>(system);
}
Nvnflinger::Nvnflinger& nvnflinger;
std::shared_ptr<AppletMessageQueue> msg_queue;
};

View File

@ -7,7 +7,9 @@
#include "core/frontend/applets/mii_edit.h"
#include "core/hle/service/am/am.h"
#include "core/hle/service/am/applets/applet_mii_edit.h"
#include "core/hle/service/mii/mii.h"
#include "core/hle/service/mii/mii_manager.h"
#include "core/hle/service/sm/sm.h"
namespace Service::AM::Applets {
@ -56,6 +58,12 @@ void MiiEdit::Initialize() {
sizeof(MiiEditAppletInputV4));
break;
}
manager = system.ServiceManager().GetService<Mii::MiiDBModule>("mii:e")->GetMiiManager();
if (manager == nullptr) {
manager = std::make_shared<Mii::MiiManager>();
}
manager->Initialize(metadata);
}
bool MiiEdit::TransactionComplete() const {
@ -78,22 +86,46 @@ void MiiEdit::Execute() {
// This is a default stub for each of the MiiEdit applet modes.
switch (applet_input_common.applet_mode) {
case MiiEditAppletMode::ShowMiiEdit:
case MiiEditAppletMode::AppendMii:
case MiiEditAppletMode::AppendMiiImage:
case MiiEditAppletMode::UpdateMiiImage:
MiiEditOutput(MiiEditResult::Success, 0);
break;
case MiiEditAppletMode::CreateMii:
case MiiEditAppletMode::EditMii: {
Mii::CharInfo char_info{};
case MiiEditAppletMode::AppendMii: {
Mii::StoreData store_data{};
store_data.BuildBase(Mii::Gender::Male);
char_info.SetFromStoreData(store_data);
store_data.BuildRandom(Mii::Age::All, Mii::Gender::All, Mii::Race::All);
store_data.SetNickname({u'y', u'u', u'z', u'u'});
store_data.SetChecksum();
const auto result = manager->AddOrReplace(metadata, store_data);
if (result.IsError()) {
MiiEditOutput(MiiEditResult::Cancel, 0);
break;
}
s32 index = manager->FindIndex(store_data.GetCreateId(), false);
if (index == -1) {
MiiEditOutput(MiiEditResult::Cancel, 0);
break;
}
MiiEditOutput(MiiEditResult::Success, index);
break;
}
case MiiEditAppletMode::CreateMii: {
Mii::CharInfo char_info{};
manager->BuildRandom(char_info, Mii::Age::All, Mii::Gender::All, Mii::Race::All);
const MiiEditCharInfo edit_char_info{
.mii_info{applet_input_common.applet_mode == MiiEditAppletMode::EditMii
? applet_input_v4.char_info.mii_info
: char_info},
.mii_info{char_info},
};
MiiEditOutputForCharInfoEditing(MiiEditResult::Success, edit_char_info);
break;
}
case MiiEditAppletMode::EditMii: {
const MiiEditCharInfo edit_char_info{
.mii_info{applet_input_v4.char_info.mii_info},
};
MiiEditOutputForCharInfoEditing(MiiEditResult::Success, edit_char_info);

View File

@ -11,6 +11,11 @@ namespace Core {
class System;
} // namespace Core
namespace Service::Mii {
struct DatabaseSessionMetadata;
class MiiManager;
} // namespace Service::Mii
namespace Service::AM::Applets {
class MiiEdit final : public Applet {
@ -40,6 +45,8 @@ private:
MiiEditAppletInputV4 applet_input_v4{};
bool is_complete{false};
std::shared_ptr<Mii::MiiManager> manager = nullptr;
Mii::DatabaseSessionMetadata metadata{};
};
} // namespace Service::AM::Applets

View File

@ -698,7 +698,7 @@ FSP_SRV::FSP_SRV(Core::System& system_)
{19, nullptr, "FormatSdCardFileSystem"},
{21, nullptr, "DeleteSaveDataFileSystem"},
{22, &FSP_SRV::CreateSaveDataFileSystem, "CreateSaveDataFileSystem"},
{23, nullptr, "CreateSaveDataFileSystemBySystemSaveDataId"},
{23, &FSP_SRV::CreateSaveDataFileSystemBySystemSaveDataId, "CreateSaveDataFileSystemBySystemSaveDataId"},
{24, nullptr, "RegisterSaveDataFileSystemAtomicDeletion"},
{25, nullptr, "DeleteSaveDataFileSystemBySaveDataSpaceId"},
{26, nullptr, "FormatSdCardDryRun"},
@ -712,7 +712,7 @@ FSP_SRV::FSP_SRV(Core::System& system_)
{35, nullptr, "CreateSaveDataFileSystemByHashSalt"},
{36, nullptr, "OpenHostFileSystemWithOption"},
{51, &FSP_SRV::OpenSaveDataFileSystem, "OpenSaveDataFileSystem"},
{52, nullptr, "OpenSaveDataFileSystemBySystemSaveDataId"},
{52, &FSP_SRV::OpenSaveDataFileSystemBySystemSaveDataId, "OpenSaveDataFileSystemBySystemSaveDataId"},
{53, &FSP_SRV::OpenReadOnlySaveDataFileSystem, "OpenReadOnlySaveDataFileSystem"},
{57, nullptr, "ReadSaveDataFileSystemExtraDataBySaveDataSpaceId"},
{58, nullptr, "ReadSaveDataFileSystemExtraData"},
@ -870,6 +870,21 @@ void FSP_SRV::CreateSaveDataFileSystem(HLERequestContext& ctx) {
rb.Push(ResultSuccess);
}
void FSP_SRV::CreateSaveDataFileSystemBySystemSaveDataId(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
auto save_struct = rp.PopRaw<FileSys::SaveDataAttribute>();
[[maybe_unused]] auto save_create_struct = rp.PopRaw<std::array<u8, 0x40>>();
LOG_DEBUG(Service_FS, "called save_struct = {}", save_struct.DebugInfo());
FileSys::VirtualDir save_data_dir{};
fsc.CreateSaveData(&save_data_dir, FileSys::SaveDataSpaceId::NandSystem, save_struct);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void FSP_SRV::OpenSaveDataFileSystem(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
@ -916,6 +931,11 @@ void FSP_SRV::OpenSaveDataFileSystem(HLERequestContext& ctx) {
rb.PushIpcInterface<IFileSystem>(std::move(filesystem));
}
void FSP_SRV::OpenSaveDataFileSystemBySystemSaveDataId(HLERequestContext& ctx) {
LOG_WARNING(Service_FS, "(STUBBED) called, delegating to 51 OpenSaveDataFilesystem");
OpenSaveDataFileSystem(ctx);
}
void FSP_SRV::OpenReadOnlySaveDataFileSystem(HLERequestContext& ctx) {
LOG_WARNING(Service_FS, "(STUBBED) called, delegating to 51 OpenSaveDataFilesystem");
OpenSaveDataFileSystem(ctx);

View File

@ -39,7 +39,9 @@ private:
void OpenFileSystemWithPatch(HLERequestContext& ctx);
void OpenSdCardFileSystem(HLERequestContext& ctx);
void CreateSaveDataFileSystem(HLERequestContext& ctx);
void CreateSaveDataFileSystemBySystemSaveDataId(HLERequestContext& ctx);
void OpenSaveDataFileSystem(HLERequestContext& ctx);
void OpenSaveDataFileSystemBySystemSaveDataId(HLERequestContext& ctx);
void OpenReadOnlySaveDataFileSystem(HLERequestContext& ctx);
void OpenSaveDataInfoReaderBySaveDataSpaceId(HLERequestContext& ctx);
void OpenSaveDataInfoReaderOnlyCacheStorage(HLERequestContext& ctx);

View File

@ -346,6 +346,7 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
}
SignalStyleSetChangedEvent(npad_id);
WriteEmptyEntry(controller.shared_memory);
hid_core.SetLastActiveController(npad_id);
}
void Controller_NPad::OnInit() {

View File

@ -18,8 +18,10 @@ namespace Service::Mii {
class IDatabaseService final : public ServiceFramework<IDatabaseService> {
public:
explicit IDatabaseService(Core::System& system_, bool is_system_)
: ServiceFramework{system_, "IDatabaseService"}, is_system{is_system_} {
explicit IDatabaseService(Core::System& system_, std::shared_ptr<MiiManager> mii_manager,
bool is_system_)
: ServiceFramework{system_, "IDatabaseService"}, manager{mii_manager}, is_system{
is_system_} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &IDatabaseService::IsUpdated, "IsUpdated"},
@ -54,7 +56,7 @@ public:
RegisterHandlers(functions);
manager.Initialize(metadata);
manager->Initialize(metadata);
}
private:
@ -64,7 +66,7 @@ private:
LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag);
const bool is_updated = manager.IsUpdated(metadata, source_flag);
const bool is_updated = manager->IsUpdated(metadata, source_flag);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
@ -74,7 +76,7 @@ private:
void IsFullDatabase(HLERequestContext& ctx) {
LOG_DEBUG(Service_Mii, "called");
const bool is_full_database = manager.IsFullDatabase();
const bool is_full_database = manager->IsFullDatabase();
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
@ -85,7 +87,7 @@ private:
IPC::RequestParser rp{ctx};
const auto source_flag{rp.PopRaw<SourceFlag>()};
const u32 mii_count = manager.GetCount(metadata, source_flag);
const u32 mii_count = manager->GetCount(metadata, source_flag);
LOG_DEBUG(Service_Mii, "called with source_flag={}, mii_count={}", source_flag, mii_count);
@ -101,7 +103,7 @@ private:
u32 mii_count{};
std::vector<CharInfoElement> char_info_elements(output_size);
const auto result = manager.Get(metadata, char_info_elements, mii_count, source_flag);
const auto result = manager->Get(metadata, char_info_elements, mii_count, source_flag);
if (mii_count != 0) {
ctx.WriteBuffer(char_info_elements);
@ -122,7 +124,7 @@ private:
u32 mii_count{};
std::vector<CharInfo> char_info(output_size);
const auto result = manager.Get(metadata, char_info, mii_count, source_flag);
const auto result = manager->Get(metadata, char_info, mii_count, source_flag);
if (mii_count != 0) {
ctx.WriteBuffer(char_info);
@ -144,7 +146,7 @@ private:
LOG_INFO(Service_Mii, "called with source_flag={}", source_flag);
CharInfo new_char_info{};
const auto result = manager.UpdateLatest(metadata, new_char_info, char_info, source_flag);
const auto result = manager->UpdateLatest(metadata, new_char_info, char_info, source_flag);
if (result.IsFailure()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
@ -183,7 +185,7 @@ private:
}
CharInfo char_info{};
manager.BuildRandom(char_info, age, gender, race);
manager->BuildRandom(char_info, age, gender, race);
IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)};
rb.Push(ResultSuccess);
@ -203,7 +205,7 @@ private:
}
CharInfo char_info{};
manager.BuildDefault(char_info, index);
manager->BuildDefault(char_info, index);
IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)};
rb.Push(ResultSuccess);
@ -217,7 +219,7 @@ private:
u32 mii_count{};
std::vector<StoreDataElement> store_data_elements(output_size);
const auto result = manager.Get(metadata, store_data_elements, mii_count, source_flag);
const auto result = manager->Get(metadata, store_data_elements, mii_count, source_flag);
if (mii_count != 0) {
ctx.WriteBuffer(store_data_elements);
@ -238,7 +240,7 @@ private:
u32 mii_count{};
std::vector<StoreData> store_data(output_size);
const auto result = manager.Get(metadata, store_data, mii_count, source_flag);
const auto result = manager->Get(metadata, store_data, mii_count, source_flag);
if (mii_count != 0) {
ctx.WriteBuffer(store_data);
@ -266,7 +268,7 @@ private:
StoreData new_store_data{};
if (result.IsSuccess()) {
result = manager.UpdateLatest(metadata, new_store_data, store_data, source_flag);
result = manager->UpdateLatest(metadata, new_store_data, store_data, source_flag);
}
if (result.IsFailure()) {
@ -288,7 +290,7 @@ private:
LOG_INFO(Service_Mii, "called with create_id={}, is_special={}",
create_id.FormattedString(), is_special);
const s32 index = manager.FindIndex(create_id, is_special);
const s32 index = manager->FindIndex(create_id, is_special);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
@ -309,14 +311,14 @@ private:
}
if (result.IsSuccess()) {
const u32 count = manager.GetCount(metadata, SourceFlag::Database);
const u32 count = manager->GetCount(metadata, SourceFlag::Database);
if (new_index < 0 || new_index >= static_cast<s32>(count)) {
result = ResultInvalidArgument;
}
}
if (result.IsSuccess()) {
result = manager.Move(metadata, new_index, create_id);
result = manager->Move(metadata, new_index, create_id);
}
IPC::ResponseBuilder rb{ctx, 2};
@ -336,7 +338,7 @@ private:
}
if (result.IsSuccess()) {
result = manager.AddOrReplace(metadata, store_data);
result = manager->AddOrReplace(metadata, store_data);
}
IPC::ResponseBuilder rb{ctx, 2};
@ -356,7 +358,7 @@ private:
}
if (result.IsSuccess()) {
result = manager.Delete(metadata, create_id);
result = manager->Delete(metadata, create_id);
}
IPC::ResponseBuilder rb{ctx, 2};
@ -376,7 +378,7 @@ private:
}
if (result.IsSuccess()) {
result = manager.DestroyFile(metadata);
result = manager->DestroyFile(metadata);
}
IPC::ResponseBuilder rb{ctx, 2};
@ -396,7 +398,7 @@ private:
}
if (result.IsSuccess()) {
result = manager.DeleteFile();
result = manager->DeleteFile();
}
IPC::ResponseBuilder rb{ctx, 2};
@ -416,7 +418,7 @@ private:
}
if (result.IsSuccess()) {
result = manager.Format(metadata);
result = manager->Format(metadata);
}
IPC::ResponseBuilder rb{ctx, 2};
@ -434,7 +436,7 @@ private:
}
if (result.IsSuccess()) {
is_broken_with_clear_flag = manager.IsBrokenWithClearFlag(metadata);
is_broken_with_clear_flag = manager->IsBrokenWithClearFlag(metadata);
}
IPC::ResponseBuilder rb{ctx, 3};
@ -449,7 +451,7 @@ private:
LOG_DEBUG(Service_Mii, "called");
s32 index{};
const auto result = manager.GetIndex(metadata, info, index);
const auto result = manager->GetIndex(metadata, info, index);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(result);
@ -462,7 +464,7 @@ private:
LOG_INFO(Service_Mii, "called, interface_version={:08X}", interface_version);
manager.SetInterfaceVersion(metadata, interface_version);
manager->SetInterfaceVersion(metadata, interface_version);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
@ -475,7 +477,7 @@ private:
LOG_INFO(Service_Mii, "called");
CharInfo char_info{};
const auto result = manager.ConvertV3ToCharInfo(char_info, mii_v3);
const auto result = manager->ConvertV3ToCharInfo(char_info, mii_v3);
IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)};
rb.Push(result);
@ -489,7 +491,7 @@ private:
LOG_INFO(Service_Mii, "called");
CharInfo char_info{};
const auto result = manager.ConvertCoreDataToCharInfo(char_info, core_data);
const auto result = manager->ConvertCoreDataToCharInfo(char_info, core_data);
IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)};
rb.Push(result);
@ -503,7 +505,7 @@ private:
LOG_INFO(Service_Mii, "called");
CoreData core_data{};
const auto result = manager.ConvertCharInfoToCoreData(core_data, char_info);
const auto result = manager->ConvertCharInfoToCoreData(core_data, char_info);
IPC::ResponseBuilder rb{ctx, 2 + sizeof(CoreData) / sizeof(u32)};
rb.Push(result);
@ -516,41 +518,46 @@ private:
LOG_INFO(Service_Mii, "called");
const auto result = manager.Append(metadata, char_info);
const auto result = manager->Append(metadata, char_info);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
MiiManager manager{};
std::shared_ptr<MiiManager> manager = nullptr;
DatabaseSessionMetadata metadata{};
bool is_system{};
};
class MiiDBModule final : public ServiceFramework<MiiDBModule> {
public:
explicit MiiDBModule(Core::System& system_, const char* name_, bool is_system_)
: ServiceFramework{system_, name_}, is_system{is_system_} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &MiiDBModule::GetDatabaseService, "GetDatabaseService"},
};
// clang-format on
MiiDBModule::MiiDBModule(Core::System& system_, const char* name_,
std::shared_ptr<MiiManager> mii_manager, bool is_system_)
: ServiceFramework{system_, name_}, manager{mii_manager}, is_system{is_system_} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &MiiDBModule::GetDatabaseService, "GetDatabaseService"},
};
// clang-format on
RegisterHandlers(functions);
RegisterHandlers(functions);
if (manager == nullptr) {
manager = std::make_shared<MiiManager>();
}
}
private:
void GetDatabaseService(HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IDatabaseService>(system, is_system);
MiiDBModule::~MiiDBModule() = default;
LOG_DEBUG(Service_Mii, "called");
}
void MiiDBModule::GetDatabaseService(HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IDatabaseService>(system, manager, is_system);
bool is_system{};
};
LOG_DEBUG(Service_Mii, "called");
}
std::shared_ptr<MiiManager> MiiDBModule::GetMiiManager() {
return manager;
}
class MiiImg final : public ServiceFramework<MiiImg> {
public:
@ -596,11 +603,12 @@ private:
void LoopProcess(Core::System& system) {
auto server_manager = std::make_unique<ServerManager>(system);
std::shared_ptr<MiiManager> manager = nullptr;
server_manager->RegisterNamedService("mii:e",
std::make_shared<MiiDBModule>(system, "mii:e", true));
server_manager->RegisterNamedService("mii:u",
std::make_shared<MiiDBModule>(system, "mii:u", false));
server_manager->RegisterNamedService(
"mii:e", std::make_shared<MiiDBModule>(system, "mii:e", manager, true));
server_manager->RegisterNamedService(
"mii:u", std::make_shared<MiiDBModule>(system, "mii:u", manager, false));
server_manager->RegisterNamedService("miiimg", std::make_shared<MiiImg>(system));
ServerManager::RunServer(std::move(server_manager));
}

View File

@ -3,11 +3,29 @@
#pragma once
#include "core/hle/service/service.h"
namespace Core {
class System;
}
namespace Service::Mii {
class MiiManager;
class MiiDBModule final : public ServiceFramework<MiiDBModule> {
public:
explicit MiiDBModule(Core::System& system_, const char* name_,
std::shared_ptr<MiiManager> mii_manager, bool is_system_);
~MiiDBModule() override;
std::shared_ptr<MiiManager> GetMiiManager();
private:
void GetDatabaseService(HLERequestContext& ctx);
std::shared_ptr<MiiManager> manager = nullptr;
bool is_system{};
};
void LoopProcess(Core::System& system);

View File

@ -130,11 +130,11 @@ Result MiiManager::GetIndex(const DatabaseSessionMetadata& metadata, const CharI
}
s32 index{};
Result result = {};
// FindIndex(index);
const bool is_special = metadata.magic == MiiMagic;
const auto result = database_manager.FindIndex(index, char_info.GetCreateId(), is_special);
if (result.IsError()) {
return ResultNotFound;
index = -1;
}
if (index == -1) {

View File

@ -614,7 +614,7 @@ struct Nickname {
}
std::size_t index = 1;
while (data[index] != 0) {
while (index < MaxNameSize && data[index] != 0) {
index++;
}
while (index < MaxNameSize && data[index] == 0) {

View File

@ -37,7 +37,7 @@ void CharInfo::SetFromStoreData(const StoreData& store_data) {
eyebrow_aspect = store_data.GetEyebrowAspect();
eyebrow_rotate = store_data.GetEyebrowRotate();
eyebrow_x = store_data.GetEyebrowX();
eyebrow_y = store_data.GetEyebrowY() + 3;
eyebrow_y = store_data.GetEyebrowY();
nose_type = store_data.GetNoseType();
nose_scale = store_data.GetNoseScale();
nose_y = store_data.GetNoseY();

View File

@ -171,7 +171,7 @@ void CoreData::BuildRandom(Age age, Gender gender, Race race) {
u8 glasses_type{};
while (glasses_type_start < glasses_type_info.values[glasses_type]) {
if (++glasses_type >= glasses_type_info.values_count) {
ASSERT(false);
glasses_type = 0;
break;
}
}
@ -179,6 +179,7 @@ void CoreData::BuildRandom(Age age, Gender gender, Race race) {
SetGlassType(static_cast<GlassType>(glasses_type));
SetGlassColor(RawData::GetGlassColorFromVer3(0));
SetGlassScale(4);
SetGlassY(static_cast<u8>(axis_y + 10));
SetMoleType(MoleType::None);
SetMoleScale(4);

View File

@ -1716,18 +1716,18 @@ const std::array<RandomMiiData4, 18> RandomMiiMouthType{
const std::array<RandomMiiData2, 3> RandomMiiGlassType{
RandomMiiData2{
.arg_1 = 0,
.values_count = 9,
.values = {90, 94, 96, 100, 0, 0, 0, 0, 0},
.values_count = 4,
.values = {90, 94, 96, 100},
},
RandomMiiData2{
.arg_1 = 1,
.values_count = 9,
.values = {83, 86, 90, 93, 94, 96, 98, 100, 0},
.values_count = 8,
.values = {83, 86, 90, 93, 94, 96, 98, 100},
},
RandomMiiData2{
.arg_1 = 2,
.values_count = 9,
.values = {78, 83, 0, 93, 0, 0, 98, 100, 0},
.values_count = 8,
.values = {78, 83, 0, 93, 0, 0, 98, 100},
},
};

View File

@ -144,7 +144,7 @@ IPlatformServiceManager::IPlatformServiceManager(Core::System& system_, const ch
{3, &IPlatformServiceManager::GetSharedMemoryAddressOffset, "GetSharedMemoryAddressOffset"},
{4, &IPlatformServiceManager::GetSharedMemoryNativeHandle, "GetSharedMemoryNativeHandle"},
{5, &IPlatformServiceManager::GetSharedFontInOrderOfPriority, "GetSharedFontInOrderOfPriority"},
{6, nullptr, "GetSharedFontInOrderOfPriorityForSystem"},
{6, &IPlatformServiceManager::GetSharedFontInOrderOfPriority, "GetSharedFontInOrderOfPriorityForSystem"},
{100, nullptr, "RequestApplicationFunctionAuthorization"},
{101, nullptr, "RequestApplicationFunctionAuthorizationByProcessId"},
{102, nullptr, "RequestApplicationFunctionAuthorizationByApplicationId"},
@ -262,8 +262,17 @@ void IPlatformServiceManager::GetSharedMemoryNativeHandle(HLERequestContext& ctx
}
void IPlatformServiceManager::GetSharedFontInOrderOfPriority(HLERequestContext& ctx) {
// The maximum number of elements that can be returned is 6. Regardless of the available fonts
// or buffer size.
constexpr std::size_t MaxElementCount = 6;
IPC::RequestParser rp{ctx};
const u64 language_code{rp.Pop<u64>()}; // TODO(ogniK): Find out what this is used for
const std::size_t font_codes_count =
std::min(MaxElementCount, ctx.GetWriteBufferNumElements<u32>(0));
const std::size_t font_offsets_count =
std::min(MaxElementCount, ctx.GetWriteBufferNumElements<u32>(1));
const std::size_t font_sizes_count =
std::min(MaxElementCount, ctx.GetWriteBufferNumElements<u32>(2));
LOG_DEBUG(Service_NS, "called, language_code={:X}", language_code);
IPC::ResponseBuilder rb{ctx, 4};
@ -280,9 +289,9 @@ void IPlatformServiceManager::GetSharedFontInOrderOfPriority(HLERequestContext&
}
// Resize buffers if game requests smaller size output
font_codes.resize(std::min(font_codes.size(), ctx.GetWriteBufferNumElements<u32>(0)));
font_offsets.resize(std::min(font_offsets.size(), ctx.GetWriteBufferNumElements<u32>(1)));
font_sizes.resize(std::min(font_sizes.size(), ctx.GetWriteBufferNumElements<u32>(2)));
font_codes.resize(std::min(font_codes.size(), font_codes_count));
font_offsets.resize(std::min(font_offsets.size(), font_offsets_count));
font_sizes.resize(std::min(font_sizes.size(), font_sizes_count));
ctx.WriteBuffer(font_codes, 0);
ctx.WriteBuffer(font_offsets, 1);

View File

@ -7,15 +7,12 @@
namespace Shader::Backend::SPIRV {
namespace {
Id Image(EmitContext& ctx, const IR::Value& index, IR::TextureInstInfo info) {
if (!index.IsImmediate()) {
throw NotImplementedException("Indirect image indexing");
}
Id Image(EmitContext& ctx, IR::TextureInstInfo info) {
if (info.type == TextureType::Buffer) {
const ImageBufferDefinition def{ctx.image_buffers.at(index.U32())};
const ImageBufferDefinition def{ctx.image_buffers.at(info.descriptor_index)};
return def.id;
} else {
const ImageDefinition def{ctx.images.at(index.U32())};
const ImageDefinition def{ctx.images.at(info.descriptor_index)};
return def.id;
}
}
@ -28,8 +25,12 @@ std::pair<Id, Id> AtomicArgs(EmitContext& ctx) {
Id ImageAtomicU32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id value,
Id (Sirit::Module::*atomic_func)(Id, Id, Id, Id, Id)) {
if (!index.IsImmediate() || index.U32() != 0) {
// TODO: handle layers
throw NotImplementedException("Image indexing");
}
const auto info{inst->Flags<IR::TextureInstInfo>()};
const Id image{Image(ctx, index, info)};
const Id image{Image(ctx, info)};
const Id pointer{ctx.OpImageTexelPointer(ctx.image_u32, image, coords, ctx.Const(0U))};
const auto [scope, semantics]{AtomicArgs(ctx)};
return (ctx.*atomic_func)(ctx.U32[1], pointer, scope, semantics, value);

View File

@ -74,11 +74,6 @@ spv::ImageFormat GetImageFormat(ImageFormat format) {
throw InvalidArgument("Invalid image format {}", format);
}
spv::ImageFormat GetImageFormatForBuffer(ImageFormat format) {
const auto spv_format = GetImageFormat(format);
return spv_format == spv::ImageFormat::Unknown ? spv::ImageFormat::R32ui : spv_format;
}
Id ImageType(EmitContext& ctx, const ImageDescriptor& desc) {
const spv::ImageFormat format{GetImageFormat(desc.format)};
const Id type{ctx.U32[1]};
@ -1275,7 +1270,7 @@ void EmitContext::DefineImageBuffers(const Info& info, u32& binding) {
if (desc.count != 1) {
throw NotImplementedException("Array of image buffers");
}
const spv::ImageFormat format{GetImageFormatForBuffer(desc.format)};
const spv::ImageFormat format{GetImageFormat(desc.format)};
const Id image_type{TypeImage(U32[1], spv::Dim::Buffer, false, false, false, 2, format)};
const Id pointer_type{TypePointer(spv::StorageClass::UniformConstant, image_type)};
const Id id{AddGlobalVariable(pointer_type, spv::StorageClass::UniformConstant)};

View File

@ -109,10 +109,11 @@ void MaxwellDMA::Launch() {
const bool is_const_a_dst = regs.remap_const.dst_x == RemapConst::Swizzle::CONST_A;
if (regs.launch_dma.remap_enable != 0 && is_const_a_dst) {
ASSERT(regs.remap_const.component_size_minus_one == 3);
accelerate.BufferClear(regs.offset_out, regs.line_length_in, regs.remap_consta_value);
accelerate.BufferClear(regs.offset_out, regs.line_length_in,
regs.remap_const.remap_consta_value);
read_buffer.resize_destructive(regs.line_length_in * sizeof(u32));
std::span<u32> span(reinterpret_cast<u32*>(read_buffer.data()), regs.line_length_in);
std::ranges::fill(span, regs.remap_consta_value);
std::ranges::fill(span, regs.remap_const.remap_consta_value);
memory_manager.WriteBlockUnsafe(regs.offset_out,
reinterpret_cast<u8*>(read_buffer.data()),
regs.line_length_in * sizeof(u32));

View File

@ -214,14 +214,15 @@ public:
NO_WRITE = 6,
};
PackedGPUVAddr address;
u32 remap_consta_value;
u32 remap_constb_value;
union {
BitField<0, 12, u32> dst_components_raw;
BitField<0, 3, Swizzle> dst_x;
BitField<4, 3, Swizzle> dst_y;
BitField<8, 3, Swizzle> dst_z;
BitField<12, 3, Swizzle> dst_w;
BitField<0, 12, u32> dst_components_raw;
BitField<16, 2, u32> component_size_minus_one;
BitField<20, 2, u32> num_src_components_minus_one;
BitField<24, 2, u32> num_dst_components_minus_one;
@ -274,55 +275,57 @@ private:
struct Regs {
union {
struct {
u32 reserved[0x40];
INSERT_PADDING_BYTES_NOINIT(0x100);
u32 nop;
u32 reserved01[0xf];
INSERT_PADDING_BYTES_NOINIT(0x3C);
u32 pm_trigger;
u32 reserved02[0x3f];
INSERT_PADDING_BYTES_NOINIT(0xFC);
Semaphore semaphore;
u32 reserved03[0x2];
INSERT_PADDING_BYTES_NOINIT(0x8);
RenderEnable render_enable;
PhysMode src_phys_mode;
PhysMode dst_phys_mode;
u32 reserved04[0x26];
INSERT_PADDING_BYTES_NOINIT(0x98);
LaunchDMA launch_dma;
u32 reserved05[0x3f];
INSERT_PADDING_BYTES_NOINIT(0xFC);
PackedGPUVAddr offset_in;
PackedGPUVAddr offset_out;
s32 pitch_in;
s32 pitch_out;
u32 line_length_in;
u32 line_count;
u32 reserved06[0xb6];
u32 remap_consta_value;
u32 remap_constb_value;
INSERT_PADDING_BYTES_NOINIT(0x2E0);
RemapConst remap_const;
DMA::Parameters dst_params;
u32 reserved07[0x1];
INSERT_PADDING_BYTES_NOINIT(0x4);
DMA::Parameters src_params;
u32 reserved08[0x275];
INSERT_PADDING_BYTES_NOINIT(0x9D4);
u32 pm_trigger_end;
u32 reserved09[0x3ba];
INSERT_PADDING_BYTES_NOINIT(0xEE8);
};
std::array<u32, NUM_REGS> reg_array;
};
} regs{};
static_assert(sizeof(Regs) == NUM_REGS * 4);
#define ASSERT_REG_POSITION(field_name, position) \
static_assert(offsetof(MaxwellDMA::Regs, field_name) == position * 4, \
static_assert(offsetof(MaxwellDMA::Regs, field_name) == position, \
"Field " #field_name " has invalid position")
ASSERT_REG_POSITION(launch_dma, 0xC0);
ASSERT_REG_POSITION(offset_in, 0x100);
ASSERT_REG_POSITION(offset_out, 0x102);
ASSERT_REG_POSITION(pitch_in, 0x104);
ASSERT_REG_POSITION(pitch_out, 0x105);
ASSERT_REG_POSITION(line_length_in, 0x106);
ASSERT_REG_POSITION(line_count, 0x107);
ASSERT_REG_POSITION(remap_const, 0x1C0);
ASSERT_REG_POSITION(dst_params, 0x1C3);
ASSERT_REG_POSITION(src_params, 0x1CA);
ASSERT_REG_POSITION(semaphore, 0x240);
ASSERT_REG_POSITION(render_enable, 0x254);
ASSERT_REG_POSITION(src_phys_mode, 0x260);
ASSERT_REG_POSITION(launch_dma, 0x300);
ASSERT_REG_POSITION(offset_in, 0x400);
ASSERT_REG_POSITION(offset_out, 0x408);
ASSERT_REG_POSITION(pitch_in, 0x410);
ASSERT_REG_POSITION(pitch_out, 0x414);
ASSERT_REG_POSITION(line_length_in, 0x418);
ASSERT_REG_POSITION(line_count, 0x41C);
ASSERT_REG_POSITION(remap_const, 0x700);
ASSERT_REG_POSITION(dst_params, 0x70C);
ASSERT_REG_POSITION(src_params, 0x728);
ASSERT_REG_POSITION(pm_trigger_end, 0x1114);
#undef ASSERT_REG_POSITION
};

View File

@ -185,7 +185,7 @@ struct FormatTuple {
{VK_FORMAT_BC2_SRGB_BLOCK}, // BC2_SRGB
{VK_FORMAT_BC3_SRGB_BLOCK}, // BC3_SRGB
{VK_FORMAT_BC7_SRGB_BLOCK}, // BC7_SRGB
{VK_FORMAT_R4G4B4A4_UNORM_PACK16}, // A4B4G4R4_UNORM
{VK_FORMAT_A4B4G4R4_UNORM_PACK16_EXT}, // A4B4G4R4_UNORM
{VK_FORMAT_R4G4_UNORM_PACK8}, // G4R4_UNORM
{VK_FORMAT_ASTC_4x4_SRGB_BLOCK}, // ASTC_2D_4X4_SRGB
{VK_FORMAT_ASTC_8x8_SRGB_BLOCK}, // ASTC_2D_8X8_SRGB

View File

@ -120,19 +120,9 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
return usage;
}
/// Returns the preferred format for a VkImage
[[nodiscard]] PixelFormat StorageFormat(PixelFormat format) {
switch (format) {
case PixelFormat::A8B8G8R8_SRGB:
return PixelFormat::A8B8G8R8_UNORM;
default:
return format;
}
}
[[nodiscard]] VkImageCreateInfo MakeImageCreateInfo(const Device& device, const ImageInfo& info) {
const PixelFormat format = StorageFormat(info.format);
const auto format_info = MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, false, format);
const auto format_info =
MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, false, info.format);
VkImageCreateFlags flags{};
if (info.type == ImageType::e2D && info.resources.layers >= 6 &&
info.size.width == info.size.height && !device.HasBrokenCubeImageCompability()) {
@ -157,7 +147,7 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
.arrayLayers = static_cast<u32>(info.resources.layers),
.samples = ConvertSampleCount(info.num_samples),
.tiling = VK_IMAGE_TILING_OPTIMAL,
.usage = ImageUsageFlags(format_info, format),
.usage = ImageUsageFlags(format_info, info.format),
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.queueFamilyIndexCount = 0,
.pQueueFamilyIndices = nullptr,
@ -600,7 +590,7 @@ void CopyBufferToImage(vk::CommandBuffer cmdbuf, VkBuffer src_buffer, VkImage im
}
void TryTransformSwizzleIfNeeded(PixelFormat format, std::array<SwizzleSource, 4>& swizzle,
bool emulate_bgr565) {
bool emulate_bgr565, bool emulate_a4b4g4r4) {
switch (format) {
case PixelFormat::A1B5G5R5_UNORM:
std::ranges::transform(swizzle, swizzle.begin(), SwapBlueRed);
@ -616,6 +606,11 @@ void TryTransformSwizzleIfNeeded(PixelFormat format, std::array<SwizzleSource, 4
case PixelFormat::G4R4_UNORM:
std::ranges::transform(swizzle, swizzle.begin(), SwapGreenRed);
break;
case PixelFormat::A4B4G4R4_UNORM:
if (emulate_a4b4g4r4) {
std::ranges::reverse(swizzle);
}
break;
default:
break;
}
@ -1044,15 +1039,27 @@ void TextureCacheRuntime::BlitImage(Framebuffer* dst_framebuffer, ImageView& dst
dst_region, src_region, filter, operation);
return;
}
ASSERT(src.format == dst.format);
if (aspect_mask == (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) {
if (!device.IsBlitDepthStencilSupported()) {
const auto format = src.format;
const auto can_blit_depth_stencil = [this, format] {
switch (format) {
case VideoCore::Surface::PixelFormat::D24_UNORM_S8_UINT:
case VideoCore::Surface::PixelFormat::S8_UINT_D24_UNORM:
return device.IsBlitDepth24Stencil8Supported();
case VideoCore::Surface::PixelFormat::D32_FLOAT_S8_UINT:
return device.IsBlitDepth32Stencil8Supported();
default:
UNREACHABLE();
}
}();
if (!can_blit_depth_stencil) {
UNIMPLEMENTED_IF(is_src_msaa || is_dst_msaa);
blit_image_helper.BlitDepthStencil(dst_framebuffer, src.DepthView(), src.StencilView(),
dst_region, src_region, filter, operation);
return;
}
}
ASSERT(src.format == dst.format);
ASSERT(!(is_dst_msaa && !is_src_msaa));
ASSERT(operation == Fermi2D::Operation::SrcCopy);
@ -1626,8 +1633,8 @@ bool Image::NeedsScaleHelper() const {
return true;
}
static constexpr auto OPTIMAL_FORMAT = FormatType::Optimal;
const PixelFormat format = StorageFormat(info.format);
const auto vk_format = MaxwellToVK::SurfaceFormat(device, OPTIMAL_FORMAT, false, format).format;
const auto vk_format =
MaxwellToVK::SurfaceFormat(device, OPTIMAL_FORMAT, false, info.format).format;
const auto blit_usage = VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT;
const bool needs_blit_helper = !device.IsFormatSupported(vk_format, blit_usage, OPTIMAL_FORMAT);
return needs_blit_helper;
@ -1649,7 +1656,8 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI
};
if (!info.IsRenderTarget()) {
swizzle = info.Swizzle();
TryTransformSwizzleIfNeeded(format, swizzle, device->MustEmulateBGR565());
TryTransformSwizzleIfNeeded(format, swizzle, device->MustEmulateBGR565(),
!device->IsExt4444FormatsSupported());
if ((aspect_mask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) != 0) {
std::ranges::transform(swizzle, swizzle.begin(), ConvertGreenRed);
}

View File

@ -76,6 +76,11 @@ constexpr std::array VK_FORMAT_R32G32B32_SFLOAT{
VK_FORMAT_UNDEFINED,
};
constexpr std::array VK_FORMAT_A4B4G4R4_UNORM_PACK16{
VK_FORMAT_R4G4B4A4_UNORM_PACK16,
VK_FORMAT_UNDEFINED,
};
} // namespace Alternatives
enum class NvidiaArchitecture {
@ -110,6 +115,8 @@ constexpr const VkFormat* GetFormatAlternatives(VkFormat format) {
return Alternatives::R8G8B8_SSCALED.data();
case VK_FORMAT_R32G32B32_SFLOAT:
return Alternatives::VK_FORMAT_R32G32B32_SFLOAT.data();
case VK_FORMAT_A4B4G4R4_UNORM_PACK16_EXT:
return Alternatives::VK_FORMAT_A4B4G4R4_UNORM_PACK16.data();
default:
return nullptr;
}
@ -238,6 +245,7 @@ std::unordered_map<VkFormat, VkFormatProperties> GetFormatProperties(vk::Physica
VK_FORMAT_R32_SINT,
VK_FORMAT_R32_UINT,
VK_FORMAT_R4G4B4A4_UNORM_PACK16,
VK_FORMAT_A4B4G4R4_UNORM_PACK16_EXT,
VK_FORMAT_R4G4_UNORM_PACK8,
VK_FORMAT_R5G5B5A1_UNORM_PACK16,
VK_FORMAT_R5G6B5_UNORM_PACK16,
@ -420,7 +428,8 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
first_next = &diagnostics_nv;
}
is_blit_depth_stencil_supported = TestDepthStencilBlits();
is_blit_depth24_stencil8_supported = TestDepthStencilBlits(VK_FORMAT_D24_UNORM_S8_UINT);
is_blit_depth32_stencil8_supported = TestDepthStencilBlits(VK_FORMAT_D32_SFLOAT_S8_UINT);
is_optimal_astc_supported = ComputeIsOptimalAstcSupported();
is_warp_potentially_bigger = !extensions.subgroup_size_control ||
properties.subgroup_size_control.maxSubgroupSize > GuestWarpSize;
@ -774,14 +783,13 @@ bool Device::ComputeIsOptimalAstcSupported() const {
return true;
}
bool Device::TestDepthStencilBlits() const {
bool Device::TestDepthStencilBlits(VkFormat format) const {
static constexpr VkFormatFeatureFlags required_features =
VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT;
const auto test_features = [](VkFormatProperties props) {
return (props.optimalTilingFeatures & required_features) == required_features;
};
return test_features(format_properties.at(VK_FORMAT_D32_SFLOAT_S8_UINT)) &&
test_features(format_properties.at(VK_FORMAT_D24_UNORM_S8_UINT));
return test_features(format_properties.at(format));
}
bool Device::IsFormatSupported(VkFormat wanted_format, VkFormatFeatureFlags wanted_usage,

View File

@ -45,6 +45,7 @@ VK_DEFINE_HANDLE(VmaAllocator)
FEATURE(EXT, ExtendedDynamicState, EXTENDED_DYNAMIC_STATE, extended_dynamic_state) \
FEATURE(EXT, ExtendedDynamicState2, EXTENDED_DYNAMIC_STATE_2, extended_dynamic_state2) \
FEATURE(EXT, ExtendedDynamicState3, EXTENDED_DYNAMIC_STATE_3, extended_dynamic_state3) \
FEATURE(EXT, 4444Formats, 4444_FORMATS, format_a4b4g4r4) \
FEATURE(EXT, IndexTypeUint8, INDEX_TYPE_UINT8, index_type_uint8) \
FEATURE(EXT, LineRasterization, LINE_RASTERIZATION, line_rasterization) \
FEATURE(EXT, PrimitiveTopologyListRestart, PRIMITIVE_TOPOLOGY_LIST_RESTART, \
@ -97,6 +98,7 @@ VK_DEFINE_HANDLE(VmaAllocator)
EXTENSION_NAME(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME) \
EXTENSION_NAME(VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME) \
EXTENSION_NAME(VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME) \
EXTENSION_NAME(VK_EXT_4444_FORMATS_EXTENSION_NAME) \
EXTENSION_NAME(VK_EXT_LINE_RASTERIZATION_EXTENSION_NAME) \
EXTENSION_NAME(VK_EXT_ROBUSTNESS_2_EXTENSION_NAME) \
EXTENSION_NAME(VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME) \
@ -144,6 +146,7 @@ VK_DEFINE_HANDLE(VmaAllocator)
#define FOR_EACH_VK_RECOMMENDED_FEATURE(FEATURE_NAME) \
FEATURE_NAME(custom_border_color, customBorderColors) \
FEATURE_NAME(extended_dynamic_state, extendedDynamicState) \
FEATURE_NAME(format_a4b4g4r4, formatA4B4G4R4) \
FEATURE_NAME(index_type_uint8, indexTypeUint8) \
FEATURE_NAME(primitive_topology_list_restart, primitiveTopologyListRestart) \
FEATURE_NAME(provoking_vertex, provokingVertexLast) \
@ -359,9 +362,14 @@ public:
return features.features.depthBounds;
}
/// Returns true when blitting from and to depth stencil images is supported.
bool IsBlitDepthStencilSupported() const {
return is_blit_depth_stencil_supported;
/// Returns true when blitting from and to D24S8 images is supported.
bool IsBlitDepth24Stencil8Supported() const {
return is_blit_depth24_stencil8_supported;
}
/// Returns true when blitting from and to D32S8 images is supported.
bool IsBlitDepth32Stencil8Supported() const {
return is_blit_depth32_stencil8_supported;
}
/// Returns true if the device supports VK_NV_viewport_swizzle.
@ -488,6 +496,11 @@ public:
return extensions.extended_dynamic_state3;
}
/// Returns true if the device supports VK_EXT_4444_formats.
bool IsExt4444FormatsSupported() const {
return features.format_a4b4g4r4.formatA4B4G4R4;
}
/// Returns true if the device supports VK_EXT_extended_dynamic_state3.
bool IsExtExtendedDynamicState3BlendingSupported() const {
return dynamic_state3_blending;
@ -666,7 +679,7 @@ private:
bool ComputeIsOptimalAstcSupported() const;
/// Returns true if the device natively supports blitting depth stencil images.
bool TestDepthStencilBlits() const;
bool TestDepthStencilBlits(VkFormat format) const;
private:
VkInstance instance; ///< Vulkan instance.
@ -730,25 +743,26 @@ private:
VkPhysicalDeviceProperties2 properties2{};
// Misc features
bool is_optimal_astc_supported{}; ///< Support for all guest ASTC formats.
bool is_blit_depth_stencil_supported{}; ///< Support for blitting from and to depth stencil.
bool is_warp_potentially_bigger{}; ///< Host warp size can be bigger than guest.
bool is_integrated{}; ///< Is GPU an iGPU.
bool is_virtual{}; ///< Is GPU a virtual GPU.
bool is_non_gpu{}; ///< Is SoftwareRasterizer, FPGA, non-GPU device.
bool has_broken_compute{}; ///< Compute shaders can cause crashes
bool has_broken_cube_compatibility{}; ///< Has broken cube compatibility bit
bool has_renderdoc{}; ///< Has RenderDoc attached
bool has_nsight_graphics{}; ///< Has Nsight Graphics attached
bool supports_d24_depth{}; ///< Supports D24 depth buffers.
bool cant_blit_msaa{}; ///< Does not support MSAA<->MSAA blitting.
bool must_emulate_scaled_formats{}; ///< Requires scaled vertex format emulation
bool must_emulate_bgr565{}; ///< Emulates BGR565 by swizzling RGB565 format.
bool dynamic_state3_blending{}; ///< Has all blending features of dynamic_state3.
bool dynamic_state3_enables{}; ///< Has all enables features of dynamic_state3.
bool supports_conditional_barriers{}; ///< Allows barriers in conditional control flow.
u64 device_access_memory{}; ///< Total size of device local memory in bytes.
u32 sets_per_pool{}; ///< Sets per Description Pool
bool is_optimal_astc_supported{}; ///< Support for all guest ASTC formats.
bool is_blit_depth24_stencil8_supported{}; ///< Support for blitting from and to D24S8.
bool is_blit_depth32_stencil8_supported{}; ///< Support for blitting from and to D32S8.
bool is_warp_potentially_bigger{}; ///< Host warp size can be bigger than guest.
bool is_integrated{}; ///< Is GPU an iGPU.
bool is_virtual{}; ///< Is GPU a virtual GPU.
bool is_non_gpu{}; ///< Is SoftwareRasterizer, FPGA, non-GPU device.
bool has_broken_compute{}; ///< Compute shaders can cause crashes
bool has_broken_cube_compatibility{}; ///< Has broken cube compatibility bit
bool has_renderdoc{}; ///< Has RenderDoc attached
bool has_nsight_graphics{}; ///< Has Nsight Graphics attached
bool supports_d24_depth{}; ///< Supports D24 depth buffers.
bool cant_blit_msaa{}; ///< Does not support MSAA<->MSAA blitting.
bool must_emulate_scaled_formats{}; ///< Requires scaled vertex format emulation
bool must_emulate_bgr565{}; ///< Emulates BGR565 by swizzling RGB565 format.
bool dynamic_state3_blending{}; ///< Has all blending features of dynamic_state3.
bool dynamic_state3_enables{}; ///< Has all enables features of dynamic_state3.
bool supports_conditional_barriers{}; ///< Allows barriers in conditional control flow.
u64 device_access_memory{}; ///< Total size of device local memory in bytes.
u32 sets_per_pool{}; ///< Sets per Description Pool
// Telemetry parameters
std::set<std::string, std::less<>> supported_extensions; ///< Reported Vulkan extensions.

View File

@ -1551,6 +1551,7 @@ void GMainWindow::ConnectMenuEvents() {
// Tools
connect_menu(ui->action_Rederive, std::bind(&GMainWindow::OnReinitializeKeys, this,
ReinitializeKeyBehavior::Warning));
connect_menu(ui->action_Load_Mii_Edit, &GMainWindow::OnMiiEdit);
connect_menu(ui->action_Capture_Screenshot, &GMainWindow::OnCaptureScreenshot);
// TAS
@ -1590,6 +1591,8 @@ void GMainWindow::UpdateMenuState() {
}
multiplayer_state->UpdateNotificationStatus();
ui->action_Load_Mii_Edit->setEnabled(CheckFirmwarePresence());
}
void GMainWindow::OnDisplayTitleBars(bool show) {
@ -4134,6 +4137,27 @@ void GMainWindow::OnToggleStatusBar() {
statusBar()->setVisible(ui->action_Show_Status_Bar->isChecked());
}
void GMainWindow::OnMiiEdit() {
constexpr u64 MiiEditId = 0x0100000000001009ull;
auto bis_system = system->GetFileSystemController().GetSystemNANDContents();
if (!bis_system) {
QMessageBox::warning(this, tr("No firmware available"),
tr("Please install the firmware to use the Mii editor."));
return;
}
auto mii_applet_nca = bis_system->GetEntry(MiiEditId, FileSys::ContentRecordType::Program);
if (!mii_applet_nca) {
QMessageBox::warning(this, tr("Mii Edit Applet"),
tr("Mii editor is not available. Please reinstall firmware."));
return;
}
const auto filename = QString::fromStdString((mii_applet_nca->GetFullPath()));
UISettings::values.roms_path = QFileInfo(filename).path();
BootGame(filename);
}
void GMainWindow::OnCaptureScreenshot() {
if (emu_thread == nullptr || !emu_thread->IsRunning()) {
return;
@ -4540,6 +4564,8 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
if (behavior == ReinitializeKeyBehavior::Warning) {
game_list->PopulateAsync(UISettings::values.game_dirs);
}
UpdateMenuState();
}
bool GMainWindow::CheckSystemArchiveDecryption() {
@ -4561,6 +4587,22 @@ bool GMainWindow::CheckSystemArchiveDecryption() {
return mii_nca->GetRomFS().get() != nullptr;
}
bool GMainWindow::CheckFirmwarePresence() {
constexpr u64 MiiEditId = 0x0100000000001009ull;
auto bis_system = system->GetFileSystemController().GetSystemNANDContents();
if (!bis_system) {
return false;
}
auto mii_applet_nca = bis_system->GetEntry(MiiEditId, FileSys::ContentRecordType::Program);
if (!mii_applet_nca) {
return false;
}
return true;
}
bool GMainWindow::SelectRomFSDumpTarget(const FileSys::ContentProvider& installed, u64 program_id,
u64* selected_title_id, u8* selected_content_record_type) {
using ContentInfo = std::pair<FileSys::TitleType, FileSys::ContentRecordType>;

View File

@ -365,6 +365,7 @@ private slots:
void ResetWindowSize720();
void ResetWindowSize900();
void ResetWindowSize1080();
void OnMiiEdit();
void OnCaptureScreenshot();
void OnReinitializeKeys(ReinitializeKeyBehavior behavior);
void OnLanguageChanged(const QString& locale);
@ -409,6 +410,7 @@ private:
void OpenPerGameConfiguration(u64 title_id, const std::string& file_name);
bool CheckDarkMode();
bool CheckSystemArchiveDecryption();
bool CheckFirmwarePresence();
void ConfigureFilesystemProvider(const std::string& filepath);
QString GetTasStateDescription() const;

View File

@ -150,6 +150,8 @@
<addaction name="action_Rederive"/>
<addaction name="action_Verify_installed_contents"/>
<addaction name="separator"/>
<addaction name="action_Load_Mii_Edit"/>
<addaction name="separator"/>
<addaction name="action_Capture_Screenshot"/>
<addaction name="menuTAS"/>
</widget>
@ -217,7 +219,7 @@
</action>
<action name="action_Verify_installed_contents">
<property name="text">
<string>Verify installed contents</string>
<string>&amp;Verify Installed Contents</string>
</property>
</action>
<action name="action_About">
@ -368,6 +370,11 @@
<string>&amp;Capture Screenshot</string>
</property>
</action>
<action name="action_Load_Mii_Edit">
<property name="text">
<string>Open &amp;Mii Editor</string>
</property>
</action>
<action name="action_Configure_Tas">
<property name="text">
<string>&amp;Configure TAS...</string>