diff options
Diffstat (limited to '')
28 files changed, 405 insertions, 115 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index e8bf68866..99602699a 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -142,6 +142,7 @@ add_library(core STATIC frontend/emu_window.h frontend/framebuffer_layout.cpp frontend/framebuffer_layout.h + frontend/graphics_context.h hid/emulated_console.cpp hid/emulated_console.h hid/emulated_controller.cpp diff --git a/src/core/core.cpp b/src/core/core.cpp index 4406ae30e..7ba704f18 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -216,6 +216,14 @@ struct System::Impl { } } + void SetNVDECActive(bool is_nvdec_active) { + nvdec_active = is_nvdec_active; + } + + bool GetNVDECActive() { + return nvdec_active; + } + void InitializeDebugger(System& system, u16 port) { debugger = std::make_unique<Debugger>(system, port); } @@ -485,6 +493,8 @@ struct System::Impl { std::atomic_bool is_powered_on{}; bool exit_lock = false; + bool nvdec_active{}; + Reporter reporter; std::unique_ptr<Memory::CheatEngine> cheat_engine; std::unique_ptr<Tools::Freezer> memory_freezer; @@ -594,6 +604,14 @@ void System::UnstallApplication() { impl->UnstallApplication(); } +void System::SetNVDECActive(bool is_nvdec_active) { + impl->SetNVDECActive(is_nvdec_active); +} + +bool System::GetNVDECActive() { + return impl->GetNVDECActive(); +} + void System::InitializeDebugger() { impl->InitializeDebugger(*this, Settings::values.gdbstub_port.GetValue()); } diff --git a/src/core/core.h b/src/core/core.h index 4f153154f..ff2e4bd30 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -189,6 +189,9 @@ public: std::unique_lock<std::mutex> StallApplication(); void UnstallApplication(); + void SetNVDECActive(bool is_nvdec_active); + [[nodiscard]] bool GetNVDECActive(); + /** * Initialize the debugger. */ diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp index 65a9fe802..4ff2c50e5 100644 --- a/src/core/crypto/key_manager.cpp +++ b/src/core/crypto/key_manager.cpp @@ -569,6 +569,10 @@ std::optional<std::pair<Key128, Key128>> ParseTicket(const Ticket& ticket, } KeyManager::KeyManager() { + ReloadKeys(); +} + +void KeyManager::ReloadKeys() { // Initialize keys const auto yuzu_keys_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::KeysDir); @@ -702,6 +706,10 @@ void KeyManager::LoadFromFile(const std::filesystem::path& file_path, bool is_ti } } +bool KeyManager::AreKeysLoaded() const { + return !s128_keys.empty() && !s256_keys.empty(); +} + bool KeyManager::BaseDeriveNecessary() const { const auto check_key_existence = [this](auto key_type, u64 index1 = 0, u64 index2 = 0) { return !HasKey(key_type, index1, index2); diff --git a/src/core/crypto/key_manager.h b/src/core/crypto/key_manager.h index 673cec463..8c864503b 100644 --- a/src/core/crypto/key_manager.h +++ b/src/core/crypto/key_manager.h @@ -267,6 +267,9 @@ public: bool AddTicketCommon(Ticket raw); bool AddTicketPersonalized(Ticket raw); + void ReloadKeys(); + bool AreKeysLoaded() const; + private: KeyManager(); diff --git a/src/core/device_memory.cpp b/src/core/device_memory.cpp index f8b5be2b4..de3f8ef8f 100644 --- a/src/core/device_memory.cpp +++ b/src/core/device_memory.cpp @@ -6,9 +6,15 @@ namespace Core { +#ifdef ANDROID +constexpr size_t VirtualReserveSize = 1ULL << 38; +#else +constexpr size_t VirtualReserveSize = 1ULL << 39; +#endif + DeviceMemory::DeviceMemory() : buffer{Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetIntendedMemorySize(), - 1ULL << 39} {} + VirtualReserveSize} {} DeviceMemory::~DeviceMemory() = default; } // namespace Core diff --git a/src/core/file_sys/control_metadata.cpp b/src/core/file_sys/control_metadata.cpp index 50f44f598..cd9ac2e75 100644 --- a/src/core/file_sys/control_metadata.cpp +++ b/src/core/file_sys/control_metadata.cpp @@ -23,8 +23,8 @@ const std::array<const char*, 16> LANGUAGE_NAMES{{ "Portuguese", "Russian", "Korean", - "Taiwanese", - "Chinese", + "TraditionalChinese", + "SimplifiedChinese", "BrazilianPortuguese", }}; @@ -45,17 +45,17 @@ constexpr std::array<Language, 18> language_to_codes = {{ Language::German, Language::Italian, Language::Spanish, - Language::Chinese, + Language::SimplifiedChinese, Language::Korean, Language::Dutch, Language::Portuguese, Language::Russian, - Language::Taiwanese, + Language::TraditionalChinese, Language::BritishEnglish, Language::CanadianFrench, Language::LatinAmericanSpanish, - Language::Chinese, - Language::Taiwanese, + Language::SimplifiedChinese, + Language::TraditionalChinese, Language::BrazilianPortuguese, }}; diff --git a/src/core/file_sys/control_metadata.h b/src/core/file_sys/control_metadata.h index 6a81873b1..c98efb00d 100644 --- a/src/core/file_sys/control_metadata.h +++ b/src/core/file_sys/control_metadata.h @@ -84,8 +84,8 @@ enum class Language : u8 { Portuguese = 10, Russian = 11, Korean = 12, - Taiwanese = 13, - Chinese = 14, + TraditionalChinese = 13, + SimplifiedChinese = 14, BrazilianPortuguese = 15, Default = 255, diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp index f786f2add..4e61d4335 100644 --- a/src/core/file_sys/patch_manager.cpp +++ b/src/core/file_sys/patch_manager.cpp @@ -25,6 +25,8 @@ #include "core/file_sys/vfs_layered.h" #include "core/file_sys/vfs_vector.h" #include "core/hle/service/filesystem/filesystem.h" +#include "core/hle/service/ns/language.h" +#include "core/hle/service/set/set.h" #include "core/loader/loader.h" #include "core/loader/nso.h" #include "core/memory/cheat_engine.h" @@ -624,8 +626,37 @@ PatchManager::Metadata PatchManager::ParseControlNCA(const NCA& nca) const { auto nacp = nacp_file == nullptr ? nullptr : std::make_unique<NACP>(nacp_file); + // Get language code from settings + const auto language_code = + Service::Set::GetLanguageCodeFromIndex(Settings::values.language_index.GetValue()); + + // Convert to application language and get priority list + const auto application_language = + Service::NS::ConvertToApplicationLanguage(language_code) + .value_or(Service::NS::ApplicationLanguage::AmericanEnglish); + const auto language_priority_list = + Service::NS::GetApplicationLanguagePriorityList(application_language); + + // Convert to language names + auto priority_language_names = FileSys::LANGUAGE_NAMES; // Copy + if (language_priority_list) { + for (size_t i = 0; i < priority_language_names.size(); ++i) { + // Relies on FileSys::LANGUAGE_NAMES being in the same order as + // Service::NS::ApplicationLanguage + const auto language_index = static_cast<u8>(language_priority_list->at(i)); + + if (language_index < FileSys::LANGUAGE_NAMES.size()) { + priority_language_names[i] = FileSys::LANGUAGE_NAMES[language_index]; + } else { + // Not a catastrophe, unlikely to happen + LOG_WARNING(Loader, "Invalid language index {}", language_index); + } + } + } + + // Get first matching icon VirtualFile icon_file; - for (const auto& language : FileSys::LANGUAGE_NAMES) { + for (const auto& language : priority_language_names) { icon_file = extracted->GetFile(std::string("icon_").append(language).append(".dat")); if (icon_file != nullptr) { break; diff --git a/src/core/frontend/emu_window.cpp b/src/core/frontend/emu_window.cpp index 1be2dccb0..d1f1ca8c9 100644 --- a/src/core/frontend/emu_window.cpp +++ b/src/core/frontend/emu_window.cpp @@ -6,8 +6,6 @@ namespace Core::Frontend { -GraphicsContext::~GraphicsContext() = default; - EmuWindow::EmuWindow() { // TODO: Find a better place to set this. config.min_client_area_size = diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h index 1093800f6..a72df034e 100644 --- a/src/core/frontend/emu_window.h +++ b/src/core/frontend/emu_window.h @@ -5,11 +5,14 @@ #include <memory> #include <utility> + #include "common/common_types.h" #include "core/frontend/framebuffer_layout.h" namespace Core::Frontend { +class GraphicsContext; + /// Information for the Graphics Backends signifying what type of screen pointer is in /// WindowInformation enum class WindowSystemType { @@ -22,51 +25,6 @@ enum class WindowSystemType { }; /** - * Represents a drawing context that supports graphics operations. - */ -class GraphicsContext { -public: - virtual ~GraphicsContext(); - - /// Inform the driver to swap the front/back buffers and present the current image - virtual void SwapBuffers() {} - - /// Makes the graphics context current for the caller thread - virtual void MakeCurrent() {} - - /// Releases (dunno if this is the "right" word) the context from the caller thread - virtual void DoneCurrent() {} - - class Scoped { - public: - [[nodiscard]] explicit Scoped(GraphicsContext& context_) : context(context_) { - context.MakeCurrent(); - } - ~Scoped() { - if (active) { - context.DoneCurrent(); - } - } - - /// In the event that context was destroyed before the Scoped is destroyed, this provides a - /// mechanism to prevent calling a destroyed object's method during the deconstructor - void Cancel() { - active = false; - } - - private: - GraphicsContext& context; - bool active{true}; - }; - - /// Calls MakeCurrent on the context and calls DoneCurrent when the scope for the returned value - /// ends - [[nodiscard]] Scoped Acquire() { - return Scoped{*this}; - } -}; - -/** * Abstraction class used to provide an interface between emulation code and the frontend * (e.g. SDL, QGLWidget, GLFW, etc...). * diff --git a/src/core/frontend/graphics_context.h b/src/core/frontend/graphics_context.h new file mode 100644 index 000000000..7554c1583 --- /dev/null +++ b/src/core/frontend/graphics_context.h @@ -0,0 +1,62 @@ +// SPDX-FileCopyrightText: 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <memory> + +#include "common/dynamic_library.h" + +namespace Core::Frontend { + +/** + * Represents a drawing context that supports graphics operations. + */ +class GraphicsContext { +public: + virtual ~GraphicsContext() = default; + + /// Inform the driver to swap the front/back buffers and present the current image + virtual void SwapBuffers() {} + + /// Makes the graphics context current for the caller thread + virtual void MakeCurrent() {} + + /// Releases (dunno if this is the "right" word) the context from the caller thread + virtual void DoneCurrent() {} + + /// Gets the GPU driver library (used by Android only) + virtual std::shared_ptr<Common::DynamicLibrary> GetDriverLibrary() { + return {}; + } + + class Scoped { + public: + [[nodiscard]] explicit Scoped(GraphicsContext& context_) : context(context_) { + context.MakeCurrent(); + } + ~Scoped() { + if (active) { + context.DoneCurrent(); + } + } + + /// In the event that context was destroyed before the Scoped is destroyed, this provides a + /// mechanism to prevent calling a destroyed object's method during the deconstructor + void Cancel() { + active = false; + } + + private: + GraphicsContext& context; + bool active{true}; + }; + + /// Calls MakeCurrent on the context and calls DoneCurrent when the scope for the returned value + /// ends + [[nodiscard]] Scoped Acquire() { + return Scoped{*this}; + } +}; + +} // namespace Core::Frontend diff --git a/src/core/hid/emulated_console.cpp b/src/core/hid/emulated_console.cpp index 17d663379..b4afd930e 100644 --- a/src/core/hid/emulated_console.cpp +++ b/src/core/hid/emulated_console.cpp @@ -13,7 +13,7 @@ EmulatedConsole::~EmulatedConsole() = default; void EmulatedConsole::ReloadFromSettings() { // Using first motion device from player 1. No need to assign any unique config at the moment const auto& player = Settings::values.players.GetValue()[0]; - motion_params = Common::ParamPackage(player.motions[0]); + motion_params[0] = Common::ParamPackage(player.motions[0]); ReloadInput(); } @@ -74,14 +74,30 @@ void EmulatedConsole::ReloadInput() { // If you load any device here add the equivalent to the UnloadInput() function SetTouchParams(); - motion_devices = Common::Input::CreateInputDevice(motion_params); - if (motion_devices) { - motion_devices->SetCallback({ + motion_params[1] = Common::ParamPackage{"engine:virtual_gamepad,port:8,motion:0"}; + + for (std::size_t index = 0; index < motion_devices.size(); ++index) { + motion_devices[index] = Common::Input::CreateInputDevice(motion_params[index]); + if (!motion_devices[index]) { + continue; + } + motion_devices[index]->SetCallback({ .on_change = [this](const Common::Input::CallbackStatus& callback) { SetMotion(callback); }, }); } + // Restore motion state + auto& emulated_motion = console.motion_values.emulated; + auto& motion = console.motion_state; + emulated_motion.ResetRotations(); + emulated_motion.ResetQuaternion(); + motion.accel = emulated_motion.GetAcceleration(); + motion.gyro = emulated_motion.GetGyroscope(); + motion.rotation = emulated_motion.GetRotations(); + motion.orientation = emulated_motion.GetOrientation(); + motion.is_at_rest = !emulated_motion.IsMoving(motion_sensitivity); + // Unique index for identifying touch device source std::size_t index = 0; for (auto& touch_device : touch_devices) { @@ -100,7 +116,9 @@ void EmulatedConsole::ReloadInput() { } void EmulatedConsole::UnloadInput() { - motion_devices.reset(); + for (auto& motion : motion_devices) { + motion.reset(); + } for (auto& touch : touch_devices) { touch.reset(); } @@ -133,11 +151,11 @@ void EmulatedConsole::RestoreConfig() { } Common::ParamPackage EmulatedConsole::GetMotionParam() const { - return motion_params; + return motion_params[0]; } void EmulatedConsole::SetMotionParam(Common::ParamPackage param) { - motion_params = std::move(param); + motion_params[0] = std::move(param); ReloadInput(); } diff --git a/src/core/hid/emulated_console.h b/src/core/hid/emulated_console.h index 697ecd2d6..79114bb6d 100644 --- a/src/core/hid/emulated_console.h +++ b/src/core/hid/emulated_console.h @@ -29,10 +29,10 @@ struct ConsoleMotionInfo { MotionInput emulated{}; }; -using ConsoleMotionDevices = std::unique_ptr<Common::Input::InputDevice>; +using ConsoleMotionDevices = std::array<std::unique_ptr<Common::Input::InputDevice>, 2>; using TouchDevices = std::array<std::unique_ptr<Common::Input::InputDevice>, MaxTouchDevices>; -using ConsoleMotionParams = Common::ParamPackage; +using ConsoleMotionParams = std::array<Common::ParamPackage, 2>; using TouchParams = std::array<Common::ParamPackage, MaxTouchDevices>; using ConsoleMotionValues = ConsoleMotionInfo; diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp index bbfea7117..0a7777732 100644 --- a/src/core/hid/emulated_controller.cpp +++ b/src/core/hid/emulated_controller.cpp @@ -193,6 +193,8 @@ void EmulatedController::LoadDevices() { Common::Input::CreateInputDevice); std::ranges::transform(virtual_stick_params, virtual_stick_devices.begin(), Common::Input::CreateInputDevice); + std::ranges::transform(virtual_motion_params, virtual_motion_devices.begin(), + Common::Input::CreateInputDevice); } void EmulatedController::LoadTASParams() { @@ -253,6 +255,12 @@ void EmulatedController::LoadVirtualGamepadParams() { for (auto& param : virtual_stick_params) { param = common_params; } + for (auto& param : virtual_stick_params) { + param = common_params; + } + for (auto& param : virtual_motion_params) { + param = common_params; + } // TODO(german77): Replace this with an input profile or something better virtual_button_params[Settings::NativeButton::A].Set("button", 0); @@ -284,6 +292,9 @@ void EmulatedController::LoadVirtualGamepadParams() { virtual_stick_params[Settings::NativeAnalog::LStick].Set("range", 1.0f); virtual_stick_params[Settings::NativeAnalog::RStick].Set("deadzone", 0.0f); virtual_stick_params[Settings::NativeAnalog::RStick].Set("range", 1.0f); + + virtual_motion_params[Settings::NativeMotion::MotionLeft].Set("motion", 0); + virtual_motion_params[Settings::NativeMotion::MotionRight].Set("motion", 0); } void EmulatedController::ReloadInput() { @@ -463,6 +474,18 @@ void EmulatedController::ReloadInput() { }, }); } + + for (std::size_t index = 0; index < virtual_motion_devices.size(); ++index) { + if (!virtual_motion_devices[index]) { + continue; + } + virtual_motion_devices[index]->SetCallback({ + .on_change = + [this, index](const Common::Input::CallbackStatus& callback) { + SetMotion(callback, index); + }, + }); + } turbo_button_state = 0; } @@ -500,6 +523,9 @@ void EmulatedController::UnloadInput() { for (auto& stick : virtual_stick_devices) { stick.reset(); } + for (auto& motion : virtual_motion_devices) { + motion.reset(); + } for (auto& camera : camera_devices) { camera.reset(); } diff --git a/src/core/hid/emulated_controller.h b/src/core/hid/emulated_controller.h index 88fad2f56..09fe1a0ab 100644 --- a/src/core/hid/emulated_controller.h +++ b/src/core/hid/emulated_controller.h @@ -568,8 +568,10 @@ private: // Virtual gamepad related variables ButtonParams virtual_button_params; StickParams virtual_stick_params; + ControllerMotionParams virtual_motion_params; ButtonDevices virtual_button_devices; StickDevices virtual_stick_devices; + ControllerMotionDevices virtual_motion_devices; mutable std::mutex mutex; mutable std::mutex callback_mutex; diff --git a/src/core/hle/kernel/k_address_space_info.cpp b/src/core/hle/kernel/k_address_space_info.cpp index c36eb5dc4..32173e52b 100644 --- a/src/core/hle/kernel/k_address_space_info.cpp +++ b/src/core/hle/kernel/k_address_space_info.cpp @@ -25,7 +25,12 @@ constexpr std::array<KAddressSpaceInfo, 13> AddressSpaceInfos{{ { .bit_width = 36, .address = 2_GiB , .size = 64_GiB - 2_GiB , .type = KAddressSpaceInfo::Type::MapLarge, }, { .bit_width = 36, .address = Size_Invalid, .size = 8_GiB , .type = KAddressSpaceInfo::Type::Heap, }, { .bit_width = 36, .address = Size_Invalid, .size = 6_GiB , .type = KAddressSpaceInfo::Type::Alias, }, +#ifdef ANDROID + // With Android, we use a 38-bit address space due to memory limitations. This should (safely) truncate ASLR region. + { .bit_width = 39, .address = 128_MiB , .size = 256_GiB - 128_MiB, .type = KAddressSpaceInfo::Type::Map39Bit, }, +#else { .bit_width = 39, .address = 128_MiB , .size = 512_GiB - 128_MiB, .type = KAddressSpaceInfo::Type::Map39Bit, }, +#endif { .bit_width = 39, .address = Size_Invalid, .size = 64_GiB , .type = KAddressSpaceInfo::Type::MapSmall }, { .bit_width = 39, .address = Size_Invalid, .size = 8_GiB , .type = KAddressSpaceInfo::Type::Heap, }, { .bit_width = 39, .address = Size_Invalid, .size = 64_GiB , .type = KAddressSpaceInfo::Type::Alias, }, diff --git a/src/core/hle/service/acc/profile_manager.cpp b/src/core/hle/service/acc/profile_manager.cpp index 63fd5bfd6..5542d6cbc 100644 --- a/src/core/hle/service/acc/profile_manager.cpp +++ b/src/core/hle/service/acc/profile_manager.cpp @@ -46,6 +46,7 @@ ProfileManager::ProfileManager() { // Create an user if none are present if (user_count == 0) { CreateNewUser(UUID::MakeRandom(), "yuzu"); + WriteUserSaveFile(); } auto current = diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp index f73a864c3..427dbc8b3 100644 --- a/src/core/hle/service/filesystem/fsp_srv.cpp +++ b/src/core/hle/service/filesystem/fsp_srv.cpp @@ -968,16 +968,20 @@ void FSP_SRV::ReadSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute(HLERequ void FSP_SRV::OpenDataStorageByCurrentProcess(HLERequestContext& ctx) { LOG_DEBUG(Service_FS, "called"); - auto current_romfs = fsc.OpenRomFSCurrentProcess(); - if (current_romfs.Failed()) { - // TODO (bunnei): Find the right error code to use here - LOG_CRITICAL(Service_FS, "no file system interface available!"); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultUnknown); - return; + if (!romfs) { + auto current_romfs = fsc.OpenRomFSCurrentProcess(); + if (current_romfs.Failed()) { + // TODO (bunnei): Find the right error code to use here + LOG_CRITICAL(Service_FS, "no file system interface available!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; + } + + romfs = current_romfs.Unwrap(); } - auto storage = std::make_shared<IStorage>(system, std::move(current_romfs.Unwrap())); + auto storage = std::make_shared<IStorage>(system, romfs); IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(ResultSuccess); diff --git a/src/core/hle/service/nfc/common/device.cpp b/src/core/hle/service/nfc/common/device.cpp index 0bd7900e1..b14f682b5 100644 --- a/src/core/hle/service/nfc/common/device.cpp +++ b/src/core/hle/service/nfc/common/device.cpp @@ -12,6 +12,11 @@ #pragma warning(pop) #endif +#include <fmt/format.h> + +#include "common/fs/file.h" +#include "common/fs/fs.h" +#include "common/fs/path_util.h" #include "common/input.h" #include "common/logging/log.h" #include "common/string_util.h" @@ -136,7 +141,7 @@ bool NfcDevice::LoadNfcTag(std::span<const u8> data) { if (!NFP::AmiiboCrypto::IsKeyAvailable()) { LOG_INFO(Service_NFC, "Loading amiibo without keys"); memcpy(&encrypted_tag_data, data.data(), sizeof(NFP::EncryptedNTAG215File)); - BuildAmiiboWithoutKeys(); + BuildAmiiboWithoutKeys(tag_data, encrypted_tag_data); is_plain_amiibo = true; is_write_protected = true; return true; @@ -366,16 +371,25 @@ Result NfcDevice::Mount(NFP::ModelType model_type, NFP::MountTarget mount_target // The loaded amiibo is not encrypted if (is_plain_amiibo) { + std::vector<u8> data(sizeof(NFP::NTAG215File)); + memcpy(data.data(), &tag_data, sizeof(tag_data)); + WriteBackupData(tag_data.uid, data); + device_state = DeviceState::TagMounted; mount_target = mount_target_; return ResultSuccess; } if (!NFP::AmiiboCrypto::DecodeAmiibo(encrypted_tag_data, tag_data)) { - LOG_ERROR(Service_NFP, "Can't decode amiibo {}", device_state); - return ResultCorruptedData; + bool has_backup = HasBackup(encrypted_tag_data.uuid.uid).IsSuccess(); + LOG_ERROR(Service_NFP, "Can't decode amiibo, has_backup= {}", has_backup); + return has_backup ? ResultCorruptedDataWithBackup : ResultCorruptedData; } + std::vector<u8> data(sizeof(NFP::EncryptedNTAG215File)); + memcpy(data.data(), &encrypted_tag_data, sizeof(encrypted_tag_data)); + WriteBackupData(encrypted_tag_data.uuid.uid, data); + device_state = DeviceState::TagMounted; mount_target = mount_target_; return ResultSuccess; @@ -470,6 +484,7 @@ Result NfcDevice::FlushWithBreak(NFP::BreakType break_type) { std::vector<u8> data(sizeof(NFP::EncryptedNTAG215File)); if (is_plain_amiibo) { memcpy(data.data(), &tag_data, sizeof(tag_data)); + WriteBackupData(tag_data.uid, data); } else { if (!NFP::AmiiboCrypto::EncodeAmiibo(tag_data, encrypted_tag_data)) { LOG_ERROR(Service_NFP, "Failed to encode data"); @@ -477,6 +492,7 @@ Result NfcDevice::FlushWithBreak(NFP::BreakType break_type) { } memcpy(data.data(), &encrypted_tag_data, sizeof(encrypted_tag_data)); + WriteBackupData(encrypted_tag_data.uuid.uid, data); } if (!npad_device->WriteNfc(data)) { @@ -488,7 +504,7 @@ Result NfcDevice::FlushWithBreak(NFP::BreakType break_type) { } Result NfcDevice::Restore() { - if (device_state != DeviceState::TagMounted) { + if (device_state != DeviceState::TagFound) { LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); if (device_state == DeviceState::TagRemoved) { return ResultTagRemoved; @@ -496,13 +512,59 @@ Result NfcDevice::Restore() { return ResultWrongDeviceState; } - if (mount_target == NFP::MountTarget::None || mount_target == NFP::MountTarget::Rom) { - LOG_ERROR(Service_NFC, "Amiibo is read only", device_state); - return ResultWrongDeviceState; + NFC::TagInfo tag_info{}; + std::array<u8, sizeof(NFP::EncryptedNTAG215File)> data{}; + Result result = GetTagInfo(tag_info, false); + + if (result.IsError()) { + return result; } - // TODO: Load amiibo from backup on system - LOG_ERROR(Service_NFP, "Not Implemented"); + result = ReadBackupData(tag_info.uuid, data); + + if (result.IsError()) { + return result; + } + + NFP::NTAG215File temporary_tag_data{}; + NFP::EncryptedNTAG215File temporary_encrypted_tag_data{}; + + // Fallback for encrypted amiibos without keys + if (is_write_protected) { + return ResultWriteAmiiboFailed; + } + + // Fallback for plain amiibos + if (is_plain_amiibo) { + LOG_INFO(Service_NFP, "Restoring backup of plain amiibo"); + memcpy(&temporary_tag_data, data.data(), sizeof(NFP::EncryptedNTAG215File)); + temporary_encrypted_tag_data = NFP::AmiiboCrypto::EncodedDataToNfcData(temporary_tag_data); + } + + if (!is_plain_amiibo) { + LOG_INFO(Service_NFP, "Restoring backup of encrypted amiibo"); + temporary_tag_data = {}; + memcpy(&temporary_encrypted_tag_data, data.data(), sizeof(NFP::EncryptedNTAG215File)); + } + + if (!NFP::AmiiboCrypto::IsAmiiboValid(temporary_encrypted_tag_data)) { + return ResultNotAnAmiibo; + } + + if (!is_plain_amiibo) { + if (!NFP::AmiiboCrypto::DecodeAmiibo(temporary_encrypted_tag_data, temporary_tag_data)) { + LOG_ERROR(Service_NFP, "Can't decode amiibo"); + return ResultCorruptedData; + } + } + + // Overwrite tag contents with backup and mount the tag + tag_data = temporary_tag_data; + encrypted_tag_data = temporary_encrypted_tag_data; + device_state = DeviceState::TagMounted; + mount_target = NFP::MountTarget::All; + is_data_moddified = true; + return ResultSuccess; } @@ -1132,13 +1194,69 @@ Result NfcDevice::BreakTag(NFP::BreakType break_type) { return FlushWithBreak(break_type); } -Result NfcDevice::ReadBackupData(std::span<u8> data) const { - // Not implemented +Result NfcDevice::HasBackup(const NFC::UniqueSerialNumber& uid) const { + constexpr auto backup_dir = "backup"; + const auto yuzu_amiibo_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::AmiiboDir); + const auto file_name = fmt::format("{0:02x}.bin", fmt::join(uid, "")); + + if (!Common::FS::Exists(yuzu_amiibo_dir / backup_dir / file_name)) { + return ResultUnableToAccessBackupFile; + } + return ResultSuccess; } -Result NfcDevice::WriteBackupData(std::span<const u8> data) { - // Not implemented +Result NfcDevice::ReadBackupData(const NFC::UniqueSerialNumber& uid, std::span<u8> data) const { + constexpr auto backup_dir = "backup"; + const auto yuzu_amiibo_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::AmiiboDir); + const auto file_name = fmt::format("{0:02x}.bin", fmt::join(uid, "")); + + const Common::FS::IOFile keys_file{yuzu_amiibo_dir / backup_dir / file_name, + Common::FS::FileAccessMode::Read, + Common::FS::FileType::BinaryFile}; + + if (!keys_file.IsOpen()) { + LOG_ERROR(Service_NFP, "Failed to open amiibo backup"); + return ResultUnableToAccessBackupFile; + } + + if (keys_file.Read(data) != data.size()) { + LOG_ERROR(Service_NFP, "Failed to read amiibo backup"); + return ResultUnableToAccessBackupFile; + } + + return ResultSuccess; +} + +Result NfcDevice::WriteBackupData(const NFC::UniqueSerialNumber& uid, std::span<const u8> data) { + constexpr auto backup_dir = "backup"; + const auto yuzu_amiibo_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::AmiiboDir); + const auto file_name = fmt::format("{0:02x}.bin", fmt::join(uid, "")); + + if (HasBackup(uid).IsError()) { + if (!Common::FS::CreateDir(yuzu_amiibo_dir / backup_dir)) { + return ResultBackupPathAlreadyExist; + } + + if (!Common::FS::NewFile(yuzu_amiibo_dir / backup_dir / file_name)) { + return ResultBackupPathAlreadyExist; + } + } + + const Common::FS::IOFile keys_file{yuzu_amiibo_dir / backup_dir / file_name, + Common::FS::FileAccessMode::ReadWrite, + Common::FS::FileType::BinaryFile}; + + if (!keys_file.IsOpen()) { + LOG_ERROR(Service_NFP, "Failed to open amiibo backup"); + return ResultUnableToAccessBackupFile; + } + + if (keys_file.Write(data) != data.size()) { + LOG_ERROR(Service_NFP, "Failed to write amiibo backup"); + return ResultUnableToAccessBackupFile; + } + return ResultSuccess; } @@ -1177,7 +1295,8 @@ NFP::AmiiboName NfcDevice::GetAmiiboName(const NFP::AmiiboSettings& settings) co return amiibo_name; } -void NfcDevice::SetAmiiboName(NFP::AmiiboSettings& settings, const NFP::AmiiboName& amiibo_name) { +void NfcDevice::SetAmiiboName(NFP::AmiiboSettings& settings, + const NFP::AmiiboName& amiibo_name) const { std::array<char16_t, NFP::amiibo_name_length> settings_amiibo_name{}; // Convert from utf8 to utf16 @@ -1258,22 +1377,23 @@ void NfcDevice::UpdateRegisterInfoCrc() { tag_data.register_info_crc = crc.checksum(); } -void NfcDevice::BuildAmiiboWithoutKeys() { +void NfcDevice::BuildAmiiboWithoutKeys(NFP::NTAG215File& stubbed_tag_data, + const NFP::EncryptedNTAG215File& encrypted_file) const { Service::Mii::MiiManager manager; - auto& settings = tag_data.settings; + auto& settings = stubbed_tag_data.settings; - tag_data = NFP::AmiiboCrypto::NfcDataToEncodedData(encrypted_tag_data); + stubbed_tag_data = NFP::AmiiboCrypto::NfcDataToEncodedData(encrypted_file); // Common info - tag_data.write_counter = 0; - tag_data.amiibo_version = 0; + stubbed_tag_data.write_counter = 0; + stubbed_tag_data.amiibo_version = 0; settings.write_date = GetAmiiboDate(GetCurrentPosixTime()); // Register info SetAmiiboName(settings, {'y', 'u', 'z', 'u', 'A', 'm', 'i', 'i', 'b', 'o'}); settings.settings.font_region.Assign(0); settings.init_date = GetAmiiboDate(GetCurrentPosixTime()); - tag_data.owner_mii = manager.BuildFromStoreData(manager.BuildDefault(0)); + stubbed_tag_data.owner_mii = manager.BuildFromStoreData(manager.BuildDefault(0)); // Admin info settings.settings.amiibo_initialized.Assign(1); diff --git a/src/core/hle/service/nfc/common/device.h b/src/core/hle/service/nfc/common/device.h index 6a37e8458..6f049b687 100644 --- a/src/core/hle/service/nfc/common/device.h +++ b/src/core/hle/service/nfc/common/device.h @@ -86,8 +86,9 @@ public: Result GetAll(NFP::NfpData& data) const; Result SetAll(const NFP::NfpData& data); Result BreakTag(NFP::BreakType break_type); - Result ReadBackupData(std::span<u8> data) const; - Result WriteBackupData(std::span<const u8> data); + Result HasBackup(const NFC::UniqueSerialNumber& uid) const; + Result ReadBackupData(const NFC::UniqueSerialNumber& uid, std::span<u8> data) const; + Result WriteBackupData(const NFC::UniqueSerialNumber& uid, std::span<const u8> data); Result WriteNtf(std::span<const u8> data); u64 GetHandle() const; @@ -103,14 +104,15 @@ private: void CloseNfcTag(); NFP::AmiiboName GetAmiiboName(const NFP::AmiiboSettings& settings) const; - void SetAmiiboName(NFP::AmiiboSettings& settings, const NFP::AmiiboName& amiibo_name); + void SetAmiiboName(NFP::AmiiboSettings& settings, const NFP::AmiiboName& amiibo_name) const; NFP::AmiiboDate GetAmiiboDate(s64 posix_time) const; u64 GetCurrentPosixTime() const; u64 RemoveVersionByte(u64 application_id) const; void UpdateSettingsCrc(); void UpdateRegisterInfoCrc(); - void BuildAmiiboWithoutKeys(); + void BuildAmiiboWithoutKeys(NFP::NTAG215File& stubbed_tag_data, + const NFP::EncryptedNTAG215File& encrypted_file) const; bool is_controller_set{}; int callback_key; diff --git a/src/core/hle/service/nfc/common/device_manager.cpp b/src/core/hle/service/nfc/common/device_manager.cpp index d5deaaf27..cffd602df 100644 --- a/src/core/hle/service/nfc/common/device_manager.cpp +++ b/src/core/hle/service/nfc/common/device_manager.cpp @@ -543,9 +543,14 @@ Result DeviceManager::ReadBackupData(u64 device_handle, std::span<u8> data) cons std::shared_ptr<NfcDevice> device = nullptr; auto result = GetDeviceHandle(device_handle, device); + NFC::TagInfo tag_info{}; if (result.IsSuccess()) { - result = device->ReadBackupData(data); + result = device->GetTagInfo(tag_info, false); + } + + if (result.IsSuccess()) { + result = device->ReadBackupData(tag_info.uuid, data); result = VerifyDeviceResult(device, result); } @@ -557,9 +562,14 @@ Result DeviceManager::WriteBackupData(u64 device_handle, std::span<const u8> dat std::shared_ptr<NfcDevice> device = nullptr; auto result = GetDeviceHandle(device_handle, device); + NFC::TagInfo tag_info{}; + + if (result.IsSuccess()) { + result = device->GetTagInfo(tag_info, false); + } if (result.IsSuccess()) { - result = device->WriteBackupData(data); + result = device->WriteBackupData(tag_info.uuid, data); result = VerifyDeviceResult(device, result); } diff --git a/src/core/hle/service/nfc/nfc_interface.cpp b/src/core/hle/service/nfc/nfc_interface.cpp index 0fa29d398..198d0f2b9 100644 --- a/src/core/hle/service/nfc/nfc_interface.cpp +++ b/src/core/hle/service/nfc/nfc_interface.cpp @@ -302,7 +302,7 @@ Result NfcInterface::TranslateResultToServiceError(Result result) const { return TranslateResultToNfp(result); } default: - if (result != ResultUnknown216) { + if (result != ResultBackupPathAlreadyExist) { return result; } return ResultUnknown74; @@ -343,6 +343,9 @@ Result NfcInterface::TranslateResultToNfp(Result result) const { if (result == ResultApplicationAreaIsNotInitialized) { return NFP::ResultApplicationAreaIsNotInitialized; } + if (result == ResultCorruptedDataWithBackup) { + return NFP::ResultCorruptedDataWithBackup; + } if (result == ResultCorruptedData) { return NFP::ResultCorruptedData; } @@ -355,6 +358,9 @@ Result NfcInterface::TranslateResultToNfp(Result result) const { if (result == ResultNotAnAmiibo) { return NFP::ResultNotAnAmiibo; } + if (result == ResultUnableToAccessBackupFile) { + return NFP::ResultUnableToAccessBackupFile; + } LOG_WARNING(Service_NFC, "Result conversion not handled"); return result; } diff --git a/src/core/hle/service/nfc/nfc_result.h b/src/core/hle/service/nfc/nfc_result.h index 917d79ef8..59a808740 100644 --- a/src/core/hle/service/nfc/nfc_result.h +++ b/src/core/hle/service/nfc/nfc_result.h @@ -9,20 +9,22 @@ namespace Service::NFC { constexpr Result ResultDeviceNotFound(ErrorModule::NFC, 64); constexpr Result ResultInvalidArgument(ErrorModule::NFC, 65); -constexpr Result ResultWrongApplicationAreaSize(ErrorModule::NFP, 68); +constexpr Result ResultWrongApplicationAreaSize(ErrorModule::NFC, 68); constexpr Result ResultWrongDeviceState(ErrorModule::NFC, 73); constexpr Result ResultUnknown74(ErrorModule::NFC, 74); constexpr Result ResultUnknown76(ErrorModule::NFC, 76); constexpr Result ResultNfcNotInitialized(ErrorModule::NFC, 77); constexpr Result ResultNfcDisabled(ErrorModule::NFC, 80); -constexpr Result ResultWriteAmiiboFailed(ErrorModule::NFP, 88); +constexpr Result ResultWriteAmiiboFailed(ErrorModule::NFC, 88); constexpr Result ResultTagRemoved(ErrorModule::NFC, 97); -constexpr Result ResultRegistrationIsNotInitialized(ErrorModule::NFP, 120); -constexpr Result ResultApplicationAreaIsNotInitialized(ErrorModule::NFP, 128); -constexpr Result ResultCorruptedData(ErrorModule::NFP, 144); -constexpr Result ResultWrongApplicationAreaId(ErrorModule::NFP, 152); -constexpr Result ResultApplicationAreaExist(ErrorModule::NFP, 168); -constexpr Result ResultNotAnAmiibo(ErrorModule::NFP, 178); -constexpr Result ResultUnknown216(ErrorModule::NFC, 216); +constexpr Result ResultUnableToAccessBackupFile(ErrorModule::NFC, 113); +constexpr Result ResultRegistrationIsNotInitialized(ErrorModule::NFC, 120); +constexpr Result ResultApplicationAreaIsNotInitialized(ErrorModule::NFC, 128); +constexpr Result ResultCorruptedDataWithBackup(ErrorModule::NFC, 136); +constexpr Result ResultCorruptedData(ErrorModule::NFC, 144); +constexpr Result ResultWrongApplicationAreaId(ErrorModule::NFC, 152); +constexpr Result ResultApplicationAreaExist(ErrorModule::NFC, 168); +constexpr Result ResultNotAnAmiibo(ErrorModule::NFC, 178); +constexpr Result ResultBackupPathAlreadyExist(ErrorModule::NFC, 216); } // namespace Service::NFC diff --git a/src/core/hle/service/nfp/nfp_interface.cpp b/src/core/hle/service/nfp/nfp_interface.cpp index 21d159154..34ef9d82d 100644 --- a/src/core/hle/service/nfp/nfp_interface.cpp +++ b/src/core/hle/service/nfp/nfp_interface.cpp @@ -126,7 +126,7 @@ void Interface::Flush(HLERequestContext& ctx) { void Interface::Restore(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto device_handle{rp.Pop<u64>()}; - LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}", device_handle); + LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); auto result = GetManager()->Restore(device_handle); result = TranslateResultToServiceError(result); @@ -394,7 +394,7 @@ void Interface::BreakTag(HLERequestContext& ctx) { void Interface::ReadBackupData(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto device_handle{rp.Pop<u64>()}; - LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}", device_handle); + LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); std::vector<u8> backup_data{}; auto result = GetManager()->ReadBackupData(device_handle, backup_data); @@ -412,7 +412,7 @@ void Interface::WriteBackupData(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto device_handle{rp.Pop<u64>()}; const auto backup_data_buffer{ctx.ReadBuffer()}; - LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}", device_handle); + LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); auto result = GetManager()->WriteBackupData(device_handle, backup_data_buffer); result = TranslateResultToServiceError(result); diff --git a/src/core/hle/service/nfp/nfp_result.h b/src/core/hle/service/nfp/nfp_result.h index 4c126cd81..618533843 100644 --- a/src/core/hle/service/nfp/nfp_result.h +++ b/src/core/hle/service/nfp/nfp_result.h @@ -17,9 +17,11 @@ constexpr Result ResultWriteAmiiboFailed(ErrorModule::NFP, 88); constexpr Result ResultTagRemoved(ErrorModule::NFP, 97); constexpr Result ResultRegistrationIsNotInitialized(ErrorModule::NFP, 120); constexpr Result ResultApplicationAreaIsNotInitialized(ErrorModule::NFP, 128); +constexpr Result ResultCorruptedDataWithBackup(ErrorModule::NFP, 136); constexpr Result ResultCorruptedData(ErrorModule::NFP, 144); constexpr Result ResultWrongApplicationAreaId(ErrorModule::NFP, 152); constexpr Result ResultApplicationAreaExist(ErrorModule::NFP, 168); constexpr Result ResultNotAnAmiibo(ErrorModule::NFP, 178); +constexpr Result ResultUnableToAccessBackupFile(ErrorModule::NFP, 200); } // namespace Service::NFP diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp index 0c7aee1b8..dc45169ad 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp @@ -69,7 +69,7 @@ NvResult nvhost_nvdec::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> in void nvhost_nvdec::OnOpen(DeviceFD fd) { LOG_INFO(Service_NVDRV, "NVDEC video stream started"); - system.AudioCore().SetNVDECActive(true); + system.SetNVDECActive(true); } void nvhost_nvdec::OnClose(DeviceFD fd) { @@ -79,7 +79,7 @@ void nvhost_nvdec::OnClose(DeviceFD fd) { if (iter != host1x_file.fd_to_id.end()) { system.GPU().ClearCdmaInstance(iter->second); } - system.AudioCore().SetNVDECActive(false); + system.SetNVDECActive(false); } } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvnflinger/nvnflinger.cpp b/src/core/hle/service/nvnflinger/nvnflinger.cpp index 4988e6e17..da2d5890f 100644 --- a/src/core/hle/service/nvnflinger/nvnflinger.cpp +++ b/src/core/hle/service/nvnflinger/nvnflinger.cpp @@ -324,6 +324,10 @@ s64 Nvnflinger::GetNextTicks() const { speed_scale = 0.01f; } } + if (system.GetNVDECActive() && settings.use_video_framerate.GetValue()) { + // Run at intended presentation rate during video playback. + speed_scale = 1.f; + } // As an extension, treat nonpositive swap interval as framerate multiplier. const f32 effective_fps = swap_interval <= 0 ? 120.f * static_cast<f32>(1 - swap_interval) |