Compare commits
4 Commits
d01b2cfb3e
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| b79cec3f8f | |||
| e7c5ae0beb | |||
| e3c1389791 | |||
| 8a3be3b67f |
26
.vscode/tasks.json
vendored
Normal file
26
.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
||||||
|
// for the documentation about the tasks.json format
|
||||||
|
"version": "2.0.0",
|
||||||
|
"tasks": [
|
||||||
|
{
|
||||||
|
"label": "build",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "msbuild",
|
||||||
|
"args": [
|
||||||
|
// Ask msbuild to generate full paths for file names.
|
||||||
|
"/property:GenerateFullPaths=true",
|
||||||
|
"/t:build",
|
||||||
|
// Do not generate summary otherwise it leads to duplicate errors in Problems panel
|
||||||
|
"/consoleloggerparameters:NoSummary"
|
||||||
|
],
|
||||||
|
"group": "build",
|
||||||
|
"presentation": {
|
||||||
|
// Reveal the output only if unrecognized errors occur.
|
||||||
|
"reveal": "silent"
|
||||||
|
},
|
||||||
|
// Use the standard MS compiler pattern to detect errors, warnings and infos
|
||||||
|
"problemMatcher": "$msCompile"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -2,8 +2,104 @@
|
|||||||
|
|
||||||
using namespace esxr;
|
using namespace esxr;
|
||||||
|
|
||||||
|
Node read_unknown_record(std::istream &in);
|
||||||
|
|
||||||
[[nodiscard]] Header esxr::read_header(std::istream &in)
|
[[nodiscard]] Header esxr::read_header(std::istream &in)
|
||||||
{
|
{
|
||||||
return read_header_impl<directly_readable>(in);
|
return read_header_impl<directly_readable>(in);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] std::size_t data_size(const RecordHeader& header)
|
||||||
|
{
|
||||||
|
return header.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] std::size_t data_size(const GroupHeader& header)
|
||||||
|
{
|
||||||
|
return header.size - header_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] std::streamsize data_ssize(const RecordHeader& header)
|
||||||
|
{
|
||||||
|
return static_cast<std::streamsize>(data_size(header));
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] std::streamsize data_ssize(const GroupHeader& header)
|
||||||
|
{
|
||||||
|
return static_cast<std::streamsize>(data_size(header));
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] std::size_t node_size(const RecordNode& node)
|
||||||
|
{
|
||||||
|
return node.header.size + header_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] std::size_t node_size(const GroupNode& node)
|
||||||
|
{
|
||||||
|
return node.header.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] std::streamsize node_ssize(const RecordNode& node)
|
||||||
|
{
|
||||||
|
return static_cast<std::streamsize>(node_size(node));
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] std::streamsize node_ssize(const GroupNode& node)
|
||||||
|
{
|
||||||
|
return static_cast<std::streamsize>(node_size(node));
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] std::streamsize node_ssize(const Node& node)
|
||||||
|
{
|
||||||
|
return std::visit(utility::overloaded{
|
||||||
|
[](const GroupNode &node) { return node_ssize(node); },
|
||||||
|
[](const RecordNode &node) { return node_ssize(node); },
|
||||||
|
}, node);
|
||||||
|
}
|
||||||
|
|
||||||
|
Node read_record(RecordHeader header, std::istream &in)
|
||||||
|
{
|
||||||
|
std::vector<char> data(data_size(header));
|
||||||
|
in.read(data.data(), data_ssize(header));
|
||||||
|
return { RecordNode{ header, std::move(data) } };
|
||||||
|
}
|
||||||
|
|
||||||
|
Node read_group(GroupHeader header, std::istream &in)
|
||||||
|
{
|
||||||
|
std::vector<Node> children{};
|
||||||
|
auto remaining = data_ssize(header);
|
||||||
|
while (remaining > 0) {
|
||||||
|
children.emplace_back(read_unknown_record(in));
|
||||||
|
remaining -= node_ssize(children.back());
|
||||||
|
}
|
||||||
|
if (remaining < 0)
|
||||||
|
throw std::runtime_error("Read past end of group data.");
|
||||||
|
return { GroupNode{ header, std::move(children) } };
|
||||||
|
}
|
||||||
|
|
||||||
|
Node read_unknown_record(std::istream &in)
|
||||||
|
{
|
||||||
|
auto header_variant = read_header(in);
|
||||||
|
struct visitor {
|
||||||
|
std::istream *in_ptr;
|
||||||
|
Node operator()(RecordHeader h) {
|
||||||
|
return read_record(h, *in_ptr);
|
||||||
|
}
|
||||||
|
Node operator()(GroupHeader h) {
|
||||||
|
return read_group(h, *in_ptr);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return std::visit(visitor{ &in }, std::move(header_variant));
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] RootNode esxr::read_esx(std::istream &in, std::size_t file_size)
|
||||||
|
{
|
||||||
|
RootNode root{};
|
||||||
|
auto remaining = static_cast<std::streamsize>(file_size);
|
||||||
|
while (remaining != 0) {
|
||||||
|
root.children.emplace_back(read_unknown_record(in));
|
||||||
|
remaining -= node_ssize(root.children.back());
|
||||||
|
}
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
#ifndef ESX_READER_HPP
|
#ifndef ESX_READER_HPP
|
||||||
#define ESX_READER_HPP
|
#define ESX_READER_HPP
|
||||||
|
|
||||||
|
#pragma warning( push )
|
||||||
|
#pragma warning( disable : 5264 )
|
||||||
|
#pragma warning( disable : 5262 )
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <variant>
|
#include <variant>
|
||||||
#include <array>
|
#include <array>
|
||||||
@@ -9,6 +12,7 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
#pragma warning( pop )
|
||||||
|
|
||||||
#include "utility.hpp"
|
#include "utility.hpp"
|
||||||
|
|
||||||
@@ -18,8 +22,8 @@ namespace esxr {
|
|||||||
// Forward declarations
|
// Forward declarations
|
||||||
//
|
//
|
||||||
|
|
||||||
class GroupNode;
|
struct RecordNode;
|
||||||
class RecordNode;
|
struct GroupNode;
|
||||||
|
|
||||||
//
|
//
|
||||||
// Enumerations
|
// Enumerations
|
||||||
@@ -58,21 +62,21 @@ enum class RecordType {
|
|||||||
// Aggregate types
|
// Aggregate types
|
||||||
//
|
//
|
||||||
|
|
||||||
struct FourCC : utility::Store<uint32_t> { };
|
using FourCC = utility::Store<struct FourCCTag , uint32_t>;
|
||||||
struct FormID : utility::Store<uint32_t> { };
|
using FormID = utility::Store<struct FormIDTag , uint32_t>;
|
||||||
struct Timestamp : utility::Store<uint16_t> { };
|
using Timestamp = utility::Store<struct TimestampTag, uint16_t>;
|
||||||
struct VCInfo : utility::Store<uint16_t> { };
|
using VCInfo = utility::Store<struct VCInfoTag , uint16_t>;
|
||||||
|
|
||||||
struct Flag {
|
struct Flag {
|
||||||
RecordType type;
|
RecordType type;
|
||||||
unsigned bit;
|
unsigned bit;
|
||||||
friend constexpr auto operator<=>(const Flag &, const Flag &) noexcept = default;
|
friend constexpr auto operator<=>(const Flag &, const Flag &) = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct RefrFlag {
|
struct RefrFlag {
|
||||||
RecordType refr_type;
|
RecordType refr_type;
|
||||||
unsigned bit;
|
unsigned bit;
|
||||||
friend constexpr auto operator<=>(const RefrFlag &, const RefrFlag &) noexcept = default;
|
friend constexpr auto operator<=>(const RefrFlag &, const RefrFlag &) = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct UnknownHeader {
|
struct UnknownHeader {
|
||||||
@@ -85,14 +89,14 @@ struct GroupHeader {
|
|||||||
int16_t y, x;
|
int16_t y, x;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct TopType : utility::Store<FourCC> { };
|
using TopType = utility::Store<struct TopTypeTag , FourCC >;
|
||||||
struct ParentWorld : utility::Store<FormID> { };
|
using ParentWorld = utility::Store<struct ParentWorldTag , FormID >;
|
||||||
struct BlockNumber : utility::Store<int32_t> { };
|
using BlockNumber = utility::Store<struct BlockNumberTag , int32_t>;
|
||||||
struct SubBlockNumber : utility::Store<int32_t> { };
|
using SubBlockNumber = utility::Store<struct SubBlockNumberTag, int32_t>;
|
||||||
struct BlockCoord : utility::Store<Coord> { };
|
using BlockCoord = utility::Store<struct BlockCoordTag , Coord >;
|
||||||
struct SubBlockCoord : utility::Store<Coord> { };
|
using SubBlockCoord = utility::Store<struct SubBlockCoordTag , Coord >;
|
||||||
struct ParentCell : utility::Store<FormID> { };
|
using ParentCell = utility::Store<struct ParentCellTag , FormID >;
|
||||||
struct ParentDialogue : utility::Store<FormID> { };
|
using ParentDialogue = utility::Store<struct ParentDialogueTag, FormID >;
|
||||||
|
|
||||||
union GroupLabel {
|
union GroupLabel {
|
||||||
TopType top_type;
|
TopType top_type;
|
||||||
@@ -115,8 +119,8 @@ struct GroupHeader {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct RecordHeader {
|
struct RecordHeader {
|
||||||
struct Flags : utility::Store<uint32_t> { };
|
using Flags = utility::Store<struct FlagsTag , uint32_t>;
|
||||||
struct Version : utility::Store<uint16_t> { };
|
using Version = utility::Store<struct VersionTag, uint16_t>;
|
||||||
|
|
||||||
FourCC type;
|
FourCC type;
|
||||||
uint32_t size;
|
uint32_t size;
|
||||||
@@ -135,30 +139,40 @@ struct RecordHeader {
|
|||||||
using Node = std::variant<GroupNode, RecordNode>;
|
using Node = std::variant<GroupNode, RecordNode>;
|
||||||
using Header = std::variant<RecordHeader, GroupHeader>;
|
using Header = std::variant<RecordHeader, GroupHeader>;
|
||||||
|
|
||||||
class GroupNode {
|
struct RootNode {
|
||||||
private:
|
std::vector<Node> children;
|
||||||
GroupHeader m_header;
|
|
||||||
std::vector<Node> m_children;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class RecordNode {
|
struct GroupNode {
|
||||||
private:
|
GroupHeader header;
|
||||||
RecordHeader m_header;
|
std::vector<Node> children;
|
||||||
std::vector<std::byte> data;
|
};
|
||||||
|
|
||||||
|
struct RecordNode {
|
||||||
|
RecordHeader header;
|
||||||
|
std::vector<char> data;
|
||||||
};
|
};
|
||||||
|
|
||||||
//
|
//
|
||||||
// Free functions
|
// Free functions
|
||||||
//
|
//
|
||||||
|
|
||||||
[[nodiscard]] std::optional<std::string_view> group_type_to_name(GroupType group_type) noexcept;
|
[[nodiscard]] std::optional<std::string_view> group_type_to_name(GroupType group_type);
|
||||||
[[nodiscard]] std::optional<FourCC> record_type_to_fourcc(RecordType record_type) noexcept;
|
[[nodiscard]] std::optional<FourCC> record_type_to_fourcc(RecordType record_type);
|
||||||
[[nodiscard]] std::optional<RecordType> fourcc_to_record_type(FourCC fourcc) noexcept;
|
[[nodiscard]] std::optional<RecordType> fourcc_to_record_type(FourCC fourcc);
|
||||||
[[nodiscard]] std::optional<std::string_view> record_type_to_name(RecordType record_type) noexcept;
|
[[nodiscard]] std::optional<std::string_view> record_type_to_name(RecordType record_type);
|
||||||
[[nodiscard]] std::optional<std::string_view> flag_to_description(Flag flag) noexcept;
|
[[nodiscard]] std::optional<std::string_view> flag_to_description(Flag flag);
|
||||||
[[nodiscard]] std::optional<std::string_view> refr_flag_to_description(RefrFlag refr_flag) noexcept;
|
[[nodiscard]] std::optional<std::string_view> refr_flag_to_description(RefrFlag refr_flag);
|
||||||
|
|
||||||
[[nodiscard]] Header read_header(std::istream &in);
|
[[nodiscard]] Header read_header(std::istream &in);
|
||||||
|
[[nodiscard]] RootNode read_esx(std::istream &in, std::size_t file_size);
|
||||||
|
|
||||||
|
// Convert a compatible C string to a FourCC (e.g. "LITR")
|
||||||
|
static consteval FourCC fourcc_from_cstr(const char(&a)[5])
|
||||||
|
{
|
||||||
|
char temp[4] = { a[0], a[1], a[2], a[3] };
|
||||||
|
return std::bit_cast<FourCC, char[4]>(temp);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,8 +10,7 @@ namespace esxr {
|
|||||||
//
|
//
|
||||||
|
|
||||||
static constexpr auto esx_endianness = std::endian::little;
|
static constexpr auto esx_endianness = std::endian::little;
|
||||||
static constexpr std::streamsize header_size = 24;
|
static constexpr std::size_t header_size = 24;
|
||||||
static constexpr bool same_endianness = std::endian::native == esx_endianness;
|
|
||||||
|
|
||||||
template <typename T, size_t N>
|
template <typename T, size_t N>
|
||||||
concept packed = sizeof(T) == N;
|
concept packed = sizeof(T) == N;
|
||||||
@@ -19,8 +18,11 @@ concept packed = sizeof(T) == N;
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
concept packed_header = packed<T, static_cast<std::size_t>(header_size)>;
|
concept packed_header = packed<T, static_cast<std::size_t>(header_size)>;
|
||||||
|
|
||||||
|
template <typename T, std::endian e>
|
||||||
|
concept directly_serializable = (std::endian::native == e) && std::is_trivially_copyable_v<T> && std::is_standard_layout_v<T>;
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
concept directly_readable_header = packed_header<T> && same_endianness;
|
concept directly_readable_header = packed_header<T> && directly_serializable<T, esx_endianness>;
|
||||||
|
|
||||||
constexpr bool directly_readable = directly_readable_header<UnknownHeader> && directly_readable_header<GroupHeader> && directly_readable_header<RecordHeader>;
|
constexpr bool directly_readable = directly_readable_header<UnknownHeader> && directly_readable_header<GroupHeader> && directly_readable_header<RecordHeader>;
|
||||||
|
|
||||||
|
|||||||
@@ -15,13 +15,6 @@ static constexpr std::pair<GroupType, std::string_view> group_type_name_map_buil
|
|||||||
{GroupType::CellTemporaryChildren , "Cell Temporary Children" },
|
{GroupType::CellTemporaryChildren , "Cell Temporary Children" },
|
||||||
};
|
};
|
||||||
|
|
||||||
// Convert a compatible C string to a FourCC (e.g. "LITR")
|
|
||||||
static consteval FourCC fourcc_from_cstr(const char(&a)[5]) noexcept
|
|
||||||
{
|
|
||||||
char temp[4] = { a[0], a[1], a[2], a[3] };
|
|
||||||
return std::bit_cast<FourCC, char[4]>(temp);
|
|
||||||
}
|
|
||||||
|
|
||||||
static constexpr std::pair<RecordType, FourCC> record_type_fourcc_map_builtin[] {
|
static constexpr std::pair<RecordType, FourCC> record_type_fourcc_map_builtin[] {
|
||||||
{RecordType::AACT, fourcc_from_cstr("AACT")},
|
{RecordType::AACT, fourcc_from_cstr("AACT")},
|
||||||
{RecordType::ACHR, fourcc_from_cstr("ACHR")},
|
{RecordType::ACHR, fourcc_from_cstr("ACHR")},
|
||||||
@@ -665,31 +658,31 @@ static constexpr auto flag_description_map = utility::map_to_soa(flag_descriptio
|
|||||||
static constexpr auto refr_flag_description_map_std = utility::array_builtin_to_std(refr_flag_description_map_builtin);
|
static constexpr auto refr_flag_description_map_std = utility::array_builtin_to_std(refr_flag_description_map_builtin);
|
||||||
static constexpr auto refr_flag_description_map = utility::map_to_soa(refr_flag_description_map_std);
|
static constexpr auto refr_flag_description_map = utility::map_to_soa(refr_flag_description_map_std);
|
||||||
|
|
||||||
[[nodiscard]] std::optional<std::string_view> esxr::group_type_to_name(GroupType group_type) noexcept
|
[[nodiscard]] std::optional<std::string_view> esxr::group_type_to_name(GroupType group_type)
|
||||||
{
|
{
|
||||||
return utility::soa_first_to_second(group_type_name_map, group_type);
|
return utility::soa_first_to_second(group_type_name_map, group_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] std::optional<FourCC> esxr::record_type_to_fourcc(RecordType record_type) noexcept
|
[[nodiscard]] std::optional<FourCC> esxr::record_type_to_fourcc(RecordType record_type)
|
||||||
{
|
{
|
||||||
return utility::soa_first_to_second(record_type_fourcc_map, record_type);
|
return utility::soa_first_to_second(record_type_fourcc_map, record_type);
|
||||||
}
|
}
|
||||||
[[nodiscard]] std::optional<RecordType> esxr::fourcc_to_record_type(FourCC fourcc) noexcept
|
[[nodiscard]] std::optional<RecordType> esxr::fourcc_to_record_type(FourCC fourcc)
|
||||||
{
|
{
|
||||||
return utility::soa_second_to_first(record_type_fourcc_map, fourcc);
|
return utility::soa_second_to_first(record_type_fourcc_map, fourcc);
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] std::optional<std::string_view> esxr::record_type_to_name(RecordType record_type) noexcept
|
[[nodiscard]] std::optional<std::string_view> esxr::record_type_to_name(RecordType record_type)
|
||||||
{
|
{
|
||||||
return utility::soa_first_to_second(record_type_name_map, record_type);
|
return utility::soa_first_to_second(record_type_name_map, record_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] std::optional<std::string_view> esxr::flag_to_description(Flag flag) noexcept
|
[[nodiscard]] std::optional<std::string_view> esxr::flag_to_description(Flag flag)
|
||||||
{
|
{
|
||||||
return utility::soa_first_to_second(flag_description_map, flag);
|
return utility::soa_first_to_second(flag_description_map, flag);
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] std::optional<std::string_view> esxr::refr_flag_to_description(RefrFlag refr_flag) noexcept
|
[[nodiscard]] std::optional<std::string_view> esxr::refr_flag_to_description(RefrFlag refr_flag)
|
||||||
{
|
{
|
||||||
return utility::soa_first_to_second(refr_flag_description_map, refr_flag);
|
return utility::soa_first_to_second(refr_flag_description_map, refr_flag);
|
||||||
}
|
}
|
||||||
@@ -1,39 +1,35 @@
|
|||||||
#include "esx_reader.hpp"
|
#include "esx_reader.hpp"
|
||||||
|
|
||||||
|
#pragma warning( push )
|
||||||
|
#pragma warning( disable : 5264 )
|
||||||
|
#pragma warning( disable : 5262 )
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
#pragma warning( pop )
|
||||||
|
|
||||||
static constexpr auto esm_name = "Skyrim.esm";
|
static constexpr auto esx_name = "Skyrim.esm";
|
||||||
|
|
||||||
/* Propagated Exceptions:
|
|
||||||
* - std::bad_alloc
|
[[nodiscard]] std::filesystem::path esm_path(void)
|
||||||
* - std::filesystem::filesystem_error
|
|
||||||
* - std::ios_base::failure
|
|
||||||
*/
|
|
||||||
[[nodiscard]] static std::ifstream open_esm(void)
|
|
||||||
{
|
{
|
||||||
std::filesystem::path cwd = std::filesystem::current_path();
|
auto cwd = std::filesystem::current_path();
|
||||||
auto esm_path = cwd / esm_name;
|
return cwd / esx_name;
|
||||||
auto esm_fs = std::ifstream(esm_path, std::ios::binary);
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] static std::ifstream open_esx(std::filesystem::path path)
|
||||||
|
{
|
||||||
|
auto esm_fs = std::ifstream(path, std::ios::binary);
|
||||||
if (esm_fs.fail())
|
if (esm_fs.fail())
|
||||||
throw std::ios_base::failure("Could not open the esm file for reading.");
|
throw std::ios_base::failure("Could not open the esm file for reading.");
|
||||||
return esm_fs;
|
return esm_fs;
|
||||||
}
|
}
|
||||||
|
|
||||||
void print_type(esxr::FourCC fcc)
|
|
||||||
{
|
|
||||||
auto type = esxr::fourcc_to_record_type(fcc).value();
|
|
||||||
auto name = esxr::record_type_to_name(type);
|
|
||||||
std::cout << name.value() << '\n';
|
|
||||||
}
|
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
auto esm_fs = open_esm();
|
auto path = esm_path();
|
||||||
auto header_variant = esxr::read_header(esm_fs);
|
auto size = std::filesystem::file_size(path);
|
||||||
std::visit(utility::overloaded {
|
auto stream = open_esx(path);
|
||||||
[](const esxr::RecordHeader &h) { print_type(h.type); },
|
auto root = esxr::read_esx(stream, size);
|
||||||
[](const esxr::GroupHeader& h) { print_type(h.grup); },
|
return 0;
|
||||||
}, header_variant);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
#ifndef UTILITY_HPP
|
#ifndef UTILITY_HPP
|
||||||
#define UTILITY_HPP
|
#define UTILITY_HPP
|
||||||
|
|
||||||
|
#pragma warning( push )
|
||||||
|
#pragma warning( disable : 5264 )
|
||||||
|
#pragma warning( disable : 5262 )
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <bit>
|
#include <bit>
|
||||||
|
#pragma warning( pop )
|
||||||
|
|
||||||
namespace utility {
|
namespace utility {
|
||||||
|
|
||||||
@@ -15,18 +19,18 @@ T::size_type index_of(const T &container, typename T::const_iterator &iter)
|
|||||||
return static_cast<T::size_type>(distance);
|
return static_cast<T::size_type>(distance);
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is a base class for a generic store for some value that can be represented by
|
// Along the lines of https://github.com/joboccara/NamedType
|
||||||
// type T but should not be type checked as type T, but rather some more constrained class.
|
// Usage: using StoreType = Store<struct StoreTypeTag, type>;
|
||||||
template <class T>
|
template <typename Tag, typename Type>
|
||||||
struct Store {
|
struct Store final {
|
||||||
static_assert(std::is_trivial_v<T>);
|
static_assert(std::is_trivial_v<Type>);
|
||||||
T value;
|
Type value;
|
||||||
friend constexpr auto operator<=>(const Store &, const Store &) noexcept = default;
|
friend constexpr auto operator<=>(const Store &, const Store &) = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Convert a builtin array to a std::array
|
// Convert a builtin array to a std::array
|
||||||
template <typename T, std::size_t N>
|
template <typename T, std::size_t N>
|
||||||
constexpr std::array<T, N> array_builtin_to_std(const T(&builtin)[N]) noexcept
|
constexpr std::array<T, N> array_builtin_to_std(const T(&builtin)[N])
|
||||||
{
|
{
|
||||||
std::array<T, N> array;
|
std::array<T, N> array;
|
||||||
std::copy(std::cbegin(builtin), std::cend(builtin), array.begin());
|
std::copy(std::cbegin(builtin), std::cend(builtin), array.begin());
|
||||||
@@ -35,7 +39,7 @@ constexpr std::array<T, N> array_builtin_to_std(const T(&builtin)[N]) noexcept
|
|||||||
|
|
||||||
// Convert a std::array of std::pairs to a std::pair of std::arrays
|
// Convert a std::array of std::pairs to a std::pair of std::arrays
|
||||||
template <typename First, typename Second, std::size_t N>
|
template <typename First, typename Second, std::size_t N>
|
||||||
constexpr std::pair<std::array<First, N>, std::array<Second, N>> map_to_soa(std::array<std::pair<First, Second>, N> map) noexcept
|
constexpr std::pair<std::array<First, N>, std::array<Second, N>> map_to_soa(std::array<std::pair<First, Second>, N> map)
|
||||||
{
|
{
|
||||||
std::array<First, N> first;
|
std::array<First, N> first;
|
||||||
std::array<Second, N> second;
|
std::array<Second, N> second;
|
||||||
@@ -47,7 +51,7 @@ constexpr std::pair<std::array<First, N>, std::array<Second, N>> map_to_soa(std:
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename First, typename Second, std::size_t N>
|
template <typename First, typename Second, std::size_t N>
|
||||||
constexpr std::optional<Second> soa_first_to_second(const std::pair<std::array<First, N>, std::array<Second, N>> &soa, const First &first) noexcept
|
constexpr std::optional<Second> soa_first_to_second(const std::pair<std::array<First, N>, std::array<Second, N>> &soa, const First &first)
|
||||||
{
|
{
|
||||||
auto lhs = std::find(soa.first.cbegin(), soa.first.cend(), first);
|
auto lhs = std::find(soa.first.cbegin(), soa.first.cend(), first);
|
||||||
if (lhs == soa.first.cend())
|
if (lhs == soa.first.cend())
|
||||||
@@ -57,7 +61,7 @@ constexpr std::optional<Second> soa_first_to_second(const std::pair<std::array<F
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename First, typename Second, std::size_t N>
|
template <typename First, typename Second, std::size_t N>
|
||||||
constexpr std::optional<First> soa_second_to_first(const std::pair<std::array<First, N>, std::array<Second, N>> &soa, const Second &second) noexcept
|
constexpr std::optional<First> soa_second_to_first(const std::pair<std::array<First, N>, std::array<Second, N>> &soa, const Second &second)
|
||||||
{
|
{
|
||||||
auto lhs = std::find(soa.second.cbegin(), soa.second.cend(), second);
|
auto lhs = std::find(soa.second.cbegin(), soa.second.cend(), second);
|
||||||
if (lhs == soa.second.cend())
|
if (lhs == soa.second.cend())
|
||||||
|
|||||||
Reference in New Issue
Block a user