1
Fork 0
mirror of https://github.com/SapphireServer/Sapphire.git synced 2025-05-21 17:47:45 +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 )
# 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 )

View file

@ -276,37 +276,61 @@ namespace xiv::dat
default:
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;
}
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 ) );
}

View file

@ -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
uint32_t Cat::getCatNum() const
{
return m_catNum;
}
}// namespace xiv::dat

View file

@ -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:

View file

@ -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 <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;
}
// 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;
}
};

View file

@ -1,5 +1,4 @@
#ifndef _SGB_H
#define _SGB_H
#pragma once
#include <cstring>
#include <memory>
@ -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
};

View file

@ -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

177
deps/datReader/Exd.h vendored
View file

@ -1,18 +1,18 @@
#pragma once
#include <memory>
#include <map>
#include <memory>
#include <unordered_map>
#include <variant>
#include "File.h"
#include "Exd/Common.h"
#include "Exd/Structs.h"
#include "Exh.h"
#include "File.h"
#include "bparse.h"
#include "stream.h"
#include <fstream>
#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<char*>( &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<char*>( &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

View file

@ -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<xiv::exd::Language, std::string> 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

View file

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

View file

@ -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

View file

@ -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<std::string>& 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<std::string, std::unique_ptr<Cat>> _cats;
std::unordered_map< std::string, std::unique_ptr< Cat > > _cats;
// 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
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
{
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;

81
deps/datReader/Exh.h vendored
View file

@ -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

View file

@ -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();
}
}

20
deps/datReader/File.h vendored
View file

@ -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

View file

@ -89,5 +89,4 @@ namespace xiv::dat
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
#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

View file

@ -1,5 +1,4 @@
#ifndef XIV_DAT_SQPACK_H
#define XIV_DAT_SQPACK_H
#pragma once
#include <fstream>
@ -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

View file

@ -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();
}

View file

@ -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 );
}

View file

@ -1,5 +1,5 @@
#include "conv.h"
#include <cstring>// for memcpy
#include <cstring>
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;
}

View file

@ -1,5 +1,4 @@
#ifndef XIV_UTILS_STREAM_H
#define XIV_UTILS_STREAM_H
#pragma once
#include <memory>
#include <iostream>
@ -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

View file

@ -1,5 +1,4 @@
#ifndef XIV_UTILS_ZLIB_H
#define XIV_UTILS_ZLIB_H
#pragma once
#include <cstddef>
#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 );
}
#endif // XIV_UTILS_ZLIB_H
} // namespace xiv::utils::zlib

View file

@ -32,14 +32,10 @@
#include <functional>
// fucking filesystem
#if _MSC_VER >= 1925
#include <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
class WatchedFileSystemExc : public std::exception {

View file

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

View file

@ -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,

View file

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

View file

@ -7,30 +7,23 @@
#include <spdlog/sinks/stdout_color_sinks.h>
#include <spdlog/sinks/daily_file_sink.h>
// #include <iostream>
#if _MSC_VER >= 1925
#include <filesystem>
namespace fs = std::filesystem;
#else
#include <experimental/filesystem>
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 };

View file

@ -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;

View file

@ -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" );
}
}

View file

@ -1,67 +1,58 @@
#pragma once
#include <memory>
#include <thread>
#include <mutex>
#include <queue>
#include <algorithm>
#include <utility>
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

View file

@ -1,16 +1,18 @@
#ifndef _LOCKED_WAIT_H
#define _LOCKED_WAIT_H
#pragma once
#include <atomic>
#include <condition_variable>
#include <mutex>
#include <queue>
#include <atomic>
#include <type_traits>
#include <utility>
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

View file

@ -1,13 +1,13 @@
#ifndef SAPPHIRE_SPAWNINDEXALLOCATOR_H
#define SAPPHIRE_SPAWNINDEXALLOCATOR_H
#pragma once
#include <queue>
#include <unordered_map>
#include <type_traits>
#include <unordered_set>
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

View file

@ -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;

View file

@ -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 >

View file

@ -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 );
}

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
#include <cmath>
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

View file

@ -6,19 +6,13 @@
#include <sstream>
#include <Logging/Logger.h>
#include <common/Util/Util.h>
#include <Util/Util.h>
using namespace Sapphire;
using namespace Sapphire::Common;
// fucking filesystem
#if _MSC_VER >= 1925
#include <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 ) :

View file

@ -2,21 +2,16 @@
#include <iostream>
#include <cctype>
#include <set>
#include <common/Logging/Logger.h>
#include <Logging/Logger.h>
#include <MySqlConnector.h>
#include <common/Util/CrashHandler.h>
#include <common/Config/ConfigMgr.h>
#include <Util/CrashHandler.h>
#include <Config/ConfigMgr.h>
Sapphire::Common::Util::CrashHandler crashHandler;
// fucking filesystem
#if _MSC_VER >= 1925
#include <filesystem>
namespace filesys = std::filesystem;
#else
#include <experimental/filesystem>
namespace filesys = std::experimental::filesystem;
#endif
namespace fs = std::filesystem;
#include <fstream>
#include <streambuf>
@ -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

View file

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

View file

@ -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++;
}

View file

@ -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 ) )

View file

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