mirror of
https://github.com/wheremyfoodat/Panda3DS.git
synced 2025-04-07 14:45:41 +12:00
Merge remote-tracking branch 'upstream/master' into CRO
This commit is contained in:
commit
08e092a28d
26 changed files with 859 additions and 15 deletions
|
@ -137,7 +137,8 @@ set(SERVICE_SOURCE_FILES src/core/services/service_manager.cpp src/core/services
|
|||
src/core/services/y2r.cpp src/core/services/cam.cpp src/core/services/ldr_ro.cpp
|
||||
src/core/services/act.cpp src/core/services/nfc.cpp src/core/services/dlp_srvr.cpp
|
||||
src/core/services/ir_user.cpp src/core/services/http.cpp src/core/services/soc.cpp
|
||||
src/core/services/ssl.cpp src/core/services/news_u.cpp
|
||||
src/core/services/ssl.cpp src/core/services/news_u.cpp src/core/services/amiibo_device.cpp
|
||||
src/core/services/csnd.cpp src/core/services/nwm_uds.cpp
|
||||
)
|
||||
set(PICA_SOURCE_FILES src/core/PICA/gpu.cpp src/core/PICA/regs.cpp src/core/PICA/shader_unit.cpp
|
||||
src/core/PICA/shader_interpreter.cpp src/core/PICA/dynapica/shader_rec.cpp
|
||||
|
@ -181,6 +182,7 @@ set(HEADER_FILES include/emulator.hpp include/helpers.hpp include/termcolor.hpp
|
|||
include/fs/romfs.hpp include/fs/ivfc.hpp include/discord_rpc.hpp include/services/http.hpp include/result/result_cfg.hpp
|
||||
include/applets/applet.hpp include/applets/mii_selector.hpp include/math_util.hpp include/services/soc.hpp
|
||||
include/services/news_u.hpp include/applets/software_keyboard.hpp include/applets/applet_manager.hpp include/fs/archive_user_save_data.hpp
|
||||
include/services/amiibo_device.hpp include/services/nfc_types.hpp include/swap.hpp include/services/csnd.hpp include/services/nwm_uds.hpp
|
||||
)
|
||||
|
||||
cmrc_add_resource_library(
|
||||
|
|
|
@ -98,6 +98,7 @@ class Emulator {
|
|||
void pause(); // Pause the emulator
|
||||
void togglePause();
|
||||
|
||||
bool loadAmiibo(const std::filesystem::path& path);
|
||||
bool loadROM(const std::filesystem::path& path);
|
||||
bool loadNCSD(const std::filesystem::path& path, ROMType type);
|
||||
bool load3DSX(const std::filesystem::path& path);
|
||||
|
|
|
@ -18,6 +18,11 @@ public:
|
|||
FileDescriptor openFile(const FSPath& path, const FilePerms& perms) override;
|
||||
std::optional<u32> readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) override;
|
||||
|
||||
Rust::Result<FormatInfo, HorizonResult> getFormatInfo(const FSPath& path) override {
|
||||
Helpers::warn("Stubbed ExtSaveData::GetFormatInfo");
|
||||
return Ok(FormatInfo{.size = 1_GB, .numOfDirectories = 255, .numOfFiles = 255, .duplicateData = false});
|
||||
}
|
||||
|
||||
// Takes in a binary ExtSaveData path, outputs a combination of the backing folder with the low and high save entries of the path
|
||||
// Used for identifying the archive format info files
|
||||
std::string getExtSaveDataPathFromBinary(const FSPath& path);
|
||||
|
|
|
@ -19,6 +19,7 @@ namespace KernelHandles {
|
|||
CECD, // More Streetpass stuff?
|
||||
CFG_U, // CFG service (Console & region info)
|
||||
CFG_I,
|
||||
CSND, // Plays audio directly from PCM samples
|
||||
DLP_SRVR, // Download Play: Server. Used for network play.
|
||||
DSP, // DSP service (Used for audio decoding and output)
|
||||
HID, // HID service (Handles input-related things including gyro. Does NOT handle New3DS controls or CirclePadPro)
|
||||
|
@ -34,6 +35,7 @@ namespace KernelHandles {
|
|||
NFC, // NFC (Duh), used for Amiibo
|
||||
NIM, // Updates, DLC, etc
|
||||
NDM, // ?????
|
||||
NWM_UDS, // Local multiplayer
|
||||
NEWS_U, // This service literally has 1 command (AddNotification) and I don't even understand what it does
|
||||
PTM, // PTM service (Used for accessing various console info, such as battery, shell and pedometer state)
|
||||
SOC, // Socket service
|
||||
|
@ -45,6 +47,7 @@ namespace KernelHandles {
|
|||
|
||||
GSPSharedMemHandle = MaxServiceHandle + 1, // Handle for the GSP shared memory
|
||||
FontSharedMemHandle,
|
||||
CSNDSharedMemHandle,
|
||||
HIDSharedMemHandle,
|
||||
|
||||
MinSharedMemHandle = GSPSharedMemHandle,
|
||||
|
@ -73,6 +76,7 @@ namespace KernelHandles {
|
|||
case CECD: return "CECD";
|
||||
case CFG_U: return "CFG:U";
|
||||
case CFG_I: return "CFG:I";
|
||||
case CSND: return "CSND";
|
||||
case DSP: return "DSP";
|
||||
case DLP_SRVR: return "DLP::SRVR";
|
||||
case HID: return "HID";
|
||||
|
@ -87,6 +91,7 @@ namespace KernelHandles {
|
|||
case MIC: return "MIC";
|
||||
case NDM: return "NDM";
|
||||
case NEWS_U: return "NEWS_U";
|
||||
case NWM_UDS: return "nwm::UDS";
|
||||
case NFC: return "NFC";
|
||||
case NIM: return "NIM";
|
||||
case PTM: return "PTM";
|
||||
|
|
|
@ -72,7 +72,15 @@ public:
|
|||
|
||||
// Signals an event, returns true on success or false if the event does not exist
|
||||
bool signalEvent(Handle e);
|
||||
private:
|
||||
|
||||
void clearEvent(Handle e) {
|
||||
KernelObject* object = getObject(e, KernelObjectType::Event);
|
||||
if (object != nullptr) {
|
||||
object->getData<Event>()->fired = false;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void signalArbiter(u32 waitingAddress, s32 threadCount);
|
||||
void sleepThread(s64 ns);
|
||||
void sleepThreadOnArbiter(u32 waitingAddress);
|
||||
|
|
|
@ -38,6 +38,7 @@ namespace Log {
|
|||
static Logger<false> camLogger;
|
||||
static Logger<false> cecdLogger;
|
||||
static Logger<false> cfgLogger;
|
||||
static Logger<false> csndLogger;
|
||||
static Logger<false> dspServiceLogger;
|
||||
static Logger<false> dlpSrvrLogger;
|
||||
static Logger<false> frdLogger;
|
||||
|
@ -52,6 +53,7 @@ namespace Log {
|
|||
static Logger<false> micLogger;
|
||||
static Logger<false> newsLogger;
|
||||
static Logger<false> nfcLogger;
|
||||
static Logger<false> nwmUdsLogger;
|
||||
static Logger<false> nimLogger;
|
||||
static Logger<false> ndmLogger;
|
||||
static Logger<false> ptmLogger;
|
||||
|
|
|
@ -112,10 +112,11 @@ class Memory {
|
|||
// This tracks our OS' memory allocations
|
||||
std::vector<KernelMemoryTypes::MemoryInfo> memoryInfo;
|
||||
|
||||
std::array<SharedMemoryBlock, 3> sharedMemBlocks = {
|
||||
std::array<SharedMemoryBlock, 4> sharedMemBlocks = {
|
||||
SharedMemoryBlock(0, 0, KernelHandles::FontSharedMemHandle), // Shared memory for the system font (size is 0 because we read the size from the cmrc filesystem
|
||||
SharedMemoryBlock(0, 0x1000, KernelHandles::GSPSharedMemHandle), // GSP shared memory
|
||||
SharedMemoryBlock(0, 0x1000, KernelHandles::HIDSharedMemHandle) // HID shared memory
|
||||
SharedMemoryBlock(0, 0x1000, KernelHandles::HIDSharedMemHandle), // HID shared memory
|
||||
SharedMemoryBlock(0, 0x3000, KernelHandles::CSNDSharedMemHandle), // CSND shared memory
|
||||
};
|
||||
|
||||
public:
|
||||
|
|
17
include/services/amiibo_device.hpp
Normal file
17
include/services/amiibo_device.hpp
Normal file
|
@ -0,0 +1,17 @@
|
|||
#pragma once
|
||||
#include <array>
|
||||
#include "helpers.hpp"
|
||||
#include "io_file.hpp"
|
||||
#include "nfc_types.hpp"
|
||||
|
||||
class AmiiboDevice {
|
||||
bool loaded = false;
|
||||
bool encrypted = false;
|
||||
|
||||
public:
|
||||
static constexpr size_t tagSize = 0x21C;
|
||||
std::array<u8, tagSize> raw;
|
||||
|
||||
void loadFromRaw();
|
||||
void reset();
|
||||
};
|
|
@ -22,7 +22,14 @@ class CFGService {
|
|||
void secureInfoGetRegion(u32 messagePointer);
|
||||
|
||||
public:
|
||||
enum class Type {
|
||||
U, // cfg:u
|
||||
I, // cfg:i
|
||||
S, // cfg:s
|
||||
NOR, // cfg:nor
|
||||
};
|
||||
|
||||
CFGService(Memory& mem) : mem(mem) {}
|
||||
void reset();
|
||||
void handleSyncRequest(u32 messagePointer);
|
||||
void handleSyncRequest(u32 messagePointer, Type type);
|
||||
};
|
36
include/services/csnd.hpp
Normal file
36
include/services/csnd.hpp
Normal file
|
@ -0,0 +1,36 @@
|
|||
#pragma once
|
||||
#include <optional>
|
||||
|
||||
#include "helpers.hpp"
|
||||
#include "kernel_types.hpp"
|
||||
#include "logger.hpp"
|
||||
#include "memory.hpp"
|
||||
|
||||
// Circular dependencies ^-^
|
||||
class Kernel;
|
||||
|
||||
class CSNDService {
|
||||
Handle handle = KernelHandles::CSND;
|
||||
Memory& mem;
|
||||
Kernel& kernel;
|
||||
MAKE_LOG_FUNCTION(log, csndLogger)
|
||||
|
||||
u8* sharedMemory = nullptr;
|
||||
std::optional<Handle> csndMutex = std::nullopt;
|
||||
size_t sharedMemSize = 0;
|
||||
bool initialized = false;
|
||||
|
||||
// Service functions
|
||||
void acquireSoundChannels(u32 messagePointer);
|
||||
void executeCommands(u32 messagePointer);
|
||||
void initialize(u32 messagePointer);
|
||||
|
||||
public:
|
||||
CSNDService(Memory& mem, Kernel& kernel) : mem(mem), kernel(kernel) {}
|
||||
void reset();
|
||||
void handleSyncRequest(u32 messagePointer);
|
||||
|
||||
void setSharedMemory(u8* ptr) {
|
||||
sharedMemory = ptr;
|
||||
}
|
||||
};
|
|
@ -12,6 +12,8 @@ class NDMService {
|
|||
|
||||
// Service commands
|
||||
void clearHalfAwakeMacFilter(u32 messagePointer);
|
||||
void enterExclusiveState(u32 messagePointer);
|
||||
void exitExclusiveState(u32 messagePointer);
|
||||
void overrideDefaultDaemons(u32 messagePointer);
|
||||
void resumeDaemons(u32 messagePointer);
|
||||
void resumeScheduler(u32 messagePointer);
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
#pragma once
|
||||
#include <filesystem>
|
||||
|
||||
#include "amiibo_device.hpp"
|
||||
#include "helpers.hpp"
|
||||
#include "kernel_types.hpp"
|
||||
#include "logger.hpp"
|
||||
|
@ -33,6 +36,7 @@ class NFCService {
|
|||
// Kernel events signaled when an NFC tag goes in and out of range respectively
|
||||
std::optional<Handle> tagInRangeEvent, tagOutOfRangeEvent;
|
||||
|
||||
AmiiboDevice device;
|
||||
Old3DSAdapterStatus adapterStatus;
|
||||
TagStatus tagStatus;
|
||||
bool initialized = false;
|
||||
|
@ -41,16 +45,22 @@ class NFCService {
|
|||
void communicationGetResult(u32 messagePointer);
|
||||
void communicationGetStatus(u32 messagePointer);
|
||||
void initialize(u32 messagePointer);
|
||||
void getModelInfo(u32 messagePointer);
|
||||
void getTagInfo(u32 messagePointer);
|
||||
void getTagInRangeEvent(u32 messagePointer);
|
||||
void getTagOutOfRangeEvent(u32 messagePointer);
|
||||
void getTagState(u32 messagePointer);
|
||||
void loadAmiiboPartially(u32 messagePointer);
|
||||
void shutdown(u32 messagePointer);
|
||||
void startCommunication(u32 messagePointer);
|
||||
void startTagScanning(u32 messagePointer);
|
||||
void stopCommunication(u32 messagePointer);
|
||||
void stopTagScanning(u32 messagePointer);
|
||||
|
||||
public:
|
||||
public:
|
||||
NFCService(Memory& mem, Kernel& kernel) : mem(mem), kernel(kernel) {}
|
||||
void reset();
|
||||
void handleSyncRequest(u32 messagePointer);
|
||||
|
||||
bool loadAmiibo(const std::filesystem::path& path);
|
||||
};
|
412
include/services/nfc_types.hpp
Normal file
412
include/services/nfc_types.hpp
Normal file
|
@ -0,0 +1,412 @@
|
|||
// Copyright 2022 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <type_traits>
|
||||
|
||||
#include "helpers.hpp"
|
||||
#include "swap.hpp"
|
||||
|
||||
namespace Service::NFC {
|
||||
static constexpr std::size_t amiiboNameLength = 0xA;
|
||||
static constexpr std::size_t applicationIDVersionOffset = 0x1c;
|
||||
static constexpr std::size_t counterLimit = 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,
|
||||
};
|
||||
|
||||
struct ChecksummedMiiData {
|
||||
u8 raw[0x60];
|
||||
};
|
||||
static_assert(sizeof(ChecksummedMiiData) == 0x60);
|
||||
|
||||
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, amiiboNameLength>;
|
||||
using DataBlock = std::array<u8, 0x10>;
|
||||
using KeyData = std::array<u8, 0x6>;
|
||||
|
||||
struct TagUuid {
|
||||
UniqueSerialNumber uid;
|
||||
u8 nintendo_id;
|
||||
LockBytes lock_bytes;
|
||||
};
|
||||
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 {
|
||||
u8 raw{};
|
||||
};
|
||||
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;
|
||||
u32 pad; // 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
|
||||
};
|
||||
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
|
||||
ChecksummedMiiData owner_mii; // Encrypted Mii data
|
||||
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;
|
||||
ChecksummedMiiData owner_mii; // Mii data
|
||||
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;
|
||||
};
|
||||
};
|
||||
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;
|
||||
};
|
||||
};
|
||||
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;
|
||||
u8 pad;
|
||||
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;
|
||||
u8 pad[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;
|
||||
u8 pad[0x2F];
|
||||
};
|
||||
static_assert(sizeof(ModelInfo) == 0x36, "ModelInfo is an invalid size");
|
||||
|
||||
struct RegisterInfo {
|
||||
ChecksummedMiiData mii_data;
|
||||
AmiiboName amiibo_name;
|
||||
u16 pad; // Zero string terminator
|
||||
u8 flags;
|
||||
u8 font_region;
|
||||
WriteDate creation_date;
|
||||
u8 pad2[0x2C];
|
||||
};
|
||||
static_assert(sizeof(RegisterInfo) == 0xA8, "RegisterInfo is an invalid size");
|
||||
|
||||
struct RegisterInfoPrivate {
|
||||
ChecksummedMiiData mii_data;
|
||||
AmiiboName amiibo_name;
|
||||
u16 pad; // Zero string terminator
|
||||
u8 flags;
|
||||
u8 font_region;
|
||||
WriteDate creation_date;
|
||||
u8 pad2[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;
|
||||
u8 pad[0x7];
|
||||
u8 pad2[0x28];
|
||||
};
|
||||
static_assert(sizeof(AdminInfo) == 0x40, "AdminInfo is an invalid size");
|
||||
|
||||
} // namespace Service::NFC
|
28
include/services/nwm_uds.hpp
Normal file
28
include/services/nwm_uds.hpp
Normal file
|
@ -0,0 +1,28 @@
|
|||
#pragma once
|
||||
#include <optional>
|
||||
|
||||
#include "helpers.hpp"
|
||||
#include "kernel_types.hpp"
|
||||
#include "logger.hpp"
|
||||
#include "memory.hpp"
|
||||
|
||||
// More circular dependencies
|
||||
class Kernel;
|
||||
|
||||
class NwmUdsService {
|
||||
Handle handle = KernelHandles::NWM_UDS;
|
||||
Memory& mem;
|
||||
Kernel& kernel;
|
||||
MAKE_LOG_FUNCTION(log, nwmUdsLogger)
|
||||
|
||||
bool initialized = false;
|
||||
std::optional<Handle> eventHandle = std::nullopt;
|
||||
|
||||
// Service commands
|
||||
void initializeWithVersion(u32 messagePointer);
|
||||
|
||||
public:
|
||||
NwmUdsService(Memory& mem, Kernel& kernel) : mem(mem), kernel(kernel) {}
|
||||
void reset();
|
||||
void handleSyncRequest(u32 messagePointer);
|
||||
};
|
|
@ -14,6 +14,7 @@
|
|||
#include "services/cam.hpp"
|
||||
#include "services/cecd.hpp"
|
||||
#include "services/cfg.hpp"
|
||||
#include "services/csnd.hpp"
|
||||
#include "services/dlp_srvr.hpp"
|
||||
#include "services/dsp.hpp"
|
||||
#include "services/frd.hpp"
|
||||
|
@ -27,6 +28,7 @@
|
|||
#include "services/mcu/mcu_hwc.hpp"
|
||||
#include "services/mic.hpp"
|
||||
#include "services/ndm.hpp"
|
||||
#include "services/nwm_uds.hpp"
|
||||
#include "services/news_u.hpp"
|
||||
#include "services/nfc.hpp"
|
||||
#include "services/nim.hpp"
|
||||
|
@ -56,6 +58,7 @@ class ServiceManager {
|
|||
CAMService cam;
|
||||
CECDService cecd;
|
||||
CFGService cfg;
|
||||
CSNDService csnd;
|
||||
DlpSrvrService dlp_srvr;
|
||||
DSPService dsp;
|
||||
HIDService hid;
|
||||
|
@ -70,6 +73,7 @@ class ServiceManager {
|
|||
NDMService ndm;
|
||||
NewsUService news_u;
|
||||
NFCService nfc;
|
||||
NwmUdsService nwm_uds;
|
||||
NIMService nim;
|
||||
PTMService ptm;
|
||||
SOCService soc;
|
||||
|
@ -99,9 +103,11 @@ class ServiceManager {
|
|||
void sendGPUInterrupt(GPUInterrupt type) { gsp_gpu.requestInterrupt(type); }
|
||||
void setGSPSharedMem(u8* ptr) { gsp_gpu.setSharedMem(ptr); }
|
||||
void setHIDSharedMem(u8* ptr) { hid.setSharedMem(ptr); }
|
||||
void setCSNDSharedMem(u8* ptr) { csnd.setSharedMemory(ptr); }
|
||||
|
||||
void signalDSPEvents() { dsp.signalEvents(); }
|
||||
|
||||
// Input function wrappers
|
||||
HIDService& getHID() { return hid; }
|
||||
NFCService& getNFC() { return nfc; }
|
||||
};
|
||||
|
|
|
@ -91,6 +91,7 @@ Panda3DS also supports controller input using the SDL2 GameController API.
|
|||
- [3DBrew](https://www.3dbrew.org/wiki/Main_Page), a wiki full of 3DS information and the main source of documentation used.
|
||||
- [GBATek](https://www.problemkaputt.de/gbatek.htm#3dsreference), a GBA, DS and 3DS reference which provided insights on some pieces of hardware as well as neatly documenting things like certain file formats used in games.
|
||||
- [Libctru](https://github.com/devkitPro/libctru), the most well-known 3DS homebrew SDK. Used for developing test ROMs, as well as a source of documentation thanks to its doxygen wiki.
|
||||
- [ctru-rs](https://github.com/rust3ds/ctru-rs), a wrapper around libctru for writing 3DS homebrew in Rust.
|
||||
|
||||
- [Citra](https://github.com/citra-emu/citra), an HLE 3DS emulator. Very useful as a reference, with some code snippets inspired or adapted from it.
|
||||
- [3dmoo](https://github.com/plutooo/3dmoo), an HLE 3DS emulator which helped similarly to Citra
|
||||
|
|
|
@ -139,6 +139,11 @@ void Kernel::mapMemoryBlock() {
|
|||
mem.copySharedFont(ptr);
|
||||
break;
|
||||
|
||||
case KernelHandles::CSNDSharedMemHandle:
|
||||
serviceManager.setCSNDSharedMem(ptr);
|
||||
printf("Mapping CSND memory block\n");
|
||||
break;
|
||||
|
||||
default: Helpers::panic("Mapping unknown shared memory block: %X", block);
|
||||
}
|
||||
} else {
|
||||
|
|
11
src/core/services/amiibo_device.cpp
Normal file
11
src/core/services/amiibo_device.cpp
Normal file
|
@ -0,0 +1,11 @@
|
|||
#include "services/amiibo_device.hpp"
|
||||
|
||||
void AmiiboDevice::reset() {
|
||||
encrypted = false;
|
||||
loaded = false;
|
||||
}
|
||||
|
||||
// Load amiibo information from our raw 540 byte array
|
||||
void AmiiboDevice::loadFromRaw() {
|
||||
|
||||
}
|
|
@ -21,7 +21,7 @@ namespace CFGCommands {
|
|||
|
||||
void CFGService::reset() {}
|
||||
|
||||
void CFGService::handleSyncRequest(u32 messagePointer) {
|
||||
void CFGService::handleSyncRequest(u32 messagePointer, CFGService::Type type) {
|
||||
const u32 command = mem.read32(messagePointer);
|
||||
switch (command) {
|
||||
case CFGCommands::GetConfigInfoBlk2: [[likely]] getConfigInfoBlk2(messagePointer); break;
|
||||
|
@ -121,6 +121,8 @@ void CFGService::getConfigInfoBlk2(u32 messagePointer) {
|
|||
}
|
||||
} else if (size == 4 && blockID == 0x170000) { // Miiverse access key
|
||||
mem.write32(output, 0);
|
||||
} else if (size == 8 && blockID == 0x00090000) {
|
||||
mem.write64(output, 0); // Some sort of key used with nwm::UDS::InitializeWithVersion
|
||||
} else {
|
||||
Helpers::panic("Unhandled GetConfigInfoBlk2 configuration. Size = %d, block = %X", size, blockID);
|
||||
}
|
||||
|
|
104
src/core/services/csnd.cpp
Normal file
104
src/core/services/csnd.cpp
Normal file
|
@ -0,0 +1,104 @@
|
|||
#include "services/csnd.hpp"
|
||||
|
||||
#include "ipc.hpp"
|
||||
#include "kernel.hpp"
|
||||
#include "result/result.hpp"
|
||||
|
||||
namespace CSNDCommands {
|
||||
enum : u32 {
|
||||
Initialize = 0x00010140,
|
||||
ExecuteCommands = 0x00030040,
|
||||
AcquireSoundChannels = 0x00050000,
|
||||
};
|
||||
}
|
||||
|
||||
void CSNDService::reset() {
|
||||
csndMutex = std::nullopt;
|
||||
initialized = false;
|
||||
sharedMemory = nullptr;
|
||||
sharedMemSize = 0;
|
||||
}
|
||||
|
||||
void CSNDService::handleSyncRequest(u32 messagePointer) {
|
||||
const u32 command = mem.read32(messagePointer);
|
||||
|
||||
switch (command) {
|
||||
case CSNDCommands::AcquireSoundChannels: acquireSoundChannels(messagePointer); break;
|
||||
case CSNDCommands::ExecuteCommands: executeCommands(messagePointer); break;
|
||||
case CSNDCommands::Initialize: initialize(messagePointer); break;
|
||||
|
||||
default:
|
||||
Helpers::warn("Unimplemented CSND service requested. Command: %08X\n", command);
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CSNDService::acquireSoundChannels(u32 messagePointer) {
|
||||
log("CSND::AcquireSoundChannels\n");
|
||||
// The CSND service talks to the DSP using the DSP FIFO to negotiate what CSND channels are allocated to the DSP, and this seems to be channels 0-7 (usually). The rest are dedicated to CSND services.
|
||||
// https://www.3dbrew.org/wiki/CSND_Services
|
||||
constexpr u32 csndChannelMask = 0xFFFFFF00;
|
||||
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x5, 2, 0));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
mem.write32(messagePointer + 8, csndChannelMask);
|
||||
}
|
||||
|
||||
void CSNDService::initialize(u32 messagePointer) {
|
||||
u32 blockSize = mem.read32(messagePointer + 4);
|
||||
const u32 offset0 = mem.read32(messagePointer + 8);
|
||||
const u32 offset1 = mem.read32(messagePointer + 12);
|
||||
const u32 offset2 = mem.read32(messagePointer + 16);
|
||||
const u32 offset3 = mem.read32(messagePointer + 20);
|
||||
|
||||
log("CSND::Initialize (Block size = %08X, offset0 = %X, offset1 = %X, offset2 = %X, offset3 = %X)\n", blockSize, offset0, offset1, offset2, offset3);
|
||||
|
||||
// Align block size to 4KB. CSND shared memory block is currently stubbed to be 0x3000 == 12KB, so panic if this is more than requested
|
||||
blockSize = (blockSize + 0xFFF) & ~0xFFF;
|
||||
if (blockSize != 12_KB) {
|
||||
Helpers::panic("Unhandled size for CSND shared memory block");
|
||||
}
|
||||
|
||||
if (initialized) {
|
||||
printf("CSND initialized twice\n");
|
||||
}
|
||||
|
||||
if (!csndMutex.has_value()) {
|
||||
csndMutex = kernel.makeMutex(false);
|
||||
}
|
||||
|
||||
initialized = true;
|
||||
sharedMemSize = blockSize;
|
||||
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x1, 1, 3));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
mem.write32(messagePointer + 8, 0x4000000);
|
||||
mem.write32(messagePointer + 12, csndMutex.value());
|
||||
mem.write32(messagePointer + 16, KernelHandles::CSNDSharedMemHandle);
|
||||
}
|
||||
|
||||
void CSNDService::executeCommands(u32 messagePointer) {
|
||||
const u32 offset = mem.read32(messagePointer + 4);
|
||||
log("CSND::ExecuteCommands (command offset = %X)\n", offset);
|
||||
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x5, 2, 0));
|
||||
|
||||
if (!sharedMemory) {
|
||||
Helpers::warn("CSND::Execute commands without shared memory");
|
||||
mem.write32(messagePointer + 4, Result::FailurePlaceholder);
|
||||
return;
|
||||
}
|
||||
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
|
||||
// This is initially zero when this command data is written by the user process, once the CSND module finishes processing the command this is set
|
||||
// to 0x1. This flag is only set to value 1 for the first command(once processing for the entire command chain is finished) at the offset
|
||||
// specified in the service command, not all type0 commands in the chain.
|
||||
constexpr u32 commandListDoneOffset = 0x4;
|
||||
|
||||
// Make sure to not access OoB of the shared memory block when marking command list processing as finished
|
||||
if (offset + commandListDoneOffset < sharedMemSize) {
|
||||
sharedMemory[offset + commandListDoneOffset] = 1;
|
||||
}
|
||||
}
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
namespace NDMCommands {
|
||||
enum : u32 {
|
||||
EnterExclusiveState = 0x00010042,
|
||||
ExitExclusiveState = 0x00020002,
|
||||
OverrideDefaultDaemons = 0x00140040,
|
||||
SuspendDaemons = 0x00060040,
|
||||
ResumeDaemons = 0x00070040,
|
||||
|
@ -17,6 +19,8 @@ void NDMService::reset() {}
|
|||
void NDMService::handleSyncRequest(u32 messagePointer) {
|
||||
const u32 command = mem.read32(messagePointer);
|
||||
switch (command) {
|
||||
case NDMCommands::EnterExclusiveState: enterExclusiveState(messagePointer); break;
|
||||
case NDMCommands::ExitExclusiveState: exitExclusiveState(messagePointer); break;
|
||||
case NDMCommands::ClearHalfAwakeMacFilter: clearHalfAwakeMacFilter(messagePointer); break;
|
||||
case NDMCommands::OverrideDefaultDaemons: overrideDefaultDaemons(messagePointer); break;
|
||||
case NDMCommands::ResumeDaemons: resumeDaemons(messagePointer); break;
|
||||
|
@ -27,6 +31,18 @@ void NDMService::handleSyncRequest(u32 messagePointer) {
|
|||
}
|
||||
}
|
||||
|
||||
void NDMService::enterExclusiveState(u32 messagePointer) {
|
||||
log("NDM::EnterExclusiveState (stubbed)\n");
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x1, 1, 0));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
}
|
||||
|
||||
void NDMService::exitExclusiveState(u32 messagePointer) {
|
||||
log("NDM::ExitExclusiveState (stubbed)\n");
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x2, 1, 0));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
}
|
||||
|
||||
void NDMService::overrideDefaultDaemons(u32 messagePointer) {
|
||||
log("NDM::OverrideDefaultDaemons (stubbed)\n");
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x14, 1, 0));
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "services/nfc.hpp"
|
||||
#include "io_file.hpp"
|
||||
#include "ipc.hpp"
|
||||
#include "kernel.hpp"
|
||||
|
||||
|
@ -9,15 +10,20 @@ namespace NFCCommands {
|
|||
StartCommunication = 0x00030000,
|
||||
StopCommunication = 0x00040000,
|
||||
StartTagScanning = 0x00050040,
|
||||
StopTagScanning = 0x00060000,
|
||||
GetTagInRangeEvent = 0x000B0000,
|
||||
GetTagOutOfRangeEvent = 0x000C0000,
|
||||
GetTagState = 0x000D0000,
|
||||
CommunicationGetStatus = 0x000F0000,
|
||||
GetTagInfo = 0x00110000,
|
||||
CommunicationGetResult = 0x00120000,
|
||||
LoadAmiiboPartially = 0x001A0000,
|
||||
GetModelInfo = 0x001B0000,
|
||||
};
|
||||
}
|
||||
|
||||
void NFCService::reset() {
|
||||
device.reset();
|
||||
tagInRangeEvent = std::nullopt;
|
||||
tagOutOfRangeEvent = std::nullopt;
|
||||
|
||||
|
@ -29,19 +35,60 @@ void NFCService::reset() {
|
|||
void NFCService::handleSyncRequest(u32 messagePointer) {
|
||||
const u32 command = mem.read32(messagePointer);
|
||||
switch (command) {
|
||||
case NFCCommands::CommunicationGetResult: communicationGetResult(messagePointer); break;
|
||||
case NFCCommands::CommunicationGetStatus: communicationGetStatus(messagePointer); break;
|
||||
case NFCCommands::Initialize: initialize(messagePointer); break;
|
||||
case NFCCommands::GetModelInfo: getModelInfo(messagePointer); break;
|
||||
case NFCCommands::GetTagInfo: getTagInfo(messagePointer); break;
|
||||
case NFCCommands::GetTagInRangeEvent: getTagInRangeEvent(messagePointer); break;
|
||||
case NFCCommands::GetTagOutOfRangeEvent: getTagOutOfRangeEvent(messagePointer); break;
|
||||
case NFCCommands::GetTagState: getTagState(messagePointer); break;
|
||||
case NFCCommands::LoadAmiiboPartially: loadAmiiboPartially(messagePointer); break;
|
||||
case NFCCommands::Shutdown: shutdown(messagePointer); break;
|
||||
case NFCCommands::StartCommunication: startCommunication(messagePointer); break;
|
||||
case NFCCommands::StartTagScanning: startTagScanning(messagePointer); break;
|
||||
case NFCCommands::StopCommunication: stopCommunication(messagePointer); break;
|
||||
case NFCCommands::StopTagScanning: stopTagScanning(messagePointer); break;
|
||||
default: Helpers::panic("NFC service requested. Command: %08X\n", command);
|
||||
}
|
||||
}
|
||||
|
||||
bool NFCService::loadAmiibo(const std::filesystem::path& path) {
|
||||
if (!initialized || tagStatus != TagStatus::Scanning) {
|
||||
Helpers::warn("It's not the correct time to load an amiibo! Make sure to load amiibi when the game is searching for one!");
|
||||
return false;
|
||||
}
|
||||
|
||||
IOFile file(path, "rb");
|
||||
if (!file.isOpen()) {
|
||||
printf("Failed to open Amiibo file");
|
||||
file.close();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
auto [success, bytesRead] = file.readBytes(&device.raw, AmiiboDevice::tagSize);
|
||||
if (!success || bytesRead != AmiiboDevice::tagSize) {
|
||||
printf("Failed to read entire tag from Amiibo file: File might not be a proper amiibo file\n");
|
||||
file.close();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
device.loadFromRaw();
|
||||
|
||||
if (tagOutOfRangeEvent.has_value()) {
|
||||
kernel.clearEvent(tagOutOfRangeEvent.value());
|
||||
}
|
||||
|
||||
if (tagInRangeEvent.has_value()) {
|
||||
kernel.signalEvent(tagInRangeEvent.value());
|
||||
}
|
||||
|
||||
file.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
void NFCService::initialize(u32 messagePointer) {
|
||||
const u8 type = mem.read8(messagePointer + 4);
|
||||
log("NFC::Initialize (type = %d)\n", type);
|
||||
|
@ -100,7 +147,7 @@ void NFCService::getTagOutOfRangeEvent(u32 messagePointer) {
|
|||
}
|
||||
|
||||
void NFCService::getTagState(u32 messagePointer) {
|
||||
log("NFC::GetTagState");
|
||||
log("NFC::GetTagState\n");
|
||||
|
||||
mem.write32(messagePointer, IPC::responseHeader(0xD, 2, 0));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
|
@ -108,7 +155,7 @@ void NFCService::getTagState(u32 messagePointer) {
|
|||
}
|
||||
|
||||
void NFCService::communicationGetStatus(u32 messagePointer) {
|
||||
log("NFC::CommunicationGetStatus");
|
||||
log("NFC::CommunicationGetStatus\n");
|
||||
|
||||
if (!initialized) {
|
||||
Helpers::warn("NFC::CommunicationGetStatus: Old 3DS NFC Adapter not initialized\n");
|
||||
|
@ -119,6 +166,21 @@ void NFCService::communicationGetStatus(u32 messagePointer) {
|
|||
mem.write8(messagePointer + 8, static_cast<u32>(adapterStatus));
|
||||
}
|
||||
|
||||
|
||||
void NFCService::communicationGetResult(u32 messagePointer) {
|
||||
log("NFC::CommunicationGetResult\n");
|
||||
|
||||
if (!initialized) {
|
||||
Helpers::warn("NFC::CommunicationGetResult: Old 3DS NFC Adapter not initialized\n");
|
||||
}
|
||||
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x12, 2, 0));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
// On N3DS: This always writes 0 here
|
||||
// On O3DS with the NFC adapter: Returns a result code for NFC communication
|
||||
mem.write32(messagePointer + 8, 0);
|
||||
}
|
||||
|
||||
void NFCService::startCommunication(u32 messagePointer) {
|
||||
log("NFC::StartCommunication\n");
|
||||
// adapterStatus = Old3DSAdapterStatus::Active;
|
||||
|
@ -130,12 +192,28 @@ void NFCService::startCommunication(u32 messagePointer) {
|
|||
|
||||
void NFCService::startTagScanning(u32 messagePointer) {
|
||||
log("NFC::StartTagScanning\n");
|
||||
if (!initialized) {
|
||||
Helpers::warn("Scanning for NFC tags before NFC service is initialized");
|
||||
}
|
||||
|
||||
tagStatus = TagStatus::Scanning;
|
||||
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x5, 1, 0));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
}
|
||||
|
||||
void NFCService::stopTagScanning(u32 messagePointer) {
|
||||
log("NFC::StopTagScanning\n");
|
||||
if (!initialized) {
|
||||
Helpers::warn("Stopping scanning for NFC tags before NFC service is initialized");
|
||||
}
|
||||
|
||||
tagStatus = TagStatus::Initialized;
|
||||
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x6, 1, 0));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
}
|
||||
|
||||
void NFCService::stopCommunication(u32 messagePointer) {
|
||||
log("NFC::StopCommunication\n");
|
||||
adapterStatus = Old3DSAdapterStatus::InitializationComplete;
|
||||
|
@ -143,4 +221,28 @@ void NFCService::stopCommunication(u32 messagePointer) {
|
|||
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x4, 1, 0));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
}
|
||||
|
||||
void NFCService::getTagInfo(u32 messagePointer) {
|
||||
log("NFC::GetTagInfo\n");
|
||||
Helpers::warn("Unimplemented NFC::GetTagInfo");
|
||||
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x11, 12, 0));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
}
|
||||
|
||||
void NFCService::loadAmiiboPartially(u32 messagePointer) {
|
||||
log("NFC::LoadAmiiboPartially\n");
|
||||
Helpers::warn("Unimplemented NFC::LoadAmiiboPartially");
|
||||
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x1A, 1, 0));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
}
|
||||
|
||||
void NFCService::getModelInfo(u32 messagePointer) {
|
||||
log("NFC::GetModelInfo\n");
|
||||
Helpers::warn("Unimplemented NFC::GetModelInfo");
|
||||
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x1B, 14, 0));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
}
|
43
src/core/services/nwm_uds.cpp
Normal file
43
src/core/services/nwm_uds.cpp
Normal file
|
@ -0,0 +1,43 @@
|
|||
#include "ipc.hpp"
|
||||
#include "kernel.hpp"
|
||||
#include "result/result.hpp"
|
||||
#include "services/nwm_uds.hpp"
|
||||
|
||||
namespace NWMCommands {
|
||||
enum : u32 {
|
||||
InitializeWithVersion = 0x001B0302,
|
||||
};
|
||||
}
|
||||
|
||||
void NwmUdsService::reset() {
|
||||
eventHandle = std::nullopt;
|
||||
initialized = false;
|
||||
}
|
||||
|
||||
void NwmUdsService::handleSyncRequest(u32 messagePointer) {
|
||||
const u32 command = mem.read32(messagePointer);
|
||||
|
||||
switch (command) {
|
||||
case NWMCommands::InitializeWithVersion: initializeWithVersion(messagePointer); break;
|
||||
default: Helpers::panic("LCD service requested. Command: %08X\n", command);
|
||||
}
|
||||
}
|
||||
|
||||
void NwmUdsService::initializeWithVersion(u32 messagePointer) {
|
||||
Helpers::warn("Initializing NWM::UDS (Local multiplayer, unimplemented)\n");
|
||||
log("NWM::UDS::InitializeWithVersion\n");
|
||||
|
||||
if (!eventHandle.has_value()) {
|
||||
eventHandle = kernel.makeEvent(ResetType::OneShot);
|
||||
}
|
||||
|
||||
if (initialized) {
|
||||
printf("NWM::UDS initialized twice\n");
|
||||
}
|
||||
|
||||
initialized = true;
|
||||
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
mem.write32(messagePointer + 8, 0);
|
||||
mem.write32(messagePointer + 12, eventHandle.value());
|
||||
}
|
|
@ -7,9 +7,9 @@
|
|||
|
||||
ServiceManager::ServiceManager(std::span<u32, 16> regs, Memory& mem, GPU& gpu, u32& currentPID, Kernel& kernel, const EmulatorConfig& config)
|
||||
: regs(regs), mem(mem), kernel(kernel), ac(mem), am(mem), boss(mem), act(mem), apt(mem, kernel), cam(mem, kernel), cecd(mem, kernel), cfg(mem),
|
||||
dlp_srvr(mem), dsp(mem, kernel), hid(mem, kernel), http(mem), ir_user(mem, kernel), frd(mem), fs(mem, kernel, config),
|
||||
gsp_gpu(mem, gpu, kernel, currentPID), gsp_lcd(mem), ldr(mem, kernel), mcu_hwc(mem, config), mic(mem, kernel), nfc(mem, kernel), nim(mem), ndm(mem),
|
||||
news_u(mem), ptm(mem, config), soc(mem), ssl(mem), y2r(mem, kernel) {}
|
||||
csnd(mem, kernel), dlp_srvr(mem), dsp(mem, kernel), hid(mem, kernel), http(mem), ir_user(mem, kernel), frd(mem), fs(mem, kernel, config),
|
||||
gsp_gpu(mem, gpu, kernel, currentPID), gsp_lcd(mem), ldr(mem), mcu_hwc(mem, config), mic(mem, kernel), nfc(mem, kernel), nim(mem), ndm(mem),
|
||||
news_u(mem), nwm_uds(mem, kernel), ptm(mem, config), soc(mem), ssl(mem), y2r(mem, kernel) {}
|
||||
|
||||
static constexpr int MAX_NOTIFICATION_COUNT = 16;
|
||||
|
||||
|
@ -23,6 +23,7 @@ void ServiceManager::reset() {
|
|||
cam.reset();
|
||||
cecd.reset();
|
||||
cfg.reset();
|
||||
csnd.reset();
|
||||
dlp_srvr.reset();
|
||||
dsp.reset();
|
||||
hid.reset();
|
||||
|
@ -104,6 +105,7 @@ static std::map<std::string, Handle> serviceMap = {
|
|||
{ "cecd:u", KernelHandles::CECD },
|
||||
{ "cfg:u", KernelHandles::CFG_U },
|
||||
{ "cfg:i", KernelHandles::CFG_I },
|
||||
{ "csnd:SND", KernelHandles::CSND },
|
||||
{ "dlp:SRVR", KernelHandles::DLP_SRVR },
|
||||
{ "dsp::DSP", KernelHandles::DSP },
|
||||
{ "hid:USER", KernelHandles::HID },
|
||||
|
@ -119,12 +121,13 @@ static std::map<std::string, Handle> serviceMap = {
|
|||
{ "ndm:u", KernelHandles::NDM },
|
||||
{ "news:u", KernelHandles::NEWS_U },
|
||||
{ "nfc:u", KernelHandles::NFC },
|
||||
{ "nwm::UDS", KernelHandles::NWM_UDS },
|
||||
{ "nim:aoc", KernelHandles::NIM },
|
||||
{ "ptm:u", KernelHandles::PTM }, // TODO: ptm:u and ptm:sysm have very different command sets
|
||||
{ "ptm:sysm", KernelHandles::PTM },
|
||||
{ "soc:U", KernelHandles::SOC },
|
||||
{ "ssl:C", KernelHandles::SSL },
|
||||
{ "y2r:u", KernelHandles::Y2R }
|
||||
{ "y2r:u", KernelHandles::Y2R },
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
|
@ -201,7 +204,9 @@ void ServiceManager::sendCommandToService(u32 messagePointer, Handle handle) {
|
|||
case KernelHandles::BOSS: boss.handleSyncRequest(messagePointer); break;
|
||||
case KernelHandles::CAM: cam.handleSyncRequest(messagePointer); break;
|
||||
case KernelHandles::CECD: cecd.handleSyncRequest(messagePointer); break;
|
||||
case KernelHandles::CFG_U: cfg.handleSyncRequest(messagePointer); break;
|
||||
case KernelHandles::CFG_U: cfg.handleSyncRequest(messagePointer, CFGService::Type::U); break;
|
||||
case KernelHandles::CFG_I: cfg.handleSyncRequest(messagePointer, CFGService::Type::I); break;
|
||||
case KernelHandles::CSND: csnd.handleSyncRequest(messagePointer); break;
|
||||
case KernelHandles::DLP_SRVR: dlp_srvr.handleSyncRequest(messagePointer); break;
|
||||
case KernelHandles::HID: hid.handleSyncRequest(messagePointer); break;
|
||||
case KernelHandles::HTTP: http.handleSyncRequest(messagePointer); break;
|
||||
|
@ -215,6 +220,7 @@ void ServiceManager::sendCommandToService(u32 messagePointer, Handle handle) {
|
|||
case KernelHandles::NIM: nim.handleSyncRequest(messagePointer); break;
|
||||
case KernelHandles::NDM: ndm.handleSyncRequest(messagePointer); break;
|
||||
case KernelHandles::NEWS_U: news_u.handleSyncRequest(messagePointer); break;
|
||||
case KernelHandles::NWM_UDS: nwm_uds.handleSyncRequest(messagePointer); break;
|
||||
case KernelHandles::PTM: ptm.handleSyncRequest(messagePointer); break;
|
||||
case KernelHandles::SOC: soc.handleSyncRequest(messagePointer); break;
|
||||
case KernelHandles::SSL: ssl.handleSyncRequest(messagePointer); break;
|
||||
|
|
|
@ -353,7 +353,14 @@ void Emulator::run() {
|
|||
char* droppedDir = event.drop.file;
|
||||
|
||||
if (droppedDir) {
|
||||
loadROM(droppedDir);
|
||||
const std::filesystem::path path(droppedDir);
|
||||
|
||||
if (path.extension() == ".amiibo") {
|
||||
loadAmiibo(path);
|
||||
} else {
|
||||
loadROM(path);
|
||||
}
|
||||
|
||||
SDL_free(droppedDir);
|
||||
}
|
||||
break;
|
||||
|
@ -478,6 +485,11 @@ bool Emulator::loadROM(const std::filesystem::path& path) {
|
|||
return success;
|
||||
}
|
||||
|
||||
bool Emulator::loadAmiibo(const std::filesystem::path& path) {
|
||||
NFCService& nfc = kernel.getServiceManager().getNFC();
|
||||
return nfc.loadAmiibo(path);
|
||||
}
|
||||
|
||||
// Used for loading both CXI and NCSD files since they are both so similar and use the same interface
|
||||
// (We promote CXI files to NCSD internally for ease)
|
||||
bool Emulator::loadNCSD(const std::filesystem::path& path, ROMType type) {
|
||||
|
|
Loading…
Add table
Reference in a new issue