1
Fork 0
mirror of https://github.com/SapphireServer/Sapphire.git synced 2025-04-29 07:37:45 +00:00

added list.pcb file mapping

- added LGB parser (all credit for LGB to contributor<https://github.com/ufx/SaintCoinach/blob/master/SaintCoinach/Graphics/Lgb/>, lgb.h is simply their work ported to c++)
- todo: correct object placement
This commit is contained in:
Tahir Akhlaq 2017-10-16 15:47:48 +01:00
parent 09391691b0
commit 04c53a9181
3 changed files with 395 additions and 71 deletions

267
src/tools/pcb_reader/lgb.h Normal file
View file

@ -0,0 +1,267 @@
#ifndef _LGB_H
#define _LGB_H
#include <memory>
#include <cstdint>
#include <iostream>
#include <vector>
#include <map>
#include <experimental/filesystem>
// 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<LGB_BGPARTS_HEADER*>(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<LGB_GIMMICK_HEADER*>(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<std::shared_ptr<LGB_MODEL_ENTRY>> entries;
LGB_GROUP(char* buf, LGB_FILE* parentStruct, uint32_t offset)
{
parent = parentStruct;
header = *reinterpret_cast<LGB_GROUP_HEADER*>(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<int32_t*>(buf + (entriesOffset + i * 4));
try
{
auto type = *reinterpret_cast<LgbEntryType*>(buf + entryOffset);
LGB_MODEL_ENTRY* entry;
if (type == LgbEntryType::BgParts)
{
entries[i] = std::make_shared<LGB_BGPARTS_ENTRY>(buf, entryOffset);
}
else if (type == LgbEntryType::Gimmick)
{
entries[i] = std::make_shared<LGB_GIMMICK_ENTRY>(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<LGB_GROUP> groups;
LGB_FILE(char* buf)
{
header = *reinterpret_cast<LGB_FILE_HEADER*>(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<int32_t*>(buf + (baseOffset + i * 4));
auto group = LGB_GROUP(buf, this, groupOffset);
groups.push_back(group);
}
};
};
std::map<std::string, LGB_FILE> getLgbFiles(const std::string& dir)
{
namespace fs = std::experimental::filesystem;
std::map<std::string, LGB_FILE> 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<char> 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

View file

@ -1,17 +1,41 @@
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <cstdint>
#include <string>
#include <experimental/filesystem>
#include "pcb.h"
#include "lgb.h"
#include <iostream>
namespace fs = std::experimental::filesystem;
int parseBlockEntry( char* data, std::vector<PCB_BLOCK_ENTRY>& entries, int gOff )
std::vector<std::string> loadDir(const std::string& dir)
{
std::vector<std::string> 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<PCB_BLOCK_ENTRY>& 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<PCB_BLOCK_ENTRY>& 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<PCB_BLOCK_ENTRY>& 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<PCB_BLOCK_ENTRY>& 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<std::string, uint32_t> 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<PCB_BLOCK_ENTRY> 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<uint8_t> 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<LGB_BGPARTS_ENTRY*>(entry.get()))
bgPartsCount++;
}
for (auto entry : group.entries)
{
auto bgParts = dynamic_cast<LGB_BGPARTS_ENTRY*>(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;
}
*/

View file

@ -1,3 +1,6 @@
#ifndef _PCB_H
#define _PCB_H
#include <stdint.h>
#include <vector>
@ -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<PCB_LIST_ENTRY> entries;
};
#endif