Figured out how to choose between different implementations of read_header based on compile-time knowledge. Rearranged things slightly to minimize what's exposed in exs_reader.hpp.
This commit is contained in:
@@ -140,6 +140,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="esx_reader.hpp" />
|
<ClInclude Include="esx_reader.hpp" />
|
||||||
|
<ClInclude Include="esx_reader_impl.hpp" />
|
||||||
<ClInclude Include="utility.hpp" />
|
<ClInclude Include="utility.hpp" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
|
|||||||
@@ -32,5 +32,8 @@
|
|||||||
<ClInclude Include="utility.hpp">
|
<ClInclude Include="utility.hpp">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="esx_reader_impl.hpp">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
@@ -1,4 +1,9 @@
|
|||||||
#include "esx_reader.hpp"
|
#include "esx_reader_impl.hpp"
|
||||||
|
|
||||||
using namespace esxr;
|
using namespace esxr;
|
||||||
|
|
||||||
|
[[nodiscard]] Header esxr::read_header(std::istream &in)
|
||||||
|
{
|
||||||
|
return read_header_impl<directly_readable>(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,14 +21,6 @@ namespace esxr {
|
|||||||
class GroupNode;
|
class GroupNode;
|
||||||
class RecordNode;
|
class RecordNode;
|
||||||
|
|
||||||
//
|
|
||||||
// Constants
|
|
||||||
//
|
|
||||||
|
|
||||||
static constexpr auto esx_endianness = std::endian::little;
|
|
||||||
static constexpr std::streamsize header_size = 24;
|
|
||||||
static constexpr bool same_endianness = std::endian::native == esx_endianness;
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Enumerations
|
// Enumerations
|
||||||
//
|
//
|
||||||
@@ -136,21 +128,6 @@ struct RecordHeader {
|
|||||||
uint16_t unknown;
|
uint16_t unknown;
|
||||||
};
|
};
|
||||||
|
|
||||||
//
|
|
||||||
// Concepts
|
|
||||||
//
|
|
||||||
|
|
||||||
template <typename T, size_t N>
|
|
||||||
concept packed = sizeof(T) == N;
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
concept packed_header = packed<T, static_cast<std::size_t>(header_size)>;
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
concept directly_readable_header = packed_header<T> && same_endianness;
|
|
||||||
|
|
||||||
constexpr bool directly_readable = directly_readable_header<UnknownHeader> && directly_readable_header<GroupHeader> && directly_readable_header<RecordHeader>;
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Classes
|
// Classes
|
||||||
//
|
//
|
||||||
@@ -181,37 +158,7 @@ private:
|
|||||||
[[nodiscard]] std::optional<std::string_view> flag_to_description(Flag flag) noexcept;
|
[[nodiscard]] std::optional<std::string_view> flag_to_description(Flag flag) noexcept;
|
||||||
[[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) noexcept;
|
||||||
|
|
||||||
|
[[nodiscard]] Header read_header(std::istream &in);
|
||||||
// Convert a compatible C string to a FourCC (e.g. "LITR")
|
|
||||||
constexpr 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename = void> requires directly_readable
|
|
||||||
[[nodiscard]] Header read_header(std::istream &in)
|
|
||||||
{
|
|
||||||
// read in header
|
|
||||||
UnknownHeader unknown_header{};
|
|
||||||
auto char_ptr = reinterpret_cast<char *>(&unknown_header);
|
|
||||||
in.read(char_ptr, header_size);
|
|
||||||
auto type_opt = fourcc_to_record_type(unknown_header.type);
|
|
||||||
|
|
||||||
// failed to read header
|
|
||||||
if (in.fail())
|
|
||||||
throw std::runtime_error("Could not read header from given input stream.")
|
|
||||||
|
|
||||||
// invalid type value
|
|
||||||
if (!type_opt)
|
|
||||||
throw std::runtime_error("Invalid header type while reading esx file.");
|
|
||||||
|
|
||||||
auto type = type_opt.value();
|
|
||||||
if (type == RecordType::GRUP)
|
|
||||||
return { std::bit_cast<GroupHeader, UnknownHeader>(unknown_header) };
|
|
||||||
else
|
|
||||||
return { std::bit_cast<RecordHeader, UnknownHeader>(unknown_header) };
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
63
Navmesher/esx_reader_impl.hpp
Normal file
63
Navmesher/esx_reader_impl.hpp
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
#ifndef ESX_READER_IMPL_HPP
|
||||||
|
#define ESX_READER_IMPL_HPP
|
||||||
|
|
||||||
|
#include "esx_reader.hpp"
|
||||||
|
|
||||||
|
namespace esxr {
|
||||||
|
|
||||||
|
//
|
||||||
|
// Constants and Concepts
|
||||||
|
//
|
||||||
|
|
||||||
|
static constexpr auto esx_endianness = std::endian::little;
|
||||||
|
static constexpr std::streamsize header_size = 24;
|
||||||
|
static constexpr bool same_endianness = std::endian::native == esx_endianness;
|
||||||
|
|
||||||
|
template <typename T, size_t N>
|
||||||
|
concept packed = sizeof(T) == N;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
concept packed_header = packed<T, static_cast<std::size_t>(header_size)>;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
concept directly_readable_header = packed_header<T> && same_endianness;
|
||||||
|
|
||||||
|
constexpr bool directly_readable = directly_readable_header<UnknownHeader> && directly_readable_header<GroupHeader> && directly_readable_header<RecordHeader>;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Template functions
|
||||||
|
//
|
||||||
|
|
||||||
|
template <bool directly_readable> requires directly_readable
|
||||||
|
[[nodiscard]] Header read_header_impl(std::istream &in)
|
||||||
|
{
|
||||||
|
// read in header
|
||||||
|
UnknownHeader unknown_header{};
|
||||||
|
auto char_ptr = reinterpret_cast<char *>(&unknown_header);
|
||||||
|
in.read(char_ptr, header_size);
|
||||||
|
auto type_opt = fourcc_to_record_type(unknown_header.type);
|
||||||
|
|
||||||
|
// failed to read header
|
||||||
|
if (in.fail())
|
||||||
|
throw std::runtime_error("Could not read header from given input stream.");
|
||||||
|
|
||||||
|
// invalid type value
|
||||||
|
if (!type_opt)
|
||||||
|
throw std::runtime_error("Invalid header type while reading esx file.");
|
||||||
|
|
||||||
|
auto type = type_opt.value();
|
||||||
|
if (type == RecordType::GRUP)
|
||||||
|
return { std::bit_cast<GroupHeader, UnknownHeader>(unknown_header) };
|
||||||
|
else
|
||||||
|
return { std::bit_cast<RecordHeader, UnknownHeader>(unknown_header) };
|
||||||
|
}
|
||||||
|
|
||||||
|
template <bool directly_readable> requires (!directly_readable)
|
||||||
|
[[nodiscard]] Header read_header_impl(std::istream &in)
|
||||||
|
{
|
||||||
|
static_assert(directly_readable, "Not implemented.");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* ESX_READER_IMPL_HPP */
|
||||||
@@ -15,6 +15,13 @@ 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")},
|
||||||
|
|||||||
Reference in New Issue
Block a user