// LegoBlobReader.cpp : This file contains the 'main' function. Program execution begins and ends there. // #include #include #include #include #include #include #include typedef unsigned char byte; typedef unsigned short uint16; typedef unsigned int uint32; typedef __int64 int64; static char const* FILE_NAME = "GAMEPROGRESS.BLOB"; //-------------------------------------------------------------------------------------------------------- static void FlipBytes(void* data, size_t buffer_len) { byte* buffer = (byte*)data; size_t flip_count = buffer_len >> 1; for (size_t i = 0; i < flip_count; ++i) { std::swap(buffer[i], buffer[buffer_len - i - 1]); } } //-------------------------------------------------------------------------------------------------------- struct type_info_t { std::string name; size_t byte_size; inline bool is_valid() const { return byte_size > 0; } }; //-------------------------------------------------------------------------------------------------------- struct class_member_info_t { std::string name; type_info_t const* type; }; //-------------------------------------------------------------------------------------------------------- struct class_info_t { std::string name; std::vector members; }; //-------------------------------------------------------------------------------------------------------- // Globals //-------------------------------------------------------------------------------------------------------- std::vector Types; std::vector Classes; //-------------------------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------------------------- class BinaryReader { public: BinaryReader(char const* filename); ~BinaryReader(); bool is_valid() const { return (nullptr != m_buffer); } bool is_finished() const { return m_offset >= m_byte_size; } public: // accessors size_t get_byte_size() const { return m_byte_size; } size_t get_read_offset() const { return m_offset; } size_t get_bytes_remaining() const { return get_byte_size() - get_read_offset(); } byte* get_read_head() const { return m_buffer + m_offset; } void set_read_offset(size_t offset) { m_offset = offset; } public: // functions bool skip(size_t num_bytes); bool read_bytes(void* dst, size_t num_bytes); std::string read_string(); template T read(T const& def_value) { T val; if (read_bytes(&val, sizeof(T))) { // FlipBytes(&val, sizeof(T)); return val; } else { return def_value; } } bool read_type_info(type_info_t* out); public: // operators byte operator[](size_t offset) const { return m_buffer[offset]; } public: byte* m_buffer = nullptr; size_t m_byte_size = 0; size_t m_offset = 0; }; //-------------------------------------------------------------------------------------------------------- // BinearyReader implementation //-------------------------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------------------------- BinaryReader::BinaryReader(char const* filename) { FILE* fp = nullptr; ::fopen_s(&fp, filename, "rb"); if (nullptr == fp) { return; } ::fseek(fp, 0, SEEK_END); size_t eof = ::ftell(fp); ::fseek(fp, 0, SEEK_SET); m_buffer = (byte*) ::malloc(eof); if (nullptr != m_buffer) { m_byte_size = ::fread(m_buffer, 1, eof, fp); } ::fclose(fp); } //-------------------------------------------------------------------------------------------------------- BinaryReader::~BinaryReader() { if (nullptr != m_buffer) { delete m_buffer; m_buffer = nullptr; } } //-------------------------------------------------------------------------------------------------------- bool BinaryReader::skip(size_t num_bytes) { if (get_bytes_remaining() < num_bytes) { return false; } m_offset += num_bytes; return true; } //-------------------------------------------------------------------------------------------------------- bool BinaryReader::read_bytes(void* dst, size_t num_bytes) { byte* src = get_read_head(); if (!skip(num_bytes)) { return false; } ::memcpy(dst, src, num_bytes); return true; } //-------------------------------------------------------------------------------------------------------- std::string BinaryReader::read_string() { uint32 len = read(0); std::string str; if (len > 0) { str.resize(len + 1); read_bytes(&str[0], len); str[len] = 0; } return str; } //-------------------------------------------------------------------------------------------------------- bool BinaryReader::read_type_info(type_info_t* out) { read(0); // offset to next read_string(); // "type" out->name = read_string(); out->byte_size = read(0); return out->is_valid(); } //-------------------------------------------------------------------------------------------------------- // termination function for file reading - just prints where we left off //-------------------------------------------------------------------------------------------------------- static void DisplayProgress(BinaryReader const& reader) { size_t bytes_to_show = reader.get_bytes_remaining(); bytes_to_show = std::min(bytes_to_show, 16); size_t offset = reader.get_read_offset(); printf("\n\n--== PROGRESS ==--\nAt %u (0x%08x) into buffer... Next %u byte(s) are...\n> ", offset, offset, bytes_to_show); for (size_t i = 0; i < bytes_to_show; ++i) { byte b = reader[offset + i]; printf("%02X ", b); } printf("<\n\n"); } //-------------------------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------------------------- static void ParseTypes(BinaryReader& reader) { uint32 type_block_size = reader.read(0); // 0x02f4 std::string typelist = reader.read_string(); uint32 type_count = reader.read(0); // 0x1A?std::vector types; printf("\n======\nType List (%u total types)...\n", type_count); for (uint32 i = 0; i < type_count; ++i) { type_info_t type; reader.read_type_info(&type); // push even bad types in to keep the index Types.push_back(type); if (type.is_valid()) { printf("> Type [%02u]: %s (%u B)\n", i, type.name.c_str(), type.byte_size); } else { printf("> Type [%02u]: REDACTED\n", i); } } } //-------------------------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------------------------- static void ParseClasses(BinaryReader& reader) { uint32 class_block_size = reader.read(0); uint32 end_of_class = reader.get_read_offset() + class_block_size - sizeof(uint32); std::string classlist = reader.read_string(); uint32 class_count = reader.read(0); printf("\n======\nClass List (%u total classes)...\n", class_count); for (uint32 i = 0; i < class_count; ++i) { class_info_t class_info; uint32 class_size = reader.read(0); // to skip to end of this class std::string class_id = reader.read_string(); // "Class" class_info.name = reader.read_string(); printf("\n> [%u] class: %s\n:", i, class_info.name.c_str()); uint32 some_count = reader.read(0); std::string ver_str = reader.read_string(); // "Version" uint32 ver_num_maybe = reader.read(0); uint32 offset_maybe = reader.read(0); std::string types_str = reader.read_string(); // "Types" reader.skip(1); // 0 uint32 mem_count = reader.read(0); // 0x07 ? printf(">> member count: %u\n", mem_count); for (uint32 mi = 0; mi < mem_count; ++mi) { uint32 type_idx = reader.read(0); // 0x02 ? type_info_t const& type = Types[type_idx]; std::string mem_name = reader.read_string(); printf(" %s : %s;\n", mem_name.c_str(), type.name.c_str()); class_member_info_t mem_info; mem_info.name = mem_name; mem_info.type = &type; class_info.members.push_back(mem_info); // uint16 skip_to_next = reader.read(0); /* uint32 type_size = type.byte_size; if (type_size != ~0U) { reader.skip(type_size); // def value - based on type } */ /* uint32 const STOP_VALUE = 0x80'00'00'00; uint32 val = reader.read(STOP_VALUE); while (val != STOP_VALUE) { val = reader.read(STOP_VALUE); } */ uint32 v0 = reader.read(0); uint32 v1 = reader.read(0); uint32 v2 = reader.read(0); uint32 v3 = reader.read(0); uint32 v4 = reader.read(0); printf(" %08X %08X %08X %08X %08X (%s)\n", v0, v1, v2, v3, v4, type.name.c_str()); Classes.push_back(class_info); } } } //-------------------------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------------------------- static void ParseOLST(BinaryReader& reader, size_t end_of_section) { reader.read(0); // 1 - subobject count? reader.read(0); // skip two bytes, why? type? (0) reader.read(0); // sub section len? 0x098c (probably how much room is left) std::string subobj_name = reader.read_string(); // MOBJ - map object? reader.read(0); // 0 reader.read(0); // 9 - number of episode sections to read. //------ // cut1 std::string path0 = reader.read_string(); // cut/story/ep1/ep1_main/ep1_mainintro_sequence.scene_baked reader.read_string(); // "" reader.read_string(); // "" reader.read_string(); // "" // level1 std::string path1 = reader.read_string(); //levels/hub/prelevels/episode_01/ep1_prelevel_01/ep1pl01_catchupcrawl.scene_baked reader.read_string(); // "" reader.read(0); // 0 reader.read(0); // 0 reader.read(0); // 1 reader.read_string(); // "" reader.read_string(); // "" reader.read(0); //0 reader.read_string(); // ep1 std::string ep01 = reader.read_string(); // Episode1 reader.read(0); // 0 //------ // cut std::string cut2 = reader.read_string(); // cut/story/ep2/ep2_main/ep2_mainintro_sequence.scene_baked reader.read_string(); // "" reader.read_string(); // "" reader.read_string(); // "" // level std::string level2 = reader.read_string(); // levels/hub/prelevels/episode_02/ep2_prelevel_01/ep2pl01_catchupcrawl.scene_baked reader.read_string(); // "" reader.read(0); // 0 reader.read(0); // 0 reader.read(0); // 1 reader.read_string(); // "" reader.read_string(); // "" reader.read(0); //0 reader.read_string(); // ep std::string ep2 = reader.read_string(); // Episode2 reader.read(0); // 0 //------ // cut std::string cut3 = reader.read_string(); // cut/story/ep3/ep3_main/ep3_mainintro_sequence.scene_baked reader.read_string(); // "" reader.read_string(); // "" reader.read_string(); // "" // level std::string level3 = reader.read_string(); // levels/hub/prelevels/episode_03/ep3_prelevel_01/ep3pl01_catchupcrawl.scene_baked reader.read_string(); // "" reader.read(0); // 0 reader.read(0); // 0 reader.read(0); // 1 reader.read_string(); // "" reader.read_string(); // "" reader.read(0); //0 reader.read_string(); // ep std::string ep3 = reader.read_string(); // Episode3 reader.read(0); // 0 //------ // cut std::string hub1 = reader.read_string(); // Levels/Hub/Planets/Tatooine/Locations/Settlements/Tatooine_desertArea/tatooine_desertarea.scene_baked std::string arrive1 = reader.read_string(); // ArrivalPointFrom4_1 std::string hub2 = reader.read_string(); // Levels/Hub/Planets/Tatooine/Locations/Settlements/Tatooine_desertArea/tatooine_desertarea.scene_baked std::string arrive2 = reader.read_string(); // ArrivalPointFrom4_1 // level std::string hub3 = reader.read_string(); // levels/hub/prelevels/episode_04/ep4_prelevel_02/e04pl02_catchupcrawl.scene_baked reader.read_string(); // "" // two names? reader.read(0); // 2 reader.read_string(); // "BB8" reader.read_string(); // "Greedo" reader.read(0); // 2 reader.read_string(); // "FirstOrder_SFTIEFighter" reader.read_string(); // "Naboo_Yacht" reader.read(0); // false reader.read_string(); // "Prelevels" reader.read_string(); // "Ep4Pre2" reader.read(0); // 1 reader.read_string(); // "HYPERSPACE_LOCKED" std::string test = reader.read_string(); // "Episode4" reader.read(0); // 1 DisplayProgress(reader); // } //-------------------------------------------------------------------------------------------------------- // main file reading function //-------------------------------------------------------------------------------------------------------- static void ParseBuffer(BinaryReader& reader) { uint32 version = reader.read(0); printf("Version: %u\n", version); std::string streaminfo = reader.read_string(); printf("Label: %s\n", streaminfo.c_str()); uint32 unknown0 = reader.read(0); // 0x33 uint32 unknown1 = reader.read(0); // 0x01 uint32 unknown2 = reader.read(0); // 0x00 ParseTypes(reader); ParseClasses(reader); // print the sections printf("\n\n\n----------------------------\nReading Sections\n----------------------------\n"); size_t num_sections = reader.read(0); size_t sections_read = 0; while (!reader.is_finished()) { uint32 section_len = reader.read(0); if (section_len == 0) { break; } uint32 end_of_section = reader.get_read_offset() - sizeof(uint32) + section_len; std::string section_name = reader.read_string(); // "OLST" ++sections_read; printf("Section '%s' at 0x%08x.\n", section_name.c_str(), end_of_section - section_len); if (_strcmpi(section_name.c_str(), "OLST") == 0) { ParseOLST(reader, end_of_section); } reader.set_read_offset(end_of_section); // this is taking me past the end by 4; } printf("\nRead %u out of %u sections.", sections_read, num_sections); printf("\n\n"); DisplayProgress(reader); } //-------------------------------------------------------------------------------------------------------- // main //-------------------------------------------------------------------------------------------------------- int main() { BinaryReader reader(FILE_NAME); if (reader.is_valid()) { ParseBuffer(reader); } printf("\n\n\nPress Any Key...\n"); _getch(); } // Run program: Ctrl + F5 or Debug > Start Without Debugging menu // Debug program: F5 or Debug > Start Debugging menu // Tips for Getting Started: // 1. Use the Solution Explorer window to add/manage files // 2. Use the Team Explorer window to connect to source control // 3. Use the Output window to see build output and other messages // 4. Use the Error List window to view errors // 5. Go to Project > Add New Item to create new code files, or Project > Add Existing Item to add existing code files to the project // 6. In the future, to open this project again, go to File > Open > Project and select the .sln file