2018-03-22 23:01:55 +01:00
# include <cstdio>
# include <cstdint>
# include <string>
# include <iostream>
# include <chrono>
# include <fstream>
# include <regex>
# include <map>
# include <vector>
# include <set>
# include <memory>
2018-10-27 22:51:16 +02:00
# include <variant>
# include <Util/Util.h>
2018-03-22 23:01:55 +01:00
2019-04-11 19:28:51 +10:00
# include <datReader/DatCategories/bg/pcb.h>
# include <datReader/DatCategories/bg/lgb.h>
# include <datReader/DatCategories/bg/sgb.h>
2018-03-22 23:01:55 +01:00
# include "tex.h"
# include "tex_decode.h"
# include <GameData.h>
# include <File.h>
# include <DatCat.h>
# include <ExdData.h>
# include <ExdCat.h>
# include <Exd.h>
2019-06-02 00:34:22 +10:00
using namespace Sapphire ;
2018-03-22 23:01:55 +01:00
// garbage to ignore models
bool ignoreModels = false ;
2018-08-03 16:54:46 +01:00
struct ZoneInfo
{
2018-08-29 21:40:59 +02:00
uint16_t id ;
std : : string name ;
std : : string path ;
2019-10-21 23:24:26 +02:00
uint16_t mapId ;
2018-08-03 16:54:46 +01:00
} ;
2018-03-22 23:01:55 +01:00
// parsing shit
2019-10-21 23:24:26 +02:00
std : : string gamePath ( " C: \\ SquareEnix \\ FINAL FANTASY XIV - A Realm Reborn \\ game \\ sqpack " ) ;
2018-03-22 23:01:55 +01:00
std : : unordered_map < uint32_t , std : : string > eobjNameMap ;
2018-08-03 16:54:46 +01:00
std : : unordered_map < uint16_t , ZoneInfo > zoneInfoMap ;
2018-03-22 23:01:55 +01:00
std : : unordered_map < uint16_t , std : : vector < std : : pair < uint16_t , std : : string > > > zoneInstanceMap ;
uint32_t zoneId ;
2019-01-18 18:02:31 +00:00
std : : string zoneName ;
2018-03-22 23:01:55 +01:00
std : : set < std : : string > zoneDumpList ;
2019-04-11 19:06:27 +10:00
xiv : : dat : : GameData * gameData = nullptr ;
2018-03-22 23:01:55 +01:00
xiv : : exd : : ExdData * eData = nullptr ;
void readFileToBuffer ( const std : : string & path , std : : vector < char > & buf ) ;
// discovery shit
struct vec2
{
2018-08-29 21:40:59 +02:00
float x , y ;
2018-03-22 23:01:55 +01:00
} ;
2019-10-21 23:24:26 +02:00
struct DiscoveryMap : std : : enable_shared_from_this < DiscoveryMap >
2018-03-22 23:01:55 +01:00
{
2018-08-29 21:40:59 +02:00
std : : string path ;
Image img ;
uint16_t mapScale ;
int16_t mapOffsetX , mapOffsetY ;
int mapId ;
constexpr static int discoveryMapRows = 3 ;
constexpr static int discoveryMapCols = 4 ;
constexpr static int tileWidth = 128 ;
constexpr static int tiles = discoveryMapCols * discoveryMapRows ;
uint32_t getColour ( uint8_t mapIndex , float x , float y , float scale )
{
auto ogX = x , ogY = y ;
int col = ( mapIndex % ( int ) ( ( float ) img . width / ( float ) tileWidth ) ) ;
int row = ( mapIndex / ( ( float ) img . width / ( float ) tileWidth ) ) ;
x = ( x / 2048.f ) * ( float ) tileWidth ;
y = ( y / 2048.f ) * ( float ) tileWidth ;
int tileX = ( col * ( float ) tileWidth ) + x ;
int tileY = ( row * ( float ) tileWidth ) + y ;
if ( tileX < 0 | | tileY < 0 | | tileY > img . data . size ( ) - 1 | | tileX > img . data [ 0 ] . size ( ) - 1 )
{
std : : cout < < " Unable to find tile coord for " < < x < < " " < < y < < " mapIndex " < < std : : to_string ( mapIndex )
< < " \n " ;
return 0 ;
}
//std::cout << "getColour col " << col << " row " << row << " tileX " << tileX << " tileY " << tileY << " tile index " << std::to_string( unkFlag2 ) << "\n";
auto colour = img . data [ tileY ] [ tileX ] ;
return colour ;
}
vec3 get3dPosFrom2d ( float x , float y )
{
vec3 ret ;
float scale2 = ( float ) mapScale / 100.0f ;
ret . x = ( x * scale2 ) + ( ( float ) img . height * 2.f ) ; //( x / scale2 ) - mapOffsetX;
ret . z = ( y * scale2 ) + ( ( float ) img . height * 2.f ) ; //( y / scale2 ) - mapOffsetY;
return ret ;
}
vec2 get2dPosFrom3d ( float x , float y , float scale )
{
vec2 ret ;
float scale2 = ( float ) ( ( float ) mapScale / 100.f ) ;
ret . x = ( ( x * scale2 ) + 1024.f ) ;
ret . y = ( ( y * scale2 ) + 1024.f ) ;
return ret ;
}
2018-03-22 23:01:55 +01:00
} ;
2018-08-29 21:40:59 +02:00
std : : map < uint16_t , std : : map < uint16_t , std : : map < uint16_t , std : : shared_ptr < DiscoveryMap > > > > discoveryMaps ;
2018-03-22 23:01:55 +01:00
2019-10-21 23:24:26 +02:00
enum class TerritoryTypeExdIndexes : size_t
2018-03-22 23:01:55 +01:00
{
2018-08-29 21:40:59 +02:00
TerritoryType = 0 ,
2019-10-21 23:24:26 +02:00
Path = 1 ,
Map = 6
2018-03-22 23:01:55 +01:00
} ;
using namespace std : : chrono_literals ;
struct face
{
2018-08-29 21:40:59 +02:00
int32_t f1 , f2 , f3 ;
2018-03-22 23:01:55 +01:00
} ;
// init
void initExd ( const std : : string & gamePath )
{
2019-04-11 19:06:27 +10:00
gameData = gameData ? gameData : new xiv : : dat : : GameData ( gamePath ) ;
eData = eData ? eData : new xiv : : exd : : ExdData ( * gameData ) ;
2018-03-22 23:01:55 +01:00
}
std : : string zoneNameToPath ( const std : : string & name )
{
2018-08-29 21:40:59 +02:00
std : : string path ;
bool found = false ;
2018-03-22 23:01:55 +01:00
2018-08-29 21:40:59 +02:00
static auto & cat = eData - > get_category ( " TerritoryType " ) ;
static auto exd = static_cast < xiv : : exd : : Exd > ( cat . get_data_ln ( xiv : : exd : : Language : : none ) ) ;
static auto & rows = exd . get_rows ( ) ;
2018-08-03 16:54:46 +01:00
2018-08-29 21:40:59 +02:00
if ( zoneInfoMap . size ( ) = = 0 )
{
for ( auto & row : rows )
{
auto & fields = row . second ;
2018-10-27 22:51:16 +02:00
auto teriName = std : : get < std : : string > (
fields . at ( static_cast < size_t > ( TerritoryTypeExdIndexes : : TerritoryType ) ) ) ;
2018-08-29 21:40:59 +02:00
if ( teriName . empty ( ) )
continue ;
2018-10-27 22:51:16 +02:00
auto teriPath = std : : get < std : : string > (
fields . at ( static_cast < size_t > ( TerritoryTypeExdIndexes : : Path ) ) ) ;
2018-08-29 21:40:59 +02:00
ZoneInfo info ;
info . id = row . first ;
info . path = teriPath ;
info . name = teriName ;
2019-10-21 23:24:26 +02:00
info . mapId = std : : get < uint16_t > (
fields . at ( static_cast < size_t > ( TerritoryTypeExdIndexes : : Map ) ) ) ;
2018-08-29 21:40:59 +02:00
zoneInfoMap [ row . first ] = info ;
2019-06-02 00:34:22 +10:00
if ( ! found & & ( Common : : Util : : toLowerCopy ( name ) = = Common : : Util : : toLowerCopy ( teriName ) ) )
2018-03-22 23:01:55 +01:00
{
2018-08-29 21:40:59 +02:00
found = true ;
path = teriPath ;
zoneId = info . id ;
2018-03-22 23:01:55 +01:00
}
2018-08-29 21:40:59 +02:00
}
}
else
{
for ( const auto & entry : zoneInfoMap )
{
2019-06-02 00:34:22 +10:00
if ( found = Common : : Util : : toLowerCopy ( name ) = = Common : : Util : : toLowerCopy ( entry . second . name ) )
2018-03-22 23:01:55 +01:00
{
2018-08-29 21:40:59 +02:00
path = entry . second . path ;
zoneId = entry . second . id ;
break ;
2018-03-22 23:01:55 +01:00
}
2018-08-29 21:40:59 +02:00
}
}
if ( found )
{
//path = path.substr( path.find_first_of( "/" ) + 1, path.size() - path.find_first_of( "/" ));
//path = std::string( "ffxiv/" ) + path;
path = std : : string ( " bg/ " ) + path . substr ( 0 , path . find ( " /level/ " ) ) ;
std : : cout < < " [Info] " < < " Found path for " < < name < < " : " < < path < < std : : endl ;
}
else
{
throw std : : runtime_error ( " Unable to find path for " + name +
" . \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 " ) ;
}
return path ;
2018-03-22 23:01:55 +01:00
}
void loadEobjNames ( )
{
2018-08-29 21:40:59 +02:00
static auto & cat = eData - > get_category ( " EObjName " ) ;
static 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 ;
2018-10-27 22:51:16 +02:00
auto name = std : : get < std : : string > ( fields . at ( 0 ) ) ;
2018-08-29 21:40:59 +02:00
eobjNameMap [ id ] = name ;
}
2018-03-22 23:01:55 +01:00
}
2019-10-21 23:24:26 +02:00
void writeMapRangeEntry ( std : : ofstream & out , LgbEntry * pObj )
2018-03-22 23:01:55 +01:00
{
2019-10-21 23:24:26 +02:00
auto pMapRange = reinterpret_cast < LGB_MAP_RANGE_ENTRY * > ( pObj ) ;
2019-10-22 22:34:34 +02:00
if ( ! pMapRange - > data . discoveryEnabled )
2019-10-21 23:24:26 +02:00
return ;
2018-08-29 21:40:59 +02:00
auto subArea = 0 ;
auto mapId = - 1 ;
2019-10-22 22:34:34 +02:00
auto discoveryIndex = pMapRange - > data . discoveryIndex ;
2018-08-29 21:40:59 +02:00
2019-10-21 23:24:26 +02:00
vec3 translation = pObj - > header . transform . translation ;
2018-08-29 21:40:59 +02:00
2019-10-21 23:24:26 +02:00
std : : string outStr ( pMapRange - > name + " " + std : : to_string ( pMapRange - > header . instanceId ) + " " +
std : : to_string ( pMapRange - > header . transform . translation . x ) + " " +
std : : to_string ( pMapRange - > header . transform . translation . y ) + " " +
std : : to_string ( pMapRange - > header . transform . translation . z ) + " " +
std : : to_string ( pMapRange - > header . transform . rotation . y ) + " " +
2019-10-22 22:34:34 +02:00
std : : to_string ( pMapRange - > data . mapId ) + " " +
std : : to_string ( pMapRange - > data . discoveryIndex ) + " \n "
2019-01-18 18:02:31 +00:00
) ;
2018-08-29 21:40:59 +02:00
2019-10-21 23:24:26 +02:00
out . write ( outStr . c_str ( ) , outStr . size ( ) ) ;
2018-03-22 23:01:55 +01:00
}
int main ( int argc , char * argv [ ] )
{
2018-08-29 21:40:59 +02:00
auto startTime = std : : chrono : : system_clock : : now ( ) ;
auto entryStartTime = std : : chrono : : system_clock : : now ( ) ;
std : : vector < std : : string > argVec ( argv + 1 , argv + argc ) ;
2019-01-18 18:02:31 +00:00
zoneName = " s1h1 " ;
2018-08-29 21:40:59 +02:00
if ( argc > 1 )
{
zoneName = argv [ 1 ] ;
if ( argc > 2 )
{
std : : string tmpPath ( argv [ 2 ] ) ;
if ( ! tmpPath . empty ( ) )
gamePath = argv [ 2 ] ;
}
}
initExd ( gamePath ) ;
2019-10-21 23:24:26 +02:00
std : : ofstream discoverySql ( " maprange_export.txt " , std : : ios : : trunc ) ;
2018-08-29 21:40:59 +02:00
2019-10-21 23:24:26 +02:00
zoneNameToPath ( " f1f1 " ) ;
zoneDumpList . emplace ( " f1f1 " ) ;
zoneDumpList . emplace ( " f1f2 " ) ;
2018-08-29 21:40:59 +02:00
2019-10-21 23:24:26 +02:00
for ( const auto & zoneName : zoneDumpList )
2018-08-29 21:40:59 +02:00
{
2019-10-21 23:24:26 +02:00
entryStartTime = std : : chrono : : system_clock : : now ( ) ;
discoverySql . write ( ( zoneName + " \n " ) . c_str ( ) , zoneName . size ( ) + 1 ) ;
try
2018-08-29 21:40:59 +02:00
{
2019-10-21 23:24:26 +02:00
const auto zonePath = zoneNameToPath ( zoneName ) ;
2018-03-22 23:01:55 +01:00
2019-10-21 23:24:26 +02:00
std : : string bgLgbPath ( zonePath + " /level/bg.lgb " ) ;
std : : string planmapLgbPath ( zonePath + " /level/planmap.lgb " ) ;
std : : vector < char > section ;
std : : vector < char > section2 ;
2018-03-22 23:01:55 +01:00
2019-10-21 23:24:26 +02:00
auto test_file = gameData - > getFile ( bgLgbPath ) ;
section = test_file - > access_data_sections ( ) . at ( 0 ) ;
2018-03-22 23:01:55 +01:00
2019-10-21 23:24:26 +02:00
auto planmap_file = gameData - > getFile ( planmapLgbPath ) ;
section2 = planmap_file - > access_data_sections ( ) . at ( 0 ) ;
2018-08-29 21:40:59 +02:00
2019-10-21 23:24:26 +02:00
std : : vector < std : : string > stringList ;
2018-08-29 21:40:59 +02:00
2019-10-21 23:24:26 +02:00
uint32_t offset1 = 0x20 ;
2018-08-29 21:40:59 +02:00
2019-10-21 23:24:26 +02:00
LGB_FILE bgLgb ( & section [ 0 ] , " bg " ) ;
LGB_FILE planmapLgb ( & section2 [ 0 ] , " planmap " ) ;
2018-08-29 21:40:59 +02:00
2019-10-21 23:24:26 +02:00
std : : vector < LGB_FILE > lgbList { bgLgb , planmapLgb } ;
uint32_t max_index = 0 ;
2018-08-29 21:40:59 +02:00
2019-10-21 23:24:26 +02:00
std : : cout < < " [Info] " < < " Dumping MapRange and EObj " < < " \n " ;
2018-08-29 21:40:59 +02:00
uint32_t totalGroups = 0 ;
uint32_t totalGroupEntries = 0 ;
for ( const auto & lgb : lgbList )
2018-03-22 23:01:55 +01:00
{
2018-08-29 21:40:59 +02:00
for ( const auto & group : lgb . groups )
{
totalGroups + + ;
for ( const auto & pEntry : group . entries )
{
2019-10-21 23:24:26 +02:00
if ( pEntry - > getType ( ) = = LgbEntryType : : MapRange )
2018-03-22 23:01:55 +01:00
{
2018-08-29 21:40:59 +02:00
totalGroupEntries + + ;
2019-10-21 23:24:26 +02:00
writeMapRangeEntry ( discoverySql , pEntry . get ( ) ) ;
}
else if ( pEntry - > getType ( ) = = LgbEntryType : : ExitRange )
{
auto pExitRange = reinterpret_cast < LGB_EXIT_RANGE_ENTRY * > ( pEntry . get ( ) ) ;
2018-03-22 23:01:55 +01:00
}
2018-08-29 21:40:59 +02:00
}
}
2018-03-22 23:01:55 +01:00
}
2018-08-29 21:40:59 +02:00
std : : cout < < " [Info] " < < " Total Groups " < < totalGroups < < " Total entries " < < totalGroupEntries < < " \n " ;
2019-10-21 23:24:26 +02:00
std : : cout < < " [Success] " < < " Exported " < < zoneName < < " in " < <
std : : chrono : : duration_cast < std : : chrono : : seconds > (
std : : chrono : : system_clock : : now ( ) - entryStartTime ) . count ( ) < < " seconds \n " ;
2018-08-29 21:40:59 +02:00
}
2019-10-21 23:24:26 +02:00
catch ( std : : exception & e )
{
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 ;
}
std : : cout < < " \n " ;
if ( discoverySql . good ( ) )
discoverySql . flush ( ) ;
2018-08-29 21:40:59 +02:00
}
2019-10-21 23:24:26 +02:00
std : : cout < < " \n [Success] Finished all tasks in " < <
2018-08-29 21:40:59 +02:00
std : : chrono : : duration_cast < std : : chrono : : seconds > ( std : : chrono : : system_clock : : now ( ) - startTime ) . count ( )
< < " seconds \n " ;
2019-10-21 23:24:26 +02:00
std : : cout < < " Press any key to exit... " ;
2018-08-29 21:40:59 +02:00
getchar ( ) ;
if ( eData )
delete eData ;
2019-04-11 19:06:27 +10:00
if ( gameData )
delete gameData ;
2018-08-29 21:40:59 +02:00
return 0 ;
2018-03-22 23:01:55 +01:00
}