Add Yuzu NFC types header, clean up headers

This commit is contained in:
wheremyfoodat 2023-09-09 15:06:04 +03:00
parent 139ea16fda
commit 679eb0f7bc
6 changed files with 428 additions and 5 deletions

View file

@ -1,15 +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;
bool loaded = false;
std::array<u8, tagSize> raw;
void loadFromRaw();
void reset();
};

View file

@ -0,0 +1,411 @@
// Copyright 2022 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#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

718
include/swap.hpp Normal file
View file

@ -0,0 +1,718 @@
// Copyright (c) 2012- PPSSPP Project / Dolphin Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#pragma once
#include <type_traits>
#if defined(_MSC_VER)
#include <cstdlib>
#endif
#include <cstring>
#include "helpers.hpp"
// GCC
#ifdef __GNUC__
#if __BYTE_ORDER__ && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) && !defined(COMMON_LITTLE_ENDIAN)
#define COMMON_LITTLE_ENDIAN 1
#elif __BYTE_ORDER__ && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) && !defined(COMMON_BIG_ENDIAN)
#define COMMON_BIG_ENDIAN 1
#endif
// LLVM/clang
#elif defined(__clang__)
#if __LITTLE_ENDIAN__ && !defined(COMMON_LITTLE_ENDIAN)
#define COMMON_LITTLE_ENDIAN 1
#elif __BIG_ENDIAN__ && !defined(COMMON_BIG_ENDIAN)
#define COMMON_BIG_ENDIAN 1
#endif
// MSVC
#elif defined(_MSC_VER) && !defined(COMMON_BIG_ENDIAN) && !defined(COMMON_LITTLE_ENDIAN)
#define COMMON_LITTLE_ENDIAN 1
#endif
// Worst case, default to little endian.
#if !COMMON_BIG_ENDIAN && !COMMON_LITTLE_ENDIAN
#define COMMON_LITTLE_ENDIAN 1
#endif
namespace Common {
#ifdef _MSC_VER
[[nodiscard]] inline u16 swap16(u16 data) noexcept {
return _byteswap_ushort(data);
}
[[nodiscard]] inline u32 swap32(u32 data) noexcept {
return _byteswap_ulong(data);
}
[[nodiscard]] inline u64 swap64(u64 data) noexcept {
return _byteswap_uint64(data);
}
#elif defined(__clang__) || defined(__GNUC__)
#if defined(__Bitrig__) || defined(__OpenBSD__)
// redefine swap16, swap32, swap64 as inline functions
#undef swap16
#undef swap32
#undef swap64
#endif
[[nodiscard]] inline u16 swap16(u16 data) noexcept {
return __builtin_bswap16(data);
}
[[nodiscard]] inline u32 swap32(u32 data) noexcept {
return __builtin_bswap32(data);
}
[[nodiscard]] inline u64 swap64(u64 data) noexcept {
return __builtin_bswap64(data);
}
#else
// Generic implementation.
[[nodiscard]] inline u16 swap16(u16 data) noexcept {
return (data >> 8) | (data << 8);
}
[[nodiscard]] inline u32 swap32(u32 data) noexcept {
return ((data & 0xFF000000U) >> 24) | ((data & 0x00FF0000U) >> 8) |
((data & 0x0000FF00U) << 8) | ((data & 0x000000FFU) << 24);
}
[[nodiscard]] inline u64 swap64(u64 data) noexcept {
return ((data & 0xFF00000000000000ULL) >> 56) | ((data & 0x00FF000000000000ULL) >> 40) |
((data & 0x0000FF0000000000ULL) >> 24) | ((data & 0x000000FF00000000ULL) >> 8) |
((data & 0x00000000FF000000ULL) << 8) | ((data & 0x0000000000FF0000ULL) << 24) |
((data & 0x000000000000FF00ULL) << 40) | ((data & 0x00000000000000FFULL) << 56);
}
#endif
[[nodiscard]] inline float swapf(float f) noexcept {
static_assert(sizeof(u32) == sizeof(float), "float must be the same size as uint32_t.");
u32 value;
std::memcpy(&value, &f, sizeof(u32));
value = swap32(value);
std::memcpy(&f, &value, sizeof(u32));
return f;
}
[[nodiscard]] inline double swapd(double f) noexcept {
static_assert(sizeof(u64) == sizeof(double), "double must be the same size as uint64_t.");
u64 value;
std::memcpy(&value, &f, sizeof(u64));
value = swap64(value);
std::memcpy(&f, &value, sizeof(u64));
return f;
}
} // Namespace Common
template <typename T, typename F>
struct swap_struct_t {
using swapped_t = swap_struct_t;
protected:
T value;
static T swap(T v) {
return F::swap(v);
}
public:
T swap() const {
return swap(value);
}
swap_struct_t() = default;
swap_struct_t(const T& v) : value(swap(v)) {}
template <typename S>
swapped_t& operator=(const S& source) {
value = swap(static_cast<T>(source));
return *this;
}
operator s8() const {
return static_cast<s8>(swap());
}
operator u8() const {
return static_cast<u8>(swap());
}
operator s16() const {
return static_cast<s16>(swap());
}
operator u16() const {
return static_cast<u16>(swap());
}
operator s32() const {
return static_cast<s32>(swap());
}
operator u32() const {
return static_cast<u32>(swap());
}
operator s64() const {
return static_cast<s64>(swap());
}
operator u64() const {
return static_cast<u64>(swap());
}
operator float() const {
return static_cast<float>(swap());
}
operator double() const {
return static_cast<double>(swap());
}
// +v
swapped_t operator+() const {
return +swap();
}
// -v
swapped_t operator-() const {
return -swap();
}
// v / 5
swapped_t operator/(const swapped_t& i) const {
return swap() / i.swap();
}
template <typename S>
swapped_t operator/(const S& i) const {
return swap() / i;
}
// v * 5
swapped_t operator*(const swapped_t& i) const {
return swap() * i.swap();
}
template <typename S>
swapped_t operator*(const S& i) const {
return swap() * i;
}
// v + 5
swapped_t operator+(const swapped_t& i) const {
return swap() + i.swap();
}
template <typename S>
swapped_t operator+(const S& i) const {
return swap() + static_cast<T>(i);
}
// v - 5
swapped_t operator-(const swapped_t& i) const {
return swap() - i.swap();
}
template <typename S>
swapped_t operator-(const S& i) const {
return swap() - static_cast<T>(i);
}
// v += 5
swapped_t& operator+=(const swapped_t& i) {
value = swap(swap() + i.swap());
return *this;
}
template <typename S>
swapped_t& operator+=(const S& i) {
value = swap(swap() + static_cast<T>(i));
return *this;
}
// v -= 5
swapped_t& operator-=(const swapped_t& i) {
value = swap(swap() - i.swap());
return *this;
}
template <typename S>
swapped_t& operator-=(const S& i) {
value = swap(swap() - static_cast<T>(i));
return *this;
}
// ++v
swapped_t& operator++() {
value = swap(swap() + 1);
return *this;
}
// --v
swapped_t& operator--() {
value = swap(swap() - 1);
return *this;
}
// v++
swapped_t operator++(int) {
swapped_t old = *this;
value = swap(swap() + 1);
return old;
}
// v--
swapped_t operator--(int) {
swapped_t old = *this;
value = swap(swap() - 1);
return old;
}
// Comparaison
// v == i
bool operator==(const swapped_t& i) const {
return swap() == i.swap();
}
template <typename S>
bool operator==(const S& i) const {
return swap() == i;
}
// v != i
bool operator!=(const swapped_t& i) const {
return swap() != i.swap();
}
template <typename S>
bool operator!=(const S& i) const {
return swap() != i;
}
// v > i
bool operator>(const swapped_t& i) const {
return swap() > i.swap();
}
template <typename S>
bool operator>(const S& i) const {
return swap() > i;
}
// v < i
bool operator<(const swapped_t& i) const {
return swap() < i.swap();
}
template <typename S>
bool operator<(const S& i) const {
return swap() < i;
}
// v >= i
bool operator>=(const swapped_t& i) const {
return swap() >= i.swap();
}
template <typename S>
bool operator>=(const S& i) const {
return swap() >= i;
}
// v <= i
bool operator<=(const swapped_t& i) const {
return swap() <= i.swap();
}
template <typename S>
bool operator<=(const S& i) const {
return swap() <= i;
}
// logical
swapped_t operator!() const {
return !swap();
}
// bitmath
swapped_t operator~() const {
return ~swap();
}
swapped_t operator&(const swapped_t& b) const {
return swap() & b.swap();
}
template <typename S>
swapped_t operator&(const S& b) const {
return swap() & b;
}
swapped_t& operator&=(const swapped_t& b) {
value = swap(swap() & b.swap());
return *this;
}
template <typename S>
swapped_t& operator&=(const S b) {
value = swap(swap() & b);
return *this;
}
swapped_t operator|(const swapped_t& b) const {
return swap() | b.swap();
}
template <typename S>
swapped_t operator|(const S& b) const {
return swap() | b;
}
swapped_t& operator|=(const swapped_t& b) {
value = swap(swap() | b.swap());
return *this;
}
template <typename S>
swapped_t& operator|=(const S& b) {
value = swap(swap() | b);
return *this;
}
swapped_t operator^(const swapped_t& b) const {
return swap() ^ b.swap();
}
template <typename S>
swapped_t operator^(const S& b) const {
return swap() ^ b;
}
swapped_t& operator^=(const swapped_t& b) {
value = swap(swap() ^ b.swap());
return *this;
}
template <typename S>
swapped_t& operator^=(const S& b) {
value = swap(swap() ^ b);
return *this;
}
template <typename S>
swapped_t operator<<(const S& b) const {
return swap() << b;
}
template <typename S>
swapped_t& operator<<=(const S& b) const {
value = swap(swap() << b);
return *this;
}
template <typename S>
swapped_t operator>>(const S& b) const {
return swap() >> b;
}
template <typename S>
swapped_t& operator>>=(const S& b) const {
value = swap(swap() >> b);
return *this;
}
// Member
/** todo **/
// Arithmetics
template <typename S, typename T2, typename F2>
friend S operator+(const S& p, const swapped_t v);
template <typename S, typename T2, typename F2>
friend S operator-(const S& p, const swapped_t v);
template <typename S, typename T2, typename F2>
friend S operator/(const S& p, const swapped_t v);
template <typename S, typename T2, typename F2>
friend S operator*(const S& p, const swapped_t v);
template <typename S, typename T2, typename F2>
friend S operator%(const S& p, const swapped_t v);
// Arithmetics + assignments
template <typename S, typename T2, typename F2>
friend S operator+=(const S& p, const swapped_t v);
template <typename S, typename T2, typename F2>
friend S operator-=(const S& p, const swapped_t v);
// Bitmath
template <typename S, typename T2, typename F2>
friend S operator&(const S& p, const swapped_t v);
// Comparison
template <typename S, typename T2, typename F2>
friend bool operator<(const S& p, const swapped_t v);
template <typename S, typename T2, typename F2>
friend bool operator>(const S& p, const swapped_t v);
template <typename S, typename T2, typename F2>
friend bool operator<=(const S& p, const swapped_t v);
template <typename S, typename T2, typename F2>
friend bool operator>=(const S& p, const swapped_t v);
template <typename S, typename T2, typename F2>
friend bool operator!=(const S& p, const swapped_t v);
template <typename S, typename T2, typename F2>
friend bool operator==(const S& p, const swapped_t v);
};
// Arithmetics
template <typename S, typename T, typename F>
S operator+(const S& i, const swap_struct_t<T, F> v) {
return i + v.swap();
}
template <typename S, typename T, typename F>
S operator-(const S& i, const swap_struct_t<T, F> v) {
return i - v.swap();
}
template <typename S, typename T, typename F>
S operator/(const S& i, const swap_struct_t<T, F> v) {
return i / v.swap();
}
template <typename S, typename T, typename F>
S operator*(const S& i, const swap_struct_t<T, F> v) {
return i * v.swap();
}
template <typename S, typename T, typename F>
S operator%(const S& i, const swap_struct_t<T, F> v) {
return i % v.swap();
}
// Arithmetics + assignments
template <typename S, typename T, typename F>
S& operator+=(S& i, const swap_struct_t<T, F> v) {
i += v.swap();
return i;
}
template <typename S, typename T, typename F>
S& operator-=(S& i, const swap_struct_t<T, F> v) {
i -= v.swap();
return i;
}
// Logical
template <typename S, typename T, typename F>
S operator&(const S& i, const swap_struct_t<T, F> v) {
return i & v.swap();
}
template <typename S, typename T, typename F>
S operator&(const swap_struct_t<T, F> v, const S& i) {
return static_cast<S>(v.swap() & i);
}
// Comparaison
template <typename S, typename T, typename F>
bool operator<(const S& p, const swap_struct_t<T, F> v) {
return p < v.swap();
}
template <typename S, typename T, typename F>
bool operator>(const S& p, const swap_struct_t<T, F> v) {
return p > v.swap();
}
template <typename S, typename T, typename F>
bool operator<=(const S& p, const swap_struct_t<T, F> v) {
return p <= v.swap();
}
template <typename S, typename T, typename F>
bool operator>=(const S& p, const swap_struct_t<T, F> v) {
return p >= v.swap();
}
template <typename S, typename T, typename F>
bool operator!=(const S& p, const swap_struct_t<T, F> v) {
return p != v.swap();
}
template <typename S, typename T, typename F>
bool operator==(const S& p, const swap_struct_t<T, F> v) {
return p == v.swap();
}
template <typename T>
struct swap_64_t {
static T swap(T x) {
return static_cast<T>(Common::swap64(x));
}
};
template <typename T>
struct swap_32_t {
static T swap(T x) {
return static_cast<T>(Common::swap32(x));
}
};
template <typename T>
struct swap_16_t {
static T swap(T x) {
return static_cast<T>(Common::swap16(x));
}
};
template <typename T>
struct swap_float_t {
static T swap(T x) {
return static_cast<T>(Common::swapf(x));
}
};
template <typename T>
struct swap_double_t {
static T swap(T x) {
return static_cast<T>(Common::swapd(x));
}
};
template <typename T>
struct swap_enum_t {
static_assert(std::is_enum_v<T>);
using base = std::underlying_type_t<T>;
public:
swap_enum_t() = default;
swap_enum_t(const T& v) : value(swap(v)) {}
swap_enum_t& operator=(const T& v) {
value = swap(v);
return *this;
}
operator T() const {
return swap(value);
}
explicit operator base() const {
return static_cast<base>(swap(value));
}
protected:
T value{};
// clang-format off
using swap_t = std::conditional_t<
std::is_same_v<base, u16>, swap_16_t<u16>, std::conditional_t<
std::is_same_v<base, s16>, swap_16_t<s16>, std::conditional_t<
std::is_same_v<base, u32>, swap_32_t<u32>, std::conditional_t<
std::is_same_v<base, s32>, swap_32_t<s32>, std::conditional_t<
std::is_same_v<base, u64>, swap_64_t<u64>, std::conditional_t<
std::is_same_v<base, s64>, swap_64_t<s64>, void>>>>>>;
// clang-format on
static T swap(T x) {
return static_cast<T>(swap_t::swap(static_cast<base>(x)));
}
};
struct SwapTag {}; // Use the different endianness from the system
struct KeepTag {}; // Use the same endianness as the system
template <typename T, typename Tag>
struct AddEndian;
// KeepTag specializations
template <typename T>
struct AddEndian<T, KeepTag> {
using type = T;
};
// SwapTag specializations
template <>
struct AddEndian<u8, SwapTag> {
using type = u8;
};
template <>
struct AddEndian<u16, SwapTag> {
using type = swap_struct_t<u16, swap_16_t<u16>>;
};
template <>
struct AddEndian<u32, SwapTag> {
using type = swap_struct_t<u32, swap_32_t<u32>>;
};
template <>
struct AddEndian<u64, SwapTag> {
using type = swap_struct_t<u64, swap_64_t<u64>>;
};
template <>
struct AddEndian<s8, SwapTag> {
using type = s8;
};
template <>
struct AddEndian<s16, SwapTag> {
using type = swap_struct_t<s16, swap_16_t<s16>>;
};
template <>
struct AddEndian<s32, SwapTag> {
using type = swap_struct_t<s32, swap_32_t<s32>>;
};
template <>
struct AddEndian<s64, SwapTag> {
using type = swap_struct_t<s64, swap_64_t<s64>>;
};
template <>
struct AddEndian<float, SwapTag> {
using type = swap_struct_t<float, swap_float_t<float>>;
};
template <>
struct AddEndian<double, SwapTag> {
using type = swap_struct_t<double, swap_double_t<double>>;
};
template <typename T>
struct AddEndian<T, SwapTag> {
static_assert(std::is_enum_v<T>);
using type = swap_enum_t<T>;
};
// Alias LETag/BETag as KeepTag/SwapTag depending on the system
#if COMMON_LITTLE_ENDIAN
using LETag = KeepTag;
using BETag = SwapTag;
#else
using BETag = KeepTag;
using LETag = SwapTag;
#endif
// Aliases for LE types
using u16_le = AddEndian<u16, LETag>::type;
using u32_le = AddEndian<u32, LETag>::type;
using u64_le = AddEndian<u64, LETag>::type;
using s16_le = AddEndian<s16, LETag>::type;
using s32_le = AddEndian<s32, LETag>::type;
using s64_le = AddEndian<s64, LETag>::type;
template <typename T>
using enum_le = std::enable_if_t<std::is_enum_v<T>, typename AddEndian<T, LETag>::type>;
using float_le = AddEndian<float, LETag>::type;
using double_le = AddEndian<double, LETag>::type;
// Aliases for BE types
using u16_be = AddEndian<u16, BETag>::type;
using u32_be = AddEndian<u32, BETag>::type;
using u64_be = AddEndian<u64, BETag>::type;
using s16_be = AddEndian<s16, BETag>::type;
using s32_be = AddEndian<s32, BETag>::type;
using s64_be = AddEndian<s64, BETag>::type;
template <typename T>
using enum_be = std::enable_if_t<std::is_enum_v<T>, typename AddEndian<T, BETag>::type>;
using float_be = AddEndian<float, BETag>::type;
using double_be = AddEndian<double, BETag>::type;