From 1968cc7b106159e2bd2166459c3cb04d5f79601f Mon Sep 17 00:00:00 2001
From: german77 <juangerman-13@hotmail.com>
Date: Mon, 8 May 2023 22:03:32 -0600
Subject: [PATCH 1/2] service: nfp: Allow to load with a different amiibo id

---
 src/common/settings.h                         |  2 ++
 src/core/hle/service/nfc/common/device.cpp    | 17 ++++++++++----
 src/yuzu/configuration/config.cpp             |  2 ++
 .../configure_input_advanced.cpp              |  2 ++
 .../configuration/configure_input_advanced.ui | 22 ++++++++++++++++---
 src/yuzu_cmd/config.cpp                       |  1 +
 6 files changed, 39 insertions(+), 7 deletions(-)

diff --git a/src/common/settings.h b/src/common/settings.h
index f4eb4e3cde..5f4caaab98 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -535,6 +535,8 @@ struct Values {
     Setting<bool> enable_ir_sensor{false, "enable_ir_sensor"};
     Setting<std::string> ir_sensor_device{"auto", "ir_sensor_device"};
 
+    Setting<bool> random_amiibo_id{false, "random_amiibo_id"};
+
     // Data Storage
     Setting<bool> use_virtual_sd{true, "use_virtual_sd"};
     Setting<bool> gamecard_inserted{false, "gamecard_inserted"};
diff --git a/src/core/hle/service/nfc/common/device.cpp b/src/core/hle/service/nfc/common/device.cpp
index e5de65ce01..9b0685bdb2 100644
--- a/src/core/hle/service/nfc/common/device.cpp
+++ b/src/core/hle/service/nfc/common/device.cpp
@@ -227,11 +227,20 @@ Result NfcDevice::GetTagInfo(NFP::TagInfo& tag_info, bool is_mifare) const {
         return ResultWrongDeviceState;
     }
 
+    UniqueSerialNumber uuid = encrypted_tag_data.uuid.uid;
+
+    // Generate random UUID to bypass amiibo load limits
+    if (Settings::values.random_amiibo_id) {
+        Common::TinyMT rng{};
+        rng.GenerateRandomBytes(uuid.data(), sizeof(UniqueSerialNumber));
+        uuid[3] = 0x88 ^ uuid[0] ^ uuid[1] ^ uuid[2];
+    }
+
     if (is_mifare) {
         tag_info = {
-            .uuid = encrypted_tag_data.uuid.uid,
+            .uuid = uuid,
             .uuid_extension = {},
-            .uuid_length = static_cast<u8>(encrypted_tag_data.uuid.uid.size()),
+            .uuid_length = static_cast<u8>(uuid.size()),
             .protocol = NfcProtocol::TypeA,
             .tag_type = TagType::Type4,
         };
@@ -240,9 +249,9 @@ Result NfcDevice::GetTagInfo(NFP::TagInfo& tag_info, bool is_mifare) const {
 
     // Protocol and tag type may change here
     tag_info = {
-        .uuid = encrypted_tag_data.uuid.uid,
+        .uuid = uuid,
         .uuid_extension = {},
-        .uuid_length = static_cast<u8>(encrypted_tag_data.uuid.uid.size()),
+        .uuid_length = static_cast<u8>(uuid.size()),
         .protocol = NfcProtocol::TypeA,
         .tag_type = TagType::Type2,
     };
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index a49d122669..b94d368388 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -443,6 +443,7 @@ void Config::ReadControlValues() {
     ReadBasicSetting(Settings::values.mouse_panning_sensitivity);
     ReadBasicSetting(Settings::values.enable_joycon_driver);
     ReadBasicSetting(Settings::values.enable_procon_driver);
+    ReadBasicSetting(Settings::values.random_amiibo_id);
 
     ReadBasicSetting(Settings::values.tas_enable);
     ReadBasicSetting(Settings::values.tas_loop);
@@ -1150,6 +1151,7 @@ void Config::SaveControlValues() {
     WriteBasicSetting(Settings::values.enable_raw_input);
     WriteBasicSetting(Settings::values.enable_joycon_driver);
     WriteBasicSetting(Settings::values.enable_procon_driver);
+    WriteBasicSetting(Settings::values.random_amiibo_id);
     WriteBasicSetting(Settings::values.keyboard_enabled);
     WriteBasicSetting(Settings::values.emulate_analog_keyboard);
     WriteBasicSetting(Settings::values.mouse_panning_sensitivity);
diff --git a/src/yuzu/configuration/configure_input_advanced.cpp b/src/yuzu/configuration/configure_input_advanced.cpp
index 8d81322f3a..f131564343 100644
--- a/src/yuzu/configuration/configure_input_advanced.cpp
+++ b/src/yuzu/configuration/configure_input_advanced.cpp
@@ -140,6 +140,7 @@ void ConfigureInputAdvanced::ApplyConfiguration() {
     Settings::values.enable_ir_sensor = ui->enable_ir_sensor->isChecked();
     Settings::values.enable_joycon_driver = ui->enable_joycon_driver->isChecked();
     Settings::values.enable_procon_driver = ui->enable_procon_driver->isChecked();
+    Settings::values.random_amiibo_id = ui->random_amiibo_id->isChecked();
 }
 
 void ConfigureInputAdvanced::LoadConfiguration() {
@@ -176,6 +177,7 @@ void ConfigureInputAdvanced::LoadConfiguration() {
     ui->enable_ir_sensor->setChecked(Settings::values.enable_ir_sensor.GetValue());
     ui->enable_joycon_driver->setChecked(Settings::values.enable_joycon_driver.GetValue());
     ui->enable_procon_driver->setChecked(Settings::values.enable_procon_driver.GetValue());
+    ui->random_amiibo_id->setChecked(Settings::values.random_amiibo_id.GetValue());
 
     UpdateUIEnabled();
 }
diff --git a/src/yuzu/configuration/configure_input_advanced.ui b/src/yuzu/configuration/configure_input_advanced.ui
index 0eb2b34bcc..2e8b136600 100644
--- a/src/yuzu/configuration/configure_input_advanced.ui
+++ b/src/yuzu/configuration/configure_input_advanced.ui
@@ -2728,6 +2728,22 @@
                      </widget>
                    </item>
                    <item row="7" column="0">
+                     <widget class="QCheckBox" name="random_amiibo_id">
+                       <property name="toolTip">
+                         <string>Allows unlimited uses of the same Amiibo in games that would otherwise limit you to one use.</string>
+                       </property>
+                       <property name="minimumSize">
+                         <size>
+                           <width>0</width>
+                           <height>23</height>
+                         </size>
+                       </property>
+                       <property name="text">
+                         <string>Use random Amiibo ID</string>
+                       </property>
+                     </widget>
+                   </item>
+                   <item row="8" column="0">
                      <widget class="QCheckBox" name="mouse_panning">
                        <property name="minimumSize">
                          <size>
@@ -2740,7 +2756,7 @@
                        </property>
                      </widget>
                    </item>
-                   <item row="7" column="2">
+                   <item row="8" column="2">
                      <widget class="QSpinBox" name="mouse_panning_sensitivity">
                        <property name="toolTip">
                          <string>Mouse sensitivity</string>
@@ -2762,14 +2778,14 @@
                        </property>
                      </widget>
                    </item>
-                   <item row="8" column="0">
+                   <item row="9" column="0">
                      <widget class="QLabel" name="motion_touch">
                        <property name="text">
                          <string>Motion / Touch</string>
                        </property>
                      </widget>
                    </item>
-                   <item row="8" column="2">
+                   <item row="9" column="2">
                      <widget class="QPushButton" name="buttonMotionTouch">
                        <property name="text">
                          <string>Configure</string>
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index abe7092fcf..dc9a3d68f1 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -169,6 +169,7 @@ void Config::ReadValues() {
     ReadSetting("ControlsGeneral", Settings::values.enable_raw_input);
     ReadSetting("ControlsGeneral", Settings::values.enable_joycon_driver);
     ReadSetting("ControlsGeneral", Settings::values.enable_procon_driver);
+    ReadSetting("ControlsGeneral", Settings::values.random_amiibo_id);
     ReadSetting("ControlsGeneral", Settings::values.emulate_analog_keyboard);
     ReadSetting("ControlsGeneral", Settings::values.vibration_enabled);
     ReadSetting("ControlsGeneral", Settings::values.enable_accurate_vibrations);

From 42e1db4b0e254d10898f4a2bceb452fbfbaae063 Mon Sep 17 00:00:00 2001
From: Narr the Reg <juangerman-13@hotmail.com>
Date: Tue, 9 May 2023 17:49:56 -0600
Subject: [PATCH 2/2] service: nfc: Seed all random values

---
 src/core/hle/service/nfc/common/device.cpp | 18 +++++++++++++-----
 src/core/hle/service/nfc/common/device.h   |  2 +-
 2 files changed, 14 insertions(+), 6 deletions(-)

diff --git a/src/core/hle/service/nfc/common/device.cpp b/src/core/hle/service/nfc/common/device.cpp
index 9b0685bdb2..322bde2ed3 100644
--- a/src/core/hle/service/nfc/common/device.cpp
+++ b/src/core/hle/service/nfc/common/device.cpp
@@ -48,9 +48,6 @@ NfcDevice::NfcDevice(Core::HID::NpadIdType npad_id_, Core::System& system_,
     };
     is_controller_set = true;
     callback_key = npad_device->SetCallback(engine_callback);
-
-    auto& standard_steady_clock{system.GetTimeManager().GetStandardSteadyClockCore()};
-    current_posix_time = standard_steady_clock.GetCurrentTimePoint(system).time_point;
 }
 
 NfcDevice::~NfcDevice() {
@@ -232,6 +229,7 @@ Result NfcDevice::GetTagInfo(NFP::TagInfo& tag_info, bool is_mifare) const {
     // Generate random UUID to bypass amiibo load limits
     if (Settings::values.random_amiibo_id) {
         Common::TinyMT rng{};
+        rng.Initialize(static_cast<u32>(GetCurrentPosixTime()));
         rng.GenerateRandomBytes(uuid.data(), sizeof(UniqueSerialNumber));
         uuid[3] = 0x88 ^ uuid[0] ^ uuid[1] ^ uuid[2];
     }
@@ -415,7 +413,7 @@ Result NfcDevice::Flush() {
 
     auto& settings = tag_data.settings;
 
-    const auto& current_date = GetAmiiboDate(current_posix_time);
+    const auto& current_date = GetAmiiboDate(GetCurrentPosixTime());
     if (settings.write_date.raw_date != current_date.raw_date) {
         settings.write_date = current_date;
         UpdateSettingsCrc();
@@ -534,6 +532,7 @@ Result NfcDevice::GetModelInfo(NFP::ModelInfo& model_info) const {
     }
 
     const auto& model_info_data = encrypted_tag_data.user_memory.model_info;
+
     model_info = {
         .character_id = model_info_data.character_id,
         .character_variant = model_info_data.character_variant,
@@ -678,6 +677,7 @@ Result NfcDevice::DeleteRegisterInfo() {
     }
 
     Common::TinyMT rng{};
+    rng.Initialize(static_cast<u32>(GetCurrentPosixTime()));
     rng.GenerateRandomBytes(&tag_data.owner_mii, sizeof(tag_data.owner_mii));
     rng.GenerateRandomBytes(&tag_data.settings.amiibo_name, sizeof(tag_data.settings.amiibo_name));
     rng.GenerateRandomBytes(&tag_data.unknown, sizeof(u8));
@@ -710,7 +710,7 @@ Result NfcDevice::SetRegisterInfoPrivate(const NFP::RegisterInfoPrivate& registe
     auto& settings = tag_data.settings;
 
     if (tag_data.settings.settings.amiibo_initialized == 0) {
-        settings.init_date = GetAmiiboDate(current_posix_time);
+        settings.init_date = GetAmiiboDate(GetCurrentPosixTime());
         settings.write_date.raw_date = 0;
     }
 
@@ -877,6 +877,7 @@ Result NfcDevice::SetApplicationArea(std::span<const u8> data) {
     }
 
     Common::TinyMT rng{};
+    rng.Initialize(static_cast<u32>(GetCurrentPosixTime()));
     std::memcpy(tag_data.application_area.data(), data.data(), data.size());
     // Fill remaining data with random numbers
     rng.GenerateRandomBytes(tag_data.application_area.data() + data.size(),
@@ -933,6 +934,7 @@ Result NfcDevice::RecreateApplicationArea(u32 access_id, std::span<const u8> dat
     }
 
     Common::TinyMT rng{};
+    rng.Initialize(static_cast<u32>(GetCurrentPosixTime()));
     std::memcpy(tag_data.application_area.data(), data.data(), data.size());
     // Fill remaining data with random numbers
     rng.GenerateRandomBytes(tag_data.application_area.data() + data.size(),
@@ -982,6 +984,7 @@ Result NfcDevice::DeleteApplicationArea() {
     }
 
     Common::TinyMT rng{};
+    rng.Initialize(static_cast<u32>(GetCurrentPosixTime()));
     rng.GenerateRandomBytes(tag_data.application_area.data(), sizeof(NFP::ApplicationArea));
     rng.GenerateRandomBytes(&tag_data.application_id, sizeof(u64));
     rng.GenerateRandomBytes(&tag_data.application_area_id, sizeof(u32));
@@ -1198,6 +1201,11 @@ NFP::AmiiboDate NfcDevice::GetAmiiboDate(s64 posix_time) const {
     return amiibo_date;
 }
 
+u64 NfcDevice::GetCurrentPosixTime() const {
+    auto& standard_steady_clock{system.GetTimeManager().GetStandardSteadyClockCore()};
+    return standard_steady_clock.GetCurrentTimePoint(system).time_point;
+}
+
 u64 NfcDevice::RemoveVersionByte(u64 application_id) const {
     return application_id & ~(0xfULL << NFP::application_id_version_offset);
 }
diff --git a/src/core/hle/service/nfc/common/device.h b/src/core/hle/service/nfc/common/device.h
index 654eda98e3..98e1945c1f 100644
--- a/src/core/hle/service/nfc/common/device.h
+++ b/src/core/hle/service/nfc/common/device.h
@@ -105,6 +105,7 @@ private:
     NFP::AmiiboName GetAmiiboName(const NFP::AmiiboSettings& settings) const;
     void SetAmiiboName(NFP::AmiiboSettings& settings, const NFP::AmiiboName& amiibo_name);
     NFP::AmiiboDate GetAmiiboDate(s64 posix_time) const;
+    u64 GetCurrentPosixTime() const;
     u64 RemoveVersionByte(u64 application_id) const;
     void UpdateSettingsCrc();
     void UpdateRegisterInfoCrc();
@@ -127,7 +128,6 @@ private:
     bool is_data_moddified{};
     bool is_app_area_open{};
     bool is_plain_amiibo{};
-    s64 current_posix_time{};
     NFP::MountTarget mount_target{NFP::MountTarget::None};
 
     NFP::NTAG215File tag_data{};