mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-30 21:30:04 +00:00 
			
		
		
		
	service: nfc: Implement amiibo encryption and appdata (#6340)
This commit is contained in:
		
							parent
							
								
									ca2d87e5e3
								
							
						
					
					
						commit
						3d0a3c2c45
					
				
					 16 changed files with 3016 additions and 323 deletions
				
			
		|  | @ -585,7 +585,7 @@ public final class NativeLibrary { | ||||||
|     /// Notifies that the activity is now in foreground and camera devices can now be reloaded |     /// Notifies that the activity is now in foreground and camera devices can now be reloaded | ||||||
|     public static native void ReloadCameraDevices(); |     public static native void ReloadCameraDevices(); | ||||||
| 
 | 
 | ||||||
|     public static native boolean LoadAmiibo(byte[] bytes); |     public static native boolean LoadAmiibo(String path); | ||||||
| 
 | 
 | ||||||
|     public static native void RemoveAmiibo(); |     public static native void RemoveAmiibo(); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -570,15 +570,7 @@ public final class EmulationActivity extends AppCompatActivity { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private void onAmiiboSelected(String selectedFile) { |     private void onAmiiboSelected(String selectedFile) { | ||||||
|         boolean success = false; |         boolean success = NativeLibrary.LoadAmiibo(selectedFile); | ||||||
|         try { |  | ||||||
|             Uri uri = Uri.parse(selectedFile); |  | ||||||
|             DocumentFile file = DocumentFile.fromSingleUri(this, uri); |  | ||||||
|             byte[] bytes = FileUtil.getBytesFromFile(this, file); |  | ||||||
|             success = NativeLibrary.LoadAmiibo(bytes); |  | ||||||
|         } catch (IOException e) { |  | ||||||
|             e.printStackTrace(); |  | ||||||
|         } |  | ||||||
| 
 | 
 | ||||||
|         if (!success) { |         if (!success) { | ||||||
|             new MaterialAlertDialogBuilder(this) |             new MaterialAlertDialogBuilder(this) | ||||||
|  |  | ||||||
|  | @ -569,20 +569,16 @@ void Java_org_citra_citra_1emu_NativeLibrary_ReloadCameraDevices(JNIEnv* env, jc | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| jboolean Java_org_citra_citra_1emu_NativeLibrary_LoadAmiibo(JNIEnv* env, jclass clazz, | jboolean Java_org_citra_citra_1emu_NativeLibrary_LoadAmiibo(JNIEnv* env, jclass clazz, | ||||||
|                                                             jbyteArray bytes) { |                                                             jstring j_file) { | ||||||
|  |     std::string filepath = GetJString(env, j_file); | ||||||
|     Core::System& system{Core::System::GetInstance()}; |     Core::System& system{Core::System::GetInstance()}; | ||||||
|     Service::SM::ServiceManager& sm = system.ServiceManager(); |     Service::SM::ServiceManager& sm = system.ServiceManager(); | ||||||
|     auto nfc = sm.GetService<Service::NFC::Module::Interface>("nfc:u"); |     auto nfc = sm.GetService<Service::NFC::Module::Interface>("nfc:u"); | ||||||
|     if (nfc == nullptr || env->GetArrayLength(bytes) != sizeof(Service::NFC::AmiiboData)) { |     if (nfc == nullptr) { | ||||||
|         return static_cast<jboolean>(false); |         return static_cast<jboolean>(false); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     Service::NFC::AmiiboData amiibo_data{}; |     return static_cast<jboolean>(nfc->LoadAmiibo(filepath)); | ||||||
|     env->GetByteArrayRegion(bytes, 0, sizeof(Service::NFC::AmiiboData), |  | ||||||
|                             reinterpret_cast<jbyte*>(&amiibo_data)); |  | ||||||
| 
 |  | ||||||
|     nfc->LoadAmiibo(amiibo_data); |  | ||||||
|     return static_cast<jboolean>(true); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Java_org_citra_citra_1emu_NativeLibrary_RemoveAmiibo(JNIEnv* env, jclass clazz) { | void Java_org_citra_citra_1emu_NativeLibrary_RemoveAmiibo(JNIEnv* env, jclass clazz) { | ||||||
|  |  | ||||||
|  | @ -142,7 +142,7 @@ JNIEXPORT void JNICALL Java_org_citra_citra_1emu_NativeLibrary_ReloadCameraDevic | ||||||
|                                                                                    jclass clazz); |                                                                                    jclass clazz); | ||||||
| 
 | 
 | ||||||
| JNIEXPORT jboolean Java_org_citra_citra_1emu_NativeLibrary_LoadAmiibo(JNIEnv* env, jclass clazz, | JNIEXPORT jboolean Java_org_citra_citra_1emu_NativeLibrary_LoadAmiibo(JNIEnv* env, jclass clazz, | ||||||
|                                                                       jbyteArray bytes); |                                                                       jstring j_file); | ||||||
| 
 | 
 | ||||||
| JNIEXPORT void Java_org_citra_citra_1emu_NativeLibrary_RemoveAmiibo(JNIEnv* env, jclass clazz); | JNIEXPORT void Java_org_citra_citra_1emu_NativeLibrary_RemoveAmiibo(JNIEnv* env, jclass clazz); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -2017,6 +2017,25 @@ void GMainWindow::OnLoadAmiibo() { | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     Core::System& system{Core::System::GetInstance()}; | ||||||
|  |     Service::SM::ServiceManager& sm = system.ServiceManager(); | ||||||
|  |     auto nfc = sm.GetService<Service::NFC::Module::Interface>("nfc:u"); | ||||||
|  |     if (nfc == nullptr) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (nfc->IsTagActive()) { | ||||||
|  |         QMessageBox::warning(this, tr("Error opening amiibo data file"), | ||||||
|  |                              tr("A tag is already in use.")); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (!nfc->IsSearchingForAmiibos()) { | ||||||
|  |         QMessageBox::warning(this, tr("Error opening amiibo data file"), | ||||||
|  |                              tr("Game is not looking for amiibos.")); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     const QString extensions{QStringLiteral("*.bin")}; |     const QString extensions{QStringLiteral("*.bin")}; | ||||||
|     const QString file_filter = tr("Amiibo File (%1);; All Files (*.*)").arg(extensions); |     const QString file_filter = tr("Amiibo File (%1);; All Files (*.*)").arg(extensions); | ||||||
|     const QString filename = QFileDialog::getOpenFileName(this, tr("Load Amiibo"), {}, file_filter); |     const QString filename = QFileDialog::getOpenFileName(this, tr("Load Amiibo"), {}, file_filter); | ||||||
|  | @ -2035,26 +2054,12 @@ void GMainWindow::LoadAmiibo(const QString& filename) { | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     QFile nfc_file{filename}; |     if (!nfc->LoadAmiibo(filename.toStdString())) { | ||||||
|     if (!nfc_file.open(QIODevice::ReadOnly)) { |         QMessageBox::warning(this, tr("Error opening amiibo data file"), | ||||||
|         QMessageBox::warning(this, tr("Error opening Amiibo data file"), |                              tr("Unable to open amiibo file \"%1\" for reading.").arg(filename)); | ||||||
|                              tr("Unable to open Amiibo file \"%1\" for reading.").arg(filename)); |  | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     Service::NFC::AmiiboData amiibo_data{}; |  | ||||||
|     const u64 read_size = |  | ||||||
|         nfc_file.read(reinterpret_cast<char*>(&amiibo_data), sizeof(Service::NFC::AmiiboData)); |  | ||||||
|     if (read_size != sizeof(Service::NFC::AmiiboData)) { |  | ||||||
|         QMessageBox::warning(this, tr("Error reading Amiibo data file"), |  | ||||||
|                              tr("Unable to fully read Amiibo data. Expected to read %1 bytes, but " |  | ||||||
|                                 "was only able to read %2 bytes.") |  | ||||||
|                                  .arg(sizeof(Service::NFC::AmiiboData)) |  | ||||||
|                                  .arg(read_size)); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     nfc->LoadAmiibo(amiibo_data); |  | ||||||
|     ui->action_Remove_Amiibo->setEnabled(true); |     ui->action_Remove_Amiibo->setEnabled(true); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -333,10 +333,16 @@ add_library(citra_core STATIC | ||||||
|     hle/service/news/news_s.h |     hle/service/news/news_s.h | ||||||
|     hle/service/news/news_u.cpp |     hle/service/news/news_u.cpp | ||||||
|     hle/service/news/news_u.h |     hle/service/news/news_u.h | ||||||
|  |     hle/service/nfc/amiibo_crypto.cpp | ||||||
|  |     hle/service/nfc/amiibo_crypto.h | ||||||
|     hle/service/nfc/nfc.cpp |     hle/service/nfc/nfc.cpp | ||||||
|     hle/service/nfc/nfc.h |     hle/service/nfc/nfc.h | ||||||
|  |     hle/service/nfc/nfc_device.cpp | ||||||
|  |     hle/service/nfc/nfc_device.h | ||||||
|     hle/service/nfc/nfc_m.cpp |     hle/service/nfc/nfc_m.cpp | ||||||
|     hle/service/nfc/nfc_m.h |     hle/service/nfc/nfc_m.h | ||||||
|  |     hle/service/nfc/nfc_results.h | ||||||
|  |     hle/service/nfc/nfc_types.h | ||||||
|     hle/service/nfc/nfc_u.cpp |     hle/service/nfc/nfc_u.cpp | ||||||
|     hle/service/nfc/nfc_u.h |     hle/service/nfc/nfc_u.h | ||||||
|     hle/service/nim/nim.cpp |     hle/service/nim/nim.cpp | ||||||
|  |  | ||||||
							
								
								
									
										374
									
								
								src/core/hle/service/nfc/amiibo_crypto.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										374
									
								
								src/core/hle/service/nfc/amiibo_crypto.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,374 @@ | ||||||
|  | // Copyright 2022 yuzu Emulator Project
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | // Copyright 2017 socram8888/amiitool
 | ||||||
|  | // Licensed under MIT
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #include <array> | ||||||
|  | #include <cryptopp/aes.h> | ||||||
|  | #include <cryptopp/hmac.h> | ||||||
|  | #include <cryptopp/modes.h> | ||||||
|  | #include <cryptopp/sha.h> | ||||||
|  | 
 | ||||||
|  | #include "common/file_util.h" | ||||||
|  | #include "common/logging/log.h" | ||||||
|  | #include "core/hle/service/nfc/amiibo_crypto.h" | ||||||
|  | 
 | ||||||
|  | namespace Service::NFC::AmiiboCrypto { | ||||||
|  | 
 | ||||||
|  | bool IsAmiiboValid(const EncryptedNTAG215File& ntag_file) { | ||||||
|  |     const auto& amiibo_data = ntag_file.user_memory; | ||||||
|  |     LOG_DEBUG(Service_NFC, "uuid_lock=0x{0:x}", ntag_file.static_lock); | ||||||
|  |     LOG_DEBUG(Service_NFC, "compability_container=0x{0:x}", ntag_file.compability_container); | ||||||
|  |     LOG_DEBUG(Service_NFC, "write_count={}", static_cast<u16>(amiibo_data.write_counter)); | ||||||
|  | 
 | ||||||
|  |     LOG_DEBUG(Service_NFC, "character_id=0x{0:x}", amiibo_data.model_info.character_id); | ||||||
|  |     LOG_DEBUG(Service_NFC, "character_variant={}", amiibo_data.model_info.character_variant); | ||||||
|  |     LOG_DEBUG(Service_NFC, "amiibo_type={}", amiibo_data.model_info.amiibo_type); | ||||||
|  |     LOG_DEBUG(Service_NFC, "model_number=0x{0:x}", | ||||||
|  |               static_cast<u16>(amiibo_data.model_info.model_number)); | ||||||
|  |     LOG_DEBUG(Service_NFC, "series={}", amiibo_data.model_info.series); | ||||||
|  |     LOG_DEBUG(Service_NFC, "tag_type=0x{0:x}", amiibo_data.model_info.tag_type); | ||||||
|  | 
 | ||||||
|  |     LOG_DEBUG(Service_NFC, "tag_dynamic_lock=0x{0:x}", ntag_file.dynamic_lock); | ||||||
|  |     LOG_DEBUG(Service_NFC, "tag_CFG0=0x{0:x}", ntag_file.CFG0); | ||||||
|  |     LOG_DEBUG(Service_NFC, "tag_CFG1=0x{0:x}", ntag_file.CFG1); | ||||||
|  | 
 | ||||||
|  |     // Validate UUID
 | ||||||
|  |     constexpr u8 CT = 0x88; // As defined in `ISO / IEC 14443 - 3`
 | ||||||
|  |     if ((CT ^ ntag_file.uuid.uid[0] ^ ntag_file.uuid.uid[1] ^ ntag_file.uuid.uid[2]) != | ||||||
|  |         ntag_file.uuid.uid[3]) { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |     if ((ntag_file.uuid.uid[4] ^ ntag_file.uuid.uid[5] ^ ntag_file.uuid.uid[6] ^ | ||||||
|  |          ntag_file.uuid.nintendo_id) != ntag_file.uuid.lock_bytes[0]) { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Check against all know constants on an amiibo binary
 | ||||||
|  |     if (ntag_file.static_lock != 0xE00F) { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |     if (ntag_file.compability_container != 0xEEFF10F1U) { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |     if (amiibo_data.model_info.tag_type != PackedTagType::Type2) { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |     if ((ntag_file.dynamic_lock & 0xFFFFFF) != 0x0F0001U) { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |     if (ntag_file.CFG0 != 0x04000000U) { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |     if (ntag_file.CFG1 != 0x5F) { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool IsAmiiboValid(const NTAG215File& ntag_file) { | ||||||
|  |     return IsAmiiboValid(EncodedDataToNfcData(ntag_file)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | NTAG215File NfcDataToEncodedData(const EncryptedNTAG215File& nfc_data) { | ||||||
|  |     NTAG215File encoded_data{}; | ||||||
|  | 
 | ||||||
|  |     encoded_data.uid = nfc_data.uuid.uid; | ||||||
|  |     encoded_data.nintendo_id = nfc_data.uuid.nintendo_id; | ||||||
|  |     encoded_data.static_lock = nfc_data.static_lock; | ||||||
|  |     encoded_data.compability_container = nfc_data.compability_container; | ||||||
|  |     encoded_data.hmac_data = nfc_data.user_memory.hmac_data; | ||||||
|  |     encoded_data.constant_value = nfc_data.user_memory.constant_value; | ||||||
|  |     encoded_data.write_counter = nfc_data.user_memory.write_counter; | ||||||
|  |     encoded_data.amiibo_version = nfc_data.user_memory.amiibo_version; | ||||||
|  |     encoded_data.settings = nfc_data.user_memory.settings; | ||||||
|  |     encoded_data.owner_mii = nfc_data.user_memory.owner_mii; | ||||||
|  |     encoded_data.padding = nfc_data.user_memory.padding; | ||||||
|  |     encoded_data.owner_mii_aes_ccm = nfc_data.user_memory.owner_mii_aes_ccm; | ||||||
|  |     encoded_data.application_id = nfc_data.user_memory.application_id; | ||||||
|  |     encoded_data.application_write_counter = nfc_data.user_memory.application_write_counter; | ||||||
|  |     encoded_data.application_area_id = nfc_data.user_memory.application_area_id; | ||||||
|  |     encoded_data.application_id_byte = nfc_data.user_memory.application_id_byte; | ||||||
|  |     encoded_data.unknown = nfc_data.user_memory.unknown; | ||||||
|  |     encoded_data.mii_extension = nfc_data.user_memory.mii_extension; | ||||||
|  |     encoded_data.unknown2 = nfc_data.user_memory.unknown2; | ||||||
|  |     encoded_data.register_info_crc = nfc_data.user_memory.register_info_crc; | ||||||
|  |     encoded_data.application_area = nfc_data.user_memory.application_area; | ||||||
|  |     encoded_data.hmac_tag = nfc_data.user_memory.hmac_tag; | ||||||
|  |     encoded_data.lock_bytes = nfc_data.uuid.lock_bytes; | ||||||
|  |     encoded_data.model_info = nfc_data.user_memory.model_info; | ||||||
|  |     encoded_data.keygen_salt = nfc_data.user_memory.keygen_salt; | ||||||
|  |     encoded_data.dynamic_lock = nfc_data.dynamic_lock; | ||||||
|  |     encoded_data.CFG0 = nfc_data.CFG0; | ||||||
|  |     encoded_data.CFG1 = nfc_data.CFG1; | ||||||
|  |     encoded_data.password = nfc_data.password; | ||||||
|  | 
 | ||||||
|  |     return encoded_data; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | EncryptedNTAG215File EncodedDataToNfcData(const NTAG215File& encoded_data) { | ||||||
|  |     EncryptedNTAG215File nfc_data{}; | ||||||
|  | 
 | ||||||
|  |     nfc_data.uuid.uid = encoded_data.uid; | ||||||
|  |     nfc_data.uuid.nintendo_id = encoded_data.nintendo_id; | ||||||
|  |     nfc_data.uuid.lock_bytes = encoded_data.lock_bytes; | ||||||
|  |     nfc_data.static_lock = encoded_data.static_lock; | ||||||
|  |     nfc_data.compability_container = encoded_data.compability_container; | ||||||
|  |     nfc_data.user_memory.hmac_data = encoded_data.hmac_data; | ||||||
|  |     nfc_data.user_memory.constant_value = encoded_data.constant_value; | ||||||
|  |     nfc_data.user_memory.write_counter = encoded_data.write_counter; | ||||||
|  |     nfc_data.user_memory.amiibo_version = encoded_data.amiibo_version; | ||||||
|  |     nfc_data.user_memory.settings = encoded_data.settings; | ||||||
|  |     nfc_data.user_memory.owner_mii = encoded_data.owner_mii; | ||||||
|  |     nfc_data.user_memory.padding = encoded_data.padding; | ||||||
|  |     nfc_data.user_memory.owner_mii_aes_ccm = encoded_data.owner_mii_aes_ccm; | ||||||
|  |     nfc_data.user_memory.application_id = encoded_data.application_id; | ||||||
|  |     nfc_data.user_memory.application_write_counter = encoded_data.application_write_counter; | ||||||
|  |     nfc_data.user_memory.application_area_id = encoded_data.application_area_id; | ||||||
|  |     nfc_data.user_memory.application_id_byte = encoded_data.application_id_byte; | ||||||
|  |     nfc_data.user_memory.unknown = encoded_data.unknown; | ||||||
|  |     nfc_data.user_memory.mii_extension = encoded_data.mii_extension; | ||||||
|  |     nfc_data.user_memory.unknown2 = encoded_data.unknown2; | ||||||
|  |     nfc_data.user_memory.register_info_crc = encoded_data.register_info_crc; | ||||||
|  |     nfc_data.user_memory.application_area = encoded_data.application_area; | ||||||
|  |     nfc_data.user_memory.hmac_tag = encoded_data.hmac_tag; | ||||||
|  |     nfc_data.user_memory.model_info = encoded_data.model_info; | ||||||
|  |     nfc_data.user_memory.keygen_salt = encoded_data.keygen_salt; | ||||||
|  |     nfc_data.dynamic_lock = encoded_data.dynamic_lock; | ||||||
|  |     nfc_data.CFG0 = encoded_data.CFG0; | ||||||
|  |     nfc_data.CFG1 = encoded_data.CFG1; | ||||||
|  |     nfc_data.password = encoded_data.password; | ||||||
|  | 
 | ||||||
|  |     return nfc_data; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | HashSeed GetSeed(const NTAG215File& data) { | ||||||
|  |     HashSeed seed{ | ||||||
|  |         .magic = data.write_counter, | ||||||
|  |         .padding = {}, | ||||||
|  |         .uid_1 = data.uid, | ||||||
|  |         .nintendo_id_1 = data.nintendo_id, | ||||||
|  |         .uid_2 = data.uid, | ||||||
|  |         .nintendo_id_2 = data.nintendo_id, | ||||||
|  |         .keygen_salt = data.keygen_salt, | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     return seed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | std::vector<u8> GenerateInternalKey(const InternalKey& key, const HashSeed& seed) { | ||||||
|  |     const std::size_t seed_part1_len = sizeof(key.magic_bytes) - key.magic_length; | ||||||
|  |     const std::size_t string_size = key.type_string.size(); | ||||||
|  |     std::vector<u8> output(string_size + seed_part1_len); | ||||||
|  | 
 | ||||||
|  |     // Copy whole type string
 | ||||||
|  |     memccpy(output.data(), key.type_string.data(), '\0', string_size); | ||||||
|  | 
 | ||||||
|  |     // Append (16 - magic_length) from the input seed
 | ||||||
|  |     memcpy(output.data() + string_size, &seed, seed_part1_len); | ||||||
|  | 
 | ||||||
|  |     // Append all bytes from magicBytes
 | ||||||
|  |     output.insert(output.end(), key.magic_bytes.begin(), | ||||||
|  |                   key.magic_bytes.begin() + key.magic_length); | ||||||
|  | 
 | ||||||
|  |     output.insert(output.end(), seed.uid_1.begin(), seed.uid_1.end()); | ||||||
|  |     output.emplace_back(seed.nintendo_id_1); | ||||||
|  |     output.insert(output.end(), seed.uid_2.begin(), seed.uid_2.end()); | ||||||
|  |     output.emplace_back(seed.nintendo_id_2); | ||||||
|  | 
 | ||||||
|  |     for (std::size_t i = 0; i < sizeof(seed.keygen_salt); i++) { | ||||||
|  |         output.emplace_back(static_cast<u8>(seed.keygen_salt[i] ^ key.xor_pad[i])); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return output; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void CryptoInit(CryptoCtx& ctx, CryptoPP::HMAC<CryptoPP::SHA256>& hmac_ctx, const HmacKey& hmac_key, | ||||||
|  |                 const std::vector<u8>& seed) { | ||||||
|  |     // Initialize context
 | ||||||
|  |     ctx.used = false; | ||||||
|  |     ctx.counter = 0; | ||||||
|  |     ctx.buffer_size = sizeof(ctx.counter) + seed.size(); | ||||||
|  |     memcpy(ctx.buffer.data() + sizeof(u16), seed.data(), seed.size()); | ||||||
|  | 
 | ||||||
|  |     // Initialize HMAC context
 | ||||||
|  |     hmac_ctx.SetKey(hmac_key.data(), hmac_key.size()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void CryptoStep(CryptoCtx& ctx, CryptoPP::HMAC<CryptoPP::SHA256>& hmac_ctx, DrgbOutput& output) { | ||||||
|  |     // If used at least once, reinitialize the HMAC
 | ||||||
|  |     if (ctx.used) { | ||||||
|  |         hmac_ctx.Restart(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     ctx.used = true; | ||||||
|  | 
 | ||||||
|  |     // Store counter in big endian, and increment it
 | ||||||
|  |     ctx.buffer[0] = static_cast<u8>(ctx.counter >> 8); | ||||||
|  |     ctx.buffer[1] = static_cast<u8>(ctx.counter >> 0); | ||||||
|  |     ctx.counter++; | ||||||
|  | 
 | ||||||
|  |     // Do HMAC magic
 | ||||||
|  |     hmac_ctx.CalculateDigest( | ||||||
|  |         output.data(), reinterpret_cast<const unsigned char*>(ctx.buffer.data()), ctx.buffer_size); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | DerivedKeys GenerateKey(const InternalKey& key, const NTAG215File& data) { | ||||||
|  |     const auto seed = GetSeed(data); | ||||||
|  | 
 | ||||||
|  |     // Generate internal seed
 | ||||||
|  |     const std::vector<u8> internal_key = GenerateInternalKey(key, seed); | ||||||
|  | 
 | ||||||
|  |     // Initialize context
 | ||||||
|  |     CryptoCtx ctx{}; | ||||||
|  |     CryptoPP::HMAC<CryptoPP::SHA256> hmac_ctx; | ||||||
|  |     CryptoInit(ctx, hmac_ctx, key.hmac_key, internal_key); | ||||||
|  | 
 | ||||||
|  |     // Generate derived keys
 | ||||||
|  |     DerivedKeys derived_keys{}; | ||||||
|  |     std::array<DrgbOutput, 2> temp{}; | ||||||
|  |     CryptoStep(ctx, hmac_ctx, temp[0]); | ||||||
|  |     CryptoStep(ctx, hmac_ctx, temp[1]); | ||||||
|  |     memcpy(&derived_keys, temp.data(), sizeof(DerivedKeys)); | ||||||
|  | 
 | ||||||
|  |     return derived_keys; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void Cipher(const DerivedKeys& keys, const NTAG215File& in_data, NTAG215File& out_data) { | ||||||
|  |     CryptoPP::CTR_Mode<CryptoPP::AES>::Decryption d2; | ||||||
|  |     d2.SetKeyWithIV(keys.aes_key.data(), keys.aes_key.size(), keys.aes_iv.data(), | ||||||
|  |                     keys.aes_iv.size()); | ||||||
|  | 
 | ||||||
|  |     constexpr std::size_t encrypted_data_size = HMAC_TAG_START - SETTINGS_START; | ||||||
|  |     d2.ProcessData(reinterpret_cast<unsigned char*>(&out_data.settings), | ||||||
|  |                    reinterpret_cast<const unsigned char*>(&in_data.settings), encrypted_data_size); | ||||||
|  | 
 | ||||||
|  |     // Copy the rest of the data directly
 | ||||||
|  |     out_data.uid = in_data.uid; | ||||||
|  |     out_data.nintendo_id = in_data.nintendo_id; | ||||||
|  |     out_data.lock_bytes = in_data.lock_bytes; | ||||||
|  |     out_data.static_lock = in_data.static_lock; | ||||||
|  |     out_data.compability_container = in_data.compability_container; | ||||||
|  | 
 | ||||||
|  |     out_data.constant_value = in_data.constant_value; | ||||||
|  |     out_data.write_counter = in_data.write_counter; | ||||||
|  | 
 | ||||||
|  |     out_data.model_info = in_data.model_info; | ||||||
|  |     out_data.keygen_salt = in_data.keygen_salt; | ||||||
|  |     out_data.dynamic_lock = in_data.dynamic_lock; | ||||||
|  |     out_data.CFG0 = in_data.CFG0; | ||||||
|  |     out_data.CFG1 = in_data.CFG1; | ||||||
|  |     out_data.password = in_data.password; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool LoadKeys(InternalKey& locked_secret, InternalKey& unfixed_info) { | ||||||
|  |     const auto citra_keys_dir = FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir); | ||||||
|  |     auto keys_file = FileUtil::IOFile(citra_keys_dir + "key_retail.bin", "rb"); | ||||||
|  | 
 | ||||||
|  |     if (!keys_file.IsOpen()) { | ||||||
|  |         LOG_ERROR(Service_NFC, "No keys detected"); | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (keys_file.ReadBytes(&unfixed_info, sizeof(InternalKey)) != sizeof(InternalKey)) { | ||||||
|  |         LOG_ERROR(Service_NFC, "Failed to read unfixed_info"); | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |     if (keys_file.ReadBytes(&locked_secret, sizeof(InternalKey)) != sizeof(InternalKey)) { | ||||||
|  |         LOG_ERROR(Service_NFC, "Failed to read locked-secret"); | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool IsKeyAvailable() { | ||||||
|  |     const auto citra_keys_dir = FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir); | ||||||
|  |     return FileUtil::Exists(citra_keys_dir + "key_retail.bin"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool DecodeAmiibo(const EncryptedNTAG215File& encrypted_tag_data, NTAG215File& tag_data) { | ||||||
|  |     InternalKey locked_secret{}; | ||||||
|  |     InternalKey unfixed_info{}; | ||||||
|  | 
 | ||||||
|  |     if (!LoadKeys(locked_secret, unfixed_info)) { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Generate keys
 | ||||||
|  |     NTAG215File encoded_data = NfcDataToEncodedData(encrypted_tag_data); | ||||||
|  |     const auto data_keys = GenerateKey(unfixed_info, encoded_data); | ||||||
|  |     const auto tag_keys = GenerateKey(locked_secret, encoded_data); | ||||||
|  | 
 | ||||||
|  |     // Decrypt
 | ||||||
|  |     Cipher(data_keys, encoded_data, tag_data); | ||||||
|  | 
 | ||||||
|  |     // Regenerate tag HMAC. Note: order matters, data HMAC depends on tag HMAC!
 | ||||||
|  |     constexpr std::size_t input_length = DYNAMIC_LOCK_START - UUID_START; | ||||||
|  |     CryptoPP::HMAC<CryptoPP::SHA256> tag_hmac(tag_keys.hmac_key.data(), sizeof(HmacKey)); | ||||||
|  |     tag_hmac.CalculateDigest(reinterpret_cast<unsigned char*>(&tag_data.hmac_tag), | ||||||
|  |                              reinterpret_cast<const unsigned char*>(&tag_data.uid), input_length); | ||||||
|  | 
 | ||||||
|  |     // Regenerate data HMAC
 | ||||||
|  |     constexpr std::size_t input_length2 = DYNAMIC_LOCK_START - WRITE_COUNTER_START; | ||||||
|  |     CryptoPP::HMAC<CryptoPP::SHA256> data_hmac(data_keys.hmac_key.data(), sizeof(HmacKey)); | ||||||
|  |     data_hmac.CalculateDigest(reinterpret_cast<unsigned char*>(&tag_data.hmac_data), | ||||||
|  |                               reinterpret_cast<const unsigned char*>(&tag_data.write_counter), | ||||||
|  |                               input_length2); | ||||||
|  | 
 | ||||||
|  |     if (tag_data.hmac_data != encrypted_tag_data.user_memory.hmac_data) { | ||||||
|  |         LOG_ERROR(Service_NFC, "hmac_data doesn't match"); | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (tag_data.hmac_tag != encrypted_tag_data.user_memory.hmac_tag) { | ||||||
|  |         LOG_ERROR(Service_NFC, "hmac_tag doesn't match"); | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool EncodeAmiibo(const NTAG215File& tag_data, EncryptedNTAG215File& encrypted_tag_data) { | ||||||
|  |     InternalKey locked_secret{}; | ||||||
|  |     InternalKey unfixed_info{}; | ||||||
|  | 
 | ||||||
|  |     if (!LoadKeys(locked_secret, unfixed_info)) { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Generate keys
 | ||||||
|  |     const auto data_keys = GenerateKey(unfixed_info, tag_data); | ||||||
|  |     const auto tag_keys = GenerateKey(locked_secret, tag_data); | ||||||
|  | 
 | ||||||
|  |     NTAG215File encoded_tag_data{}; | ||||||
|  | 
 | ||||||
|  |     // Generate tag HMAC
 | ||||||
|  |     constexpr std::size_t input_length = DYNAMIC_LOCK_START - UUID_START; | ||||||
|  |     constexpr std::size_t input_length2 = HMAC_TAG_START - WRITE_COUNTER_START; | ||||||
|  |     CryptoPP::HMAC<CryptoPP::SHA256> tag_hmac(tag_keys.hmac_key.data(), sizeof(HmacKey)); | ||||||
|  |     tag_hmac.CalculateDigest(reinterpret_cast<unsigned char*>(&encoded_tag_data.hmac_tag), | ||||||
|  |                              reinterpret_cast<const unsigned char*>(&tag_data.uid), input_length); | ||||||
|  | 
 | ||||||
|  |     // Generate data HMAC
 | ||||||
|  |     CryptoPP::HMAC<CryptoPP::SHA256> data_hmac(data_keys.hmac_key.data(), sizeof(HmacKey)); | ||||||
|  |     data_hmac.Update(reinterpret_cast<const unsigned char*>(&tag_data.write_counter), | ||||||
|  |                      input_length2); | ||||||
|  |     data_hmac.Update(reinterpret_cast<unsigned char*>(&encoded_tag_data.hmac_tag), | ||||||
|  |                      sizeof(HashData)); | ||||||
|  |     data_hmac.Update(reinterpret_cast<const unsigned char*>(&tag_data.uid), input_length); | ||||||
|  |     data_hmac.Final(reinterpret_cast<unsigned char*>(&encoded_tag_data.hmac_data)); | ||||||
|  | 
 | ||||||
|  |     // Encrypt
 | ||||||
|  |     Cipher(data_keys, tag_data, encoded_tag_data); | ||||||
|  | 
 | ||||||
|  |     // Convert back to hardware
 | ||||||
|  |     encrypted_tag_data = EncodedDataToNfcData(encoded_tag_data); | ||||||
|  | 
 | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } // namespace Service::NFC::AmiiboCrypto
 | ||||||
							
								
								
									
										109
									
								
								src/core/hle/service/nfc/amiibo_crypto.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								src/core/hle/service/nfc/amiibo_crypto.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,109 @@ | ||||||
|  | // Copyright 2022 yuzu Emulator Project
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <array> | ||||||
|  | #include <vector> | ||||||
|  | 
 | ||||||
|  | #include "core/hle/service/nfc/nfc_types.h" | ||||||
|  | 
 | ||||||
|  | namespace CryptoPP { | ||||||
|  | class SHA256; | ||||||
|  | template <class T> | ||||||
|  | class HMAC; | ||||||
|  | } // namespace CryptoPP
 | ||||||
|  | 
 | ||||||
|  | namespace Service::NFC::AmiiboCrypto { | ||||||
|  | // Byte locations in Service::NFC::NTAG215File
 | ||||||
|  | constexpr std::size_t HMAC_DATA_START = 0x8; | ||||||
|  | constexpr std::size_t SETTINGS_START = 0x2c; | ||||||
|  | constexpr std::size_t WRITE_COUNTER_START = 0x29; | ||||||
|  | constexpr std::size_t HMAC_TAG_START = 0x1B4; | ||||||
|  | constexpr std::size_t UUID_START = 0x1D4; | ||||||
|  | constexpr std::size_t DYNAMIC_LOCK_START = 0x208; | ||||||
|  | 
 | ||||||
|  | using HmacKey = std::array<u8, 0x10>; | ||||||
|  | using DrgbOutput = std::array<u8, 0x20>; | ||||||
|  | 
 | ||||||
|  | struct HashSeed { | ||||||
|  |     u16_be magic; | ||||||
|  |     std::array<u8, 0xE> padding; | ||||||
|  |     UniqueSerialNumber uid_1; | ||||||
|  |     u8 nintendo_id_1; | ||||||
|  |     UniqueSerialNumber uid_2; | ||||||
|  |     u8 nintendo_id_2; | ||||||
|  |     std::array<u8, 0x20> keygen_salt; | ||||||
|  | }; | ||||||
|  | static_assert(sizeof(HashSeed) == 0x40, "HashSeed is an invalid size"); | ||||||
|  | 
 | ||||||
|  | struct InternalKey { | ||||||
|  |     HmacKey hmac_key; | ||||||
|  |     std::array<char, 0xE> type_string; | ||||||
|  |     u8 reserved; | ||||||
|  |     u8 magic_length; | ||||||
|  |     std::array<u8, 0x10> magic_bytes; | ||||||
|  |     std::array<u8, 0x20> xor_pad; | ||||||
|  | }; | ||||||
|  | static_assert(sizeof(InternalKey) == 0x50, "InternalKey is an invalid size"); | ||||||
|  | static_assert(std::is_trivially_copyable_v<InternalKey>, "InternalKey must be trivially copyable."); | ||||||
|  | 
 | ||||||
|  | struct CryptoCtx { | ||||||
|  |     std::array<char, 480> buffer; | ||||||
|  |     bool used; | ||||||
|  |     std::size_t buffer_size; | ||||||
|  |     s16 counter; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct DerivedKeys { | ||||||
|  |     std::array<u8, 0x10> aes_key; | ||||||
|  |     std::array<u8, 0x10> aes_iv; | ||||||
|  |     std::array<u8, 0x10> hmac_key; | ||||||
|  | }; | ||||||
|  | static_assert(sizeof(DerivedKeys) == 0x30, "DerivedKeys is an invalid size"); | ||||||
|  | 
 | ||||||
|  | /// Validates that the amiibo file is not corrupted
 | ||||||
|  | bool IsAmiiboValid(const EncryptedNTAG215File& ntag_file); | ||||||
|  | 
 | ||||||
|  | /// Validates that the amiibo file is not corrupted
 | ||||||
|  | bool IsAmiiboValid(const NTAG215File& ntag_file); | ||||||
|  | 
 | ||||||
|  | /// Converts from encrypted file format to encoded file format
 | ||||||
|  | NTAG215File NfcDataToEncodedData(const EncryptedNTAG215File& nfc_data); | ||||||
|  | 
 | ||||||
|  | /// Converts from encoded file format to encrypted file format
 | ||||||
|  | EncryptedNTAG215File EncodedDataToNfcData(const NTAG215File& encoded_data); | ||||||
|  | 
 | ||||||
|  | // Generates Seed needed for key derivation
 | ||||||
|  | HashSeed GetSeed(const NTAG215File& data); | ||||||
|  | 
 | ||||||
|  | // Middle step on the generation of derived keys
 | ||||||
|  | std::vector<u8> GenerateInternalKey(const InternalKey& key, const HashSeed& seed); | ||||||
|  | 
 | ||||||
|  | // Initializes mbedtls context
 | ||||||
|  | void CryptoInit(CryptoCtx& ctx, CryptoPP::HMAC<CryptoPP::SHA256>& hmac_ctx, const HmacKey& hmac_key, | ||||||
|  |                 const std::vector<u8>& seed); | ||||||
|  | 
 | ||||||
|  | // Feeds data to mbedtls context to generate the derived key
 | ||||||
|  | void CryptoStep(CryptoCtx& ctx, CryptoPP::HMAC<CryptoPP::SHA256>& hmac_ctx, DrgbOutput& output); | ||||||
|  | 
 | ||||||
|  | // Generates the derived key from amiibo data
 | ||||||
|  | DerivedKeys GenerateKey(const InternalKey& key, const NTAG215File& data); | ||||||
|  | 
 | ||||||
|  | // Encodes or decodes amiibo data
 | ||||||
|  | void Cipher(const DerivedKeys& keys, const NTAG215File& in_data, NTAG215File& out_data); | ||||||
|  | 
 | ||||||
|  | /// Loads both amiibo keys from key_retail.bin
 | ||||||
|  | bool LoadKeys(InternalKey& locked_secret, InternalKey& unfixed_info); | ||||||
|  | 
 | ||||||
|  | /// Returns true if key_retail.bin exist
 | ||||||
|  | bool IsKeyAvailable(); | ||||||
|  | 
 | ||||||
|  | /// Decodes encripted amiibo data returns true if output is valid
 | ||||||
|  | bool DecodeAmiibo(const EncryptedNTAG215File& encrypted_tag_data, NTAG215File& tag_data); | ||||||
|  | 
 | ||||||
|  | /// Encodes plain amiibo data returns true if output is valid
 | ||||||
|  | bool EncodeAmiibo(const NTAG215File& tag_data, EncryptedNTAG215File& encrypted_tag_data); | ||||||
|  | 
 | ||||||
|  | } // namespace Service::NFC::AmiiboCrypto
 | ||||||
|  | @ -18,334 +18,662 @@ namespace Service::NFC { | ||||||
| 
 | 
 | ||||||
| template <class Archive> | template <class Archive> | ||||||
| void Module::serialize(Archive& ar, const unsigned int) { | void Module::serialize(Archive& ar, const unsigned int) { | ||||||
|     ar& tag_in_range_event; |     ar& nfc_mode; | ||||||
|     ar& tag_out_of_range_event; |     ar& device; | ||||||
|     ar& nfc_tag_state; |  | ||||||
|     ar& nfc_status; |  | ||||||
|     ar& amiibo_data; |  | ||||||
|     ar& amiibo_in_range; |  | ||||||
| } | } | ||||||
| SERIALIZE_IMPL(Module) | SERIALIZE_IMPL(Module) | ||||||
| 
 | 
 | ||||||
| struct TagInfo { |  | ||||||
|     u16_le id_offset_size; |  | ||||||
|     u8 unk1; |  | ||||||
|     u8 unk2; |  | ||||||
|     std::array<u8, 7> uuid; |  | ||||||
|     INSERT_PADDING_BYTES(0x20); |  | ||||||
| }; |  | ||||||
| static_assert(sizeof(TagInfo) == 0x2C, "TagInfo is an invalid size"); |  | ||||||
| 
 |  | ||||||
| struct AmiiboConfig { |  | ||||||
|     u16_le lastwritedate_year; |  | ||||||
|     u8 lastwritedate_month; |  | ||||||
|     u8 lastwritedate_day; |  | ||||||
|     u16_le write_counter; |  | ||||||
|     std::array<u8, 3> characterID; |  | ||||||
|     u8 series; |  | ||||||
|     u16_le amiiboID; |  | ||||||
|     u8 type; |  | ||||||
|     u8 pagex4_byte3; |  | ||||||
|     u16_le appdata_size; |  | ||||||
|     INSERT_PADDING_BYTES(0x30); |  | ||||||
| }; |  | ||||||
| static_assert(sizeof(AmiiboConfig) == 0x40, "AmiiboConfig is an invalid size"); |  | ||||||
| 
 |  | ||||||
| struct IdentificationBlockReply { |  | ||||||
|     u16_le char_id; |  | ||||||
|     u8 char_variant; |  | ||||||
|     u8 series; |  | ||||||
|     u16_le model_number; |  | ||||||
|     u8 figure_type; |  | ||||||
|     INSERT_PADDING_BYTES(0x2F); |  | ||||||
| }; |  | ||||||
| static_assert(sizeof(IdentificationBlockReply) == 0x36, |  | ||||||
|               "IdentificationBlockReply is an invalid size"); |  | ||||||
| 
 |  | ||||||
| void Module::Interface::Initialize(Kernel::HLERequestContext& ctx) { | void Module::Interface::Initialize(Kernel::HLERequestContext& ctx) { | ||||||
|     IPC::RequestParser rp(ctx, 0x01, 1, 0); |     IPC::RequestParser rp(ctx, 0x01, 1, 0); | ||||||
|     u8 param = rp.Pop<u8>(); |     const auto communication_mode = rp.PopEnum<CommunicationMode>(); | ||||||
|  | 
 | ||||||
|  |     LOG_INFO(Service_NFC, "called, communication_mode={}", communication_mode); | ||||||
| 
 | 
 | ||||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); |     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||||
|     if (nfc->nfc_tag_state != TagState::NotInitialized) { | 
 | ||||||
|         LOG_ERROR(Service_NFC, "Invalid TagState {}", nfc->nfc_tag_state); |     if (nfc->nfc_mode != CommunicationMode::NotInitialized) { | ||||||
|         rb.Push(ResultCode(ErrCodes::CommandInvalidForState, ErrorModule::NFC, |         rb.Push(ResultCommandInvalidForState); | ||||||
|                            ErrorSummary::InvalidState, ErrorLevel::Status)); |  | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     nfc->nfc_tag_state = TagState::NotScanning; |     ResultCode result = RESULT_SUCCESS; | ||||||
|  |     switch (communication_mode) { | ||||||
|  |     case CommunicationMode::Ntag: | ||||||
|  |     case CommunicationMode::Amiibo: | ||||||
|  |         nfc->device->Initialize(); | ||||||
|  |         break; | ||||||
|  |     case CommunicationMode::TrainTag: | ||||||
|  |         LOG_ERROR(Service_NFC, "CommunicationMode  {} not implemented", communication_mode); | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         result = ResultInvalidArgumentValue; | ||||||
|  |         break; | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     rb.Push(RESULT_SUCCESS); |     if (result.IsSuccess()) { | ||||||
|     LOG_WARNING(Service_NFC, "(STUBBED) called, param={}", param); |         nfc->nfc_mode = communication_mode; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     rb.Push(result); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Module::Interface::Shutdown(Kernel::HLERequestContext& ctx) { | void Module::Interface::Shutdown(Kernel::HLERequestContext& ctx) { | ||||||
|     IPC::RequestParser rp(ctx, 0x02, 1, 0); |     IPC::RequestParser rp(ctx, 0x02, 1, 0); | ||||||
|     u8 param = rp.Pop<u8>(); |     const auto communication_mode = rp.PopEnum<CommunicationMode>(); | ||||||
| 
 | 
 | ||||||
|     nfc->nfc_tag_state = TagState::NotInitialized; |     LOG_INFO(Service_NFC, "called, communication_mode={}", communication_mode); | ||||||
| 
 | 
 | ||||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); |     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||||
|     rb.Push(RESULT_SUCCESS); | 
 | ||||||
|     LOG_WARNING(Service_NFC, "(STUBBED) called, param={}", param); |     if (nfc->nfc_mode != communication_mode) { | ||||||
|  |         rb.Push(ResultCommandInvalidForState); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     ResultCode result = RESULT_SUCCESS; | ||||||
|  |     switch (communication_mode) { | ||||||
|  |     case CommunicationMode::Ntag: | ||||||
|  |     case CommunicationMode::Amiibo: | ||||||
|  |         nfc->device->Finalize(); | ||||||
|  |         break; | ||||||
|  |     case CommunicationMode::TrainTag: | ||||||
|  |         LOG_ERROR(Service_NFC, "CommunicationMode  {} not implemented", communication_mode); | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         result = ResultInvalidArgumentValue; | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (result.IsSuccess()) { | ||||||
|  |         nfc->nfc_mode = CommunicationMode::NotInitialized; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     rb.Push(result); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Module::Interface::StartCommunication(Kernel::HLERequestContext& ctx) { | void Module::Interface::StartCommunication(Kernel::HLERequestContext& ctx) { | ||||||
|     IPC::RequestParser rp(ctx, 0x03, 0, 0); |     IPC::RequestParser rp(ctx, 0x03, 0, 0); | ||||||
| 
 | 
 | ||||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); |  | ||||||
|     rb.Push(RESULT_SUCCESS); |  | ||||||
|     LOG_WARNING(Service_NFC, "(STUBBED) called"); |     LOG_WARNING(Service_NFC, "(STUBBED) called"); | ||||||
|  | 
 | ||||||
|  |     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||||
|  |     if (nfc->nfc_mode == CommunicationMode::TrainTag) { | ||||||
|  |         LOG_ERROR(Service_NFC, "CommunicationMode  {} not implemented", nfc->nfc_mode); | ||||||
|  |         rb.Push(RESULT_SUCCESS); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // TODO: call start communication instead
 | ||||||
|  |     const auto result = nfc->device->StartCommunication(); | ||||||
|  |     rb.Push(result); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Module::Interface::StopCommunication(Kernel::HLERequestContext& ctx) { | void Module::Interface::StopCommunication(Kernel::HLERequestContext& ctx) { | ||||||
|     IPC::RequestParser rp(ctx, 0x04, 0, 0); |     IPC::RequestParser rp(ctx, 0x04, 0, 0); | ||||||
| 
 | 
 | ||||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); |  | ||||||
|     rb.Push(RESULT_SUCCESS); |  | ||||||
|     LOG_WARNING(Service_NFC, "(STUBBED) called"); |     LOG_WARNING(Service_NFC, "(STUBBED) called"); | ||||||
|  | 
 | ||||||
|  |     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||||
|  |     if (nfc->nfc_mode == CommunicationMode::TrainTag) { | ||||||
|  |         LOG_ERROR(Service_NFC, "CommunicationMode  {} not implemented", nfc->nfc_mode); | ||||||
|  |         rb.Push(RESULT_SUCCESS); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // TODO: call stop communication instead
 | ||||||
|  |     const auto result = nfc->device->StopCommunication(); | ||||||
|  |     rb.Push(result); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Module::Interface::StartTagScanning(Kernel::HLERequestContext& ctx) { | void Module::Interface::StartTagScanning(Kernel::HLERequestContext& ctx) { | ||||||
|     IPC::RequestParser rp(ctx, 0x05, 1, 0); // 0x00050040
 |     IPC::RequestParser rp(ctx, 0x05, 1, 0); | ||||||
|     u16 in_val = rp.Pop<u16>(); |     u16 in_val = rp.Pop<u16>(); | ||||||
| 
 | 
 | ||||||
|  |     LOG_INFO(Service_NFC, "called, in_val={:04x}", in_val); | ||||||
|  | 
 | ||||||
|  |     ResultCode result = RESULT_SUCCESS; | ||||||
|  |     switch (nfc->nfc_mode) { | ||||||
|  |     case CommunicationMode::Ntag: | ||||||
|  |     case CommunicationMode::Amiibo: | ||||||
|  |         // in_val probably correlates to the tag protocol to be detected
 | ||||||
|  |         result = nfc->device->StartDetection(TagProtocol::All); | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         result = ResultInvalidArgumentValue; | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); |     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||||
|     if (nfc->nfc_tag_state != TagState::NotScanning && |     rb.Push(result); | ||||||
|         nfc->nfc_tag_state != TagState::TagOutOfRange) { |  | ||||||
|         LOG_ERROR(Service_NFC, "Invalid TagState {}", nfc->nfc_tag_state); |  | ||||||
|         rb.Push(ResultCode(ErrCodes::CommandInvalidForState, ErrorModule::NFC, |  | ||||||
|                            ErrorSummary::InvalidState, ErrorLevel::Status)); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     nfc->nfc_tag_state = TagState::Scanning; |  | ||||||
|     nfc->SyncTagState(); |  | ||||||
| 
 |  | ||||||
|     rb.Push(RESULT_SUCCESS); |  | ||||||
|     LOG_WARNING(Service_NFC, "(STUBBED) called, in_val={:04x}", in_val); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void Module::Interface::GetTagInfo(Kernel::HLERequestContext& ctx) { |  | ||||||
|     IPC::RequestParser rp(ctx, 0x11, 0, 0); |  | ||||||
| 
 |  | ||||||
|     if (nfc->nfc_tag_state != TagState::TagInRange && |  | ||||||
|         nfc->nfc_tag_state != TagState::TagDataLoaded && nfc->nfc_tag_state != TagState::Unknown6) { |  | ||||||
|         LOG_ERROR(Service_NFC, "Invalid TagState {}", nfc->nfc_tag_state); |  | ||||||
|         IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); |  | ||||||
|         rb.Push(ResultCode(ErrCodes::CommandInvalidForState, ErrorModule::NFC, |  | ||||||
|                            ErrorSummary::InvalidState, ErrorLevel::Status)); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     TagInfo tag_info{}; |  | ||||||
|     tag_info.uuid = nfc->amiibo_data.uuid; |  | ||||||
|     tag_info.id_offset_size = static_cast<u16>(tag_info.uuid.size()); |  | ||||||
|     tag_info.unk1 = 0x0; |  | ||||||
|     tag_info.unk2 = 0x2; |  | ||||||
| 
 |  | ||||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(12, 0); |  | ||||||
|     rb.Push(RESULT_SUCCESS); |  | ||||||
|     rb.PushRaw<TagInfo>(tag_info); |  | ||||||
|     LOG_WARNING(Service_NFC, "(STUBBED) called"); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void Module::Interface::GetAmiiboConfig(Kernel::HLERequestContext& ctx) { |  | ||||||
|     IPC::RequestParser rp(ctx, 0x18, 0, 0); |  | ||||||
| 
 |  | ||||||
|     AmiiboConfig amiibo_config{}; |  | ||||||
|     amiibo_config.lastwritedate_year = 2017; |  | ||||||
|     amiibo_config.lastwritedate_month = 10; |  | ||||||
|     amiibo_config.lastwritedate_day = 10; |  | ||||||
|     amiibo_config.write_counter = 0x0; |  | ||||||
|     std::memcpy(amiibo_config.characterID.data(), &nfc->amiibo_data.char_id, |  | ||||||
|                 sizeof(nfc->amiibo_data.char_id)); |  | ||||||
|     amiibo_config.series = nfc->amiibo_data.series; |  | ||||||
|     amiibo_config.amiiboID = nfc->amiibo_data.model_number; |  | ||||||
|     amiibo_config.type = nfc->amiibo_data.figure_type; |  | ||||||
|     amiibo_config.pagex4_byte3 = 0x0; |  | ||||||
|     amiibo_config.appdata_size = 0xD8; |  | ||||||
| 
 |  | ||||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(17, 0); |  | ||||||
|     rb.Push(RESULT_SUCCESS); |  | ||||||
|     rb.PushRaw<AmiiboConfig>(amiibo_config); |  | ||||||
|     LOG_WARNING(Service_NFC, "(STUBBED) called"); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Module::Interface::StopTagScanning(Kernel::HLERequestContext& ctx) { | void Module::Interface::StopTagScanning(Kernel::HLERequestContext& ctx) { | ||||||
|     IPC::RequestParser rp(ctx, 0x06, 0, 0); |     IPC::RequestParser rp(ctx, 0x06, 0, 0); | ||||||
| 
 | 
 | ||||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); |     LOG_INFO(Service_NFC, "called"); | ||||||
|     if (nfc->nfc_tag_state == TagState::NotInitialized || | 
 | ||||||
|         nfc->nfc_tag_state == TagState::NotScanning) { |     ResultCode result = RESULT_SUCCESS; | ||||||
|         LOG_ERROR(Service_NFC, "Invalid TagState {}", nfc->nfc_tag_state); |     switch (nfc->nfc_mode) { | ||||||
|         rb.Push(ResultCode(ErrCodes::CommandInvalidForState, ErrorModule::NFC, |     case CommunicationMode::Ntag: | ||||||
|                            ErrorSummary::InvalidState, ErrorLevel::Status)); |     case CommunicationMode::Amiibo: | ||||||
|         return; |         result = nfc->device->StopDetection(); | ||||||
|  |         break; | ||||||
|  |     case CommunicationMode::TrainTag: | ||||||
|  |         LOG_ERROR(Service_NFC, "CommunicationMode  {} not implemented", nfc->nfc_mode); | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         result = ResultCommandInvalidForState; | ||||||
|  |         break; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     nfc->nfc_tag_state = TagState::NotScanning; |     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||||
| 
 |     rb.Push(result); | ||||||
|     rb.Push(RESULT_SUCCESS); |  | ||||||
|     LOG_DEBUG(Service_NFC, "called"); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Module::Interface::LoadAmiiboData(Kernel::HLERequestContext& ctx) { | void Module::Interface::LoadAmiiboData(Kernel::HLERequestContext& ctx) { | ||||||
|     IPC::RequestParser rp(ctx, 0x07, 0, 0); |     IPC::RequestParser rp(ctx, 0x07, 0, 0); | ||||||
| 
 | 
 | ||||||
|     // TODO(FearlessTobi): Add state checking when this function gets properly implemented
 |     LOG_INFO(Service_NFC, "called"); | ||||||
| 
 | 
 | ||||||
|     nfc->nfc_tag_state = TagState::TagDataLoaded; |     ResultCode result = RESULT_SUCCESS; | ||||||
|  |     switch (nfc->nfc_mode) { | ||||||
|  |     case CommunicationMode::Ntag: | ||||||
|  |         result = nfc->device->Mount(); | ||||||
|  |         break; | ||||||
|  |     case CommunicationMode::Amiibo: | ||||||
|  |         result = nfc->device->MountAmiibo(); | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         result = ResultCommandInvalidForState; | ||||||
|  |         break; | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); |     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||||
|     rb.Push(RESULT_SUCCESS); |     rb.Push(result); | ||||||
|     LOG_WARNING(Service_NFC, "(STUBBED) called"); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Module::Interface::ResetTagScanState(Kernel::HLERequestContext& ctx) { | void Module::Interface::ResetTagScanState(Kernel::HLERequestContext& ctx) { | ||||||
|     IPC::RequestParser rp(ctx, 0x08, 0, 0); |     IPC::RequestParser rp(ctx, 0x08, 0, 0); | ||||||
| 
 | 
 | ||||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); |     LOG_INFO(Service_NFC, "called"); | ||||||
|     if (nfc->nfc_tag_state != TagState::TagDataLoaded && nfc->nfc_tag_state != TagState::Unknown6) { | 
 | ||||||
|         LOG_ERROR(Service_NFC, "Invalid TagState {}", nfc->nfc_tag_state); |     ResultCode result = RESULT_SUCCESS; | ||||||
|         rb.Push(ResultCode(ErrCodes::CommandInvalidForState, ErrorModule::NFC, |     switch (nfc->nfc_mode) { | ||||||
|                            ErrorSummary::InvalidState, ErrorLevel::Status)); |     case CommunicationMode::Ntag: | ||||||
|         return; |     case CommunicationMode::Amiibo: | ||||||
|  |         result = nfc->device->ResetTagScanState(); | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         result = ResultCommandInvalidForState; | ||||||
|  |         break; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     nfc->nfc_tag_state = TagState::TagInRange; |     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||||
|     nfc->SyncTagState(); |     rb.Push(result); | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
|     rb.Push(RESULT_SUCCESS); | void Module::Interface::UpdateStoredAmiiboData(Kernel::HLERequestContext& ctx) { | ||||||
|     LOG_DEBUG(Service_NFC, "called"); |     IPC::RequestParser rp(ctx, 0x09, 0, 0); | ||||||
|  | 
 | ||||||
|  |     LOG_INFO(Service_NFC, "called"); | ||||||
|  | 
 | ||||||
|  |     ResultCode result = RESULT_SUCCESS; | ||||||
|  |     switch (nfc->nfc_mode) { | ||||||
|  |     case CommunicationMode::Ntag: | ||||||
|  |         LOG_ERROR(Service_NFC, "CommunicationMode  {} not implemented", nfc->nfc_mode); | ||||||
|  |         break; | ||||||
|  |     case CommunicationMode::Amiibo: | ||||||
|  |         result = nfc->device->Flush(); | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         result = ResultCommandInvalidForState; | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||||
|  |     rb.Push(result); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Module::Interface::GetTagInRangeEvent(Kernel::HLERequestContext& ctx) { | void Module::Interface::GetTagInRangeEvent(Kernel::HLERequestContext& ctx) { | ||||||
|     IPC::RequestParser rp(ctx, 0x0B, 0, 0); |     IPC::RequestParser rp(ctx, 0x0B, 0, 0); | ||||||
| 
 | 
 | ||||||
|     if (nfc->nfc_tag_state != TagState::NotScanning) { |     LOG_INFO(Service_NFC, "called"); | ||||||
|         LOG_ERROR(Service_NFC, "Invalid TagState {}", nfc->nfc_tag_state); | 
 | ||||||
|  |     if (nfc->nfc_mode == CommunicationMode::TrainTag) { | ||||||
|  |         LOG_ERROR(Service_NFC, "CommunicationMode  {} not implemented", nfc->nfc_mode); | ||||||
|         IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); |         IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||||
|         rb.Push(ResultCode(ErrCodes::CommandInvalidForState, ErrorModule::NFC, |         rb.Push(ResultCommandInvalidForState); | ||||||
|                            ErrorSummary::InvalidState, ErrorLevel::Status)); |  | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); |     IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); | ||||||
|     rb.Push(RESULT_SUCCESS); |     rb.Push(RESULT_SUCCESS); | ||||||
|     rb.PushCopyObjects(nfc->tag_in_range_event); |     rb.PushCopyObjects(nfc->device->GetActivateEvent()); | ||||||
|     LOG_DEBUG(Service_NFC, "called"); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Module::Interface::GetTagOutOfRangeEvent(Kernel::HLERequestContext& ctx) { | void Module::Interface::GetTagOutOfRangeEvent(Kernel::HLERequestContext& ctx) { | ||||||
|     IPC::RequestParser rp(ctx, 0x0C, 0, 0); |     IPC::RequestParser rp(ctx, 0x0C, 0, 0); | ||||||
| 
 | 
 | ||||||
|     if (nfc->nfc_tag_state != TagState::NotScanning) { |     LOG_INFO(Service_NFC, "called"); | ||||||
|         LOG_ERROR(Service_NFC, "Invalid TagState {}", nfc->nfc_tag_state); | 
 | ||||||
|  |     if (nfc->nfc_mode == CommunicationMode::TrainTag) { | ||||||
|  |         LOG_ERROR(Service_NFC, "CommunicationMode  {} not implemented", nfc->nfc_mode); | ||||||
|         IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); |         IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||||
|         rb.Push(ResultCode(ErrCodes::CommandInvalidForState, ErrorModule::NFC, |         rb.Push(ResultCommandInvalidForState); | ||||||
|                            ErrorSummary::InvalidState, ErrorLevel::Status)); |  | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); |     IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); | ||||||
|     rb.Push(RESULT_SUCCESS); |     rb.Push(RESULT_SUCCESS); | ||||||
|     rb.PushCopyObjects(nfc->tag_out_of_range_event); |     rb.PushCopyObjects(nfc->device->GetDeactivateEvent()); | ||||||
|     LOG_DEBUG(Service_NFC, "called"); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Module::Interface::GetTagState(Kernel::HLERequestContext& ctx) { | void Module::Interface::GetTagState(Kernel::HLERequestContext& ctx) { | ||||||
|     IPC::RequestParser rp(ctx, 0x0D, 0, 0); |     IPC::RequestParser rp(ctx, 0x0D, 0, 0); | ||||||
|  |     DeviceState state = DeviceState::NotInitialized; | ||||||
|  | 
 | ||||||
|  |     LOG_DEBUG(Service_NFC, "called"); | ||||||
|  | 
 | ||||||
|  |     if (nfc->nfc_mode == CommunicationMode::TrainTag) { | ||||||
|  |         LOG_ERROR(Service_NFC, "CommunicationMode  {} not implemented", nfc->nfc_mode); | ||||||
|  |     } else { | ||||||
|  |         state = nfc->device->GetCurrentState(); | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); |     IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); | ||||||
|     rb.Push(RESULT_SUCCESS); |     rb.Push(RESULT_SUCCESS); | ||||||
|     rb.PushEnum(nfc->nfc_tag_state); |     rb.PushEnum(state); | ||||||
|     LOG_DEBUG(Service_NFC, "called"); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Module::Interface::CommunicationGetStatus(Kernel::HLERequestContext& ctx) { | void Module::Interface::CommunicationGetStatus(Kernel::HLERequestContext& ctx) { | ||||||
|     IPC::RequestParser rp(ctx, 0x0F, 0, 0); |     IPC::RequestParser rp(ctx, 0x0F, 0, 0); | ||||||
| 
 | 
 | ||||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); |     LOG_DEBUG(Service_NFC, "called"); | ||||||
|     rb.Push(RESULT_SUCCESS); |  | ||||||
|     rb.PushEnum(nfc->nfc_status); |  | ||||||
|     LOG_DEBUG(Service_NFC, "(STUBBED) called"); |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| void Module::Interface::Unknown0x1A(Kernel::HLERequestContext& ctx) { |     if (nfc->nfc_mode == CommunicationMode::TrainTag) { | ||||||
|     IPC::RequestParser rp(ctx, 0x1A, 0, 0); |         LOG_ERROR(Service_NFC, "CommunicationMode  {} not implemented", nfc->nfc_mode); | ||||||
| 
 |         IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); | ||||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); |         rb.Push(RESULT_SUCCESS); | ||||||
|     if (nfc->nfc_tag_state != TagState::TagInRange) { |         rb.PushEnum(CommunicationState::Idle); | ||||||
|         LOG_ERROR(Service_NFC, "Invalid TagState {}", nfc->nfc_tag_state); |  | ||||||
|         rb.Push(ResultCode(ErrCodes::CommandInvalidForState, ErrorModule::NFC, |  | ||||||
|                            ErrorSummary::InvalidState, ErrorLevel::Status)); |  | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     nfc->nfc_tag_state = TagState::Unknown6; |     CommunicationState status{}; | ||||||
|  |     const auto result = nfc->device->GetCommunicationStatus(status); | ||||||
|  |     IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); | ||||||
|  |     rb.Push(result); | ||||||
|  |     rb.PushEnum(status); | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
|  | void Module::Interface::GetTagInfo2(Kernel::HLERequestContext& ctx) { | ||||||
|  |     IPC::RequestParser rp(ctx, 0x10, 0, 0); | ||||||
|  | 
 | ||||||
|  |     LOG_INFO(Service_NFC, "called"); | ||||||
|  | 
 | ||||||
|  |     if (nfc->nfc_mode == CommunicationMode::TrainTag) { | ||||||
|  |         LOG_ERROR(Service_NFC, "CommunicationMode  {} not implemented", nfc->nfc_mode); | ||||||
|  |         IPC::RequestBuilder rb = rp.MakeBuilder(26, 0); | ||||||
|  |         rb.Push(RESULT_SUCCESS); | ||||||
|  |         rb.PushRaw<TagInfo2>({}); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     TagInfo2 tag_info{}; | ||||||
|  |     const auto result = nfc->device->GetTagInfo2(tag_info); | ||||||
|  |     IPC::RequestBuilder rb = rp.MakeBuilder(26, 0); | ||||||
|  |     rb.Push(result); | ||||||
|  |     rb.PushRaw<TagInfo2>(tag_info); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void Module::Interface::GetTagInfo(Kernel::HLERequestContext& ctx) { | ||||||
|  |     IPC::RequestParser rp(ctx, 0x11, 0, 0); | ||||||
|  | 
 | ||||||
|  |     LOG_INFO(Service_NFC, "called"); | ||||||
|  | 
 | ||||||
|  |     if (nfc->nfc_mode == CommunicationMode::TrainTag) { | ||||||
|  |         LOG_ERROR(Service_NFC, "CommunicationMode  {} not implemented", nfc->nfc_mode); | ||||||
|  |         IPC::RequestBuilder rb = rp.MakeBuilder(12, 0); | ||||||
|  |         rb.Push(RESULT_SUCCESS); | ||||||
|  |         rb.PushRaw<TagInfo>({}); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     TagInfo tag_info{}; | ||||||
|  |     const auto result = nfc->device->GetTagInfo(tag_info); | ||||||
|  |     IPC::RequestBuilder rb = rp.MakeBuilder(12, 0); | ||||||
|  |     rb.Push(result); | ||||||
|  |     rb.PushRaw<TagInfo>(tag_info); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void Module::Interface::CommunicationGetResult(Kernel::HLERequestContext& ctx) { | ||||||
|  |     IPC::RequestParser rp(ctx, 0x12, 0, 0); | ||||||
|  | 
 | ||||||
|  |     IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); | ||||||
|     rb.Push(RESULT_SUCCESS); |     rb.Push(RESULT_SUCCESS); | ||||||
|     LOG_DEBUG(Service_NFC, "called"); |     rb.Push(0); | ||||||
|  |     LOG_WARNING(Service_NFC, "(STUBBED) called"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void Module::Interface::OpenAppData(Kernel::HLERequestContext& ctx) { | ||||||
|  |     IPC::RequestParser rp(ctx, 0x13, 1, 0); | ||||||
|  |     u32 access_id = rp.Pop<u32>(); | ||||||
|  | 
 | ||||||
|  |     LOG_INFO(Service_NFC, "called, access_id={}", access_id); | ||||||
|  | 
 | ||||||
|  |     if (nfc->nfc_mode != CommunicationMode::Amiibo) { | ||||||
|  |         IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||||
|  |         rb.Push(ResultCommandInvalidForState); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     const auto result = nfc->device->OpenApplicationArea(access_id); | ||||||
|  | 
 | ||||||
|  |     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||||
|  |     rb.Push(result); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void Module::Interface::InitializeWriteAppData(Kernel::HLERequestContext& ctx) { | ||||||
|  |     IPC::RequestParser rp(ctx, 0x14, 18, 2); | ||||||
|  |     u32 access_id = rp.Pop<u32>(); | ||||||
|  |     [[maybe_unused]] u32 size = rp.Pop<u32>(); | ||||||
|  |     std::vector<u8> buffer = rp.PopStaticBuffer(); | ||||||
|  | 
 | ||||||
|  |     LOG_CRITICAL(Service_NFC, "called, size={}", size); | ||||||
|  | 
 | ||||||
|  |     if (nfc->nfc_mode != CommunicationMode::Amiibo) { | ||||||
|  |         IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||||
|  |         rb.Push(ResultCommandInvalidForState); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     const auto result = nfc->device->CreateApplicationArea(access_id, buffer); | ||||||
|  | 
 | ||||||
|  |     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||||
|  |     rb.Push(result); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void Module::Interface::ReadAppData(Kernel::HLERequestContext& ctx) { | ||||||
|  |     IPC::RequestParser rp(ctx, 0x15, 0, 0); | ||||||
|  | 
 | ||||||
|  |     LOG_INFO(Service_NFC, "called"); | ||||||
|  | 
 | ||||||
|  |     if (nfc->nfc_mode != CommunicationMode::Amiibo) { | ||||||
|  |         IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||||
|  |         rb.Push(ResultCommandInvalidForState); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     std::vector<u8> buffer(sizeof(ApplicationArea)); | ||||||
|  |     const auto result = nfc->device->GetApplicationArea(buffer); | ||||||
|  | 
 | ||||||
|  |     IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); | ||||||
|  |     rb.Push(result); | ||||||
|  |     rb.PushStaticBuffer(buffer, 0); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void Module::Interface::WriteAppData(Kernel::HLERequestContext& ctx) { | ||||||
|  |     IPC::RequestParser rp(ctx, 0x16, 12, 2); | ||||||
|  |     [[maybe_unused]] u32 size = rp.Pop<u32>(); | ||||||
|  |     std::vector<u8> tag_uuid_info = rp.PopStaticBuffer(); | ||||||
|  |     std::vector<u8> buffer = rp.PopStaticBuffer(); | ||||||
|  | 
 | ||||||
|  |     LOG_CRITICAL(Service_NFC, "called, size={}", size); | ||||||
|  | 
 | ||||||
|  |     if (nfc->nfc_mode != CommunicationMode::Amiibo) { | ||||||
|  |         IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||||
|  |         rb.Push(ResultCommandInvalidForState); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     const auto result = nfc->device->SetApplicationArea(buffer); | ||||||
|  | 
 | ||||||
|  |     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||||
|  |     rb.Push(result); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void Module::Interface::GetRegisterInfo(Kernel::HLERequestContext& ctx) { | ||||||
|  |     IPC::RequestParser rp(ctx, 0x17, 0, 0); | ||||||
|  | 
 | ||||||
|  |     LOG_INFO(Service_NFC, "called"); | ||||||
|  | 
 | ||||||
|  |     if (nfc->nfc_mode != CommunicationMode::Amiibo) { | ||||||
|  |         IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||||
|  |         rb.Push(ResultCommandInvalidForState); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     RegisterInfo settings_info{}; | ||||||
|  |     const auto result = nfc->device->GetRegisterInfo(settings_info); | ||||||
|  | 
 | ||||||
|  |     IPC::RequestBuilder rb = rp.MakeBuilder(43, 0); | ||||||
|  |     rb.Push(result); | ||||||
|  |     rb.PushRaw<RegisterInfo>(settings_info); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void Module::Interface::GetCommonInfo(Kernel::HLERequestContext& ctx) { | ||||||
|  |     IPC::RequestParser rp(ctx, 0x18, 0, 0); | ||||||
|  | 
 | ||||||
|  |     LOG_INFO(Service_NFC, "called"); | ||||||
|  | 
 | ||||||
|  |     if (nfc->nfc_mode != CommunicationMode::Amiibo) { | ||||||
|  |         IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||||
|  |         rb.Push(ResultCommandInvalidForState); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     CommonInfo amiibo_config{}; | ||||||
|  |     const auto result = nfc->device->GetCommonInfo(amiibo_config); | ||||||
|  | 
 | ||||||
|  |     IPC::RequestBuilder rb = rp.MakeBuilder(17, 0); | ||||||
|  |     rb.Push(result); | ||||||
|  |     rb.PushRaw<CommonInfo>(amiibo_config); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void Module::Interface::GetAppDataInitStruct(Kernel::HLERequestContext& ctx) { | ||||||
|  |     IPC::RequestParser rp(ctx, 0x19, 0, 0); | ||||||
|  | 
 | ||||||
|  |     LOG_INFO(Service_NFC, "called"); | ||||||
|  | 
 | ||||||
|  |     if (nfc->nfc_mode != CommunicationMode::Amiibo) { | ||||||
|  |         IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||||
|  |         rb.Push(ResultCommandInvalidForState); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     using InitialStruct = std::array<u8, 0x3c>; | ||||||
|  |     InitialStruct empty{}; | ||||||
|  | 
 | ||||||
|  |     IPC::RequestBuilder rb = rp.MakeBuilder(16, 0); | ||||||
|  |     rb.Push(RESULT_SUCCESS); | ||||||
|  |     rb.PushRaw<InitialStruct>(empty); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void Module::Interface::LoadAmiiboPartially(Kernel::HLERequestContext& ctx) { | ||||||
|  |     IPC::RequestParser rp(ctx, 0x1A, 0, 0); | ||||||
|  | 
 | ||||||
|  |     LOG_INFO(Service_NFC, "called"); | ||||||
|  | 
 | ||||||
|  |     ResultCode result = RESULT_SUCCESS; | ||||||
|  |     switch (nfc->nfc_mode) { | ||||||
|  |     case CommunicationMode::Ntag: | ||||||
|  |         result = nfc->device->PartiallyMount(); | ||||||
|  |         break; | ||||||
|  |     case CommunicationMode::Amiibo: | ||||||
|  |         result = nfc->device->PartiallyMountAmiibo(); | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         result = ResultCommandInvalidForState; | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||||
|  |     rb.Push(result); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Module::Interface::GetIdentificationBlock(Kernel::HLERequestContext& ctx) { | void Module::Interface::GetIdentificationBlock(Kernel::HLERequestContext& ctx) { | ||||||
|     IPC::RequestParser rp(ctx, 0x1B, 0, 0); |     IPC::RequestParser rp(ctx, 0x1B, 0, 0); | ||||||
| 
 | 
 | ||||||
|     if (nfc->nfc_tag_state != TagState::TagDataLoaded && nfc->nfc_tag_state != TagState::Unknown6) { |     LOG_INFO(Service_NFC, "called"); | ||||||
|         LOG_ERROR(Service_NFC, "Invalid TagState {}", nfc->nfc_tag_state); | 
 | ||||||
|  |     if (nfc->nfc_mode != CommunicationMode::Amiibo) { | ||||||
|         IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); |         IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||||
|         rb.Push(ResultCode(ErrCodes::CommandInvalidForState, ErrorModule::NFC, |         rb.Push(ResultCommandInvalidForState); | ||||||
|                            ErrorSummary::InvalidState, ErrorLevel::Status)); |  | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     IdentificationBlockReply identification_block_reply{}; |     ModelInfo model_info{}; | ||||||
|     identification_block_reply.char_id = nfc->amiibo_data.char_id; |     const auto result = nfc->device->GetModelInfo(model_info); | ||||||
|     identification_block_reply.char_variant = nfc->amiibo_data.char_variant; |  | ||||||
|     identification_block_reply.series = nfc->amiibo_data.series; |  | ||||||
|     identification_block_reply.model_number = nfc->amiibo_data.model_number; |  | ||||||
|     identification_block_reply.figure_type = nfc->amiibo_data.figure_type; |  | ||||||
| 
 | 
 | ||||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(0x1F, 0); |     IPC::RequestBuilder rb = rp.MakeBuilder(0x1F, 0); | ||||||
|  |     rb.Push(result); | ||||||
|  |     rb.PushRaw<ModelInfo>(model_info); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void Module::Interface::Format(Kernel::HLERequestContext& ctx) { | ||||||
|  |     IPC::RequestParser rp(ctx, 0x401, 3, 2); | ||||||
|  |     [[maybe_unused]] u32 unknown1 = rp.Pop<u32>(); | ||||||
|  |     [[maybe_unused]] u32 unknown2 = rp.Pop<u32>(); | ||||||
|  |     [[maybe_unused]] u32 unknown3 = rp.Pop<u32>(); | ||||||
|  |     [[maybe_unused]] std::vector<u8> buffer = rp.PopStaticBuffer(); | ||||||
|  | 
 | ||||||
|  |     const auto result = nfc->device->Format(); | ||||||
|  | 
 | ||||||
|  |     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||||
|  |     rb.Push(result); | ||||||
|  |     LOG_WARNING(Service_NFC, "(STUBBED) called"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void Module::Interface::GetAdminInfo(Kernel::HLERequestContext& ctx) { | ||||||
|  |     IPC::RequestParser rp(ctx, 0x402, 0, 0); | ||||||
|  | 
 | ||||||
|  |     LOG_INFO(Service_NFC, "called"); | ||||||
|  | 
 | ||||||
|  |     if (nfc->nfc_mode != CommunicationMode::Amiibo) { | ||||||
|  |         IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||||
|  |         rb.Push(ResultCommandInvalidForState); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     AdminInfo admin_info{}; | ||||||
|  |     const auto result = nfc->device->GetAdminInfo(admin_info); | ||||||
|  | 
 | ||||||
|  |     IPC::RequestBuilder rb = rp.MakeBuilder(17, 0); | ||||||
|  |     rb.Push(result); | ||||||
|  |     rb.PushRaw<AdminInfo>(admin_info); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void Module::Interface::GetEmptyRegisterInfo(Kernel::HLERequestContext& ctx) { | ||||||
|  |     IPC::RequestParser rp(ctx, 0x403, 0, 0); | ||||||
|  | 
 | ||||||
|  |     LOG_INFO(Service_NFC, "called"); | ||||||
|  | 
 | ||||||
|  |     if (nfc->nfc_mode != CommunicationMode::Amiibo) { | ||||||
|  |         IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||||
|  |         rb.Push(ResultCommandInvalidForState); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     IPC::RequestBuilder rb = rp.MakeBuilder(43, 0); | ||||||
|     rb.Push(RESULT_SUCCESS); |     rb.Push(RESULT_SUCCESS); | ||||||
|     rb.PushRaw<IdentificationBlockReply>(identification_block_reply); |     rb.PushRaw<RegisterInfo>({}); | ||||||
|     LOG_DEBUG(Service_NFC, "called"); | } | ||||||
|  | 
 | ||||||
|  | void Module::Interface::SetRegisterInfo(Kernel::HLERequestContext& ctx) { | ||||||
|  |     IPC::RequestParser rp(ctx, 0x404, 41, 0); | ||||||
|  |     const auto register_info = rp.PopRaw<RegisterInfoPrivate>(); | ||||||
|  | 
 | ||||||
|  |     LOG_INFO(Service_NFC, "called"); | ||||||
|  | 
 | ||||||
|  |     if (nfc->nfc_mode != CommunicationMode::Amiibo) { | ||||||
|  |         IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||||
|  |         rb.Push(ResultCommandInvalidForState); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     const auto result = nfc->device->SetRegisterInfoPrivate(register_info); | ||||||
|  | 
 | ||||||
|  |     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||||
|  |     rb.Push(result); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void Module::Interface::DeleteRegisterInfo(Kernel::HLERequestContext& ctx) { | ||||||
|  |     IPC::RequestParser rp(ctx, 0x405, 0, 0); | ||||||
|  | 
 | ||||||
|  |     LOG_INFO(Service_NFC, "called"); | ||||||
|  | 
 | ||||||
|  |     if (nfc->nfc_mode != CommunicationMode::Amiibo) { | ||||||
|  |         IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||||
|  |         rb.Push(ResultCommandInvalidForState); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     const auto result = nfc->device->DeleteRegisterInfo(); | ||||||
|  | 
 | ||||||
|  |     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||||
|  |     rb.Push(result); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void Module::Interface::DeleteApplicationArea(Kernel::HLERequestContext& ctx) { | ||||||
|  |     IPC::RequestParser rp(ctx, 0x406, 0, 0); | ||||||
|  | 
 | ||||||
|  |     LOG_INFO(Service_NFC, "called"); | ||||||
|  | 
 | ||||||
|  |     if (nfc->nfc_mode != CommunicationMode::Amiibo) { | ||||||
|  |         IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||||
|  |         rb.Push(ResultCommandInvalidForState); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     const auto result = nfc->device->DeleteApplicationArea(); | ||||||
|  | 
 | ||||||
|  |     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||||
|  |     rb.Push(result); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void Module::Interface::ExistsApplicationArea(Kernel::HLERequestContext& ctx) { | ||||||
|  |     IPC::RequestParser rp(ctx, 0x407, 0, 0); | ||||||
|  | 
 | ||||||
|  |     if (nfc->nfc_mode != CommunicationMode::Amiibo) { | ||||||
|  |         IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||||
|  |         rb.Push(ResultCommandInvalidForState); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     bool has_application_area = false; | ||||||
|  |     const auto result = nfc->device->ApplicationAreaExist(has_application_area); | ||||||
|  | 
 | ||||||
|  |     IPC::RequestBuilder rb = rp.MakeBuilder(0x2, 0); | ||||||
|  |     rb.Push(result); | ||||||
|  |     rb.Push(has_application_area); | ||||||
|  |     LOG_INFO(Service_NFC, "called"); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| std::shared_ptr<Module> Module::Interface::GetModule() const { | std::shared_ptr<Module> Module::Interface::GetModule() const { | ||||||
|     return nfc; |     return nfc; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Module::Interface::LoadAmiibo(const AmiiboData& amiibo_data) { | bool Module::Interface::IsSearchingForAmiibos() { | ||||||
|     std::lock_guard lock(HLE::g_hle_lock); |     std::lock_guard lock(HLE::g_hle_lock); | ||||||
|     nfc->amiibo_data = amiibo_data; | 
 | ||||||
|     nfc->amiibo_in_range = true; |     const auto state = nfc->device->GetCurrentState(); | ||||||
|     nfc->SyncTagState(); |     return state == DeviceState::SearchingForTag; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool Module::Interface::IsTagActive() { | ||||||
|  |     std::lock_guard lock(HLE::g_hle_lock); | ||||||
|  | 
 | ||||||
|  |     const auto state = nfc->device->GetCurrentState(); | ||||||
|  |     return state == DeviceState::TagFound || state == DeviceState::TagMounted || | ||||||
|  |            state == DeviceState::TagPartiallyMounted; | ||||||
|  | } | ||||||
|  | bool Module::Interface::LoadAmiibo(const std::string& fullpath) { | ||||||
|  |     std::lock_guard lock(HLE::g_hle_lock); | ||||||
|  |     return nfc->device->LoadAmiibo(fullpath); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Module::Interface::RemoveAmiibo() { | void Module::Interface::RemoveAmiibo() { | ||||||
|     std::lock_guard lock(HLE::g_hle_lock); |     std::lock_guard lock(HLE::g_hle_lock); | ||||||
|     nfc->amiibo_in_range = false; |     nfc->device->UnloadAmiibo(); | ||||||
|     nfc->SyncTagState(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void Module::SyncTagState() { |  | ||||||
|     if (amiibo_in_range && |  | ||||||
|         (nfc_tag_state == TagState::TagOutOfRange || nfc_tag_state == TagState::Scanning)) { |  | ||||||
|         // TODO (wwylele): Should TagOutOfRange->TagInRange transition only happen on the same tag
 |  | ||||||
|         // detected on Scanning->TagInRange?
 |  | ||||||
|         nfc_tag_state = TagState::TagInRange; |  | ||||||
|         tag_in_range_event->Signal(); |  | ||||||
|     } else if (!amiibo_in_range && |  | ||||||
|                (nfc_tag_state == TagState::TagInRange || nfc_tag_state == TagState::TagDataLoaded || |  | ||||||
|                 nfc_tag_state == TagState::Unknown6)) { |  | ||||||
|         // TODO (wwylele): If a tag is removed during TagDataLoaded/Unknown6, should this event
 |  | ||||||
|         // signals early?
 |  | ||||||
|         nfc_tag_state = TagState::TagOutOfRange; |  | ||||||
|         tag_out_of_range_event->Signal(); |  | ||||||
|     } |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| Module::Interface::Interface(std::shared_ptr<Module> nfc, const char* name, u32 max_session) | Module::Interface::Interface(std::shared_ptr<Module> nfc, const char* name, u32 max_session) | ||||||
|  | @ -354,10 +682,7 @@ Module::Interface::Interface(std::shared_ptr<Module> nfc, const char* name, u32 | ||||||
| Module::Interface::~Interface() = default; | Module::Interface::~Interface() = default; | ||||||
| 
 | 
 | ||||||
| Module::Module(Core::System& system) { | Module::Module(Core::System& system) { | ||||||
|     tag_in_range_event = |     device = std::make_shared<NfcDevice>(system); | ||||||
|         system.Kernel().CreateEvent(Kernel::ResetType::OneShot, "NFC::tag_in_range_event"); |  | ||||||
|     tag_out_of_range_event = |  | ||||||
|         system.Kernel().CreateEvent(Kernel::ResetType::OneShot, "NFC::tag_out_range_event"); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| Module::~Module() = default; | Module::~Module() = default; | ||||||
|  |  | ||||||
|  | @ -8,6 +8,8 @@ | ||||||
| #include <memory> | #include <memory> | ||||||
| #include <boost/serialization/binary_object.hpp> | #include <boost/serialization/binary_object.hpp> | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
|  | #include "core/hle/service/nfc/nfc_device.h" | ||||||
|  | #include "core/hle/service/nfc/nfc_types.h" | ||||||
| #include "core/hle/service/service.h" | #include "core/hle/service/service.h" | ||||||
| 
 | 
 | ||||||
| namespace Core { | namespace Core { | ||||||
|  | @ -20,45 +22,11 @@ class Event; | ||||||
| 
 | 
 | ||||||
| namespace Service::NFC { | namespace Service::NFC { | ||||||
| 
 | 
 | ||||||
| namespace ErrCodes { | enum class CommunicationMode : u8 { | ||||||
| enum { |  | ||||||
|     CommandInvalidForState = 512, |  | ||||||
| }; |  | ||||||
| } // namespace ErrCodes
 |  | ||||||
| 
 |  | ||||||
| // TODO(FearlessTobi): Add more members to this struct
 |  | ||||||
| struct AmiiboData { |  | ||||||
|     std::array<u8, 7> uuid; |  | ||||||
|     INSERT_PADDING_BYTES(0x4D); |  | ||||||
|     u16_le char_id; |  | ||||||
|     u8 char_variant; |  | ||||||
|     u8 figure_type; |  | ||||||
|     u16_be model_number; |  | ||||||
|     u8 series; |  | ||||||
|     INSERT_PADDING_BYTES(0x1C1); |  | ||||||
| 
 |  | ||||||
| private: |  | ||||||
|     template <class Archive> |  | ||||||
|     void serialize(Archive& ar, const unsigned int) { |  | ||||||
|         ar& boost::serialization::make_binary_object(this, sizeof(AmiiboData)); |  | ||||||
|     } |  | ||||||
|     friend class boost::serialization::access; |  | ||||||
| }; |  | ||||||
| static_assert(sizeof(AmiiboData) == 0x21C, "AmiiboData is an invalid size"); |  | ||||||
| 
 |  | ||||||
| enum class TagState : u8 { |  | ||||||
|     NotInitialized = 0, |     NotInitialized = 0, | ||||||
|     NotScanning = 1, |     Ntag = 1, | ||||||
|     Scanning = 2, |     Amiibo = 2, | ||||||
|     TagInRange = 3, |     TrainTag = 3, | ||||||
|     TagOutOfRange = 4, |  | ||||||
|     TagDataLoaded = 5, |  | ||||||
|     Unknown6 = 6, |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| enum class CommunicationStatus : u8 { |  | ||||||
|     AttemptInitialize = 1, |  | ||||||
|     NfcInitialized = 2, |  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| class Module final { | class Module final { | ||||||
|  | @ -73,7 +41,11 @@ public: | ||||||
| 
 | 
 | ||||||
|         std::shared_ptr<Module> GetModule() const; |         std::shared_ptr<Module> GetModule() const; | ||||||
| 
 | 
 | ||||||
|         void LoadAmiibo(const AmiiboData& amiibo_data); |         bool IsSearchingForAmiibos(); | ||||||
|  | 
 | ||||||
|  |         bool IsTagActive(); | ||||||
|  | 
 | ||||||
|  |         bool LoadAmiibo(const std::string& fullpath); | ||||||
| 
 | 
 | ||||||
|         void RemoveAmiibo(); |         void RemoveAmiibo(); | ||||||
| 
 | 
 | ||||||
|  | @ -82,7 +54,7 @@ public: | ||||||
|          * NFC::Initialize service function |          * NFC::Initialize service function | ||||||
|          *  Inputs: |          *  Inputs: | ||||||
|          *      0 : Header code [0x00010040] |          *      0 : Header code [0x00010040] | ||||||
|          *      1 : (u8) unknown parameter. Can be either value 0x1 or 0x2 |          *      1 : (u8) CommunicationMode. Can be either value 0x1, 0x2 or 0x3 | ||||||
|          *  Outputs: |          *  Outputs: | ||||||
|          *      1 : Result of function, 0 on success, otherwise error code |          *      1 : Result of function, 0 on success, otherwise error code | ||||||
|          */ |          */ | ||||||
|  | @ -92,7 +64,7 @@ public: | ||||||
|          * NFC::Shutdown service function |          * NFC::Shutdown service function | ||||||
|          *  Inputs: |          *  Inputs: | ||||||
|          *      0 : Header code [0x00020040] |          *      0 : Header code [0x00020040] | ||||||
|          *      1 : (u8) unknown parameter |          *      1 : (u8) CommunicationMode. | ||||||
|          *  Outputs: |          *  Outputs: | ||||||
|          *      1 : Result of function, 0 on success, otherwise error code |          *      1 : Result of function, 0 on success, otherwise error code | ||||||
|          */ |          */ | ||||||
|  | @ -153,6 +125,15 @@ public: | ||||||
|          */ |          */ | ||||||
|         void ResetTagScanState(Kernel::HLERequestContext& ctx); |         void ResetTagScanState(Kernel::HLERequestContext& ctx); | ||||||
| 
 | 
 | ||||||
|  |         /**
 | ||||||
|  |          * NFC::UpdateStoredAmiiboData service function | ||||||
|  |          *  Inputs: | ||||||
|  |          *      0 : Header code [0x00090002] | ||||||
|  |          *  Outputs: | ||||||
|  |          *      1 : Result of function, 0 on success, otherwise error code | ||||||
|  |          */ | ||||||
|  |         void UpdateStoredAmiiboData(Kernel::HLERequestContext& ctx); | ||||||
|  | 
 | ||||||
|         /**
 |         /**
 | ||||||
|          * NFC::GetTagInRangeEvent service function |          * NFC::GetTagInRangeEvent service function | ||||||
|          *  Inputs: |          *  Inputs: | ||||||
|  | @ -195,6 +176,16 @@ public: | ||||||
|          */ |          */ | ||||||
|         void CommunicationGetStatus(Kernel::HLERequestContext& ctx); |         void CommunicationGetStatus(Kernel::HLERequestContext& ctx); | ||||||
| 
 | 
 | ||||||
|  |         /**
 | ||||||
|  |          * NFC::GetTagInfo2 service function | ||||||
|  |          *  Inputs: | ||||||
|  |          *      0 : Header code [0x00100000] | ||||||
|  |          *  Outputs: | ||||||
|  |          *      1 : Result of function, 0 on success, otherwise error code | ||||||
|  |          *   2-26 : 0x60-byte struct | ||||||
|  |          */ | ||||||
|  |         void GetTagInfo2(Kernel::HLERequestContext& ctx); | ||||||
|  | 
 | ||||||
|         /**
 |         /**
 | ||||||
|          * NFC::GetTagInfo service function |          * NFC::GetTagInfo service function | ||||||
|          *  Inputs: |          *  Inputs: | ||||||
|  | @ -206,23 +197,102 @@ public: | ||||||
|         void GetTagInfo(Kernel::HLERequestContext& ctx); |         void GetTagInfo(Kernel::HLERequestContext& ctx); | ||||||
| 
 | 
 | ||||||
|         /**
 |         /**
 | ||||||
|          * NFC::GetAmiiboConfig service function |          * NFC::GetTagInfo service function | ||||||
|  |          *  Inputs: | ||||||
|  |          *      0 : Header code [0x00120000] | ||||||
|  |          *  Outputs: | ||||||
|  |          *      1 : Result of function, 0 on success, otherwise error code | ||||||
|  |          *      2 : Output NFC-adapter result-code | ||||||
|  |          */ | ||||||
|  |         void CommunicationGetResult(Kernel::HLERequestContext& ctx); | ||||||
|  | 
 | ||||||
|  |         /**
 | ||||||
|  |          * NFC::OpenAppData service function | ||||||
|  |          *  Inputs: | ||||||
|  |          *      0 : Header code [0x00130040] | ||||||
|  |          *      1 : (u32) App ID | ||||||
|  |          *  Outputs: | ||||||
|  |          *      1 : Result of function, 0 on success, otherwise error code | ||||||
|  |          */ | ||||||
|  |         void OpenAppData(Kernel::HLERequestContext& ctx); | ||||||
|  | 
 | ||||||
|  |         /**
 | ||||||
|  |          * NFC::InitializeWriteAppData service function | ||||||
|  |          *  Inputs: | ||||||
|  |          *      0 : Header code [0x00140384] | ||||||
|  |          *      1 : (u32) App ID | ||||||
|  |          *      2 : Size | ||||||
|  |          *   3-14 : 0x30-byte zeroed-out struct | ||||||
|  |          *     15 : 0x20, PID translate-header for kernel | ||||||
|  |          *     16 : PID written by kernel | ||||||
|  |          *     17 : (Size << 14) | 2 | ||||||
|  |          *     18 : Pointer to input buffer | ||||||
|  |          *  Outputs: | ||||||
|  |          *      1 : Result of function, 0 on success, otherwise error code | ||||||
|  |          */ | ||||||
|  |         void InitializeWriteAppData(Kernel::HLERequestContext& ctx); | ||||||
|  | 
 | ||||||
|  |         /**
 | ||||||
|  |          * NFC::ReadAppData service function | ||||||
|  |          *  Inputs: | ||||||
|  |          *      0 : Header code [0x00150040] | ||||||
|  |          *      1 : Size (unused? Hard-coded to be 0xD8) | ||||||
|  |          *  Outputs: | ||||||
|  |          *      1 : Result of function, 0 on success, otherwise error code | ||||||
|  |          */ | ||||||
|  |         void ReadAppData(Kernel::HLERequestContext& ctx); | ||||||
|  | 
 | ||||||
|  |         /**
 | ||||||
|  |          * NFC::WriteAppData service function | ||||||
|  |          *  Inputs: | ||||||
|  |          *      0 : Header code [0x00160242] | ||||||
|  |          *      1 : Size | ||||||
|  |          *    2-9 : AmiiboWriteRequest struct (see above) | ||||||
|  |          *     10 : (Size << 14) | 2 | ||||||
|  |          *     11 : Pointer to input appdata buffer | ||||||
|  |          *  Outputs: | ||||||
|  |          *      1 : Result of function, 0 on success, otherwise error code | ||||||
|  |          */ | ||||||
|  |         void WriteAppData(Kernel::HLERequestContext& ctx); | ||||||
|  | 
 | ||||||
|  |         /**
 | ||||||
|  |          * NFC::GetRegisterInfo service function | ||||||
|  |          *  Inputs: | ||||||
|  |          *      0 : Header code [0x00170000] | ||||||
|  |          *  Outputs: | ||||||
|  |          *      1 : Result of function, 0 on success, otherwise error code | ||||||
|  |          *   2-43 : AmiiboSettings struct (see above) | ||||||
|  |          */ | ||||||
|  |         void GetRegisterInfo(Kernel::HLERequestContext& ctx); | ||||||
|  | 
 | ||||||
|  |         /**
 | ||||||
|  |          * NFC::GetCommonInfo service function | ||||||
|          *  Inputs: |          *  Inputs: | ||||||
|          *      0 : Header code [0x00180000] |          *      0 : Header code [0x00180000] | ||||||
|          *  Outputs: |          *  Outputs: | ||||||
|          *      1 : Result of function, 0 on success, otherwise error code |          *      1 : Result of function, 0 on success, otherwise error code | ||||||
|          *   2-17 : 0x40-byte config struct |          *   2-17 : 0x40-byte config struct | ||||||
|          */ |          */ | ||||||
|         void GetAmiiboConfig(Kernel::HLERequestContext& ctx); |         void GetCommonInfo(Kernel::HLERequestContext& ctx); | ||||||
| 
 | 
 | ||||||
|         /**
 |         /**
 | ||||||
|          * NFC::Unknown0x1A service function |          * NFC::GetAppDataInitStruct service function | ||||||
|  |          *  Inputs: | ||||||
|  |          *      0 : Header code [0x00180000] | ||||||
|  |          *  Outputs: | ||||||
|  |          *      1 : Result of function, 0 on success, otherwise error code | ||||||
|  |          *   2-16 : 0x3C-byte config struct | ||||||
|  |          */ | ||||||
|  |         void GetAppDataInitStruct(Kernel::HLERequestContext& ctx); | ||||||
|  | 
 | ||||||
|  |         /**
 | ||||||
|  |          * NFC::LoadAmiiboPartially service function | ||||||
|          *  Inputs: |          *  Inputs: | ||||||
|          *      0 : Header code [0x001A0000] |          *      0 : Header code [0x001A0000] | ||||||
|          *  Outputs: |          *  Outputs: | ||||||
|          *      1 : Result of function, 0 on success, otherwise error code |          *      1 : Result of function, 0 on success, otherwise error code | ||||||
|          */ |          */ | ||||||
|         void Unknown0x1A(Kernel::HLERequestContext& ctx); |         void LoadAmiiboPartially(Kernel::HLERequestContext& ctx); | ||||||
| 
 | 
 | ||||||
|         /**
 |         /**
 | ||||||
|          * NFC::GetIdentificationBlock service function |          * NFC::GetIdentificationBlock service function | ||||||
|  | @ -234,21 +304,77 @@ public: | ||||||
|          */ |          */ | ||||||
|         void GetIdentificationBlock(Kernel::HLERequestContext& ctx); |         void GetIdentificationBlock(Kernel::HLERequestContext& ctx); | ||||||
| 
 | 
 | ||||||
|  |         /**
 | ||||||
|  |          * NFC::Format service function | ||||||
|  |          *  Inputs: | ||||||
|  |          *      0 : Header code [0x040100C2] | ||||||
|  |          *  Outputs: | ||||||
|  |          *      1 : Result of function, 0 on success, otherwise error code | ||||||
|  |          */ | ||||||
|  |         void Format(Kernel::HLERequestContext& ctx); | ||||||
|  | 
 | ||||||
|  |         /**
 | ||||||
|  |          * NFC::GetAdminInfo service function | ||||||
|  |          *  Inputs: | ||||||
|  |          *      0 : Header code [0x04020000] | ||||||
|  |          *  Outputs: | ||||||
|  |          *      1 : Result of function, 0 on success, otherwise error code | ||||||
|  |          */ | ||||||
|  |         void GetAdminInfo(Kernel::HLERequestContext& ctx); | ||||||
|  | 
 | ||||||
|  |         /**
 | ||||||
|  |          * NFC::GetEmptyRegisterInfo service function | ||||||
|  |          *  Inputs: | ||||||
|  |          *      0 : Header code [0x04030000] | ||||||
|  |          *  Outputs: | ||||||
|  |          *      1 : Result of function, 0 on success, otherwise error code | ||||||
|  |          */ | ||||||
|  |         void GetEmptyRegisterInfo(Kernel::HLERequestContext& ctx); | ||||||
|  | 
 | ||||||
|  |         /**
 | ||||||
|  |          * NFC::SetRegisterInfo service function | ||||||
|  |          *  Inputs: | ||||||
|  |          *      0 : Header code [0x04040A40] | ||||||
|  |          *  Outputs: | ||||||
|  |          *      1 : Result of function, 0 on success, otherwise error code | ||||||
|  |          */ | ||||||
|  |         void SetRegisterInfo(Kernel::HLERequestContext& ctx); | ||||||
|  | 
 | ||||||
|  |         /**
 | ||||||
|  |          * NFC::DeleteRegisterInfo service function | ||||||
|  |          *  Inputs: | ||||||
|  |          *      0 : Header code [0x04050000] | ||||||
|  |          *  Outputs: | ||||||
|  |          *      1 : Result of function, 0 on success, otherwise error code | ||||||
|  |          */ | ||||||
|  |         void DeleteRegisterInfo(Kernel::HLERequestContext& ctx); | ||||||
|  | 
 | ||||||
|  |         /**
 | ||||||
|  |          * NFC::DeleteApplicationArea service function | ||||||
|  |          *  Inputs: | ||||||
|  |          *      0 : Header code [0x04060000] | ||||||
|  |          *  Outputs: | ||||||
|  |          *      1 : Result of function, 0 on success, otherwise error code | ||||||
|  |          */ | ||||||
|  |         void DeleteApplicationArea(Kernel::HLERequestContext& ctx); | ||||||
|  | 
 | ||||||
|  |         /**
 | ||||||
|  |          * NFC::ExistsApplicationArea service function | ||||||
|  |          *  Inputs: | ||||||
|  |          *      0 : Header code [0x04070000] | ||||||
|  |          *  Outputs: | ||||||
|  |          *      1 : Result of function, 0 on success, otherwise error code | ||||||
|  |          */ | ||||||
|  |         void ExistsApplicationArea(Kernel::HLERequestContext& ctx); | ||||||
|  | 
 | ||||||
|     protected: |     protected: | ||||||
|         std::shared_ptr<Module> nfc; |         std::shared_ptr<Module> nfc; | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|     // Sync nfc_tag_state with amiibo_in_range and signal events on state change.
 |     CommunicationMode nfc_mode = CommunicationMode::NotInitialized; | ||||||
|     void SyncTagState(); |  | ||||||
| 
 | 
 | ||||||
|     std::shared_ptr<Kernel::Event> tag_in_range_event; |     std::shared_ptr<NfcDevice> device = nullptr; | ||||||
|     std::shared_ptr<Kernel::Event> tag_out_of_range_event; |  | ||||||
|     TagState nfc_tag_state = TagState::NotInitialized; |  | ||||||
|     CommunicationStatus nfc_status = CommunicationStatus::NfcInitialized; |  | ||||||
| 
 |  | ||||||
|     AmiiboData amiibo_data{}; |  | ||||||
|     bool amiibo_in_range = false; |  | ||||||
| 
 | 
 | ||||||
|     template <class Archive> |     template <class Archive> | ||||||
|     void serialize(Archive& ar, const unsigned int); |     void serialize(Archive& ar, const unsigned int); | ||||||
|  |  | ||||||
							
								
								
									
										1110
									
								
								src/core/hle/service/nfc/nfc_device.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1110
									
								
								src/core/hle/service/nfc/nfc_device.cpp
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										111
									
								
								src/core/hle/service/nfc/nfc_device.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								src/core/hle/service/nfc/nfc_device.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,111 @@ | ||||||
|  | // Copyright 2022 yuzu Emulator Project
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <span> | ||||||
|  | #include <vector> | ||||||
|  | #include <boost/serialization/binary_object.hpp> | ||||||
|  | 
 | ||||||
|  | #include "common/common_types.h" | ||||||
|  | #include "core/hle/service/nfc/nfc_results.h" | ||||||
|  | #include "core/hle/service/nfc/nfc_types.h" | ||||||
|  | #include "core/hle/service/service.h" | ||||||
|  | 
 | ||||||
|  | namespace Kernel { | ||||||
|  | class KEvent; | ||||||
|  | class KReadableEvent; | ||||||
|  | } // namespace Kernel
 | ||||||
|  | 
 | ||||||
|  | namespace Service::NFC { | ||||||
|  | class NfcDevice { | ||||||
|  | public: | ||||||
|  |     NfcDevice(Core::System& system); | ||||||
|  |     ~NfcDevice(); | ||||||
|  | 
 | ||||||
|  |     bool LoadAmiibo(std::string filename); | ||||||
|  |     void UnloadAmiibo(); | ||||||
|  |     void CloseAmiibo(); | ||||||
|  | 
 | ||||||
|  |     void Initialize(); | ||||||
|  |     void Finalize(); | ||||||
|  | 
 | ||||||
|  |     ResultCode StartCommunication(); | ||||||
|  |     ResultCode StopCommunication(); | ||||||
|  |     ResultCode StartDetection(TagProtocol allowed_protocol); | ||||||
|  |     ResultCode StopDetection(); | ||||||
|  |     ResultCode Mount(); | ||||||
|  |     ResultCode MountAmiibo(); | ||||||
|  |     ResultCode PartiallyMount(); | ||||||
|  |     ResultCode PartiallyMountAmiibo(); | ||||||
|  |     ResultCode ResetTagScanState(); | ||||||
|  |     ResultCode Flush(); | ||||||
|  | 
 | ||||||
|  |     ResultCode GetTagInfo2(TagInfo2& tag_info) const; | ||||||
|  |     ResultCode GetTagInfo(TagInfo& tag_info) const; | ||||||
|  |     ResultCode GetCommonInfo(CommonInfo& common_info) const; | ||||||
|  |     ResultCode GetModelInfo(ModelInfo& model_info) const; | ||||||
|  |     ResultCode GetRegisterInfo(RegisterInfo& register_info) const; | ||||||
|  |     ResultCode GetAdminInfo(AdminInfo& admin_info) const; | ||||||
|  | 
 | ||||||
|  |     ResultCode DeleteRegisterInfo(); | ||||||
|  |     ResultCode SetRegisterInfoPrivate(const RegisterInfoPrivate& register_info); | ||||||
|  |     ResultCode RestoreAmiibo(); | ||||||
|  |     ResultCode Format(); | ||||||
|  | 
 | ||||||
|  |     ResultCode OpenApplicationArea(u32 access_id); | ||||||
|  |     ResultCode GetApplicationAreaId(u32& application_area_id) const; | ||||||
|  |     ResultCode GetApplicationArea(std::vector<u8>& data) const; | ||||||
|  |     ResultCode SetApplicationArea(std::span<const u8> data); | ||||||
|  |     ResultCode CreateApplicationArea(u32 access_id, std::span<const u8> data); | ||||||
|  |     ResultCode RecreateApplicationArea(u32 access_id, std::span<const u8> data); | ||||||
|  |     ResultCode DeleteApplicationArea(); | ||||||
|  |     ResultCode ApplicationAreaExist(bool& has_application_area); | ||||||
|  | 
 | ||||||
|  |     constexpr u32 GetApplicationAreaSize() const; | ||||||
|  |     DeviceState GetCurrentState() const; | ||||||
|  |     ResultCode GetCommunicationStatus(CommunicationState& status) const; | ||||||
|  |     ResultCode CheckConnectionState() const; | ||||||
|  | 
 | ||||||
|  |     std::shared_ptr<Kernel::Event> GetActivateEvent() const; | ||||||
|  |     std::shared_ptr<Kernel::Event> GetDeactivateEvent() const; | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     time_t GetCurrentTime() const; | ||||||
|  |     void SetAmiiboName(AmiiboSettings& settings, const AmiiboName& amiibo_name); | ||||||
|  |     AmiiboDate GetAmiiboDate() const; | ||||||
|  |     u64 RemoveVersionByte(u64 application_id) const; | ||||||
|  |     void UpdateSettingsCrc(); | ||||||
|  |     void UpdateRegisterInfoCrc(); | ||||||
|  | 
 | ||||||
|  |     void BuildAmiiboWithoutKeys(); | ||||||
|  | 
 | ||||||
|  |     std::shared_ptr<Kernel::Event> tag_in_range_event = nullptr; | ||||||
|  |     std::shared_ptr<Kernel::Event> tag_out_of_range_event = nullptr; | ||||||
|  |     Core::TimingEventType* remove_amiibo_event = nullptr; | ||||||
|  | 
 | ||||||
|  |     bool is_initalized{}; | ||||||
|  |     bool is_data_moddified{}; | ||||||
|  |     bool is_app_area_open{}; | ||||||
|  |     bool is_plain_amiibo{}; | ||||||
|  |     bool is_write_protected{}; | ||||||
|  |     bool is_tag_in_range{}; | ||||||
|  |     TagProtocol allowed_protocols{}; | ||||||
|  |     DeviceState device_state{DeviceState::NotInitialized}; | ||||||
|  |     ConnectionState connection_state = ConnectionState::Success; | ||||||
|  |     CommunicationState communication_state = CommunicationState::Idle; | ||||||
|  | 
 | ||||||
|  |     std::string amiibo_filename = ""; | ||||||
|  | 
 | ||||||
|  |     SerializableAmiiboFile tag{}; | ||||||
|  |     SerializableEncryptedAmiiboFile encrypted_tag{}; | ||||||
|  | 
 | ||||||
|  |     template <class Archive> | ||||||
|  |     void serialize(Archive& ar, const unsigned int); | ||||||
|  |     friend class boost::serialization::access; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } // namespace Service::NFC
 | ||||||
|  | 
 | ||||||
|  | SERVICE_CONSTRUCT(Service::NFC::NfcDevice) | ||||||
|  | @ -21,25 +21,33 @@ NFC_M::NFC_M(std::shared_ptr<Module> nfc) : Module::Interface(std::move(nfc), "n | ||||||
|         {IPC::MakeHeader(0x0006, 0, 0), &NFC_M::StopTagScanning, "StopTagScanning"}, |         {IPC::MakeHeader(0x0006, 0, 0), &NFC_M::StopTagScanning, "StopTagScanning"}, | ||||||
|         {IPC::MakeHeader(0x0007, 0, 0), &NFC_M::LoadAmiiboData, "LoadAmiiboData"}, |         {IPC::MakeHeader(0x0007, 0, 0), &NFC_M::LoadAmiiboData, "LoadAmiiboData"}, | ||||||
|         {IPC::MakeHeader(0x0008, 0, 0), &NFC_M::ResetTagScanState, "ResetTagScanState"}, |         {IPC::MakeHeader(0x0008, 0, 0), &NFC_M::ResetTagScanState, "ResetTagScanState"}, | ||||||
|         {IPC::MakeHeader(0x0009, 0, 2), nullptr, "UpdateStoredAmiiboData"}, |         {IPC::MakeHeader(0x0009, 0, 2), &NFC_M::UpdateStoredAmiiboData, "UpdateStoredAmiiboData"}, | ||||||
|  |         {IPC::MakeHeader(0x000A, 0, 0), nullptr, "Unknown0x0A"}, | ||||||
|         {IPC::MakeHeader(0x000B, 0, 0), &NFC_M::GetTagInRangeEvent, "GetTagInRangeEvent"}, |         {IPC::MakeHeader(0x000B, 0, 0), &NFC_M::GetTagInRangeEvent, "GetTagInRangeEvent"}, | ||||||
|         {IPC::MakeHeader(0x000C, 0, 0), &NFC_M::GetTagOutOfRangeEvent, "GetTagOutOfRangeEvent"}, |         {IPC::MakeHeader(0x000C, 0, 0), &NFC_M::GetTagOutOfRangeEvent, "GetTagOutOfRangeEvent"}, | ||||||
|         {IPC::MakeHeader(0x000D, 0, 0), &NFC_M::GetTagState, "GetTagState"}, |         {IPC::MakeHeader(0x000D, 0, 0), &NFC_M::GetTagState, "GetTagState"}, | ||||||
|  |         {IPC::MakeHeader(0x000E, 0, 0), nullptr, "Unknown0x0E"}, | ||||||
|         {IPC::MakeHeader(0x000F, 0, 0), &NFC_M::CommunicationGetStatus, "CommunicationGetStatus"}, |         {IPC::MakeHeader(0x000F, 0, 0), &NFC_M::CommunicationGetStatus, "CommunicationGetStatus"}, | ||||||
|         {IPC::MakeHeader(0x0010, 0, 0), nullptr, "GetTagInfo2"}, |         {IPC::MakeHeader(0x0010, 0, 0), &NFC_M::GetTagInfo2, "GetTagInfo2"}, | ||||||
|         {IPC::MakeHeader(0x0011, 0, 0), &NFC_M::GetTagInfo, "GetTagInfo"}, |         {IPC::MakeHeader(0x0011, 0, 0), &NFC_M::GetTagInfo, "GetTagInfo"}, | ||||||
|         {IPC::MakeHeader(0x0012, 0, 0), nullptr, "CommunicationGetResult"}, |         {IPC::MakeHeader(0x0012, 0, 0), &NFC_M::CommunicationGetResult, "CommunicationGetResult"}, | ||||||
|         {IPC::MakeHeader(0x0013, 1, 0), nullptr, "OpenAppData"}, |         {IPC::MakeHeader(0x0013, 1, 0), &NFC_M::OpenAppData, "OpenAppData"}, | ||||||
|         {IPC::MakeHeader(0x0014, 14, 4), nullptr, "InitializeWriteAppData"}, |         {IPC::MakeHeader(0x0014, 14, 4), &NFC_M::InitializeWriteAppData, "InitializeWriteAppData"}, | ||||||
|         {IPC::MakeHeader(0x0015, 1, 0), nullptr, "ReadAppData"}, |         {IPC::MakeHeader(0x0015, 1, 0), &NFC_M::ReadAppData, "ReadAppData"}, | ||||||
|         {IPC::MakeHeader(0x0016, 9, 2), nullptr, "WriteAppData"}, |         {IPC::MakeHeader(0x0016, 9, 2), &NFC_M::WriteAppData, "WriteAppData"}, | ||||||
|         {IPC::MakeHeader(0x0017, 0, 0), nullptr, "GetAmiiboSettings"}, |         {IPC::MakeHeader(0x0017, 0, 0), &NFC_M::GetRegisterInfo, "GetRegisterInfo"}, | ||||||
|         {IPC::MakeHeader(0x0018, 0, 0), &NFC_M::GetAmiiboConfig, "GetAmiiboConfig"}, |         {IPC::MakeHeader(0x0018, 0, 0), &NFC_M::GetCommonInfo, "GetCommonInfo"}, | ||||||
|         {IPC::MakeHeader(0x0019, 0, 0), nullptr, "GetAppDataInitStruct"}, |         {IPC::MakeHeader(0x0019, 0, 0), &NFC_M::GetAppDataInitStruct, "GetAppDataInitStruct"}, | ||||||
|         {IPC::MakeHeader(0x001A, 0, 0), &NFC_M::Unknown0x1A, "Unknown0x1A"}, |         {IPC::MakeHeader(0x001A, 0, 0), &NFC_M::LoadAmiiboPartially, "LoadAmiiboPartially"}, | ||||||
|         {IPC::MakeHeader(0x001B, 0, 0), &NFC_M::GetIdentificationBlock, "GetIdentificationBlock"}, |         {IPC::MakeHeader(0x001B, 0, 0), &NFC_M::GetIdentificationBlock, "GetIdentificationBlock"}, | ||||||
|         // nfc:m
 |         // nfc:m
 | ||||||
|         {IPC::MakeHeader(0x0404, 41, 0), nullptr, "SetAmiiboSettings"} |         {IPC::MakeHeader(0x0401, 3, 2), &NFC_M::Format, "Format"}, | ||||||
|  |         {IPC::MakeHeader(0x0402, 0, 0), &NFC_M::GetAdminInfo, "GetAdminInfo"}, | ||||||
|  |         {IPC::MakeHeader(0x0403, 0, 0), &NFC_M::GetEmptyRegisterInfo, "GetEmptyRegisterInfo"}, | ||||||
|  |         {IPC::MakeHeader(0x0404, 41, 0), &NFC_M::SetRegisterInfo, "SetRegisterInfo"}, | ||||||
|  |         {IPC::MakeHeader(0x0405, 0, 0), &NFC_M::DeleteRegisterInfo, "DeleteRegisterInfo"}, | ||||||
|  |         {IPC::MakeHeader(0x0406, 0, 0), &NFC_M::DeleteApplicationArea, "DeleteApplicationArea"}, | ||||||
|  |         {IPC::MakeHeader(0x0407, 0, 0), &NFC_M::ExistsApplicationArea, "ExistsApplicationArea"} | ||||||
|         // clang-format on
 |         // clang-format on
 | ||||||
|     }; |     }; | ||||||
|     RegisterHandlers(functions); |     RegisterHandlers(functions); | ||||||
|  |  | ||||||
							
								
								
									
										62
									
								
								src/core/hle/service/nfc/nfc_results.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								src/core/hle/service/nfc/nfc_results.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,62 @@ | ||||||
|  | // Copyright 2023 Citra Emulator Project
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include "core/hle/result.h" | ||||||
|  | 
 | ||||||
|  | namespace Service::NFC { | ||||||
|  | 
 | ||||||
|  | namespace ErrCodes { | ||||||
|  | enum { | ||||||
|  |     InvalidArgumentValue = 80, | ||||||
|  |     InvalidArgument = 81, | ||||||
|  | 
 | ||||||
|  |     InvalidChecksum = 200, | ||||||
|  |     WriteFailed = 328, | ||||||
|  | 
 | ||||||
|  |     CommandInvalidForState = 512, | ||||||
|  |     NotAnAmiibo = 522, | ||||||
|  |     CorruptedData = 536, | ||||||
|  |     AppDataUninitialized = 544, | ||||||
|  |     RegistrationUnitialized = 552, | ||||||
|  |     ApplicationAreaExist = 560, | ||||||
|  |     AppIdMismatch = 568, | ||||||
|  | 
 | ||||||
|  |     CommunicationLost = 608, | ||||||
|  |     NoAdapterDetected = 616, | ||||||
|  | }; | ||||||
|  | } // namespace ErrCodes
 | ||||||
|  | 
 | ||||||
|  | constexpr ResultCode ResultInvalidArgumentValue(ErrCodes::InvalidArgumentValue, ErrorModule::NFC, | ||||||
|  |                                                 ErrorSummary::InvalidArgument, ErrorLevel::Status); | ||||||
|  | constexpr ResultCode ResultInvalidArgument(ErrCodes::InvalidArgument, ErrorModule::NFC, | ||||||
|  |                                            ErrorSummary::InvalidArgument, ErrorLevel::Status); | ||||||
|  | constexpr ResultCode ResultCommandInvalidForState(ErrCodes::CommandInvalidForState, | ||||||
|  |                                                   ErrorModule::NFC, ErrorSummary::InvalidState, | ||||||
|  |                                                   ErrorLevel::Status); | ||||||
|  | constexpr ResultCode ResultNotAnAmiibo(ErrCodes::NotAnAmiibo, ErrorModule::NFC, | ||||||
|  |                                        ErrorSummary::InvalidState, ErrorLevel::Status); | ||||||
|  | constexpr ResultCode ResultCorruptedData(ErrCodes::CorruptedData, ErrorModule::NFC, | ||||||
|  |                                          ErrorSummary::InvalidState, ErrorLevel::Status); | ||||||
|  | constexpr ResultCode ResultWriteAmiiboFailed(ErrCodes::WriteFailed, ErrorModule::NFC, | ||||||
|  |                                              ErrorSummary::InvalidState, ErrorLevel::Status); | ||||||
|  | constexpr ResultCode ResultApplicationAreaIsNotInitialized(ErrCodes::AppDataUninitialized, | ||||||
|  |                                                            ErrorModule::NFC, | ||||||
|  |                                                            ErrorSummary::InvalidState, | ||||||
|  |                                                            ErrorLevel::Status); | ||||||
|  | constexpr ResultCode ResultRegistrationIsNotInitialized(ErrCodes::RegistrationUnitialized, | ||||||
|  |                                                         ErrorModule::NFC, | ||||||
|  |                                                         ErrorSummary::InvalidState, | ||||||
|  |                                                         ErrorLevel::Status); | ||||||
|  | constexpr ResultCode ResultApplicationAreaExist(ErrCodes::ApplicationAreaExist, ErrorModule::NFC, | ||||||
|  |                                                 ErrorSummary::InvalidState, ErrorLevel::Status); | ||||||
|  | constexpr ResultCode ResultWrongApplicationAreaId(ErrCodes::AppIdMismatch, ErrorModule::NFC, | ||||||
|  |                                                   ErrorSummary::InvalidState, ErrorLevel::Status); | ||||||
|  | constexpr ResultCode ResultCommunicationLost(ErrCodes::CommunicationLost, ErrorModule::NFC, | ||||||
|  |                                              ErrorSummary::InvalidState, ErrorLevel::Status); | ||||||
|  | constexpr ResultCode ResultNoAdapterDetected(ErrCodes::NoAdapterDetected, ErrorModule::NFC, | ||||||
|  |                                              ErrorSummary::InvalidState, ErrorLevel::Status); | ||||||
|  | 
 | ||||||
|  | } // namespace Service::NFC
 | ||||||
							
								
								
									
										460
									
								
								src/core/hle/service/nfc/nfc_types.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										460
									
								
								src/core/hle/service/nfc/nfc_types.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,460 @@ | ||||||
|  | // Copyright 2022 yuzu Emulator Project
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <array> | ||||||
|  | 
 | ||||||
|  | #include "common/bit_field.h" | ||||||
|  | #include "common/common_types.h" | ||||||
|  | #include "core/hle/applets/mii_selector.h" | ||||||
|  | 
 | ||||||
|  | namespace Service::NFC { | ||||||
|  | static constexpr std::size_t amiibo_name_length = 0xA; | ||||||
|  | static constexpr std::size_t application_id_version_offset = 0x1c; | ||||||
|  | static constexpr std::size_t counter_limit = 0xffff; | ||||||
|  | 
 | ||||||
|  | enum class ServiceType : u32 { | ||||||
|  |     User, | ||||||
|  |     Debug, | ||||||
|  |     System, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | enum class CommunicationState : u8 { | ||||||
|  |     Idle = 0, | ||||||
|  |     SearchingForAdapter = 1, | ||||||
|  |     Initialized = 2, | ||||||
|  |     Active = 3, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | enum class ConnectionState : u8 { | ||||||
|  |     Success = 0, | ||||||
|  |     NoAdapter = 1, | ||||||
|  |     Lost = 2, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | enum class DeviceState : u32 { | ||||||
|  |     NotInitialized = 0, | ||||||
|  |     Initialized = 1, | ||||||
|  |     SearchingForTag = 2, | ||||||
|  |     TagFound = 3, | ||||||
|  |     TagRemoved = 4, | ||||||
|  |     TagMounted = 5, | ||||||
|  |     TagPartiallyMounted = 6, // Validate this one seems to have other name
 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | enum class ModelType : u32 { | ||||||
|  |     Amiibo, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | enum class MountTarget : u32 { | ||||||
|  |     None, | ||||||
|  |     Rom, | ||||||
|  |     Ram, | ||||||
|  |     All, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | enum class AmiiboType : u8 { | ||||||
|  |     Figure, | ||||||
|  |     Card, | ||||||
|  |     Yarn, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | enum class AmiiboSeries : u8 { | ||||||
|  |     SuperSmashBros, | ||||||
|  |     SuperMario, | ||||||
|  |     ChibiRobo, | ||||||
|  |     YoshiWoollyWorld, | ||||||
|  |     Splatoon, | ||||||
|  |     AnimalCrossing, | ||||||
|  |     EightBitMario, | ||||||
|  |     Skylanders, | ||||||
|  |     Unknown8, | ||||||
|  |     TheLegendOfZelda, | ||||||
|  |     ShovelKnight, | ||||||
|  |     Unknown11, | ||||||
|  |     Kiby, | ||||||
|  |     Pokemon, | ||||||
|  |     MarioSportsSuperstars, | ||||||
|  |     MonsterHunter, | ||||||
|  |     BoxBoy, | ||||||
|  |     Pikmin, | ||||||
|  |     FireEmblem, | ||||||
|  |     Metroid, | ||||||
|  |     Others, | ||||||
|  |     MegaMan, | ||||||
|  |     Diablo, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | enum class TagType : u32 { | ||||||
|  |     None, | ||||||
|  |     Type1, // ISO14443A RW 96-2k bytes 106kbit/s
 | ||||||
|  |     Type2, // ISO14443A RW/RO 540 bytes 106kbit/s
 | ||||||
|  |     Type3, // Sony Felica RW/RO 2k bytes 212kbit/s
 | ||||||
|  |     Type4, // ISO14443A RW/RO 4k-32k bytes 424kbit/s
 | ||||||
|  |     Type5, // ISO15693 RW/RO 540 bytes 106kbit/s
 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | enum class PackedTagType : u8 { | ||||||
|  |     None, | ||||||
|  |     Type1, // ISO14443A RW 96-2k bytes 106kbit/s
 | ||||||
|  |     Type2, // ISO14443A RW/RO 540 bytes 106kbit/s
 | ||||||
|  |     Type3, // Sony Felica RW/RO 2k bytes 212kbit/s
 | ||||||
|  |     Type4, // ISO14443A RW/RO 4k-32k bytes 424kbit/s
 | ||||||
|  |     Type5, // ISO15693 RW/RO 540 bytes 106kbit/s
 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // Verify this enum. It might be completely wrong default protocol is 0x0
 | ||||||
|  | enum class TagProtocol : u32 { | ||||||
|  |     None, | ||||||
|  |     TypeA = 1U << 0, // ISO14443A
 | ||||||
|  |     TypeB = 1U << 1, // ISO14443B
 | ||||||
|  |     TypeF = 1U << 2, // Sony Felica
 | ||||||
|  |     Unknown1 = 1U << 3, | ||||||
|  |     Unknown2 = 1U << 5, | ||||||
|  |     All = 0xFFFFFFFFU, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // Verify this enum. It might be completely wrong default protocol is 0x0
 | ||||||
|  | enum class PackedTagProtocol : u8 { | ||||||
|  |     None, | ||||||
|  |     TypeA = 1U << 0, // ISO14443A
 | ||||||
|  |     TypeB = 1U << 1, // ISO14443B
 | ||||||
|  |     TypeF = 1U << 2, // Sony Felica
 | ||||||
|  |     Unknown1 = 1U << 3, | ||||||
|  |     Unknown2 = 1U << 5, | ||||||
|  |     All = 0xFF, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | enum class AppAreaVersion : u8 { | ||||||
|  |     Nintendo3DS = 0, | ||||||
|  |     NintendoWiiU = 1, | ||||||
|  |     Nintendo3DSv2 = 2, | ||||||
|  |     NintendoSwitch = 3, | ||||||
|  |     NotSet = 0xFF, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | using UniqueSerialNumber = std::array<u8, 7>; | ||||||
|  | using LockBytes = std::array<u8, 2>; | ||||||
|  | using HashData = std::array<u8, 0x20>; | ||||||
|  | using ApplicationArea = std::array<u8, 0xD8>; | ||||||
|  | using AmiiboName = std::array<u16_be, amiibo_name_length>; | ||||||
|  | using DataBlock = std::array<u8, 0x10>; | ||||||
|  | using KeyData = std::array<u8, 0x6>; | ||||||
|  | 
 | ||||||
|  | struct TagUuid { | ||||||
|  |     UniqueSerialNumber uid; | ||||||
|  |     u8 nintendo_id; | ||||||
|  |     LockBytes lock_bytes; | ||||||
|  | 
 | ||||||
|  |     template <class Archive> | ||||||
|  |     void serialize(Archive& ar, const unsigned int) { | ||||||
|  |         ar& uid; | ||||||
|  |         ar& nintendo_id; | ||||||
|  |         ar& lock_bytes; | ||||||
|  |     } | ||||||
|  |     friend class boost::serialization::access; | ||||||
|  | }; | ||||||
|  | static_assert(sizeof(TagUuid) == 10, "TagUuid is an invalid size"); | ||||||
|  | 
 | ||||||
|  | struct WriteDate { | ||||||
|  |     u16 year; | ||||||
|  |     u8 month; | ||||||
|  |     u8 day; | ||||||
|  | }; | ||||||
|  | static_assert(sizeof(WriteDate) == 0x4, "WriteDate is an invalid size"); | ||||||
|  | 
 | ||||||
|  | struct AmiiboDate { | ||||||
|  |     u16 raw_date{}; | ||||||
|  | 
 | ||||||
|  |     u16 GetValue() const { | ||||||
|  |         return Common::swap16(raw_date); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     u16 GetYear() const { | ||||||
|  |         return static_cast<u16>(((GetValue() & 0xFE00) >> 9) + 2000); | ||||||
|  |     } | ||||||
|  |     u8 GetMonth() const { | ||||||
|  |         return static_cast<u8>((GetValue() & 0x01E0) >> 5); | ||||||
|  |     } | ||||||
|  |     u8 GetDay() const { | ||||||
|  |         return static_cast<u8>(GetValue() & 0x001F); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     WriteDate GetWriteDate() const { | ||||||
|  |         if (!IsValidDate()) { | ||||||
|  |             return { | ||||||
|  |                 .year = 2000, | ||||||
|  |                 .month = 1, | ||||||
|  |                 .day = 1, | ||||||
|  |             }; | ||||||
|  |         } | ||||||
|  |         return { | ||||||
|  |             .year = GetYear(), | ||||||
|  |             .month = GetMonth(), | ||||||
|  |             .day = GetDay(), | ||||||
|  |         }; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void SetYear(u16 year) { | ||||||
|  |         const u16 year_converted = static_cast<u16>((year - 2000) << 9); | ||||||
|  |         raw_date = Common::swap16((GetValue() & ~0xFE00) | year_converted); | ||||||
|  |     } | ||||||
|  |     void SetMonth(u8 month) { | ||||||
|  |         const u16 month_converted = static_cast<u16>(month << 5); | ||||||
|  |         raw_date = Common::swap16((GetValue() & ~0x01E0) | month_converted); | ||||||
|  |     } | ||||||
|  |     void SetDay(u8 day) { | ||||||
|  |         const u16 day_converted = static_cast<u16>(day); | ||||||
|  |         raw_date = Common::swap16((GetValue() & ~0x001F) | day_converted); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     bool IsValidDate() const { | ||||||
|  |         const bool is_day_valid = GetDay() > 0 && GetDay() < 32; | ||||||
|  |         const bool is_month_valid = GetMonth() > 0 && GetMonth() < 13; | ||||||
|  |         const bool is_year_valid = GetYear() >= 2000; | ||||||
|  |         return is_year_valid && is_month_valid && is_day_valid; | ||||||
|  |     } | ||||||
|  | }; | ||||||
|  | static_assert(sizeof(AmiiboDate) == 2, "AmiiboDate is an invalid size"); | ||||||
|  | 
 | ||||||
|  | struct Settings { | ||||||
|  |     union { | ||||||
|  |         u8 raw{}; | ||||||
|  | 
 | ||||||
|  |         BitField<0, 4, u8> font_region; | ||||||
|  |         BitField<4, 1, u8> amiibo_initialized; | ||||||
|  |         BitField<5, 1, u8> appdata_initialized; | ||||||
|  |     }; | ||||||
|  | }; | ||||||
|  | static_assert(sizeof(Settings) == 1, "AmiiboDate is an invalid size"); | ||||||
|  | 
 | ||||||
|  | struct AmiiboSettings { | ||||||
|  |     Settings settings; | ||||||
|  |     u8 country_code_id; | ||||||
|  |     u16_be crc_counter; // Incremented each time crc is changed
 | ||||||
|  |     AmiiboDate init_date; | ||||||
|  |     AmiiboDate write_date; | ||||||
|  |     u32_be crc; | ||||||
|  |     AmiiboName amiibo_name; // UTF-16 text
 | ||||||
|  | }; | ||||||
|  | static_assert(sizeof(AmiiboSettings) == 0x20, "AmiiboSettings is an invalid size"); | ||||||
|  | 
 | ||||||
|  | struct AmiiboModelInfo { | ||||||
|  |     u16 character_id; | ||||||
|  |     u8 character_variant; | ||||||
|  |     AmiiboType amiibo_type; | ||||||
|  |     u16_be model_number; | ||||||
|  |     AmiiboSeries series; | ||||||
|  |     PackedTagType tag_type; | ||||||
|  |     INSERT_PADDING_BYTES(0x4); // Unknown
 | ||||||
|  | }; | ||||||
|  | static_assert(sizeof(AmiiboModelInfo) == 0xC, "AmiiboModelInfo is an invalid size"); | ||||||
|  | 
 | ||||||
|  | struct NTAG215Password { | ||||||
|  |     u32 PWD;  // Password to allow write access
 | ||||||
|  |     u16 PACK; // Password acknowledge reply
 | ||||||
|  |     u16 RFUI; // Reserved for future use
 | ||||||
|  | 
 | ||||||
|  |     template <class Archive> | ||||||
|  |     void serialize(Archive& ar, const unsigned int) { | ||||||
|  |         ar& PWD; | ||||||
|  |         ar& PACK; | ||||||
|  |         ar& RFUI; | ||||||
|  |     } | ||||||
|  |     friend class boost::serialization::access; | ||||||
|  | }; | ||||||
|  | static_assert(sizeof(NTAG215Password) == 0x8, "NTAG215Password is an invalid size"); | ||||||
|  | 
 | ||||||
|  | #pragma pack(1) | ||||||
|  | struct EncryptedAmiiboFile { | ||||||
|  |     u8 constant_value;                // Must be A5
 | ||||||
|  |     u16_be write_counter;             // Number of times the amiibo has been written?
 | ||||||
|  |     u8 amiibo_version;                // Amiibo file version
 | ||||||
|  |     AmiiboSettings settings;          // Encrypted amiibo settings
 | ||||||
|  |     HashData hmac_tag;                // Hash
 | ||||||
|  |     AmiiboModelInfo model_info;       // Encrypted amiibo model info
 | ||||||
|  |     HashData keygen_salt;             // Salt
 | ||||||
|  |     HashData hmac_data;               // Hash
 | ||||||
|  |     HLE::Applets::MiiData owner_mii;  // Encrypted Mii data
 | ||||||
|  |     u16 padding;                      // Mii Padding
 | ||||||
|  |     u16_be owner_mii_aes_ccm;         // Mii data AES-CCM MAC
 | ||||||
|  |     u64_be application_id;            // Encrypted Game id
 | ||||||
|  |     u16_be application_write_counter; // Encrypted Counter
 | ||||||
|  |     u32_be application_area_id;       // Encrypted Game id
 | ||||||
|  |     u8 application_id_byte; | ||||||
|  |     u8 unknown; | ||||||
|  |     u64 mii_extension; | ||||||
|  |     std::array<u32, 0x5> unknown2; | ||||||
|  |     u32_be register_info_crc; | ||||||
|  |     ApplicationArea application_area; // Encrypted Game data
 | ||||||
|  | }; | ||||||
|  | static_assert(sizeof(EncryptedAmiiboFile) == 0x1F8, "AmiiboFile is an invalid size"); | ||||||
|  | 
 | ||||||
|  | struct NTAG215File { | ||||||
|  |     LockBytes lock_bytes;      // Tag UUID
 | ||||||
|  |     u16 static_lock;           // Set defined pages as read only
 | ||||||
|  |     u32 compability_container; // Defines available memory
 | ||||||
|  |     HashData hmac_data;        // Hash
 | ||||||
|  |     u8 constant_value;         // Must be A5
 | ||||||
|  |     u16_be write_counter;      // Number of times the amiibo has been written?
 | ||||||
|  |     u8 amiibo_version;         // Amiibo file version
 | ||||||
|  |     AmiiboSettings settings; | ||||||
|  |     HLE::Applets::MiiData owner_mii;  // Mii data
 | ||||||
|  |     u16 padding;                      // Mii Padding
 | ||||||
|  |     u16_be owner_mii_aes_ccm;         // Mii data AES-CCM MAC
 | ||||||
|  |     u64_be application_id;            // Game id
 | ||||||
|  |     u16_be application_write_counter; // Counter
 | ||||||
|  |     u32_be application_area_id; | ||||||
|  |     u8 application_id_byte; | ||||||
|  |     u8 unknown; | ||||||
|  |     u64 mii_extension; | ||||||
|  |     std::array<u32, 0x5> unknown2; | ||||||
|  |     u32_be register_info_crc; | ||||||
|  |     ApplicationArea application_area; // Game data
 | ||||||
|  |     HashData hmac_tag;                // Hash
 | ||||||
|  |     UniqueSerialNumber uid;           // Unique serial number
 | ||||||
|  |     u8 nintendo_id;                   // Tag UUID
 | ||||||
|  |     AmiiboModelInfo model_info; | ||||||
|  |     HashData keygen_salt;     // Salt
 | ||||||
|  |     u32 dynamic_lock;         // Dynamic lock
 | ||||||
|  |     u32 CFG0;                 // Defines memory protected by password
 | ||||||
|  |     u32 CFG1;                 // Defines number of verification attempts
 | ||||||
|  |     NTAG215Password password; // Password data
 | ||||||
|  | }; | ||||||
|  | static_assert(sizeof(NTAG215File) == 0x21C, "NTAG215File is an invalid size"); | ||||||
|  | static_assert(std::is_trivially_copyable_v<NTAG215File>, "NTAG215File must be trivially copyable."); | ||||||
|  | #pragma pack() | ||||||
|  | 
 | ||||||
|  | struct EncryptedNTAG215File { | ||||||
|  |     TagUuid uuid;                    // Unique serial number
 | ||||||
|  |     u16 static_lock;                 // Set defined pages as read only
 | ||||||
|  |     u32 compability_container;       // Defines available memory
 | ||||||
|  |     EncryptedAmiiboFile user_memory; // Writable data
 | ||||||
|  |     u32 dynamic_lock;                // Dynamic lock
 | ||||||
|  |     u32 CFG0;                        // Defines memory protected by password
 | ||||||
|  |     u32 CFG1;                        // Defines number of verification attempts
 | ||||||
|  |     NTAG215Password password;        // Password data
 | ||||||
|  | }; | ||||||
|  | static_assert(sizeof(EncryptedNTAG215File) == 0x21C, "EncryptedNTAG215File is an invalid size"); | ||||||
|  | static_assert(std::is_trivially_copyable_v<EncryptedNTAG215File>, | ||||||
|  |               "EncryptedNTAG215File must be trivially copyable."); | ||||||
|  | 
 | ||||||
|  | struct SerializableAmiiboFile { | ||||||
|  |     union { | ||||||
|  |         std::array<u8, 0x21C> raw; | ||||||
|  |         NTAG215File file; | ||||||
|  |     }; | ||||||
|  |     template <class Archive> | ||||||
|  |     void serialize(Archive& ar, const unsigned int) { | ||||||
|  |         ar& raw; | ||||||
|  |     } | ||||||
|  |     friend class boost::serialization::access; | ||||||
|  | }; | ||||||
|  | static_assert(sizeof(SerializableAmiiboFile) == 0x21C, "SerializableAmiiboFile is an invalid size"); | ||||||
|  | static_assert(std::is_trivially_copyable_v<SerializableAmiiboFile>, | ||||||
|  |               "SerializableAmiiboFile must be trivially copyable."); | ||||||
|  | 
 | ||||||
|  | struct SerializableEncryptedAmiiboFile { | ||||||
|  |     union { | ||||||
|  |         std::array<u8, 0x21C> raw; | ||||||
|  |         EncryptedNTAG215File file; | ||||||
|  |     }; | ||||||
|  |     template <class Archive> | ||||||
|  |     void serialize(Archive& ar, const unsigned int) { | ||||||
|  |         ar& raw; | ||||||
|  |     } | ||||||
|  |     friend class boost::serialization::access; | ||||||
|  | }; | ||||||
|  | static_assert(sizeof(SerializableEncryptedAmiiboFile) == 0x21C, | ||||||
|  |               "SerializableEncryptedAmiiboFile is an invalid size"); | ||||||
|  | static_assert(std::is_trivially_copyable_v<SerializableEncryptedAmiiboFile>, | ||||||
|  |               "SerializableEncryptedAmiiboFile must be trivially copyable."); | ||||||
|  | 
 | ||||||
|  | struct TagInfo { | ||||||
|  |     u16 uuid_length; | ||||||
|  |     PackedTagProtocol protocol; | ||||||
|  |     PackedTagType tag_type; | ||||||
|  |     UniqueSerialNumber uuid; | ||||||
|  |     std::array<u8, 0x21> extra_data; | ||||||
|  | }; | ||||||
|  | static_assert(sizeof(TagInfo) == 0x2C, "TagInfo is an invalid size"); | ||||||
|  | 
 | ||||||
|  | struct TagInfo2 { | ||||||
|  |     u16 uuid_length; | ||||||
|  |     INSERT_PADDING_BYTES(0x1); | ||||||
|  |     PackedTagType tag_type; | ||||||
|  |     UniqueSerialNumber uuid; | ||||||
|  |     std::array<u8, 0x21> extra_data; | ||||||
|  |     TagProtocol protocol; | ||||||
|  |     std::array<u8, 0x30> extra_data2; | ||||||
|  | }; | ||||||
|  | static_assert(sizeof(TagInfo2) == 0x60, "TagInfo2 is an invalid size"); | ||||||
|  | 
 | ||||||
|  | struct CommonInfo { | ||||||
|  |     WriteDate last_write_date; | ||||||
|  |     u16 application_write_counter; | ||||||
|  |     u16 character_id; | ||||||
|  |     u8 character_variant; | ||||||
|  |     AmiiboSeries series; | ||||||
|  |     u16 model_number; | ||||||
|  |     AmiiboType amiibo_type; | ||||||
|  |     u8 version; | ||||||
|  |     u16 application_area_size; | ||||||
|  |     INSERT_PADDING_BYTES(0x30); | ||||||
|  | }; | ||||||
|  | static_assert(sizeof(CommonInfo) == 0x40, "CommonInfo is an invalid size"); | ||||||
|  | 
 | ||||||
|  | struct ModelInfo { | ||||||
|  |     u16 character_id; | ||||||
|  |     u8 character_variant; | ||||||
|  |     AmiiboSeries series; | ||||||
|  |     u16 model_number; | ||||||
|  |     AmiiboType amiibo_type; | ||||||
|  |     INSERT_PADDING_BYTES(0x2F); | ||||||
|  | }; | ||||||
|  | static_assert(sizeof(ModelInfo) == 0x36, "ModelInfo is an invalid size"); | ||||||
|  | 
 | ||||||
|  | struct RegisterInfo { | ||||||
|  |     HLE::Applets::MiiData mii_data; | ||||||
|  |     INSERT_PADDING_BYTES(0x2); | ||||||
|  |     u16_be owner_mii_aes_ccm; // Mii data AES-CCM MAC
 | ||||||
|  |     AmiiboName amiibo_name; | ||||||
|  |     INSERT_PADDING_BYTES(0x2); // Zero string terminator
 | ||||||
|  |     u8 flags; | ||||||
|  |     u8 font_region; | ||||||
|  |     WriteDate creation_date; | ||||||
|  |     INSERT_PADDING_BYTES(0x2C); | ||||||
|  | }; | ||||||
|  | static_assert(sizeof(RegisterInfo) == 0xA8, "RegisterInfo is an invalid size"); | ||||||
|  | 
 | ||||||
|  | struct RegisterInfoPrivate { | ||||||
|  |     HLE::Applets::MiiData mii_data; | ||||||
|  |     INSERT_PADDING_BYTES(0x2); | ||||||
|  |     u16_be owner_mii_aes_ccm; // Mii data AES-CCM MAC
 | ||||||
|  |     AmiiboName amiibo_name; | ||||||
|  |     INSERT_PADDING_BYTES(0x2); // Zero string terminator
 | ||||||
|  |     u8 flags; | ||||||
|  |     u8 font_region; | ||||||
|  |     WriteDate creation_date; | ||||||
|  |     INSERT_PADDING_BYTES(0x28); | ||||||
|  | }; | ||||||
|  | static_assert(sizeof(RegisterInfoPrivate) == 0xA4, "RegisterInfoPrivate is an invalid size"); | ||||||
|  | static_assert(std::is_trivial_v<RegisterInfoPrivate>, "RegisterInfoPrivate must be trivial."); | ||||||
|  | static_assert(std::is_trivially_copyable_v<RegisterInfoPrivate>, | ||||||
|  |               "RegisterInfoPrivate must be trivially copyable."); | ||||||
|  | 
 | ||||||
|  | struct AdminInfo { | ||||||
|  |     u64_be application_id; | ||||||
|  |     u32_be application_area_id; | ||||||
|  |     u16 crc_counter; | ||||||
|  |     u8 flags; | ||||||
|  |     PackedTagType tag_type; | ||||||
|  |     AppAreaVersion app_area_version; | ||||||
|  |     INSERT_PADDING_BYTES(0x7); | ||||||
|  |     INSERT_PADDING_BYTES(0x28); | ||||||
|  | }; | ||||||
|  | static_assert(sizeof(AdminInfo) == 0x40, "AdminInfo is an invalid size"); | ||||||
|  | 
 | ||||||
|  | } // namespace Service::NFC
 | ||||||
|  | @ -20,23 +20,32 @@ NFC_U::NFC_U(std::shared_ptr<Module> nfc) : Module::Interface(std::move(nfc), "n | ||||||
|         {IPC::MakeHeader(0x0006, 0, 0), &NFC_U::StopTagScanning, "StopTagScanning"}, |         {IPC::MakeHeader(0x0006, 0, 0), &NFC_U::StopTagScanning, "StopTagScanning"}, | ||||||
|         {IPC::MakeHeader(0x0007, 0, 0), &NFC_U::LoadAmiiboData, "LoadAmiiboData"}, |         {IPC::MakeHeader(0x0007, 0, 0), &NFC_U::LoadAmiiboData, "LoadAmiiboData"}, | ||||||
|         {IPC::MakeHeader(0x0008, 0, 0), &NFC_U::ResetTagScanState, "ResetTagScanState"}, |         {IPC::MakeHeader(0x0008, 0, 0), &NFC_U::ResetTagScanState, "ResetTagScanState"}, | ||||||
|         {IPC::MakeHeader(0x0009, 0, 2), nullptr, "UpdateStoredAmiiboData"}, |         {IPC::MakeHeader(0x0009, 0, 2), &NFC_U::UpdateStoredAmiiboData, "UpdateStoredAmiiboData"}, | ||||||
|  |         {IPC::MakeHeader(0x000A, 0, 0), nullptr, "Unknown0x0A"}, | ||||||
|         {IPC::MakeHeader(0x000B, 0, 0), &NFC_U::GetTagInRangeEvent, "GetTagInRangeEvent"}, |         {IPC::MakeHeader(0x000B, 0, 0), &NFC_U::GetTagInRangeEvent, "GetTagInRangeEvent"}, | ||||||
|         {IPC::MakeHeader(0x000C, 0, 0), &NFC_U::GetTagOutOfRangeEvent, "GetTagOutOfRangeEvent"}, |         {IPC::MakeHeader(0x000C, 0, 0), &NFC_U::GetTagOutOfRangeEvent, "GetTagOutOfRangeEvent"}, | ||||||
|         {IPC::MakeHeader(0x000D, 0, 0), &NFC_U::GetTagState, "GetTagState"}, |         {IPC::MakeHeader(0x000D, 0, 0), &NFC_U::GetTagState, "GetTagState"}, | ||||||
|  |         {IPC::MakeHeader(0x000E, 0, 0), nullptr, "Unknown0x0E"}, | ||||||
|         {IPC::MakeHeader(0x000F, 0, 0), &NFC_U::CommunicationGetStatus, "CommunicationGetStatus"}, |         {IPC::MakeHeader(0x000F, 0, 0), &NFC_U::CommunicationGetStatus, "CommunicationGetStatus"}, | ||||||
|         {IPC::MakeHeader(0x0010, 0, 0), nullptr, "GetTagInfo2"}, |         {IPC::MakeHeader(0x0010, 0, 0), &NFC_U::GetTagInfo2, "GetTagInfo2"}, | ||||||
|         {IPC::MakeHeader(0x0011, 0, 0), &NFC_U::GetTagInfo, "GetTagInfo"}, |         {IPC::MakeHeader(0x0011, 0, 0), &NFC_U::GetTagInfo, "GetTagInfo"}, | ||||||
|         {IPC::MakeHeader(0x0012, 0, 0), nullptr, "CommunicationGetResult"}, |         {IPC::MakeHeader(0x0012, 0, 0), &NFC_U::CommunicationGetResult, "CommunicationGetResult"}, | ||||||
|         {IPC::MakeHeader(0x0013, 1, 0), nullptr, "OpenAppData"}, |         {IPC::MakeHeader(0x0013, 1, 0), &NFC_U::OpenAppData, "OpenAppData"}, | ||||||
|         {IPC::MakeHeader(0x0014, 14, 4), nullptr, "InitializeWriteAppData"}, |         {IPC::MakeHeader(0x0014, 14, 4), &NFC_U::InitializeWriteAppData, "InitializeWriteAppData"}, | ||||||
|         {IPC::MakeHeader(0x0015, 1, 0), nullptr, "ReadAppData"}, |         {IPC::MakeHeader(0x0015, 1, 0), &NFC_U::ReadAppData, "ReadAppData"}, | ||||||
|         {IPC::MakeHeader(0x0016, 9, 2), nullptr, "WriteAppData"}, |         {IPC::MakeHeader(0x0016, 9, 2), &NFC_U::WriteAppData, "WriteAppData"}, | ||||||
|         {IPC::MakeHeader(0x0017, 0, 0), nullptr, "GetAmiiboSettings"}, |         {IPC::MakeHeader(0x0017, 0, 0), &NFC_U::GetRegisterInfo, "GetRegisterInfo"}, | ||||||
|         {IPC::MakeHeader(0x0018, 0, 0), &NFC_U::GetAmiiboConfig, "GetAmiiboConfig"}, |         {IPC::MakeHeader(0x0018, 0, 0), &NFC_U::GetCommonInfo, "GetCommonInfo"}, | ||||||
|         {IPC::MakeHeader(0x0019, 0, 0), nullptr, "GetAppDataInitStruct"}, |         {IPC::MakeHeader(0x0019, 0, 0), &NFC_U::GetAppDataInitStruct, "GetAppDataInitStruct"}, | ||||||
|         {IPC::MakeHeader(0x001A, 0, 0), &NFC_U::Unknown0x1A, "Unknown0x1A"}, |         {IPC::MakeHeader(0x001A, 0, 0), &NFC_U::LoadAmiiboPartially, "LoadAmiiboPartially"}, | ||||||
|         {IPC::MakeHeader(0x001B, 0, 0), &NFC_U::GetIdentificationBlock, "GetIdentificationBlock"}, |         {IPC::MakeHeader(0x001B, 0, 0), &NFC_U::GetIdentificationBlock, "GetIdentificationBlock"}, | ||||||
|  |         {IPC::MakeHeader(0x001C, 0, 0), nullptr, "Unknown0x1C"}, | ||||||
|  |         {IPC::MakeHeader(0x001D, 0, 0), nullptr, "Unknown0x1D"}, | ||||||
|  |         {IPC::MakeHeader(0x001E, 0, 0), nullptr, "Unknown0x1E"}, | ||||||
|  |         {IPC::MakeHeader(0x001F, 0, 0), nullptr, "Unknown0x1F"}, | ||||||
|  |         {IPC::MakeHeader(0x0020, 0, 0), nullptr, "Unknown0x20"}, | ||||||
|  |         {IPC::MakeHeader(0x0021, 0, 0), nullptr, "Unknown0x21"}, | ||||||
|  |         {IPC::MakeHeader(0x0022, 0, 0), nullptr, "Unknown0x22"}, | ||||||
|         // clang-format on
 |         // clang-format on
 | ||||||
|     }; |     }; | ||||||
|     RegisterHandlers(functions); |     RegisterHandlers(functions); | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue