#ifndef _LGB_H #define _LGB_H #include "matrix4.h" #include "vec3.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; 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); } 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::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