Move MiiData to its own namespace and add ChecksummedMiiData (#6824)
* Move mii to own namespace and add checksummed mii data * Fix compile issues * Make mii classes trivial and add cast operator * Fix Android side * Add new line at the end of files. * Make miidata a struct and crc16 a u32_be as per switch code. * Apply suggestions * Change back crc to u16 and set padding to 0.
This commit is contained in:
parent
f8b8b6e53c
commit
35e208b447
|
@ -53,7 +53,7 @@ void AndroidMiiSelector::Setup(const Frontend::MiiSelectorConfig& config) {
|
||||||
const u32 return_code = static_cast<u32>(
|
const u32 return_code = static_cast<u32>(
|
||||||
env->GetLongField(data, env->GetFieldID(s_mii_selector_data_class, "return_code", "J")));
|
env->GetLongField(data, env->GetFieldID(s_mii_selector_data_class, "return_code", "J")));
|
||||||
if (return_code == 1) {
|
if (return_code == 1) {
|
||||||
Finalize(return_code, HLE::Applets::MiiData{});
|
Finalize(return_code, Mii::MiiData{});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -67,6 +67,5 @@ void QtMiiSelector::OpenDialog() {
|
||||||
dialog.return_code, index);
|
dialog.return_code, index);
|
||||||
|
|
||||||
const auto mii_data = dialog.miis.at(index);
|
const auto mii_data = dialog.miis.at(index);
|
||||||
Finalize(dialog.return_code,
|
Finalize(dialog.return_code, dialog.return_code == 0 ? std::move(mii_data) : Mii::MiiData{});
|
||||||
dialog.return_code == 0 ? std::move(mii_data) : HLE::Applets::MiiData{});
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,7 @@ private:
|
||||||
QVBoxLayout* layout;
|
QVBoxLayout* layout;
|
||||||
QtMiiSelector* mii_selector;
|
QtMiiSelector* mii_selector;
|
||||||
u32 return_code = 0;
|
u32 return_code = 0;
|
||||||
std::vector<HLE::Applets::MiiData> miis;
|
std::vector<Mii::MiiData> miis;
|
||||||
|
|
||||||
friend class QtMiiSelector;
|
friend class QtMiiSelector;
|
||||||
};
|
};
|
||||||
|
|
|
@ -184,6 +184,8 @@ add_library(citra_core STATIC
|
||||||
hle/kernel/wait_object.h
|
hle/kernel/wait_object.h
|
||||||
hle/lock.cpp
|
hle/lock.cpp
|
||||||
hle/lock.h
|
hle/lock.h
|
||||||
|
hle/mii.h
|
||||||
|
hle/mii.cpp
|
||||||
hle/result.h
|
hle/result.h
|
||||||
hle/romfs.cpp
|
hle/romfs.cpp
|
||||||
hle/romfs.h
|
hle/romfs.h
|
||||||
|
|
|
@ -11,12 +11,12 @@
|
||||||
|
|
||||||
namespace Frontend {
|
namespace Frontend {
|
||||||
|
|
||||||
void MiiSelector::Finalize(u32 return_code, HLE::Applets::MiiData mii) {
|
void MiiSelector::Finalize(u32 return_code, Mii::MiiData mii) {
|
||||||
data = {return_code, mii};
|
data = {return_code, mii};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<HLE::Applets::MiiData> LoadMiis() {
|
std::vector<Mii::MiiData> LoadMiis() {
|
||||||
std::vector<HLE::Applets::MiiData> miis;
|
std::vector<Mii::MiiData> miis;
|
||||||
|
|
||||||
std::string nand_directory{FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)};
|
std::string nand_directory{FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)};
|
||||||
FileSys::ArchiveFactory_ExtSaveData extdata_archive_factory(nand_directory, true);
|
FileSys::ArchiveFactory_ExtSaveData extdata_archive_factory(nand_directory, true);
|
||||||
|
@ -36,7 +36,7 @@ std::vector<HLE::Applets::MiiData> LoadMiis() {
|
||||||
u32 saved_miis_offset = 0x8;
|
u32 saved_miis_offset = 0x8;
|
||||||
// The Mii Maker has a 100 Mii limit on the 3ds
|
// The Mii Maker has a 100 Mii limit on the 3ds
|
||||||
for (int i = 0; i < 100; ++i) {
|
for (int i = 0; i < 100; ++i) {
|
||||||
HLE::Applets::MiiData mii;
|
Mii::MiiData mii;
|
||||||
std::array<u8, sizeof(mii)> mii_raw;
|
std::array<u8, sizeof(mii)> mii_raw;
|
||||||
file->Read(saved_miis_offset, sizeof(mii), mii_raw.data());
|
file->Read(saved_miis_offset, sizeof(mii), mii_raw.data());
|
||||||
std::memcpy(&mii, mii_raw.data(), sizeof(mii));
|
std::memcpy(&mii, mii_raw.data(), sizeof(mii));
|
||||||
|
|
|
@ -25,7 +25,7 @@ struct MiiSelectorConfig {
|
||||||
|
|
||||||
struct MiiSelectorData {
|
struct MiiSelectorData {
|
||||||
u32 return_code;
|
u32 return_code;
|
||||||
HLE::Applets::MiiData mii;
|
Mii::MiiData mii;
|
||||||
};
|
};
|
||||||
|
|
||||||
class MiiSelector {
|
class MiiSelector {
|
||||||
|
@ -43,14 +43,14 @@ public:
|
||||||
* Stores the data so that the HLE applet in core can
|
* Stores the data so that the HLE applet in core can
|
||||||
* send this to the calling application
|
* send this to the calling application
|
||||||
*/
|
*/
|
||||||
void Finalize(u32 return_code, HLE::Applets::MiiData mii);
|
void Finalize(u32 return_code, Mii::MiiData mii);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
MiiSelectorConfig config;
|
MiiSelectorConfig config;
|
||||||
MiiSelectorData data;
|
MiiSelectorData data;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::vector<HLE::Applets::MiiData> LoadMiis();
|
std::vector<Mii::MiiData> LoadMiis();
|
||||||
|
|
||||||
class DefaultMiiSelector final : public MiiSelector {
|
class DefaultMiiSelector final : public MiiSelector {
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -71,9 +71,6 @@ void MiiSelector::Update() {
|
||||||
const MiiSelectorData& data = frontend_applet->ReceiveData();
|
const MiiSelectorData& data = frontend_applet->ReceiveData();
|
||||||
result.return_code = data.return_code;
|
result.return_code = data.return_code;
|
||||||
result.selected_mii_data = data.mii;
|
result.selected_mii_data = data.mii;
|
||||||
// Calculate the checksum of the selected Mii, see https://www.3dbrew.org/wiki/Mii#Checksum
|
|
||||||
result.mii_data_checksum = boost::crc<16, 0x1021, 0, 0, false, false>(
|
|
||||||
&result.selected_mii_data, sizeof(HLE::Applets::MiiData) + sizeof(result.unknown1));
|
|
||||||
result.selected_guest_mii_index = 0xFFFFFFFF;
|
result.selected_guest_mii_index = 0xFFFFFFFF;
|
||||||
|
|
||||||
// TODO(Subv): We're finalizing the applet immediately after it's started,
|
// TODO(Subv): We're finalizing the applet immediately after it's started,
|
||||||
|
@ -92,29 +89,31 @@ MiiResult MiiSelector::GetStandardMiiResult() {
|
||||||
// This data was obtained by writing the returned buffer in AppletManager::GlanceParameter of
|
// This data was obtained by writing the returned buffer in AppletManager::GlanceParameter of
|
||||||
// the LLEd Mii picker of version system version 11.8.0 to a file and then matching the values
|
// the LLEd Mii picker of version system version 11.8.0 to a file and then matching the values
|
||||||
// to the members of the MiiResult struct
|
// to the members of the MiiResult struct
|
||||||
MiiData mii_data;
|
Mii::MiiData mii_data;
|
||||||
mii_data.mii_id = 0x03001030;
|
mii_data.magic = 0x03;
|
||||||
|
mii_data.mii_options.raw = 0x00;
|
||||||
|
mii_data.mii_pos.raw = 0x10;
|
||||||
|
mii_data.console_identity.raw = 0x30;
|
||||||
mii_data.system_id = 0xD285B6B300C8850A;
|
mii_data.system_id = 0xD285B6B300C8850A;
|
||||||
mii_data.specialness_and_creation_date = 0x98391EE4;
|
mii_data.mii_id = 0x98391EE4;
|
||||||
mii_data.creator_mac = {0x40, 0xF4, 0x07, 0xB7, 0x37, 0x10};
|
mii_data.mac = {0x40, 0xF4, 0x07, 0xB7, 0x37, 0x10};
|
||||||
mii_data.padding = 0x0;
|
mii_data.pad = 0x0000;
|
||||||
mii_data.mii_information = 0xA600;
|
mii_data.mii_details.raw = 0xA600;
|
||||||
mii_data.mii_name = {'C', 'i', 't', 'r', 'a', 0x0, 0x0, 0x0, 0x0, 0x0};
|
mii_data.mii_name = {'C', 'i', 't', 'r', 'a', 0x0, 0x0, 0x0, 0x0, 0x0};
|
||||||
mii_data.width_height = 0x4040;
|
mii_data.height = 0x40;
|
||||||
mii_data.appearance_bits1.raw = 0x0;
|
mii_data.width = 0x40;
|
||||||
mii_data.appearance_bits2.raw = 0x0;
|
mii_data.face_style.raw = 0x00;
|
||||||
|
mii_data.face_details.raw = 0x00;
|
||||||
mii_data.hair_style = 0x21;
|
mii_data.hair_style = 0x21;
|
||||||
mii_data.appearance_bits3.hair_color.Assign(0x1);
|
mii_data.hair_details.raw = 0x01;
|
||||||
mii_data.appearance_bits3.flip_hair.Assign(0x0);
|
mii_data.eye_details.raw = 0x02684418;
|
||||||
mii_data.unknown1 = 0x02684418;
|
mii_data.eyebrow_details.raw = 0x26344614;
|
||||||
mii_data.appearance_bits4.eyebrow_style.Assign(0x6);
|
mii_data.nose_details.raw = 0x8112;
|
||||||
mii_data.appearance_bits4.eyebrow_color.Assign(0x1);
|
mii_data.mouth_details.raw = 0x1768;
|
||||||
mii_data.appearance_bits5.eyebrow_scale.Assign(0x4);
|
mii_data.mustache_details.raw = 0x0D00;
|
||||||
mii_data.appearance_bits5.eyebrow_yscale.Assign(0x3);
|
mii_data.beard_details.raw = 0x0029;
|
||||||
mii_data.appearance_bits6 = 0x4614;
|
mii_data.glasses_details.raw = 0x0052;
|
||||||
mii_data.unknown2 = 0x81121768;
|
mii_data.mole_details.raw = 0x4850;
|
||||||
mii_data.allow_copying = 0x0D;
|
|
||||||
mii_data.unknown3 = {0x0, 0x0, 0x29, 0x0, 0x52, 0x48, 0x50};
|
|
||||||
mii_data.author_name = {'f', 'l', 'T', 'o', 'b', 'i', 0x0, 0x0, 0x0, 0x0};
|
mii_data.author_name = {'f', 'l', 'T', 'o', 'b', 'i', 0x0, 0x0, 0x0, 0x0};
|
||||||
|
|
||||||
MiiResult result;
|
MiiResult result;
|
||||||
|
@ -122,8 +121,6 @@ MiiResult MiiSelector::GetStandardMiiResult() {
|
||||||
result.is_guest_mii_selected = 0x0;
|
result.is_guest_mii_selected = 0x0;
|
||||||
result.selected_guest_mii_index = 0xFFFFFFFF;
|
result.selected_guest_mii_index = 0xFFFFFFFF;
|
||||||
result.selected_mii_data = mii_data;
|
result.selected_mii_data = mii_data;
|
||||||
result.unknown1 = 0x0;
|
|
||||||
result.mii_data_checksum = 0x056C;
|
|
||||||
result.guest_mii_name.fill(0x0);
|
result.guest_mii_name.fill(0x0);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "core/hle/applets/applet.h"
|
#include "core/hle/applets/applet.h"
|
||||||
#include "core/hle/kernel/shared_memory.h"
|
#include "core/hle/kernel/shared_memory.h"
|
||||||
|
#include "core/hle/mii.h"
|
||||||
#include "core/hle/result.h"
|
#include "core/hle/result.h"
|
||||||
#include "core/hle/service/apt/apt.h"
|
#include "core/hle/service/apt/apt.h"
|
||||||
|
|
||||||
|
@ -44,65 +45,11 @@ ASSERT_REG_POSITION(initially_selected_mii_index, 0x90);
|
||||||
ASSERT_REG_POSITION(guest_mii_whitelist, 0x94);
|
ASSERT_REG_POSITION(guest_mii_whitelist, 0x94);
|
||||||
#undef ASSERT_REG_POSITION
|
#undef ASSERT_REG_POSITION
|
||||||
|
|
||||||
#pragma pack(push, 1)
|
|
||||||
struct MiiData {
|
|
||||||
u32_be mii_id;
|
|
||||||
u64_be system_id;
|
|
||||||
u32_be specialness_and_creation_date;
|
|
||||||
std::array<u8, 0x6> creator_mac;
|
|
||||||
u16_be padding;
|
|
||||||
u16_be mii_information;
|
|
||||||
std::array<u16_le, 0xA> mii_name;
|
|
||||||
u16_be width_height;
|
|
||||||
union {
|
|
||||||
u8 raw;
|
|
||||||
|
|
||||||
BitField<0, 1, u8> disable_sharing;
|
|
||||||
BitField<1, 4, u8> face_shape;
|
|
||||||
BitField<5, 3, u8> skin_color;
|
|
||||||
} appearance_bits1;
|
|
||||||
union {
|
|
||||||
u8 raw;
|
|
||||||
|
|
||||||
BitField<0, 4, u8> wrinkles;
|
|
||||||
BitField<4, 4, u8> makeup;
|
|
||||||
} appearance_bits2;
|
|
||||||
u8 hair_style;
|
|
||||||
union {
|
|
||||||
u8 raw;
|
|
||||||
|
|
||||||
BitField<0, 3, u8> hair_color;
|
|
||||||
BitField<3, 1, u8> flip_hair;
|
|
||||||
} appearance_bits3;
|
|
||||||
u32_be unknown1;
|
|
||||||
union {
|
|
||||||
u8 raw;
|
|
||||||
|
|
||||||
BitField<0, 5, u8> eyebrow_style;
|
|
||||||
BitField<5, 3, u8> eyebrow_color;
|
|
||||||
} appearance_bits4;
|
|
||||||
union {
|
|
||||||
u8 raw;
|
|
||||||
|
|
||||||
BitField<0, 4, u8> eyebrow_scale;
|
|
||||||
BitField<4, 3, u8> eyebrow_yscale;
|
|
||||||
} appearance_bits5;
|
|
||||||
u16_be appearance_bits6;
|
|
||||||
u32_be unknown2;
|
|
||||||
u8 allow_copying;
|
|
||||||
std::array<u8, 0x7> unknown3;
|
|
||||||
std::array<u16_le, 0xA> author_name;
|
|
||||||
};
|
|
||||||
static_assert(sizeof(MiiData) == 0x5C, "MiiData structure has incorrect size");
|
|
||||||
#pragma pack(pop)
|
|
||||||
|
|
||||||
struct MiiResult {
|
struct MiiResult {
|
||||||
u32_be return_code;
|
u32_be return_code;
|
||||||
u32_be is_guest_mii_selected;
|
u32_be is_guest_mii_selected;
|
||||||
u32_be selected_guest_mii_index;
|
u32_be selected_guest_mii_index;
|
||||||
MiiData selected_mii_data;
|
Mii::ChecksummedMiiData selected_mii_data;
|
||||||
u16_be unknown1;
|
|
||||||
u16_be mii_data_checksum;
|
|
||||||
std::array<u16_le, 0xC> guest_mii_name;
|
std::array<u16_le, 0xC> guest_mii_name;
|
||||||
};
|
};
|
||||||
static_assert(sizeof(MiiResult) == 0x84, "MiiResult structure has incorrect size");
|
static_assert(sizeof(MiiResult) == 0x84, "MiiResult structure has incorrect size");
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
// Copyright 2023 Citra Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <boost/crc.hpp>
|
||||||
|
#include "core/hle/mii.h"
|
||||||
|
|
||||||
|
namespace Mii {
|
||||||
|
u16 ChecksummedMiiData::CalculateChecksum() {
|
||||||
|
// Calculate the checksum of the selected Mii, see https://www.3dbrew.org/wiki/Mii#Checksum
|
||||||
|
return boost::crc<16, 0x1021, 0, 0, false, false>(this, offsetof(ChecksummedMiiData, crc16));
|
||||||
|
}
|
||||||
|
} // namespace Mii
|
|
@ -0,0 +1,239 @@
|
||||||
|
// Copyright 2023 Citra Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <boost/serialization/base_object.hpp>
|
||||||
|
#include <boost/serialization/binary_object.hpp>
|
||||||
|
#include "common/bit_field.h"
|
||||||
|
#include "common/common_types.h"
|
||||||
|
|
||||||
|
namespace Mii {
|
||||||
|
|
||||||
|
#pragma pack(push, 1)
|
||||||
|
// Reference: https://github.com/devkitPro/libctru/blob/master/libctru/include/3ds/mii.h
|
||||||
|
struct MiiData {
|
||||||
|
u8 magic; ///< Always 3?
|
||||||
|
|
||||||
|
/// Mii options
|
||||||
|
union {
|
||||||
|
u8 raw;
|
||||||
|
|
||||||
|
BitField<0, 1, u8> allow_copying; ///< True if copying is allowed
|
||||||
|
BitField<1, 1, u8> is_private_name; ///< Private name?
|
||||||
|
BitField<2, 2, u8> region_lock; ///< Region lock (0=no lock, 1=JPN, 2=USA, 3=EUR)
|
||||||
|
BitField<4, 2, u8> char_set; ///< Character set (0=JPN+USA+EUR, 1=CHN, 2=KOR, 3=TWN)
|
||||||
|
} mii_options;
|
||||||
|
|
||||||
|
/// Mii position in Mii selector or Mii maker
|
||||||
|
union {
|
||||||
|
u8 raw;
|
||||||
|
|
||||||
|
BitField<0, 4, u8> page_index; ///< Page index of Mii
|
||||||
|
BitField<4, 4, u8> slot_index; ///< Slot offset of Mii on its Page
|
||||||
|
} mii_pos;
|
||||||
|
|
||||||
|
/// Console Identity
|
||||||
|
union {
|
||||||
|
u8 raw;
|
||||||
|
|
||||||
|
BitField<0, 4, u8> unknown0; ///< Mabye padding (always seems to be 0)?
|
||||||
|
BitField<4, 3, u8>
|
||||||
|
origin_console; ///< Console that the Mii was created on (1=WII, 2=DSI, 3=3DS)
|
||||||
|
} console_identity;
|
||||||
|
|
||||||
|
u64_be system_id; ///< Identifies the system that the Mii was created on (Determines pants)
|
||||||
|
u32_be mii_id; ///< ID of Mii
|
||||||
|
std::array<u8, 6> mac; ///< Creator's system's full MAC address
|
||||||
|
u16 pad; ///< Padding
|
||||||
|
|
||||||
|
/// Mii details
|
||||||
|
union {
|
||||||
|
u16_be raw;
|
||||||
|
|
||||||
|
BitField<0, 1, u16> sex; ///< Sex of Mii (False=Male, True=Female)
|
||||||
|
BitField<1, 4, u16> bday_month; ///< Month of Mii's birthday
|
||||||
|
BitField<5, 5, u16> bday_day; ///< Day of Mii's birthday
|
||||||
|
BitField<10, 4, u16> shirt_color; ///< Color of Mii's shirt
|
||||||
|
BitField<14, 1, u16> favorite; ///< Whether the Mii is one of your 10 favorite Mii's
|
||||||
|
} mii_details;
|
||||||
|
|
||||||
|
std::array<u16_le, 10> mii_name; ///< Name of Mii (Encoded using UTF16)
|
||||||
|
u8 height; ///< How tall the Mii is
|
||||||
|
u8 width; ///< How wide the Mii is
|
||||||
|
|
||||||
|
/// Face style
|
||||||
|
union {
|
||||||
|
u8 raw;
|
||||||
|
|
||||||
|
BitField<0, 1, u8> disable_sharing; ///< Whether or not Sharing of the Mii is allowed
|
||||||
|
BitField<1, 4, u8> shape; ///< Face shape
|
||||||
|
BitField<5, 3, u8> skin_color; ///< Color of skin
|
||||||
|
} face_style;
|
||||||
|
|
||||||
|
/// Face details
|
||||||
|
union {
|
||||||
|
u8 raw;
|
||||||
|
|
||||||
|
BitField<0, 4, u8> wrinkles;
|
||||||
|
BitField<4, 4, u8> makeup;
|
||||||
|
} face_details;
|
||||||
|
|
||||||
|
u8 hair_style;
|
||||||
|
|
||||||
|
/// Hair details
|
||||||
|
union {
|
||||||
|
u8 raw;
|
||||||
|
|
||||||
|
BitField<0, 3, u8> color;
|
||||||
|
BitField<3, 1, u8> flip;
|
||||||
|
} hair_details;
|
||||||
|
|
||||||
|
/// Eye details
|
||||||
|
union {
|
||||||
|
u32_be raw;
|
||||||
|
|
||||||
|
BitField<0, 6, u32> style;
|
||||||
|
BitField<6, 3, u32> color;
|
||||||
|
BitField<9, 4, u32> scale;
|
||||||
|
BitField<13, 3, u32> y_scale;
|
||||||
|
BitField<16, 5, u32> rotation;
|
||||||
|
BitField<21, 4, u32> x_spacing;
|
||||||
|
BitField<25, 5, u32> y_position;
|
||||||
|
} eye_details;
|
||||||
|
|
||||||
|
/// Eyebrow details
|
||||||
|
union {
|
||||||
|
u32_be raw;
|
||||||
|
|
||||||
|
BitField<0, 5, u32> style;
|
||||||
|
BitField<5, 3, u32> color;
|
||||||
|
BitField<8, 4, u32> scale;
|
||||||
|
BitField<12, 3, u32> y_scale;
|
||||||
|
BitField<15, 1, u32> pad;
|
||||||
|
BitField<16, 5, u32> rotation;
|
||||||
|
BitField<21, 4, u32> x_spacing;
|
||||||
|
BitField<25, 5, u32> y_position;
|
||||||
|
} eyebrow_details;
|
||||||
|
|
||||||
|
/// Nose details
|
||||||
|
union {
|
||||||
|
u16_be raw;
|
||||||
|
|
||||||
|
BitField<0, 5, u16> style;
|
||||||
|
BitField<5, 4, u16> scale;
|
||||||
|
BitField<9, 5, u16> y_position;
|
||||||
|
} nose_details;
|
||||||
|
|
||||||
|
/// Mouth details
|
||||||
|
union {
|
||||||
|
u16_be raw;
|
||||||
|
|
||||||
|
BitField<0, 6, u16> style;
|
||||||
|
BitField<6, 3, u16> color;
|
||||||
|
BitField<9, 4, u16> scale;
|
||||||
|
BitField<13, 3, u16> y_scale;
|
||||||
|
} mouth_details;
|
||||||
|
|
||||||
|
/// Mustache details
|
||||||
|
union {
|
||||||
|
u16_be raw;
|
||||||
|
|
||||||
|
BitField<0, 5, u16> mouth_yposition;
|
||||||
|
BitField<5, 3, u16> mustach_style;
|
||||||
|
BitField<8, 2, u16> pad;
|
||||||
|
} mustache_details;
|
||||||
|
|
||||||
|
/// Beard details
|
||||||
|
union {
|
||||||
|
u16_be raw;
|
||||||
|
|
||||||
|
BitField<0, 3, u16> style;
|
||||||
|
BitField<3, 3, u16> color;
|
||||||
|
BitField<6, 4, u16> scale;
|
||||||
|
BitField<10, 5, u16> y_pos;
|
||||||
|
} beard_details;
|
||||||
|
|
||||||
|
/// Glasses details
|
||||||
|
union {
|
||||||
|
u16_be raw;
|
||||||
|
|
||||||
|
BitField<0, 4, u16> style;
|
||||||
|
BitField<4, 3, u16> color;
|
||||||
|
BitField<7, 4, u16> scale;
|
||||||
|
BitField<11, 5, u16> y_pos;
|
||||||
|
} glasses_details;
|
||||||
|
|
||||||
|
/// Mole details
|
||||||
|
union {
|
||||||
|
u16_be raw;
|
||||||
|
|
||||||
|
BitField<0, 1, u16> enable;
|
||||||
|
BitField<1, 5, u16> scale;
|
||||||
|
BitField<6, 5, u16> x_pos;
|
||||||
|
BitField<11, 5, u16> y_pos;
|
||||||
|
} mole_details;
|
||||||
|
|
||||||
|
std::array<u16_le, 10> author_name; ///< Name of Mii's author (Encoded using UTF16)
|
||||||
|
private:
|
||||||
|
template <class Archive>
|
||||||
|
void serialize(Archive& ar, const unsigned int) {
|
||||||
|
ar& boost::serialization::make_binary_object(this, sizeof(MiiData));
|
||||||
|
}
|
||||||
|
friend class boost::serialization::access;
|
||||||
|
};
|
||||||
|
|
||||||
|
static_assert(sizeof(MiiData) == 0x5C, "MiiData structure has incorrect size");
|
||||||
|
static_assert(std::is_trivial_v<MiiData>, "MiiData must be trivial.");
|
||||||
|
static_assert(std::is_trivially_copyable_v<MiiData>, "MiiData must be trivially copyable.");
|
||||||
|
|
||||||
|
struct ChecksummedMiiData {
|
||||||
|
private:
|
||||||
|
MiiData mii_data;
|
||||||
|
u16 padding;
|
||||||
|
u16_be crc16;
|
||||||
|
|
||||||
|
public:
|
||||||
|
ChecksummedMiiData& operator=(const MiiData& data) {
|
||||||
|
mii_data = data;
|
||||||
|
padding = 0;
|
||||||
|
FixChecksum();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
ChecksummedMiiData& operator=(MiiData&& data) {
|
||||||
|
mii_data = std::move(data);
|
||||||
|
padding = 0;
|
||||||
|
FixChecksum();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
operator MiiData() const {
|
||||||
|
return mii_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsChecksumValid() {
|
||||||
|
return crc16 == CalculateChecksum();
|
||||||
|
}
|
||||||
|
|
||||||
|
u16 CalculateChecksum();
|
||||||
|
|
||||||
|
void FixChecksum() {
|
||||||
|
crc16 = CalculateChecksum();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
template <class Archive>
|
||||||
|
void serialize(Archive& ar, const unsigned int) {
|
||||||
|
ar& boost::serialization::make_binary_object(this, sizeof(ChecksummedMiiData));
|
||||||
|
}
|
||||||
|
friend class boost::serialization::access;
|
||||||
|
};
|
||||||
|
#pragma pack(pop)
|
||||||
|
static_assert(sizeof(ChecksummedMiiData) == 0x60,
|
||||||
|
"ChecksummedMiiData structure has incorrect size");
|
||||||
|
static_assert(std::is_trivial_v<ChecksummedMiiData>, "ChecksummedMiiData must be trivial.");
|
||||||
|
static_assert(std::is_trivially_copyable_v<ChecksummedMiiData>,
|
||||||
|
"ChecksummedMiiData must be trivially copyable.");
|
||||||
|
} // namespace Mii
|
|
@ -86,8 +86,6 @@ NTAG215File NfcDataToEncodedData(const EncryptedNTAG215File& nfc_data) {
|
||||||
encoded_data.amiibo_version = nfc_data.user_memory.amiibo_version;
|
encoded_data.amiibo_version = nfc_data.user_memory.amiibo_version;
|
||||||
encoded_data.settings = nfc_data.user_memory.settings;
|
encoded_data.settings = nfc_data.user_memory.settings;
|
||||||
encoded_data.owner_mii = nfc_data.user_memory.owner_mii;
|
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_id = nfc_data.user_memory.application_id;
|
||||||
encoded_data.application_write_counter = nfc_data.user_memory.application_write_counter;
|
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_area_id = nfc_data.user_memory.application_area_id;
|
||||||
|
@ -123,8 +121,6 @@ EncryptedNTAG215File EncodedDataToNfcData(const NTAG215File& encoded_data) {
|
||||||
nfc_data.user_memory.amiibo_version = encoded_data.amiibo_version;
|
nfc_data.user_memory.amiibo_version = encoded_data.amiibo_version;
|
||||||
nfc_data.user_memory.settings = encoded_data.settings;
|
nfc_data.user_memory.settings = encoded_data.settings;
|
||||||
nfc_data.user_memory.owner_mii = encoded_data.owner_mii;
|
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_id = encoded_data.application_id;
|
||||||
nfc_data.user_memory.application_write_counter = encoded_data.application_write_counter;
|
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_area_id = encoded_data.application_area_id;
|
||||||
|
|
|
@ -539,7 +539,6 @@ ResultCode NfcDevice::GetRegisterInfo(RegisterInfo& register_info) const {
|
||||||
// TODO: Validate this data
|
// TODO: Validate this data
|
||||||
register_info = {
|
register_info = {
|
||||||
.mii_data = tag.file.owner_mii,
|
.mii_data = tag.file.owner_mii,
|
||||||
.owner_mii_aes_ccm = tag.file.owner_mii_aes_ccm,
|
|
||||||
.amiibo_name = settings.amiibo_name,
|
.amiibo_name = settings.amiibo_name,
|
||||||
.flags = static_cast<u8>(settings.settings.raw & 0xf),
|
.flags = static_cast<u8>(settings.settings.raw & 0xf),
|
||||||
.font_region = settings.country_code_id,
|
.font_region = settings.country_code_id,
|
||||||
|
@ -628,8 +627,7 @@ ResultCode NfcDevice::DeleteRegisterInfo() {
|
||||||
}
|
}
|
||||||
|
|
||||||
CryptoPP::AutoSeededRandomPool rng;
|
CryptoPP::AutoSeededRandomPool rng;
|
||||||
const std::size_t mii_data_size =
|
const std::size_t mii_data_size = sizeof(tag.file.owner_mii);
|
||||||
sizeof(tag.file.owner_mii) + sizeof(tag.file.padding) + sizeof(tag.file.owner_mii_aes_ccm);
|
|
||||||
std::array<CryptoPP::byte, mii_data_size> buffer{};
|
std::array<CryptoPP::byte, mii_data_size> buffer{};
|
||||||
rng.GenerateBlock(buffer.data(), mii_data_size);
|
rng.GenerateBlock(buffer.data(), mii_data_size);
|
||||||
|
|
||||||
|
@ -664,12 +662,9 @@ ResultCode NfcDevice::SetRegisterInfoPrivate(const RegisterInfoPrivate& register
|
||||||
settings.write_date = GetAmiiboDate();
|
settings.write_date = GetAmiiboDate();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate mii CRC with the padding
|
|
||||||
tag.file.owner_mii_aes_ccm = boost::crc<16, 0x1021, 0, 0, false, false>(
|
|
||||||
®ister_info.mii_data, sizeof(HLE::Applets::MiiData) + sizeof(u16));
|
|
||||||
|
|
||||||
settings.amiibo_name = register_info.amiibo_name;
|
settings.amiibo_name = register_info.amiibo_name;
|
||||||
tag.file.owner_mii = register_info.mii_data;
|
tag.file.owner_mii = register_info.mii_data;
|
||||||
|
tag.file.owner_mii.FixChecksum();
|
||||||
tag.file.mii_extension = {};
|
tag.file.mii_extension = {};
|
||||||
tag.file.unknown = 0;
|
tag.file.unknown = 0;
|
||||||
tag.file.unknown2 = {};
|
tag.file.unknown2 = {};
|
||||||
|
@ -1061,9 +1056,7 @@ void NfcDevice::UpdateSettingsCrc() {
|
||||||
void NfcDevice::UpdateRegisterInfoCrc() {
|
void NfcDevice::UpdateRegisterInfoCrc() {
|
||||||
#pragma pack(push, 1)
|
#pragma pack(push, 1)
|
||||||
struct CrcData {
|
struct CrcData {
|
||||||
HLE::Applets::MiiData mii;
|
Mii::ChecksummedMiiData mii;
|
||||||
INSERT_PADDING_BYTES(0x2);
|
|
||||||
u16 mii_crc;
|
|
||||||
u8 application_id_byte;
|
u8 application_id_byte;
|
||||||
u8 unknown;
|
u8 unknown;
|
||||||
u64 mii_extension;
|
u64 mii_extension;
|
||||||
|
@ -1074,7 +1067,6 @@ void NfcDevice::UpdateRegisterInfoCrc() {
|
||||||
|
|
||||||
const CrcData crc_data{
|
const CrcData crc_data{
|
||||||
.mii = tag.file.owner_mii,
|
.mii = tag.file.owner_mii,
|
||||||
.mii_crc = tag.file.owner_mii_aes_ccm,
|
|
||||||
.application_id_byte = tag.file.application_id_byte,
|
.application_id_byte = tag.file.application_id_byte,
|
||||||
.unknown = tag.file.unknown,
|
.unknown = tag.file.unknown,
|
||||||
.mii_extension = tag.file.mii_extension,
|
.mii_extension = tag.file.mii_extension,
|
||||||
|
@ -1102,8 +1094,6 @@ void NfcDevice::BuildAmiiboWithoutKeys() {
|
||||||
settings.settings.font_region.Assign(0);
|
settings.settings.font_region.Assign(0);
|
||||||
settings.init_date = GetAmiiboDate();
|
settings.init_date = GetAmiiboDate();
|
||||||
tag.file.owner_mii = default_mii.selected_mii_data;
|
tag.file.owner_mii = default_mii.selected_mii_data;
|
||||||
tag.file.padding = default_mii.unknown1;
|
|
||||||
tag.file.owner_mii_aes_ccm = default_mii.mii_data_checksum;
|
|
||||||
|
|
||||||
// Admin info
|
// Admin info
|
||||||
settings.settings.amiibo_initialized.Assign(1);
|
settings.settings.amiibo_initialized.Assign(1);
|
||||||
|
|
|
@ -269,20 +269,18 @@ static_assert(sizeof(NTAG215Password) == 0x8, "NTAG215Password is an invalid siz
|
||||||
|
|
||||||
#pragma pack(1)
|
#pragma pack(1)
|
||||||
struct EncryptedAmiiboFile {
|
struct EncryptedAmiiboFile {
|
||||||
u8 constant_value; // Must be A5
|
u8 constant_value; // Must be A5
|
||||||
u16_be write_counter; // Number of times the amiibo has been written?
|
u16_be write_counter; // Number of times the amiibo has been written?
|
||||||
u8 amiibo_version; // Amiibo file version
|
u8 amiibo_version; // Amiibo file version
|
||||||
AmiiboSettings settings; // Encrypted amiibo settings
|
AmiiboSettings settings; // Encrypted amiibo settings
|
||||||
HashData hmac_tag; // Hash
|
HashData hmac_tag; // Hash
|
||||||
AmiiboModelInfo model_info; // Encrypted amiibo model info
|
AmiiboModelInfo model_info; // Encrypted amiibo model info
|
||||||
HashData keygen_salt; // Salt
|
HashData keygen_salt; // Salt
|
||||||
HashData hmac_data; // Hash
|
HashData hmac_data; // Hash
|
||||||
HLE::Applets::MiiData owner_mii; // Encrypted Mii data
|
Mii::ChecksummedMiiData owner_mii; // Encrypted Mii data
|
||||||
u16 padding; // Mii Padding
|
u64_be application_id; // Encrypted Game id
|
||||||
u16_be owner_mii_aes_ccm; // Mii data AES-CCM MAC
|
u16_be application_write_counter; // Encrypted Counter
|
||||||
u64_be application_id; // Encrypted Game id
|
u32_be application_area_id; // Encrypted Game id
|
||||||
u16_be application_write_counter; // Encrypted Counter
|
|
||||||
u32_be application_area_id; // Encrypted Game id
|
|
||||||
u8 application_id_byte;
|
u8 application_id_byte;
|
||||||
u8 unknown;
|
u8 unknown;
|
||||||
u64 mii_extension;
|
u64 mii_extension;
|
||||||
|
@ -301,11 +299,9 @@ struct NTAG215File {
|
||||||
u16_be write_counter; // Number of times the amiibo has been written?
|
u16_be write_counter; // Number of times the amiibo has been written?
|
||||||
u8 amiibo_version; // Amiibo file version
|
u8 amiibo_version; // Amiibo file version
|
||||||
AmiiboSettings settings;
|
AmiiboSettings settings;
|
||||||
HLE::Applets::MiiData owner_mii; // Mii data
|
Mii::ChecksummedMiiData owner_mii; // Mii data
|
||||||
u16 padding; // Mii Padding
|
u64_be application_id; // Game id
|
||||||
u16_be owner_mii_aes_ccm; // Mii data AES-CCM MAC
|
u16_be application_write_counter; // Counter
|
||||||
u64_be application_id; // Game id
|
|
||||||
u16_be application_write_counter; // Counter
|
|
||||||
u32_be application_area_id;
|
u32_be application_area_id;
|
||||||
u8 application_id_byte;
|
u8 application_id_byte;
|
||||||
u8 unknown;
|
u8 unknown;
|
||||||
|
@ -417,9 +413,7 @@ struct ModelInfo {
|
||||||
static_assert(sizeof(ModelInfo) == 0x36, "ModelInfo is an invalid size");
|
static_assert(sizeof(ModelInfo) == 0x36, "ModelInfo is an invalid size");
|
||||||
|
|
||||||
struct RegisterInfo {
|
struct RegisterInfo {
|
||||||
HLE::Applets::MiiData mii_data;
|
Mii::ChecksummedMiiData mii_data;
|
||||||
INSERT_PADDING_BYTES(0x2);
|
|
||||||
u16_be owner_mii_aes_ccm; // Mii data AES-CCM MAC
|
|
||||||
AmiiboName amiibo_name;
|
AmiiboName amiibo_name;
|
||||||
INSERT_PADDING_BYTES(0x2); // Zero string terminator
|
INSERT_PADDING_BYTES(0x2); // Zero string terminator
|
||||||
u8 flags;
|
u8 flags;
|
||||||
|
@ -430,9 +424,7 @@ struct RegisterInfo {
|
||||||
static_assert(sizeof(RegisterInfo) == 0xA8, "RegisterInfo is an invalid size");
|
static_assert(sizeof(RegisterInfo) == 0xA8, "RegisterInfo is an invalid size");
|
||||||
|
|
||||||
struct RegisterInfoPrivate {
|
struct RegisterInfoPrivate {
|
||||||
HLE::Applets::MiiData mii_data;
|
Mii::ChecksummedMiiData mii_data;
|
||||||
INSERT_PADDING_BYTES(0x2);
|
|
||||||
u16_be owner_mii_aes_ccm; // Mii data AES-CCM MAC
|
|
||||||
AmiiboName amiibo_name;
|
AmiiboName amiibo_name;
|
||||||
INSERT_PADDING_BYTES(0x2); // Zero string terminator
|
INSERT_PADDING_BYTES(0x2); // Zero string terminator
|
||||||
u8 flags;
|
u8 flags;
|
||||||
|
|
Reference in New Issue