diff --git a/cmake/compiler.cmake b/cmake/compiler.cmake index 5272b2b3..00d622c3 100644 --- a/cmake/compiler.cmake +++ b/cmake/compiler.cmake @@ -1,7 +1,24 @@ - 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_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() add_definitions( -D_WIN32_WINNT=0x601 ) add_definitions( -D_CRT_SECURE_NO_WARNINGS ) diff --git a/deps/datReader/Dat.cpp b/deps/datReader/Dat.cpp index f8f458d9..eab4ed01 100644 --- a/deps/datReader/Dat.cpp +++ b/deps/datReader/Dat.cpp @@ -276,37 +276,61 @@ namespace xiv::dat default: throw std::runtime_error( - "Invalid entry_type: " + std::to_string( static_cast(file_header.entry_type) ) ); + "Invalid entry_type: " + std::to_string( static_cast< uint32_t >( file_header.entry_type ) ) ); } } 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 ); + if( !m_handle.good() ) + { + throw std::runtime_error( "Failed to set stream position." ); + } + 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(); - 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 if( block_header.compressed_size == 32000 ) { 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 { // If it is compressed use zlib // Read the data to be decompressed - std::vector< char > temp_buffer( block_header.compressed_size ); - m_handle.read( temp_buffer.data(), block_header.compressed_size ); + auto temp_buffer = std::make_unique< char[] >( 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() ), - temp_buffer.size(), + if( !m_handle.good() ) + { + 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 ), static_cast< size_t >( block_header.uncompressed_size ) ); } diff --git a/deps/datReader/DatCat.cpp b/deps/datReader/DatCat.cpp index 6f612552..d6662dc9 100644 --- a/deps/datReader/DatCat.cpp +++ b/deps/datReader/DatCat.cpp @@ -5,73 +5,71 @@ #include "GameData.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 ), + m_catNum( catNum ), + m_chunk( -1 ) { + // Format the category number as a two-digit hex string + std::stringstream ss; + ss << std::setw( 2 ) << std::setfill( '0' ) << std::hex << catNum; + std::string prefix = ss.str() + "0000.win32"; - Cat::Cat( const std::filesystem::path& basePath, uint32_t catNum, const std::string& name ) : m_name( name ), - m_catNum( catNum ), - m_chunk( -1 ) + // Create an Index object using the formatted category number and basePath + 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 ) { - std::stringstream ss; - ss << std::setw( 2 ) << std::setfill( '0' ) << std::hex << catNum; - std::string prefix = ss.str() + "0000.win32"; - - m_index = std::make_unique< Index >( basePath / "ffxiv" / ( prefix + ".index" ) ); - - 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 ) ); } + } - Cat::Cat( const std::filesystem::path& basePath, uint32_t catNum, const std::string& name, uint32_t exNum, uint32_t chunk ) : m_name( name ), - m_catNum( catNum ), - m_chunk( chunk ) + Cat::Cat( const std::filesystem::path& basePath, uint32_t catNum, const std::string& name, uint32_t exNum, uint32_t chunk ) : m_name( name ), + m_catNum( catNum ), + 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" ) ); + + // Create Dat objects for each entry in the index + for( uint32_t i = 0; i < getIndex().getDatCount(); ++i ) { - m_index = std::make_unique< Index >( basePath / GameData::buildDatStr( "ex" + std::to_string( exNum ), catNum, exNum, chunk, "win32", "index" ) ); - - 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 - { - return *m_index; - } + const Index& Cat::getIndex() const + { + return *m_index; + } - std::unique_ptr< File > Cat::getFile( uint32_t dir_hash, uint32_t filename_hash ) const - { - auto& hash_table_entry = getIndex().getHashTableEntry( dir_hash, filename_hash ); - return m_dats[ hash_table_entry.datNum ]->getFile( hash_table_entry.datOffset ); - } + std::unique_ptr< File > Cat::getFile( uint32_t dir_hash, uint32_t filename_hash ) const + { + auto& hash_table_entry = getIndex().getHashTableEntry( dir_hash, filename_hash ); + return m_dats[ hash_table_entry.datNum ]->getFile( hash_table_entry.datOffset ); + } - bool Cat::doesFileExist( uint32_t dir_hash, uint32_t filename_hash ) const - { - return getIndex().doesFileExist( dir_hash, filename_hash ); - } + bool Cat::doesFileExist( uint32_t dir_hash, uint32_t filename_hash ) const + { + return getIndex().doesFileExist( dir_hash, filename_hash ); + } - bool Cat::doesDirExist( uint32_t dir_hash ) const - { - return getIndex().doesDirExist( dir_hash ); - } + bool Cat::doesDirExist( uint32_t dir_hash ) const + { + return getIndex().doesDirExist( dir_hash ); + } - const std::string& Cat::getName() const - { - return m_name; - } + const std::string& Cat::getName() const + { + return m_name; + } - uint32_t Cat::getCatNum() const - { - return m_catNum; - } - - }// namespace dat -}// namespace xiv \ No newline at end of file + uint32_t Cat::getCatNum() const + { + return m_catNum; + } +}// namespace xiv::dat \ No newline at end of file diff --git a/deps/datReader/DatCat.h b/deps/datReader/DatCat.h index deb5477a..f5ab5203 100644 --- a/deps/datReader/DatCat.h +++ b/deps/datReader/DatCat.h @@ -38,16 +38,16 @@ namespace xiv::dat // Retrieve a file from the category given its hashes 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; + // Returns whether a directory exists in the Cat based on the given directory hash bool doesDirExist( uint32_t dir_hash ) const; - - // Returns thename of the category + // Returns the name of the Cat object const std::string& getName() const; - // Returns the number of the category + // Returns the category number of the Cat object uint32_t getCatNum() const; protected: diff --git a/deps/datReader/DatCategories/bg/lgb.h b/deps/datReader/DatCategories/bg/lgb.h index 547d5b0e..1c3b0d93 100644 --- a/deps/datReader/DatCategories/bg/lgb.h +++ b/deps/datReader/DatCategories/bg/lgb.h @@ -82,7 +82,8 @@ public: }; }; -struct LGB_ENPC_ENTRY : public LgbEntry { +struct LGB_ENPC_ENTRY : public LgbEntry +{ public: ENpcData data; std::string_view name; @@ -94,7 +95,8 @@ public: }; }; -struct LGB_EOBJ_ENTRY : public LgbEntry { +struct LGB_EOBJ_ENTRY : public LgbEntry +{ public: EObjData data; std::string_view name; @@ -106,7 +108,8 @@ public: }; }; -struct LGB_MAP_RANGE_ENTRY : public LgbEntry { +struct LGB_MAP_RANGE_ENTRY : public LgbEntry +{ public: MapRangeData data; std::string_view name; @@ -118,7 +121,8 @@ public: }; }; -struct LGB_EXIT_RANGE_ENTRY : public LgbEntry { +struct LGB_EXIT_RANGE_ENTRY : public LgbEntry +{ public: ExitRangeData data; std::string_view name; @@ -130,7 +134,8 @@ public: }; }; -struct LGB_POP_RANGE_ENTRY : public LgbEntry { +struct LGB_POP_RANGE_ENTRY : public LgbEntry +{ public: PopRangeData data; @@ -140,7 +145,8 @@ public: }; }; -struct LGB_EVENT_RANGE_ENTRY : public LgbEntry { +struct LGB_EVENT_RANGE_ENTRY : public LgbEntry +{ public: EventRangeData data; @@ -198,7 +204,7 @@ struct LGB_GROUP LGB_FILE* parent; LGB_GROUP_HEADER header; LayerSetReferencedList layerSetReferencedList; - std::string name; + std::string_view name; std::vector< std::shared_ptr< LgbEntry > > entries; std::vector< LayerSetReferenced > refs; @@ -206,19 +212,32 @@ struct LGB_GROUP { parent = parentStruct; 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 ); + // Check if there are any layer set references to initialize if( layerSetReferencedList.LayerSetCount > 0 ) { - refs.resize( layerSetReferencedList.LayerSetCount ); - std::memcpy( refs.data(), buf + offset + header.LayerSetRef + layerSetReferencedList.LayerSets, layerSetReferencedList.LayerSetCount * sizeof( LayerSetReferenced ) ); + // Reserve memory for layer set references + 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 ); + // Calculate the offset for entries 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 ) { const auto entryOffset = entriesOffset + *reinterpret_cast< int32_t* >( buf + ( entriesOffset + i * 4 ) ); @@ -277,7 +296,7 @@ struct LGB_GROUP } catch( std::exception& e ) { - std::cout << name << " " << e.what() << std::endl; + throw e; } } }; @@ -296,63 +315,38 @@ struct LGB_FILE_HEADER int32_t groupCount; }; -struct LGB_FILE { +struct LGB_FILE +{ LGB_FILE_HEADER header; std::vector< LGB_GROUP > groups; 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 ) { m_name = name; } + // Constructor that initializes an LGB_FILE object from a buffer LGB_FILE( char* buf ) { + // Reinterpret the buffer as an LGB_FILE_HEADER pointer and dereference it 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 ) + { throw std::runtime_error( "Invalid LGB file!" ); + } 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 ) { const auto groupOffset = baseOffset + *reinterpret_cast< int32_t* >( buf + ( baseOffset + i * 4 ) ); groups.emplace_back( buf, this, groupOffset ); } - }; + } }; - -/* -#if __cplusplus >= 201703L -#include -std::map getLgbFiles( const std::string& dir ) -{ - namespace fs = std::experimental::filesystem; - std::map 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 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 -*/ \ No newline at end of file diff --git a/deps/datReader/DatCategories/bg/matrix4.h b/deps/datReader/DatCategories/bg/matrix4.h index fdcee84c..bd0608a1 100644 --- a/deps/datReader/DatCategories/bg/matrix4.h +++ b/deps/datReader/DatCategories/bg/matrix4.h @@ -86,24 +86,29 @@ struct matrix4 return ret; } + // Multiplies two 4x4 matrices and returns the result matrix4 operator*( const matrix4& rhs ) const { 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 ) = - ( *this )( i, 0 ) * rhs( 0, 0 ) + ( *this )( i, 1 ) * rhs( 1, 0 ) + ( *this )( i, 2 ) * rhs( 2, 0 ) + - ( *this )( i, 3 ) * rhs( 3, 0 ); - ret( i, 1 ) = - ( *this )( i, 0 ) * rhs( 0, 1 ) + ( *this )( i, 1 ) * rhs( 1, 1 ) + ( *this )( i, 2 ) * rhs( 2, 1 ) + - ( *this )( i, 3 ) * rhs( 3, 1 ); - ret( i, 2 ) = - ( *this )( i, 0 ) * rhs( 0, 2 ) + ( *this )( i, 1 ) * rhs( 1, 2 ) + ( *this )( i, 2 ) * rhs( 2, 2 ) + - ( *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 ); + // Iterate through each column of the resulting matrix + for( unsigned int col = 0; col < 4; col++ ) + { + // Calculate the value for the current cell by summing the product of the corresponding row and column elements + float value = 0; + for( unsigned int k = 0; k < 4; k++ ) + { + value += ( *this )( row, k ) * rhs( k, col ); + } + + // Assign the calculated value to the corresponding cell of the resulting matrix + ret( row, col ) = value; + } } + return ret; } }; diff --git a/deps/datReader/DatCategories/bg/sgb.h b/deps/datReader/DatCategories/bg/sgb.h index d595593a..0ab5676a 100644 --- a/deps/datReader/DatCategories/bg/sgb.h +++ b/deps/datReader/DatCategories/bg/sgb.h @@ -1,5 +1,4 @@ -#ifndef _SGB_H -#define _SGB_H +#pragma once #include #include @@ -132,24 +131,24 @@ struct SGB_MODEL_ENTRY : public SGB_GROUP_ENTRY { SGB_MODEL_HEADER header; SgbGroupEntryType type; - std::string name; - std::string modelFileName; - std::string collisionFileName; + std::string_view name; + std::string_view modelFileName; + std::string_view collisionFileName; SGB_MODEL_ENTRY( char* buf, size_t offset, SgbGroupEntryType type ) { this->type = type; header = *reinterpret_cast< SGB_MODEL_HEADER* >( buf + offset ); - name = std::string( buf + offset + header.nameOffset ); - modelFileName = std::string( buf + offset + header.modelFileOffset ); - collisionFileName = std::string( buf + offset + header.collisionFileOffset ); + name = std::string_view( buf + offset + header.nameOffset ); + modelFileName = std::string_view( buf + offset + header.modelFileOffset ); + collisionFileName = std::string_view( buf + offset + header.collisionFileOffset ); } }; struct SGB_GROUP { SGB_GROUP_HEADER header; - std::string name; + std::string_view name; SGB_FILE* parent; std::vector< std::shared_ptr< SGB_GROUP_ENTRY > > entries; @@ -157,23 +156,32 @@ struct SGB_GROUP { parent = file; 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 ); + entries.reserve( header.entryCount ); + for( auto i = 0; i < header.entryCount; ++i ) { auto entryOffset = entriesOffset + *reinterpret_cast< uint32_t* >( buf + ( entriesOffset + ( i * 4 ) ) ); if( entryOffset > fileSize ) throw std::runtime_error( "SGB_GROUP entry offset was larger than SGB file size!" ); + 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 ) ); - } - else - { - // std::cout << "\t\tUnknown SGB entry! Group: " << name << " type: " << type << " index: " << i << " entryOffset: " << entryOffset << "\n"; + case SgbGroupEntryType::Model: + case SgbGroupEntryType::Gimmick: + { + entries.emplace_back( std::make_shared< SGB_MODEL_ENTRY >( buf, entryOffset, ( SgbGroupEntryType ) type ) ); + break; + } + default: + { + break; + } } } } @@ -268,27 +276,28 @@ struct SGB_FILE try { + entries.reserve( 2 ); + 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 ); - entries.push_back( group2 ); + entries.emplace_back( group2 ); + uint32_t stateCount = *reinterpret_cast< uint32_t* >( buf + baseOffset + header.statesOffset + 4 ); if( stateCount > 0 ) { - stateCount = stateCount; + stateEntries.reserve( stateCount ); + for( size_t i = 0; i < stateCount; ++i ) { auto state = SGB_STATE_ENTRY( buf + baseOffset + header.statesOffset + 8 + i * sizeof( SGB_STATE_HEADER ) ); - stateEntries.push_back( state ); - std::cout << state.name << "\n"; + stateEntries.emplace_back( state ); } } } 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 \ No newline at end of file +}; \ No newline at end of file diff --git a/deps/datReader/Exd.cpp b/deps/datReader/Exd.cpp index b1a2a2b5..82c05e8e 100644 --- a/deps/datReader/Exd.cpp +++ b/deps/datReader/Exd.cpp @@ -9,12 +9,11 @@ using xiv::utils::bparse::extract; 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; _files = i_files; - // Iterates over all the files const uint32_t member_count = static_cast< uint32_t >( _exh->get_members().size() ); for( auto& file_ptr : _files ) @@ -29,8 +28,6 @@ namespace xiv::exd // Preallocate and extract the record_indices 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 ) { 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 ) { - } - - const std::vector< Field > Exd::get_row( uint32_t id, uint32_t subRow ) - { - + // Check if id is in the cache auto cacheEntryIt = _idCache.find( id ); if( cacheEntryIt == _idCache.end() ) 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() ); 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::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 ]; fields.reserve( member_count ); 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 ) ); + // Iterate over the member entries and extract the corresponding data for( auto& member_entry : _exh->get_exh_members() ) { - // Seek to the position of the member to extract. - // 6 is because we have uint32_t/uint16_t at the start of each record + // Seek to the position of the member to extract iss.seekg( offset + member_entry.offset ); // Switch depending on the type to extract diff --git a/deps/datReader/Exd.h b/deps/datReader/Exd.h index e08f6e54..66122f53 100644 --- a/deps/datReader/Exd.h +++ b/deps/datReader/Exd.h @@ -1,18 +1,18 @@ #pragma once -#include #include +#include #include #include -#include "File.h" #include "Exd/Common.h" #include "Exd/Structs.h" +#include "Exh.h" +#include "File.h" +#include "bparse.h" #include "stream.h" #include -#include "Exh.h" -#include "bparse.h" namespace xiv::exd { @@ -27,7 +27,7 @@ namespace xiv::exd uint32_t id; uint32_t offset; }; -} +}// namespace xiv::exd namespace xiv::utils::bparse { @@ -63,16 +63,16 @@ namespace xiv::exd // Field type containing all the possible types in the data files using Field = std::variant< - std::string, - bool, - int8_t, - uint8_t, - int16_t, - uint16_t, - int32_t, - uint32_t, - float, - uint64_t >; + std::string, + bool, + int8_t, + uint8_t, + int16_t, + uint16_t, + int32_t, + uint32_t, + float, + uint64_t >; struct ExdCacheEntry { @@ -86,13 +86,11 @@ namespace xiv::exd public: // i_exh: the header // 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 const std::vector< Field > get_row( uint32_t id ); @@ -107,9 +105,8 @@ namespace xiv::exd if( sizeof( T ) != _exh->get_header().data_offset ) { - throw std::runtime_error( - "the struct size (" + std::to_string( sizeof( T ) ) + ") doesn't match the size in the header (" + - std::to_string( _exh->get_header().data_offset ) + ")!" ); + throw std::runtime_error( "the struct size (" + std::to_string( sizeof( T ) ) + ") doesn't match the size in the header (" + + std::to_string( _exh->get_header().data_offset ) + ")!" ); } // Iterates over all the files @@ -126,7 +123,7 @@ namespace xiv::exd fields.reserve( member_count ); iss.seekg( cacheEntryIt->second.offset + 6 ); - iss.read( reinterpret_cast( &pSheet.get()->_data ), sizeof( T ) ); + iss.read( reinterpret_cast< char* >( &pSheet.get()->_data ), sizeof( T ) ); int stringCount = 0; for( auto& member_entry : _exh->get_exh_members() ) @@ -148,7 +145,7 @@ namespace xiv::exd std::string value = utils::bparse::extract_cstring( iss, "string" ); auto it = pSheet->_strings.insert( pSheet->_strings.end(), value ); *reinterpret_cast< uint32_t* >( pSheet->ptr() + member_entry.offset ) = - static_cast< uint32_t >( std::distance( pSheet->_strings.begin(), it ) ); + static_cast< uint32_t >( std::distance( pSheet->_strings.begin(), it ) ); } break; @@ -166,46 +163,46 @@ namespace xiv::exd case DataType::int16: - { - int16_t value = bparse::extract< int16_t >( iss, "int16_t", false ); - *reinterpret_cast< int16_t* >( pSheet->ptr() + member_entry.offset ) = value; - } - break; + { + int16_t value = bparse::extract< int16_t >( iss, "int16_t", false ); + *reinterpret_cast< int16_t* >( pSheet->ptr() + member_entry.offset ) = value; + } + break; case DataType::uint16: - { - uint16_t value = bparse::extract< uint16_t >( iss, "uint16_t", false ); - *reinterpret_cast< uint16_t* >( pSheet->ptr() + member_entry.offset ) = value; - } - break; + { + uint16_t value = bparse::extract< uint16_t >( iss, "uint16_t", false ); + *reinterpret_cast< uint16_t* >( pSheet->ptr() + member_entry.offset ) = value; + } + break; case DataType::int32: - { - int32_t value = bparse::extract< int32_t >( iss, "int32_t", false ); - *reinterpret_cast< int32_t* >( pSheet->ptr() + member_entry.offset ) = value; - } - break; + { + int32_t value = bparse::extract< int32_t >( iss, "int32_t", false ); + *reinterpret_cast< int32_t* >( pSheet->ptr() + member_entry.offset ) = value; + } + break; case DataType::uint32: - { - uint32_t value = bparse::extract< uint32_t >( iss, "uint32_t", false ); - *reinterpret_cast< uint32_t* >( pSheet->ptr() + member_entry.offset ) = value; - } - break; + { + uint32_t value = bparse::extract< uint32_t >( iss, "uint32_t", false ); + *reinterpret_cast< uint32_t* >( pSheet->ptr() + member_entry.offset ) = value; + } + break; case DataType::float32: - { - float value = bparse::extract< float >( iss, "float", false ); - *reinterpret_cast< float* >( pSheet->ptr() + member_entry.offset ) = value; - } - break; + { + float value = bparse::extract< float >( iss, "float", false ); + *reinterpret_cast< float* >( pSheet->ptr() + member_entry.offset ) = value; + } + break; case DataType::uint64: - { - uint64_t value = bparse::extract< uint64_t >( iss, "uint64_t", false ); - *reinterpret_cast< uint64_t* >( pSheet->ptr() + member_entry.offset ) = value; - } - break; + { + uint64_t value = bparse::extract< uint64_t >( iss, "uint64_t", false ); + *reinterpret_cast< uint64_t* >( pSheet->ptr() + member_entry.offset ) = value; + } + break; default: auto type = static_cast< uint16_t >( member_entry.type ); @@ -221,7 +218,6 @@ namespace xiv::exd } return pSheet; - } // Get a row by its id and sub-row @@ -237,7 +233,7 @@ namespace xiv::exd std::unordered_map< uint32_t, std::shared_ptr< Excel::ExcelStruct< T > > > sheets; // 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 ) { // Get a stream @@ -270,7 +266,7 @@ namespace xiv::exd fields.reserve( member_count ); iss.seekg( cacheEntryIt->second.offset + 6 ); - iss.read( reinterpret_cast( &pSheet.get()->_data ), sizeof( T ) ); + iss.read( reinterpret_cast< char* >( &pSheet.get()->_data ), sizeof( T ) ); int stringCount = 0; for( auto& member_entry : _exh->get_exh_members() ) @@ -292,7 +288,7 @@ namespace xiv::exd std::string value = xiv::utils::bparse::extract_cstring( iss, "string" ); auto it = pSheet->_strings.insert( pSheet->_strings.end(), value ); *reinterpret_cast< uint32_t* >( pSheet->ptr() + member_entry.offset ) = - static_cast< uint32_t >( std::distance( pSheet->_strings.begin(), it ) ); + static_cast< uint32_t >( std::distance( pSheet->_strings.begin(), it ) ); } break; @@ -310,46 +306,46 @@ namespace xiv::exd case DataType::int16: - { - int16_t value = xiv::utils::bparse::extract< int16_t >( iss, "int16_t", false ); - *reinterpret_cast< int16_t* >( pSheet->ptr() + member_entry.offset ) = value; - } - break; + { + int16_t value = xiv::utils::bparse::extract< int16_t >( iss, "int16_t", false ); + *reinterpret_cast< int16_t* >( pSheet->ptr() + member_entry.offset ) = value; + } + break; case DataType::uint16: - { - uint16_t value = xiv::utils::bparse::extract< uint16_t >( iss, "uint16_t", false ); - *reinterpret_cast< uint16_t* >( pSheet->ptr() + member_entry.offset ) = value; - } - break; + { + uint16_t value = xiv::utils::bparse::extract< uint16_t >( iss, "uint16_t", false ); + *reinterpret_cast< uint16_t* >( pSheet->ptr() + member_entry.offset ) = value; + } + break; case DataType::int32: - { - int32_t value = xiv::utils::bparse::extract< int32_t >( iss, "int32_t", false ); - *reinterpret_cast< int32_t* >( pSheet->ptr() + member_entry.offset ) = value; - } - break; + { + int32_t value = xiv::utils::bparse::extract< int32_t >( iss, "int32_t", false ); + *reinterpret_cast< int32_t* >( pSheet->ptr() + member_entry.offset ) = value; + } + break; case DataType::uint32: - { - uint32_t value = xiv::utils::bparse::extract< uint32_t >( iss, "uint32_t", false ); - *reinterpret_cast< uint32_t* >( pSheet->ptr() + member_entry.offset ) = value; - } - break; + { + uint32_t value = xiv::utils::bparse::extract< uint32_t >( iss, "uint32_t", false ); + *reinterpret_cast< uint32_t* >( pSheet->ptr() + member_entry.offset ) = value; + } + break; case DataType::float32: - { - float value = xiv::utils::bparse::extract< float >( iss, "float", false ); - *reinterpret_cast< float* >( pSheet->ptr() + member_entry.offset ) = value; - } - break; + { + float value = xiv::utils::bparse::extract< float >( iss, "float", false ); + *reinterpret_cast< float* >( pSheet->ptr() + member_entry.offset ) = value; + } + break; case DataType::uint64: - { - uint64_t value = xiv::utils::bparse::extract< uint64_t >( iss, "uint64_t", false ); - *reinterpret_cast< uint64_t* >( pSheet->ptr() + member_entry.offset ) = value; - } - break; + { + uint64_t value = xiv::utils::bparse::extract< uint64_t >( iss, "uint64_t", false ); + *reinterpret_cast< uint64_t* >( pSheet->ptr() + member_entry.offset ) = value; + } + break; default: auto type = static_cast< uint16_t >( member_entry.type ); @@ -379,5 +375,4 @@ namespace xiv::exd std::map< uint32_t, ExdCacheEntry > _idCache; }; -} - +}// namespace xiv::exd \ No newline at end of file diff --git a/deps/datReader/ExdCat.cpp b/deps/datReader/ExdCat.cpp index eb7534f8..dd7ae4bb 100644 --- a/deps/datReader/ExdCat.cpp +++ b/deps/datReader/ExdCat.cpp @@ -4,47 +4,42 @@ #include "GameData.h" -#include "Exh.h" #include "Exd.h" +#include "Exh.h" namespace { // Suffix of the filenames given a language - std::map language_map = + const std::map< xiv::exd::Language, std::string > language_map = { - {xiv::exd::Language::none, ""}, - {xiv::exd::Language::ja, "_ja"}, - {xiv::exd::Language::en, "_en"}, - {xiv::exd::Language::de, "_de"}, - {xiv::exd::Language::fr, "_fr"}, - {xiv::exd::Language::chs, "_chs"} + { xiv::exd::Language::none, "" }, + { xiv::exd::Language::ja, "_ja" }, + { xiv::exd::Language::en, "_en" }, + { xiv::exd::Language::de, "_de" }, + { xiv::exd::Language::fr, "_fr" }, + { xiv::exd::Language::chs, "_chs" } }; -} +}// namespace namespace xiv::exd { - Cat::Cat( dat::GameData& i_game_data, const std::string& i_name ) : - _name( i_name ) + Cat::Cat( dat::GameData& i_game_data, const std::string& i_name ) : _name( i_name ) { - //XIV_INFO(xiv_exd_logger, "Initializing Cat with name: " << i_name); // creates the header .exh - { - auto header_file = i_game_data.getFile( "exd/" + i_name + ".exh" ); - _header = std::shared_ptr< Exh >( new Exh( *header_file ) ); + auto header_file = i_game_data.getFile( "exd/" + i_name + ".exh" ); + _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 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) - std::vector< std::shared_ptr< dat::File>> files; - for( auto& exd_def: _header->get_exd_defs() ) + std::vector< std::shared_ptr< dat::File > > files; + for( auto& exd_def : _header->get_exd_defs() ) { 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" ) ); } // Instantiate the data for this language _data[ language ] = std::make_unique< Exd >( _header, files ); @@ -52,10 +47,7 @@ namespace xiv::exd } } - Cat::~Cat() - { - - } + Cat::~Cat() = default; const std::string& Cat::get_name() const { @@ -72,7 +64,7 @@ namespace xiv::exd auto ln_it = _data.find( i_language ); 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 ); @@ -89,4 +81,4 @@ namespace xiv::exd return *( ln_it->second ); } -} +}// namespace xiv::exd \ No newline at end of file diff --git a/deps/datReader/ExdCat.h b/deps/datReader/ExdCat.h index 5d4e9c4a..263fffb0 100644 --- a/deps/datReader/ExdCat.h +++ b/deps/datReader/ExdCat.h @@ -61,5 +61,5 @@ namespace xiv std::map> _data; }; - } -} + }// namespace exd +}// namespace xiv diff --git a/deps/datReader/ExdData.cpp b/deps/datReader/ExdData.cpp index 5307f5de..019cd798 100644 --- a/deps/datReader/ExdData.cpp +++ b/deps/datReader/ExdData.cpp @@ -7,90 +7,78 @@ #include "ExdCat.h" -namespace xiv::exd { - -ExdData::ExdData( dat::GameData& i_game_data ) try : - _game_data( i_game_data ) +namespace xiv::exd { - //XIV_INFO(xiv_exd_logger, "Initializing ExdData"); - - // Fetch the root.exl and get a stream from it - auto root_exl = i_game_data.getFile( "exd/root.exl" ); - std::vector< char > dataCpy = root_exl->get_data_sections().front(); - xiv::utils::stream::vectorwrapbuf< char > databuf( dataCpy ); - std::istream stream( &databuf ); - - // Iterates over the lines while skipping the first one - std::string line; - std::getline( stream, line ); // extract first line EXLT,2 - std::getline( stream, line ); - - // Until the EOF - while( !line.empty() ) + ExdData::ExdData( dat::GameData& i_game_data ) + try : _game_data( i_game_data ) { - // Format is cat_name,XX - // XX being an internal identifier - // Get only the cat_name - auto sep = line.find( ',' ); - auto category = line.substr( 0, sep ); - - // Add to the list of category name - // creates the empty category in the cats map - // instantiate the creation mutex for this category - _cat_names.push_back( category ); - _cats[ category ] = std::unique_ptr< Cat >(); - _cat_creation_mutexes[ category ] = std::make_unique< std::mutex >(); + // Fetch the root.exl and get a stream from it + auto root_exl = i_game_data.getFile( "exd/root.exl" ); + std::vector< char > dataCpy = root_exl->get_data_sections().front(); + xiv::utils::stream::vectorwrapbuf< char > databuf( dataCpy ); + std::istream stream( &databuf ); + // Skip the first line (EXLT,2) + std::string line; std::getline( stream, line ); - } -} -catch( std::exception& e ) -{ - // 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() ) ); -} -ExdData::~ExdData() -{ + // Read the remaining lines + while( std::getline( stream, line ) && !line.empty() ) + { + // Format is cat_name,XX + // XX being an internal identifier + // Get only the cat_name + auto sep = line.find( ',' ); + auto category = line.substr( 0, sep ); -} - -const std::vector< std::string >& ExdData::get_cat_names() const -{ - return _cat_names; -} - -const Cat& ExdData::get_category( const std::string& i_cat_name ) -{ - // Get the category from its name - auto cat_it = _cats.find( i_cat_name ); - if( cat_it == _cats.end() ) + // Add to the list of category names + // Create the empty category in the cats map + // Instantiate the creation mutex for this category + _cat_names.emplace_back( category ); + _cats[ category ] = nullptr; + _cat_creation_mutexes[ category ] = std::make_unique< std::mutex >(); + } + } catch( std::exception& e ) { - throw std::runtime_error( "Category not found: " + i_cat_name ); + // 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() ) ); } - if( cat_it->second ) + const std::vector< std::string >& ExdData::get_cat_names() const { - // If valid return it - return *( cat_it->second ); + return _cat_names; } - else - { - // If not, create it and return it - create_category( i_cat_name ); - return *( _cats[ i_cat_name ] ); - } -} -void ExdData::create_category( const std::string& i_cat_name ) -{ - // Lock mutex in this scope - 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) - if( !_cats[ i_cat_name ] ) + const Cat& ExdData::get_category( const std::string& i_cat_name ) { - _cats[ i_cat_name ] = std::make_unique< Cat >( _game_data, i_cat_name ); - } -} + // Get the category from its name + auto cat_it = _cats.find( i_cat_name ); + if( cat_it == _cats.end() ) + { + throw std::runtime_error( "Category not found: " + i_cat_name ); + } -} + if( cat_it->second ) + { + // If valid return it + return *( cat_it->second ); + } + else + { + // If not, create it and return it + create_category( i_cat_name ); + return *( _cats[ i_cat_name ] ); + } + } + + void ExdData::create_category( const std::string& i_cat_name ) + { + // Lock mutex in this scope + 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) + if( !_cats[ i_cat_name ] ) + { + _cats[ i_cat_name ] = std::make_unique< Cat >( _game_data, i_cat_name ); + } + } +}// namespace xiv::exd \ No newline at end of file diff --git a/deps/datReader/ExdData.h b/deps/datReader/ExdData.h index e6eea4fa..c3fc2511 100644 --- a/deps/datReader/ExdData.h +++ b/deps/datReader/ExdData.h @@ -25,32 +25,32 @@ namespace xiv { public: // Need an initialized dat::GameData to retrieve the files from the dat - ExdData(dat::GameData& i_game_data); - ~ExdData(); + ExdData( dat::GameData& i_game_data ); + ~ExdData() = default; // Get the list of thenames of the categories - const std::vector& get_cat_names() const; + const std::vector< std::string >& get_cat_names() const; // 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 - void export_as_csvs(const std::filesystem::path& i_output_path); + void export_as_csvs( const std::filesystem::path& i_output_path ); protected: // 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 dat::GameData& _game_data; // Categories, indexed by their name - std::unordered_map> _cats; + std::unordered_map< std::string, std::unique_ptr< Cat > > _cats; // List of category names = m_cats.keys() - std::vector _cat_names; + std::vector< std::string > _cat_names; // Mutexes used to avoid race condition when lazy instantiating a category - std::unordered_map> _cat_creation_mutexes; + std::unordered_map< std::string, std::unique_ptr< std::mutex > > _cat_creation_mutexes; }; - } -} + }// namespace exd +}// namespace xiv diff --git a/deps/datReader/Exh.cpp b/deps/datReader/Exh.cpp index a8c300b8..2e00e931 100644 --- a/deps/datReader/Exh.cpp +++ b/deps/datReader/Exh.cpp @@ -9,12 +9,12 @@ using xiv::utils::bparse::extract; namespace xiv::exd { - Exh::Exh( const dat::File& i_file ) { // Get a stream from the file 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 _header = extract< ExhHeader >( iss ); @@ -43,10 +43,6 @@ namespace xiv::exd } } - Exh::~Exh() - { - } - const ExhHeader& Exh::get_header() const { return _header; diff --git a/deps/datReader/Exh.h b/deps/datReader/Exh.h index b011a382..75592b61 100644 --- a/deps/datReader/Exh.h +++ b/deps/datReader/Exh.h @@ -44,46 +44,47 @@ namespace xiv::exd uint32_t start_id; uint32_t count_id; }; -}; +};// namespace xiv::exd -namespace xiv::utils::bparse { -template<> -inline void reorder< xiv::exd::ExhHeader >( xiv::exd::ExhHeader& i_struct ) +namespace xiv::utils::bparse { - for( int32_t i = 0; i < 0x4; ++i ) + template<> + inline void reorder< xiv::exd::ExhHeader >( xiv::exd::ExhHeader& i_struct ) { - xiv::utils::bparse::reorder( i_struct.magic[ i ] ); + for( int32_t i = 0; i < 0x4; ++i ) + { + xiv::utils::bparse::reorder( i_struct.magic[ i ] ); + } + i_struct.unknown = xiv::utils::bparse::byteswap( i_struct.unknown ); + xiv::utils::bparse::reorder( i_struct.unknown ); + i_struct.data_offset = xiv::utils::bparse::byteswap( i_struct.data_offset ); + xiv::utils::bparse::reorder( i_struct.data_offset ); + i_struct.field_count = xiv::utils::bparse::byteswap( i_struct.field_count ); + xiv::utils::bparse::reorder( i_struct.field_count ); + i_struct.exd_count = xiv::utils::bparse::byteswap( i_struct.exd_count ); + xiv::utils::bparse::reorder( i_struct.exd_count ); + i_struct.language_count = xiv::utils::bparse::byteswap( i_struct.language_count ); + xiv::utils::bparse::reorder( i_struct.language_count ); } - i_struct.unknown = xiv::utils::bparse::byteswap( i_struct.unknown ); - xiv::utils::bparse::reorder( i_struct.unknown ); - i_struct.data_offset = xiv::utils::bparse::byteswap( i_struct.data_offset ); - xiv::utils::bparse::reorder( i_struct.data_offset ); - i_struct.field_count = xiv::utils::bparse::byteswap( i_struct.field_count ); - xiv::utils::bparse::reorder( i_struct.field_count ); - i_struct.exd_count = xiv::utils::bparse::byteswap( i_struct.exd_count ); - xiv::utils::bparse::reorder( i_struct.exd_count ); - i_struct.language_count = xiv::utils::bparse::byteswap( i_struct.language_count ); - xiv::utils::bparse::reorder( i_struct.language_count ); -} -template<> -inline void reorder< xiv::exd::ExhMember >( xiv::exd::ExhMember& i_struct ) -{ - i_struct.type = xiv::utils::bparse::byteswap( i_struct.type ); - xiv::utils::bparse::reorder( i_struct.type ); - i_struct.offset = xiv::utils::bparse::byteswap( i_struct.offset ); - xiv::utils::bparse::reorder( i_struct.offset ); -} + template<> + inline void reorder< xiv::exd::ExhMember >( xiv::exd::ExhMember& i_struct ) + { + i_struct.type = xiv::utils::bparse::byteswap( i_struct.type ); + xiv::utils::bparse::reorder( i_struct.type ); + i_struct.offset = xiv::utils::bparse::byteswap( i_struct.offset ); + xiv::utils::bparse::reorder( i_struct.offset ); + } -template<> -inline void reorder< xiv::exd::ExhExdDef >( xiv::exd::ExhExdDef& i_struct ) -{ - i_struct.start_id = xiv::utils::bparse::byteswap( i_struct.start_id ); - xiv::utils::bparse::reorder( i_struct.start_id ); - i_struct.count_id = xiv::utils::bparse::byteswap( i_struct.count_id ); - xiv::utils::bparse::reorder( i_struct.count_id ); -} -}; + template<> + inline void reorder< xiv::exd::ExhExdDef >( xiv::exd::ExhExdDef& i_struct ) + { + i_struct.start_id = xiv::utils::bparse::byteswap( i_struct.start_id ); + xiv::utils::bparse::reorder( i_struct.start_id ); + i_struct.count_id = xiv::utils::bparse::byteswap( i_struct.count_id ); + xiv::utils::bparse::reorder( i_struct.count_id ); + } +};// namespace xiv::utils::bparse namespace xiv { @@ -105,16 +106,21 @@ namespace xiv // The header file Exh( const dat::File& i_file ); - ~Exh(); + ~Exh() = default; + // Returns a const reference to the ExhHeader object const ExhHeader& get_header() const; + // Returns a const reference to a vector of ExhExdDef objects 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; + // Returns a const reference to a map of ExhMember objects, indexed by their offset 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; protected: @@ -126,6 +132,5 @@ namespace xiv std::vector< Language > _languages; }; - } -} - + }// namespace exd +}// namespace xiv::exd \ No newline at end of file diff --git a/deps/datReader/File.cpp b/deps/datReader/File.cpp index e83659c1..133e08d5 100644 --- a/deps/datReader/File.cpp +++ b/deps/datReader/File.cpp @@ -10,10 +10,6 @@ namespace xiv::dat { } - File::~File() - { - } - FileType File::get_type() const { return _type; @@ -31,12 +27,20 @@ namespace xiv::dat void File::exportToFile( const std::filesystem::path& i_path ) const { - std::ofstream ofs( i_path.string(), std::ios_base::binary | std::ios_base::out ); - for( auto& data_section : _data_sections ) + std::ofstream ofs( i_path, std::ios::binary | std::ios::out ); + + 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(); } } diff --git a/deps/datReader/File.h b/deps/datReader/File.h index 3676a796..c1839fda 100644 --- a/deps/datReader/File.h +++ b/deps/datReader/File.h @@ -27,20 +27,26 @@ namespace xiv::dat public: File(); - ~File(); + ~File() = default; + // Returns the file type of the File object FileType get_type() const; - // Getters functions for the data in the file - const std::vector< std::vector< char>>& get_data_sections() const; + // Returns a const reference to the data sections in the File object + 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; protected: + // Stores the file type of the File object 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 diff --git a/deps/datReader/GameData.h b/deps/datReader/GameData.h index 19ab0cfa..8e43b2bf 100644 --- a/deps/datReader/GameData.h +++ b/deps/datReader/GameData.h @@ -89,5 +89,4 @@ namespace xiv::dat std::unordered_map< uint32_t, std::unique_ptr< std::mutex>> m_catCreationMutexes; }; -} - +}// namespace xiv::dat \ No newline at end of file diff --git a/deps/datReader/Index.h b/deps/datReader/Index.h index d98b96a6..f6b88152 100644 --- a/deps/datReader/Index.h +++ b/deps/datReader/Index.h @@ -1,5 +1,4 @@ -#ifndef XIV_DAT_INDEX_H -#define XIV_DAT_INDEX_H +#pragma once #include "SqPack.h" @@ -58,6 +57,4 @@ namespace xiv::dat HashTable m_hashTable; }; -} - -#endif // XIV_DAT_INDEX_H +}// namespace xiv::dat diff --git a/deps/datReader/SqPack.h b/deps/datReader/SqPack.h index 4c6e34c4..dc59c5f7 100644 --- a/deps/datReader/SqPack.h +++ b/deps/datReader/SqPack.h @@ -1,5 +1,4 @@ -#ifndef XIV_DAT_SQPACK_H -#define XIV_DAT_SQPACK_H +#pragma once #include @@ -15,7 +14,7 @@ namespace xiv::dat uint8_t hash[0x14]; uint32_t padding[0xB]; }; -} +}// namespace xiv::dat namespace xiv::utils::bparse { @@ -31,7 +30,7 @@ namespace xiv::utils::bparse xiv::utils::bparse::reorder( i_struct.padding[ i ] ); } } -}; +};// namespace xiv::utils::bparse namespace xiv::dat { @@ -53,6 +52,4 @@ namespace xiv::dat std::ifstream m_handle; }; -} - -#endif // XIV_DAT_SQPACK_H +}// namespace xiv::dat diff --git a/deps/datReader/bparse.cpp b/deps/datReader/bparse.cpp index dc36a7d6..ef4a3d69 100644 --- a/deps/datReader/bparse.cpp +++ b/deps/datReader/bparse.cpp @@ -2,7 +2,12 @@ std::string xiv::utils::bparse::extract_cstring( std::istream& i_stream, const std::string& i_name ) { - std::string temp_str; - std::getline( i_stream, temp_str, '\0' ); - return temp_str; + // Using a stringstream and reading character by character avoids this issue and ensures all input is processed correctly. + std::stringstream ss; + char c; + while( i_stream.get( c ) && c != '\0' ) + { + ss << c; + } + return ss.str(); } \ No newline at end of file diff --git a/deps/datReader/bparse.h b/deps/datReader/bparse.h index 6c722126..fbb7be6a 100644 --- a/deps/datReader/bparse.h +++ b/deps/datReader/bparse.h @@ -7,14 +7,62 @@ namespace xiv::utils::bparse { - // Internal macro for byteswapping - template< int N > - void byteswap_impl( char (& bytes)[N] ) + // Helper struct for compile-time unrolling of byteswap + template< int N, bool Unroll > + 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 ) { - std::swap( *p, *end ); + // 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 ); + } + } + }; + + 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) @@ -91,7 +139,6 @@ namespace xiv::utils::bparse } } - // For cstrings std::string extract_cstring( std::istream& i_stream, const std::string& i_name ); } diff --git a/deps/datReader/conv.cpp b/deps/datReader/conv.cpp index 111b5592..65ed5e7c 100644 --- a/deps/datReader/conv.cpp +++ b/deps/datReader/conv.cpp @@ -1,5 +1,5 @@ #include "conv.h" -#include // for memcpy +#include namespace xiv::utils::conv { @@ -23,7 +23,7 @@ namespace xiv::utils::conv t1 |= t2; // Re-insert sign bit float result; - memcpy( &result, &t1, sizeof( float ) );// Convert uint32_t to float using memcpy + memcpy( &result, &t1, sizeof( float ) ); return result; } diff --git a/deps/datReader/stream.h b/deps/datReader/stream.h index 37b9f28e..9e5512a7 100644 --- a/deps/datReader/stream.h +++ b/deps/datReader/stream.h @@ -1,5 +1,4 @@ -#ifndef XIV_UTILS_STREAM_H -#define XIV_UTILS_STREAM_H +#pragma once #include #include @@ -8,14 +7,12 @@ namespace xiv::utils::stream { template< typename CharT, typename TraitsT = std::char_traits< CharT > > - class vectorwrapbuf : - public std::basic_streambuf< CharT, TraitsT > + class vectorwrapbuf : public std::basic_streambuf< CharT, TraitsT > { 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() ); } }; -} -#endif // XIV_UTILS_STREAM_H +}// namespace xiv::utils::stream diff --git a/deps/datReader/zlib.h b/deps/datReader/zlib.h index 680014ef..f8f433cc 100644 --- a/deps/datReader/zlib.h +++ b/deps/datReader/zlib.h @@ -1,5 +1,4 @@ -#ifndef XIV_UTILS_ZLIB_H -#define XIV_UTILS_ZLIB_H +#pragma once #include #include @@ -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 ); -} - -#endif // XIV_UTILS_ZLIB_H +} // namespace xiv::utils::zlib diff --git a/deps/watchdog/Watchdog.h b/deps/watchdog/Watchdog.h index 08cc0678..f996d244 100644 --- a/deps/watchdog/Watchdog.h +++ b/deps/watchdog/Watchdog.h @@ -32,14 +32,10 @@ #include - // fucking filesystem -#if _MSC_VER >= 1925 + #include namespace ci { namespace fs = std::filesystem; } -#else -#include -namespace ci { namespace fs = std::experimental::filesystem; } -#endif + //! Exception for when Watchdog can't locate a file or parse the wildcard class WatchedFileSystemExc : public std::exception { diff --git a/src/api/main.cpp b/src/api/main.cpp index d0c231ce..d72368a5 100644 --- a/src/api/main.cpp +++ b/src/api/main.cpp @@ -31,15 +31,8 @@ #include -// fucking filesystem -#if _MSC_VER >= 1925 #include namespace fs = std::filesystem; -#else -#include -namespace fs = std::experimental::filesystem; -#endif - Sapphire::Common::Util::CrashHandler crashHandler; diff --git a/src/common/Common.h b/src/common/Common.h index 18e529d1..96573c8c 100644 --- a/src/common/Common.h +++ b/src/common/Common.h @@ -434,7 +434,7 @@ namespace Sapphire::Common Currency = 2000, Crystal = 2001, //UNKNOWN_0 = 2003, - KeyItem = 2004, + KeyItem = 2004, HandIn = 2005, DamagedGear = 2007, //UNKNOWN_1 = 2008, diff --git a/src/common/Config/ConfigMgr.cpp b/src/common/Config/ConfigMgr.cpp index 5d95e17f..5aed187f 100644 --- a/src/common/Config/ConfigMgr.cpp +++ b/src/common/Config/ConfigMgr.cpp @@ -2,13 +2,8 @@ #include #include -#if _MSC_VER >= 1925 #include namespace fs = std::filesystem; -#else -#include -namespace fs = std::experimental::filesystem; -#endif using namespace Sapphire; using namespace Sapphire::Common; diff --git a/src/common/Logging/Logger.cpp b/src/common/Logging/Logger.cpp index 82867199..af430a68 100644 --- a/src/common/Logging/Logger.cpp +++ b/src/common/Logging/Logger.cpp @@ -7,30 +7,23 @@ #include #include -// #include -#if _MSC_VER >= 1925 #include namespace fs = std::filesystem; -#else -#include -namespace fs = std::experimental::filesystem; -#endif - 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( realPath ); + fs::create_directories(log_directory); } spdlog::init_thread_pool( 8192, 1 ); 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 }; diff --git a/src/common/Network/Acceptor.cpp b/src/common/Network/Acceptor.cpp index 43c17d8f..a48fb275 100644 --- a/src/common/Network/Acceptor.cpp +++ b/src/common/Network/Acceptor.cpp @@ -1,15 +1,13 @@ -#include "Hive.h" #include "Acceptor.h" #include "Connection.h" - +#include "Hive.h" using namespace Sapphire; -Network::Acceptor::Acceptor( HivePtr hive ) : - m_hive( hive ), - m_acceptor( hive->getService() ), - m_io_strand( hive->getService() ), - m_error_state( 0 ) +Network::Acceptor::Acceptor( HivePtr hive ) : m_hive( hive ), + m_acceptor( hive->getService() ), + m_io_strand( hive->getService() ), + m_error_state( 0 ) { } @@ -77,7 +75,10 @@ void Network::Acceptor::handleAccept( const asio::error_code& error, ConnectionP 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 ) @@ -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.bind( endpoint ); m_acceptor.listen( asio::socket_base::max_connections ); - } - catch( ... ) + } catch( const asio::system_error& ex ) { - // this should not happen - assert( true ); + // Call the onError function to handle the error + onError( ex.code() ); + } catch( ... ) + { + // Call the onError function with a generic error code + onError( asio::error::operation_aborted ); } - } Network::HivePtr Network::Acceptor::getHive() @@ -116,6 +119,11 @@ asio::ip::tcp::acceptor& Network::Acceptor::getAcceptor() return m_acceptor; } +asio::strand& Network::Acceptor::getStrand() +{ + return m_io_strand; +} + bool Network::Acceptor::hasError() { uint32_t v1 = 1; diff --git a/src/common/Network/Connection.h b/src/common/Network/Connection.h index e64ce649..b80898df 100644 --- a/src/common/Network/Connection.h +++ b/src/common/Network/Connection.h @@ -148,9 +148,9 @@ namespace Sapphire::Network acceptor->accept( connection ); return connection; } - catch( std::runtime_error e ) + catch( ... ) { - throw; + throw std::runtime_error( "Failed to add server to hive" ); } } diff --git a/src/common/Util/LockedQueue.h b/src/common/Util/LockedQueue.h index 57397b59..1c1118a2 100644 --- a/src/common/Util/LockedQueue.h +++ b/src/common/Util/LockedQueue.h @@ -1,67 +1,58 @@ #pragma once #include -#include #include #include -#include #include 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 > class LockedQueue { 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(); - //we can pass this in by reference, instead of copying - void push( const T object ); - - //we can pass this in by reference - //this will push it onto the queue, and swap the 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 a const reference. + // The object is copied 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( const T& object ); + // 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: 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 > - LockedQueue< T >::LockedQueue() - { - - } - - template< class T > - std::size_t LockedQueue< T >::size() + std::size_t LockedQueue< T >::size() const { std::lock_guard< std::mutex > lock( m_mutex ); return m_queue.size(); } - - template< class T > - LockedQueue< T >::~LockedQueue() - { - - - } - - template< class T > T LockedQueue< T >::pop() { @@ -72,58 +63,24 @@ namespace Sapphire::Common::Util return T(); } - T result = m_queue.front(); - + T result = std::move( m_queue.front() ); m_queue.pop(); return result; } 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 ); m_queue.push( object ); } template< class T > - void LockedQueue< T >::push_swap( T& object ) + void LockedQueue< T >::push( T&& object ) { std::lock_guard< std::mutex > lock( m_mutex ); - - 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. - + m_queue.emplace( std::forward< T >( object ) ); } - template< class T > - 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. - - } - -} +}// namespace Sapphire::Common::Util \ No newline at end of file diff --git a/src/common/Util/LockedWaitQueue.h b/src/common/Util/LockedWaitQueue.h index 1a1ffdc3..16609c8a 100644 --- a/src/common/Util/LockedWaitQueue.h +++ b/src/common/Util/LockedWaitQueue.h @@ -1,16 +1,18 @@ -#ifndef _LOCKED_WAIT_H -#define _LOCKED_WAIT_H +#pragma once +#include #include #include #include -#include #include #include 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 > class LockedWaitQueue { @@ -21,20 +23,25 @@ namespace Sapphire::Common::Util std::atomic< bool > m_shutdown; 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 ); - m_queue.push( std::move( value ) ); + m_queue.push( std::forward< U >( value ) ); m_condition.notify_one(); } + // Returns true if the queue is empty, false otherwise. The queue is locked + // during the check. bool empty() { std::lock_guard< std::mutex > lock( m_queueLock ); @@ -42,6 +49,10 @@ namespace Sapphire::Common::Util 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 ) { std::lock_guard< std::mutex > lock( m_queueLock ); @@ -49,13 +60,17 @@ namespace Sapphire::Common::Util if( m_queue.empty() || m_shutdown ) return false; - value = m_queue.front(); + value = std::move( m_queue.front() ); m_queue.pop(); 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 ) { std::unique_lock< std::mutex > lock( m_queueLock ); @@ -66,11 +81,15 @@ namespace Sapphire::Common::Util if( m_queue.empty() || m_shutdown ) return; - value = m_queue.front(); + value = std::move( m_queue.front() ); 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() { std::unique_lock< std::mutex > lock( m_queueLock ); @@ -90,6 +109,8 @@ namespace Sapphire::Common::Util } private: + // Helper functions for deleting queued objects depending on whether T is + // a pointer type or not. template< typename E = T > typename std::enable_if< std::is_pointer< E >::value >::type deleteQueuedObject( E& obj ) { @@ -101,7 +122,4 @@ namespace Sapphire::Common::Util { } }; -} - -#endif - +}// namespace Sapphire::Common::Util diff --git a/src/common/Util/SpawnIndexAllocator.h b/src/common/Util/SpawnIndexAllocator.h index 27003e8f..c16d9ed1 100644 --- a/src/common/Util/SpawnIndexAllocator.h +++ b/src/common/Util/SpawnIndexAllocator.h @@ -1,13 +1,13 @@ -#ifndef SAPPHIRE_SPAWNINDEXALLOCATOR_H -#define SAPPHIRE_SPAWNINDEXALLOCATOR_H +#pragma once -#include -#include #include +#include 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 > class SpawnIndexAllocator { @@ -16,92 +16,109 @@ namespace Sapphire::Common::Util 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" ); - SpawnIndexAllocator() : - m_maxSlotId( 0 ), - m_reserveFirstSlot( false ) + // Constructor for the SpawnIndexAllocator, initializing internal variables + // such as maximum slot ID, reserve first slot flag, and allocation failure ID. + 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 ) { m_maxSlotId = maxSlotId; 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 ) { - auto it = m_actorIdToAllocatedMap.find( actorId ); - if( it == m_actorIdToAllocatedMap.end() ) - return getAllocFailId(); + auto it = m_usedIndexes.find( actorId ); + if( it == m_usedIndexes.end() ) + return m_allocFailId; - auto index = it->second; - m_availableIds.push( index ); - m_actorIdToAllocatedMap.erase( it ); + auto index = *it; + m_usedIndexes.erase( it ); + m_freeIndexes.insert( 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 ) { assert( m_maxSlotId != 0 ); - if( m_availableIds.empty() ) - return getAllocFailId(); + if( m_freeIndexes.empty() ) + return m_allocFailId; - auto nextId = m_availableIds.front(); - m_availableIds.pop(); - - m_actorIdToAllocatedMap[ actorId ] = nextId; + auto nextId = *m_freeIndexes.begin(); + m_freeIndexes.erase( m_freeIndexes.begin() ); + m_usedIndexes.insert( nextId ); return nextId; } + // Frees all used spawn indices and clears the used set. Resets the free + // set to contain all possible indices. 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 ) { - return spawnIndex != getAllocFailId(); + return spawnIndex != m_allocFailId; } + // Returns the allocation failure ID. constexpr T getAllocFailId() const { - return static_cast< T >( -1 ); + return m_allocFailId; } 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 ); - while( !m_availableIds.empty() ) - m_availableIds.pop(); + m_freeIndexes.clear(); 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 if( m_reserveFirstSlot ) start = 1; for( uint32_t i = start; i < m_maxSlotId; i++ ) - m_availableIds.push( i ); + m_freeIndexes.insert( i ); } - std::queue< T > m_availableIds; - std::unordered_map< ActorIdType, T > m_actorIdToAllocatedMap; + std::unordered_set< T > m_freeIndexes; + std::unordered_set< T > m_usedIndexes; T m_maxSlotId; bool m_reserveFirstSlot; + const T m_allocFailId; }; -} - -#endif //SAPPHIRE_SPAWNINDEXALLOCATOR_H +}// namespace Sapphire::Common::Util \ No newline at end of file diff --git a/src/common/Util/Util.cpp b/src/common/Util/Util.cpp index 1085bd6a..34bc685f 100644 --- a/src/common/Util/Util.cpp +++ b/src/common/Util/Util.cpp @@ -122,15 +122,14 @@ std::string Util::binaryToHexDump( uint8_t* pBinData, uint16_t size ) uint64_t Util::getTimeMs() { - std::chrono::milliseconds epoch = std::chrono::duration_cast< std::chrono::milliseconds > - ( std::chrono::system_clock::now().time_since_epoch() ); + const auto epoch = std::chrono::duration_cast< std::chrono::milliseconds >( std::chrono::steady_clock::now().time_since_epoch() ); return epoch.count(); } uint32_t Util::getTimeSeconds() { - auto currClock = std::chrono::system_clock::now(); - return static_cast< uint32_t >( std::chrono::time_point_cast< std::chrono::seconds >( currClock ).time_since_epoch().count() ); + const auto currClock = std::chrono::steady_clock::now(); + return static_cast< uint32_t >( std::chrono::duration_cast< std::chrono::seconds >( currClock.time_since_epoch() ).count() ); } uint64_t Util::getEorzeanTimeStamp() @@ -140,18 +139,17 @@ uint64_t Util::getEorzeanTimeStamp() void Util::valueToFlagByteIndexValue( uint32_t inVal, uint8_t& outVal, uint16_t& outIndex ) { - uint32_t id = inVal; + const uint32_t id = inVal; outIndex = id / 8; - uint8_t bitIndex = id % 8; + const uint8_t bitIndex = id % 8; outVal = 1 << bitIndex; } - std::string Util::fmtUtcTime( const std::string& fmt ) { - auto t = std::time( nullptr ); - auto tm = std::gmtime( &t ); + const auto t = std::time( nullptr ); + const auto tm = std::gmtime( &t ); std::stringstream ss; diff --git a/src/common/Util/Util.h b/src/common/Util/Util.h index 3f80b6fc..4b06ade1 100644 --- a/src/common/Util/Util.h +++ b/src/common/Util/Util.h @@ -8,33 +8,44 @@ namespace Sapphire::Common::Util { - + // Retrieves the operation code from a raw network packet 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 ); + // Converts binary data to a formatted hexadecimal dump 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 ); + // Erases all occurrences of the specified character from the input/output string 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 ); + // Returns a lowercase copy of the input string 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 ); + // Gets the current time in milliseconds uint64_t getTimeMs(); /*! + * Gets the current time in seconds * @brief Get a POSIX epoch representation of the current time * @return 32-bit unsigned integer */ - uint32_t getTimeSeconds(); + // Gets the current Eorzean timestamp (used in the game world) uint64_t getEorzeanTimeStamp(); + // Converts a value to a flag byte index value pair void valueToFlagByteIndexValue( uint32_t inVal, uint8_t& outVal, uint16_t& outIndex ); template< class T > diff --git a/src/common/Util/UtilMath.h b/src/common/Util/UtilMath.h index 1f6ddd50..82cab745 100644 --- a/src/common/Util/UtilMath.h +++ b/src/common/Util/UtilMath.h @@ -7,28 +7,40 @@ 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 ); + // 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 ); + + // Computes the distance between two 3D positions using FFXIVARR_POSITION3 structures 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 ); + // Computes the distance between two 2D points (x, y) and (x1, 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 ); + // 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 ); + // Truncates a floating-point value to a specified number of digits float trunc( float value, uint8_t digitsToRemain ); + // Converts a float to a uint16_t uint16_t floatToUInt16( float val ); + // Converts a float to a uint16_t representing a rotation angle uint16_t floatToUInt16Rot( float val ); + // Converts a float to a uint8_t representing a rotation angle uint8_t floatToUInt8Rot( float val ); + // Clamps a value between a minimum and maximum value template < typename T > T clamp( T val, T minimum, T maximum ) { @@ -41,8 +53,10 @@ namespace Sapphire::Common::Util return val; } + // Transforms a 3D position vector using a 3x3 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 ); } diff --git a/src/common/Vector3.cpp b/src/common/Vector3.cpp deleted file mode 100644 index a98c4fde..00000000 --- a/src/common/Vector3.cpp +++ /dev/null @@ -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; -} \ No newline at end of file diff --git a/src/common/Vector3.h b/src/common/Vector3.h index 1658aa76..79b61825 100644 --- a/src/common/Vector3.h +++ b/src/common/Vector3.h @@ -1,5 +1,7 @@ #pragma once +#include + namespace Sapphire::Common { struct FFXIVARR_POSITION3 @@ -7,7 +9,24 @@ namespace Sapphire::Common float x; float y; 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 @@ -16,11 +35,59 @@ namespace Sapphire::Common float y; float z; 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 { float m[ 3 ][ 3 ]; }; -} +}// namespace Sapphire::Common \ No newline at end of file diff --git a/src/dbm/DbManager.cpp b/src/dbm/DbManager.cpp index 0f706212..f334c7a9 100644 --- a/src/dbm/DbManager.cpp +++ b/src/dbm/DbManager.cpp @@ -6,19 +6,13 @@ #include #include -#include +#include using namespace Sapphire; using namespace Sapphire::Common; -// fucking filesystem -#if _MSC_VER >= 1925 #include namespace fs = std::filesystem; -#else -#include -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 ) : diff --git a/src/dbm/main.cpp b/src/dbm/main.cpp index 7eaff903..c7a4dd18 100644 --- a/src/dbm/main.cpp +++ b/src/dbm/main.cpp @@ -2,21 +2,16 @@ #include #include #include -#include +#include #include -#include -#include +#include +#include Sapphire::Common::Util::CrashHandler crashHandler; -// fucking filesystem -#if _MSC_VER >= 1925 + #include -namespace filesys = std::filesystem; -#else -#include -namespace filesys = std::experimental::filesystem; -#endif +namespace fs = std::filesystem; #include #include @@ -36,19 +31,19 @@ std::vector< std::string > getAllFilesInDir( const std::string& dirPath, try { // 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 - filesys::recursive_directory_iterator iter( dirPath ); + fs::recursive_directory_iterator iter( dirPath ); // Create a Recursive Directory Iterator object pointing to end. - filesys::recursive_directory_iterator end; + fs::recursive_directory_iterator end; // Iterate till end while( iter != end ) { // 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() ) ) { // Skip the iteration of current directory pointed by iterator diff --git a/src/world/Network/Handlers/PacketHandlers.cpp b/src/world/Network/Handlers/PacketHandlers.cpp index c66b80d0..897238d5 100644 --- a/src/world/Network/Handlers/PacketHandlers.cpp +++ b/src/world/Network/Handlers/PacketHandlers.cpp @@ -1,5 +1,5 @@ #include -#include +#include #include #include #include diff --git a/src/world/Script/ScriptMgr.cpp b/src/world/Script/ScriptMgr.cpp index d56a0626..027c96a3 100644 --- a/src/world/Script/ScriptMgr.cpp +++ b/src/world/Script/ScriptMgr.cpp @@ -56,7 +56,8 @@ bool Sapphire::Scripting::ScriptMgr::init() std::set< std::string > files; 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 ) { @@ -67,13 +68,13 @@ bool Sapphire::Scripting::ScriptMgr::init() uint32_t scriptsFound = 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++; - if( m_nativeScriptMgr->loadScript( path ) ) + if( m_nativeScriptMgr->loadScript( file_path.string() ) ) scriptsLoaded++; } diff --git a/src/world/WorldServer.cpp b/src/world/WorldServer.cpp index c89cf550..ecf1016a 100644 --- a/src/world/WorldServer.cpp +++ b/src/world/WorldServer.cpp @@ -180,14 +180,28 @@ void WorldServer::run( int32_t argc, char* argv[] ) auto verString = readFileToString( verPath ); if( verString != m_config.global.general.dataVersion ) { - Logger::fatal( "Sqpack version {} does not match expected version {}!", verString, m_config.global.general.dataVersion ); - return; + Logger::warn( "Sqpack version {} does not match expected version {}!", verString, m_config.global.general.dataVersion ); + + std::string response; + do + { + 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" ) + { + return; + } } } - catch ( const std::exception& e ) + catch( const std::exception& e ) { - Logger::fatal( e.what() ); - return; + 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 ) ) diff --git a/src/world/WorldServer.h b/src/world/WorldServer.h index 0877389c..bfe553a1 100644 --- a/src/world/WorldServer.h +++ b/src/world/WorldServer.h @@ -5,6 +5,7 @@ #include #include #include +#include #include "ForwardsZone.h" #include