/* ArchiveTreeNode::merge * Merges [node] with this node. Entries within [node] are added * at [position] within this node. Returns false if [node] is invalid, * true otherwise *******************************************************************/ bool ArchiveTreeNode::merge(ArchiveTreeNode* node, unsigned position, int state) { // Check node was given to merge if (!node) return false; // Merge entries for (unsigned a = 0; a < node->numEntries(); a++) { if (node->getEntry(a)) { string name = Misc::lumpNameToFileName(node->getEntry(a)->getName()); node->getEntry(a)->setName(name); } ArchiveEntry* nentry = new ArchiveEntry(*(node->getEntry(a))); addEntry(nentry, position); nentry->setState(state); if (position < entries.size()) position++; } // Merge subdirectories for (unsigned a = 0; a < node->nChildren(); a++) { ArchiveTreeNode* child = (ArchiveTreeNode*)STreeNode::addChild(node->getChild(a)->getName()); child->merge((ArchiveTreeNode*)node->getChild(a)); child->getDirEntry()->setState(state); } return true; }
/* Archive::importDir * Imports all files (including subdirectories) from [directory] into * the archive *******************************************************************/ bool Archive::importDir(string directory) { // Get a list of all files in the directory wxArrayString files; wxDir::GetAllFiles(directory, &files); // Go through files for (unsigned a = 0; a < files.size(); a++) { string name = files[a]; name.Replace(directory, "", false); // Remove directory from entry name // Split filename into dir+name wxFileName fn(name); string ename = fn.GetFullName(); string edir = fn.GetPath(); // Remove beginning \ or / from dir if (edir.StartsWith("\\") || edir.StartsWith("/")) edir.Remove(0, 1); // Add the entry ArchiveTreeNode* dir = createDir(edir); ArchiveEntry* entry = addNewEntry(ename, dir->numEntries()+1, dir); // Load data entry->importFile(files[a]); // Set unmodified entry->setState(0); dir->getDirEntry()->setState(0); } return true; }
// ----------------------------------------------------------------------------- // Writes the wad archive to a MemChunk // Returns true if successful, false otherwise // ----------------------------------------------------------------------------- bool Wad2Archive::write(MemChunk& mc, bool update) { // Determine directory offset & individual lump offsets uint32_t dir_offset = 12; ArchiveEntry* entry = nullptr; for (uint32_t l = 0; l < numEntries(); l++) { entry = entryAt(l); entry->exProp("Offset") = (int)dir_offset; dir_offset += entry->size(); } // Clear/init MemChunk mc.clear(); mc.seek(0, SEEK_SET); mc.reSize(dir_offset + numEntries() * 32); // Setup wad type char wad_type[4] = { 'W', 'A', 'D', '2' }; if (wad3_) wad_type[3] = '3'; // Write the header uint32_t num_lumps = numEntries(); mc.write(wad_type, 4); mc.write(&num_lumps, 4); mc.write(&dir_offset, 4); // Write the lumps for (uint32_t l = 0; l < num_lumps; l++) { entry = entryAt(l); mc.write(entry->rawData(), entry->size()); } // Write the directory for (uint32_t l = 0; l < num_lumps; l++) { entry = entryAt(l); // Setup directory entry Wad2Entry info; memset(info.name, 0, 16); memcpy(info.name, CHR(entry->name()), entry->name().Len()); info.cmprs = (bool)entry->exProp("W2Comp"); info.dsize = entry->size(); info.size = entry->size(); info.offset = (int)entry->exProp("Offset"); info.type = (int)entry->exProp("W2Type"); // Write it mc.write(&info, 32); if (update) entry->setState(ArchiveEntry::State::Unmodified); } return true; }
/* GobArchive::write * Writes the gob archive to a MemChunk * Returns true if successful, false otherwise *******************************************************************/ bool GobArchive::write(MemChunk& mc, bool update) { // Determine directory offset & individual lump offsets uint32_t dir_offset = 8; ArchiveEntry* entry = NULL; for (uint32_t l = 0; l < numEntries(); l++) { entry = getEntry(l); setEntryOffset(entry, dir_offset); dir_offset += entry->getSize(); } // Clear/init MemChunk mc.clear(); mc.seek(0, SEEK_SET); mc.reSize(dir_offset + 4 + numEntries() * 21); // Write the header uint32_t num_lumps = wxINT32_SWAP_ON_BE(numEntries()); dir_offset = wxINT32_SWAP_ON_BE(dir_offset); char header[4] = { 'G', 'O', 'B', 0xA }; mc.write(header, 4); mc.write(&dir_offset, 4); // Write the lumps for (uint32_t l = 0; l < numEntries(); l++) { entry = getEntry(l); mc.write(entry->getData(), entry->getSize()); } // Write the directory mc.write(&num_lumps, 4); for (uint32_t l = 0; l < numEntries(); l++) { entry = getEntry(l); char name[13] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; long offset = wxINT32_SWAP_ON_BE(getEntryOffset(entry)); long size = wxINT32_SWAP_ON_BE(entry->getSize()); for (size_t c = 0; c < entry->getName().length() && c < 13; c++) name[c] = entry->getName()[c]; mc.write(&offset, 4); mc.write(&size, 4); mc.write(name, 13); if (update) { entry->setState(0); entry->exProp("Offset") = (int)offset; } } return true; }
/* HogArchive::write * Writes the hog archive to a MemChunk * Returns true if successful, false otherwise *******************************************************************/ bool HogArchive::write(MemChunk& mc, bool update) { // Determine individual lump offsets uint32_t offset = 3; ArchiveEntry* entry = NULL; for (uint32_t l = 0; l < numEntries(); l++) { offset += 17; entry = getEntry(l); setEntryOffset(entry, offset); if (update) { entry->setState(0); entry->exProp("Offset") = (int)offset; } offset += entry->getSize(); } // Clear/init MemChunk mc.clear(); mc.seek(0, SEEK_SET); mc.reSize(offset); // Write the header char header[3] = { 'D', 'H', 'F' }; mc.write(header, 3); // Write the lumps for (uint32_t l = 0; l < numEntries(); l++) { entry = getEntry(l); mc.write(entry->getData(), entry->getSize()); } // Write the directory for (uint32_t l = 0; l < numEntries(); l++) { entry = getEntry(l); char name[13] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; long size = wxINT32_SWAP_ON_BE(entry->getSize()); for (size_t c = 0; c < entry->getName().length() && c < 13; c++) name[c] = entry->getName()[c]; mc.write(name, 13); mc.write(&size, 4); mc.write(entry->getData(), entry->getSize()); } return true; }
// ----------------------------------------------------------------------------- // Writes the grp archive to a MemChunk // Returns true if successful, false otherwise // ----------------------------------------------------------------------------- bool GrpArchive::write(MemChunk& mc, bool update) { // Clear/init MemChunk mc.clear(); mc.seek(0, SEEK_SET); mc.reSize((1 + numEntries()) * 16); ArchiveEntry* entry; // Write the header uint32_t num_lumps = numEntries(); mc.write("KenSilverman", 12); mc.write(&num_lumps, 4); // Write the directory for (uint32_t l = 0; l < num_lumps; l++) { entry = entryAt(l); char name[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; long size = entry->size(); for (size_t c = 0; c < entry->name().length() && c < 12; c++) name[c] = entry->name()[c]; mc.write(name, 12); mc.write(&size, 4); if (update) { long offset = getEntryOffset(entry); entry->setState(ArchiveEntry::State::Unmodified); entry->exProp("Offset") = (int)offset; } } // Write the lumps for (uint32_t l = 0; l < num_lumps; l++) { entry = entryAt(l); mc.write(entry->rawData(), entry->size()); } return true; }
/* DatArchive::write * Writes the dat archive to a MemChunk * Returns true if successful, false otherwise *******************************************************************/ bool DatArchive::write(MemChunk& mc, bool update) { // Only two bytes are used for storing entry amount, // so abort for excessively large files: if (numEntries() > 65535) return false; // Determine directory offset, name offsets & individual lump offsets uint32_t dir_offset = 10; uint16_t name_offset = numEntries() * 12; uint32_t name_size = 0; string previousname = ""; uint16_t* nameoffsets = new uint16_t[numEntries()]; ArchiveEntry* entry = NULL; for (uint16_t l = 0; l < numEntries(); l++) { entry = getEntry(l); setEntryOffset(entry, dir_offset); dir_offset += entry->getSize(); // Does the entry has a name? string name = entry->getName(); if (l > 0 && previousname.length() > 0 && name.length() > previousname.length() && !previousname.compare(0, previousname.length(), name, 0, previousname.length()) && name.at(previousname.length()) == '+') { // This is a fake name name = ""; nameoffsets[l] = 0; } else { // This is a true name previousname = name; nameoffsets[l] = uint16_t(name_offset + name_size); name_size += name.length() + 1; } } // Clear/init MemChunk mc.clear(); mc.seek(0, SEEK_SET); mc.reSize(dir_offset + name_size + numEntries() * 12); // Write the header uint16_t num_lumps = wxINT16_SWAP_ON_BE(numEntries()); dir_offset = wxINT32_SWAP_ON_BE(dir_offset); uint32_t unknown = 0; mc.write(&num_lumps, 2); mc.write(&dir_offset, 4); mc.write(&unknown, 4); // Write the lumps for (uint16_t l = 0; l < numEntries(); l++) { entry = getEntry(l); mc.write(entry->getData(), entry->getSize()); } // Write the directory for (uint16_t l = 0; l < num_lumps; l++) { entry = getEntry(l); uint32_t offset = wxINT32_SWAP_ON_BE(getEntryOffset(entry)); uint32_t size = wxINT32_SWAP_ON_BE(entry->getSize()); uint16_t nameofs = wxINT16_SWAP_ON_BE(nameoffsets[l]); uint16_t flags = wxINT16_SWAP_ON_BE((entry->isEncrypted() == ENC_SCRLE0) ? 1 : 0); mc.write(&offset, 4); // Offset mc.write(&size, 4); // Size mc.write(&nameofs, 2); // Name offset mc.write(&flags, 2); // Flags if (update) { entry->setState(0); entry->exProp("Offset") = (int)wxINT32_SWAP_ON_BE(offset); } } // Write the names for (uint16_t l = 0; l < num_lumps; l++) { uint8_t zero = 0; entry = getEntry(l); if (nameoffsets[l]) { mc.write(CHR(entry->getName()), entry->getName().length()); mc.write(&zero, 1); } } // Clean-up delete[] nameoffsets; // Finished! return true; }
/* DatArchive::open * Reads wad format data from a MemChunk * Returns true if successful, false otherwise *******************************************************************/ bool DatArchive::open(MemChunk& mc) { // Check data was given if (!mc.hasData()) return false; const uint8_t* mcdata = mc.getData(); // Read dat header mc.seek(0, SEEK_SET); uint16_t num_lumps; uint32_t dir_offset, unknown; mc.read(&num_lumps, 2); // Size mc.read(&dir_offset, 4); // Directory offset mc.read(&unknown, 4); // Unknown value num_lumps = wxINT16_SWAP_ON_BE(num_lumps); dir_offset = wxINT32_SWAP_ON_BE(dir_offset); unknown = wxINT32_SWAP_ON_BE(unknown); string lastname(wxString::FromAscii("-noname-")); size_t namecount = 0; // Stop announcements (don't want to be announcing modification due to entries being added etc) setMuted(true); // Read the directory mc.seek(dir_offset, SEEK_SET); theSplashWindow->setProgressMessage("Reading dat archive data"); for (uint32_t d = 0; d < num_lumps; d++) { // Update splash window progress theSplashWindow->setProgress(((float)d / (float)num_lumps)); // Read lump info uint32_t offset = 0; uint32_t size = 0; uint16_t nameofs = 0; uint16_t flags = 0; mc.read(&offset, 4); // Offset mc.read(&size, 4); // Size mc.read(&nameofs, 2); // Name offset mc.read(&flags, 2); // Flags (only one: RLE encoded) // Byteswap values for big endian if needed offset = wxINT32_SWAP_ON_BE(offset); size = wxINT32_SWAP_ON_BE(size); nameofs = wxINT16_SWAP_ON_BE(nameofs); flags = wxINT16_SWAP_ON_BE(flags); // If the lump data goes past the directory, // the data file is invalid if (offset + size > mc.getSize()) { wxLogMessage("DatArchive::open: Dat archive is invalid or corrupt at entry %i", d); Global::error = "Archive is invalid and/or corrupt"; setMuted(false); return false; } string myname; if (nameofs != 0) { size_t len = 1; size_t start = nameofs+dir_offset; for (size_t i = start; mcdata[i] != 0; ++i) { ++len; } lastname = myname = wxString::FromAscii(mcdata+start, len); namecount = 0; } else { myname = S_FMT("%s+%d", lastname, ++namecount); } // Create & setup lump ArchiveEntry* nlump = new ArchiveEntry(myname, size); nlump->setLoaded(false); nlump->exProp("Offset") = (int)offset; nlump->setState(0); if (flags & 1) nlump->setEncryption(ENC_SCRLE0); // Check for markers if (!nlump->getName().Cmp("startflats")) flats[0] = d; if (!nlump->getName().Cmp("endflats")) flats[1] = d; if (!nlump->getName().Cmp("startsprites")) sprites[0] = d; if (!nlump->getName().Cmp("endmonsters")) sprites[1] = d; if (!nlump->getName().Cmp("startwalls")) walls[0] = d; if (!nlump->getName().Cmp("endwalls")) walls[1] = d; // Add to entry list getRoot()->addEntry(nlump); } // Detect all entry types MemChunk edata; theSplashWindow->setProgressMessage("Detecting entry types"); for (size_t a = 0; a < numEntries(); a++) { // Update splash window progress theSplashWindow->setProgress((((float)a / (float)num_lumps))); // Get entry ArchiveEntry* entry = getEntry(a); // Read entry data if it isn't zero-sized if (entry->getSize() > 0) { // Read the entry data mc.exportMemChunk(edata, getEntryOffset(entry), entry->getSize()); entry->importMemChunk(edata); } // Detect entry type EntryType::detectEntryType(entry); // Set entry to unchanged entry->setState(0); } // Detect maps (will detect map entry types) //theSplashWindow->setProgressMessage("Detecting maps"); //detectMaps(); // Setup variables setMuted(false); setModified(false); announce("opened"); theSplashWindow->setProgressMessage(""); return true; }
/* ADatArchive::open * Reads dat format data from a MemChunk * Returns true if successful, false otherwise *******************************************************************/ bool ADatArchive::open(MemChunk& mc) { // Check given data is valid if (mc.getSize() < 16) return false; // Read dat header char magic[4]; long dir_offset; long dir_size; mc.seek(0, SEEK_SET); mc.read(magic, 4); mc.read(&dir_offset, 4); mc.read(&dir_size, 4); // Check it if (magic[0] != 'A' || magic[1] != 'D' || magic[2] != 'A' || magic[3] != 'T') { wxLogMessage("ADatArchive::open: Opening failed, invalid header"); Global::error = "Invalid dat header"; return false; } // Stop announcements (don't want to be announcing modification due to entries being added etc) setMuted(true); // Read the directory size_t num_entries = dir_size / DIRENTRY; mc.seek(dir_offset, SEEK_SET); theSplashWindow->setProgressMessage("Reading dat archive data"); for (uint32_t d = 0; d < num_entries; d++) { // Update splash window progress theSplashWindow->setProgress(((float)d / (float)num_entries)); // Read entry info char name[128]; long offset; long decsize; long compsize; long whatever; // No idea what this could be mc.read(name, 128); mc.read(&offset, 4); mc.read(&decsize, 4); mc.read(&compsize, 4); mc.read(&whatever, 4); // Byteswap if needed offset = wxINT32_SWAP_ON_BE(offset); decsize = wxINT32_SWAP_ON_BE(decsize); compsize = wxINT32_SWAP_ON_BE(compsize); // Check offset+size if ((unsigned)(offset + compsize) > mc.getSize()) { wxLogMessage("ADatArchive::open: dat archive is invalid or corrupt (entry goes past end of file)"); Global::error = "Archive is invalid and/or corrupt"; setMuted(false); return false; } // Parse name wxFileName fn(wxString::FromAscii(name, 128)); // Create directory if needed ArchiveTreeNode* dir = createDir(fn.GetPath(true, wxPATH_UNIX)); // Create entry ArchiveEntry* entry = new ArchiveEntry(fn.GetFullName(), compsize); entry->exProp("Offset") = (int)offset; entry->exProp("FullSize") = (int)decsize; entry->setLoaded(false); entry->setState(0); // Add to directory dir->addEntry(entry); } // Detect all entry types MemChunk edata; vector<ArchiveEntry*> all_entries; getEntryTreeAsList(all_entries); theSplashWindow->setProgressMessage("Detecting entry types"); for (size_t a = 0; a < all_entries.size(); a++) { // Update splash window progress theSplashWindow->setProgress((((float)a / (float)num_entries))); // Get entry ArchiveEntry* entry = all_entries[a]; // Read entry data if it isn't zero-sized if (entry->getSize() > 0) { // Read the entry data mc.exportMemChunk(edata, (int)entry->exProp("Offset"), entry->getSize()); MemChunk xdata; if (Compression::ZlibInflate(edata, xdata, (int)entry->exProp("FullSize"))) entry->importMemChunk(xdata); else { wxLogMessage("Entry %s couldn't be inflated", CHR(entry->getName())); entry->importMemChunk(edata); } } // Detect entry type EntryType::detectEntryType(entry); // Unload entry data if needed if (!archive_load_data) entry->unloadData(); // Set entry to unchanged entry->setState(0); } // Setup variables setMuted(false); setModified(false); announce("opened"); theSplashWindow->setProgressMessage(""); return true; }
/* ResArchive::readDirectory * Reads a res directory from a MemChunk * Returns true if successful, false otherwise *******************************************************************/ bool ResArchive::readDirectory(MemChunk& mc, size_t dir_offset, size_t num_lumps, ArchiveTreeNode* parent) { if (!parent) { LOG_MESSAGE(1, "ReadDir: No parent node"); Global::error = "Archive is invalid and/or corrupt"; return false; } mc.seek(dir_offset, SEEK_SET); for (uint32_t d = 0; d < num_lumps; d++) { // Update splash window progress UI::setSplashProgress(((float)d / (float)num_lumps)); // Read lump info char magic[4] = ""; char name[15] = ""; uint32_t dumzero1, dumzero2; uint16_t dumff, dumze; uint8_t flags = 0; uint32_t offset = 0; uint32_t size = 0; mc.read(magic, 4); // ReS\0 mc.read(name, 14); // Name mc.read(&offset, 4); // Offset mc.read(&size, 4); // Size // Check the identifier if (magic[0] != 'R' || magic[1] != 'e' || magic[2] != 'S' || magic[3] != 0) { LOG_MESSAGE(1, "ResArchive::readDir: Entry %s (%i@0x%x) has invalid directory entry", name, size, offset); Global::error = "Archive is invalid and/or corrupt"; return false; } // Byteswap values for big endian if needed offset = wxINT32_SWAP_ON_BE(offset); size = wxINT32_SWAP_ON_BE(size); name[14] = '\0'; mc.read(&dumze, 2); if (dumze) LOG_MESSAGE(1, "Flag guard not null for entry %s", name); mc.read(&flags, 1); if (flags != 1 && flags != 17) LOG_MESSAGE(1, "Unknown flag value for entry %s", name); mc.read(&dumzero1, 4); if (dumzero1) LOG_MESSAGE(1, "Near-end values not set to zero for entry %s", name); mc.read(&dumff, 2); if (dumff != 0xFFFF) LOG_MESSAGE(1, "Dummy set to a non-FF value for entry %s", name); mc.read(&dumzero2, 4); if (dumzero2) LOG_MESSAGE(1, "Trailing values not set to zero for entry %s", name); // If the lump data goes past the end of the file, // the resfile is invalid if (offset + size > mc.getSize()) { LOG_MESSAGE(1, "ResArchive::readDirectory: Res archive is invalid or corrupt, offset overflow"); Global::error = "Archive is invalid and/or corrupt"; setMuted(false); return false; } // Create & setup lump ArchiveEntry* nlump = new ArchiveEntry(wxString::FromAscii(name), size); nlump->setLoaded(false); nlump->exProp("Offset") = (int)offset; nlump->setState(0); // Read entry data if it isn't zero-sized if (nlump->getSize() > 0) { // Read the entry data MemChunk edata; mc.exportMemChunk(edata, offset, size); nlump->importMemChunk(edata); } // What if the entry is a directory? size_t d_o, n_l; if (isResArchive(nlump->getMCData(), d_o, n_l)) { ArchiveTreeNode* ndir = createDir(name, parent); if (ndir) { UI::setSplashProgressMessage(S_FMT("Reading res archive data: %s directory", name)); // Save offset to restore it once the recursion is done size_t myoffset = mc.currentPos(); readDirectory(mc, d_o, n_l, ndir); ndir->getDirEntry()->setState(0); // Restore offset and clean out the entry mc.seek(myoffset, SEEK_SET); delete nlump; } else { delete nlump; return false; } // Not a directory, then add to entry list } else { parent->addEntry(nlump); // Detect entry type EntryType::detectEntryType(nlump); // Unload entry data if needed if (!archive_load_data) nlump->unloadData(); // Set entry to unchanged nlump->setState(0); } } return true; }
/* PakArchive::open * Reads pak format data from a MemChunk * Returns true if successful, false otherwise *******************************************************************/ bool PakArchive::open(MemChunk& mc) { // Check given data is valid if (mc.getSize() < 12) return false; // Read pak header char pack[4]; long dir_offset; long dir_size; mc.seek(0, SEEK_SET); mc.read(pack, 4); mc.read(&dir_offset, 4); mc.read(&dir_size, 4); // Check it if (pack[0] != 'P' || pack[1] != 'A' || pack[2] != 'C' || pack[3] != 'K') { wxLogMessage("PakArchive::open: Opening failed, invalid header"); Global::error = "Invalid pak header"; return false; } // Stop announcements (don't want to be announcing modification due to entries being added etc) setMuted(true); // Read the directory size_t num_entries = dir_size / 64; mc.seek(dir_offset, SEEK_SET); theSplashWindow->setProgressMessage("Reading pak archive data"); for (uint32_t d = 0; d < num_entries; d++) { // Update splash window progress theSplashWindow->setProgress(((float)d / (float)num_entries)); // Read entry info char name[56]; long offset; long size; mc.read(name, 56); mc.read(&offset, 4); mc.read(&size, 4); // Byteswap if needed offset = wxINT32_SWAP_ON_BE(offset); size = wxINT32_SWAP_ON_BE(size); // Check offset+size if ((unsigned)(offset + size) > mc.getSize()) { wxLogMessage("PakArchive::open: Pak archive is invalid or corrupt (entry goes past end of file)"); Global::error = "Archive is invalid and/or corrupt"; setMuted(false); return false; } // Parse name wxFileName fn(wxString::FromAscii(name, 56)); // Create directory if needed ArchiveTreeNode* dir = createDir(fn.GetPath(true, wxPATH_UNIX)); // Create entry ArchiveEntry* entry = new ArchiveEntry(fn.GetFullName(), size); entry->exProp("Offset") = (int)offset; entry->setLoaded(false); entry->setState(0); // Add to directory dir->addEntry(entry); } // Detect all entry types MemChunk edata; vector<ArchiveEntry*> all_entries; getEntryTreeAsList(all_entries); theSplashWindow->setProgressMessage("Detecting entry types"); for (size_t a = 0; a < all_entries.size(); a++) { // Update splash window progress theSplashWindow->setProgress((((float)a / (float)num_entries))); // Get entry ArchiveEntry* entry = all_entries[a]; // Read entry data if it isn't zero-sized if (entry->getSize() > 0) { // Read the entry data mc.exportMemChunk(edata, (int)entry->exProp("Offset"), entry->getSize()); entry->importMemChunk(edata); } // Detect entry type EntryType::detectEntryType(entry); // Unload entry data if needed if (!archive_load_data) entry->unloadData(); // Set entry to unchanged entry->setState(0); } // Setup variables setMuted(false); setModified(false); announce("opened"); theSplashWindow->setProgressMessage(""); return true; }
/* RffArchive::open * Reads grp format data from a MemChunk * Returns true if successful, false otherwise *******************************************************************/ bool RffArchive::open(MemChunk& mc) { // Check data was given if (!mc.hasData()) return false; // Read grp header uint8_t magic[4]; uint32_t version, dir_offset, num_lumps; mc.seek(0, SEEK_SET); mc.read(magic, 4); // Should be "RFF\x18" mc.read(&version, 4); // 0x01 0x03 \x00 \x00 mc.read(&dir_offset, 4); // Offset to directory mc.read(&num_lumps, 4); // No. of lumps in rff // Byteswap values for big endian if needed dir_offset = wxINT32_SWAP_ON_BE(dir_offset); num_lumps = wxINT32_SWAP_ON_BE(num_lumps); version = wxINT32_SWAP_ON_BE(version); // Check the header if (magic[0] != 'R' || magic[1] != 'F' || magic[2] != 'F' || magic[3] != 0x1A || version != 0x301) { wxLogMessage("RffArchive::openFile: File %s has invalid header", filename); Global::error = "Invalid rff header"; return false; } // Stop announcements (don't want to be announcing modification due to entries being added etc) setMuted(true); // Read the directory RFFLump* lumps = new RFFLump[num_lumps]; mc.seek(dir_offset, SEEK_SET); theSplashWindow->setProgressMessage("Reading rff archive data"); mc.read (lumps, num_lumps * sizeof(RFFLump)); BloodCrypt (lumps, dir_offset, num_lumps * sizeof(RFFLump)); for (uint32_t d = 0; d < num_lumps; d++) { // Update splash window progress theSplashWindow->setProgress(((float)d / (float)num_lumps)); // Read lump info char name[13] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; uint32_t offset = wxINT32_SWAP_ON_BE(lumps[d].FilePos); uint32_t size = wxINT32_SWAP_ON_BE(lumps[d].Size); // Reconstruct name int i, j = 0; for (i = 0; i < 8; ++i) { if (lumps[d].Name[i] == 0) break; name[i] = lumps[d].Name[i]; } for (name[i++] = '.'; j < 3; ++j) name[i+j] = lumps[d].Extension[j]; // If the lump data goes past the end of the file, // the rfffile is invalid if (offset + size > mc.getSize()) { wxLogMessage("RffArchive::open: rff archive is invalid or corrupt"); Global::error = "Archive is invalid and/or corrupt"; setMuted(false); return false; } // Create & setup lump ArchiveEntry* nlump = new ArchiveEntry(wxString::FromAscii(name), size); nlump->setLoaded(false); nlump->exProp("Offset") = (int)offset; nlump->setState(0); // Is the entry encrypted? if (lumps[d].Flags & 0x10) nlump->setEncryption(ENC_BLOOD); // Add to entry list getRoot()->addEntry(nlump); } delete[] lumps; // Detect all entry types MemChunk edata; theSplashWindow->setProgressMessage("Detecting entry types"); for (size_t a = 0; a < numEntries(); a++) { // Update splash window progress theSplashWindow->setProgress((((float)a / (float)num_lumps))); // Get entry ArchiveEntry* entry = getEntry(a); // Read entry data if it isn't zero-sized if (entry->getSize() > 0) { // Read the entry data mc.exportMemChunk(edata, getEntryOffset(entry), entry->getSize()); // If the entry is encrypted, decrypt it if (entry->isEncrypted()) { uint8_t* cdata = new uint8_t[entry->getSize()]; memcpy(cdata, edata.getData(), entry->getSize()); int cryptlen = entry->getSize() < 256 ? entry->getSize() : 256; BloodCrypt(cdata, 0, cryptlen); edata.importMem(cdata, entry->getSize()); delete[] cdata; } // Import data entry->importMemChunk(edata); } // Detect entry type EntryType::detectEntryType(entry); // Unload entry data if needed if (!archive_load_data) entry->unloadData(); // Set entry to unchanged entry->setState(0); } // Detect maps (will detect map entry types) //theSplashWindow->setProgressMessage("Detecting maps"); //detectMaps(); // Setup variables setMuted(false); setModified(false); announce("opened"); theSplashWindow->setProgressMessage(""); return true; }
/* Wad2Archive::open * Reads wad format data from a MemChunk * Returns true if successful, false otherwise *******************************************************************/ bool Wad2Archive::open(MemChunk& mc) { // Check data was given if (!mc.hasData()) return false; // Read wad header uint32_t num_lumps = 0; uint32_t dir_offset = 0; char wad_type[4] = ""; mc.seek(0, SEEK_SET); mc.read(&wad_type, 4); // Wad type mc.read(&num_lumps, 4); // No. of lumps in wad mc.read(&dir_offset, 4); // Offset to directory // Byteswap values for big endian if needed num_lumps = wxINT32_SWAP_ON_BE(num_lumps); dir_offset = wxINT32_SWAP_ON_BE(dir_offset); // Check the header if (wad_type[0] != 'W' || wad_type[1] != 'A' || wad_type[2] != 'D' || (wad_type[3] != '2' && wad_type[3] != '3')) { wxLogMessage("Wad2Archive::open: Invalid header"); Global::error = "Invalid wad2 header"; return false; } if (wad_type[3] == '3') wad3 = true; // Stop announcements (don't want to be announcing modification due to entries being added etc) setMuted(true); // Read the directory mc.seek(dir_offset, SEEK_SET); theSplashWindow->setProgressMessage("Reading wad archive data"); for (uint32_t d = 0; d < num_lumps; d++) { // Update splash window progress theSplashWindow->setProgress(((float)d / (float)num_lumps)); // Read lump info wad2entry_t info; mc.read(&info, 32); // Byteswap values for big endian if needed info.offset = wxINT32_SWAP_ON_BE(info.offset); info.size = wxINT32_SWAP_ON_BE(info.size); info.dsize = wxINT32_SWAP_ON_BE(info.dsize); // If the lump data goes past the end of the file, // the wadfile is invalid if ((unsigned)(info.offset + info.dsize) > mc.getSize()) { wxLogMessage("Wad2Archive::open: Wad2 archive is invalid or corrupt"); Global::error = "Archive is invalid and/or corrupt"; setMuted(false); return false; } // Create & setup lump ArchiveEntry* nlump = new ArchiveEntry(wxString::FromAscii(info.name, 16), info.dsize); nlump->setLoaded(false); nlump->exProp("Offset") = (int)info.offset; nlump->exProp("W2Type") = info.type; nlump->exProp("W2Size") = (int)info.size; nlump->exProp("W2Comp") = !!(info.cmprs); nlump->setState(0); // Add to entry list getRoot()->addEntry(nlump); } // Detect all entry types MemChunk edata; theSplashWindow->setProgressMessage("Detecting entry types"); for (size_t a = 0; a < numEntries(); a++) { // Update splash window progress theSplashWindow->setProgress((((float)a / (float)num_lumps))); // Get entry ArchiveEntry* entry = getEntry(a); // Read entry data if it isn't zero-sized if (entry->getSize() > 0) { // Read the entry data mc.exportMemChunk(edata, (int)entry->exProp("Offset"), entry->getSize()); entry->importMemChunk(edata); } // Detect entry type EntryType::detectEntryType(entry); // Unload entry data if needed if (!archive_load_data) entry->unloadData(); // Set entry to unchanged entry->setState(0); } // Detect maps (will detect map entry types) theSplashWindow->setProgressMessage("Detecting maps"); detectMaps(); // Setup variables setMuted(false); setModified(false); announce("opened"); theSplashWindow->setProgressMessage(""); return true; }
/* GobArchive::open * Reads gob format data from a MemChunk * Returns true if successful, false otherwise *******************************************************************/ bool GobArchive::open(MemChunk& mc) { // Check data was given if (!mc.hasData()) return false; // Check size if (mc.getSize() < 12) return false; // Check magic header if (mc[0] != 'G' || mc[1] != 'O' || mc[2] != 'B' || mc[3] != 0xA) return false; // Get directory offset uint32_t dir_offset = 0; mc.seek(4, SEEK_SET); mc.read(&dir_offset, 4); dir_offset = wxINT32_SWAP_ON_BE(dir_offset); // Check size if ((unsigned)mc.getSize() < (dir_offset + 4)) return false; // Get number of lumps uint32_t num_lumps = 0; mc.seek(dir_offset, SEEK_SET); mc.read(&num_lumps, 4); num_lumps = wxINT32_SWAP_ON_BE(num_lumps); // Compute directory size uint32_t dir_size = (num_lumps * 21) + 4; if ((unsigned)mc.getSize() < (dir_offset + dir_size)) return false; // Stop announcements (don't want to be announcing modification due to entries being added etc) setMuted(true); // Read the directory theSplashWindow->setProgressMessage("Reading gob archive data"); for (uint32_t d = 0; d < num_lumps; d++) { // Update splash window progress theSplashWindow->setProgress(((float)d / (float)num_lumps)); // Read lump info uint32_t offset = 0; uint32_t size = 0; char name[13] = ""; mc.read(&offset, 4); // Offset mc.read(&size, 4); // Size mc.read(name, 13); // Name name[12] = '\0'; // Byteswap values for big endian if needed size = wxINT32_SWAP_ON_BE(size); // If the lump data goes past the end of the file, // the gobfile is invalid if (offset + size > mc.getSize()) { wxLogMessage("GobArchive::open: gob archive is invalid or corrupt"); Global::error = "Archive is invalid and/or corrupt"; setMuted(false); return false; } // Create & setup lump ArchiveEntry* nlump = new ArchiveEntry(wxString::FromAscii(name), size); nlump->setLoaded(false); nlump->exProp("Offset") = (int)offset; nlump->setState(0); // Add to entry list getRoot()->addEntry(nlump); } // Detect all entry types MemChunk edata; theSplashWindow->setProgressMessage("Detecting entry types"); for (size_t a = 0; a < numEntries(); a++) { // Update splash window progress theSplashWindow->setProgress((((float)a / (float)num_lumps))); // Get entry ArchiveEntry* entry = getEntry(a); // Read entry data if it isn't zero-sized if (entry->getSize() > 0) { // Read the entry data mc.exportMemChunk(edata, getEntryOffset(entry), entry->getSize()); entry->importMemChunk(edata); } // Detect entry type EntryType::detectEntryType(entry); // Unload entry data if needed if (!archive_load_data) entry->unloadData(); // Set entry to unchanged entry->setState(0); } // Setup variables setMuted(false); setModified(false); announce("opened"); theSplashWindow->setProgressMessage(""); return true; }
/* WadArchive::open * Reads wad format data from a MemChunk * Returns true if successful, false otherwise *******************************************************************/ bool WadArchive::open(MemChunk& mc) { // Check data was given if (!mc.hasData()) return false; // Read wad header uint32_t num_lumps = 0; uint32_t dir_offset = 0; char wad_type[4] = ""; mc.seek(0, SEEK_SET); mc.read(&wad_type, 4); // Wad type mc.read(&num_lumps, 4); // No. of lumps in wad mc.read(&dir_offset, 4); // Offset to directory // Byteswap values for big endian if needed num_lumps = wxINT32_SWAP_ON_BE(num_lumps); dir_offset = wxINT32_SWAP_ON_BE(dir_offset); // Check the header if (wad_type[1] != 'W' || wad_type[2] != 'A' || wad_type[3] != 'D') { wxLogMessage("WadArchive::openFile: File %s has invalid header", filename); Global::error = "Invalid wad header"; return false; } // Check for iwad if (wad_type[0] == 'I') iwad = true; // Stop announcements (don't want to be announcing modification due to entries being added etc) setMuted(true); vector<uint32_t> offsets; // Read the directory mc.seek(dir_offset, SEEK_SET); theSplashWindow->setProgressMessage("Reading wad archive data"); for (uint32_t d = 0; d < num_lumps; d++) { // Update splash window progress theSplashWindow->setProgress(((float)d / (float)num_lumps)); // Read lump info char name[9] = ""; uint32_t offset = 0; uint32_t size = 0; mc.read(&offset, 4); // Offset mc.read(&size, 4); // Size mc.read(name, 8); // Name name[8] = '\0'; // Byteswap values for big endian if needed offset = wxINT32_SWAP_ON_BE(offset); size = wxINT32_SWAP_ON_BE(size); // Check to catch stupid shit if (size > 0) { if (offset == 0) { LOG_MESSAGE(2, "No."); continue; } if (VECTOR_EXISTS(offsets, offset)) { LOG_MESSAGE(1, "Ignoring entry %d: %s, is a clone of a previous entry", d, name); continue; } offsets.push_back(offset); } // Hack to open Operation: Rheingold WAD files if (size == 0 && offset > mc.getSize()) offset = 0; // Is there a compression/encryption thing going on? bool jaguarencrypt = !!(name[0] & 0x80); // look at high bit name[0] = name[0] & 0x7F; // then strip it away // Look for encryption shenanigans size_t actualsize = size; if (jaguarencrypt) { if (d < num_lumps - 1) { size_t pos = mc.currentPos(); uint32_t nextoffset = 0; for (int i = 0; i + d < num_lumps; ++i) { mc.read(&nextoffset, 4); if (nextoffset != 0) break; mc.seek(12, SEEK_CUR); } nextoffset = wxINT32_SWAP_ON_BE(nextoffset); if (nextoffset == 0) nextoffset = dir_offset; mc.seek(pos, SEEK_SET); actualsize = nextoffset - offset; } else { if (offset > dir_offset) { actualsize = mc.getSize() - offset; } else { actualsize = dir_offset - offset; } } } // If the lump data goes past the end of the file, // the wadfile is invalid if (offset + actualsize > mc.getSize()) { wxLogMessage("WadArchive::open: Wad archive is invalid or corrupt"); Global::error = S_FMT("Archive is invalid and/or corrupt (lump %d: %s data goes past end of file)", d, name); setMuted(false); return false; } // Create & setup lump ArchiveEntry* nlump = new ArchiveEntry(wxString::FromAscii(name), size); nlump->setLoaded(false); nlump->exProp("Offset") = (int)offset; nlump->setState(0); if (jaguarencrypt) { nlump->setEncryption(ENC_JAGUAR); nlump->exProp("FullSize") = (int)size; } // Add to entry list getRoot()->addEntry(nlump); } // Detect namespaces (needs to be done before type detection as some types // rely on being within certain namespaces) updateNamespaces(); // Detect all entry types MemChunk edata; theSplashWindow->setProgressMessage("Detecting entry types"); for (size_t a = 0; a < numEntries(); a++) { // Update splash window progress theSplashWindow->setProgress((((float)a / (float)numEntries()))); // Get entry ArchiveEntry* entry = getEntry(a); // Read entry data if it isn't zero-sized if (entry->getSize() > 0) { // Read the entry data mc.exportMemChunk(edata, getEntryOffset(entry), entry->getSize()); if (entry->isEncrypted()) { if (entry->exProps().propertyExists("FullSize") && (unsigned)(int)(entry->exProp("FullSize")) > entry->getSize()) edata.reSize((int)(entry->exProp("FullSize")), true); if (!JaguarDecode(edata)) wxLogMessage("%i: %s (following %s), did not decode properly", a, entry->getName(), a>0?getEntry(a-1)->getName():"nothing"); } entry->importMemChunk(edata); } // Detect entry type EntryType::detectEntryType(entry); // Unload entry data if needed if (!archive_load_data) entry->unloadData(); // Set entry to unchanged entry->setState(0); } // Identify #included lumps (DECORATE, GLDEFS, etc.) detectIncludes(); // Detect maps (will detect map entry types) theSplashWindow->setProgressMessage("Detecting maps"); detectMaps(); // Setup variables setMuted(false); setModified(false); //if (iwad && iwad_lock) read_only = true; announce("opened"); theSplashWindow->setProgressMessage(""); return true; }
/* WadArchive::write * Writes the wad archive to a MemChunk * Returns true if successful, false otherwise *******************************************************************/ bool WadArchive::write(MemChunk& mc, bool update) { // Don't write if iwad if (iwad && iwad_lock) { Global::error = "IWAD saving disabled"; return false; } // Determine directory offset & individual lump offsets uint32_t dir_offset = 12; ArchiveEntry* entry = NULL; for (uint32_t l = 0; l < numEntries(); l++) { entry = getEntry(l); setEntryOffset(entry, dir_offset); dir_offset += entry->getSize(); } // Clear/init MemChunk mc.clear(); mc.seek(0, SEEK_SET); mc.reSize(dir_offset + numEntries() * 16); // Setup wad type char wad_type[4] = { 'P', 'W', 'A', 'D' }; if (iwad) wad_type[0] = 'I'; // Write the header uint32_t num_lumps = numEntries(); mc.write(wad_type, 4); mc.write(&num_lumps, 4); mc.write(&dir_offset, 4); // Write the lumps for (uint32_t l = 0; l < num_lumps; l++) { entry = getEntry(l); mc.write(entry->getData(), entry->getSize()); } // Write the directory for (uint32_t l = 0; l < num_lumps; l++) { entry = getEntry(l); char name[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; long offset = getEntryOffset(entry); long size = entry->getSize(); for (size_t c = 0; c < entry->getName().length() && c < 8; c++) name[c] = entry->getName()[c]; mc.write(&offset, 4); mc.write(&size, 4); mc.write(name, 8); if (update) { entry->setState(0); entry->exProp("Offset") = (int)offset; } } return true; }
// ----------------------------------------------------------------------------- // Reads disk format data from a MemChunk. // Returns true if successful, false otherwise // ----------------------------------------------------------------------------- bool DiskArchive::open(MemChunk& mc) { size_t mcsize = mc.getSize(); // Check given data is valid if (mcsize < 80) return false; // Read disk header uint32_t num_entries; mc.seek(0, SEEK_SET); mc.read(&num_entries, 4); num_entries = wxUINT32_SWAP_ON_LE(num_entries); size_t start_offset = (72 * num_entries) + 8; if (mcsize < start_offset) return false; // Stop announcements (don't want to be announcing modification due to entries being added etc) setMuted(true); // Read the directory UI::setSplashProgressMessage("Reading disk archive data"); for (uint32_t d = 0; d < num_entries; d++) { // Update splash window progress UI::setSplashProgress(((float)d / (float)num_entries)); // Read entry info DiskEntry dent; mc.read(&dent, 72); // Byteswap if needed dent.length = wxUINT32_SWAP_ON_LE(dent.length); dent.offset = wxUINT32_SWAP_ON_LE(dent.offset); // Increase offset to make it relative to start of archive dent.offset += start_offset; // Check offset+size if (dent.offset + dent.length > mcsize) { LOG_MESSAGE(1, "DiskArchive::open: Disk archive is invalid or corrupt (entry goes past end of file)"); Global::error = "Archive is invalid and/or corrupt"; setMuted(false); return false; } // Parse name string name = wxString::FromAscii(dent.name, 64); name.Replace("\\", "/"); name.Replace("GAME:/", ""); wxFileName fn(name); // Create directory if needed ArchiveTreeNode* dir = createDir(fn.GetPath(true, wxPATH_UNIX)); // Create entry ArchiveEntry* entry = new ArchiveEntry(fn.GetFullName(), dent.length); entry->exProp("Offset") = (int)dent.offset; entry->setLoaded(false); entry->setState(0); // Add to directory dir->addEntry(entry); } // Detect all entry types MemChunk edata; vector<ArchiveEntry*> all_entries; getEntryTreeAsList(all_entries); UI::setSplashProgressMessage("Detecting entry types"); for (size_t a = 0; a < all_entries.size(); a++) { // Update splash window progress UI::setSplashProgress((((float)a / (float)num_entries))); // Get entry ArchiveEntry* entry = all_entries[a]; // Read entry data if it isn't zero-sized if (entry->getSize() > 0) { // Read the entry data mc.exportMemChunk(edata, (int)entry->exProp("Offset"), entry->getSize()); entry->importMemChunk(edata); } // Detect entry type EntryType::detectEntryType(entry); // Unload entry data if needed if (!archive_load_data) entry->unloadData(); // Set entry to unchanged entry->setState(0); } // Setup variables setMuted(false); setModified(false); announce("opened"); UI::setSplashProgressMessage(""); return true; }
/* LfdArchive::open * Reads lfd format data from a MemChunk * Returns true if successful, false otherwise *******************************************************************/ bool LfdArchive::open(MemChunk& mc) { // Check data was given if (!mc.hasData()) return false; // Check size if (mc.getSize() < 16) return false; // Check magic header if (mc[0] != 'R' || mc[1] != 'M' || mc[2] != 'A' || mc[3] != 'P') return false; // Get directory length uint32_t dir_len = 0; mc.seek(12, SEEK_SET); mc.read(&dir_len, 4); dir_len = wxINT32_SWAP_ON_BE(dir_len); // Check size if ((unsigned)mc.getSize() < (dir_len) || dir_len % 16) return false; // Guess number of lumps uint32_t num_lumps = dir_len / 16; // Stop announcements (don't want to be announcing modification due to entries being added etc) setMuted(true); // Read each entry theSplashWindow->setProgressMessage("Reading lfd archive data"); size_t offset = dir_len + 16; size_t size = mc.getSize(); for (uint32_t d = 0; offset < size; d++) { // Update splash window progress theSplashWindow->setProgress(((float)d / (float)num_lumps)); // Read lump info uint32_t length = 0; char type[5] = ""; char name[9] = ""; mc.read(type, 4); // Type mc.read(name, 8); // Name mc.read(&length, 4); // Size name[8] = '\0'; type[4] = 0; // Move past the header offset += 16; // Byteswap values for big endian if needed length = wxINT32_SWAP_ON_BE(length); // If the lump data goes past the end of the file, // the gobfile is invalid if (offset + length > size) { wxLogMessage("LfdArchive::open: lfd archive is invalid or corrupt"); Global::error = "Archive is invalid and/or corrupt"; setMuted(false); return false; } // Create & setup lump wxFileName fn(name); fn.SetExt(type); ArchiveEntry* nlump = new ArchiveEntry(fn.GetFullName(), length); nlump->setLoaded(false); nlump->exProp("Offset") = (int)offset; nlump->setState(0); // Add to entry list getRoot()->addEntry(nlump); // Move to next entry offset += length; mc.seek(offset, SEEK_SET); } if (num_lumps != numEntries()) wxLogMessage("Warning: computed %i lumps, but actually %i entries", num_lumps, numEntries()); // Detect all entry types MemChunk edata; theSplashWindow->setProgressMessage("Detecting entry types"); for (size_t a = 0; a < numEntries(); a++) { // Update splash window progress theSplashWindow->setProgress((((float)a / (float)num_lumps))); // Get entry ArchiveEntry* entry = getEntry(a); // Read entry data if it isn't zero-sized if (entry->getSize() > 0) { // Read the entry data mc.exportMemChunk(edata, getEntryOffset(entry), entry->getSize()); entry->importMemChunk(edata); } // Detect entry type EntryType::detectEntryType(entry); // Unload entry data if needed if (!archive_load_data) entry->unloadData(); // Set entry to unchanged entry->setState(0); } // Setup variables setMuted(false); setModified(false); announce("opened"); theSplashWindow->setProgressMessage(""); return true; }
/* HogArchive::open * Reads hog format data from a MemChunk * Returns true if successful, false otherwise *******************************************************************/ bool HogArchive::open(MemChunk& mc) { // Check data was given if (!mc.hasData()) return false; // Check size size_t archive_size = mc.getSize(); if (archive_size < 3) return false; // Check magic header (DHF for "Descent Hog File") if (mc[0] != 'D' || mc[1] != 'H' || mc[2] != 'F') return false; // Stop announcements (don't want to be announcing modification due to entries being added etc) setMuted(true); // Iterate through files to see if the size seems okay theSplashWindow->setProgressMessage("Reading hog archive data"); size_t iter_offset = 3; uint32_t num_lumps = 0; while (iter_offset < archive_size) { // Update splash window progress theSplashWindow->setProgress(((float)iter_offset / (float)archive_size)); // If the lump data goes past the end of the file, // the hogfile is invalid if (iter_offset + 17 > archive_size) { wxLogMessage("HogArchive::open: hog archive is invalid or corrupt"); Global::error = "Archive is invalid and/or corrupt"; setMuted(false); return false; } // Setup variables num_lumps++; size_t offset = iter_offset + 17; size_t size = READ_L32(mc, iter_offset + 13); char name[14] = ""; mc.seek(iter_offset, SEEK_SET); mc.read(name, 13); name[13] = 0; // Create & setup lump ArchiveEntry* nlump = new ArchiveEntry(wxString::FromAscii(name), size); nlump->setLoaded(false); nlump->exProp("Offset") = (int)offset; nlump->setState(0); // Add to entry list getRoot()->addEntry(nlump); // Update entry size to compute next offset iter_offset = offset + size; } // Detect all entry types MemChunk edata; theSplashWindow->setProgressMessage("Detecting entry types"); for (size_t a = 0; a < numEntries(); a++) { // Update splash window progress theSplashWindow->setProgress((((float)a / (float)num_lumps))); // Get entry ArchiveEntry* entry = getEntry(a); // Read entry data if it isn't zero-sized if (entry->getSize() > 0) { // Read the entry data mc.exportMemChunk(edata, getEntryOffset(entry), entry->getSize()); entry->importMemChunk(edata); } // Detect entry type EntryType::detectEntryType(entry); // Unload entry data if needed if (!archive_load_data) entry->unloadData(); // Set entry to unchanged entry->setState(0); } // Setup variables setMuted(false); setModified(false); announce("opened"); theSplashWindow->setProgressMessage(""); return true; }
/* GZipArchive::open * Reads gzip format data from a MemChunk * Returns true if successful, false otherwise *******************************************************************/ bool GZipArchive::open(MemChunk& mc) { // Minimal metadata size is 18: 10 for header, 8 for footer size_t mds = 18; size_t size = mc.getSize(); if (mds > size) return false; // Read header uint8_t header[4]; mc.read(header, 4); // Check for GZip header; we'll only accept deflated gzip files // and reject any field using unknown flags if ((!(header[0] == GZIP_ID1 && header[1] == GZIP_ID2 && header[2] == GZIP_DEFLATE)) || (header[3] & GZIP_FLG_FUNKN)) return false; bool ftext, fhcrc, fxtra, fname, fcmnt; ftext = (header[3] & GZIP_FLG_FTEXT) ? true : false; fhcrc = (header[3] & GZIP_FLG_FHCRC) ? true : false; fxtra = (header[3] & GZIP_FLG_FXTRA) ? true : false; fname = (header[3] & GZIP_FLG_FNAME) ? true : false; fcmnt = (header[3] & GZIP_FLG_FCMNT) ? true : false; flags = header[3]; mc.read(&mtime, 4); mtime = wxUINT32_SWAP_ON_BE(mtime); mc.read(&xfl, 1); mc.read(&os, 1); // Skip extra fields which may be there if (fxtra) { uint16_t xlen; mc.read(&xlen, 2); xlen = wxUINT16_SWAP_ON_BE(xlen); mds += xlen + 2; if (mds > size) return false; mc.exportMemChunk(xtra, mc.currentPos(), xlen); mc.seek(xlen, SEEK_CUR); } // Skip past name, if any string name; if (fname) { char c; do { mc.read(&c, 1); if (c) name += c; ++mds; } while (c != 0 && size > mds); } else { // Build name from filename name = getFilename(false); wxFileName fn(name); if (!fn.GetExt().CmpNoCase("tgz")) fn.SetExt("tar"); else if (!fn.GetExt().CmpNoCase("gz")) fn.ClearExt(); name = fn.GetFullName(); } // Skip past comment if (fcmnt) { char c; do { mc.read(&c, 1); if (c) comment += c; ++mds; } while (c != 0 && size > mds); wxLogMessage("Archive %s says:\n %s", CHR(getFilename(true)), CHR(comment)); } // Skip past CRC 16 check if (fhcrc) { uint8_t* crcbuffer = new uint8_t[mc.currentPos()]; memcpy(crcbuffer, mc.getData(), mc.currentPos()); uint32_t fullcrc = Misc::crc(crcbuffer, mc.currentPos()); delete[] crcbuffer; uint16_t hcrc; mc.read(&hcrc, 2); hcrc = wxUINT16_SWAP_ON_BE(hcrc); mds += 2; if (hcrc != (fullcrc & 0x0000FFFF)) { wxLogMessage("CRC-16 mismatch for GZip header"); } } // Header is over if (mds > size || mc.currentPos() + 8 > size) return false; // Let's create the entry setMuted(true); ArchiveEntry* entry = new ArchiveEntry(name, size - mds); MemChunk xdata; if (Compression::GZipInflate(mc, xdata)) { entry->importMemChunk(xdata); } else { delete entry; setMuted(false); return false; } getRoot()->addEntry(entry); EntryType::detectEntryType(entry); entry->setState(0); setMuted(false); setModified(false); announce("opened"); // Finish return true; }
/* LfdArchive::write * Writes the lfd archive to a MemChunk * Returns true if successful, false otherwise *******************************************************************/ bool LfdArchive::write(MemChunk& mc, bool update) { // Determine total size uint32_t dir_size = (numEntries() + 1)<<4; uint32_t total_size = dir_size; ArchiveEntry* entry = NULL; for (uint32_t l = 0; l < numEntries(); l++) { entry = getEntry(l); total_size += 16; setEntryOffset(entry, total_size); if (update) { entry->setState(0); entry->exProp("Offset") = (int)total_size; } total_size += entry->getSize(); } // Clear/init MemChunk mc.clear(); mc.seek(0, SEEK_SET); mc.reSize(total_size); // Variables char type[5] = "RMAP"; char name[9] = "resource"; size_t size = wxINT32_SWAP_ON_BE(numEntries()<<4); // Write the resource map first mc.write(type, 4); mc.write(name, 8); mc.write(&size,4); for (uint32_t l = 0; l < numEntries(); l++) { entry = getEntry(l); for (int t = 0; t < 5; ++t) type[t] = 0; for (int n = 0; n < 9; ++n) name[n] = 0; size = wxINT32_SWAP_ON_BE(entry->getSize()); wxFileName fn(entry->getName()); for (size_t c = 0; c < fn.GetName().length() && c < 9; c++) name[c] = fn.GetName()[c]; for (size_t c = 0; c < fn.GetExt().length() && c < 5; c++) type[c] = fn.GetExt()[c]; mc.write(type, 4); mc.write(name, 8); mc.write(&size,4); } // Write the lumps for (uint32_t l = 0; l < numEntries(); l++) { entry = getEntry(l); for (int t = 0; t < 5; ++t) type[t] = 0; for (int n = 0; n < 9; ++n) name[n] = 0; size = wxINT32_SWAP_ON_BE(entry->getSize()); wxFileName fn(entry->getName()); for (size_t c = 0; c < fn.GetName().length() && c < 9; c++) name[c] = fn.GetName()[c]; for (size_t c = 0; c < fn.GetExt().length() && c < 5; c++) type[c] = fn.GetExt()[c]; mc.write(type, 4); mc.write(name, 8); mc.write(&size,4); mc.write(entry->getData(), entry->getSize()); } return true; }