diff --git a/CMakeLists.txt b/CMakeLists.txt index 3ada33ad..b9b359a5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -52,5 +52,6 @@ add_subdirectory( "src/tools/exd_struct_gen" ) add_subdirectory( "src/tools/exd_struct_test" ) add_subdirectory( "src/tools/quest_parser" ) add_subdirectory( "src/tools/discovery_parser" ) +add_subdirectory( "src/tools/mob_parse" ) #add_subdirectory("src/tools/pcb_reader") #add_subdirectory("src/tools/event_object_parser") diff --git a/src/tools/mob_parse/CMakeLists.txt b/src/tools/mob_parse/CMakeLists.txt new file mode 100644 index 00000000..a2a8a440 --- /dev/null +++ b/src/tools/mob_parse/CMakeLists.txt @@ -0,0 +1,33 @@ +cmake_minimum_required(VERSION 2.6) +cmake_policy(SET CMP0015 NEW) +project(Tool_mob_parse) + +set(SAPPHIRE_BOOST_VER 1.63.0) +set(SAPPHIRE_BOOST_FOLDER_NAME boost_1_63_0) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../bin/") + + +file(GLOB SERVER_PUBLIC_INCLUDE_FILES "${CMAKE_CURRENT_SOURCE_DIR}/*") +file(GLOB SERVER_SOURCE_FILES "${CMAKE_CURRENT_SOURCE_DIR}*.c*") + +#set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "../bin/") +add_executable(mob_parse ${SERVER_PUBLIC_INCLUDE_FILES} ${SERVER_SOURCE_FILES}) + +set_target_properties(mob_parse PROPERTIES + CXX_STANDARD 14 + CXX_STANDARD_REQUIRED ON + CXX_EXTENSIONS ON + RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_CURRENT_SOURCE_DIR}/../bin/" + RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_SOURCE_DIR}/../bin/" + RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_CURRENT_SOURCE_DIR}/../bin/" + RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_CURRENT_SOURCE_DIR}/../bin/" +) + +if (UNIX) + target_link_libraries (mob_parse common xivdat pthread mysqlclient dl z) +else() + target_link_libraries (mob_parse common xivdat libmysql zlib1) +endif() + +target_link_libraries(mob_parse ${Boost_LIBRARIES} ${Boost_LIBRARIES}) + diff --git a/src/tools/mob_parse/main.cpp b/src/tools/mob_parse/main.cpp new file mode 100644 index 00000000..84427731 --- /dev/null +++ b/src/tools/mob_parse/main.cpp @@ -0,0 +1,328 @@ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace boost::system; +namespace filesys = boost::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::vector< std::string > getAllFilesInDir( const std::string &dirPath, + const std::vector 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() ); + } + + 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; +} + + + +int main() +{ + + 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 str = file.substr( 0, pos ); + pos = str.find_last_of( "\\" ); + 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 ) ); + } +*/ + + } + + 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.bNPCName ].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.first ); + g_log.info( "|--> " + nameStruct->singular + "(" + std::to_string( mobName.second.size() ) + ")" ); + + 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 += "]"; + + + //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 ) ); + } + } + nameToPacketList.clear(); + } + + /*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; +}