diff --git a/src/tools/pcb_reader/lgb.h b/src/tools/pcb_reader/lgb.h index eb337ef6..4354ca55 100644 --- a/src/tools/pcb_reader/lgb.h +++ b/src/tools/pcb_reader/lgb.h @@ -118,7 +118,7 @@ public: }; }; -struct LGB_GIMMICK_HEADER +struct LGB_ENTRY_HEADER { LgbEntryType type; uint32_t unknown; @@ -126,6 +126,10 @@ struct LGB_GIMMICK_HEADER vec3 translation; vec3 rotation; vec3 scale; +}; + +struct LGB_GIMMICK_HEADER : public LGB_ENTRY_HEADER +{ uint32_t gimmickFileOffset; char unknownBytes[100]; }; @@ -136,7 +140,7 @@ public: LGB_GIMMICK_HEADER header; std::string name; std::string gimmickFileName; - + LGB_GIMMICK_ENTRY( char* buf, uint32_t offset ) { header = *reinterpret_cast( buf + offset ); @@ -145,6 +149,45 @@ public: }; }; +struct LGB_ENPC_HEADER : public LGB_ENTRY_HEADER +{ + uint32_t enpcId; + uint8_t unknown1[0x24]; +}; + +class LGB_ENPC_ENTRY : public LGB_MODEL_ENTRY +{ +public: + LGB_ENPC_HEADER header; + std::string name; + + LGB_ENPC_ENTRY( char* buf, uint32_t offset ) + { + header = *reinterpret_cast< LGB_ENPC_HEADER* >( buf + offset ); + name = std::string( buf + offset + header.nameOffset ); + }; +}; + +struct LGB_EOBJ_HEADER : public LGB_ENTRY_HEADER +{ + uint32_t eobjId; + uint8_t unknown1[0x10]; +}; + +class LGB_EOBJ_ENTRY : public LGB_MODEL_ENTRY +{ +public: + LGB_EOBJ_HEADER header; + std::string name; + + LGB_EOBJ_ENTRY( char* buf, uint32_t offset ) + { + header = *reinterpret_cast< LGB_EOBJ_HEADER* >( buf + offset ); + std::cout << header.eobjId << std::endl; + name = std::string( buf + offset + header.nameOffset ); + }; +}; + struct LGB_GROUP_HEADER { uint32_t unknown; @@ -167,30 +210,38 @@ struct LGB_GROUP LGB_FILE* parent; LGB_GROUP_HEADER header; std::string name; - std::vector> entries; + std::vector< std::shared_ptr< LGB_MODEL_ENTRY > > entries; LGB_GROUP( char* buf, LGB_FILE* parentStruct, uint32_t offset ) { parent = parentStruct; - header = *reinterpret_cast( buf + offset ); + header = *reinterpret_cast< LGB_GROUP_HEADER* >( buf + offset ); name = std::string( buf + offset + header.groupNameOffset ); //entries.resize( header.entryCount ); - //std::cout << name << std::endl; + std::cout << name << std::endl; const auto entriesOffset = offset + header.entriesOffset; for( auto i = 0; i < header.entryCount; ++i ) { - const auto entryOffset = entriesOffset + *reinterpret_cast( buf + ( entriesOffset + i * 4 ) ); + const auto entryOffset = entriesOffset + *reinterpret_cast< int32_t* >( buf + ( entriesOffset + i * 4 ) ); try { const auto type = *reinterpret_cast( buf + entryOffset ); if( type == LgbEntryType::BgParts ) { - entries.push_back( std::make_shared( buf, entryOffset ) ); - } + entries.push_back( std::make_shared< LGB_BGPARTS_ENTRY >( buf, entryOffset ) ); + } else if( type == LgbEntryType::Gimmick ) { - entries.push_back( std::make_shared( buf, entryOffset ) ); + entries.push_back( std::make_shared< LGB_GIMMICK_ENTRY >( buf, entryOffset ) ); + } + else if( type == LgbEntryType::EventNpc ) + { + entries.push_back( std::make_shared< LGB_ENPC_ENTRY >( buf, entryOffset ) ); + } + else if( type == LgbEntryType::EventObject ) + { + entries.push_back( std::make_shared< LGB_EOBJ_ENTRY >( buf, entryOffset ) ); } /* else @@ -223,11 +274,11 @@ struct LGB_FILE_HEADER struct LGB_FILE { LGB_FILE_HEADER header; - std::vector groups; + std::vector< LGB_GROUP > groups; LGB_FILE( char* buf ) { - header = *reinterpret_cast( buf ); + header = *reinterpret_cast< LGB_FILE_HEADER* >( buf ); if( strncmp( &header.magic[0], "LGB1", 4 ) != 0 || strncmp( &header.magic2[0], "LGP1", 4 ) != 0 ) throw std::runtime_error( "Invalid LGB file!" ); @@ -236,7 +287,7 @@ struct LGB_FILE constexpr auto baseOffset = sizeof( header ); for( auto i = 0; i < header.groupCount; ++i ) { - const auto groupOffset = baseOffset + *reinterpret_cast( buf + ( baseOffset + i * 4 ) ); + const auto groupOffset = baseOffset + *reinterpret_cast< int32_t* >( buf + ( baseOffset + i * 4 ) ); const auto group = LGB_GROUP( buf, this, groupOffset ); groups.push_back( group ); } @@ -277,4 +328,4 @@ std::map getLgbFiles( const std::string& dir ) } #endif */ -#endif +#endif \ No newline at end of file diff --git a/src/tools/pcb_reader/main.cpp b/src/tools/pcb_reader/main.cpp index 77276c40..7448da38 100644 --- a/src/tools/pcb_reader/main.cpp +++ b/src/tools/pcb_reader/main.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include "pcb.h" #include "lgb.h" @@ -20,14 +21,15 @@ #include #endif +std::string gamePath("C:\\Program Files (x86)\\SquareEnix\\FINAL FANTASY XIV - A Realm Reborn\\game\\sqpack\\ffxiv"); +std::unordered_map< uint32_t, std::string > eobjNameMap; + enum class TerritoryTypeExdIndexes : size_t { TerritoryType = 0, Path = 1 }; -std::string gamePath( "C:\\Program Files (x86)\\SquareEnix\\FINAL FANTASY XIV - A Realm Reborn\\game\\sqpack\\ffxiv" ); - using namespace std::chrono_literals; struct face @@ -91,9 +93,51 @@ int parseBlockEntry( char* data, std::vector& entries, int gOff return 0; } +void dumpLevelExdEntries( uint32_t zoneId, const std::string& name = std::string() ) +{ + xiv::dat::GameData dat( gamePath ); + xiv::exd::ExdData eData( dat ); + auto& cat = eData.get_category( "Level" ); + auto exd = static_cast< xiv::exd::Exd >( cat.get_data_ln( xiv::exd::Language::none ) ); + + std::string fileName( name + "_" + std::to_string( zoneId ) + "_Level" + ".csv" ); + std::ofstream outfile( fileName, std::ios::trunc ); + + if( outfile.good() ) + { + outfile.close(); + outfile.open( fileName, std::ios::app ); + + for( auto& row : exd.get_rows() ) + { + auto id = row.first; + auto& fields = row.second; + auto x = *boost::get< float >( &fields.at( 0 ) ); + auto y = *boost::get< float >( &fields.at( 1 ) ); + auto z = *boost::get< float >( &fields.at( 2 ) ); + auto yaw = *boost::get< float >( &fields.at( 3 ) ); + auto radius = *boost::get< float >( &fields.at( 4 ) ); + auto type = *boost::get< uint8_t >( &fields.at( 5 ) ); + auto objectid = *boost::get< uint32_t >( &fields.at( 6 ) ); + auto zone = *boost::get< uint16_t >( &fields.at( 9 ) ); + + if( zone == zoneId ) + { + std::string outStr( + std::to_string( id ) + ", " + std::to_string( objectid ) + ", " + + std::to_string( x ) + ", " + std::to_string( y ) + ", " + std::to_string( z ) + ", " + + std::to_string( yaw ) + ", " + std::to_string( radius ) + ", " + std::to_string( type ) + "\n" + ); + outfile.write( outStr.c_str(), outStr.size() ); + } + } + } +} + std::string zoneNameToPath( const std::string& name ) { std::string path; + uint32_t id; #ifdef STANDALONE auto inFile = std::ifstream( "territorytype.exh.csv" ); if( inFile.good() ) @@ -107,6 +151,7 @@ std::string zoneNameToPath( const std::string& name ) { if( name == match[2].str() ) { + id = match[1].str(); path = match[3].str(); break; } @@ -126,6 +171,7 @@ std::string zoneNameToPath( const std::string& name ) if( teriName.empty() ) continue; auto teriPath = *boost::get< std::string >( &fields.at( static_cast< size_t >( TerritoryTypeExdIndexes::Path ) ) ); + id = row.first; if( boost::iequals( name, teriName ) ) { path = teriPath; @@ -146,9 +192,34 @@ std::string zoneNameToPath( const std::string& name ) throw std::runtime_error( "Unable to find path for " + name + ".\n\tPlease double check spelling or open 0a0000.win32.index with FFXIV Explorer and extract territorytype.exh as CSV\n\tand copy territorytype.exh.csv into pcb_reader.exe directory if using standalone" ); } + dumpLevelExdEntries( id, name ); return path; } +void loadEobjNames() +{ + xiv::dat::GameData dat( gamePath ); + xiv::exd::ExdData eData( dat ); + auto& cat = eData.get_category( "EObjName" ); + auto exd = static_cast< xiv::exd::Exd >( cat.get_data_ln( xiv::exd::Language::en ) ); + for( auto& row : exd.get_rows() ) + { + auto id = row.first; + auto& fields = row.second; + auto name = *boost::get< std::string >( &fields.at( 0 ) ); + eobjNameMap[id] = name; + } +} + +void writeEobjEntry( std::ofstream& out, LGB_EOBJ_ENTRY* pEobj, const std::string& name ) +{ + std::string outStr( + std::to_string( pEobj->header.eobjId ) + ", \"" + name + "\", " + + std::to_string( pEobj->header.translation.x ) + ", " + std::to_string( pEobj->header.translation.y ) + ", " + std::to_string( pEobj->header.translation.z ) + "\n" + ); + out.write( outStr.c_str(), outStr.size() ); +} + void readFileToBuffer( const std::string& path, std::vector< char >& buf ) { auto inFile = std::ifstream( path, std::ios::binary ); @@ -187,7 +258,7 @@ int main( int argc, char* argv[] ) { const auto& zonePath = zoneNameToPath( zoneName ); std::string listPcbPath( zonePath + "/collision/list.pcb" ); - std::string bgLgbPath( zonePath + "/level/bg.lgb" ); + std::string bgLgbPath( zonePath + "/level/planmap.lgb" ); std::string collisionFilePath( zonePath + "/collision/" ); std::vector< char > section; std::vector< char > section1; @@ -215,6 +286,16 @@ int main( int argc, char* argv[] ) std::vector< std::string > stringList; uint32_t offset1 = 0x20; + + loadEobjNames(); + std::string eobjFileName( zoneName + "eobj.csv" ); + std::ofstream eobjOut( eobjFileName, std::ios::trunc ); + if( !eobjOut.good() ) + throw std::string( "Unable to create " + zoneName + "_eobj.csv for eobj entries. Run as admin or check there isnt already a handle on the file." ).c_str(); + + eobjOut.close(); + eobjOut.open( eobjFileName, std::ios::app ); + for( ; ; ) { @@ -435,6 +516,7 @@ int main( int argc, char* argv[] ) { auto pGimmick = dynamic_cast< LGB_GIMMICK_ENTRY* >( pEntry.get() ); auto pBgParts = dynamic_cast< LGB_BGPARTS_ENTRY* >( pEntry.get() ); + auto pEventObj = dynamic_cast< LGB_EOBJ_ENTRY* >( pEntry.get() ); std::string fileName( "" ); fileName.resize( 256 ); @@ -494,6 +576,13 @@ int main( int argc, char* argv[] ) } } } + + if( pEventObj ) + { + fileName = pEventObj->name.empty() ? eobjNameMap[pEventObj->header.eobjId] : pEventObj->name; + writeEobjEntry( eobjOut, pEventObj, fileName ); + writeOutput( fileName, &pEventObj->header.scale, &pEventObj->header.rotation, &pEventObj->header.translation ); + } } } std::cout << "\n[Info] " << "Loaded " << pcbFiles.size() << " PCB Files \n"; diff --git a/src/tools/pcb_reader/sgb.h b/src/tools/pcb_reader/sgb.h index 8c597d0e..d989acab 100644 --- a/src/tools/pcb_reader/sgb.h +++ b/src/tools/pcb_reader/sgb.h @@ -100,7 +100,7 @@ struct SGB_MODEL_ENTRY : public SGB_GROUP_ENTRY SGB_MODEL_ENTRY( char* buf, uint32_t offset ) { - header = *reinterpret_cast( buf + offset ); + 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 ); @@ -112,25 +112,25 @@ struct SGB_GROUP SGB_GROUP_HEADER header; std::string name; SGB_FILE* parent; - std::vector> entries; + std::vector< std::shared_ptr< SGB_GROUP_ENTRY > > entries; SGB_GROUP( char* buf, SGB_FILE* file, uint32_t fileSize, uint32_t offset ) { parent = file; - header = *reinterpret_cast( buf + offset ); + header = *reinterpret_cast< SGB_GROUP_HEADER* >( buf + offset ); name = std::string( buf + offset + header.nameOffset ); auto entriesOffset = offset + sizeof( header ); for( auto i = 0; i < header.entryCount; ++i ) { - auto entryOffset = entriesOffset + *reinterpret_cast( buf + ( entriesOffset + (i * 4) ) ); + 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( buf + entryOffset ); + auto type = *reinterpret_cast< uint32_t* >( buf + entryOffset ); if( type == SgbGroupEntryType::Model ) { - entries.push_back( std::make_shared( buf, entryOffset ) ); + entries.push_back( std::make_shared< SGB_MODEL_ENTRY >( buf, entryOffset ) ); } } } @@ -179,7 +179,7 @@ struct SGB_FILE SGB_FILE( char* buf ) { constexpr int baseOffset = 0x14; - header = *reinterpret_cast( buf ); + header = *reinterpret_cast< SGB_HEADER* >( buf ); if( strncmp( &header.magic[0], "SGB1", 4 ) != 0 || strncmp( &header.magic2[0], "SCN1", 4 ) != 0 ) throw std::runtime_error( "Unable to load SGB File!" );