diff --git a/src/tools/pcb_reader/lgb.h b/src/tools/pcb_reader/lgb.h new file mode 100644 index 00000000..091ae3b0 --- /dev/null +++ b/src/tools/pcb_reader/lgb.h @@ -0,0 +1,267 @@ +#ifndef _LGB_H +#define _LGB_H + +#include +#include +#include +#include +#include +#include + +// 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; + +struct vec3 +{ + float x, y, z; +}; + +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, +}; + +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_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(){}; +}; + +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; + + LGB_GIMMICK_ENTRY(char* buf, uint32_t offset) + { + header = *reinterpret_cast(buf + offset); + name = std::string(buf + offset + header.nameOffset); + }; +}; + +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; + auto entriesOffset = offset + header.entriesOffset; + for( auto i = 0; i < header.entryCount; ++i) + { + auto entryOffset = entriesOffset + *reinterpret_cast(buf + (entriesOffset + i * 4)); + + try + { + auto type = *reinterpret_cast(buf + entryOffset); + LGB_MODEL_ENTRY* entry; + if (type == LgbEntryType::BgParts) + { + entries[i] = std::make_shared(buf, entryOffset); + } + else if (type == LgbEntryType::Gimmick) + { + entries[i] = std::make_shared(buf, entryOffset); + } + } + catch (std::exception& e) + { + std::cout << 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::exception("Invalid LGB file!"); + + //groups.resize(header.groupCount); + + auto baseOffset = sizeof(header); + for(auto i = 0; i < header.groupCount; ++i) + { + auto groupOffset = baseOffset + *reinterpret_cast(buf + (baseOffset + i * 4)); + auto group = LGB_GROUP(buf, this, groupOffset); + groups.push_back(group); + } + }; +}; + +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") + { + auto strPath = path.path().string(); + auto f = fopen(strPath.c_str(), "rb"); + fseek(f, 0, SEEK_END); + 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 \ No newline at end of file diff --git a/src/tools/pcb_reader/main.cpp b/src/tools/pcb_reader/main.cpp index 6da22cd5..3cb02fed 100644 --- a/src/tools/pcb_reader/main.cpp +++ b/src/tools/pcb_reader/main.cpp @@ -1,17 +1,41 @@ #include -#include -#include +#include +#include +#include #include "pcb.h" +#include "lgb.h" #include +namespace fs = std::experimental::filesystem; -int parseBlockEntry( char* data, std::vector& entries, int gOff ) +std::vector loadDir(const std::string& dir) +{ + std::vector files; + + auto fsDir = fs::current_path().append(fs::path(dir)); + std::cout << fsDir.string() << "\n"; + if (fs::exists(fsDir)) + { + for (const auto& entry : fs::directory_iterator(fsDir)) + { + auto filename = entry.path().filename(); + if (filename.string().find("~") == -1) + files.push_back(entry.path().string()); + if (filename.string().find("list") != std::string::npos) + std::cout << filename.string() << "\n"; + } + } + return files; +} + +int parseBlockEntry( char* data, std::vector& entries, int gOff, uint32_t& groupCount, LGB_BGPARTS_ENTRY& bgParts ) { int offset = 0; bool isgroup = true; while( isgroup ) { + groupCount++; PCB_BLOCK_ENTRY block_entry; memcpy( &block_entry.header, data + offset, sizeof( block_entry.header ) ); isgroup = block_entry.header.type == 0x30 ? true : false; @@ -20,14 +44,13 @@ int parseBlockEntry( char* data, std::vector& entries, int gOff if( isgroup ) { - parseBlockEntry( data + offset + 0x30, entries, gOff + offset ); + parseBlockEntry( data + offset + 0x30, entries, gOff + offset, groupCount, bgParts ); 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 + @@ -58,18 +81,18 @@ int parseBlockEntry( char* data, std::vector& entries, int gOff } entries.push_back( block_entry ); - /* printf( "Vertices: \n" ); + //printf( "Vertices: \n" ); for( auto& entry1 : block_entry.data.vertices ) { - printf( "\t %f, %f, %f \n", - entry1.x, entry1.y, entry1.z ); + // 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" ); + //printf( "Vertices I16: \n" ); for( auto& entry1 : block_entry.data.vertices_i16 ) { uint16_t var1 = entry1.x; @@ -78,12 +101,12 @@ int parseBlockEntry( char* data, std::vector& entries, int gOff 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" ); + //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" ); - }*/ + } } } @@ -96,56 +119,69 @@ int main() uint32_t offset = 0; //r1f1_b1_dor00.pcb + PCB_LIST_FILE listFile; + int groupsCount = 0; + //auto list = loadDir("bg/ffxiv/roc_r1/collision"); + auto lgbFiles = getLgbFiles("bg/ffxiv/roc_r1/"); - //std::string filename( "f1h0_s_rof0003.pcb" ); - std::string filename( "tr0924.pcb" ); - FILE *fp = nullptr; - fp = fopen( filename.c_str(), "rb" ); - if( fp == nullptr ) + std::cout << "Loaded " << lgbFiles.size() << " LGB files" << std::endl; + + uint64_t vertexCount = 0; + std::map groupCounts; + for (const auto& pair : lgbFiles) { - return 0; - } - - 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 ) - { - PCB_BLOCK_ENTRY block_entry; - memcpy( &block_entry.header, data + offset, sizeof( block_entry.header ) ); - isgroup = block_entry.header.type == 0x30 ? true : false; - - //printf( "BLOCKHEADER_%X: type: %i, group_size: %i\n", offset, block_entry.header.type, block_entry.header.group_size ); - - if( isgroup ) + for (const auto& group : pair.second.groups) { - 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; - } - else - { - parseBlockEntry( data + offset, entries, offset ); - } - + uint32_t bgPartsCount = 0; + std::cout << group.name << "\n"; + for (auto entry : group.entries) + { + if (dynamic_cast(entry.get())) + bgPartsCount++; + } + for (auto entry : group.entries) + { + auto bgParts = dynamic_cast(entry.get()); + if (!bgParts) + continue; + const auto& filename = bgParts->collisionFileName; + std::cout << filename << std::endl; + //std::string filename( "f1h0_s_rof0003.pcb" ); + //std::string filename("tr0924.pcb"); + FILE *fp = nullptr; + fp = fopen(filename.c_str(), "rb"); + if (fp == nullptr) + { + return 0; + } + + 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)); + auto offset = sizeof(pcb_file.header); + try + { + parseBlockEntry(data + offset, pcb_file.entries, offset, groupCounts[filename], *bgParts); + } + catch(std::exception& e) + { + std::cout << filename << " " << e.what() << "\n"; + } + } + } } + return 0; +} +/* for( uint16_t i = 0; i <= pcb_file.header.num_entries; i++ ) { PCB_BLOCK_ENTRY block_entry; @@ -187,7 +223,7 @@ int main() rest = 0x10 - rest; } offset += rest ; - + pcb_file.entries.push_back( block_entry ); } @@ -197,14 +233,14 @@ int main() 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 ", + + 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 ); @@ -219,7 +255,7 @@ int main() 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 ) ); @@ -240,7 +276,7 @@ int main() } - + fprintf( fp_out, "Indices: \n" ); @@ -253,6 +289,4 @@ int main() } fclose( fp_out ); - - return 0; -} +*/ \ No newline at end of file diff --git a/src/tools/pcb_reader/pcb.h b/src/tools/pcb_reader/pcb.h index a9c65e2e..0139c431 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; @@ -67,3 +70,23 @@ 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