/* Wad2Archive::isWad2Archive * Checks if the given data is a valid Quake wad2 archive *******************************************************************/ bool Wad2Archive::isWad2Archive(MemChunk& mc) { // Check size if (mc.getSize() < 12) return false; // Check for IWAD/PWAD header if (mc[0] != 'W' || mc[1] != 'A' || mc[2] != 'D' || (mc[3] != '2' && mc[3] != '3')) return false; // Get number of lumps and directory offset int32_t num_lumps = 0; int32_t dir_offset = 0; mc.seek(4, SEEK_SET); mc.read(&num_lumps, 4); mc.read(&dir_offset, 4); // Reset MemChunk (just in case) mc.seek(0, SEEK_SET); // Byteswap values for big endian if needed num_lumps = wxINT32_SWAP_ON_BE(num_lumps); dir_offset = wxINT32_SWAP_ON_BE(dir_offset); // Check directory offset is decent if ((unsigned)(dir_offset + (num_lumps * 32)) > mc.getSize() || dir_offset < 12) return false; // If it's passed to here it's probably a wad2 file return true; }
/* ChasmBinArchive::isChasmBinArchive * Checks if the given data is a valid Chasm bin archive *******************************************************************/ bool ChasmBinArchive::isChasmBinArchive(MemChunk& mc) { // Check given data is valid if (mc.getSize() < HEADER_SIZE) { return false; } // Read bin header and check it char magic[4] = {}; mc.read(magic, sizeof magic); if ( magic[0] != 'C' || magic[1] != 'S' || magic[2] != 'i' || magic[3] != 'd') { return false; } uint16_t num_entries = 0; mc.read(&num_entries, sizeof num_entries); num_entries = wxUINT16_SWAP_ON_BE(num_entries); return num_entries > MAX_ENTRY_COUNT || (HEADER_SIZE + ENTRY_SIZE * MAX_ENTRY_COUNT) <= mc.getSize(); }
/* PakArchive::isPakArchive * Checks if the given data is a valid Quake pak archive *******************************************************************/ bool PakArchive::isPakArchive(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); // Byteswap values for big endian if needed dir_size = wxINT32_SWAP_ON_BE(dir_size); dir_offset = wxINT32_SWAP_ON_BE(dir_offset); // Check header if (pack[0] != 'P' || pack[1] != 'A' || pack[2] != 'C' || pack[3] != 'K') return false; // Check directory is sane if (dir_offset < 12 || (unsigned)(dir_offset + dir_size) > mc.getSize()) return false; // That'll do return true; }
/* ADatArchive::isADatArchive * Checks if the given data is a valid Anachronox dat archive *******************************************************************/ bool ADatArchive::isADatArchive(MemChunk& mc) { // Check it opened ok if (mc.getSize() < 16) return false; // Read dat header char magic[4]; long dir_offset; long dir_size; long version; mc.seek(0, SEEK_SET); mc.read(magic, 4); mc.read(&dir_offset, 4); mc.read(&dir_size, 4); mc.read(&version, 4); // Byteswap values for big endian if needed dir_size = wxINT32_SWAP_ON_BE(dir_size); dir_offset = wxINT32_SWAP_ON_BE(dir_offset); // Check version if (wxINT32_SWAP_ON_BE(version) != 9) return false; // Check header if (magic[0] != 'A' || magic[1] != 'D' || magic[2] != 'A' || magic[3] != 'T') return false; // Check directory is sane if (dir_offset < 16 || (unsigned)(dir_offset + dir_size) > mc.getSize()) return false; // That'll do return true; }
// ----------------------------------------------------------------------------- // Checks if the given data is a valid pod archive // ----------------------------------------------------------------------------- bool PodArchive::isPodArchive(MemChunk& mc) { // Check size for header if (mc.size() < 84) return false; // Read no. of files mc.seek(0, 0); uint32_t num_files; mc.read(&num_files, 4); // Read id char id[80]; mc.read(id, 80); // Check size for directory if (mc.size() < 84 + (num_files * 40)) return false; // Read directory and check offsets FileEntry entry; for (unsigned a = 0; a < num_files; a++) { mc.read(&entry, 40); if (entry.offset + entry.size > mc.size()) return false; } return true; }
/* GobArchive::isGobArchive * Checks if the given data is a valid Dark Forces gob archive *******************************************************************/ bool GobArchive::isGobArchive(MemChunk& mc) { // 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; // If it's passed to here it's probably a gob file return true; }
/* ResArchive::open * Reads res format data from a MemChunk * Returns true if successful, false otherwise *******************************************************************/ bool ResArchive::open(MemChunk& mc) { // Check data was given if (!mc.hasData()) return false; // Read res header uint32_t dir_size = 0; uint32_t dir_offset = 0; char magic[4] = ""; mc.seek(0, SEEK_SET); mc.read(&magic, 4); // "Res!" mc.read(&dir_offset, 4); // Offset to directory mc.read(&dir_size, 4); // No. of lumps in res // Byteswap values for big endian if needed dir_size = wxINT32_SWAP_ON_BE(dir_size); dir_offset = wxINT32_SWAP_ON_BE(dir_offset); // Check the header if (magic[0] != 'R' || magic[1] != 'e' || magic[2] != 's' || magic[3] != '!') { LOG_MESSAGE(1, "ResArchive::openFile: File %s has invalid header", filename); Global::error = "Invalid res header"; return false; } if (dir_size % RESDIRENTRYSIZE) { LOG_MESSAGE(1, "ResArchive::openFile: File %s has invalid directory size", filename); Global::error = "Invalid res directory size"; return false; } uint32_t num_lumps = dir_size / RESDIRENTRYSIZE; // 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); UI::setSplashProgressMessage("Reading res archive data"); if (!readDirectory(mc, dir_offset, num_lumps, getRoot())) return false; // Detect maps (will detect map entry types) UI::setSplashProgressMessage("Detecting maps"); detectMaps(); // Setup variables setMuted(false); setModified(false); announce("opened"); UI::setSplashProgressMessage(""); return true; }
void MapTextureManager::onAnnouncement(Announcer* announcer, string event_name, MemChunk& event_data) { // Only interested in the resource manager, // archive manager and palette chooser. if (announcer != theResourceManager && announcer != thePaletteChooser && announcer != theArchiveManager) return; // If the map's archive is being closed, // we need to close the map editor if (event_name == "archive_closing") { event_data.seek(0, SEEK_SET); int32_t ac_index; event_data.read(&ac_index, 4); if (theArchiveManager->getArchive(ac_index) == archive) { theMapEditor->Hide(); theMapEditor->mapEditor().clearMap(); archive = NULL; } } // If the resources have been updated if (event_name == "resources_updated") refreshResources(); if (event_name == "main_palette_changed") refreshResources(); }
// ----------------------------------------------------------------------------- // Handles announcements from any announcers listened to // ----------------------------------------------------------------------------- void MapTextureManager::onAnnouncement(Announcer* announcer, std::string_view event_name, MemChunk& event_data) { // Only interested in the resource manager, // archive manager and palette chooser. if (announcer != &App::resources() && announcer != theMainWindow->paletteChooser() && announcer != &App::archiveManager()) return; // If the map's archive is being closed, // we need to close the map editor if (event_name == "archive_closing") { event_data.seek(0, SEEK_SET); int32_t ac_index; event_data.read(&ac_index, 4); if (App::archiveManager().getArchive(ac_index) == archive_) { MapEditor::windowWx()->Hide(); MapEditor::editContext().clearMap(); archive_ = nullptr; } } // If the resources have been updated if (event_name == "resources_updated") refreshResources(); if (event_name == "main_palette_changed") refreshResources(); }
/* RffArchive::isRffArchive * Checks if the given data is a valid Duke Nukem 3D grp archive *******************************************************************/ bool RffArchive::isRffArchive(MemChunk& mc) { // Check size if (mc.getSize() < 12) 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) return false; // Compute total size 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)); uint32_t totalsize = 12 + num_lumps * sizeof(RFFLump); uint32_t size = 0; for (uint32_t a = 0; a < num_lumps; ++a) { totalsize += lumps[a].Size; } // Check if total size is correct if (totalsize > mc.getSize()) return false; // If it's passed to here it's probably a grp file return true; }
// ----------------------------------------------------------------------------- // Checks if the given data is a valid Nerve disk archive // ----------------------------------------------------------------------------- bool DiskArchive::isDiskArchive(MemChunk& mc) { // Check given data is valid size_t mcsize = mc.getSize(); if (mcsize < 80) return false; // Read disk header uint32_t num_entries; uint32_t size_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; // Read the directory for (uint32_t d = 0; d < num_entries; d++) { // Read entry info DiskEntry entry; mc.read(&entry, 72); // Byteswap if needed entry.length = wxUINT32_SWAP_ON_LE(entry.length); entry.offset = wxUINT32_SWAP_ON_LE(entry.offset); // Increase offset to make it relative to start of archive entry.offset += start_offset; // Check offset+size if (entry.offset + entry.length > mcsize) return false; } mc.read(&size_entries, 4); size_entries = wxUINT32_SWAP_ON_LE(size_entries); if (size_entries + start_offset != mcsize) return false; // That'll do return true; }
/* DatArchive::isDatArchive * Checks if the given data is a valid Shadowcaster dat archive *******************************************************************/ bool DatArchive::isDatArchive(MemChunk& mc) { // Read dat header mc.seek(0, SEEK_SET); uint16_t num_lumps; uint32_t dir_offset, junk; mc.read(&num_lumps, 2); // Size mc.read(&dir_offset, 4); // Directory offset mc.read(&junk, 4); // Unknown value num_lumps = wxINT16_SWAP_ON_BE(num_lumps); dir_offset = wxINT32_SWAP_ON_BE(dir_offset); junk = wxINT32_SWAP_ON_BE(junk); if (dir_offset >= mc.getSize()) return false; // Read the directory mc.seek(dir_offset, SEEK_SET); // 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 // 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); // The first lump should have a name (subsequent lumps need not have one). // Also, sanity check the values. if (nameofs == 0 || nameofs >= mc.getSize() || offset + size >= mc.getSize()) { return false; } size_t len = 1; size_t start = nameofs+dir_offset; // Sanity checks again. Make sure there is actually a name. if (start > mc.getSize() || mc[start] < 33) return false; for (size_t i = start; (mc[i] != 0 && i < mc.getSize()); ++i, ++len) { // Names should not contain garbage characters if (mc[i] < 32 || mc[i] > 126) return false; } // Let's be reasonable here. While names aren't limited, if it's too long, it's suspicious. if (len > 60) return false; return true; }
void onAnnouncement(Announcer* announcer, string event_name, MemChunk& event_data) { if (announcer != entry->getParent()) return; bool finished = false; // Entry removed if (event_name == "entry_removed") { int index; wxUIntPtr ptr; event_data.read(&index, sizeof(int)); event_data.read(&ptr, sizeof(wxUIntPtr)); if (wxUIntToPtr(ptr) == entry) finished = true; } if (finished) delete this; }
bool readImage(SImage& image, MemChunk& data, int index) { // Get info SImage::info_t info = getInfo(data, index); // Create image from data image.create(info.width, info.height, PALMASK); data.read(imageData(image), info.width * info.height, 0); image.fillAlpha(255); return true; }
bool ResArchive::isResArchive(MemChunk& mc, size_t& dir_offset, size_t& num_lumps) { // Check size if (mc.getSize() < 12) return false; // Check for "Res!" header if (!(mc[0] == 'R' && mc[1] == 'e' && mc[2] == 's' && mc[3] == '!')) return false; // Get number of lumps and directory offset uint32_t offset_offset = 0; uint32_t rel_offset = 0; uint32_t dir_size = 0; mc.seek(4, SEEK_SET); mc.read(&dir_offset, 4); mc.read(&dir_size, 4); // Byteswap values for big endian if needed dir_size = wxINT32_SWAP_ON_BE(dir_size); dir_offset = wxINT32_SWAP_ON_BE(dir_offset); // A&A contains nested resource files. The offsets are then always relative to // the top-level file. This causes problem with the embedded archive system // used by SLADE3. The solution is to compute the offset offset. :) offset_offset = dir_offset - (mc.getSize() - dir_size); rel_offset = dir_offset - offset_offset; // Check directory offset and size are both decent if (dir_size % RESDIRENTRYSIZE || (rel_offset + dir_size) > mc.getSize()) return false; num_lumps = dir_size / RESDIRENTRYSIZE; // Reset MemChunk (just in case) mc.seek(0, SEEK_SET); // If it's passed to here it's probably a res file return true; }
// ----------------------------------------------------------------------------- // Checks if the given data is a valid Duke Nukem 3D grp archive // ----------------------------------------------------------------------------- bool GrpArchive::isGrpArchive(MemChunk& mc) { // Check size if (mc.size() < 16) return false; // Get number of lumps uint32_t num_lumps = 0; char ken_magic[13] = ""; mc.seek(0, SEEK_SET); mc.read(ken_magic, 12); // "KenSilverman" mc.read(&num_lumps, 4); // No. of lumps in grp // Byteswap values for big endian if needed num_lumps = wxINT32_SWAP_ON_BE(num_lumps); // Null-terminate the magic header ken_magic[12] = 0; // Check the header if (!(S_CMP(wxString::From8BitData(ken_magic), "KenSilverman"))) return false; // Compute total size uint32_t totalsize = (1 + num_lumps) * 16; uint32_t size = 0; for (uint32_t a = 0; a < num_lumps; ++a) { mc.read(ken_magic, 12); mc.read(&size, 4); totalsize += size; } // Check if total size is correct if (totalsize > mc.size()) return false; // If it's passed to here it's probably a grp file return true; }
/* LfdArchive::isLfdArchive * Checks if the given data is a valid Dark Forces lfd archive *******************************************************************/ bool LfdArchive::isLfdArchive(MemChunk& mc) { // Check size if (mc.getSize() < 12) return false; // Check magic header if (mc[0] != 'R' || mc[1] != 'M' || mc[2] != 'A' || mc[3] != 'P') return false; // Get offset of first entry uint32_t dir_offset = 0; mc.seek(12, SEEK_SET); mc.read(&dir_offset, 4); dir_offset = wxINT32_SWAP_ON_BE(dir_offset) + 16; if (dir_offset % 16) return false; char type1[5]; char type2[5]; char name1[9]; char name2[9]; uint32_t len1; uint32_t len2; mc.read(type1, 4); type1[4] = 0; mc.read(name1, 8); name1[8] = 0; mc.read(&len1, 4); len1 = wxINT32_SWAP_ON_BE(len1); // Check size if ((unsigned)mc.getSize() < (dir_offset + 16 + len1)) return false; // Compare mc.seek(dir_offset, SEEK_SET); mc.read(type2, 4); type2[4] = 0; mc.read(name2, 8); name2[8] = 0; mc.read(&len2, 4); len2 = wxINT32_SWAP_ON_BE(len2); if (strcmp(type1, type2) || strcmp(name1, name2) || len1 != len2) return false; // If it's passed to here it's probably a lfd file return true; }
// ----------------------------------------------------------------------------- // Checks if the given data is a valid BZip2 archive // ----------------------------------------------------------------------------- bool BZip2Archive::isBZip2Archive(MemChunk& mc) { size_t size = mc.size(); if (size < 14) return false; // Read header uint8_t header[4]; mc.read(header, 4); // Check for BZip2 header (reject BZip1 headers) if (header[0] == 'B' && header[1] == 'Z' && header[2] == 'h' && (header[3] >= '1' && header[3] <= '9')) return true; return false; }
// ----------------------------------------------------------------------------- // Reads bzip2 format data from a MemChunk // Returns true if successful, false otherwise // ----------------------------------------------------------------------------- bool BZip2Archive::open(MemChunk& mc) { size_t size = mc.size(); if (size < 14) return false; // Read header uint8_t header[4]; mc.read(header, 4); // Check for BZip2 header (reject BZip1 headers) if (!(header[0] == 'B' && header[1] == 'Z' && header[2] == 'h' && (header[3] >= '1' && header[3] <= '9'))) return false; // Build name from filename string name = filename(false); wxFileName fn(name); if (!fn.GetExt().CmpNoCase("tbz") || !fn.GetExt().CmpNoCase("tb2") || !fn.GetExt().CmpNoCase("tbz2")) fn.SetExt("tar"); else if (!fn.GetExt().CmpNoCase("bz2")) fn.ClearExt(); name = fn.GetFullName(); // Let's create the entry setMuted(true); auto entry = std::make_shared<ArchiveEntry>(name, size); MemChunk xdata; if (Compression::bzip2Decompress(mc, xdata)) { entry->importMemChunk(xdata); } else { setMuted(false); return false; } rootDir()->addEntry(entry); EntryType::detectEntryType(entry.get()); entry->setState(ArchiveEntry::State::Unmodified); setMuted(false); setModified(false); announce("opened"); // Finish return true; }
/* ZipArchive::isZipArchive * Checks if the given data is a valid zip archive *******************************************************************/ bool ZipArchive::isZipArchive(MemChunk& mc) { // Check size if (mc.getSize() < sizeof(zip_file_header_t)) return false; // Read first file header zip_file_header_t header; mc.seek(0, SEEK_SET); mc.read(&header, sizeof(zip_file_header_t)); // Check header signature if (header.sig != 0x04034b50) return false; // The zip format is horrendous, so this will do for checking 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; }
/* 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; }
/* 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; }
/* 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; }
/* 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; }
/* 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; }
/* 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; }
// ----------------------------------------------------------------------------- // Reads grp format data from a MemChunk // Returns true if successful, false otherwise // ----------------------------------------------------------------------------- bool GrpArchive::open(MemChunk& mc) { // Check data was given if (!mc.hasData()) return false; // Read grp header uint32_t num_lumps = 0; char ken_magic[13] = ""; mc.seek(0, SEEK_SET); mc.read(ken_magic, 12); // "KenSilverman" mc.read(&num_lumps, 4); // No. of lumps in grp // Byteswap values for big endian if needed num_lumps = wxINT32_SWAP_ON_BE(num_lumps); // Null-terminate the magic header ken_magic[12] = 0; // Check the header if (!(S_CMP(wxString::FromAscii(ken_magic), "KenSilverman"))) { Log::error(S_FMT("GrpArchive::openFile: File %s has invalid header", filename_)); Global::error = "Invalid grp header"; return false; } // Stop announcements (don't want to be announcing modification due to entries being added etc) setMuted(true); // The header takes as much space as a directory entry uint32_t entryoffset = 16 * (1 + num_lumps); // Read the directory UI::setSplashProgressMessage("Reading grp archive data"); for (uint32_t d = 0; d < num_lumps; d++) { // Update splash window progress UI::setSplashProgress(((float)d / (float)num_lumps)); // Read lump info char name[13] = ""; uint32_t offset = entryoffset; uint32_t size = 0; mc.read(name, 12); // Name mc.read(&size, 4); // Size name[12] = '\0'; // Byteswap values for big endian if needed size = wxINT32_SWAP_ON_BE(size); // Increase offset of next entry by this entry's size entryoffset += size; // If the lump data goes past the end of the file, // the grpfile is invalid if (offset + size > mc.size()) { Log::error("GrpArchive::open: grp archive is invalid or corrupt"); Global::error = "Archive is invalid and/or corrupt"; setMuted(false); return false; } // Create & setup lump auto nlump = std::make_shared<ArchiveEntry>(wxString::FromAscii(name), size); nlump->setLoaded(false); nlump->exProp("Offset") = (int)offset; nlump->setState(ArchiveEntry::State::Unmodified); // Add to entry list rootDir()->addEntry(nlump); } // Detect all entry types MemChunk edata; UI::setSplashProgressMessage("Detecting entry types"); for (size_t a = 0; a < numEntries(); a++) { // Update splash window progress UI::setSplashProgress((((float)a / (float)num_lumps))); // Get entry auto entry = entryAt(a); // Read entry data if it isn't zero-sized if (entry->size() > 0) { // Read the entry data mc.exportMemChunk(edata, getEntryOffset(entry), entry->size()); 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(ArchiveEntry::State::Unmodified); } // Setup variables setMuted(false); setModified(false); announce("opened"); UI::setSplashProgressMessage(""); return true; }
// ----------------------------------------------------------------------------- // Reads Chasm bin format data from a MemChunk // Returns true if successful, false otherwise // ----------------------------------------------------------------------------- bool ChasmBinArchive::open(MemChunk& mc) { // Check given data is valid if (mc.getSize() < HEADER_SIZE) { return false; } // Read .bin header and check it char magic[4] = {}; mc.read(magic, sizeof magic); if (magic[0] != 'C' || magic[1] != 'S' || magic[2] != 'i' || magic[3] != 'd') { LOG_MESSAGE(1, "ChasmBinArchive::open: Opening failed, invalid header"); Global::error = "Invalid Chasm bin header"; return false; } // Stop announcements (don't want to be announcing modification due to entries being added etc) setMuted(true); uint16_t num_entries = 0; mc.read(&num_entries, sizeof num_entries); num_entries = wxUINT16_SWAP_ON_BE(num_entries); // Read the directory UI::setSplashProgressMessage("Reading Chasm bin archive data"); for (uint16_t i = 0; i < num_entries; ++i) { // Update splash window progress UI::setSplashProgress(static_cast<float>(i) / num_entries); // Read entry info char name[NAME_SIZE] = {}; mc.read(name, sizeof name); uint32_t size; mc.read(&size, sizeof size); size = wxUINT32_SWAP_ON_BE(size); uint32_t offset; mc.read(&offset, sizeof offset); offset = wxUINT32_SWAP_ON_BE(offset); // Check offset+size if (offset + size > mc.getSize()) { LOG_MESSAGE(1, "ChasmBinArchive::open: Bin archive is invalid or corrupt (entry goes past end of file)"); Global::error = "Archive is invalid and/or corrupt"; setMuted(false); return false; } // Convert Pascal to zero-terminated string memmove(name, name + 1, sizeof name - 1); name[sizeof name - 1] = '\0'; // Create entry ArchiveEntry* const entry = new ArchiveEntry(name, size); entry->exProp("Offset") = static_cast<int>(offset); entry->setLoaded(false); entry->setState(0); rootDir()->addEntry(entry); } // Detect all entry types UI::setSplashProgressMessage("Detecting entry types"); vector<ArchiveEntry*> all_entries; getEntryTreeAsList(all_entries); MemChunk edata; for (size_t i = 0; i < all_entries.size(); ++i) { // Update splash window progress UI::setSplashProgress(static_cast<float>(i) / num_entries); // Get entry ArchiveEntry* const entry = all_entries[i]; // Read entry data if it isn't zero-sized if (entry->getSize() > 0) { // Read the entry data mc.exportMemChunk(edata, static_cast<int>(entry->exProp("Offset")), entry->getSize()); entry->importMemChunk(edata); } // Detect entry type EntryType::detectEntryType(entry); fixBrokenWave(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; }
/* 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; }