#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace filesys = std::experimental::filesystem; #include #include #include #include Core::Logger g_log; Core::Data::ExdDataGenerated g_exdData; //const std::string datLocation( "/opt/sapphire_3_15_0/bin/sqpack" ); const std::string datLocation( "C:\\SquareEnix\\FINAL FANTASY XIV - A Realm Reborn\\game\\sqpack" ); struct StatusEffect { uint16_t effect_id; uint16_t unknown1; float duration; uint32_t sourceActorId; }; struct FFXIVIpcNpcSpawn { uint32_t gimmickId; // needs to be existing in the map, mob will snap to it uint8_t u2b; uint8_t u2ab; uint8_t gmRank; uint8_t u3b; uint8_t aggressionMode; // 1 passive, 2 aggressive uint8_t onlineStatus; uint8_t u3c; uint8_t pose; uint32_t u4; uint64_t targetId; uint32_t u6; uint32_t u7; uint64_t mainWeaponModel; uint64_t secWeaponModel; uint64_t craftToolModel; uint32_t u14; uint32_t u15; uint32_t bNPCBase; uint32_t bNPCName; uint32_t u18; uint32_t u19; uint32_t directorId; uint32_t spawnerId; uint32_t parentActorId; uint32_t hPMax; uint32_t hPCurr; uint32_t displayFlags; uint16_t fateID; uint16_t mPCurr; uint16_t tPCurr; uint16_t mPMax; uint16_t tPMax; uint16_t modelChara; uint16_t rotation; uint16_t activeMinion; uint8_t spawnIndex; uint8_t state; uint8_t persistantEmote; uint8_t modelType; uint8_t subtype; uint8_t voice; uint16_t u25c; uint8_t enemyType; uint8_t level; uint8_t classJob; uint8_t u26d; uint16_t u27a; uint8_t currentMount; uint8_t mountHead; uint8_t mountBody; uint8_t mountFeet; uint8_t mountColor; uint8_t scale; uint32_t u29b; uint32_t u30b; StatusEffect effect[30]; float posX; float posY; float posZ; uint32_t models[10]; char name[32]; uint8_t look[26]; char fcTag[6]; uint32_t unk30; uint32_t unk31; uint8_t bNPCPartSlot; uint8_t unk32; uint16_t unk33; uint32_t unk34; }; std::string binaryToHexString( uint8_t* pBinData, uint16_t size ) { std::string outStr; for( uint32_t i = 0; i < size; i++ ) { outStr += boost::str( boost::format( "%|02X|" ) % ( int32_t ) ( pBinData[ i ] & 0xFF ) ); } return outStr; } std::vector< std::string > getAllFilesInDir( const std::string& dirPath, const std::vector< std::string > dirSkipList = {} ) { // Create a vector of string std::vector< std::string > listOfFiles; try { // Check if given path exists and points to a directory if( filesys::exists( dirPath ) && filesys::is_directory( dirPath ) ) { // Create a Recursive Directory Iterator object and points to the starting of directory filesys::recursive_directory_iterator iter( dirPath ); // Create a Recursive Directory Iterator object pointing to end. filesys::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() ) && ( std::find( dirSkipList.begin(), dirSkipList.end(), iter->path().filename() ) != dirSkipList.end() ) ) { // Skip the iteration of current directory pointed by iterator #ifdef USING_BOOST // Boost Fileystsem API to skip current directory iteration iter.no_push(); #else // c++17 Filesystem API to skip current directory iteration iter.disable_recursion_pending(); #endif } else { // Add the name in vector listOfFiles.push_back( iter->path().string() ); } std::error_code ec; // Increment the iterator to point to next entry in recursive iteration iter.increment( ec ); if( ec ) { std::cerr << "Error While Accessing : " << iter->path().string() << " :: " << ec.message() << '\n'; } } } } catch( std::system_error& e ) { std::cerr << "Exception :: " << e.what(); } return listOfFiles; } std::string delChar( std::string &str, char del ) { str.erase( std::remove( str.begin(), str.end(), del ), str.end() ); return str; } int dumpSpawns() { g_log.init(); g_log.info( "Setting up EXD data" ); if( !g_exdData.init( datLocation ) ) { g_log.fatal( "Error setting up EXD data " ); return 0; } std::map< int, std::vector< FFXIVIpcNpcSpawn > > nameToPacketList; std::map< int, std::vector< FFXIVIpcNpcSpawn > > zoneToPacketList; auto listOfFiles = getAllFilesInDir( "G:\\programming\\sapphire\\github\\ffxivmon\\bin\\CapturedNpcs", { ".svn", "logs", "backup" } ); for( auto file : listOfFiles ) { if( !filesys::is_directory( file ) ) { auto pos = file.find_last_of( "\\" ); if( pos != std::string::npos ) { auto pos = file.find_last_of( filesys::path::preferred_separator ); if( pos != std::string::npos ) { auto str = file.substr( 0, pos ); pos = str.find_last_of( filesys::path::preferred_separator ); auto zone = str.substr( pos + 1 ); //g_log.info( zone ); FFXIVIpcNpcSpawn packet; std::ifstream is; is.open( file, std::ios::binary ); is.seekg( 0x20, std::ios::beg ); is.read( ( char* ) &packet, sizeof( FFXIVIpcNpcSpawn ) ); is.close(); if( packet.subtype != 2 && packet.subtype != 3 && packet.enemyType != 0 && packet.spawnerId == 0xE0000000 && packet.fateID == 0 && packet.directorId == 0 ) zoneToPacketList[ std::stoi( zone ) ].push_back( packet ); } } } } //std::ofstream out("output.txt"); int spawngroups = 0; for( auto entry : zoneToPacketList ) { //auto nameStruct = g_exdData.get< Core::Data::BNpcName >( entry.first ); auto teri1 = g_exdData.get< Core::Data::TerritoryType >( entry.first ); auto teriPlaceName = g_exdData.get< Core::Data::PlaceName >( teri1->placeName ); g_log.info( std::to_string( entry.first ) + " - " + teri1->name + " - " + teriPlaceName->name ); g_log.info( "Mob Count: " + std::to_string( entry.second.size() ) ); for( auto mob : entry.second ) { nameToPacketList[ mob.bNPCBase ].push_back( mob ); auto nameStruct = g_exdData.get< Core::Data::BNpcName >( mob.bNPCName ); //g_log.info( nameStruct->singular + " " + std::to_string( packet.bNPCBase ) ); } std::map< std::string, std::vector< FFXIVIpcNpcSpawn > > lvlToPacket; for( auto mobName : nameToPacketList ) { for( FFXIVIpcNpcSpawn instance : mobName.second ) { lvlToPacket[ std::to_string( instance.level ) + "_" + std::to_string( instance.bNPCBase ) ].push_back( instance ); } } for( auto mobName : lvlToPacket ) { auto nameStruct = g_exdData.get< Core::Data::BNpcName >( mobName.second.at(0).bNPCName ); g_log.info( "|--> " + nameStruct->singular + "(" + std::to_string( mobName.second.size() ) + ")" ); spawngroups++; for( FFXIVIpcNpcSpawn instance : mobName.second ) { std::string modelStr = "["; for( auto modelEntry : instance.models ) { modelStr += std::to_string( modelEntry ) + ", "; } modelStr += "]"; std::string cusStr = "["; for( auto cusEntry : instance.look ) { cusStr += std::to_string( cusEntry ) + ", "; } cusStr += "]"; modelStr = binaryToHexString( (uint8_t*)instance.models, 40 ); cusStr = binaryToHexString( (uint8_t*)instance.look, 26 ); std::string name = delChar( nameStruct->singular, ' ' ); name = delChar( name, '\'' ); g_log.info( "|----> " + name + "_" + std::to_string( instance.bNPCBase ) + " " + std::to_string( instance.posX ) + ", " + std::to_string( instance.posY ) + ", " + std::to_string( instance.posZ ) + ", " + std::to_string( instance.modelChara ) + ", " + std::to_string( instance.gimmickId ) + ", " + std::to_string( instance.level ) + ", " + std::to_string( instance.hPMax ) ); //g_log.info( "|----> " + name + " - " + std::to_string( instance.bNPCBase ) + ", " + std::to_string( instance.gimmickId ) ); /*std::string output = "INSERT IGNORE INTO `bnpctemplate` ( `Name`, `bNPCBaseId`, `bNPCNameId`, `mainWeaponModel`, `secWeaponModel`, `aggressionMode`, `enemyType`, `pose`, `modelChara`, `displayFlags`, `Look`, `Models`) " "VALUES ( \"" + name +"_" + std::to_string( instance.bNPCBase ) + "\", " + std::to_string( instance.bNPCBase ) + ", " + std::to_string( instance.bNPCName ) + ", " + std::to_string( instance.mainWeaponModel ) + ", " + std::to_string( instance.secWeaponModel ) + ", " + std::to_string( instance.aggressionMode ) + ", " + std::to_string( instance.enemyType ) + ", " + std::to_string( instance.pose ) + ", " + std::to_string( instance.modelChara ) + ", " + std::to_string( instance.displayFlags ) + ", " + "UNHEX( '" + cusStr + "'), " + "UNHEX( '" + modelStr + "') );\n";*/ //g_log.info( output ); //out << output; } } nameToPacketList.clear(); } g_log.info( "|--> Total SpawnGroups: " + std::to_string( spawngroups ) ); return 0; } int dumpTemplates() { g_log.init(); g_log.info( "Setting up EXD data" ); if( !g_exdData.init( datLocation ) ) { g_log.fatal( "Error setting up EXD data " ); return 0; } std::map< int, std::vector< FFXIVIpcNpcSpawn > > nameToPacketList; std::map< int, std::vector< FFXIVIpcNpcSpawn > > zoneToPacketList; auto listOfFiles = getAllFilesInDir( "G:\\programming\\sapphire\\github\\ffxivmon\\bin\\CapturedNpcs", { ".svn", "logs", "backup" } ); for( auto file : listOfFiles ) { if( !filesys::is_directory( file ) ) { auto pos = file.find_last_of( "\\" ); if( pos != std::string::npos ) { auto pos = file.find_last_of( filesys::path::preferred_separator ); if( pos != std::string::npos ) { auto str = file.substr( 0, pos ); pos = str.find_last_of( filesys::path::preferred_separator ); auto zone = str.substr( pos + 1 ); //g_log.info( zone ); FFXIVIpcNpcSpawn packet; std::ifstream is; is.open( file, std::ios::binary ); is.seekg( 0x20, std::ios::beg ); is.read( ( char* ) &packet, sizeof( FFXIVIpcNpcSpawn ) ); is.close(); if( packet.subtype != 2 && packet.subtype != 3 && packet.enemyType != 0 && packet.spawnerId == 0xE0000000 && packet.fateID == 0 ) zoneToPacketList[ std::stoi( zone ) ].push_back( packet ); } } } /* if( filesys::is_directory( file ) ) { auto pos = file.find_last_of( "\\" ); if( pos != std::string::npos ) { auto zoneIdStr = file.substr( pos + 1 ); auto teri1 = g_exdData.get< Core::Data::TerritoryType >( std::stoi( zoneIdStr ) ); g_log.info( zoneIdStr + " - " + teri1->name ); } } else { FFXIVIpcNpcSpawn packet; std::ifstream is; is.open( file, std::ios::binary ); is.seekg( 0x20, std::ios::beg ); is.read( ( char* )&packet, sizeof( FFXIVIpcNpcSpawn ) ); is.close(); nameToPacketList[ packet.bNPCName ].push_back( packet ); auto nameStruct = g_exdData.get< Core::Data::BNpcName >( packet.bNPCName ); //g_log.info( nameStruct->singular + " " + std::to_string( packet.bNPCBase ) ); } */ } std::ofstream out("output.txt"); for( auto entry : zoneToPacketList ) { //auto nameStruct = g_exdData.get< Core::Data::BNpcName >( entry.first ); auto teri1 = g_exdData.get< Core::Data::TerritoryType >( entry.first ); auto teriPlaceName = g_exdData.get< Core::Data::PlaceName >( teri1->placeName ); g_log.info( std::to_string( entry.first ) + " - " + teri1->name + " - " + teriPlaceName->name ); g_log.info( "Mob Count: " + std::to_string( entry.second.size() ) ); for( auto mob : entry.second ) { nameToPacketList[ mob.bNPCBase ].push_back( mob ); auto nameStruct = g_exdData.get< Core::Data::BNpcName >( mob.bNPCName ); //g_log.info( nameStruct->singular + " " + std::to_string( packet.bNPCBase ) ); } g_log.info( "Unique Mobs: " + std::to_string( nameToPacketList.size() ) ); for( auto mobName : nameToPacketList ) { auto nameStruct = g_exdData.get< Core::Data::BNpcName >( mobName.second.at(0).bNPCName ); g_log.info( "|--> " + nameStruct->singular + "(" + std::to_string( mobName.second.size() ) + ")" ); auto instance = mobName.second.at(0); //for( FFXIVIpcNpcSpawn instance : mobName.second ) { std::string modelStr = "["; for( auto modelEntry : instance.models ) { modelStr += std::to_string( modelEntry ) + ", "; } modelStr += "]"; std::string cusStr = "["; for( auto cusEntry : instance.look ) { cusStr += std::to_string( cusEntry ) + ", "; } cusStr += "]"; modelStr = binaryToHexString( (uint8_t*)instance.models, 40 ); cusStr = binaryToHexString( (uint8_t*)instance.look, 26 ); //g_log.info( "|----> " + std::to_string( instance.bNPCBase ) + " " + std::to_string( instance.posX ) + ", " + std::to_string( instance.posY ) + ", " + std::to_string( instance.posZ ) ); // g_log.info( "|----> " + std::to_string( instance.bNPCBase ) + // " " + std::to_string( instance.mainWeaponModel ) + // ", " + std::to_string( instance.secWeaponModel ) + // ", " + std::to_string( instance.aggressionMode ) + // ", " + std::to_string( instance.enemyType ) + // ", " + std::to_string( instance.onlineStatus ) + // ", " + std::to_string( instance.pose ) + // ", " + std::to_string( instance.modelChara ) + // ", " + std::to_string( instance.displayFlags ) + ", " + modelStr + ", " + cusStr + ", " + std::to_string( instance.gimmickId ) ); std::string name = delChar( nameStruct->singular, ' ' ); name = delChar( name, '\'' ); std::string output = "INSERT IGNORE INTO `bnpctemplate` ( `Name`, `bNPCBaseId`, `bNPCNameId`, `mainWeaponModel`, `secWeaponModel`, `aggressionMode`, `enemyType`, `pose`, `modelChara`, `displayFlags`, `Look`, `Models`) " "VALUES ( \"" + name +"_" + std::to_string( instance.bNPCBase ) + "\", " + std::to_string( instance.bNPCBase ) + ", " + std::to_string( instance.bNPCName ) + ", " + std::to_string( instance.mainWeaponModel ) + ", " + std::to_string( instance.secWeaponModel ) + ", " + std::to_string( instance.aggressionMode ) + ", " + std::to_string( instance.enemyType ) + ", " + std::to_string( instance.pose ) + ", " + std::to_string( instance.modelChara ) + ", " + std::to_string( instance.displayFlags ) + ", " + "UNHEX( '" + cusStr + "'), " + "UNHEX( '" + modelStr + "') );\n"; g_log.info( output ); out << output; /* g_log.info( "|----> " + std::to_string( instance.bNPCBase ) + " " + std::to_string( instance.u2ab ) + ", " + std::to_string( instance.u2b ) + ", " + std::to_string( instance.u3b ) + ", " + std::to_string( instance.u3c ) + ", " + std::to_string( instance.u4 ) + ", " + std::to_string( instance.u6 ) + ", " + std::to_string( instance.u7 ) + ", " + std::to_string( instance.u14 ) + ", " + std::to_string( instance.u15 ) + ", " + std::to_string( instance.u18 ) + ", " + std::to_string( instance.u19 ) + ", " + std::to_string( instance.u25c ) + ", " + std::to_string( instance.u26d ) + ", " + std::to_string( instance.u27a ) + ", " + std::to_string( instance.u29b ) + ", " + std::to_string( instance.u30b ) + ", " + std::to_string( instance.unk30 ) + ", " + std::to_string( instance.unk31 ) + ", " + std::to_string( instance.unk32 ) + ", " + std::to_string( instance.unk33 ) + ", " + std::to_string( instance.unk34 ) );*/ } } nameToPacketList.clear(); } out.close(); /*g_log.info( "getting id list " ); auto idList = g_exdData.getTerritoryTypeIdList(); g_log.info( "getting id list done" ); for( auto id : idList ) { auto teri1 = g_exdData.get( id ); g_log.info( teri1->name ); }*/ return 0; } int main() { dumpTemplates(); return 0; }