From bc89049adaddd9f4763bf60d071e8c74008d5fca Mon Sep 17 00:00:00 2001 From: Tahir Akhlaq Date: Thu, 19 Oct 2017 16:29:17 +0100 Subject: [PATCH] fixed collision dumping - added saintcoinach's sgb parsing --- src/tools/pcb_reader/lgb.h | 11 +- src/tools/pcb_reader/main.cpp | 115 ++++++++++++++++----- src/tools/pcb_reader/sgb.h | 189 ++++++++++++++++++++++++++++++++++ 3 files changed, 285 insertions(+), 30 deletions(-) create mode 100644 src/tools/pcb_reader/sgb.h diff --git a/src/tools/pcb_reader/lgb.h b/src/tools/pcb_reader/lgb.h index 3ad58b89..eb337ef6 100644 --- a/src/tools/pcb_reader/lgb.h +++ b/src/tools/pcb_reader/lgb.h @@ -11,6 +11,7 @@ #include "matrix4.h" #include "vec3.h" +#include "sgb.h" // all credit to // https://github.com/ufx/SaintCoinach/blob/master/SaintCoinach/Graphics/Lgb/ @@ -134,11 +135,13 @@ 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 ); }; }; @@ -184,12 +187,12 @@ struct LGB_GROUP if( type == LgbEntryType::BgParts ) { entries.push_back( std::make_shared( buf, entryOffset ) ); - } - /* + } else if( type == LgbEntryType::Gimmick ) { - //entries[i] = std::make_shared( buf, entryOffset ); + entries.push_back( std::make_shared( buf, entryOffset ) ); } + /* else { //entries[i] = nullptr; diff --git a/src/tools/pcb_reader/main.cpp b/src/tools/pcb_reader/main.cpp index e6dbffd5..2774f30f 100644 --- a/src/tools/pcb_reader/main.cpp +++ b/src/tools/pcb_reader/main.cpp @@ -4,6 +4,7 @@ #include "pcb.h" #include "lgb.h" +#include "sgb.h" #include #include @@ -198,8 +199,9 @@ int main( int argc, char* argv[] ) if( fp_out ) { std::map pcbFiles; + std::map sgbFiles; std::map objCount; - auto loadPcbFile = [&]( const std::string& fileName ) + auto loadPcbFile = [&]( const std::string& fileName ) -> bool { try { @@ -214,7 +216,7 @@ int main( int argc, char* argv[] ) 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 ) { @@ -235,16 +237,38 @@ int main( int argc, char* argv[] ) } } 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 ) { - std::string name2 = ( name + "_" + std::to_string( objCount[name]++ ) ); - fprintf( fp_out, "o %s\n", name2.c_str() ); + 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 ) { @@ -321,37 +345,76 @@ int main( int argc, char* argv[] ) totalGroups++; for( const auto pEntry : group.entries ) { - auto pBgParts = static_cast( pEntry.get() ); - auto& fileName = pBgParts->collisionFileName; + LGB_GIMMICK_ENTRY* pGimmick = nullptr; + auto pBgParts = dynamic_cast( pEntry.get() ); + if( pBgParts && pBgParts->header.type != LgbEntryType::BgParts ) + pBgParts = nullptr; - if( fileName.empty() ) + auto& fileName = pBgParts ? pBgParts->collisionFileName : ""; + + if( pBgParts && fileName.empty() ) continue; + // write files + auto writeOutput = [&]() { - //boost::replace_all( fileName, "bgparts", "collision" ); - //boost::replace_all( fileName, ".mdl", ".pcb" ); - } - - { - const auto& it = pcbFiles.find( fileName ); - if( it == pcbFiles.end() ) { - loadPcbFile( fileName ); - //std::cout << "\t\tLoaded PCB File " << pBgParts->collisionFileName << "\n"; + 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() ) + 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 ) { - totalGroupEntries++; + 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; - const auto* scale = &pBgParts->header.scale; - const auto* rotation = &pBgParts->header.rotation; - const auto* translation = &pBgParts->header.translation; - - const auto& pcb_file = it->second; - pushVerts( pcb_file, fileName, scale, rotation, translation ); + 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"; 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