2018-10-24 23:31:26 +11:00
|
|
|
#include "Dat.h"
|
|
|
|
|
|
|
|
#include "zlib.h"
|
|
|
|
|
|
|
|
#include "File.h"
|
|
|
|
|
2020-02-10 14:05:04 +11:00
|
|
|
namespace {
|
|
|
|
const uint32_t model_section_count = 0xB;
|
2018-10-24 23:31:26 +11:00
|
|
|
}
|
|
|
|
|
2020-02-10 14:05:04 +11:00
|
|
|
namespace xiv::dat
|
2018-10-24 23:31:26 +11:00
|
|
|
{
|
2020-02-10 14:05:04 +11:00
|
|
|
struct DatFileHeader
|
|
|
|
{
|
|
|
|
uint32_t size;
|
|
|
|
FileType entry_type;
|
|
|
|
uint32_t total_uncompressed_size;
|
|
|
|
uint32_t unknown[0x2];
|
|
|
|
};
|
|
|
|
|
|
|
|
struct DatBlockRecord
|
|
|
|
{
|
|
|
|
uint32_t offset;
|
|
|
|
uint32_t size;
|
|
|
|
uint32_t unknown[0x4];
|
|
|
|
SqPackBlockHash block_hash;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct DatBlockHeader
|
|
|
|
{
|
|
|
|
uint32_t size;
|
|
|
|
uint32_t unknown1;
|
|
|
|
uint32_t compressed_size;
|
|
|
|
uint32_t uncompressed_size;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct DatStdFileBlockInfos
|
|
|
|
{
|
|
|
|
uint32_t offset;
|
|
|
|
uint16_t size;
|
|
|
|
uint16_t uncompressed_size;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct DatMdlFileBlockInfos
|
|
|
|
{
|
|
|
|
uint32_t unknown1;
|
|
|
|
uint32_t uncompressed_sizes[::model_section_count];
|
|
|
|
uint32_t compressed_sizes[::model_section_count];
|
|
|
|
uint32_t offsets[::model_section_count];
|
|
|
|
uint16_t block_ids[::model_section_count];
|
|
|
|
uint16_t block_counts[::model_section_count];
|
|
|
|
uint32_t unknown2[0x2];
|
|
|
|
};
|
|
|
|
|
|
|
|
struct DatTexFileBlockInfos
|
|
|
|
{
|
|
|
|
uint32_t offset;
|
|
|
|
uint32_t size;
|
|
|
|
uint32_t uncompressed_size;
|
|
|
|
uint32_t block_id;
|
|
|
|
uint32_t block_count;
|
|
|
|
};
|
2018-10-24 23:31:26 +11:00
|
|
|
}
|
|
|
|
|
2020-02-10 14:05:04 +11:00
|
|
|
namespace xiv::utils::bparse
|
2018-10-24 23:31:26 +11:00
|
|
|
{
|
2020-02-10 14:05:04 +11:00
|
|
|
template<>
|
|
|
|
inline void reorder< xiv::dat::DatFileHeader >( xiv::dat::DatFileHeader& i_struct )
|
|
|
|
{
|
|
|
|
xiv::utils::bparse::reorder( i_struct.size );
|
|
|
|
xiv::utils::bparse::reorder( i_struct.entry_type );
|
|
|
|
xiv::utils::bparse::reorder( i_struct.total_uncompressed_size );
|
2021-11-27 00:53:57 +01:00
|
|
|
for( unsigned int & i : i_struct.unknown )
|
2020-02-10 14:05:04 +11:00
|
|
|
{
|
2021-11-27 00:53:57 +01:00
|
|
|
xiv::utils::bparse::reorder( i );
|
2020-02-10 14:05:04 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template<>
|
|
|
|
inline void reorder< xiv::dat::DatBlockRecord >( xiv::dat::DatBlockRecord& i_struct )
|
|
|
|
{
|
|
|
|
xiv::utils::bparse::reorder( i_struct.offset );
|
|
|
|
xiv::utils::bparse::reorder( i_struct.size );
|
2021-11-27 00:53:57 +01:00
|
|
|
for( unsigned int & i : i_struct.unknown )
|
2020-02-10 14:05:04 +11:00
|
|
|
{
|
2021-11-27 00:53:57 +01:00
|
|
|
xiv::utils::bparse::reorder( i );
|
2020-02-10 14:05:04 +11:00
|
|
|
}
|
|
|
|
xiv::utils::bparse::reorder( i_struct.block_hash );
|
|
|
|
}
|
|
|
|
|
|
|
|
template<>
|
|
|
|
inline void reorder< xiv::dat::DatBlockHeader >( xiv::dat::DatBlockHeader& i_struct )
|
|
|
|
{
|
|
|
|
xiv::utils::bparse::reorder( i_struct.size );
|
|
|
|
xiv::utils::bparse::reorder( i_struct.unknown1 );
|
|
|
|
xiv::utils::bparse::reorder( i_struct.compressed_size );
|
|
|
|
xiv::utils::bparse::reorder( i_struct.uncompressed_size );
|
|
|
|
}
|
|
|
|
|
|
|
|
template<>
|
|
|
|
inline void reorder< xiv::dat::DatStdFileBlockInfos >( xiv::dat::DatStdFileBlockInfos& i_struct )
|
|
|
|
{
|
|
|
|
xiv::utils::bparse::reorder( i_struct.offset );
|
|
|
|
xiv::utils::bparse::reorder( i_struct.size );
|
|
|
|
xiv::utils::bparse::reorder( i_struct.uncompressed_size );
|
|
|
|
}
|
|
|
|
|
|
|
|
template<>
|
|
|
|
inline void reorder< xiv::dat::DatMdlFileBlockInfos >( xiv::dat::DatMdlFileBlockInfos& i_struct )
|
|
|
|
{
|
|
|
|
xiv::utils::bparse::reorder( i_struct.unknown1 );
|
2021-11-27 00:53:57 +01:00
|
|
|
for( unsigned int& uncompressed_size : i_struct.uncompressed_sizes )
|
2020-02-10 14:05:04 +11:00
|
|
|
{
|
2021-11-27 00:53:57 +01:00
|
|
|
xiv::utils::bparse::reorder( uncompressed_size );
|
2020-02-10 14:05:04 +11:00
|
|
|
}
|
2021-11-27 00:53:57 +01:00
|
|
|
for( unsigned int& compressed_size : i_struct.compressed_sizes )
|
2020-02-10 14:05:04 +11:00
|
|
|
{
|
2021-11-27 00:53:57 +01:00
|
|
|
xiv::utils::bparse::reorder( compressed_size );
|
2020-02-10 14:05:04 +11:00
|
|
|
}
|
2021-11-27 00:53:57 +01:00
|
|
|
for( unsigned int& offset : i_struct.offsets )
|
2020-02-10 14:05:04 +11:00
|
|
|
{
|
2021-11-27 00:53:57 +01:00
|
|
|
xiv::utils::bparse::reorder( offset );
|
2020-02-10 14:05:04 +11:00
|
|
|
}
|
2021-11-27 00:53:57 +01:00
|
|
|
for( unsigned short& block_id : i_struct.block_ids )
|
2020-02-10 14:05:04 +11:00
|
|
|
{
|
2021-11-27 00:53:57 +01:00
|
|
|
xiv::utils::bparse::reorder( block_id );
|
2020-02-10 14:05:04 +11:00
|
|
|
}
|
2021-11-27 00:53:57 +01:00
|
|
|
for( unsigned short& block_count : i_struct.block_counts )
|
2020-02-10 14:05:04 +11:00
|
|
|
{
|
2021-11-27 00:53:57 +01:00
|
|
|
xiv::utils::bparse::reorder( block_count );
|
2020-02-10 14:05:04 +11:00
|
|
|
}
|
2021-11-27 00:53:57 +01:00
|
|
|
for( unsigned int &i : i_struct.unknown2 )
|
2020-02-10 14:05:04 +11:00
|
|
|
{
|
2021-11-27 00:53:57 +01:00
|
|
|
xiv::utils::bparse::reorder( i );
|
2020-02-10 14:05:04 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template<>
|
|
|
|
inline void reorder< xiv::dat::DatTexFileBlockInfos >( xiv::dat::DatTexFileBlockInfos& i_struct )
|
|
|
|
{
|
|
|
|
xiv::utils::bparse::reorder( i_struct.offset );
|
|
|
|
xiv::utils::bparse::reorder( i_struct.size );
|
|
|
|
xiv::utils::bparse::reorder( i_struct.uncompressed_size );
|
|
|
|
xiv::utils::bparse::reorder( i_struct.block_id );
|
|
|
|
xiv::utils::bparse::reorder( i_struct.block_count );
|
|
|
|
}
|
2018-10-24 23:31:26 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
using xiv::utils::bparse::extract;
|
|
|
|
|
2020-02-10 14:05:04 +11:00
|
|
|
namespace xiv::dat
|
2018-10-24 23:31:26 +11:00
|
|
|
{
|
|
|
|
|
2020-02-10 14:05:04 +11:00
|
|
|
Dat::Dat( const std::filesystem::path& i_path, uint32_t i_nb ) :
|
|
|
|
SqPack( i_path ),
|
|
|
|
m_num( i_nb )
|
|
|
|
{
|
|
|
|
auto block_record = extract< DatBlockRecord >( m_handle );
|
|
|
|
block_record.offset *= 0x80;
|
|
|
|
isBlockValid( block_record.offset, block_record.size, block_record.block_hash );
|
|
|
|
}
|
|
|
|
|
2021-11-27 00:53:57 +01:00
|
|
|
Dat::~Dat() = default;
|
2020-02-10 14:05:04 +11:00
|
|
|
|
|
|
|
std::unique_ptr< File > Dat::getFile( uint32_t i_offset )
|
|
|
|
{
|
|
|
|
std::unique_ptr< File > outputFile( new File() );
|
|
|
|
{
|
2018-10-24 23:31:26 +11:00
|
|
|
// Lock in this scope
|
2020-02-10 14:05:04 +11:00
|
|
|
std::lock_guard< std::mutex > lock( m_fileMutex );
|
2018-10-24 23:31:26 +11:00
|
|
|
|
|
|
|
// Seek to the start of the header of the file record and extract it
|
2020-02-10 14:05:04 +11:00
|
|
|
m_handle.seekg( i_offset );
|
|
|
|
auto file_header = extract< DatFileHeader >( m_handle );
|
2018-10-24 23:31:26 +11:00
|
|
|
|
2020-02-10 14:05:04 +11:00
|
|
|
switch( file_header.entry_type )
|
2018-10-24 23:31:26 +11:00
|
|
|
{
|
2020-02-10 14:05:04 +11:00
|
|
|
case FileType::empty:
|
|
|
|
throw std::runtime_error( "File is empty" );
|
|
|
|
|
|
|
|
case FileType::standard:
|
|
|
|
{
|
|
|
|
outputFile->_type = FileType::standard;
|
|
|
|
|
2021-11-27 00:53:57 +01:00
|
|
|
auto number_of_blocks = extract< uint32_t >( m_handle, "number_of_blocks" );
|
2020-02-10 14:05:04 +11:00
|
|
|
|
|
|
|
// Just extract offset infos for the blocks to extract
|
|
|
|
std::vector< DatStdFileBlockInfos > std_file_block_infos;
|
|
|
|
extract< DatStdFileBlockInfos >( m_handle, number_of_blocks, std_file_block_infos );
|
|
|
|
|
|
|
|
// Pre allocate data vector for the whole file
|
|
|
|
outputFile->_data_sections.resize( 1 );
|
|
|
|
auto& data_section = outputFile->_data_sections.front();
|
|
|
|
|
|
|
|
data_section.reserve( file_header.total_uncompressed_size );
|
|
|
|
// Extract each block
|
|
|
|
for( auto& file_block_info : std_file_block_infos )
|
|
|
|
{
|
|
|
|
extractBlock( i_offset + file_header.size + file_block_info.offset, data_section );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case FileType::model:
|
|
|
|
{
|
|
|
|
outputFile->_type = FileType::model;
|
|
|
|
|
2021-11-27 00:53:57 +01:00
|
|
|
auto mdlBlockInfo = extract< DatMdlFileBlockInfos >( m_handle );
|
2020-02-10 14:05:04 +11:00
|
|
|
|
|
|
|
// Getting the block number and read their sizes
|
|
|
|
const uint32_t block_count = mdlBlockInfo.block_ids[ ::model_section_count - 1 ] +
|
|
|
|
mdlBlockInfo.block_counts[ ::model_section_count - 1 ];
|
|
|
|
std::vector< uint16_t > block_sizes;
|
|
|
|
extract< uint16_t >( m_handle, "block_size", block_count, block_sizes );
|
|
|
|
|
|
|
|
// Preallocate sufficient space
|
|
|
|
outputFile->_data_sections.resize( ::model_section_count );
|
|
|
|
|
|
|
|
for( uint32_t i = 0; i < ::model_section_count; ++i )
|
|
|
|
{
|
|
|
|
// Preallocating for section
|
|
|
|
auto& data_section = outputFile->_data_sections[ i ];
|
|
|
|
data_section.reserve( mdlBlockInfo.uncompressed_sizes[ i ] );
|
|
|
|
|
|
|
|
uint32_t current_offset = i_offset + file_header.size + mdlBlockInfo.offsets[ i ];
|
|
|
|
for( uint32_t j = 0; j < mdlBlockInfo.block_counts[ i ]; ++j )
|
2018-10-24 23:31:26 +11:00
|
|
|
{
|
2020-02-10 14:05:04 +11:00
|
|
|
extractBlock( current_offset, data_section );
|
|
|
|
current_offset += block_sizes[ mdlBlockInfo.block_ids[ i ] + j ];
|
2018-10-24 23:31:26 +11:00
|
|
|
}
|
2020-02-10 14:05:04 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
2018-10-24 23:31:26 +11:00
|
|
|
|
2020-02-10 14:05:04 +11:00
|
|
|
case FileType::texture:
|
|
|
|
{
|
|
|
|
outputFile->_type = FileType::texture;
|
2018-10-24 23:31:26 +11:00
|
|
|
|
2020-02-10 14:05:04 +11:00
|
|
|
// Extracts mipmap entries and the block sizes
|
2021-11-27 00:53:57 +01:00
|
|
|
auto sectionCount = extract< uint32_t >( m_handle, "sections_count" );
|
2018-10-24 23:31:26 +11:00
|
|
|
|
2020-02-10 14:05:04 +11:00
|
|
|
std::vector< DatTexFileBlockInfos > texBlockInfo;
|
|
|
|
extract< DatTexFileBlockInfos >( m_handle, sectionCount, texBlockInfo );
|
2018-10-24 23:31:26 +11:00
|
|
|
|
2020-02-10 14:05:04 +11:00
|
|
|
// Extracting block sizes
|
|
|
|
uint32_t block_count = texBlockInfo.back().block_id + texBlockInfo.back().block_count;
|
|
|
|
std::vector< uint16_t > block_sizes;
|
|
|
|
extract< uint16_t >( m_handle, "block_size", block_count, block_sizes );
|
2018-10-24 23:31:26 +11:00
|
|
|
|
2020-02-10 14:05:04 +11:00
|
|
|
outputFile->_data_sections.resize( sectionCount + 1 );
|
2018-10-24 23:31:26 +11:00
|
|
|
|
2020-02-10 14:05:04 +11:00
|
|
|
// Extracting header in section 0
|
|
|
|
const uint32_t header_size = texBlockInfo.front().offset;
|
|
|
|
auto& header_section = outputFile->_data_sections[ 0 ];
|
|
|
|
header_section.resize( header_size );
|
2018-10-24 23:31:26 +11:00
|
|
|
|
2020-02-10 14:05:04 +11:00
|
|
|
m_handle.seekg( i_offset + file_header.size );
|
|
|
|
m_handle.read( header_section.data(), header_size );
|
2018-10-24 23:31:26 +11:00
|
|
|
|
2020-02-10 14:05:04 +11:00
|
|
|
// Extracting other sections
|
|
|
|
for( uint32_t i = 0; i < sectionCount; ++i )
|
|
|
|
{
|
|
|
|
auto& data_section = outputFile->_data_sections[ i + 1 ];
|
|
|
|
auto& section_infos = texBlockInfo[ i ];
|
|
|
|
data_section.reserve( section_infos.uncompressed_size );
|
2018-10-24 23:31:26 +11:00
|
|
|
|
2020-02-10 14:05:04 +11:00
|
|
|
uint32_t current_offset = i_offset + file_header.size + section_infos.offset;
|
|
|
|
for( uint32_t j = 0; j < section_infos.block_count; ++j )
|
2018-10-24 23:31:26 +11:00
|
|
|
{
|
2020-02-10 14:05:04 +11:00
|
|
|
extractBlock( current_offset, data_section );
|
|
|
|
current_offset += block_sizes[ section_infos.block_id + j ];
|
2018-10-24 23:31:26 +11:00
|
|
|
}
|
2020-02-10 14:05:04 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
2018-10-24 23:31:26 +11:00
|
|
|
|
2020-02-10 14:05:04 +11:00
|
|
|
default:
|
|
|
|
throw std::runtime_error(
|
|
|
|
"Invalid entry_type: " + std::to_string( static_cast<uint32_t>(file_header.entry_type) ) );
|
2018-10-24 23:31:26 +11:00
|
|
|
}
|
2020-02-10 14:05:04 +11:00
|
|
|
}
|
2018-10-24 23:31:26 +11:00
|
|
|
|
2020-02-10 14:05:04 +11:00
|
|
|
return outputFile;
|
|
|
|
}
|
2018-10-24 23:31:26 +11:00
|
|
|
|
2020-02-10 14:05:04 +11:00
|
|
|
void Dat::extractBlock( uint32_t i_offset, std::vector< char >& o_data )
|
|
|
|
{
|
|
|
|
m_handle.seekg( i_offset );
|
2018-10-24 23:31:26 +11:00
|
|
|
|
2021-11-27 00:53:57 +01:00
|
|
|
auto block_header = extract< DatBlockHeader >( m_handle );
|
2018-10-24 23:31:26 +11:00
|
|
|
|
2020-02-10 14:05:04 +11:00
|
|
|
// Resizing the vector to write directly into it
|
2021-11-27 00:53:57 +01:00
|
|
|
const auto data_size = o_data.size();
|
2020-02-10 14:05:04 +11:00
|
|
|
o_data.resize( data_size + block_header.uncompressed_size );
|
2018-10-24 23:31:26 +11:00
|
|
|
|
2020-02-10 14:05:04 +11:00
|
|
|
// 32000 in compressed_size means it is not compressed so take uncompressed_size
|
|
|
|
if( block_header.compressed_size == 32000 )
|
|
|
|
{
|
|
|
|
m_handle.read( o_data.data() + data_size, block_header.uncompressed_size );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2018-10-24 23:31:26 +11:00
|
|
|
// If it is compressed use zlib
|
|
|
|
// Read the data to be decompressed
|
2020-02-10 14:05:04 +11:00
|
|
|
std::vector< char > temp_buffer( block_header.compressed_size );
|
|
|
|
m_handle.read( temp_buffer.data(), block_header.compressed_size );
|
2018-10-24 23:31:26 +11:00
|
|
|
|
2021-11-27 00:53:57 +01:00
|
|
|
utils::zlib::no_header_decompress( reinterpret_cast< uint8_t* >( temp_buffer.data() ),
|
2020-02-10 14:05:04 +11:00
|
|
|
temp_buffer.size(),
|
2021-11-27 00:53:57 +01:00
|
|
|
reinterpret_cast< uint8_t* >( o_data.data() + data_size ),
|
|
|
|
static_cast< size_t >( block_header.uncompressed_size ) );
|
2020-02-10 14:05:04 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t Dat::getNum() const
|
|
|
|
{
|
|
|
|
return m_num;
|
|
|
|
}
|
2018-10-24 23:31:26 +11:00
|
|
|
|
|
|
|
}
|