2017-08-08 13:53:47 +02:00
# include <stdio.h>
2017-10-16 15:47:48 +01:00
# include <cstdint>
# include <string>
2017-11-04 08:13:35 +00:00
# include <iostream>
# include <chrono>
# include <fstream>
2017-12-09 23:37:24 +00:00
# include <regex>
2018-01-24 00:15:46 +00:00
# include <map>
2017-08-08 13:53:47 +02:00
# include "pcb.h"
2017-10-16 15:47:48 +01:00
# include "lgb.h"
2017-10-19 16:29:17 +01:00
# include "sgb.h"
2017-08-08 13:53:47 +02:00
2017-11-03 20:22:11 +00:00
# ifndef STANDALONE
2017-10-16 23:54:27 +02:00
# include <GameData.h>
# include <File.h>
# include <DatCat.h>
# include <ExdData.h>
# include <ExdCat.h>
# include <Exd.h>
2018-01-23 13:13:35 +00:00
# include <boost/algorithm/string.hpp>
2017-11-03 20:22:11 +00:00
# endif
2017-10-16 23:54:27 +02:00
2018-01-24 00:15:46 +00:00
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 ;
2018-02-04 20:09:55 +00:00
std : : unordered_map < uint16_t , std : : string > zoneNameMap ;
2018-01-24 15:34:01 +00:00
xiv : : dat : : GameData * data1 = nullptr ;
xiv : : exd : : ExdData * eData = nullptr ;
2018-01-24 00:15:46 +00:00
2018-01-23 13:13:35 +00:00
enum class TerritoryTypeExdIndexes : size_t
{
TerritoryType = 0 ,
Path = 1
} ;
2017-10-17 21:49:01 +01:00
using namespace std : : chrono_literals ;
2017-10-18 15:06:10 +01:00
2017-10-17 15:14:48 +01:00
struct face
{
int32_t f1 , f2 , f3 ;
} ;
2017-08-08 13:53:47 +02:00
2018-01-24 15:34:01 +00:00
void initExd ( const std : : string & gamePath )
{
data1 = data1 ? data1 : new xiv : : dat : : GameData ( gamePath ) ;
eData = eData ? eData : new xiv : : exd : : ExdData ( * data1 ) ;
}
2017-10-16 20:31:47 +01:00
int parseBlockEntry ( char * data , std : : vector < PCB_BLOCK_ENTRY > & entries , int gOff )
2017-08-08 13:53:47 +02:00
{
int offset = 0 ;
bool isgroup = true ;
while ( isgroup )
{
PCB_BLOCK_ENTRY block_entry ;
memcpy ( & block_entry . header , data + offset , sizeof ( block_entry . header ) ) ;
2017-10-17 21:49:01 +01:00
isgroup = block_entry . header . type = = 0x30 ;
2017-08-08 13:53:47 +02:00
//printf( " BLOCKHEADER_%X: type: %i, group_size: %i\n", gOff + offset, block_entry.header.type, block_entry.header.group_size );
if ( isgroup )
{
2017-10-16 20:31:47 +01:00
parseBlockEntry ( data + offset + 0x30 , entries , gOff + offset ) ;
2017-08-08 13:53:47 +02:00
offset + = block_entry . header . group_size ;
}
else
{
2017-10-16 20:31:47 +01:00
/* printf( "\tnum_v16: %i, num_indices: %i, num_vertices: %i\n\n",
block_entry . header . num_v16 , block_entry . header . num_indices , block_entry . header . num_vertices ) ; */
2017-08-08 13:53:47 +02:00
int doffset = sizeof ( block_entry . header ) + offset ;
uint16_t block_size = sizeof ( block_entry . header ) +
block_entry . header . num_vertices * 3 * 4 +
block_entry . header . num_v16 * 6 +
block_entry . header . num_indices * 6 ;
if ( block_entry . header . num_vertices ! = 0 )
{
block_entry . data . vertices . resize ( block_entry . header . num_vertices ) ;
int32_t size_vertexbuffer = block_entry . header . num_vertices * 3 ;
memcpy ( & block_entry . data . vertices [ 0 ] , data + doffset , size_vertexbuffer * 4 ) ;
doffset + = size_vertexbuffer * 4 ;
}
if ( block_entry . header . num_v16 ! = 0 )
{
block_entry . data . vertices_i16 . resize ( block_entry . header . num_v16 ) ;
int32_t size_unknownbuffer = block_entry . header . num_v16 * 6 ;
memcpy ( & block_entry . data . vertices_i16 [ 0 ] , data + doffset , size_unknownbuffer ) ;
doffset + = block_entry . header . num_v16 * 6 ;
}
if ( block_entry . header . num_indices ! = 0 )
{
block_entry . data . indices . resize ( block_entry . header . num_indices ) ;
2017-10-16 23:54:27 +02:00
int32_t size_indexbuffer = block_entry . header . num_indices * 12 ;
2017-08-08 13:53:47 +02:00
memcpy ( & block_entry . data . indices [ 0 ] , data + doffset , size_indexbuffer ) ;
doffset + = size_indexbuffer ;
}
entries . push_back ( block_entry ) ;
}
}
return 0 ;
}
2018-01-24 00:15:46 +00:00
void dumpLevelExdEntries ( uint32_t zoneId , const std : : string & name = std : : string ( ) )
{
2018-02-04 20:09:55 +00:00
static auto & cat = eData - > get_category ( " Level " ) ;
static auto exd = static_cast < xiv : : exd : : Exd > ( cat . get_data_ln ( xiv : : exd : : Language : : none ) ) ;
2018-01-24 00:15:46 +00:00
std : : string fileName ( name + " _ " + std : : to_string ( zoneId ) + " _Level " + " .csv " ) ;
std : : ofstream outfile ( fileName , std : : ios : : trunc ) ;
2018-01-24 15:34:01 +00:00
std : : cout < < " [Info] Writing level.exd entries to " < < fileName < < " \n " ;
2018-01-24 00:15:46 +00:00
if ( outfile . good ( ) )
{
outfile . close ( ) ;
outfile . open ( fileName , std : : ios : : app ) ;
2018-02-04 20:09:55 +00:00
static auto rows = exd . get_rows ( ) ;
for ( auto & row : rows )
2018-01-24 00:15:46 +00:00
{
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 ( ) ) ;
}
}
}
}
2017-10-17 12:15:46 +01:00
std : : string zoneNameToPath ( const std : : string & name )
{
2017-12-09 23:37:24 +00:00
std : : string path ;
2018-01-24 00:15:46 +00:00
uint32_t id ;
2018-02-04 20:09:55 +00:00
bool found = false ;
2018-01-23 13:13:35 +00:00
# ifdef STANDALONE
2017-12-09 23:37:24 +00:00
auto inFile = std : : ifstream ( " territorytype.exh.csv " ) ;
if ( inFile . good ( ) )
{
std : : string line ;
2017-12-16 13:13:49 +00:00
std : : regex re ( " ( \\ d+), \" (.*) \" , \" (.*) \" ,.* " ) ;
2017-12-09 23:37:24 +00:00
while ( std : : getline ( inFile , line ) )
{
std : : smatch match ;
2018-02-04 20:09:55 +00:00
if ( std : : regex_match ( line , match , re )
2017-12-09 23:37:24 +00:00
{
2018-02-04 20:09:55 +00:00
if ( ! found & & name = = match [ 2 ] . str ( ) )
2017-12-09 23:37:24 +00:00
{
2018-01-24 00:15:46 +00:00
id = match [ 1 ] . str ( ) ;
2017-12-09 23:37:24 +00:00
path = match [ 3 ] . str ( ) ;
2018-02-04 20:09:55 +00:00
found = true ;
2017-12-09 23:37:24 +00:00
}
2018-02-04 20:09:55 +00:00
zoneNameMap [ std : : stoul ( match [ 1 ] . str ( ) ) ] = match [ 2 ] . str ( ) ;
2017-12-09 23:37:24 +00:00
}
}
2017-12-16 13:13:49 +00:00
inFile . close ( ) ;
2017-12-09 23:37:24 +00:00
}
2018-01-23 13:13:35 +00:00
# else
2018-02-04 20:09:55 +00:00
auto & cat = eData - > get_category ( " TerritoryType " ) ;
2018-01-23 13:13:35 +00:00
auto exd = static_cast < xiv : : exd : : Exd > ( cat . get_data_ln ( xiv : : exd : : Language : : none ) ) ;
for ( auto & row : exd . get_rows ( ) )
{
auto & fields = row . second ;
auto teriName = * boost : : get < std : : string > ( & fields . at ( static_cast < size_t > ( TerritoryTypeExdIndexes : : TerritoryType ) ) ) ;
if ( teriName . empty ( ) )
continue ;
auto teriPath = * boost : : get < std : : string > ( & fields . at ( static_cast < size_t > ( TerritoryTypeExdIndexes : : Path ) ) ) ;
2018-02-04 20:09:55 +00:00
if ( ! found & & boost : : iequals ( name , teriName ) )
2018-01-23 13:13:35 +00:00
{
path = teriPath ;
2018-02-04 20:09:55 +00:00
found = true ;
id = row . first ;
2018-01-23 13:13:35 +00:00
}
2018-02-04 20:09:55 +00:00
zoneNameMap [ row . first ] = teriName ;
2018-01-23 13:13:35 +00:00
}
# endif
2018-02-04 20:09:55 +00:00
if ( found )
2017-10-17 12:15:46 +01:00
{
2017-12-09 23:37:24 +00:00
//path = path.substr( path.find_first_of( "/" ) + 1, path.size() - path.find_first_of( "/" ));
2017-12-16 13:13:49 +00:00
//path = std::string( "ffxiv/" ) + path;
path = std : : string ( " bg/ " ) + path . substr ( 0 , path . find ( " /level/ " ) ) ;
2017-12-09 23:37:24 +00:00
std : : cout < < " [Info] " < < " Found path for " < < name < < " : " < < path < < std : : endl ;
}
else
2017-10-17 12:15:46 +01:00
{
2017-12-09 23:37:24 +00:00
throw std : : runtime_error ( " Unable to find path for " + name +
2017-12-16 13:13:49 +00:00
" . \n \t Please double check spelling or open 0a0000.win32.index with FFXIV Explorer and extract territorytype.exh as CSV \n \t and copy territorytype.exh.csv into pcb_reader.exe directory if using standalone " ) ;
2017-12-09 23:37:24 +00:00
}
2018-01-24 00:15:46 +00:00
dumpLevelExdEntries ( id , name ) ;
2017-12-09 23:37:24 +00:00
return path ;
2017-10-17 12:15:46 +01:00
}
2018-01-24 00:15:46 +00:00
void loadEobjNames ( )
{
2018-01-24 15:34:01 +00:00
auto & cat = eData - > get_category ( " EObjName " ) ;
2018-01-24 00:15:46 +00:00
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 )
{
2018-02-04 20:09:55 +00:00
static std : : string mapRangeStr ( " \" MapRange \" , " ) ;
static std : : string eobjStr ( " \" EObj \" , " ) ;
2018-01-24 00:15:46 +00:00
std : : string outStr (
2018-02-04 20:09:55 +00:00
std : : to_string ( pEobj - > header . eobjId ) + " , \" " + name + " \" , " + std : : string ( pEobj - > header . type = = LgbEntryType : : MapRange ? mapRangeStr : eobjStr ) +
2018-01-24 00:15:46 +00:00
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 ( ) ) ;
}
2018-02-04 20:09:55 +00:00
void dumpAllInstanceContentEntries ( )
{
auto & catInstance = eData - > get_category ( " InstanceContent " ) ;
auto exdInstance = static_cast < xiv : : exd : : Exd > ( catInstance . get_data_ln ( xiv : : exd : : Language : : en ) ) ;
if ( zoneNameMap . size ( ) = = 0 )
{
zoneNameToPath ( " f1d1 " ) ;
}
for ( auto & row : exdInstance . get_rows ( ) )
{
auto id = row . first ;
auto & fields = row . second ;
auto name = * boost : : get < std : : string > ( & fields . at ( 3 ) ) ;
if ( name . empty ( ) )
continue ;
auto teri = * boost : : get < uint32_t > ( & fields . at ( 7 ) ) ;
auto i = 0 ;
while ( ( i = name . find ( ' ' ) ) ! = std : : string : : npos )
name = name . replace ( name . begin ( ) + i , name . begin ( ) + i + 1 , { ' _ ' } ) ;
dumpLevelExdEntries ( teri , name ) ;
}
}
2017-11-03 20:22:11 +00:00
void readFileToBuffer ( const std : : string & path , std : : vector < char > & buf )
{
auto inFile = std : : ifstream ( path , std : : ios : : binary ) ;
if ( inFile . good ( ) )
{
inFile . seekg ( 0 , inFile . end ) ;
2017-12-17 10:37:29 +00:00
int32_t fileSize = ( int32_t ) inFile . tellg ( ) ;
2017-11-03 20:22:11 +00:00
buf . resize ( fileSize ) ;
inFile . seekg ( 0 , inFile . beg ) ;
inFile . read ( & buf [ 0 ] , fileSize ) ;
inFile . close ( ) ;
}
else
{
throw std : : runtime_error ( " Unable to open " + path ) ;
}
}
2017-10-17 12:15:46 +01:00
int main ( int argc , char * argv [ ] )
2017-08-08 13:53:47 +02:00
{
2017-10-17 21:49:01 +01:00
auto startTime = std : : chrono : : system_clock : : now ( ) ;
2017-12-09 23:37:24 +00:00
// todo: support expansions
2017-10-17 12:15:46 +01:00
std : : string zoneName = " r1f1 " ;
if ( argc > 1 )
{
2017-12-09 23:37:24 +00:00
zoneName = argv [ 1 ] ;
2017-10-17 12:15:46 +01:00
if ( argc > 2 )
{
2017-12-09 23:37:24 +00:00
gamePath = argv [ 2 ] ;
2017-10-17 12:15:46 +01:00
}
}
2018-01-24 15:34:01 +00:00
initExd ( gamePath ) ;
2017-10-17 21:49:01 +01:00
try
{
2017-12-09 23:37:24 +00:00
const auto & zonePath = zoneNameToPath ( zoneName ) ;
std : : string listPcbPath ( zonePath + " /collision/list.pcb " ) ;
2018-01-24 15:34:01 +00:00
std : : string bgLgbPath ( zonePath + " /level/bg.lgb " ) ;
std : : string planmapLgbPath ( zonePath + " /level/planmap.lgb " ) ;
2017-12-09 23:37:24 +00:00
std : : string collisionFilePath ( zonePath + " /collision/ " ) ;
2017-11-03 20:22:11 +00:00
std : : vector < char > section ;
std : : vector < char > section1 ;
2018-01-24 15:34:01 +00:00
std : : vector < char > section2 ;
2017-11-03 20:22:11 +00:00
2017-12-16 13:13:49 +00:00
# ifndef STANDALONE
2018-01-24 15:34:01 +00:00
const xiv : : dat : : Cat & test = data1 - > get_category ( " bg " ) ;
2017-10-16 23:54:27 +02:00
2018-01-24 15:34:01 +00:00
auto test_file = data1 - > get_file ( bgLgbPath ) ;
2017-11-03 20:22:11 +00:00
section = test_file - > access_data_sections ( ) . at ( 0 ) ;
2018-01-24 15:34:01 +00:00
auto planmap_file = data1 - > get_file ( planmapLgbPath ) ;
section2 = planmap_file - > access_data_sections ( ) . at ( 0 ) ;
auto test_file1 = data1 - > get_file ( listPcbPath ) ;
2017-11-03 20:22:11 +00:00
section1 = test_file1 - > access_data_sections ( ) . at ( 0 ) ;
2017-12-16 13:13:49 +00:00
# else
2017-11-03 20:22:11 +00:00
{
readFileToBuffer ( bgLgbPath , section ) ;
readFileToBuffer ( listPcbPath , section1 ) ;
}
2017-12-16 13:13:49 +00:00
# endif
2018-02-04 20:09:55 +00:00
if ( argc > 3 & & argv [ 3 ] = = " --instanceDump " )
dumpAllInstanceContentEntries ( ) ;
2017-11-03 20:22:11 +00:00
2017-11-04 10:03:46 +00:00
std : : vector < std : : string > stringList ;
2017-10-16 23:54:27 +02:00
2017-10-20 13:13:47 +01:00
uint32_t offset1 = 0x20 ;
2018-01-24 00:15:46 +00:00
loadEobjNames ( ) ;
2018-02-04 20:09:55 +00:00
std : : string eobjFileName ( zoneName + " _eobj.csv " ) ;
2018-01-24 00:15:46 +00:00
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 ) ;
2018-02-04 20:09:55 +00:00
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 ( ) ;
2017-10-17 21:49:01 +01:00
for ( ; ; )
{
2017-10-16 23:54:27 +02:00
2017-10-17 21:49:01 +01:00
uint16_t trId = * ( uint16_t * ) & section1 [ offset1 ] ;
2017-10-16 23:54:27 +02:00
2017-10-17 21:49:01 +01:00
char someString [ 200 ] ;
2017-11-03 20:22:11 +00:00
sprintf ( someString , " %str%04d.pcb " , collisionFilePath . c_str ( ) , trId ) ;
2017-10-17 21:49:01 +01:00
stringList . push_back ( std : : string ( someString ) ) ;
//std::cout << someString << "\n";
offset1 + = 0x20 ;
2017-10-16 23:54:27 +02:00
2017-10-17 21:49:01 +01:00
if ( offset1 > = section1 . size ( ) )
{
break ;
}
2017-10-16 23:54:27 +02:00
}
2017-10-17 15:14:48 +01:00
2017-10-17 21:49:01 +01:00
LGB_FILE bgLgb ( & section [ 0 ] ) ;
2018-01-24 15:34:01 +00:00
LGB_FILE planmapLgb ( & section2 [ 0 ] ) ;
std : : vector < LGB_FILE > lgbList { bgLgb , planmapLgb } ;
2017-10-20 13:13:47 +01:00
uint32_t max_index = 0 ;
2017-10-16 23:54:27 +02:00
2017-10-17 21:49:01 +01:00
// dont bother if we cant write to a file
2017-10-18 15:06:10 +01:00
auto fp_out = fopen ( ( zoneName + " .obj " ) . c_str ( ) , " w " ) ;
2017-10-17 21:49:01 +01:00
if ( fp_out )
{
fprintf ( fp_out , " \n " ) ;
fclose ( fp_out ) ;
}
else
{
std : : string errorMessage ( " Cannot create " + zoneName + " .obj \n " +
" Check no programs have a handle to file and run as admin. \n " ) ;
std : : cout < < errorMessage ;
2017-10-18 15:06:10 +01:00
throw std : : runtime_error ( errorMessage . c_str ( ) ) ;
2017-10-17 21:49:01 +01:00
return 0 ;
}
2017-10-16 23:54:27 +02:00
2017-10-18 15:06:10 +01:00
fp_out = fopen ( ( zoneName + " .obj " ) . c_str ( ) , " ab+ " ) ;
2017-10-17 21:49:01 +01:00
if ( fp_out )
2017-10-16 23:54:27 +02:00
{
2017-11-04 10:03:46 +00:00
std : : map < std : : string , PCB_FILE > pcbFiles ;
std : : map < std : : string , SGB_FILE > sgbFiles ;
std : : map < std : : string , uint32_t > objCount ;
2017-10-19 16:29:17 +01:00
auto loadPcbFile = [ & ] ( const std : : string & fileName ) - > bool
2017-10-17 15:14:48 +01:00
{
2017-10-17 21:49:01 +01:00
try
{
2018-01-24 15:34:01 +00:00
if ( fileName . find ( ' . ' ) = = std : : string : : npos )
return false ;
2018-02-04 20:09:55 +00:00
else if ( fileName . substr ( fileName . find_last_of ( ' . ' ) ) ! = " .pcb " )
2018-01-24 15:34:01 +00:00
throw std : : runtime_error ( " Not a PCB file. " ) ;
2017-11-03 20:22:11 +00:00
char * dataSection = nullptr ;
2017-10-17 21:49:01 +01:00
//std::cout << fileName << " ";
2017-12-16 13:13:49 +00:00
# ifndef STANDALONE
2018-01-24 15:34:01 +00:00
auto file = data1 - > get_file ( fileName ) ;
2017-10-17 21:49:01 +01:00
auto sections = file - > get_data_sections ( ) ;
2017-11-03 20:22:11 +00:00
dataSection = & sections . at ( 0 ) [ 0 ] ;
2017-12-16 13:13:49 +00:00
# else
2017-11-03 20:22:11 +00:00
std : : vector < char > buf ;
readFileToBuffer ( fileName , buf ) ;
dataSection = & buf [ 0 ] ;
2017-12-16 13:13:49 +00:00
# endif
2017-10-17 21:49:01 +01:00
//std::cout << sections.size() << "\n";
2017-10-16 23:54:27 +02:00
2017-10-17 21:49:01 +01:00
uint32_t offset = 0 ;
PCB_FILE pcb_file ;
memcpy ( & pcb_file . header , & dataSection [ 0 ] , sizeof ( pcb_file . header ) ) ;
offset + = sizeof ( pcb_file . header ) ;
2017-10-19 16:29:17 +01:00
pcb_file . entries . resize ( pcb_file . header . num_entries ) ;
2017-10-17 21:49:01 +01:00
bool isgroup = true ;
while ( isgroup )
2017-10-17 15:14:48 +01:00
{
2017-10-17 21:49:01 +01:00
PCB_BLOCK_ENTRY block_entry ;
memcpy ( & block_entry . header , & dataSection [ 0 ] + offset , sizeof ( block_entry . header ) ) ;
isgroup = block_entry . header . type = = 0x30 ;
//printf( "BLOCKHEADER_%X: type: %i, group_size: %i\n", offset, block_entry.header.type, block_entry.header.group_size );
//
if ( isgroup )
{
parseBlockEntry ( & dataSection [ 0 ] + offset + 0x30 , pcb_file . entries , offset ) ;
offset + = block_entry . header . group_size ;
}
else
{
parseBlockEntry ( & dataSection [ 0 ] + offset , pcb_file . entries , offset ) ;
}
2017-10-17 15:14:48 +01:00
}
2017-10-17 21:49:01 +01:00
pcbFiles . insert ( std : : make_pair ( fileName , pcb_file ) ) ;
2017-10-19 16:29:17 +01:00
return true ;
2017-10-17 12:15:46 +01:00
}
2017-10-17 21:49:01 +01:00
catch ( std : : exception & e )
{
2017-12-09 23:37:24 +00:00
std : : cout < < " [Error] " < < " Unable to load collision mesh " < < fileName < < " \n \t Error: \n \t " < < e . what ( ) < < " \n " ;
2017-10-19 16:29:17 +01:00
return false ;
}
} ;
auto loadSgbFile = [ & ] ( const std : : string & fileName ) - > bool
{
2017-10-20 17:56:01 +01:00
SGB_FILE sgbFile ;
2017-10-19 16:29:17 +01:00
try
{
2017-11-03 20:22:11 +00:00
char * dataSection = nullptr ;
//std::cout << fileName << " ";
2017-12-16 13:13:49 +00:00
# ifndef STANDALONE
2018-01-24 15:34:01 +00:00
auto file = data1 - > get_file ( fileName ) ;
2017-10-19 16:29:17 +01:00
auto sections = file - > get_data_sections ( ) ;
2017-11-03 20:22:11 +00:00
dataSection = & sections . at ( 0 ) [ 0 ] ;
2017-12-16 13:13:49 +00:00
# else
2017-11-03 20:22:11 +00:00
std : : vector < char > buf ;
readFileToBuffer ( fileName , buf ) ;
dataSection = & buf [ 0 ] ;
2017-12-16 13:13:49 +00:00
# endif
2017-10-20 17:56:01 +01:00
sgbFile = SGB_FILE ( & dataSection [ 0 ] ) ;
2017-10-19 16:29:17 +01:00
sgbFiles . insert ( std : : make_pair ( fileName , sgbFile ) ) ;
return true ;
}
catch ( std : : exception & e )
{
2017-12-09 23:37:24 +00:00
std : : cout < < " [Error] " < < " Unable to load SGB " < < fileName < < " \n \t Error: \n \t " < < e . what ( ) < < " \n " ;
2017-10-20 17:56:01 +01:00
sgbFiles . insert ( std : : make_pair ( fileName , sgbFile ) ) ;
2017-10-17 21:49:01 +01:00
}
2017-10-20 17:56:01 +01:00
return false ;
2017-10-17 21:49:01 +01:00
} ;
2017-10-20 22:50:21 +01:00
auto pushVerts = [ & ] ( const PCB_FILE & pcb_file , const std : : string & name ,
const vec3 * scale = nullptr ,
const vec3 * rotation = nullptr ,
const vec3 * translation = nullptr ,
const SGB_MODEL_ENTRY * pSgbEntry = nullptr )
2017-10-17 12:15:46 +01:00
{
2017-10-20 13:13:47 +01:00
char name2 [ 0x100 ] ;
memset ( name2 , 0 , 0x100 ) ;
2017-10-20 17:56:01 +01:00
sprintf ( & name2 [ 0 ] , " %s_%u " , & name [ 0 ] , objCount [ name ] + + ) ;
2017-10-20 22:50:21 +01:00
fprintf ( fp_out , " o %s \n " , & name2 [ 0 ] ) ;
2017-10-20 17:56:01 +01:00
2017-10-17 21:49:01 +01:00
uint32_t groupCount = 0 ;
2017-10-18 15:06:10 +01:00
for ( const auto & entry : pcb_file . entries )
2017-10-17 12:15:46 +01:00
{
2017-10-17 21:49:01 +01:00
float x_base = abs ( float ( entry . header . x1 - entry . header . x ) ) ;
float y_base = abs ( float ( entry . header . y1 - entry . header . y ) ) ;
float z_base = abs ( float ( entry . header . z1 - entry . header . z ) ) ;
2017-10-17 15:14:48 +01:00
2017-10-17 21:49:01 +01:00
auto makeTranslation = [ & ] ( vec3 & v )
{
2017-10-20 22:50:21 +01:00
if ( pSgbEntry )
{
v . x * = pSgbEntry - > header . scale . x ;
v . y * = pSgbEntry - > header . scale . y ;
v . z * = pSgbEntry - > header . scale . z ;
v = v * matrix4 : : rotateX ( pSgbEntry - > header . rotation . x ) ;
v = v * matrix4 : : rotateY ( pSgbEntry - > header . rotation . y ) ;
v = v * matrix4 : : rotateZ ( pSgbEntry - > header . rotation . z ) ;
v . x + = pSgbEntry - > header . translation . x ;
v . y + = pSgbEntry - > header . translation . y ;
v . z + = pSgbEntry - > header . translation . z ;
}
2017-10-17 21:49:01 +01:00
if ( scale )
{
v . x * = scale - > x ;
v . y * = scale - > y ;
v . z * = scale - > z ;
2017-10-17 15:14:48 +01:00
2017-10-17 21:49:01 +01:00
v = v * matrix4 : : rotateX ( rotation - > x ) ;
v = v * matrix4 : : rotateY ( rotation - > y ) ;
v = v * matrix4 : : rotateZ ( rotation - > z ) ;
2017-10-16 23:54:27 +02:00
2017-10-17 21:49:01 +01:00
v . x + = translation - > x ;
v . y + = translation - > y ;
v . z + = translation - > z ;
}
2017-10-16 23:54:27 +02:00
2017-10-17 21:49:01 +01:00
} ;
2017-10-17 12:15:46 +01:00
2017-10-17 21:49:01 +01:00
for ( auto & vertex : entry . data . vertices )
{
vec3 v ( vertex . x , vertex . y , vertex . z ) ;
makeTranslation ( v ) ;
fprintf ( fp_out , " v %f %f %f \n " , v . x , v . y , v . z ) ;
}
2017-10-16 23:54:27 +02:00
2017-10-17 21:49:01 +01:00
for ( const auto & link : entry . data . vertices_i16 )
2017-10-17 15:14:48 +01:00
{
2017-10-17 21:49:01 +01:00
vec3 v ( float ( link . x ) / 0xFFFF , float ( link . y ) / 0xFFFF , float ( link . z ) / 0xFFFF ) ;
v . x = v . x * x_base + entry . header . x ;
v . y = v . y * y_base + entry . header . y ;
v . z = v . z * z_base + entry . header . z ;
makeTranslation ( v ) ;
fprintf ( fp_out , " v %f %f %f \n " , v . x , v . y , v . z ) ;
}
2017-10-17 15:14:48 +01:00
2017-10-17 21:49:01 +01:00
//fprintf( fp_out, "g %s_", (name2 + "_" + std::to_string( groupCount++ )).c_str() );
for ( const auto & index : entry . data . indices )
{
2017-10-20 17:56:01 +01:00
fprintf ( fp_out , " f %i %i %i \n " ,
index . index [ 0 ] + max_index + 1 ,
index . index [ 1 ] + max_index + 1 ,
index . index [ 2 ] + max_index + 1 ) ;
2017-10-18 15:06:10 +01:00
// std::cout << std::to_string( index.unknown[0] )<< " " << std::to_string( index.unknown[1] )<< " " << std::to_string( index.unknown[2]) << std::endl;
2017-10-17 15:14:48 +01:00
}
2017-10-17 21:49:01 +01:00
max_index + = entry . data . vertices . size ( ) + entry . data . vertices_i16 . size ( ) ;
2017-10-16 23:54:27 +02:00
}
2017-10-17 21:49:01 +01:00
} ;
2017-10-16 23:54:27 +02:00
2017-10-17 21:49:01 +01:00
for ( const auto & fileName : stringList )
2017-10-16 15:47:48 +01:00
{
2017-10-17 21:49:01 +01:00
loadPcbFile ( fileName ) ;
pushVerts ( pcbFiles [ fileName ] , fileName ) ;
}
2017-12-09 23:37:24 +00:00
std : : cout < < " [Info] " < < " Writing obj file " < < " \n " ;
std : : cout < < " [Info] " < < bgLgb . groups . size ( ) < < " groups " < < " \n " ;
2017-10-18 13:26:06 +01:00
uint32_t totalGroups = 0 ;
uint32_t totalGroupEntries = 0 ;
2018-01-24 15:34:01 +00:00
for ( const auto & lgb : lgbList )
2017-10-17 21:49:01 +01:00
{
2018-01-24 15:34:01 +00:00
for ( const auto & group : bgLgb . groups )
2017-10-17 15:14:48 +01:00
{
2018-01-24 15:34:01 +00:00
//std::cout << "\t" << group.name << " Size " << group.header.entryCount << "\n";
totalGroups + + ;
for ( const auto & pEntry : group . entries )
{
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 ( ) ) ;
2017-10-18 13:26:06 +01:00
2018-01-24 15:34:01 +00:00
std : : string fileName ( " " ) ;
fileName . resize ( 256 ) ;
totalGroupEntries + + ;
2017-10-18 15:06:10 +01:00
2018-01-24 15:34:01 +00:00
// write files
auto writeOutput = [ & ] ( const std : : string & fileName , const vec3 * scale , const vec3 * rotation , const vec3 * translation , const SGB_MODEL_ENTRY * pModel = nullptr ) - > bool
2017-10-18 13:26:06 +01:00
{
2018-01-24 15:34:01 +00:00
{
const auto & it = pcbFiles . find ( fileName ) ;
if ( it = = pcbFiles . end ( ) )
{
if ( fileName . empty ( ) | | ! loadPcbFile ( fileName ) )
return false ;
//std::cout << "\t\tLoaded PCB File " << pBgParts->collisionFileName << "\n";
}
}
2017-10-19 16:29:17 +01:00
const auto & it = pcbFiles . find ( fileName ) ;
2018-01-24 15:34:01 +00:00
if ( it ! = pcbFiles . end ( ) )
2017-10-19 16:29:17 +01:00
{
2018-01-24 15:34:01 +00:00
const auto & pcb_file = it - > second ;
pushVerts ( pcb_file , fileName , scale , rotation , translation , pModel ) ;
2017-10-19 16:29:17 +01:00
}
2018-01-24 15:34:01 +00:00
return true ;
} ;
if ( pBgParts )
2017-10-19 16:29:17 +01:00
{
2018-01-24 15:34:01 +00:00
fileName = pBgParts - > collisionFileName ;
writeOutput ( fileName , & pBgParts - > header . scale , & pBgParts - > header . rotation , & pBgParts - > header . translation ) ;
2017-10-19 16:29:17 +01:00
}
2017-10-20 17:56:01 +01:00
2018-01-24 15:34:01 +00:00
// gimmick entry
if ( pGimmick )
2017-10-19 16:29:17 +01:00
{
{
2018-01-24 15:34:01 +00:00
const auto & it = sgbFiles . find ( pGimmick - > gimmickFileName ) ;
if ( it = = sgbFiles . end ( ) )
{
// std::cout << "\tGIMMICK:\n\t\t" << pGimmick->gimmickFileName << "\n";
loadSgbFile ( pGimmick - > gimmickFileName ) ;
}
2017-10-19 16:29:17 +01:00
}
2018-01-24 15:34:01 +00:00
const auto & it = sgbFiles . find ( pGimmick - > gimmickFileName ) ;
if ( it ! = sgbFiles . end ( ) )
2017-10-19 16:29:17 +01:00
{
2018-01-24 15:34:01 +00:00
const auto & sgbFile = it - > second ;
for ( const auto & group : sgbFile . entries )
2017-10-19 16:29:17 +01:00
{
2018-01-24 15:34:01 +00:00
for ( const auto & pEntry : group . entries )
{
auto pModel = dynamic_cast < SGB_MODEL_ENTRY * > ( pEntry . get ( ) ) ;
fileName = pModel - > collisionFileName ;
writeOutput ( fileName , & pGimmick - > header . scale , & pGimmick - > header . rotation , & pGimmick - > header . translation , pModel ) ;
}
2017-10-19 16:29:17 +01:00
}
}
}
2018-01-24 00:15:46 +00:00
2018-02-04 20:09:55 +00:00
if ( pEntry - > header . type = = LgbEntryType : : EventObject | | pEntry - > header . type = = LgbEntryType : : EventNpc | |
pEntry - > header . type = = LgbEntryType : : MapRange )
{
std : : cout < < " HEADER SHIT " < < std : : to_string ( ( int ) pEntry - > header . type ) < < " \n " ;
}
2018-01-24 15:34:01 +00:00
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 );
}
2018-01-24 00:15:46 +00:00
}
2017-10-17 15:14:48 +01:00
}
2017-10-16 15:47:48 +01:00
}
2017-12-09 23:37:24 +00:00
std : : cout < < " \n [Info] " < < " Loaded " < < pcbFiles . size ( ) < < " PCB Files \n " ;
std : : cout < < " [Info] " < < " Total Groups " < < totalGroups < < " Total entries " < < totalGroupEntries < < " \n " ;
2017-10-16 15:47:48 +01:00
}
2017-12-09 23:37:24 +00:00
std : : cout < < " [Success] " < < " Finished exporting " < < zoneName < < " in " < <
2017-11-04 10:03:46 +00:00
std : : chrono : : duration_cast < std : : chrono : : seconds > ( std : : chrono : : system_clock : : now ( ) - startTime ) . count ( ) < < " seconds \n " ;
2017-10-17 21:49:01 +01:00
}
catch ( std : : exception & e )
{
2017-12-09 23:37:24 +00:00
std : : cout < < " [Error] " < < e . what ( ) < < std : : endl ;
std : : cout < < " [Error] " < < " Unable to extract collision data. \n \t If using standalone ensure your working directory folder layout is \n \t bg/[ffxiv|ex1|ex2]/teri/type/zone/[level|collision] " < < std : : endl ;
std : : cout < < std : : endl ;
std : : cout < < " [Info] " < < " Usage: pcb_reader2 territory \" path/to/game/sqpack/ffxiv \" " < < std : : endl ;
2017-08-08 13:53:47 +02:00
}
2018-01-24 15:34:01 +00:00
if ( eData )
delete eData ;
if ( data1 )
delete data1 ;
2017-10-16 15:47:48 +01:00
return 0 ;
2017-10-18 15:06:10 +01:00
}