1
Fork 0
mirror of https://github.com/SapphireServer/Sapphire.git synced 2025-05-23 10:17:44 +00:00

Cleanup, Improvements

- Refactored datReader and several other files for code cleanliness
- Enhanced runtime performance by optimizing select functions, utilizing std::string_view in place of std::string where appropriate
- Removed deprecated filesystem implementation
- Introduced Link Time Optimization (LTO) support for Linux builds
- Enabled parallel builds for GCC/Clang compilers
- Expanded and improved comments for various functions
- Replaced version check failure with warning, allowing for continued use with a cautionary message

Tested on MSVC/Windows and Clang/Ubuntu
This commit is contained in:
AriAvery 2023-04-26 13:58:41 +02:00
parent 4256d55d23
commit 8dd40b1378
46 changed files with 825 additions and 701 deletions

View file

@ -1,7 +1,24 @@
if( UNIX ) if( UNIX )
# override policy CMP0069 to NEW
set( CMAKE_POLICY_DEFAULT_CMP0069 NEW )
cmake_policy( SET CMP0069 NEW )
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17 -fPIC" ) set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17 -fPIC" )
set( CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS} -O3") set( CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS} -O3" )
# enable parallel builds for GCC/Clang with Make
if(${CMAKE_GENERATOR} MATCHES "Make")
set( CMAKE_MAKE_PROGRAM "${CMAKE_MAKE_PROGRAM} -j$(nproc)" )
endif()
# enable Link Time Optimization (LTO)
include( CheckIPOSupported )
check_ipo_supported( RESULT result )
if( result )
set( CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE )
else()
message( WARNING "IPO is not supported on this platform." )
endif()
else() else()
add_definitions( -D_WIN32_WINNT=0x601 ) add_definitions( -D_WIN32_WINNT=0x601 )
add_definitions( -D_CRT_SECURE_NO_WARNINGS ) add_definitions( -D_CRT_SECURE_NO_WARNINGS )

View file

@ -276,37 +276,61 @@ namespace xiv::dat
default: default:
throw std::runtime_error( throw std::runtime_error(
"Invalid entry_type: " + std::to_string( static_cast<uint32_t>(file_header.entry_type) ) ); "Invalid entry_type: " + std::to_string( static_cast< uint32_t >( file_header.entry_type ) ) );
} }
} }
return outputFile; return outputFile;
} }
void Dat::extractBlock( uint32_t i_offset, std::vector< char >& o_data ) void Dat::extractBlock( uint32_t i_offset, std::vector< char >& o_data )
{ {
m_handle.seekg( i_offset ); m_handle.seekg( i_offset );
if( !m_handle.good() )
{
throw std::runtime_error( "Failed to set stream position." );
}
auto block_header = extract< DatBlockHeader >( m_handle ); auto block_header = extract< DatBlockHeader >( m_handle );
// Resizing the vector to write directly into it if( !m_handle.good() )
{
throw std::runtime_error( "Failed to read block header." );
}
// Reserving space in the output vector to avoid reallocations
const auto data_size = o_data.size(); const auto data_size = o_data.size();
o_data.resize( data_size + block_header.uncompressed_size ); o_data.reserve( data_size + block_header.uncompressed_size );
// 32000 in compressed_size means it is not compressed so take uncompressed_size // 32000 in compressed_size means it is not compressed so take uncompressed_size
if( block_header.compressed_size == 32000 ) if( block_header.compressed_size == 32000 )
{ {
m_handle.read( o_data.data() + data_size, block_header.uncompressed_size ); m_handle.read( o_data.data() + data_size, block_header.uncompressed_size );
if( !m_handle.good() )
{
throw std::runtime_error( "Failed to read uncompressed data." );
}
o_data.resize( data_size + block_header.uncompressed_size );
} }
else else
{ {
// If it is compressed use zlib // If it is compressed use zlib
// Read the data to be decompressed // Read the data to be decompressed
std::vector< char > temp_buffer( block_header.compressed_size ); auto temp_buffer = std::make_unique< char[] >( block_header.compressed_size );
m_handle.read( temp_buffer.data(), block_header.compressed_size ); m_handle.read( temp_buffer.get(), block_header.compressed_size );
utils::zlib::no_header_decompress( reinterpret_cast< uint8_t* >( temp_buffer.data() ), if( !m_handle.good() )
temp_buffer.size(), {
throw std::runtime_error( "Failed to read compressed data." );
}
o_data.resize( data_size + block_header.uncompressed_size );
utils::zlib::no_header_decompress( reinterpret_cast< uint8_t* >( temp_buffer.get() ),
block_header.compressed_size,
reinterpret_cast< uint8_t* >( o_data.data() + data_size ), reinterpret_cast< uint8_t* >( o_data.data() + data_size ),
static_cast< size_t >( block_header.uncompressed_size ) ); static_cast< size_t >( block_header.uncompressed_size ) );
} }

View file

@ -5,21 +5,21 @@
#include "GameData.h" #include "GameData.h"
#include "Index.h" #include "Index.h"
namespace xiv namespace xiv::dat
{ {
namespace dat
{
Cat::Cat( const std::filesystem::path& basePath, uint32_t catNum, const std::string& name ) : m_name( name ), Cat::Cat( const std::filesystem::path& basePath, uint32_t catNum, const std::string& name ) : m_name( name ),
m_catNum( catNum ), m_catNum( catNum ),
m_chunk( -1 ) m_chunk( -1 )
{ {
// Format the category number as a two-digit hex string
std::stringstream ss; std::stringstream ss;
ss << std::setw( 2 ) << std::setfill( '0' ) << std::hex << catNum; ss << std::setw( 2 ) << std::setfill( '0' ) << std::hex << catNum;
std::string prefix = ss.str() + "0000.win32"; std::string prefix = ss.str() + "0000.win32";
// Create an Index object using the formatted category number and basePath
m_index = std::make_unique< Index >( basePath / "ffxiv" / ( prefix + ".index" ) ); m_index = std::make_unique< Index >( basePath / "ffxiv" / ( prefix + ".index" ) );
// Create Dat objects for each entry in the index
for( uint32_t i = 0; i < getIndex().getDatCount(); ++i ) for( uint32_t i = 0; i < getIndex().getDatCount(); ++i )
{ {
m_dats.emplace_back( std::make_unique< Dat >( basePath / "ffxiv" / ( prefix + ".dat" + std::to_string( i ) ), i ) ); m_dats.emplace_back( std::make_unique< Dat >( basePath / "ffxiv" / ( prefix + ".dat" + std::to_string( i ) ), i ) );
@ -30,17 +30,17 @@ namespace xiv
m_catNum( catNum ), m_catNum( catNum ),
m_chunk( chunk ) m_chunk( chunk )
{ {
// Create an Index object using the basePath and formatted information
m_index = std::make_unique< Index >( basePath / GameData::buildDatStr( "ex" + std::to_string( exNum ), catNum, exNum, chunk, "win32", "index" ) ); m_index = std::make_unique< Index >( basePath / GameData::buildDatStr( "ex" + std::to_string( exNum ), catNum, exNum, chunk, "win32", "index" ) );
// Create Dat objects for each entry in the index
for( uint32_t i = 0; i < getIndex().getDatCount(); ++i ) for( uint32_t i = 0; i < getIndex().getDatCount(); ++i )
{ {
m_dats.emplace_back( std::make_unique< Dat >( basePath / GameData::buildDatStr( "ex" + std::to_string( exNum ), catNum, exNum, chunk, "win32", "dat" + std::to_string( i ) ), i ) ); m_dats.emplace_back( std::make_unique< Dat >( basePath / GameData::buildDatStr( "ex" + std::to_string( exNum ), catNum, exNum, chunk, "win32", "dat" + std::to_string( i ) ), i ) );
} }
} }
Cat::~Cat() Cat::~Cat() = default;
{
}
const Index& Cat::getIndex() const const Index& Cat::getIndex() const
{ {
@ -72,6 +72,4 @@ namespace xiv
{ {
return m_catNum; return m_catNum;
} }
}// namespace xiv::dat
}// namespace dat
}// namespace xiv

View file

@ -38,16 +38,16 @@ namespace xiv::dat
// Retrieve a file from the category given its hashes // Retrieve a file from the category given its hashes
std::unique_ptr< File > getFile( uint32_t dir_hash, uint32_t filename_hash ) const; std::unique_ptr< File > getFile( uint32_t dir_hash, uint32_t filename_hash ) const;
// Returns whether a file exists in the Cat based on the given directory and filename hashes
bool doesFileExist( uint32_t dir_hash, uint32_t filename_hash ) const; bool doesFileExist( uint32_t dir_hash, uint32_t filename_hash ) const;
// Returns whether a directory exists in the Cat based on the given directory hash
bool doesDirExist( uint32_t dir_hash ) const; bool doesDirExist( uint32_t dir_hash ) const;
// Returns the name of the Cat object
// Returns thename of the category
const std::string& getName() const; const std::string& getName() const;
// Returns the number of the category // Returns the category number of the Cat object
uint32_t getCatNum() const; uint32_t getCatNum() const;
protected: protected:

View file

@ -82,7 +82,8 @@ public:
}; };
}; };
struct LGB_ENPC_ENTRY : public LgbEntry { struct LGB_ENPC_ENTRY : public LgbEntry
{
public: public:
ENpcData data; ENpcData data;
std::string_view name; std::string_view name;
@ -94,7 +95,8 @@ public:
}; };
}; };
struct LGB_EOBJ_ENTRY : public LgbEntry { struct LGB_EOBJ_ENTRY : public LgbEntry
{
public: public:
EObjData data; EObjData data;
std::string_view name; std::string_view name;
@ -106,7 +108,8 @@ public:
}; };
}; };
struct LGB_MAP_RANGE_ENTRY : public LgbEntry { struct LGB_MAP_RANGE_ENTRY : public LgbEntry
{
public: public:
MapRangeData data; MapRangeData data;
std::string_view name; std::string_view name;
@ -118,7 +121,8 @@ public:
}; };
}; };
struct LGB_EXIT_RANGE_ENTRY : public LgbEntry { struct LGB_EXIT_RANGE_ENTRY : public LgbEntry
{
public: public:
ExitRangeData data; ExitRangeData data;
std::string_view name; std::string_view name;
@ -130,7 +134,8 @@ public:
}; };
}; };
struct LGB_POP_RANGE_ENTRY : public LgbEntry { struct LGB_POP_RANGE_ENTRY : public LgbEntry
{
public: public:
PopRangeData data; PopRangeData data;
@ -140,7 +145,8 @@ public:
}; };
}; };
struct LGB_EVENT_RANGE_ENTRY : public LgbEntry { struct LGB_EVENT_RANGE_ENTRY : public LgbEntry
{
public: public:
EventRangeData data; EventRangeData data;
@ -198,7 +204,7 @@ struct LGB_GROUP
LGB_FILE* parent; LGB_FILE* parent;
LGB_GROUP_HEADER header; LGB_GROUP_HEADER header;
LayerSetReferencedList layerSetReferencedList; LayerSetReferencedList layerSetReferencedList;
std::string name; std::string_view name;
std::vector< std::shared_ptr< LgbEntry > > entries; std::vector< std::shared_ptr< LgbEntry > > entries;
std::vector< LayerSetReferenced > refs; std::vector< LayerSetReferenced > refs;
@ -206,19 +212,32 @@ struct LGB_GROUP
{ {
parent = parentStruct; parent = parentStruct;
header = *reinterpret_cast< LGB_GROUP_HEADER* >( buf + offset ); header = *reinterpret_cast< LGB_GROUP_HEADER* >( buf + offset );
name = std::string( buf + offset + header.groupNameOffset ); name = std::string_view( buf + offset + header.groupNameOffset );
// Initialize the layerSetReferencedList from the buffer and offset
layerSetReferencedList = *reinterpret_cast< LayerSetReferencedList* >( buf + offset + header.LayerSetRef ); layerSetReferencedList = *reinterpret_cast< LayerSetReferencedList* >( buf + offset + header.LayerSetRef );
// Check if there are any layer set references to initialize
if( layerSetReferencedList.LayerSetCount > 0 ) if( layerSetReferencedList.LayerSetCount > 0 )
{ {
refs.resize( layerSetReferencedList.LayerSetCount ); // Reserve memory for layer set references
std::memcpy( refs.data(), buf + offset + header.LayerSetRef + layerSetReferencedList.LayerSets, layerSetReferencedList.LayerSetCount * sizeof( LayerSetReferenced ) ); refs.reserve( layerSetReferencedList.LayerSetCount );
// Iterate through each layer set reference and construct LayerSetReferenced objects from the buffer
for( size_t i = 0; i < layerSetReferencedList.LayerSetCount; ++i )
{
LayerSetReferenced ref = *reinterpret_cast< LayerSetReferenced* >( buf + offset + header.LayerSetRef + layerSetReferencedList.LayerSets + i * sizeof( LayerSetReferenced ) );
refs.emplace_back( ref );
}
} }
// Reserve memory for entries
entries.reserve( header.entryCount ); entries.reserve( header.entryCount );
// Calculate the offset for entries
const auto entriesOffset = offset + header.entriesOffset; const auto entriesOffset = offset + header.entriesOffset;
// Iterate through each entry and construct the appropriate objects (not shown in the code snippet)
for( auto i = 0; i < header.entryCount; ++i ) for( auto i = 0; i < header.entryCount; ++i )
{ {
const auto entryOffset = entriesOffset + *reinterpret_cast< int32_t* >( buf + ( entriesOffset + i * 4 ) ); const auto entryOffset = entriesOffset + *reinterpret_cast< int32_t* >( buf + ( entriesOffset + i * 4 ) );
@ -277,7 +296,7 @@ struct LGB_GROUP
} }
catch( std::exception& e ) catch( std::exception& e )
{ {
std::cout << name << " " << e.what() << std::endl; throw e;
} }
} }
}; };
@ -296,63 +315,38 @@ struct LGB_FILE_HEADER
int32_t groupCount; int32_t groupCount;
}; };
struct LGB_FILE { struct LGB_FILE
{
LGB_FILE_HEADER header; LGB_FILE_HEADER header;
std::vector< LGB_GROUP > groups; std::vector< LGB_GROUP > groups;
std::string m_name; std::string m_name;
// Constructor that initializes an LGB_FILE object from a buffer and a name
LGB_FILE( char* buf, const std::string& name ) : LGB_FILE( buf ) LGB_FILE( char* buf, const std::string& name ) : LGB_FILE( buf )
{ {
m_name = name; m_name = name;
} }
// Constructor that initializes an LGB_FILE object from a buffer
LGB_FILE( char* buf ) LGB_FILE( char* buf )
{ {
// Reinterpret the buffer as an LGB_FILE_HEADER pointer and dereference it
header = *reinterpret_cast< LGB_FILE_HEADER* >( buf ); header = *reinterpret_cast< LGB_FILE_HEADER* >( buf );
// Check for a valid file header
if( strncmp( &header.magic[ 0 ], "LGB1", 4 ) != 0 || strncmp( &header.magic2[ 0 ], "LGP1", 4 ) != 0 ) if( strncmp( &header.magic[ 0 ], "LGB1", 4 ) != 0 || strncmp( &header.magic2[ 0 ], "LGP1", 4 ) != 0 )
{
throw std::runtime_error( "Invalid LGB file!" ); throw std::runtime_error( "Invalid LGB file!" );
}
constexpr auto baseOffset = sizeof( header ); constexpr auto baseOffset = sizeof( header );
groups.reserve( header.groupCount ); groups.reserve( header.groupCount );// Reserve memory for the groups
// Iterate through each group and construct LGB_GROUP objects from the buffer
for( size_t i = 0; i < header.groupCount; ++i ) for( size_t i = 0; i < header.groupCount; ++i )
{ {
const auto groupOffset = baseOffset + *reinterpret_cast< int32_t* >( buf + ( baseOffset + i * 4 ) ); const auto groupOffset = baseOffset + *reinterpret_cast< int32_t* >( buf + ( baseOffset + i * 4 ) );
groups.emplace_back( buf, this, groupOffset ); groups.emplace_back( buf, this, groupOffset );
} }
}; }
}; };
/*
#if __cplusplus >= 201703L
#include <experimental/filesystem>
std::map<std::string, LGB_FILE> getLgbFiles( const std::string& dir )
{
namespace fs = std::experimental::filesystem;
std::map<std::string, LGB_FILE> fileMap;
for( const auto& path : fs::recursive_directory_iterator( dir ) )
{
if( path.path().extension() == ".lgb" )
{
const auto& strPath = path.path().string();
auto f = fopen( strPath.c_str(), "rb" );
fseek( f, 0, SEEK_END );
const auto size = ftell( f );
std::vector<char> bytes( size );
rewind( f );
fread( bytes.data(), 1, size, f );
fclose( f );
try
{
LGB_FILE lgbFile( bytes.data() );
fileMap.insert( std::make_pair( strPath, lgbFile ) );
}
catch( std::exception& e )
{
std::cout << "Unable to load " << strPath << std::endl;
}
}
}
return fileMap;
}
#endif
*/

View file

@ -86,24 +86,29 @@ struct matrix4
return ret; return ret;
} }
// Multiplies two 4x4 matrices and returns the result
matrix4 operator*( const matrix4& rhs ) const matrix4 operator*( const matrix4& rhs ) const
{ {
matrix4 ret; matrix4 ret;
for( unsigned int i = 0; i < 4; i++ )
// Iterate through each row of the resulting matrix
for( unsigned int row = 0; row < 4; row++ )
{ {
ret( i, 0 ) = // Iterate through each column of the resulting matrix
( *this )( i, 0 ) * rhs( 0, 0 ) + ( *this )( i, 1 ) * rhs( 1, 0 ) + ( *this )( i, 2 ) * rhs( 2, 0 ) + for( unsigned int col = 0; col < 4; col++ )
( *this )( i, 3 ) * rhs( 3, 0 ); {
ret( i, 1 ) = // Calculate the value for the current cell by summing the product of the corresponding row and column elements
( *this )( i, 0 ) * rhs( 0, 1 ) + ( *this )( i, 1 ) * rhs( 1, 1 ) + ( *this )( i, 2 ) * rhs( 2, 1 ) + float value = 0;
( *this )( i, 3 ) * rhs( 3, 1 ); for( unsigned int k = 0; k < 4; k++ )
ret( i, 2 ) = {
( *this )( i, 0 ) * rhs( 0, 2 ) + ( *this )( i, 1 ) * rhs( 1, 2 ) + ( *this )( i, 2 ) * rhs( 2, 2 ) + value += ( *this )( row, k ) * rhs( k, col );
( *this )( i, 3 ) * rhs( 3, 2 );
ret( i, 3 ) =
( *this )( i, 0 ) * rhs( 0, 3 ) + ( *this )( i, 1 ) * rhs( 1, 3 ) + ( *this )( i, 2 ) * rhs( 2, 3 ) +
( *this )( i, 3 ) * rhs( 3, 3 );
} }
// Assign the calculated value to the corresponding cell of the resulting matrix
ret( row, col ) = value;
}
}
return ret; return ret;
} }
}; };

View file

@ -1,5 +1,4 @@
#ifndef _SGB_H #pragma once
#define _SGB_H
#include <cstring> #include <cstring>
#include <memory> #include <memory>
@ -132,24 +131,24 @@ struct SGB_MODEL_ENTRY : public SGB_GROUP_ENTRY
{ {
SGB_MODEL_HEADER header; SGB_MODEL_HEADER header;
SgbGroupEntryType type; SgbGroupEntryType type;
std::string name; std::string_view name;
std::string modelFileName; std::string_view modelFileName;
std::string collisionFileName; std::string_view collisionFileName;
SGB_MODEL_ENTRY( char* buf, size_t offset, SgbGroupEntryType type ) SGB_MODEL_ENTRY( char* buf, size_t offset, SgbGroupEntryType type )
{ {
this->type = type; this->type = type;
header = *reinterpret_cast< SGB_MODEL_HEADER* >( buf + offset ); header = *reinterpret_cast< SGB_MODEL_HEADER* >( buf + offset );
name = std::string( buf + offset + header.nameOffset ); name = std::string_view( buf + offset + header.nameOffset );
modelFileName = std::string( buf + offset + header.modelFileOffset ); modelFileName = std::string_view( buf + offset + header.modelFileOffset );
collisionFileName = std::string( buf + offset + header.collisionFileOffset ); collisionFileName = std::string_view( buf + offset + header.collisionFileOffset );
} }
}; };
struct SGB_GROUP struct SGB_GROUP
{ {
SGB_GROUP_HEADER header; SGB_GROUP_HEADER header;
std::string name; std::string_view name;
SGB_FILE* parent; SGB_FILE* parent;
std::vector< std::shared_ptr< SGB_GROUP_ENTRY > > entries; std::vector< std::shared_ptr< SGB_GROUP_ENTRY > > entries;
@ -157,23 +156,32 @@ struct SGB_GROUP
{ {
parent = file; parent = file;
header = *reinterpret_cast< SGB_GROUP_HEADER* >( buf + offset ); header = *reinterpret_cast< SGB_GROUP_HEADER* >( buf + offset );
name = std::string( buf + offset + header.nameOffset ); name = std::string_view( buf + offset + header.nameOffset );
auto entriesOffset = offset + sizeof( header ); auto entriesOffset = offset + sizeof( header );
entries.reserve( header.entryCount );
for( auto i = 0; i < header.entryCount; ++i ) for( auto i = 0; i < header.entryCount; ++i )
{ {
auto entryOffset = entriesOffset + *reinterpret_cast< uint32_t* >( buf + ( entriesOffset + ( i * 4 ) ) ); auto entryOffset = entriesOffset + *reinterpret_cast< uint32_t* >( buf + ( entriesOffset + ( i * 4 ) ) );
if( entryOffset > fileSize ) if( entryOffset > fileSize )
throw std::runtime_error( "SGB_GROUP entry offset was larger than SGB file size!" ); throw std::runtime_error( "SGB_GROUP entry offset was larger than SGB file size!" );
auto type = *reinterpret_cast< uint32_t* >( buf + entryOffset ); auto type = *reinterpret_cast< uint32_t* >( buf + entryOffset );
if( type == SgbGroupEntryType::Model || type == SgbGroupEntryType::Gimmick )
switch( type )
{ {
entries.push_back( std::make_shared< SGB_MODEL_ENTRY >( buf, entryOffset, ( SgbGroupEntryType )type ) ); case SgbGroupEntryType::Model:
case SgbGroupEntryType::Gimmick:
{
entries.emplace_back( std::make_shared< SGB_MODEL_ENTRY >( buf, entryOffset, ( SgbGroupEntryType ) type ) );
break;
} }
else default:
{ {
// std::cout << "\t\tUnknown SGB entry! Group: " << name << " type: " << type << " index: " << i << " entryOffset: " << entryOffset << "\n"; break;
}
} }
} }
} }
@ -268,27 +276,28 @@ struct SGB_FILE
try try
{ {
entries.reserve( 2 );
auto group = SGB_GROUP( buf, this, header.fileSize, baseOffset + header.sharedOffset ); auto group = SGB_GROUP( buf, this, header.fileSize, baseOffset + header.sharedOffset );
entries.push_back( group ); entries.emplace_back( group );
auto group2 = SGB_GROUP( buf, this, header.fileSize, baseOffset + header.offset1C ); auto group2 = SGB_GROUP( buf, this, header.fileSize, baseOffset + header.offset1C );
entries.push_back( group2 ); entries.emplace_back( group2 );
uint32_t stateCount = *reinterpret_cast< uint32_t* >( buf + baseOffset + header.statesOffset + 4 ); uint32_t stateCount = *reinterpret_cast< uint32_t* >( buf + baseOffset + header.statesOffset + 4 );
if( stateCount > 0 ) if( stateCount > 0 )
{ {
stateCount = stateCount; stateEntries.reserve( stateCount );
for( size_t i = 0; i < stateCount; ++i ) for( size_t i = 0; i < stateCount; ++i )
{ {
auto state = SGB_STATE_ENTRY( buf + baseOffset + header.statesOffset + 8 + i * sizeof( SGB_STATE_HEADER ) ); auto state = SGB_STATE_ENTRY( buf + baseOffset + header.statesOffset + 8 + i * sizeof( SGB_STATE_HEADER ) );
stateEntries.push_back( state ); stateEntries.emplace_back( state );
std::cout << state.name << "\n";
} }
} }
} }
catch( std::exception& e ) catch( std::exception& e )
{ {
std::cout << e.what() << "\n"; throw std::runtime_error( std::string( "Failed to load SGB file: " ) + e.what() );
} }
}; };
}; };
#endif // !_SGB_H

View file

@ -9,12 +9,11 @@ using xiv::utils::bparse::extract;
namespace xiv::exd namespace xiv::exd
{ {
Exd::Exd( std::shared_ptr< Exh > i_exh, const std::vector< std::shared_ptr< dat::File>>& i_files ) Exd::Exd( std::shared_ptr< Exh > i_exh, const std::vector< std::shared_ptr< dat::File > >& i_files )
{ {
_exh = i_exh; _exh = i_exh;
_files = i_files; _files = i_files;
// Iterates over all the files // Iterates over all the files
const uint32_t member_count = static_cast< uint32_t >( _exh->get_members().size() ); const uint32_t member_count = static_cast< uint32_t >( _exh->get_members().size() );
for( auto& file_ptr : _files ) for( auto& file_ptr : _files )
@ -29,8 +28,6 @@ namespace xiv::exd
// Preallocate and extract the record_indices // Preallocate and extract the record_indices
const uint32_t record_count = exd_header.index_size / sizeof( ExdRecordIndexData ); const uint32_t record_count = exd_header.index_size / sizeof( ExdRecordIndexData );
std::vector< ExdRecordIndexData > record_indices;
record_indices.reserve( record_count );
for( uint32_t i = 0; i < record_count; ++i ) for( uint32_t i = 0; i < record_count; ++i )
{ {
auto recordIndex = extract< ExdRecordIndexData >( iss ); auto recordIndex = extract< ExdRecordIndexData >( iss );
@ -39,25 +36,22 @@ namespace xiv::exd
} }
} }
Exd::~Exd() const std::vector< Field > Exd::get_row( uint32_t id, uint32_t subRow )
{ {
} // Check if id is in the cache
const std::vector< Field > Exd::get_row( uint32_t id, uint32_t subRow )
{
auto cacheEntryIt = _idCache.find( id ); auto cacheEntryIt = _idCache.find( id );
if( cacheEntryIt == _idCache.end() ) if( cacheEntryIt == _idCache.end() )
throw std::runtime_error( "Id not found: " + std::to_string( id ) ); throw std::runtime_error( "Id not found: " + std::to_string( id ) );
// Iterates over all the files // Retrieve the corresponding file
const uint32_t member_count = static_cast< uint32_t >( _exh->get_members().size() ); const uint32_t member_count = static_cast< uint32_t >( _exh->get_members().size() );
auto& file_ptr = cacheEntryIt->second.file; auto& file_ptr = cacheEntryIt->second.file;
// Create a string from the data section and use it to initialize an input string stream
std::vector< char > dataCpy = file_ptr->get_data_sections().front(); std::vector< char > dataCpy = file_ptr->get_data_sections().front();
std::istringstream iss( std::string( dataCpy.begin(), dataCpy.end() ) ); std::istringstream iss( std::string( dataCpy.begin(), dataCpy.end() ) );
// Get the vector fields for the given record and preallocate it // Retrieve the vector fields for the given record and preallocate it
auto fields = _data[ id ]; auto fields = _data[ id ];
fields.reserve( member_count ); fields.reserve( member_count );
iss.seekg( cacheEntryIt->second.offset + 6 ); iss.seekg( cacheEntryIt->second.offset + 6 );
@ -69,10 +63,10 @@ namespace xiv::exd
int offset = cacheEntryIt->second.offset + 6 + ( subRow * _exh->get_header().data_offset + 2 * ( subRow + 1 ) ); int offset = cacheEntryIt->second.offset + 6 + ( subRow * _exh->get_header().data_offset + 2 * ( subRow + 1 ) );
// Iterate over the member entries and extract the corresponding data
for( auto& member_entry : _exh->get_exh_members() ) for( auto& member_entry : _exh->get_exh_members() )
{ {
// Seek to the position of the member to extract. // Seek to the position of the member to extract
// 6 is because we have uint32_t/uint16_t at the start of each record
iss.seekg( offset + member_entry.offset ); iss.seekg( offset + member_entry.offset );
// Switch depending on the type to extract // Switch depending on the type to extract

29
deps/datReader/Exd.h vendored
View file

@ -1,18 +1,18 @@
#pragma once #pragma once
#include <memory>
#include <map> #include <map>
#include <memory>
#include <unordered_map> #include <unordered_map>
#include <variant> #include <variant>
#include "File.h"
#include "Exd/Common.h" #include "Exd/Common.h"
#include "Exd/Structs.h" #include "Exd/Structs.h"
#include "Exh.h"
#include "File.h"
#include "bparse.h"
#include "stream.h" #include "stream.h"
#include <fstream> #include <fstream>
#include "Exh.h"
#include "bparse.h"
namespace xiv::exd namespace xiv::exd
{ {
@ -27,7 +27,7 @@ namespace xiv::exd
uint32_t id; uint32_t id;
uint32_t offset; uint32_t offset;
}; };
} }// namespace xiv::exd
namespace xiv::utils::bparse namespace xiv::utils::bparse
{ {
@ -86,13 +86,11 @@ namespace xiv::exd
public: public:
// i_exh: the header // i_exh: the header
// i_files: the multiple exd files // i_files: the multiple exd files
Exd() Exd() = default;
{
}
Exd( std::shared_ptr< Exh > i_exh, const std::vector< std::shared_ptr< dat::File>>& i_files ); Exd( std::shared_ptr< Exh > i_exh, const std::vector< std::shared_ptr< dat::File > >& i_files );
~Exd(); ~Exd() = default;
// Get a row by its id // Get a row by its id
const std::vector< Field > get_row( uint32_t id ); const std::vector< Field > get_row( uint32_t id );
@ -107,8 +105,7 @@ namespace xiv::exd
if( sizeof( T ) != _exh->get_header().data_offset ) if( sizeof( T ) != _exh->get_header().data_offset )
{ {
throw std::runtime_error( throw std::runtime_error( "the struct size (" + std::to_string( sizeof( T ) ) + ") doesn't match the size in the header (" +
"the struct size (" + std::to_string( sizeof( T ) ) + ") doesn't match the size in the header (" +
std::to_string( _exh->get_header().data_offset ) + ")!" ); std::to_string( _exh->get_header().data_offset ) + ")!" );
} }
@ -126,7 +123,7 @@ namespace xiv::exd
fields.reserve( member_count ); fields.reserve( member_count );
iss.seekg( cacheEntryIt->second.offset + 6 ); iss.seekg( cacheEntryIt->second.offset + 6 );
iss.read( reinterpret_cast<char*>( &pSheet.get()->_data ), sizeof( T ) ); iss.read( reinterpret_cast< char* >( &pSheet.get()->_data ), sizeof( T ) );
int stringCount = 0; int stringCount = 0;
for( auto& member_entry : _exh->get_exh_members() ) for( auto& member_entry : _exh->get_exh_members() )
@ -221,7 +218,6 @@ namespace xiv::exd
} }
return pSheet; return pSheet;
} }
// Get a row by its id and sub-row // Get a row by its id and sub-row
@ -270,7 +266,7 @@ namespace xiv::exd
fields.reserve( member_count ); fields.reserve( member_count );
iss.seekg( cacheEntryIt->second.offset + 6 ); iss.seekg( cacheEntryIt->second.offset + 6 );
iss.read( reinterpret_cast<char*>( &pSheet.get()->_data ), sizeof( T ) ); iss.read( reinterpret_cast< char* >( &pSheet.get()->_data ), sizeof( T ) );
int stringCount = 0; int stringCount = 0;
for( auto& member_entry : _exh->get_exh_members() ) for( auto& member_entry : _exh->get_exh_members() )
@ -379,5 +375,4 @@ namespace xiv::exd
std::map< uint32_t, ExdCacheEntry > _idCache; std::map< uint32_t, ExdCacheEntry > _idCache;
}; };
} }// namespace xiv::exd

View file

@ -4,44 +4,39 @@
#include "GameData.h" #include "GameData.h"
#include "Exh.h"
#include "Exd.h" #include "Exd.h"
#include "Exh.h"
namespace namespace
{ {
// Suffix of the filenames given a language // Suffix of the filenames given a language
std::map<xiv::exd::Language, std::string> language_map = const std::map< xiv::exd::Language, std::string > language_map =
{ {
{xiv::exd::Language::none, ""}, { xiv::exd::Language::none, "" },
{xiv::exd::Language::ja, "_ja"}, { xiv::exd::Language::ja, "_ja" },
{xiv::exd::Language::en, "_en"}, { xiv::exd::Language::en, "_en" },
{xiv::exd::Language::de, "_de"}, { xiv::exd::Language::de, "_de" },
{xiv::exd::Language::fr, "_fr"}, { xiv::exd::Language::fr, "_fr" },
{xiv::exd::Language::chs, "_chs"} { xiv::exd::Language::chs, "_chs" }
}; };
} }// namespace
namespace xiv::exd namespace xiv::exd
{ {
Cat::Cat( dat::GameData& i_game_data, const std::string& i_name ) : Cat::Cat( dat::GameData& i_game_data, const std::string& i_name ) : _name( i_name )
_name( i_name )
{ {
//XIV_INFO(xiv_exd_logger, "Initializing Cat with name: " << i_name);
// creates the header .exh // creates the header .exh
{
auto header_file = i_game_data.getFile( "exd/" + i_name + ".exh" ); auto header_file = i_game_data.getFile( "exd/" + i_name + ".exh" );
_header = std::shared_ptr< Exh >( new Exh( *header_file ) ); _header = std::make_shared< Exh >( *header_file );
} for( auto language : _header->get_languages() )
for( auto language: _header->get_languages() )
{ {
// chs not yet in data files // chs not yet in data files
if( language == Language::en || language == Language::none ) if( language == Language::en || language == Language::none )
{ {
// Get all the files for a given category/language, in case of multiple range of IDs in separate files (like Quest) // Get all the files for a given category/language, in case of multiple range of IDs in separate files (like Quest)
std::vector< std::shared_ptr< dat::File>> files; std::vector< std::shared_ptr< dat::File > > files;
for( auto& exd_def: _header->get_exd_defs() ) for( auto& exd_def : _header->get_exd_defs() )
{ {
files.emplace_back( i_game_data.getFile( files.emplace_back( i_game_data.getFile(
"exd/" + i_name + "_" + std::to_string( exd_def.start_id ) + language_map.at( language ) + ".exd" ) ); "exd/" + i_name + "_" + std::to_string( exd_def.start_id ) + language_map.at( language ) + ".exd" ) );
@ -52,10 +47,7 @@ namespace xiv::exd
} }
} }
Cat::~Cat() Cat::~Cat() = default;
{
}
const std::string& Cat::get_name() const const std::string& Cat::get_name() const
{ {
@ -72,7 +64,7 @@ namespace xiv::exd
auto ln_it = _data.find( i_language ); auto ln_it = _data.find( i_language );
if( ln_it == _data.end() ) if( ln_it == _data.end() )
{ {
throw std::runtime_error( "No data for language: " + std::to_string( uint16_t( i_language ) ) ); throw std::runtime_error( "No data for language: " + std::to_string( static_cast< uint16_t >( i_language ) ) );
} }
return *( ln_it->second ); return *( ln_it->second );
@ -89,4 +81,4 @@ namespace xiv::exd
return *( ln_it->second ); return *( ln_it->second );
} }
} }// namespace xiv::exd

View file

@ -61,5 +61,5 @@ namespace xiv
std::map<Language, std::unique_ptr<Exd>> _data; std::map<Language, std::unique_ptr<Exd>> _data;
}; };
} }// namespace exd
} }// namespace xiv

View file

@ -7,26 +7,23 @@
#include "ExdCat.h" #include "ExdCat.h"
namespace xiv::exd { namespace xiv::exd
ExdData::ExdData( dat::GameData& i_game_data ) try :
_game_data( i_game_data )
{ {
//XIV_INFO(xiv_exd_logger, "Initializing ExdData"); ExdData::ExdData( dat::GameData& i_game_data )
try : _game_data( i_game_data )
{
// Fetch the root.exl and get a stream from it // Fetch the root.exl and get a stream from it
auto root_exl = i_game_data.getFile( "exd/root.exl" ); auto root_exl = i_game_data.getFile( "exd/root.exl" );
std::vector< char > dataCpy = root_exl->get_data_sections().front(); std::vector< char > dataCpy = root_exl->get_data_sections().front();
xiv::utils::stream::vectorwrapbuf< char > databuf( dataCpy ); xiv::utils::stream::vectorwrapbuf< char > databuf( dataCpy );
std::istream stream( &databuf ); std::istream stream( &databuf );
// Iterates over the lines while skipping the first one // Skip the first line (EXLT,2)
std::string line; std::string line;
std::getline( stream, line ); // extract first line EXLT,2
std::getline( stream, line ); std::getline( stream, line );
// Until the EOF // Read the remaining lines
while( !line.empty() ) while( std::getline( stream, line ) && !line.empty() )
{ {
// Format is cat_name,XX // Format is cat_name,XX
// XX being an internal identifier // XX being an internal identifier
@ -34,34 +31,26 @@ ExdData::ExdData( dat::GameData& i_game_data ) try :
auto sep = line.find( ',' ); auto sep = line.find( ',' );
auto category = line.substr( 0, sep ); auto category = line.substr( 0, sep );
// Add to the list of category name // Add to the list of category names
// creates the empty category in the cats map // Create the empty category in the cats map
// instantiate the creation mutex for this category // Instantiate the creation mutex for this category
_cat_names.push_back( category ); _cat_names.emplace_back( category );
_cats[ category ] = std::unique_ptr< Cat >(); _cats[ category ] = nullptr;
_cat_creation_mutexes[ category ] = std::make_unique< std::mutex >(); _cat_creation_mutexes[ category ] = std::make_unique< std::mutex >();
std::getline( stream, line );
} }
} } catch( std::exception& e )
catch( std::exception& e ) {
{
// In case of failure here, client is supposed to catch the exception because it is not recoverable on our side // In case of failure here, client is supposed to catch the exception because it is not recoverable on our side
throw std::runtime_error( "ExdData initialization failed: " + std::string( e.what() ) ); throw std::runtime_error( "ExdData initialization failed: " + std::string( e.what() ) );
} }
ExdData::~ExdData() const std::vector< std::string >& ExdData::get_cat_names() const
{ {
}
const std::vector< std::string >& ExdData::get_cat_names() const
{
return _cat_names; return _cat_names;
} }
const Cat& ExdData::get_category( const std::string& i_cat_name ) const Cat& ExdData::get_category( const std::string& i_cat_name )
{ {
// Get the category from its name // Get the category from its name
auto cat_it = _cats.find( i_cat_name ); auto cat_it = _cats.find( i_cat_name );
if( cat_it == _cats.end() ) if( cat_it == _cats.end() )
@ -80,10 +69,10 @@ const Cat& ExdData::get_category( const std::string& i_cat_name )
create_category( i_cat_name ); create_category( i_cat_name );
return *( _cats[ i_cat_name ] ); return *( _cats[ i_cat_name ] );
} }
} }
void ExdData::create_category( const std::string& i_cat_name ) void ExdData::create_category( const std::string& i_cat_name )
{ {
// Lock mutex in this scope // Lock mutex in this scope
std::lock_guard< std::mutex > lock( *( _cat_creation_mutexes[ i_cat_name ] ) ); std::lock_guard< std::mutex > lock( *( _cat_creation_mutexes[ i_cat_name ] ) );
// Maybe after unlocking it has already been created, so check (most likely if it blocked) // Maybe after unlocking it has already been created, so check (most likely if it blocked)
@ -91,6 +80,5 @@ void ExdData::create_category( const std::string& i_cat_name )
{ {
_cats[ i_cat_name ] = std::make_unique< Cat >( _game_data, i_cat_name ); _cats[ i_cat_name ] = std::make_unique< Cat >( _game_data, i_cat_name );
} }
} }
}// namespace xiv::exd
}

View file

@ -25,32 +25,32 @@ namespace xiv
{ {
public: public:
// Need an initialized dat::GameData to retrieve the files from the dat // Need an initialized dat::GameData to retrieve the files from the dat
ExdData(dat::GameData& i_game_data); ExdData( dat::GameData& i_game_data );
~ExdData(); ~ExdData() = default;
// Get the list of thenames of the categories // Get the list of thenames of the categories
const std::vector<std::string>& get_cat_names() const; const std::vector< std::string >& get_cat_names() const;
// Get a category by its name // Get a category by its name
const Cat& get_category(const std::string& i_cat_name); const Cat& get_category( const std::string& i_cat_name );
// Export in csv in base flder i_ouput_path // Export in csv in base flder i_ouput_path
void export_as_csvs(const std::filesystem::path& i_output_path); void export_as_csvs( const std::filesystem::path& i_output_path );
protected: protected:
// Lazy instantiation of category // Lazy instantiation of category
void create_category(const std::string& i_cat_name); void create_category( const std::string& i_cat_name );
// Reference to the game_data object // Reference to the game_data object
dat::GameData& _game_data; dat::GameData& _game_data;
// Categories, indexed by their name // Categories, indexed by their name
std::unordered_map<std::string, std::unique_ptr<Cat>> _cats; std::unordered_map< std::string, std::unique_ptr< Cat > > _cats;
// List of category names = m_cats.keys() // List of category names = m_cats.keys()
std::vector<std::string> _cat_names; std::vector< std::string > _cat_names;
// Mutexes used to avoid race condition when lazy instantiating a category // Mutexes used to avoid race condition when lazy instantiating a category
std::unordered_map<std::string, std::unique_ptr<std::mutex>> _cat_creation_mutexes; std::unordered_map< std::string, std::unique_ptr< std::mutex > > _cat_creation_mutexes;
}; };
} }// namespace exd
} }// namespace xiv

View file

@ -9,12 +9,12 @@ using xiv::utils::bparse::extract;
namespace xiv::exd namespace xiv::exd
{ {
Exh::Exh( const dat::File& i_file ) Exh::Exh( const dat::File& i_file )
{ {
// Get a stream from the file // Get a stream from the file
std::vector< char > dataCpy = i_file.get_data_sections().front(); std::vector< char > dataCpy = i_file.get_data_sections().front();
std::istringstream iss( std::string( dataCpy.begin(), dataCpy.end() ) ); std::string dataStr( dataCpy.begin(), dataCpy.end() );
std::istringstream iss( std::move( dataStr ) );
// Extract header and skip to member definitions // Extract header and skip to member definitions
_header = extract< ExhHeader >( iss ); _header = extract< ExhHeader >( iss );
@ -43,10 +43,6 @@ namespace xiv::exd
} }
} }
Exh::~Exh()
{
}
const ExhHeader& Exh::get_header() const const ExhHeader& Exh::get_header() const
{ {
return _header; return _header;

41
deps/datReader/Exh.h vendored
View file

@ -44,12 +44,13 @@ namespace xiv::exd
uint32_t start_id; uint32_t start_id;
uint32_t count_id; uint32_t count_id;
}; };
}; };// namespace xiv::exd
namespace xiv::utils::bparse { namespace xiv::utils::bparse
template<>
inline void reorder< xiv::exd::ExhHeader >( xiv::exd::ExhHeader& i_struct )
{ {
template<>
inline void reorder< xiv::exd::ExhHeader >( xiv::exd::ExhHeader& i_struct )
{
for( int32_t i = 0; i < 0x4; ++i ) for( int32_t i = 0; i < 0x4; ++i )
{ {
xiv::utils::bparse::reorder( i_struct.magic[ i ] ); xiv::utils::bparse::reorder( i_struct.magic[ i ] );
@ -64,26 +65,26 @@ inline void reorder< xiv::exd::ExhHeader >( xiv::exd::ExhHeader& i_struct )
xiv::utils::bparse::reorder( i_struct.exd_count ); xiv::utils::bparse::reorder( i_struct.exd_count );
i_struct.language_count = xiv::utils::bparse::byteswap( i_struct.language_count ); i_struct.language_count = xiv::utils::bparse::byteswap( i_struct.language_count );
xiv::utils::bparse::reorder( i_struct.language_count ); xiv::utils::bparse::reorder( i_struct.language_count );
} }
template<> template<>
inline void reorder< xiv::exd::ExhMember >( xiv::exd::ExhMember& i_struct ) inline void reorder< xiv::exd::ExhMember >( xiv::exd::ExhMember& i_struct )
{ {
i_struct.type = xiv::utils::bparse::byteswap( i_struct.type ); i_struct.type = xiv::utils::bparse::byteswap( i_struct.type );
xiv::utils::bparse::reorder( i_struct.type ); xiv::utils::bparse::reorder( i_struct.type );
i_struct.offset = xiv::utils::bparse::byteswap( i_struct.offset ); i_struct.offset = xiv::utils::bparse::byteswap( i_struct.offset );
xiv::utils::bparse::reorder( i_struct.offset ); xiv::utils::bparse::reorder( i_struct.offset );
} }
template<> template<>
inline void reorder< xiv::exd::ExhExdDef >( xiv::exd::ExhExdDef& i_struct ) inline void reorder< xiv::exd::ExhExdDef >( xiv::exd::ExhExdDef& i_struct )
{ {
i_struct.start_id = xiv::utils::bparse::byteswap( i_struct.start_id ); i_struct.start_id = xiv::utils::bparse::byteswap( i_struct.start_id );
xiv::utils::bparse::reorder( i_struct.start_id ); xiv::utils::bparse::reorder( i_struct.start_id );
i_struct.count_id = xiv::utils::bparse::byteswap( i_struct.count_id ); i_struct.count_id = xiv::utils::bparse::byteswap( i_struct.count_id );
xiv::utils::bparse::reorder( i_struct.count_id ); xiv::utils::bparse::reorder( i_struct.count_id );
} }
}; };// namespace xiv::utils::bparse
namespace xiv namespace xiv
{ {
@ -105,16 +106,21 @@ namespace xiv
// The header file // The header file
Exh( const dat::File& i_file ); Exh( const dat::File& i_file );
~Exh(); ~Exh() = default;
// Returns a const reference to the ExhHeader object
const ExhHeader& get_header() const; const ExhHeader& get_header() const;
// Returns a const reference to a vector of ExhExdDef objects
const std::vector< ExhExdDef >& get_exd_defs() const; const std::vector< ExhExdDef >& get_exd_defs() const;
// Returns a const reference to a vector of Language enums
const std::vector< Language >& get_languages() const; const std::vector< Language >& get_languages() const;
// Returns a const reference to a map of ExhMember objects, indexed by their offset
const std::map< uint32_t, ExhMember >& get_members() const; const std::map< uint32_t, ExhMember >& get_members() const;
// Returns a const reference to a vector of ExhMember objects
const std::vector< ExhMember >& get_exh_members() const; const std::vector< ExhMember >& get_exh_members() const;
protected: protected:
@ -126,6 +132,5 @@ namespace xiv
std::vector< Language > _languages; std::vector< Language > _languages;
}; };
} }// namespace exd
} }// namespace xiv::exd

View file

@ -10,10 +10,6 @@ namespace xiv::dat
{ {
} }
File::~File()
{
}
FileType File::get_type() const FileType File::get_type() const
{ {
return _type; return _type;
@ -31,12 +27,20 @@ namespace xiv::dat
void File::exportToFile( const std::filesystem::path& i_path ) const void File::exportToFile( const std::filesystem::path& i_path ) const
{ {
std::ofstream ofs( i_path.string(), std::ios_base::binary | std::ios_base::out ); std::ofstream ofs( i_path, std::ios::binary | std::ios::out );
for( auto& data_section : _data_sections )
if( !ofs )
{ {
ofs.write( data_section.data(), data_section.size() ); throw std::runtime_error( "Failed to open the output file: " + i_path.string() );
} }
ofs.close();
for( const auto& data_section : _data_sections )
{
ofs.write( reinterpret_cast< const char* >( data_section.data() ), static_cast< std::streamsize >( data_section.size() ) );
}
// The file stream will be closed automatically when the ofstream object goes out of scope
// ofs.close();
} }
} }

20
deps/datReader/File.h vendored
View file

@ -27,20 +27,26 @@ namespace xiv::dat
public: public:
File(); File();
~File(); ~File() = default;
// Returns the file type of the File object
FileType get_type() const; FileType get_type() const;
// Getters functions for the data in the file // Returns a const reference to the data sections in the File object
const std::vector< std::vector< char>>& get_data_sections() const; const std::vector< std::vector< char > >& get_data_sections() const;
std::vector< std::vector< char>>& access_data_sections(); // Returns a reference to the data sections in the File object
std::vector< std::vector< char > >& access_data_sections();
// Exports the content of the File object to a file on disk at the given path
void exportToFile( const std::filesystem::path& i_path ) const; void exportToFile( const std::filesystem::path& i_path ) const;
protected: protected:
// Stores the file type of the File object
FileType _type; FileType _type;
std::vector< std::vector< char>> _data_sections;
}; // Stores the data sections of the File object as a vector of vectors of chars
} std::vector< std::vector< char > > _data_sections;
};
}// namespace xiv::dat

View file

@ -89,5 +89,4 @@ namespace xiv::dat
std::unordered_map< uint32_t, std::unique_ptr< std::mutex>> m_catCreationMutexes; std::unordered_map< uint32_t, std::unique_ptr< std::mutex>> m_catCreationMutexes;
}; };
} }// namespace xiv::dat

View file

@ -1,5 +1,4 @@
#ifndef XIV_DAT_INDEX_H #pragma once
#define XIV_DAT_INDEX_H
#include "SqPack.h" #include "SqPack.h"
@ -58,6 +57,4 @@ namespace xiv::dat
HashTable m_hashTable; HashTable m_hashTable;
}; };
} }// namespace xiv::dat
#endif // XIV_DAT_INDEX_H

View file

@ -1,5 +1,4 @@
#ifndef XIV_DAT_SQPACK_H #pragma once
#define XIV_DAT_SQPACK_H
#include <fstream> #include <fstream>
@ -15,7 +14,7 @@ namespace xiv::dat
uint8_t hash[0x14]; uint8_t hash[0x14];
uint32_t padding[0xB]; uint32_t padding[0xB];
}; };
} }// namespace xiv::dat
namespace xiv::utils::bparse namespace xiv::utils::bparse
{ {
@ -31,7 +30,7 @@ namespace xiv::utils::bparse
xiv::utils::bparse::reorder( i_struct.padding[ i ] ); xiv::utils::bparse::reorder( i_struct.padding[ i ] );
} }
} }
}; };// namespace xiv::utils::bparse
namespace xiv::dat namespace xiv::dat
{ {
@ -53,6 +52,4 @@ namespace xiv::dat
std::ifstream m_handle; std::ifstream m_handle;
}; };
} }// namespace xiv::dat
#endif // XIV_DAT_SQPACK_H

View file

@ -2,7 +2,12 @@
std::string xiv::utils::bparse::extract_cstring( std::istream& i_stream, const std::string& i_name ) std::string xiv::utils::bparse::extract_cstring( std::istream& i_stream, const std::string& i_name )
{ {
std::string temp_str; // Using a stringstream and reading character by character avoids this issue and ensures all input is processed correctly.
std::getline( i_stream, temp_str, '\0' ); std::stringstream ss;
return temp_str; char c;
while( i_stream.get( c ) && c != '\0' )
{
ss << c;
}
return ss.str();
} }

View file

@ -7,15 +7,63 @@
namespace xiv::utils::bparse namespace xiv::utils::bparse
{ {
// Internal macro for byteswapping // Helper struct for compile-time unrolling of byteswap
template< int N > template< int N, bool Unroll >
void byteswap_impl( char (& bytes)[N] ) struct byteswap_impl_helper
{ {
for( auto p = std::begin( bytes ), end = std::end( bytes ) - 1; p < end; ++p, --end ) 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 ] );
}
}
}
}
}
};
template< int N >
struct byteswap_impl_helper< N, false >
{
static void swap( char ( &bytes )[ N ], int start )
{
// 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 ); std::swap( *p, *end );
} }
} }
};
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 );
}
// byteswapping any type (no pointers to array) // byteswapping any type (no pointers to array)
template< typename T > template< typename T >
@ -91,7 +139,6 @@ namespace xiv::utils::bparse
} }
} }
// For cstrings
std::string extract_cstring( std::istream& i_stream, const std::string& i_name ); std::string extract_cstring( std::istream& i_stream, const std::string& i_name );
} }

View file

@ -1,5 +1,5 @@
#include "conv.h" #include "conv.h"
#include <cstring>// for memcpy #include <cstring>
namespace xiv::utils::conv namespace xiv::utils::conv
{ {
@ -23,7 +23,7 @@ namespace xiv::utils::conv
t1 |= t2; // Re-insert sign bit t1 |= t2; // Re-insert sign bit
float result; float result;
memcpy( &result, &t1, sizeof( float ) );// Convert uint32_t to float using memcpy memcpy( &result, &t1, sizeof( float ) );
return result; return result;
} }

View file

@ -1,5 +1,4 @@
#ifndef XIV_UTILS_STREAM_H #pragma once
#define XIV_UTILS_STREAM_H
#include <memory> #include <memory>
#include <iostream> #include <iostream>
@ -8,14 +7,12 @@
namespace xiv::utils::stream namespace xiv::utils::stream
{ {
template< typename CharT, typename TraitsT = std::char_traits< CharT > > template< typename CharT, typename TraitsT = std::char_traits< CharT > >
class vectorwrapbuf : class vectorwrapbuf : public std::basic_streambuf< CharT, TraitsT >
public std::basic_streambuf< CharT, TraitsT >
{ {
public: public:
vectorwrapbuf( std::vector< CharT >& vec ) vectorwrapbuf( std::vector< CharT >& vec ) : std::basic_streambuf< CharT, TraitsT >()
{ {
this->setg( vec.data(), vec.data(), vec.data() + vec.size() ); this->setg( vec.data(), vec.data(), vec.data() + vec.size() );
} }
}; };
} }// namespace xiv::utils::stream
#endif // XIV_UTILS_STREAM_H

View file

@ -1,5 +1,4 @@
#ifndef XIV_UTILS_ZLIB_H #pragma once
#define XIV_UTILS_ZLIB_H
#include <cstddef> #include <cstddef>
#include <cstdint> #include <cstdint>
@ -12,6 +11,4 @@ namespace xiv::utils::zlib
void no_header_decompress( const uint8_t* in, size_t in_size, uint8_t* out, size_t out_size ); void no_header_decompress( const uint8_t* in, size_t in_size, uint8_t* out, size_t out_size );
} } // namespace xiv::utils::zlib
#endif // XIV_UTILS_ZLIB_H

View file

@ -32,14 +32,10 @@
#include <functional> #include <functional>
// fucking filesystem
#if _MSC_VER >= 1925
#include <filesystem> #include <filesystem>
namespace ci { namespace fs = std::filesystem; } namespace ci { namespace fs = std::filesystem; }
#else
#include <experimental/filesystem>
namespace ci { namespace fs = std::experimental::filesystem; }
#endif
//! Exception for when Watchdog can't locate a file or parse the wildcard //! Exception for when Watchdog can't locate a file or parse the wildcard
class WatchedFileSystemExc : public std::exception { class WatchedFileSystemExc : public std::exception {

View file

@ -31,15 +31,8 @@
#include <Util/CrashHandler.h> #include <Util/CrashHandler.h>
// fucking filesystem
#if _MSC_VER >= 1925
#include <filesystem> #include <filesystem>
namespace fs = std::filesystem; namespace fs = std::filesystem;
#else
#include <experimental/filesystem>
namespace fs = std::experimental::filesystem;
#endif
Sapphire::Common::Util::CrashHandler crashHandler; Sapphire::Common::Util::CrashHandler crashHandler;

View file

@ -2,13 +2,8 @@
#include <iostream> #include <iostream>
#include <fstream> #include <fstream>
#if _MSC_VER >= 1925
#include <filesystem> #include <filesystem>
namespace fs = std::filesystem; namespace fs = std::filesystem;
#else
#include <experimental/filesystem>
namespace fs = std::experimental::filesystem;
#endif
using namespace Sapphire; using namespace Sapphire;
using namespace Sapphire::Common; using namespace Sapphire::Common;

View file

@ -7,30 +7,23 @@
#include <spdlog/sinks/stdout_color_sinks.h> #include <spdlog/sinks/stdout_color_sinks.h>
#include <spdlog/sinks/daily_file_sink.h> #include <spdlog/sinks/daily_file_sink.h>
// #include <iostream>
#if _MSC_VER >= 1925
#include <filesystem> #include <filesystem>
namespace fs = std::filesystem; namespace fs = std::filesystem;
#else
#include <experimental/filesystem>
namespace fs = std::experimental::filesystem;
#endif
void Sapphire::Logger::init( const std::string& logPath ) void Sapphire::Logger::init( const std::string& logPath )
{ {
auto pos = logPath.find_last_of( fs::path::preferred_separator ); fs::path log_file_path(logPath);
fs::path log_directory = log_file_path.parent_path();
if( pos != std::string::npos ) if( !log_directory.empty() )
{ {
std::string realPath = logPath.substr( 0, pos ); fs::create_directories(log_directory);
fs::create_directories( realPath );
} }
spdlog::init_thread_pool( 8192, 1 ); spdlog::init_thread_pool( 8192, 1 );
auto stdout_sink = std::make_shared< spdlog::sinks::stdout_color_sink_mt >(); auto stdout_sink = std::make_shared< spdlog::sinks::stdout_color_sink_mt >();
auto daily_sink = std::make_shared< spdlog::sinks::daily_file_sink_mt >( logPath + ".log", 0, 0 ); auto daily_sink = std::make_shared< spdlog::sinks::daily_file_sink_mt >( log_file_path.string() + ".log", 0, 0 );
std::vector< spdlog::sink_ptr > sinks { stdout_sink, daily_sink }; std::vector< spdlog::sink_ptr > sinks { stdout_sink, daily_sink };

View file

@ -1,12 +1,10 @@
#include "Hive.h"
#include "Acceptor.h" #include "Acceptor.h"
#include "Connection.h" #include "Connection.h"
#include "Hive.h"
using namespace Sapphire; using namespace Sapphire;
Network::Acceptor::Acceptor( HivePtr hive ) : Network::Acceptor::Acceptor( HivePtr hive ) : m_hive( hive ),
m_hive( hive ),
m_acceptor( hive->getService() ), m_acceptor( hive->getService() ),
m_io_strand( hive->getService() ), m_io_strand( hive->getService() ),
m_error_state( 0 ) m_error_state( 0 )
@ -77,7 +75,10 @@ void Network::Acceptor::handleAccept( const asio::error_code& error, ConnectionP
void Network::Acceptor::stop() void Network::Acceptor::stop()
{ {
// Cancel all operations and close the acceptor
asio::error_code ec;
m_acceptor.cancel( ec );
m_acceptor.close( ec );
} }
void Network::Acceptor::accept( ConnectionPtr connection ) void Network::Acceptor::accept( ConnectionPtr connection )
@ -97,13 +98,15 @@ void Network::Acceptor::listen( const std::string& host, const uint16_t& port )
m_acceptor.set_option( asio::ip::tcp::acceptor::reuse_address( false ) ); m_acceptor.set_option( asio::ip::tcp::acceptor::reuse_address( false ) );
m_acceptor.bind( endpoint ); m_acceptor.bind( endpoint );
m_acceptor.listen( asio::socket_base::max_connections ); m_acceptor.listen( asio::socket_base::max_connections );
} } catch( const asio::system_error& ex )
catch( ... )
{ {
// this should not happen // Call the onError function to handle the error
assert( true ); onError( ex.code() );
} catch( ... )
{
// Call the onError function with a generic error code
onError( asio::error::operation_aborted );
} }
} }
Network::HivePtr Network::Acceptor::getHive() Network::HivePtr Network::Acceptor::getHive()
@ -116,6 +119,11 @@ asio::ip::tcp::acceptor& Network::Acceptor::getAcceptor()
return m_acceptor; return m_acceptor;
} }
asio::strand& Network::Acceptor::getStrand()
{
return m_io_strand;
}
bool Network::Acceptor::hasError() bool Network::Acceptor::hasError()
{ {
uint32_t v1 = 1; uint32_t v1 = 1;

View file

@ -148,9 +148,9 @@ namespace Sapphire::Network
acceptor->accept( connection ); acceptor->accept( connection );
return connection; return connection;
} }
catch( std::runtime_error e ) catch( ... )
{ {
throw; throw std::runtime_error( "Failed to add server to hive" );
} }
} }

View file

@ -1,67 +1,58 @@
#pragma once #pragma once
#include <memory> #include <memory>
#include <thread>
#include <mutex> #include <mutex>
#include <queue> #include <queue>
#include <algorithm>
#include <utility> #include <utility>
namespace Sapphire::Common::Util namespace Sapphire::Common::Util
{ {
// The LockedQueue class template is a thread-safe wrapper around std::queue.
// It ensures that only one thread can access the underlying queue at a time
// by using a std::mutex for synchronization.
template< class T > template< class T >
class LockedQueue class LockedQueue
{ {
public: public:
LockedQueue(); LockedQueue() = default;
~LockedQueue() = default;
~LockedQueue(); // Returns the size of the queue in a thread-safe manner.
// Locks the mutex before accessing the queue and unlocks it after
// the operation is complete.
std::size_t size() const;
// Removes the front element from the queue and returns it
// in a thread-safe manner. If the queue is empty, it returns
// a default-constructed object of type T.
// Locks the mutex before accessing the queue and unlocks it after
// the operation is complete.
T pop(); T pop();
//we can pass this in by reference, instead of copying // Adds an object to the end of the queue, using a const reference.
void push( const T object ); // The object is copied into the queue in a thread-safe manner.
// Locks the mutex before accessing the queue and unlocks it after
//we can pass this in by reference // the operation is complete.
//this will push it onto the queue, and swap the object void push( const T& object );
// with a default-constructed T at the same time.
void push_swap( T& object );
void push_reset( T& object );
std::size_t size();
// Adds an object to the end of the queue, using an rvalue reference.
// The object is moved into the queue in a thread-safe manner.
// Locks the mutex before accessing the queue and unlocks it after
// the operation is complete.
void push( T&& object );
protected: protected:
std::queue< T > m_queue; std::queue< T > m_queue;
std::mutex m_mutex; mutable std::mutex m_mutex;// Make mutex mutable to be used in const member functions
}; };
template< class T > template< class T >
LockedQueue< T >::LockedQueue() std::size_t LockedQueue< T >::size() const
{
}
template< class T >
std::size_t LockedQueue< T >::size()
{ {
std::lock_guard< std::mutex > lock( m_mutex ); std::lock_guard< std::mutex > lock( m_mutex );
return m_queue.size(); return m_queue.size();
} }
template< class T >
LockedQueue< T >::~LockedQueue()
{
}
template< class T > template< class T >
T LockedQueue< T >::pop() T LockedQueue< T >::pop()
{ {
@ -72,58 +63,24 @@ namespace Sapphire::Common::Util
return T(); return T();
} }
T result = m_queue.front(); T result = std::move( m_queue.front() );
m_queue.pop(); m_queue.pop();
return result; return result;
} }
template< class T > template< class T >
void LockedQueue< T >::push( const T object ) void LockedQueue< T >::push( const T& object )
{ {
std::lock_guard< std::mutex > lock( m_mutex ); std::lock_guard< std::mutex > lock( m_mutex );
m_queue.push( object ); m_queue.push( object );
} }
template< class T > template< class T >
void LockedQueue< T >::push_swap( T& object ) void LockedQueue< T >::push( T&& object )
{ {
std::lock_guard< std::mutex > lock( m_mutex ); std::lock_guard< std::mutex > lock( m_mutex );
m_queue.emplace( std::forward< T >( object ) );
m_queue.push( object );
T default_ctored_object = T();
//this is a special swap that will do a legit naive swap normally,
// except if there exists a function called T::swap(), which is
// specialized and possibly faster.
std::swap( object, default_ctored_object );
//default_ctored_object is now the value of object, and it will go out
// of scope here. In the case that T is a shared_ptr of some kind,
// this will allow that the object on the queue is the *last* shared_ptr
// in existance by the time this function returns.
} }
template< class T > }// namespace Sapphire::Common::Util
void LockedQueue< T >::push_reset( T& object )
{
std::lock_guard< std::mutex > lock( m_mutex );
m_queue.push( object );
T default_ctored_object = T();
object.reset();
//default_ctored_object is now the value of object, and it will go out
// of scope here. In the case that T is a shared_ptr of some kind,
// this will allow that the object on the queue is the *last* shared_ptr
// in existance by the time this function returns.
}
}

View file

@ -1,16 +1,18 @@
#ifndef _LOCKED_WAIT_H #pragma once
#define _LOCKED_WAIT_H
#include <atomic>
#include <condition_variable> #include <condition_variable>
#include <mutex> #include <mutex>
#include <queue> #include <queue>
#include <atomic>
#include <type_traits> #include <type_traits>
#include <utility> #include <utility>
namespace Sapphire::Common::Util namespace Sapphire::Common::Util
{ {
// The LockedWaitQueue class template is a thread-safe wrapper around std::queue
// that provides blocking and non-blocking operations for concurrent access.
// It uses a combination of a std::mutex and a std::condition_variable to
// synchronize access to the queue and allow waiting for new elements to arrive.
template< typename T > template< typename T >
class LockedWaitQueue class LockedWaitQueue
{ {
@ -21,20 +23,25 @@ namespace Sapphire::Common::Util
std::atomic< bool > m_shutdown; std::atomic< bool > m_shutdown;
public: public:
LockedWaitQueue< T >() : m_shutdown( false )
LockedWaitQueue< T >() :
m_shutdown( false )
{ {
} }
void push( const T& value ) // Adds an element to the end of the queue. The element can be passed as an
// lvalue or rvalue reference, and it will be either copied or moved into
// the queue as appropriate. The queue is locked during the push operation,
// and a waiting thread is notified using the condition variable.
template< typename U >
void push( U&& value )
{ {
std::lock_guard< std::mutex > lock( m_queueLock ); std::lock_guard< std::mutex > lock( m_queueLock );
m_queue.push( std::move( value ) ); m_queue.push( std::forward< U >( value ) );
m_condition.notify_one(); m_condition.notify_one();
} }
// Returns true if the queue is empty, false otherwise. The queue is locked
// during the check.
bool empty() bool empty()
{ {
std::lock_guard< std::mutex > lock( m_queueLock ); std::lock_guard< std::mutex > lock( m_queueLock );
@ -42,6 +49,10 @@ namespace Sapphire::Common::Util
return m_queue.empty(); return m_queue.empty();
} }
// Tries to remove the front element from the queue and move it into the
// provided reference. Returns true if an element was successfully popped,
// or false if the queue was empty or the wait queue was in shutdown state.
// The queue is locked during the pop operation.
bool pop( T& value ) bool pop( T& value )
{ {
std::lock_guard< std::mutex > lock( m_queueLock ); std::lock_guard< std::mutex > lock( m_queueLock );
@ -49,13 +60,17 @@ namespace Sapphire::Common::Util
if( m_queue.empty() || m_shutdown ) if( m_queue.empty() || m_shutdown )
return false; return false;
value = m_queue.front(); value = std::move( m_queue.front() );
m_queue.pop(); m_queue.pop();
return true; return true;
} }
// Waits for an element to become available in the queue and then removes
// it and moves it into the provided reference. If the queue is empty and
// not in shutdown state, the thread will block until an element is added
// or the wait queue is shut down. The queue is locked during the operation.
void waitAndPop( T& value ) void waitAndPop( T& value )
{ {
std::unique_lock< std::mutex > lock( m_queueLock ); std::unique_lock< std::mutex > lock( m_queueLock );
@ -66,11 +81,15 @@ namespace Sapphire::Common::Util
if( m_queue.empty() || m_shutdown ) if( m_queue.empty() || m_shutdown )
return; return;
value = m_queue.front(); value = std::move( m_queue.front() );
m_queue.pop(); m_queue.pop();
} }
// Shuts down the wait queue, deleting all elements currently in the queue
// (if T is a pointer type) and notifying all waiting threads. Any future
// calls to pop() or waitAndPop() will return immediately with false or
// without modifying the provided reference, respectively.
void cancel() void cancel()
{ {
std::unique_lock< std::mutex > lock( m_queueLock ); std::unique_lock< std::mutex > lock( m_queueLock );
@ -90,6 +109,8 @@ namespace Sapphire::Common::Util
} }
private: private:
// Helper functions for deleting queued objects depending on whether T is
// a pointer type or not.
template< typename E = T > template< typename E = T >
typename std::enable_if< std::is_pointer< E >::value >::type deleteQueuedObject( E& obj ) typename std::enable_if< std::is_pointer< E >::value >::type deleteQueuedObject( E& obj )
{ {
@ -101,7 +122,4 @@ namespace Sapphire::Common::Util
{ {
} }
}; };
} }// namespace Sapphire::Common::Util
#endif

View file

@ -1,13 +1,13 @@
#ifndef SAPPHIRE_SPAWNINDEXALLOCATOR_H #pragma once
#define SAPPHIRE_SPAWNINDEXALLOCATOR_H
#include <queue>
#include <unordered_map>
#include <type_traits> #include <type_traits>
#include <unordered_set>
namespace Sapphire::Common::Util namespace Sapphire::Common::Util
{ {
// The SpawnIndexAllocator class template is a utility class that allocates
// unique indices for spawned objects in the game world. It is parameterized
// by the index type T and an optional actor ID type.
template< typename T, typename ActorIdType = uint32_t > template< typename T, typename ActorIdType = uint32_t >
class SpawnIndexAllocator class SpawnIndexAllocator
{ {
@ -16,92 +16,109 @@ namespace Sapphire::Common::Util
std::is_same< T, uint32_t >::value || std::is_same< T, uint64_t >::value, std::is_same< T, uint32_t >::value || std::is_same< T, uint64_t >::value,
"T must be uint8_t, uint16_t, uint32_t, uint64_t" ); "T must be uint8_t, uint16_t, uint32_t, uint64_t" );
SpawnIndexAllocator() : // Constructor for the SpawnIndexAllocator, initializing internal variables
m_maxSlotId( 0 ), // such as maximum slot ID, reserve first slot flag, and allocation failure ID.
m_reserveFirstSlot( false ) SpawnIndexAllocator() : m_maxSlotId( 0 ),
m_reserveFirstSlot( false ),
m_allocFailId( static_cast< T >( -1 ) )
{ {
} }
// Initializes the SpawnIndexAllocator by setting the maximum slot ID and
// whether to reserve the first slot. Sets up the free and used index sets.
void init( T maxSlotId, bool reserveFirstSlot = false ) void init( T maxSlotId, bool reserveFirstSlot = false )
{ {
m_maxSlotId = maxSlotId; m_maxSlotId = maxSlotId;
m_reserveFirstSlot = reserveFirstSlot; m_reserveFirstSlot = reserveFirstSlot;
setupQueue(); setupSet();
// todo: reserve max slot id in map to prevent any runtime reshashing // reserve max slot id in set to prevent any runtime rehashing
m_freeIndexes.reserve( m_maxSlotId );
m_usedIndexes.reserve( m_maxSlotId );
} }
// Frees a used spawn index, given an actor ID. Removes the index from the
// used set and adds it back to the free set. Returns the freed index or
// the allocation failure ID if the actor ID is not found.
T freeUsedSpawnIndex( ActorIdType actorId ) T freeUsedSpawnIndex( ActorIdType actorId )
{ {
auto it = m_actorIdToAllocatedMap.find( actorId ); auto it = m_usedIndexes.find( actorId );
if( it == m_actorIdToAllocatedMap.end() ) if( it == m_usedIndexes.end() )
return getAllocFailId(); return m_allocFailId;
auto index = it->second; auto index = *it;
m_availableIds.push( index ); m_usedIndexes.erase( it );
m_actorIdToAllocatedMap.erase( it ); m_freeIndexes.insert( index );
return index; return index;
} }
// Allocates the next free spawn index and associates it with the given
// actor ID. Removes the index from the free set and adds it to the used set.
// Returns the allocated index or the allocation failure ID if there are no
// free indices.
T getNextFreeSpawnIndex( ActorIdType actorId ) T getNextFreeSpawnIndex( ActorIdType actorId )
{ {
assert( m_maxSlotId != 0 ); assert( m_maxSlotId != 0 );
if( m_availableIds.empty() ) if( m_freeIndexes.empty() )
return getAllocFailId(); return m_allocFailId;
auto nextId = m_availableIds.front(); auto nextId = *m_freeIndexes.begin();
m_availableIds.pop(); m_freeIndexes.erase( m_freeIndexes.begin() );
m_usedIndexes.insert( nextId );
m_actorIdToAllocatedMap[ actorId ] = nextId;
return nextId; return nextId;
} }
// Frees all used spawn indices and clears the used set. Resets the free
// set to contain all possible indices.
void freeAllSpawnIndexes() void freeAllSpawnIndexes()
{ {
setupQueue(); setupSet();
m_actorIdToAllocatedMap.clear(); m_usedIndexes.clear();
} }
// Returns true if the given spawn index is valid (i.e., not equal to the
// allocation failure ID), false otherwise.
bool isSpawnIndexValid( T spawnIndex ) bool isSpawnIndexValid( T spawnIndex )
{ {
return spawnIndex != getAllocFailId(); return spawnIndex != m_allocFailId;
} }
// Returns the allocation failure ID.
constexpr T getAllocFailId() const constexpr T getAllocFailId() const
{ {
return static_cast< T >( -1 ); return m_allocFailId;
} }
protected: protected:
void setupQueue() // Sets up the free index set by inserting all possible spawn indices
// into the set, optionally reserving the first slot.
void setupSet()
{ {
assert( m_maxSlotId != 0 ); assert( m_maxSlotId != 0 );
while( !m_availableIds.empty() ) m_freeIndexes.clear();
m_availableIds.pop();
uint32_t start = 0; uint32_t start = 0;
// slot 0 is reserved when used for spawning actors/players otherwise the local player actor spawnIndex // slot is reserved when used for spawning actors/players otherwise the local player actor spawnIndex
// will be used by another actor and despawn the local player // will be used by another actor and despawn the local player
if( m_reserveFirstSlot ) if( m_reserveFirstSlot )
start = 1; start = 1;
for( uint32_t i = start; i < m_maxSlotId; i++ ) for( uint32_t i = start; i < m_maxSlotId; i++ )
m_availableIds.push( i ); m_freeIndexes.insert( i );
} }
std::queue< T > m_availableIds; std::unordered_set< T > m_freeIndexes;
std::unordered_map< ActorIdType, T > m_actorIdToAllocatedMap; std::unordered_set< T > m_usedIndexes;
T m_maxSlotId; T m_maxSlotId;
bool m_reserveFirstSlot; bool m_reserveFirstSlot;
const T m_allocFailId;
}; };
} }// namespace Sapphire::Common::Util
#endif //SAPPHIRE_SPAWNINDEXALLOCATOR_H

View file

@ -122,15 +122,14 @@ std::string Util::binaryToHexDump( uint8_t* pBinData, uint16_t size )
uint64_t Util::getTimeMs() uint64_t Util::getTimeMs()
{ {
std::chrono::milliseconds epoch = std::chrono::duration_cast< std::chrono::milliseconds > const auto epoch = std::chrono::duration_cast< std::chrono::milliseconds >( std::chrono::steady_clock::now().time_since_epoch() );
( std::chrono::system_clock::now().time_since_epoch() );
return epoch.count(); return epoch.count();
} }
uint32_t Util::getTimeSeconds() uint32_t Util::getTimeSeconds()
{ {
auto currClock = std::chrono::system_clock::now(); const auto currClock = std::chrono::steady_clock::now();
return static_cast< uint32_t >( std::chrono::time_point_cast< std::chrono::seconds >( currClock ).time_since_epoch().count() ); return static_cast< uint32_t >( std::chrono::duration_cast< std::chrono::seconds >( currClock.time_since_epoch() ).count() );
} }
uint64_t Util::getEorzeanTimeStamp() uint64_t Util::getEorzeanTimeStamp()
@ -140,18 +139,17 @@ uint64_t Util::getEorzeanTimeStamp()
void Util::valueToFlagByteIndexValue( uint32_t inVal, uint8_t& outVal, uint16_t& outIndex ) void Util::valueToFlagByteIndexValue( uint32_t inVal, uint8_t& outVal, uint16_t& outIndex )
{ {
uint32_t id = inVal; const uint32_t id = inVal;
outIndex = id / 8; outIndex = id / 8;
uint8_t bitIndex = id % 8; const uint8_t bitIndex = id % 8;
outVal = 1 << bitIndex; outVal = 1 << bitIndex;
} }
std::string Util::fmtUtcTime( const std::string& fmt ) std::string Util::fmtUtcTime( const std::string& fmt )
{ {
auto t = std::time( nullptr ); const auto t = std::time( nullptr );
auto tm = std::gmtime( &t ); const auto tm = std::gmtime( &t );
std::stringstream ss; std::stringstream ss;

View file

@ -8,33 +8,44 @@
namespace Sapphire::Common::Util namespace Sapphire::Common::Util
{ {
// Retrieves the operation code from a raw network packet
uint16_t getOpCode( Network::Packets::FFXIVARR_PACKET_RAW& raw ); uint16_t getOpCode( Network::Packets::FFXIVARR_PACKET_RAW& raw );
// Converts binary data to a hexadecimal string representation
std::string binaryToHexString( uint8_t* pBinData, uint16_t size ); std::string binaryToHexString( uint8_t* pBinData, uint16_t size );
// Converts binary data to a formatted hexadecimal dump
std::string binaryToHexDump( uint8_t* pBinData, uint16_t size ); std::string binaryToHexDump( uint8_t* pBinData, uint16_t size );
// Converts an integer value to a hexadecimal string representation with the specified width
std::string intToHexString( uint64_t intValue, uint8_t width = 2 ); std::string intToHexString( uint64_t intValue, uint8_t width = 2 );
// Erases all occurrences of the specified character from the input/output string
void eraseAll( std::string& inOutStr, char remove ); void eraseAll( std::string& inOutStr, char remove );
// Erases all occurrences of any character in the remove string from the input/output string
void eraseAllIn( std::string& inOutStr, std::string& remove ); void eraseAllIn( std::string& inOutStr, std::string& remove );
// Returns a lowercase copy of the input string
std::string toLowerCopy( const std::string& inStr ); std::string toLowerCopy( const std::string& inStr );
// Formats the current UTC time according to the given format string
std::string fmtUtcTime( const std::string& fmt ); std::string fmtUtcTime( const std::string& fmt );
// Gets the current time in milliseconds
uint64_t getTimeMs(); uint64_t getTimeMs();
/*! /*!
* Gets the current time in seconds
* @brief Get a POSIX epoch representation of the current time * @brief Get a POSIX epoch representation of the current time
* @return 32-bit unsigned integer * @return 32-bit unsigned integer
*/ */
uint32_t getTimeSeconds(); uint32_t getTimeSeconds();
// Gets the current Eorzean timestamp (used in the game world)
uint64_t getEorzeanTimeStamp(); uint64_t getEorzeanTimeStamp();
// Converts a value to a flag byte index value pair
void valueToFlagByteIndexValue( uint32_t inVal, uint8_t& outVal, uint16_t& outIndex ); void valueToFlagByteIndexValue( uint32_t inVal, uint8_t& outVal, uint16_t& outIndex );
template< class T > template< class T >

View file

@ -7,28 +7,40 @@
namespace Sapphire::Common::Util namespace Sapphire::Common::Util
{ {
// Computes the squared distance between two 3D points (x, y, z) and (x1, y1, z1)
float distanceSq( float x, float y, float z, float x1, float y1, float z1 ); float distanceSq( float x, float y, float z, float x1, float y1, float z1 );
// Computes the distance between two 3D points (x, y, z) and (x1, y1, z1)
float distance( float x, float y, float z, float x1, float y1, float z1 ); float distance( float x, float y, float z, float x1, float y1, float z1 );
// Computes the distance between two 3D positions using FFXIVARR_POSITION3 structures
float distance( const Common::FFXIVARR_POSITION3& pos1, const Common::FFXIVARR_POSITION3& pos2 ); float distance( const Common::FFXIVARR_POSITION3& pos1, const Common::FFXIVARR_POSITION3& pos2 );
// Computes the squared distance between two 2D points (x, y) and (x1, y1)
float distance2DSq( float x, float y, float x1, float y1 ); float distance2DSq( float x, float y, float x1, float y1 );
// Computes the distance between two 2D points (x, y) and (x1, y1)
float distance2D( float x, float y, float x1, float y1 ); float distance2D( float x, float y, float x1, float y1 );
// Calculates the angle (in radians) to a point (x1, y1) from a point (x, y)
float calcAngTo( float x, float y, float x1, float y1 ); float calcAngTo( float x, float y, float x1, float y1 );
// Calculates the angle (in radians) from a point (x1, y1) to a point (x, y)
float calcAngFrom( float x, float y, float x1, float y1 ); float calcAngFrom( float x, float y, float x1, float y1 );
// Truncates a floating-point value to a specified number of digits
float trunc( float value, uint8_t digitsToRemain ); float trunc( float value, uint8_t digitsToRemain );
// Converts a float to a uint16_t
uint16_t floatToUInt16( float val ); uint16_t floatToUInt16( float val );
// Converts a float to a uint16_t representing a rotation angle
uint16_t floatToUInt16Rot( float val ); uint16_t floatToUInt16Rot( float val );
// Converts a float to a uint8_t representing a rotation angle
uint8_t floatToUInt8Rot( float val ); uint8_t floatToUInt8Rot( float val );
// Clamps a value between a minimum and maximum value
template < typename T > template < typename T >
T clamp( T val, T minimum, T maximum ) T clamp( T val, T minimum, T maximum )
{ {
@ -41,8 +53,10 @@ namespace Sapphire::Common::Util
return val; return val;
} }
// Transforms a 3D position vector using a 3x3 matrix
FFXIVARR_POSITION3 transform( const FFXIVARR_POSITION3& vector, const Matrix33& matrix ); FFXIVARR_POSITION3 transform( const FFXIVARR_POSITION3& vector, const Matrix33& matrix );
// Converts a 3D position representing Euler angles to a direction
float eulerToDirection( const FFXIVARR_POSITION3& euler ); float eulerToDirection( const FFXIVARR_POSITION3& euler );
} }

View file

@ -1,14 +0,0 @@
#include "Vector3.h"
using namespace Sapphire::Common;
inline bool FFXIVARR_POSITION3::operator == ( const FFXIVARR_POSITION3& target ) const
{
return x == target.x && y == target.y && z == target.z;
}
inline bool Vector3::operator == ( const Vector3& target ) const
{
return x == target.x && y == target.y && z == target.z && reserve == target.reserve;
}

View file

@ -1,5 +1,7 @@
#pragma once #pragma once
#include <cmath>
namespace Sapphire::Common namespace Sapphire::Common
{ {
struct FFXIVARR_POSITION3 struct FFXIVARR_POSITION3
@ -7,7 +9,24 @@ namespace Sapphire::Common
float x; float x;
float y; float y;
float z; float z;
inline bool operator == ( const FFXIVARR_POSITION3& target ) const;
// Checks for equality between two FFXIVARR_POSITION3 objects
inline bool operator==( const FFXIVARR_POSITION3& target ) const
{
return x == target.x && y == target.y && z == target.z;
}
// Adds two FFXIVARR_POSITION3 objects
inline FFXIVARR_POSITION3 operator+( const FFXIVARR_POSITION3& target ) const
{
return { x + target.x, y + target.y, z + target.z };
}
// Subtracts two FFXIVARR_POSITION3 objects
inline FFXIVARR_POSITION3 operator-( const FFXIVARR_POSITION3& target ) const
{
return { x - target.x, y - target.y, z - target.z };
}
}; };
struct Vector3 struct Vector3
@ -16,11 +35,59 @@ namespace Sapphire::Common
float y; float y;
float z; float z;
float reserve; float reserve;
inline bool operator == ( const Vector3& target ) const;
// Checks for equality between two Vector3 objects
inline bool operator==( const Vector3& target ) const
{
return x == target.x && y == target.y && z == target.z;
}
// Adds two Vector3 objects
inline Vector3 operator+( const Vector3& target ) const
{
return { x + target.x, y + target.y, z + target.z, reserve };
}
// Subtracts two Vector3 objects
inline Vector3 operator-( const Vector3& target ) const
{
return { x - target.x, y - target.y, z - target.z, reserve };
}
// Scales a Vector3 object by a scalar value
inline Vector3 operator*( float scalar ) const
{
return { x * scalar, y * scalar, z * scalar, reserve };
}
// Computes the dot product of two Vector3 objects
inline float dot( const Vector3& target ) const
{
return x * target.x + y * target.y + z * target.z;
}
// Computes the cross product of two Vector3 objects
inline Vector3 cross( const Vector3& target ) const
{
return { y * target.z - z * target.y, z * target.x - x * target.z, x * target.y - y * target.x, reserve };
}
// Computes the length (magnitude) of a Vector3 object
inline float length() const
{
return std::sqrt( x * x + y * y + z * z );
}
// Normalizes a Vector3 object
inline Vector3 normalize() const
{
float len = length();
return { x / len, y / len, z / len, reserve };
}
}; };
struct Matrix33 struct Matrix33
{ {
float m[ 3 ][ 3 ]; float m[ 3 ][ 3 ];
}; };
} }// namespace Sapphire::Common

View file

@ -6,19 +6,13 @@
#include <sstream> #include <sstream>
#include <Logging/Logger.h> #include <Logging/Logger.h>
#include <common/Util/Util.h> #include <Util/Util.h>
using namespace Sapphire; using namespace Sapphire;
using namespace Sapphire::Common; using namespace Sapphire::Common;
// fucking filesystem
#if _MSC_VER >= 1925
#include <filesystem> #include <filesystem>
namespace fs = std::filesystem; namespace fs = std::filesystem;
#else
#include <experimental/filesystem>
namespace fs = std::experimental::filesystem;
#endif
DbManager::DbManager( const std::string& host, const std::string& database, const std::string& user, const std::string& pw, uint16_t port ) : DbManager::DbManager( const std::string& host, const std::string& database, const std::string& user, const std::string& pw, uint16_t port ) :

View file

@ -2,21 +2,16 @@
#include <iostream> #include <iostream>
#include <cctype> #include <cctype>
#include <set> #include <set>
#include <common/Logging/Logger.h> #include <Logging/Logger.h>
#include <MySqlConnector.h> #include <MySqlConnector.h>
#include <common/Util/CrashHandler.h> #include <Util/CrashHandler.h>
#include <common/Config/ConfigMgr.h> #include <Config/ConfigMgr.h>
Sapphire::Common::Util::CrashHandler crashHandler; Sapphire::Common::Util::CrashHandler crashHandler;
// fucking filesystem
#if _MSC_VER >= 1925
#include <filesystem> #include <filesystem>
namespace filesys = std::filesystem; namespace fs = std::filesystem;
#else
#include <experimental/filesystem>
namespace filesys = std::experimental::filesystem;
#endif
#include <fstream> #include <fstream>
#include <streambuf> #include <streambuf>
@ -36,19 +31,19 @@ std::vector< std::string > getAllFilesInDir( const std::string& dirPath,
try try
{ {
// Check if given path exists and points to a directory // Check if given path exists and points to a directory
if( filesys::exists( dirPath ) && filesys::is_directory( dirPath ) ) if( fs::exists( dirPath ) && fs::is_directory( dirPath ) )
{ {
// Create a Recursive Directory Iterator object and points to the starting of directory // Create a Recursive Directory Iterator object and points to the starting of directory
filesys::recursive_directory_iterator iter( dirPath ); fs::recursive_directory_iterator iter( dirPath );
// Create a Recursive Directory Iterator object pointing to end. // Create a Recursive Directory Iterator object pointing to end.
filesys::recursive_directory_iterator end; fs::recursive_directory_iterator end;
// Iterate till end // Iterate till end
while( iter != end ) while( iter != end )
{ {
// Check if current entry is a directory and if exists in skip list // Check if current entry is a directory and if exists in skip list
if( filesys::is_directory( iter->path() ) && if( fs::is_directory( iter->path() ) &&
( std::find( dirSkipList.begin(), dirSkipList.end(), iter->path().filename() ) != dirSkipList.end() ) ) ( std::find( dirSkipList.begin(), dirSkipList.end(), iter->path().filename() ) != dirSkipList.end() ) )
{ {
// Skip the iteration of current directory pointed by iterator // Skip the iteration of current directory pointed by iterator

View file

@ -1,5 +1,5 @@
#include <Common.h> #include <Common.h>
#include <Vector3.cpp> #include <Vector3.h>
#include <Network/CommonNetwork.h> #include <Network/CommonNetwork.h>
#include <Network/GamePacket.h> #include <Network/GamePacket.h>
#include <Network/CommonActorControl.h> #include <Network/CommonActorControl.h>

View file

@ -56,7 +56,8 @@ bool Sapphire::Scripting::ScriptMgr::init()
std::set< std::string > files; std::set< std::string > files;
auto& server = Common::Service< World::WorldServer >::ref(); auto& server = Common::Service< World::WorldServer >::ref();
auto status = loadDir( server.getConfig().scripts.path, files, m_nativeScriptMgr->getModuleExtension() ); fs::path script_path( server.getConfig().scripts.path );
auto status = loadDir( script_path.string(), files, m_nativeScriptMgr->getModuleExtension() );
if( !status ) if( !status )
{ {
@ -67,13 +68,13 @@ bool Sapphire::Scripting::ScriptMgr::init()
uint32_t scriptsFound = 0; uint32_t scriptsFound = 0;
uint32_t scriptsLoaded = 0; uint32_t scriptsLoaded = 0;
for( auto itr = files.begin(); itr != files.end(); ++itr ) for( const auto& file_path_str : files )
{ {
auto& path = *itr; fs::path file_path( file_path_str );
scriptsFound++; scriptsFound++;
if( m_nativeScriptMgr->loadScript( path ) ) if( m_nativeScriptMgr->loadScript( file_path.string() ) )
scriptsLoaded++; scriptsLoaded++;
} }

View file

@ -180,15 +180,29 @@ void WorldServer::run( int32_t argc, char* argv[] )
auto verString = readFileToString( verPath ); auto verString = readFileToString( verPath );
if( verString != m_config.global.general.dataVersion ) if( verString != m_config.global.general.dataVersion )
{ {
Logger::fatal( "Sqpack version {} does not match expected version {}!", verString, m_config.global.general.dataVersion ); Logger::warn( "Sqpack version {} does not match expected version {}!", verString, m_config.global.general.dataVersion );
return;
} std::string response;
} do
catch ( const std::exception& e ) {
Logger::warn( "Continuing with mismatched versions may cause unexpected behavior, and you will not receive support. Continue at your own risk." );
Logger::warn( "Do you wish to continue? (yes/no): " );
std::cin >> response;
std::transform( response.begin(), response.end(), response.begin(), ::tolower );
} while( response != "yes" && response != "no" );
if( response == "no" )
{ {
Logger::fatal( e.what() );
return; return;
} }
}
}
catch( const std::exception& e )
{
Logger::warn( "Failed to retrieve the server's SqPack version. Supported server version is: {}", m_config.global.general.dataVersion );
Logger::warn( "Ensure the server data version is configured correctly before seeking help or creating an issue." );
Logger::warn( "Reason: {}", e.what() );
}
if( !pExdData->init( dataPath ) ) if( !pExdData->init( dataPath ) )
{ {

View file

@ -5,6 +5,7 @@
#include <mutex> #include <mutex>
#include <map> #include <map>
#include <set> #include <set>
#include <thread>
#include "ForwardsZone.h" #include "ForwardsZone.h"
#include <Config/ConfigDef.h> #include <Config/ConfigDef.h>