Merge pull request #4244 from wwylele/swap-enum
common/swap: add swap template for enum
This commit is contained in:
		@@ -17,6 +17,8 @@
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <type_traits>
 | 
			
		||||
 | 
			
		||||
#if defined(_MSC_VER)
 | 
			
		||||
#include <cstdlib>
 | 
			
		||||
#elif defined(__linux__)
 | 
			
		||||
@@ -605,6 +607,44 @@ struct swap_double_t {
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
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)));
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#if COMMON_LITTLE_ENDIAN
 | 
			
		||||
using u16_le = u16;
 | 
			
		||||
using u32_le = u32;
 | 
			
		||||
@@ -614,6 +654,9 @@ using s16_le = s16;
 | 
			
		||||
using s32_le = s32;
 | 
			
		||||
using s64_le = s64;
 | 
			
		||||
 | 
			
		||||
template <typename T>
 | 
			
		||||
using enum_le = std::enable_if_t<std::is_enum_v<T>, T>;
 | 
			
		||||
 | 
			
		||||
using float_le = float;
 | 
			
		||||
using double_le = double;
 | 
			
		||||
 | 
			
		||||
@@ -626,6 +669,9 @@ using s32_be = swap_struct_t<s32, swap_32_t<s32>>;
 | 
			
		||||
using u16_be = swap_struct_t<u16, swap_16_t<u16>>;
 | 
			
		||||
using s16_be = swap_struct_t<s16, swap_16_t<s16>>;
 | 
			
		||||
 | 
			
		||||
template <typename T>
 | 
			
		||||
using enum_be = swap_enum_t<T>;
 | 
			
		||||
 | 
			
		||||
using float_be = swap_struct_t<float, swap_float_t<float>>;
 | 
			
		||||
using double_be = swap_struct_t<double, swap_double_t<double>>;
 | 
			
		||||
#else
 | 
			
		||||
@@ -639,6 +685,9 @@ using s32_le = swap_struct_t<s32, swap_32_t<s32>>;
 | 
			
		||||
using u16_le = swap_struct_t<u16, swap_16_t<u16>>;
 | 
			
		||||
using s16_le = swap_struct_t<s16, swap_16_t<s16>>;
 | 
			
		||||
 | 
			
		||||
template <typename T>
 | 
			
		||||
using enum_le = swap_enum_t<T>;
 | 
			
		||||
 | 
			
		||||
using float_le = swap_struct_t<float, swap_float_t<float>>;
 | 
			
		||||
using double_le = swap_struct_t<double, swap_double_t<double>>;
 | 
			
		||||
 | 
			
		||||
@@ -650,6 +699,9 @@ using s16_be = s16;
 | 
			
		||||
using s32_be = s32;
 | 
			
		||||
using s64_be = s64;
 | 
			
		||||
 | 
			
		||||
template <typename T>
 | 
			
		||||
using enum_be = std::enable_if_t<std::is_enum_v<T>, T>;
 | 
			
		||||
 | 
			
		||||
using float_be = float;
 | 
			
		||||
using double_be = double;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -12,6 +12,7 @@
 | 
			
		||||
#include "common/file_util.h"
 | 
			
		||||
#include "common/logging/log.h"
 | 
			
		||||
#include "common/string_util.h"
 | 
			
		||||
#include "common/swap.h"
 | 
			
		||||
#include "core/core.h"
 | 
			
		||||
#include "core/file_sys/archive_ncch.h"
 | 
			
		||||
#include "core/file_sys/errors.h"
 | 
			
		||||
@@ -36,9 +37,9 @@ struct NCCHArchivePath {
 | 
			
		||||
static_assert(sizeof(NCCHArchivePath) == 0x10, "NCCHArchivePath has wrong size!");
 | 
			
		||||
 | 
			
		||||
struct NCCHFilePath {
 | 
			
		||||
    u32_le open_type;
 | 
			
		||||
    enum_le<NCCHFileOpenType> open_type;
 | 
			
		||||
    u32_le content_index;
 | 
			
		||||
    u32_le filepath_type;
 | 
			
		||||
    enum_le<NCCHFilePathType> filepath_type;
 | 
			
		||||
    std::array<char, 8> exefs_filepath;
 | 
			
		||||
};
 | 
			
		||||
static_assert(sizeof(NCCHFilePath) == 0x14, "NCCHFilePath has wrong size!");
 | 
			
		||||
@@ -56,9 +57,9 @@ Path MakeNCCHArchivePath(u64 tid, Service::FS::MediaType media_type) {
 | 
			
		||||
Path MakeNCCHFilePath(NCCHFileOpenType open_type, u32 content_index, NCCHFilePathType filepath_type,
 | 
			
		||||
                      std::array<char, 8>& exefs_filepath) {
 | 
			
		||||
    NCCHFilePath path;
 | 
			
		||||
    path.open_type = static_cast<u32_le>(open_type);
 | 
			
		||||
    path.open_type = open_type;
 | 
			
		||||
    path.content_index = static_cast<u32_le>(content_index);
 | 
			
		||||
    path.filepath_type = static_cast<u32_le>(filepath_type);
 | 
			
		||||
    path.filepath_type = filepath_type;
 | 
			
		||||
    path.exefs_filepath = exefs_filepath;
 | 
			
		||||
    std::vector<u8> file(sizeof(path));
 | 
			
		||||
    std::memcpy(&file[0], &path, sizeof(path));
 | 
			
		||||
@@ -89,15 +90,14 @@ ResultVal<std::unique_ptr<FileBackend>> NCCHArchive::OpenFile(const Path& path,
 | 
			
		||||
    std::unique_ptr<FileBackend> file;
 | 
			
		||||
 | 
			
		||||
    // NCCH RomFS
 | 
			
		||||
    NCCHFilePathType filepath_type = static_cast<NCCHFilePathType>(openfile_path.filepath_type);
 | 
			
		||||
    if (filepath_type == NCCHFilePathType::RomFS) {
 | 
			
		||||
    if (openfile_path.filepath_type == NCCHFilePathType::RomFS) {
 | 
			
		||||
        std::shared_ptr<RomFSReader> romfs_file;
 | 
			
		||||
 | 
			
		||||
        result = ncch_container.ReadRomFS(romfs_file);
 | 
			
		||||
        std::unique_ptr<DelayGenerator> delay_generator = std::make_unique<RomFSDelayGenerator>();
 | 
			
		||||
        file = std::make_unique<IVFCFile>(std::move(romfs_file), std::move(delay_generator));
 | 
			
		||||
    } else if (filepath_type == NCCHFilePathType::Code ||
 | 
			
		||||
               filepath_type == NCCHFilePathType::ExeFS) {
 | 
			
		||||
    } else if (openfile_path.filepath_type == NCCHFilePathType::Code ||
 | 
			
		||||
               openfile_path.filepath_type == NCCHFilePathType::ExeFS) {
 | 
			
		||||
        std::vector<u8> buffer;
 | 
			
		||||
 | 
			
		||||
        // Load NCCH .code or icon/banner/logo
 | 
			
		||||
@@ -105,7 +105,8 @@ ResultVal<std::unique_ptr<FileBackend>> NCCHArchive::OpenFile(const Path& path,
 | 
			
		||||
        std::unique_ptr<DelayGenerator> delay_generator = std::make_unique<ExeFSDelayGenerator>();
 | 
			
		||||
        file = std::make_unique<NCCHFile>(std::move(buffer), std::move(delay_generator));
 | 
			
		||||
    } else {
 | 
			
		||||
        LOG_ERROR(Service_FS, "Unknown NCCH archive type {}!", openfile_path.filepath_type);
 | 
			
		||||
        LOG_ERROR(Service_FS, "Unknown NCCH archive type {}!",
 | 
			
		||||
                  static_cast<u32>(openfile_path.filepath_type));
 | 
			
		||||
        result = Loader::ResultStatus::Error;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -25,7 +25,7 @@ enum class SelfNCCHFilePathType : u32 {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct SelfNCCHFilePath {
 | 
			
		||||
    u32_le type;
 | 
			
		||||
    enum_le<SelfNCCHFilePathType> type;
 | 
			
		||||
    std::array<char, 8> exefs_filename;
 | 
			
		||||
};
 | 
			
		||||
static_assert(sizeof(SelfNCCHFilePath) == 12, "NCCHFilePath has wrong size!");
 | 
			
		||||
@@ -102,7 +102,7 @@ public:
 | 
			
		||||
        SelfNCCHFilePath file_path;
 | 
			
		||||
        std::memcpy(&file_path, binary.data(), sizeof(SelfNCCHFilePath));
 | 
			
		||||
 | 
			
		||||
        switch (static_cast<SelfNCCHFilePathType>(file_path.type)) {
 | 
			
		||||
        switch (file_path.type) {
 | 
			
		||||
        case SelfNCCHFilePathType::UpdateRomFS:
 | 
			
		||||
            return OpenUpdateRomFS();
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -110,7 +110,7 @@ void SoftwareKeyboard::Update() {
 | 
			
		||||
        break;
 | 
			
		||||
    default:
 | 
			
		||||
        LOG_CRITICAL(Applet_SWKBD, "Unknown button config {}",
 | 
			
		||||
                     static_cast<int>(config.num_buttons_m1));
 | 
			
		||||
                     static_cast<u32>(config.num_buttons_m1));
 | 
			
		||||
        UNREACHABLE();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -143,8 +143,9 @@ Frontend::KeyboardConfig SoftwareKeyboard::ToFrontendConfig(
 | 
			
		||||
    const SoftwareKeyboardConfig& config) const {
 | 
			
		||||
    using namespace Frontend;
 | 
			
		||||
    KeyboardConfig frontend_config;
 | 
			
		||||
    frontend_config.button_config = static_cast<ButtonConfig>(config.num_buttons_m1);
 | 
			
		||||
    frontend_config.accept_mode = static_cast<AcceptedInput>(config.valid_input);
 | 
			
		||||
    frontend_config.button_config =
 | 
			
		||||
        static_cast<ButtonConfig>(static_cast<u32>(config.num_buttons_m1));
 | 
			
		||||
    frontend_config.accept_mode = static_cast<AcceptedInput>(static_cast<u32>(config.valid_input));
 | 
			
		||||
    frontend_config.multiline_mode = config.multiline;
 | 
			
		||||
    frontend_config.max_text_length = config.max_text_length;
 | 
			
		||||
    frontend_config.max_digits = config.max_digits;
 | 
			
		||||
 
 | 
			
		||||
@@ -121,20 +121,20 @@ enum class SoftwareKeyboardResult : s32 {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct SoftwareKeyboardConfig {
 | 
			
		||||
    SoftwareKeyboardType type;
 | 
			
		||||
    SoftwareKeyboardButtonConfig num_buttons_m1;
 | 
			
		||||
    SoftwareKeyboardValidInput valid_input;
 | 
			
		||||
    SoftwareKeyboardPasswordMode password_mode;
 | 
			
		||||
    s32 is_parental_screen;
 | 
			
		||||
    s32 darken_top_screen;
 | 
			
		||||
    u32 filter_flags;
 | 
			
		||||
    u32 save_state_flags;
 | 
			
		||||
    u16 max_text_length;
 | 
			
		||||
    u16 dict_word_count;
 | 
			
		||||
    u16 max_digits;
 | 
			
		||||
    std::array<std::array<u16, MAX_BUTTON_TEXT_LEN + 1>, MAX_BUTTON> button_text;
 | 
			
		||||
    std::array<u16, 2> numpad_keys;
 | 
			
		||||
    std::array<u16, MAX_HINT_TEXT_LEN + 1>
 | 
			
		||||
    enum_le<SoftwareKeyboardType> type;
 | 
			
		||||
    enum_le<SoftwareKeyboardButtonConfig> num_buttons_m1;
 | 
			
		||||
    enum_le<SoftwareKeyboardValidInput> valid_input;
 | 
			
		||||
    enum_le<SoftwareKeyboardPasswordMode> password_mode;
 | 
			
		||||
    s32_le is_parental_screen;
 | 
			
		||||
    s32_le darken_top_screen;
 | 
			
		||||
    u32_le filter_flags;
 | 
			
		||||
    u32_le save_state_flags;
 | 
			
		||||
    u16_le max_text_length;
 | 
			
		||||
    u16_le dict_word_count;
 | 
			
		||||
    u16_le max_digits;
 | 
			
		||||
    std::array<std::array<u16_le, MAX_BUTTON_TEXT_LEN + 1>, MAX_BUTTON> button_text;
 | 
			
		||||
    std::array<u16_le, 2> numpad_keys;
 | 
			
		||||
    std::array<u16_le, MAX_HINT_TEXT_LEN + 1>
 | 
			
		||||
        hint_text; ///< Text to display when asking the user for input
 | 
			
		||||
    bool predictive_input;
 | 
			
		||||
    bool multiline;
 | 
			
		||||
@@ -145,25 +145,25 @@ struct SoftwareKeyboardConfig {
 | 
			
		||||
    bool unknown;
 | 
			
		||||
    bool default_qwerty;
 | 
			
		||||
    std::array<bool, 4> button_submits_text;
 | 
			
		||||
    u16 language;
 | 
			
		||||
    u16_le language;
 | 
			
		||||
 | 
			
		||||
    u32 initial_text_offset; ///< Offset of the default text in the output SharedMemory
 | 
			
		||||
    u32 dict_offset;
 | 
			
		||||
    u32 initial_status_offset;
 | 
			
		||||
    u32 initial_learning_offset;
 | 
			
		||||
    u32 shared_memory_size; ///< Size of the SharedMemory
 | 
			
		||||
    u32 version;
 | 
			
		||||
    u32_le initial_text_offset; ///< Offset of the default text in the output SharedMemory
 | 
			
		||||
    u32_le dict_offset;
 | 
			
		||||
    u32_le initial_status_offset;
 | 
			
		||||
    u32_le initial_learning_offset;
 | 
			
		||||
    u32_le shared_memory_size; ///< Size of the SharedMemory
 | 
			
		||||
    u32_le version;
 | 
			
		||||
 | 
			
		||||
    SoftwareKeyboardResult return_code;
 | 
			
		||||
    enum_le<SoftwareKeyboardResult> return_code;
 | 
			
		||||
 | 
			
		||||
    u32 status_offset;
 | 
			
		||||
    u32 learning_offset;
 | 
			
		||||
    u32_le status_offset;
 | 
			
		||||
    u32_le learning_offset;
 | 
			
		||||
 | 
			
		||||
    u32 text_offset; ///< Offset in the SharedMemory where the output text starts
 | 
			
		||||
    u16 text_length; ///< Length in characters of the output text
 | 
			
		||||
    u32_le text_offset; ///< Offset in the SharedMemory where the output text starts
 | 
			
		||||
    u16_le text_length; ///< Length in characters of the output text
 | 
			
		||||
 | 
			
		||||
    int callback_result;
 | 
			
		||||
    std::array<u16, MAX_CALLBACK_MSG_LEN + 1> callback_msg;
 | 
			
		||||
    s32_le callback_result;
 | 
			
		||||
    std::array<u16_le, MAX_CALLBACK_MSG_LEN + 1> callback_msg;
 | 
			
		||||
    bool skip_at_check;
 | 
			
		||||
    INSERT_PADDING_BYTES(0xAB);
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -235,7 +235,7 @@ private:
 | 
			
		||||
    struct SegmentEntry {
 | 
			
		||||
        u32_le offset;
 | 
			
		||||
        u32_le size;
 | 
			
		||||
        SegmentType type;
 | 
			
		||||
        enum_le<SegmentType> type;
 | 
			
		||||
 | 
			
		||||
        static constexpr HeaderField TABLE_OFFSET_FIELD = SegmentTableOffset;
 | 
			
		||||
    };
 | 
			
		||||
 
 | 
			
		||||
@@ -14,7 +14,7 @@ constexpr u16 DefaultExtraCapabilities = 0x0431;
 | 
			
		||||
 | 
			
		||||
std::vector<u8> GenerateAuthenticationFrame(AuthenticationSeq seq) {
 | 
			
		||||
    AuthenticationFrame frame{};
 | 
			
		||||
    frame.auth_seq = static_cast<u16>(seq);
 | 
			
		||||
    frame.auth_seq = seq;
 | 
			
		||||
 | 
			
		||||
    std::vector<u8> data(sizeof(frame));
 | 
			
		||||
    std::memcpy(data.data(), &frame, sizeof(frame));
 | 
			
		||||
@@ -26,7 +26,7 @@ AuthenticationSeq GetAuthenticationSeqNumber(const std::vector<u8>& body) {
 | 
			
		||||
    AuthenticationFrame frame;
 | 
			
		||||
    std::memcpy(&frame, body.data(), sizeof(frame));
 | 
			
		||||
 | 
			
		||||
    return static_cast<AuthenticationSeq>(frame.auth_seq);
 | 
			
		||||
    return frame.auth_seq;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -57,7 +57,7 @@ static std::vector<u8> GenerateSSIDTag(u32 network_id) {
 | 
			
		||||
std::vector<u8> GenerateAssocResponseFrame(AssocStatus status, u16 association_id, u32 network_id) {
 | 
			
		||||
    AssociationResponseFrame frame{};
 | 
			
		||||
    frame.capabilities = DefaultExtraCapabilities;
 | 
			
		||||
    frame.status_code = static_cast<u16>(status);
 | 
			
		||||
    frame.status_code = status;
 | 
			
		||||
    // The association id is ORed with this magic value (0xC000)
 | 
			
		||||
    constexpr u16 AssociationIdMagic = 0xC000;
 | 
			
		||||
    frame.assoc_id = association_id | AssociationIdMagic;
 | 
			
		||||
@@ -79,8 +79,7 @@ std::tuple<AssocStatus, u16> GetAssociationResult(const std::vector<u8>& body) {
 | 
			
		||||
    memcpy(&frame, body.data(), sizeof(frame));
 | 
			
		||||
 | 
			
		||||
    constexpr u16 AssociationIdMask = 0x3FFF;
 | 
			
		||||
    return std::make_tuple(static_cast<AssocStatus>(frame.status_code),
 | 
			
		||||
                           frame.assoc_id & AssociationIdMask);
 | 
			
		||||
    return std::make_tuple(frame.status_code, frame.assoc_id & AssociationIdMask);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace Service::NWM
 | 
			
		||||
 
 | 
			
		||||
@@ -22,16 +22,16 @@ enum class AuthStatus : u16 { Successful = 0 };
 | 
			
		||||
enum class AssocStatus : u16 { Successful = 0 };
 | 
			
		||||
 | 
			
		||||
struct AuthenticationFrame {
 | 
			
		||||
    u16_le auth_algorithm = static_cast<u16>(AuthAlgorithm::OpenSystem);
 | 
			
		||||
    u16_le auth_seq;
 | 
			
		||||
    u16_le status_code = static_cast<u16>(AuthStatus::Successful);
 | 
			
		||||
    enum_le<AuthAlgorithm> auth_algorithm = AuthAlgorithm::OpenSystem;
 | 
			
		||||
    enum_le<AuthenticationSeq> auth_seq;
 | 
			
		||||
    enum_le<AuthStatus> status_code = AuthStatus::Successful;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static_assert(sizeof(AuthenticationFrame) == 6, "AuthenticationFrame has wrong size");
 | 
			
		||||
 | 
			
		||||
struct AssociationResponseFrame {
 | 
			
		||||
    u16_le capabilities;
 | 
			
		||||
    u16_le status_code;
 | 
			
		||||
    enum_le<AssocStatus> status_code;
 | 
			
		||||
    u16_le assoc_id;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -25,7 +25,7 @@ using MacAddress = std::array<u8, 6>;
 | 
			
		||||
 */
 | 
			
		||||
static std::vector<u8> GenerateLLCHeader(EtherType protocol) {
 | 
			
		||||
    LLCHeader header{};
 | 
			
		||||
    header.protocol = static_cast<u16>(protocol);
 | 
			
		||||
    header.protocol = protocol;
 | 
			
		||||
 | 
			
		||||
    std::vector<u8> buffer(sizeof(header));
 | 
			
		||||
    memcpy(buffer.data(), &header, sizeof(header));
 | 
			
		||||
@@ -312,9 +312,7 @@ std::vector<u8> GenerateEAPoLStartFrame(u16 association_id, const NodeInfo& node
 | 
			
		||||
EtherType GetFrameEtherType(const std::vector<u8>& frame) {
 | 
			
		||||
    LLCHeader header;
 | 
			
		||||
    std::memcpy(&header, frame.data(), sizeof(header));
 | 
			
		||||
 | 
			
		||||
    u16 ethertype = header.protocol;
 | 
			
		||||
    return static_cast<EtherType>(ethertype);
 | 
			
		||||
    return header.protocol;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
u16 GetEAPoLFrameType(const std::vector<u8>& frame) {
 | 
			
		||||
 
 | 
			
		||||
@@ -25,11 +25,11 @@ enum class EtherType : u16 { SecureData = 0x876D, EAPoL = 0x888E };
 | 
			
		||||
 * and the OUI is always 0.
 | 
			
		||||
 */
 | 
			
		||||
struct LLCHeader {
 | 
			
		||||
    u8 dsap = static_cast<u8>(SAP::SNAPExtensionUsed);
 | 
			
		||||
    u8 ssap = static_cast<u8>(SAP::SNAPExtensionUsed);
 | 
			
		||||
    u8 control = static_cast<u8>(PDUControl::UnnumberedInformation);
 | 
			
		||||
    SAP dsap = SAP::SNAPExtensionUsed;
 | 
			
		||||
    SAP ssap = SAP::SNAPExtensionUsed;
 | 
			
		||||
    PDUControl control = PDUControl::UnnumberedInformation;
 | 
			
		||||
    std::array<u8, 3> OUI = {};
 | 
			
		||||
    u16_be protocol;
 | 
			
		||||
    enum_be<EtherType> protocol;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static_assert(sizeof(LLCHeader) == 8, "LLCHeader has the wrong size");
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user