2021-11-27 00:53:57 +01:00
|
|
|
#pragma once
|
2018-10-24 23:31:26 +11:00
|
|
|
#include <type_traits>
|
|
|
|
#include <iomanip>
|
|
|
|
#include <sstream>
|
|
|
|
#include <vector>
|
|
|
|
|
2020-02-10 14:05:04 +11:00
|
|
|
namespace xiv::utils::bparse
|
2018-10-24 23:31:26 +11:00
|
|
|
{
|
|
|
|
|
2023-04-26 13:58:41 +02:00
|
|
|
// Helper struct for compile-time unrolling of byteswap
|
|
|
|
template< int N, bool Unroll >
|
|
|
|
struct byteswap_impl_helper
|
|
|
|
{
|
|
|
|
static void swap( char ( &bytes )[ N ], int start )
|
|
|
|
{
|
|
|
|
// Intentionally left empty. This specialization should never be used.
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// Specialization of byteswap_impl_helper for compile-time unrolling (true)
|
|
|
|
template< int N >
|
|
|
|
struct byteswap_impl_helper< N, true >
|
|
|
|
{
|
|
|
|
static void swap( char ( &bytes )[ N ], int start )
|
|
|
|
{
|
|
|
|
// Swap pairs of bytes recursively, unrolling the loop at compile-time
|
|
|
|
if constexpr( N >= 2 )
|
|
|
|
{
|
|
|
|
std::swap( bytes[ start ], bytes[ N - start - 1 ] );
|
|
|
|
if constexpr( N >= 4 )
|
|
|
|
{
|
|
|
|
std::swap( bytes[ start + 1 ], bytes[ N - start - 2 ] );
|
|
|
|
if constexpr( N >= 6 )
|
|
|
|
{
|
|
|
|
std::swap( bytes[ start + 2 ], bytes[ N - start - 3 ] );
|
|
|
|
if constexpr( N >= 8 )
|
|
|
|
{
|
|
|
|
std::swap( bytes[ start + 3 ], bytes[ N - start - 4 ] );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2020-02-10 14:05:04 +11:00
|
|
|
template< int N >
|
2023-04-26 13:58:41 +02:00
|
|
|
struct byteswap_impl_helper< N, false >
|
2020-02-10 14:05:04 +11:00
|
|
|
{
|
2023-04-26 13:58:41 +02:00
|
|
|
static void swap( char ( &bytes )[ N ], int start )
|
2020-02-10 14:05:04 +11:00
|
|
|
{
|
2023-04-26 13:58:41 +02:00
|
|
|
// Swap pairs of bytes using a loop
|
|
|
|
for( auto p = std::begin( bytes ), end = std::end( bytes ) - 1; p < end;
|
|
|
|
++p, --end )
|
|
|
|
{
|
|
|
|
std::swap( *p, *end );
|
|
|
|
}
|
2020-02-10 14:05:04 +11:00
|
|
|
}
|
2023-04-26 13:58:41 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
template< int N >
|
|
|
|
void byteswap_impl( char ( &bytes )[ N ] )
|
|
|
|
{
|
|
|
|
// Decide whether to use compile-time unrolling or loop-based swapping
|
|
|
|
constexpr bool Unroll = N <= 8;
|
|
|
|
byteswap_impl_helper< N, Unroll >::swap( bytes, 0 );
|
2020-02-10 14:05:04 +11:00
|
|
|
}
|
2018-10-24 23:31:26 +11:00
|
|
|
|
2020-02-10 14:05:04 +11:00
|
|
|
// byteswapping any type (no pointers to array)
|
|
|
|
template< typename T >
|
|
|
|
T byteswap( T value )
|
|
|
|
{
|
|
|
|
byteswap_impl( *reinterpret_cast<char ( * )[sizeof( T )]>(&value) );
|
2018-10-24 23:31:26 +11:00
|
|
|
return value;
|
2020-02-10 14:05:04 +11:00
|
|
|
}
|
2018-10-24 23:31:26 +11:00
|
|
|
|
2020-02-10 14:05:04 +11:00
|
|
|
// Read a struct from a stream
|
|
|
|
template< typename StructType >
|
|
|
|
void read( std::istream& i_stream, StructType& i_struct )
|
|
|
|
{
|
2021-11-27 00:53:57 +01:00
|
|
|
static_assert( std::is_trivially_copyable< StructType >::value, "StructType must be a POD to be able to use read." );
|
2020-02-10 14:05:04 +11:00
|
|
|
i_stream.read( reinterpret_cast<char*>( &i_struct ), sizeof( StructType ) );
|
|
|
|
}
|
2018-10-24 23:31:26 +11:00
|
|
|
|
2020-02-10 14:05:04 +11:00
|
|
|
// By default a type does not need reordering
|
|
|
|
template< typename StructType >
|
|
|
|
void reorder( StructType& i_struct )
|
|
|
|
{
|
|
|
|
}
|
2018-10-24 23:31:26 +11:00
|
|
|
|
2020-02-10 14:05:04 +11:00
|
|
|
// "Overload" for passed struct as arg
|
|
|
|
template< typename StructType >
|
|
|
|
void extract( std::istream& i_stream, StructType& o_struct )
|
|
|
|
{
|
|
|
|
read( i_stream, o_struct );
|
|
|
|
reorder( o_struct );
|
|
|
|
}
|
2018-10-24 23:31:26 +11:00
|
|
|
|
2020-02-10 14:05:04 +11:00
|
|
|
// This should not copy because of RVO
|
|
|
|
// Extract a struct from a stream and log it
|
|
|
|
template< typename StructType >
|
|
|
|
StructType extract( std::istream& i_stream )
|
|
|
|
{
|
|
|
|
StructType temp_struct;
|
|
|
|
extract< StructType >( i_stream, temp_struct );
|
|
|
|
return temp_struct;
|
|
|
|
}
|
2018-10-24 23:31:26 +11:00
|
|
|
|
2020-02-10 14:05:04 +11:00
|
|
|
template< typename StructType >
|
|
|
|
void extract( std::istream& i_stream, uint32_t i_size, std::vector< StructType >& o_structs )
|
|
|
|
{
|
|
|
|
o_structs.reserve( i_size );
|
|
|
|
for( uint32_t i = 0; i < i_size; ++i )
|
|
|
|
{
|
|
|
|
o_structs.emplace_back( extract< StructType >( i_stream ) );
|
|
|
|
}
|
|
|
|
}
|
2018-10-24 23:31:26 +11:00
|
|
|
|
2020-02-10 14:05:04 +11:00
|
|
|
// For simple (integral) types just provide name and endianness directly
|
|
|
|
template< typename StructType >
|
|
|
|
StructType extract( std::istream& i_stream, const std::string& i_name, bool i_is_le = true )
|
|
|
|
{
|
|
|
|
StructType temp_struct;
|
|
|
|
read( i_stream, temp_struct );
|
|
|
|
if( !i_is_le )
|
|
|
|
{
|
2018-10-24 23:31:26 +11:00
|
|
|
temp_struct = byteswap( temp_struct );
|
2020-02-10 14:05:04 +11:00
|
|
|
}
|
|
|
|
return temp_struct;
|
|
|
|
}
|
2018-10-24 23:31:26 +11:00
|
|
|
|
2020-02-10 14:05:04 +11:00
|
|
|
template< typename StructType >
|
|
|
|
void extract( std::istream& i_stream, const std::string& i_name, uint32_t i_size, std::vector< StructType >& o_structs,
|
|
|
|
bool i_is_le = true )
|
|
|
|
{
|
|
|
|
o_structs.reserve( i_size );
|
|
|
|
for( uint32_t i = 0; i < i_size; ++i )
|
|
|
|
{
|
|
|
|
o_structs.emplace_back( extract< StructType >( i_stream, i_name ) );
|
|
|
|
}
|
|
|
|
}
|
2018-10-24 23:31:26 +11:00
|
|
|
|
2020-02-10 14:05:04 +11:00
|
|
|
std::string extract_cstring( std::istream& i_stream, const std::string& i_name );
|
2018-10-24 23:31:26 +11:00
|
|
|
|
|
|
|
}
|