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:
2022-10-27 15:46:00 +11:00
parent 4bd1faf0b3
commit d01b2cfb3e
6 changed files with 81 additions and 55 deletions

View File

@@ -140,6 +140,7 @@
</ItemGroup>
<ItemGroup>
<ClInclude Include="esx_reader.hpp" />
<ClInclude Include="esx_reader_impl.hpp" />
<ClInclude Include="utility.hpp" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />

View File

@@ -32,5 +32,8 @@
<ClInclude Include="utility.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="esx_reader_impl.hpp">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>

View File

@@ -1,4 +1,9 @@
#include "esx_reader.hpp"
#include "esx_reader_impl.hpp"
using namespace esxr;
[[nodiscard]] Header esxr::read_header(std::istream &in)
{
return read_header_impl<directly_readable>(in);
}

View File

@@ -21,14 +21,6 @@ namespace esxr {
class GroupNode;
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
//
@@ -136,21 +128,6 @@ struct RecordHeader {
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
//
@@ -181,37 +158,7 @@ private:
[[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;
// 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) };
}
[[nodiscard]] Header read_header(std::istream &in);
}

View 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 */

View File

@@ -15,6 +15,13 @@ static constexpr std::pair<GroupType, std::string_view> group_type_name_map_buil
{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[] {
{RecordType::AACT, fourcc_from_cstr("AACT")},
{RecordType::ACHR, fourcc_from_cstr("ACHR")},