diff --git a/.travis.yml b/.travis.yml index 5a08c891..6c5c62cd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,17 +6,19 @@ before_install: - sudo apt-get update - sudo apt-get install -y software-properties-common - sudo apt-get update - - sudo apt-get install gcc-4.9 g++-4.9 gcc-4.9-multilib g++-4.9-multilib cmake3 -y + - sudo apt-get install gcc-7 g++-7 gcc-7-multilib g++-7-multilib cmake3 -y - sudo apt-get install libboost-dev libboost-all-dev libmysqlclient-dev -y - - sudo apt-get install libmysqlcppconn-dev -y + - sudo apt-get install libmysqlcppconn-dev -y + compiler: - - gcc + - g++ # Build steps script: + - g++ --version - mkdir build - cd build - - cmake .. -DSAPPHIRE_BOOST_VER="1.54.0" -DCMAKE_CXX_COMPILER=g++-4.9 -DCMAKE_C_COMPILER=gcc-4.9 && make -j 3 + - cmake .. -DSAPPHIRE_BOOST_VER="1.54.0" -DCMAKE_CXX_COMPILER=g++-7 && make -j 3 - cd .. - bash sql_import.sh diff --git a/CMakeLists.txt b/CMakeLists.txt index 2e9a4b09..c153a1ac 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -59,3 +59,4 @@ add_subdirectory("src/servers") add_subdirectory("src/libraries/sapphire/datReader") add_subdirectory("src/tools/exd_common_gen") add_subdirectory("src/tools/quest_parser") +add_subdirectory("src/tools/pcb_reader") diff --git a/src/tools/pcb_reader/CMakeLists.txt b/src/tools/pcb_reader/CMakeLists.txt index 9bd07e39..a73b4816 100644 --- a/src/tools/pcb_reader/CMakeLists.txt +++ b/src/tools/pcb_reader/CMakeLists.txt @@ -1,23 +1,39 @@ -set( CMAKE_CXX_FLAGS "-std=c++11 -m32") +cmake_minimum_required(VERSION 2.6) +cmake_policy(SET CMP0015 NEW) +project(Tool_pcb_reader2) -include_directories("../../") +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/") + +include_directories("../../lib/ChaiScript-6.0.0/include/") + +include_directories("../../sapphire/datReader/") +include_directories("../../sapphire/") include_directories("../") file(GLOB SERVER_PUBLIC_INCLUDE_FILES "${CMAKE_CURRENT_SOURCE_DIR}/*") -file(GLOB SERVER_SOURCE_FILES "${CMAKE_CURRENT_SOURCE_DIR}*.cpp") +file(GLOB SERVER_SOURCE_FILES "${CMAKE_CURRENT_SOURCE_DIR}*.c*") -SET(Boost_USE_STATIC_LIBS ON) -set(Boost_INCLUDE_DIR /opt/build_libs/boost_1_60_0) -set(Boost_LIBRARY_DIR /opt/build_libs/boost_1_60_0/stage/lib) -set(SERVER_COMMON_DIR ../../servers/Server_Common) -find_package(Boost COMPONENTS log log_setup thread date_time filesystem system REQUIRED) -include_directories(${Boost_INCLUDE_DIR}) -link_directories(${Boost_LIBRARY_DIR}) -link_directories(${SERVER_COMMON_DIR}) -#set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ".") -add_executable(pcb_parser2 ${SERVER_PUBLIC_INCLUDE_FILES} ${SERVER_SOURCE_FILES}) +#set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "../bin/") +add_executable(pcb_reader2 ${SERVER_PUBLIC_INCLUDE_FILES} ${SERVER_SOURCE_FILES}) -target_link_libraries (pcb_parser2 server_common.a pthread mysqlclient dl z) -target_link_libraries(pcb_parser2 ${Boost_LIBRARIES} ${Boost_LIBRARIES}) +set_target_properties(pcb_reader2 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 (pcb_reader2 Common xivdat pthread mysqlclient dl z) +else() + target_link_libraries (pcb_reader2 Common xivdat libmysql zlib1) +endif() + +target_link_libraries(pcb_reader2 ${Boost_LIBRARIES} ${Boost_LIBRARIES}) diff --git a/src/tools/pcb_reader/lgb.h b/src/tools/pcb_reader/lgb.h new file mode 100644 index 00000000..eb337ef6 --- /dev/null +++ b/src/tools/pcb_reader/lgb.h @@ -0,0 +1,280 @@ +#ifndef _LGB_H +#define _LGB_H + +#include +#include +#include +#include +#include +#include +#include + +#include "matrix4.h" +#include "vec3.h" +#include "sgb.h" + +// all credit to +// https://github.com/ufx/SaintCoinach/blob/master/SaintCoinach/Graphics/Lgb/ +// this is simply their work ported to c++ since we dont c# +struct LGB_FILE; +struct LGB_FILE_HEADER; +struct LGB_GROUP; +struct LGB_GROUP_HEADER; + +enum class LgbEntryType : uint32_t +{ + BgParts = 1, + Light = 3, + Vfx = 4, + PositionMarker = 5, + Gimmick = 6, + SharedGroup6 = 6,// secondary variable is set to 2 + Sound = 7, + EventNpc = 8, + BattleNpc = 9, + Aetheryte = 12, + EnvSpace = 13, + Gathering = 14, + SharedGroup15 = 15,// secondary variable is set to 13 + Treasure = 16, + Weapon = 39, + PopRange = 40, + ExitRange = 41, + MapRange = 43, + NaviMeshRange = 44, + EventObject = 45, + EnvLocation = 47, + EventRange = 49, + QuestMarker = 51, + CollisionBox = 57, + DoorRange = 58, + LineVfx = 59, + ClientPath = 65, + ServerPath = 66, + GimmickRange = 67, + TargetMarker = 68, + ChairMarker = 69, + ClickableRange = 70, + PrefetchRange = 71, + FateRange = 72, + SphereCastRange = 75, +}; + +class LGB_MODEL_ENTRY +{ +public: + char* m_buf; + uint32_t m_offset; + + LGB_MODEL_ENTRY() + { + m_buf = nullptr; + m_offset = 0; + }; + LGB_MODEL_ENTRY( char* buf, uint32_t offset ) + { + m_buf = buf; + m_offset = offset; + }; + virtual ~LGB_MODEL_ENTRY() {}; +}; + + +struct LGB_BGPARTS_HEADER +{ + LgbEntryType type; + uint32_t unknown2; + uint32_t nameOffset; + vec3 translation; + vec3 rotation; + vec3 scale; + uint32_t modelFileOffset; + uint32_t collisionFileOffset; + uint32_t unknown4; + uint32_t unknown5; + uint32_t unknown6; + uint32_t unknown7; + uint32_t unknown8; + uint32_t unknown9; +}; + +class LGB_BGPARTS_ENTRY : public LGB_MODEL_ENTRY +{ +public: + LGB_BGPARTS_HEADER header; + std::string name; + std::string modelFileName; + std::string collisionFileName; + LGB_BGPARTS_ENTRY() {}; + LGB_BGPARTS_ENTRY( char* buf, uint32_t offset ) + { + header = *reinterpret_cast( buf + offset ); + name = std::string( buf + offset + header.nameOffset ); + modelFileName = std::string( buf + offset + header.modelFileOffset ); + collisionFileName = std::string( buf + offset + header.collisionFileOffset ); + //std::cout << "BGPARTS_ENTRY " << name << "\n"; + //std::cout << " " << modelFileName << "\n"; + //std::cout << " " << collisionFileName << "\n"; + }; +}; + +struct LGB_GIMMICK_HEADER +{ + LgbEntryType type; + uint32_t unknown; + uint32_t nameOffset; + vec3 translation; + vec3 rotation; + vec3 scale; + uint32_t gimmickFileOffset; + char unknownBytes[100]; +}; + +class LGB_GIMMICK_ENTRY : public LGB_MODEL_ENTRY +{ +public: + LGB_GIMMICK_HEADER header; + std::string name; + std::string gimmickFileName; + + LGB_GIMMICK_ENTRY( char* buf, uint32_t offset ) + { + header = *reinterpret_cast( buf + offset ); + name = std::string( buf + offset + header.nameOffset ); + gimmickFileName = std::string( buf + offset + header.gimmickFileOffset ); + }; +}; + +struct LGB_GROUP_HEADER +{ + uint32_t unknown; + int32_t groupNameOffset; + int32_t entriesOffset; + int32_t entryCount; + uint32_t unknown2; + uint32_t unknown3; + uint32_t unknown4; + uint32_t unknown5; + uint32_t unknown6; + uint32_t unknown7; + uint32_t unknown8; + uint32_t unknown9; + uint32_t unknown10; +}; + +struct LGB_GROUP +{ + LGB_FILE* parent; + LGB_GROUP_HEADER header; + std::string name; + std::vector> entries; + + LGB_GROUP( char* buf, LGB_FILE* parentStruct, uint32_t offset ) + { + parent = parentStruct; + header = *reinterpret_cast( buf + offset ); + name = std::string( buf + offset + header.groupNameOffset ); + //entries.resize( header.entryCount ); + //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 ) ); + + try + { + const auto type = *reinterpret_cast( buf + entryOffset ); + if( type == LgbEntryType::BgParts ) + { + entries.push_back( std::make_shared( buf, entryOffset ) ); + } + else if( type == LgbEntryType::Gimmick ) + { + entries.push_back( std::make_shared( buf, entryOffset ) ); + } + /* + else + { + //entries[i] = nullptr; + } + */ + } + catch( std::exception& e ) + { + std::cout << name << " " << e.what() << std::endl; + } + } + }; +}; + +struct LGB_FILE_HEADER +{ + char magic[4]; // LGB 1 + uint32_t fileSize; + uint32_t unknown; + char magic2[4]; // LGP1 + uint32_t unknown2; + uint32_t unknown3; + uint32_t unknown4; + uint32_t unknown5; + int32_t groupCount; +}; + +struct LGB_FILE +{ + LGB_FILE_HEADER header; + std::vector groups; + + LGB_FILE( char* buf ) + { + header = *reinterpret_cast( buf ); + if( strncmp( &header.magic[0], "LGB1", 4 ) != 0 || strncmp( &header.magic2[0], "LGP1", 4 ) != 0 ) + throw std::runtime_error( "Invalid LGB file!" ); + + //groups.resize(header.groupCount); + + 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 group = LGB_GROUP( buf, this, groupOffset ); + groups.push_back( group ); + } + }; +}; + +/* +#if __cplusplus >= 201703L +#include +std::map getLgbFiles( const std::string& dir ) +{ + namespace fs = std::experimental::filesystem; + std::map fileMap; + for( const auto& path : fs::recursive_directory_iterator( dir ) ) + { + if( path.path().extension() == ".lgb" ) + { + const auto& strPath = path.path().string(); + auto f = fopen( strPath.c_str(), "rb" ); + fseek( f, 0, SEEK_END ); + const auto size = ftell( f ); + std::vector bytes( size ); + rewind( f ); + fread( bytes.data(), 1, size, f ); + fclose( f ); + try + { + LGB_FILE lgbFile( bytes.data() ); + fileMap.insert( std::make_pair( strPath, lgbFile ) ); + } + catch( std::exception& e ) + { + std::cout << "Unable to load " << strPath << std::endl; + } + } + } + return fileMap; +} +#endif +*/ +#endif diff --git a/src/tools/pcb_reader/main.cpp b/src/tools/pcb_reader/main.cpp index 6da22cd5..412695ce 100644 --- a/src/tools/pcb_reader/main.cpp +++ b/src/tools/pcb_reader/main.cpp @@ -1,10 +1,28 @@ #include -#include -#include +#include +#include #include "pcb.h" +#include "lgb.h" +#include "sgb.h" + +#include +#include +#include +#include +#include +#include #include +#include +#include + +using namespace std::chrono_literals; + +struct face +{ + int32_t f1, f2, f3; +}; int parseBlockEntry( char* data, std::vector& entries, int gOff ) { @@ -14,7 +32,7 @@ int parseBlockEntry( char* data, std::vector& entries, int gOff { PCB_BLOCK_ENTRY block_entry; memcpy( &block_entry.header, data + offset, sizeof( block_entry.header ) ); - isgroup = block_entry.header.type == 0x30 ? true : false; + isgroup = block_entry.header.type == 0x30; //printf( " BLOCKHEADER_%X: type: %i, group_size: %i\n", gOff + offset, block_entry.header.type, block_entry.header.group_size ); @@ -22,12 +40,11 @@ int parseBlockEntry( char* data, std::vector& entries, int gOff { parseBlockEntry( data + offset + 0x30, entries, gOff + offset ); offset += block_entry.header.group_size; - } else { - 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 ); + /* 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 );*/ int doffset = sizeof( block_entry.header ) + offset; uint16_t block_size = sizeof( block_entry.header ) + block_entry.header.num_vertices * 3 * 4 + @@ -52,207 +69,363 @@ int parseBlockEntry( char* data, std::vector& entries, int gOff if( block_entry.header.num_indices != 0 ) { block_entry.data.indices.resize( block_entry.header.num_indices ); - int32_t size_indexbuffer = block_entry.header.num_indices * 6; + int32_t size_indexbuffer = block_entry.header.num_indices * 12; memcpy( &block_entry.data.indices[0], data + doffset, size_indexbuffer ); doffset += size_indexbuffer; } entries.push_back( block_entry ); - - /* printf( "Vertices: \n" ); - for( auto& entry1 : block_entry.data.vertices ) - { - printf( "\t %f, %f, %f \n", - entry1.x, entry1.y, entry1.z ); - } - - float x_base = abs( float( block_entry.header.x1 - block_entry.header.x ) ); - float y_base = abs( float( block_entry.header.y1 - block_entry.header.y ) ); - float z_base = abs( float( block_entry.header.z1 - block_entry.header.z ) ); - - printf( "Vertices I16: \n" ); - for( auto& entry1 : block_entry.data.vertices_i16 ) - { - uint16_t var1 = entry1.x; - uint16_t var2 = entry1.y; - uint16_t var3 = entry1.z; - float x = ( var1 ); - float y = ( var2 ); - float z = ( var3 ); - printf( "\t%f, ", ( x / 0xFFFF ) * x_base + block_entry.header.x ); - printf( "%f, ", ( y / 0xFFFF ) * y_base + block_entry.header.y ); - printf( "%f ", ( z / 0xFFFF ) * z_base + block_entry.header.z ); - printf( "\n" ); - - }*/ } } return 0; } -int main() +std::string zoneNameToPath( const std::string& name ) { - char *data; - - uint32_t offset = 0; - //r1f1_b1_dor00.pcb - - //std::string filename( "f1h0_s_rof0003.pcb" ); - std::string filename( "tr0924.pcb" ); - FILE *fp = nullptr; - fp = fopen( filename.c_str(), "rb" ); - if( fp == nullptr ) + char teri = name[0]; + char region = name[1]; + char type = name[2]; + char zone = name[3]; + static std::map teriMap { - return 0; - } + { 'r', "roc" }, + { 'w', "wil" }, + { 'l', "lak" }, + { 'o', "ocn" }, + { 'f', "fst" }, + { 'a', "air" }, + { 's', "sea" }, + { 'z', "zon" } + }; - fseek( fp, 0, SEEK_END ); - int32_t size = ftell( fp ); - data = new char[size]; - rewind( fp ); - fread( data, sizeof( char ), size, fp ); - fclose( fp ); - - PCB_FILE pcb_file; - memcpy( &pcb_file.header, data, sizeof( pcb_file.header ) ); - offset += sizeof( pcb_file.header ); - - - - std::vector entries; - - bool isgroup = true; - while( isgroup ) + static std::map typeMap { - PCB_BLOCK_ENTRY block_entry; - memcpy( &block_entry.header, data + offset, sizeof( block_entry.header ) ); - isgroup = block_entry.header.type == 0x30 ? true : false; + { 'f', "fld" }, + { 't', "twn" }, + { 'd', "dun" }, + { 'b', "bah" }, + { 'i', "ind" }, + { 'e', "evt" }, + }; + std::string ret; + const auto& teriRet = teriMap[teri]; + const auto& typeRet = typeMap[type]; + ret += teriRet + "_"; + ret += teri; + ret += region; + ret += "/" + typeRet + "/" + name; + return ret; +} - //printf( "BLOCKHEADER_%X: type: %i, group_size: %i\n", offset, block_entry.header.type, block_entry.header.group_size ); - - if( isgroup ) +int main( int argc, char* argv[] ) +{ + auto startTime = std::chrono::system_clock::now(); + + std::string gamePath = "C:\\SquareEnix\\FINAL FANTASY XIV - A Realm Reborn\\game\\sqpack\\ffxiv"; + std::string zoneName = "r1f1"; + + if( argc > 1 ) + { + gamePath = argv[1]; + if( argc > 2 ) { - std::vector data_block( block_entry.header.group_size ); - memcpy( &data_block[0], data + offset, block_entry.header.group_size ); - parseBlockEntry( (char*)&data_block[0] + 0x30, entries, offset ); - offset += block_entry.header.group_size; + zoneName = argv[2]; + } + } + const auto& zonePath = zoneNameToPath( zoneName ); + + try + { + xiv::dat::GameData data1( gamePath ); + xiv::exd::ExdData eData( data1 ); + + const xiv::dat::Cat& test = data1.get_category( "bg" ); + + auto test_file = data1.get_file( "bg/ffxiv/" + zonePath + "/level/bg.lgb" ); + auto section = test_file->access_data_sections().at( 0 ); + int32_t list_offset = *(uint32_t*)§ion[0x18]; + int32_t size = *(uint32_t*)§ion[4]; + + std::vector stringList; + + auto test_file1 = data1.get_file( "bg/ffxiv/" + zonePath + "/collision/list.pcb" ); + auto section1 = test_file1->access_data_sections().at( 0 ); + std::string path = "bg/ffxiv/" + zonePath + "/collision/"; + int offset1 = 0x20; + for( ; ; ) + { + + uint16_t trId = *(uint16_t*)§ion1[offset1]; + + char someString[200]; + sprintf( someString, "%str%04d.pcb", path.c_str(), trId ); + stringList.push_back( std::string( someString ) ); + //std::cout << someString << "\n"; + offset1 += 0x20; + + if( offset1 >= section1.size() ) + { + break; + } + } + + LGB_FILE bgLgb( §ion[0] ); + + int max_index = 0; + + std::vector vertices; + std::vector indices; + + char *data; + int counter = 0; + + // dont bother if we cant write to a file + auto fp_out = fopen( ( zoneName + ".obj" ).c_str(), "w" ); + if( fp_out ) + { + fprintf( fp_out, "\n" ); + fclose( fp_out ); } else { - parseBlockEntry( data + offset, entries, offset ); + std::string errorMessage( "Cannot create " + zoneName + ".obj\n" + + " Check no programs have a handle to file and run as admin.\n" ); + std::cout << errorMessage; + throw std::runtime_error( errorMessage.c_str() ); + return 0; } - + fp_out = fopen( ( zoneName + ".obj" ).c_str(), "ab+" ); + if( fp_out ) + { + std::map pcbFiles; + std::map sgbFiles; + std::map objCount; + auto loadPcbFile = [&]( const std::string& fileName ) -> bool + { + try + { + //std::cout << fileName << " "; + auto file = data1.get_file( fileName ); + auto sections = file->get_data_sections(); + auto dataSection = §ions.at( 0 )[0]; + //std::cout << sections.size() << "\n"; + + uint32_t offset = 0; + PCB_FILE pcb_file; + memcpy( &pcb_file.header, &dataSection[0], sizeof( pcb_file.header ) ); + offset += sizeof( pcb_file.header ); + pcb_file.entries.resize( pcb_file.header.num_entries ); + bool isgroup = true; + while( isgroup ) + { + 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 ); + } + } + pcbFiles.insert( std::make_pair( fileName, pcb_file ) ); + return true; + } + catch( std::exception& e ) + { + std::cout << "Unable to load collision mesh " << fileName << "\n\tError:\n\t" << e.what() << "\n"; + return false; + } + }; + + auto loadSgbFile = [&]( const std::string& fileName ) -> bool + { + try + { + auto file = data1.get_file( fileName ); + auto sections = file->get_data_sections(); + auto dataSection = §ions.at( 0 )[0]; + SGB_FILE sgbFile = SGB_FILE( &dataSection[0] ); + sgbFiles.insert( std::make_pair( fileName, sgbFile ) ); + return true; + } + catch( std::exception& e ) + { + std::cout << "Unable to load SGB " << fileName << "\n\tError:\n\t" << e.what() << "\n"; + return false; + } + }; + auto pushVerts = [&]( const PCB_FILE& pcb_file, const std::string& name, const vec3* scale = nullptr, const vec3* rotation = nullptr, const vec3* translation = nullptr ) + { + char name2[0x7F]; + memset( name2, 0, 0x7F ); + sprintf(&name2[0], "%s_%u", &name[0], objCount[name]++ ); + fprintf( fp_out, "o %s\n", &name2[0] ); + uint32_t groupCount = 0; + for( const auto &entry : pcb_file.entries ) + { + 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 ) ); + + auto makeTranslation = [&]( vec3& v ) + { + if( scale ) + { + v.x *= scale->x; + v.y *= scale->y; + v.z *= scale->z; + + v = v * matrix4::rotateX( rotation->x ); + v = v * matrix4::rotateY( rotation->y ); + v = v * matrix4::rotateZ( rotation->z ); + + v.x += translation->x; + v.y += translation->y; + v.z += translation->z; + } + + }; + + 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 ); + } + + for( const auto &link : entry.data.vertices_i16 ) + { + 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 ); + } + + //fprintf( fp_out, "g %s_", (name2 + "_" + std::to_string( groupCount++ )).c_str() ); + for( const auto &index : entry.data.indices ) + { + //if( index.index[0] != 0 || index.index[1] != 0 || index.index[2] != 0 ) + { + 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 ); + } + // std::cout << std::to_string( index.unknown[0] )<< " " << std::to_string( index.unknown[1] )<< " " << std::to_string( index.unknown[2]) << std::endl; + } + max_index += entry.data.vertices.size() + entry.data.vertices_i16.size(); + } + }; + + for( const auto& fileName : stringList ) + { + loadPcbFile( fileName ); + pushVerts( pcbFiles[fileName], fileName ); + } + std::cout << "Writing obj file " << "\n"; + std::cout << bgLgb.groups.size() << " groups " << "\n"; + uint32_t totalGroups = 0; + uint32_t totalGroupEntries = 0; + for( const auto& group : bgLgb.groups ) + { + //std::cout << "\t" << group.name << " Size " << group.header.entryCount << "\n"; + totalGroups++; + for( const auto pEntry : group.entries ) + { + LGB_GIMMICK_ENTRY* pGimmick = nullptr; + auto pBgParts = dynamic_cast( pEntry.get() ); + if( pBgParts && pBgParts->header.type != LgbEntryType::BgParts ) + pBgParts = nullptr; + + auto fileName = pBgParts ? pBgParts->collisionFileName : std::string(""); + + if( pBgParts && fileName.empty() ) + continue; + + // write files + auto writeOutput = [&]() + { + { + const auto& it = pcbFiles.find( fileName ); + if( it == pcbFiles.end() ) + { + if( !fileName.empty() ) + loadPcbFile( fileName ); + //std::cout << "\t\tLoaded PCB File " << pBgParts->collisionFileName << "\n"; + } + } + const auto& it = pcbFiles.find( fileName ); + if( it != pcbFiles.end() ) + { + totalGroupEntries++; + + const auto* scale = pBgParts ? &pBgParts->header.scale : &pGimmick->header.scale; + const auto* rotation = pBgParts ? &pBgParts->header.rotation : &pGimmick->header.rotation; + const auto* translation = pBgParts ? &pBgParts->header.translation : &pGimmick->header.translation; + + const auto& pcb_file = it->second; + pushVerts( pcb_file, fileName, scale, rotation, translation ); + } + }; + + // gimmick entry + if( !pBgParts ) + { + pGimmick = dynamic_cast( pEntry.get() ); + { + const auto& it = sgbFiles.find( pGimmick->gimmickFileName ); + if( it == sgbFiles.end() ) + { + loadSgbFile( pGimmick->gimmickFileName ); + } + } + const auto& it = sgbFiles.find( pGimmick->gimmickFileName ); + if( it != sgbFiles.end() ) + { + totalGroupEntries++; + const auto& sgbFile = it->second; + + for( const auto& group : sgbFile.entries ) + { + for( const auto& pEntry : group.entries ) + { + auto pModel = dynamic_cast( pEntry.get() ); + if( !pModel->collisionFileName.empty() ) + { + fileName = pModel->collisionFileName; + writeOutput(); + } + } + } + } + continue; + } + // bgparts + writeOutput(); + } + } + std::cout << "\n\nLoaded " << pcbFiles.size() << " PCB Files \n"; + std::cout << "Total Groups " << totalGroups << " Total entries " << totalGroupEntries << "\n"; + } + std::cout << "Finished exporting " << zoneName << " in " << + std::chrono::duration_cast( std::chrono::system_clock::now() - startTime ).count() << " seconds\n"; } - - for( uint16_t i = 0; i <= pcb_file.header.num_entries; i++ ) + catch( std::exception& e ) { - PCB_BLOCK_ENTRY block_entry; - memcpy( &block_entry.header, data + offset, sizeof( block_entry.header ) ); - offset += sizeof( block_entry.header ); - - 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 + offset, size_vertexbuffer * 4 ); - offset += 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 + offset, size_unknownbuffer ); - offset += block_entry.header.num_v16 * 6; - } - if( block_entry.header.num_indices != 0 ) - { - block_entry.data.indices.resize( block_entry.header.num_indices ); - int32_t size_indexbuffer = block_entry.header.num_indices * 6; - memcpy( &block_entry.data.indices[0], data + offset, size_indexbuffer ); - offset += size_indexbuffer; - } - - // blocks always align to 16 bytes + 8 bytes padding till the next block - int rest = ( offset % 16 ); - if( rest > 0 ) - { - rest = 0x10 - rest; - } - offset += rest ; - - - pcb_file.entries.push_back( block_entry ); + std::cout << e.what() << std::endl; } - - FILE* fp_out1 = fopen( std::string( filename + ".plain" ).c_str(), "w" ); - fprintf( fp_out1, ""); - fclose( fp_out1 ); - - FILE* fp_out = fopen( std::string( filename + ".plain" ).c_str(), "w+" ); - - fprintf( fp_out, "HEADER: num_entries: %i, total_indices: %i, unknown_1: %i\n\n", pcb_file.header.num_entries, pcb_file.header.total_indices, pcb_file.header.unknown_1 ); - - int block_cnt = 0; - for( auto& entry : pcb_file.entries ) - { - - fprintf( fp_out, "BLOCKHEADER_%i: type: %i, group_size: %i\n ", - block_cnt, entry.header.type, entry.header.group_size ); - fprintf( fp_out, "\tAABB: x: %f, y: %f, z: %f\n ", - entry.header.x, entry.header.y, entry.header.z ); - fprintf( fp_out, "\t\t x1: %f, y1: %f, z1: %f\n ", - entry.header.x1, entry.header.y1, entry.header.z1 ); - fprintf( fp_out, "\tnum_v16: %i, num_indices: %i, num_vertices: %i\n\n", - entry.header.num_v16, entry.header.num_indices, entry.header.num_vertices ); - - fprintf( fp_out, "Vertices: \n"); - for( auto& entry1 : entry.data.vertices ) - { - fprintf( fp_out, "\t %f, %f, %f \n", - entry1.x, entry1.y, entry1.z ); - } - - 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 ) ); - - fprintf( fp_out, "Vertices I16: \n" ); - for( auto& entry1 : entry.data.vertices_i16 ) - { - uint16_t var1 = entry1.x; - uint16_t var2 = entry1.y; - uint16_t var3 = entry1.z; - float x = ( var1 ); - float y = ( var2 ); - float z = ( var3 ); - fprintf( fp_out, "\t%f, ", (x / 0xFFFF) * x_base + entry.header.x ); - fprintf( fp_out, "%f, ", (y / 0xFFFF) * y_base + entry.header.y ); - fprintf( fp_out, "%f ", (z / 0xFFFF) * z_base + entry.header.z ); - fprintf( fp_out, "\n"); - - } - - - - - fprintf( fp_out, "Indices: \n" ); - for( auto& entry1 : entry.data.indices ) - { - fprintf( fp_out, "\t %i, %i, %i - %x,%x,%x \n", - entry1.index[0], entry1.index[1], entry1.index[2], entry1.unknown[0], entry1.unknown[1], entry1.unknown[2] ); - } - fprintf( fp_out, "\n" ); - } - - fclose( fp_out ); - return 0; } diff --git a/src/tools/pcb_reader/matrix4.h b/src/tools/pcb_reader/matrix4.h new file mode 100644 index 00000000..d02d2c84 --- /dev/null +++ b/src/tools/pcb_reader/matrix4.h @@ -0,0 +1,100 @@ +#ifndef _MATRIX4_H +#define _MATRIX4_H + +#include +#include + +// https://github.com/jpd002/Play--Framework/tree/master/include/math +struct matrix4 +{ + // 4x4 + float grid[16]; + matrix4() + { + memset( &grid[0], 0, sizeof( grid ) ); + } + + float operator()( int row, int col ) const + { + return grid[(row * 4) + col]; + } + + float& operator()( int row, int col ) + { + return grid[(row * 4) + col]; + } + static matrix4 rotateX( float angle ) + { + matrix4 ret = matrix4(); + ret(0, 0) = 1.000000000f; + ret(1, 1) = cos(angle); + ret(1, 2) = -sin(angle); + ret(2, 1) = sin(angle); + ret(2, 2) = cos(angle); + ret(3, 3) = 1.000000000f; + return ret; + } + + static matrix4 rotateY( float angle ) + { + matrix4 ret = matrix4(); + ret(0, 0) = cos(angle); + ret(0, 2) = sin(angle); + ret(1, 1) = 1.000000000f; + ret(2, 0) = -sin(angle); + ret(2, 2) = cos(angle); + ret(3, 3) = 1.000000000f; + return ret; + } + + static matrix4 rotateZ( float angle ) + { + matrix4 ret = matrix4(); + ret(0, 0) = cos(angle); + ret(0, 1) = -sin(angle); + ret(1, 0) = sin(angle); + ret(1, 1) = cos(angle); + ret(2, 2) = 1.000000000f; + ret(3, 3) = 1.000000000f; + return ret; + } + + static matrix4 scale( float x, float y, float z ) + { + matrix4 ret = matrix4(); + ret(0, 0) = x; + ret(1, 1) = y; + ret(2, 2) = z; + ret(3, 3) = 1; + + return ret; + } + + static matrix4 translate( float x, float y, float z ) + { + matrix4 ret = matrix4(); + ret(0, 0) = 1; + ret(1, 1) = 1; + ret(2, 2) = 1; + ret(3, 3) = 1; + + ret(3, 0) = x; + ret(3, 1) = y; + ret(3, 2) = z; + return ret; + } + + matrix4 operator *( const matrix4& rhs ) const + { + matrix4 ret; + for( unsigned int i = 0; i < 4; i++ ) + { + ret( i, 0 ) = (*this)(i, 0) * rhs( 0, 0 ) + (*this)(i, 1) * rhs( 1, 0 ) + (*this)(i, 2) * rhs( 2, 0 ) + (*this)(i, 3) * rhs( 3, 0 ); + ret( i, 1 ) = (*this)(i, 0) * rhs( 0, 1 ) + (*this)(i, 1) * rhs( 1, 1 ) + (*this)(i, 2) * rhs( 2, 1 ) + (*this)(i, 3) * rhs( 3, 1 ); + ret( i, 2 ) = (*this)(i, 0) * rhs( 0, 2 ) + (*this)(i, 1) * rhs( 1, 2 ) + (*this)(i, 2) * rhs( 2, 2 ) + (*this)(i, 3) * rhs( 3, 2 ); + ret( i, 3 ) = (*this)(i, 0) * rhs( 0, 3 ) + (*this)(i, 1) * rhs( 1, 3 ) + (*this)(i, 2) * rhs( 2, 3 ) + (*this)(i, 3) * rhs( 3, 3 ); + } + return ret; + } +}; +#endif diff --git a/src/tools/pcb_reader/pcb.h b/src/tools/pcb_reader/pcb.h index a9c65e2e..4b775d84 100644 --- a/src/tools/pcb_reader/pcb.h +++ b/src/tools/pcb_reader/pcb.h @@ -1,3 +1,6 @@ +#ifndef _PCB_H +#define _PCB_H + #include #include @@ -6,7 +9,7 @@ struct PCB_HEADER uint32_t unknown_1; uint32_t unknown_2; uint32_t num_entries; // count starts at 0 - uint32_t total_indices; + uint32_t total_indices; uint64_t padding; }; @@ -15,13 +18,13 @@ struct PCB_BLOCK_HEADER uint32_t type; // 0 for entry, 0x30 for group uint32_t group_size; // when group size in bytes for the group block // bounding box - float x; + float x; float y; float z; float x1; float y1; float z1; - // number of vertices packed into 16 bit + // number of vertices packed into 16 bit uint16_t num_v16; // number of indices uint16_t num_indices; @@ -40,6 +43,7 @@ struct PCB_INDEXDATA { uint8_t index[3]; uint8_t unknown[3]; + uint8_t unknown1[6]; }; struct PCB_VERTEXDATAI16 @@ -67,3 +71,22 @@ struct PCB_FILE PCB_HEADER header; std::vector< PCB_BLOCK_ENTRY > entries; }; + +struct PCB_LIST_ENTRY +{ + uint32_t id; + float x, y, z, x2, y2, z2, rot; +}; + +struct PCB_LIST_BASE_ENTRY +{ + float x, y, z, x2, y2, z2, rot; +}; + +struct PCB_LIST_FILE +{ + uint32_t count; + PCB_LIST_BASE_ENTRY entry; + std::vector entries; +}; +#endif \ No newline at end of file diff --git a/src/tools/pcb_reader/sgb.h b/src/tools/pcb_reader/sgb.h new file mode 100644 index 00000000..5ca9e3a4 --- /dev/null +++ b/src/tools/pcb_reader/sgb.h @@ -0,0 +1,189 @@ +#ifndef _SGB_H +#define _SGB_H + +#include +#include +#include +#include +#include +#include +#include + +#include "vec3.h" + +// +// ported from https://github.com/ufx/SaintCoinach/blob/master/SaintCoinach/Graphics/Sgb/SgbDataType.cs + +struct SGB_FILE; +struct SGB_HEADER; +struct SGB_MODEL_ENTRY; +struct SGB_MODEL_HEADER; +struct SGB_GROUP; +struct SGB_GROUP_HEADER; + + +enum SgbDataType : uint32_t +{ + Unknown0008 = 0x0008, + Group = 0x0100, +}; + +enum SgbGroupEntryType : uint32_t +{ + Model = 0x01, +}; + +struct SGB_GROUP_HEADER +{ + SgbDataType type; + int32_t nameOffset; + uint32_t unknown08; + uint32_t unknown0C; + + uint32_t unknown10; + uint32_t unknown14; + uint32_t unknown18; + uint32_t unknown1C; + + int32_t entryCount; + uint32_t unknown24; + uint32_t unknown28; + uint32_t unknown2C; + + uint32_t unknown30; + uint32_t unknown34; + uint32_t unknown38; + uint32_t unknown3C; + + uint32_t unknown40; + uint32_t unknown44; +}; + +struct SGB_GROUP_ENTRY +{ +public: + char* m_buf; + uint32_t m_offset; + + SGB_GROUP_ENTRY() + { + m_buf = nullptr; + m_offset = 0; + }; + SGB_GROUP_ENTRY( char* buf, uint32_t offset ) + { + m_buf = buf; + m_offset = offset; + }; + virtual ~SGB_GROUP_ENTRY() {}; +}; + +struct SGB_MODEL_HEADER +{ + SgbGroupEntryType type; + uint32_t unknown2; + int32_t nameOffset; + vec3 translation; + vec3 rotation; + vec3 scale; + int32_t modelFileOffset; + int32_t collisionFileOffset; +}; + +struct SGB_MODEL_ENTRY : public SGB_GROUP_ENTRY +{ + SGB_MODEL_HEADER header; + SgbGroupEntryType type; + std::string name; + std::string modelFileName; + std::string collisionFileName; + + SGB_MODEL_ENTRY( char* buf, uint32_t offset ) + { + header = *reinterpret_cast( buf + offset ); + name = std::string( buf + offset + header.nameOffset ); + modelFileName = std::string( buf + offset + header.modelFileOffset ); + collisionFileName = std::string( buf + offset + header.collisionFileOffset ); + } +}; + +struct SGB_GROUP +{ + SGB_GROUP_HEADER header; + std::string name; + SGB_FILE* parent; + std::vector> entries; + + SGB_GROUP( char* buf, SGB_FILE* file, uint32_t offset ) + { + parent = file; + header = *reinterpret_cast( 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 type = *reinterpret_cast( buf + entryOffset ); + if( type == SgbGroupEntryType::Model ) + { + entries.push_back( std::make_shared( buf, entryOffset ) ); + } + } + } +}; + +struct SGB_HEADER +{ + char magic[4]; // SGB1 + uint32_t fileSize; + uint32_t unknown1; + char magic2[4]; // SCN1 + + uint32_t unknown10; + int32_t sharedOffset; + uint32_t unknown18; + int32_t offset1C; + + uint32_t unknown20; + uint32_t unknown24; + uint32_t unknown28; + uint32_t unknown2C; + + uint32_t unknown30; + uint32_t unknown34; + uint32_t unknown38; + uint32_t unknown3C; + + uint32_t unknown40; + uint32_t unknown44; + uint32_t unknown48; + uint32_t unknown4C; + + uint32_t unknown50; + uint32_t unknown54; +}; + +struct SGB_FILE +{ + SGB_HEADER header; + std::vector entries; + + SGB_FILE( char* buf ) + { + constexpr int baseOffset = 0x14; + header = *reinterpret_cast( 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!" ); + + auto group = SGB_GROUP( buf, this, baseOffset + header.sharedOffset ); + //auto group2 = SGB_GROUP( buf, this, baseOffset + header.offset1C ); + entries.push_back( group ); + //entries.push_back( group2 ); + }; +}; + + +#endif // !_SGB_H diff --git a/src/tools/pcb_reader/vec3.h b/src/tools/pcb_reader/vec3.h new file mode 100644 index 00000000..a8fdfbd1 --- /dev/null +++ b/src/tools/pcb_reader/vec3.h @@ -0,0 +1,31 @@ +#ifndef _VEC3_H +#define _VEC3_H + +#include +#include "matrix4.h" + +struct vec3 +{ + float x, y, z; + vec3() + { + x = 0.0f; + y = 0.0f; + z = 0.0f; + } + vec3(float x, float y, float z) + { + this->x = x; + this->y = y; + this->z = z; + }; +}; +static vec3 operator *(const vec3& lhs, const matrix4& rhs) +{ + vec3 ret; + ret.x = rhs(0, 0) * lhs.x + rhs(0, 1) * lhs.y + rhs(0, 2) * lhs.z; + ret.y = rhs(1, 0) * lhs.x + rhs(1, 1) * lhs.y + rhs(1, 2) * lhs.z; + ret.z = rhs(2, 0) * lhs.x + rhs(2, 1) * lhs.y + rhs(2, 2) * lhs.z; + return ret; +}; +#endif \ No newline at end of file