/* 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; }
/* 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; }
/* 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; }
/* 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; }
/* 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(); }
/* 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; }
/* DecodeTXB * TXB files are text files with a bit shift xor cipher. It makes an exception * for the newline character probably so that standard string functions will * continue to work. As an extension we also except the encoded version of 0xA * in order to produce a lossless conversion. This allows us to semi-effectively * handle this at the archive level instead of as a filter at the text editor. *******************************************************************/ void DecodeTXB(MemChunk &mc) { const uint8_t* data = mc.getData(); const uint8_t* const dataend = data + mc.getSize(); uint8_t* odata = new uint8_t[mc.getSize()]; uint8_t* const ostart = odata; while (data != dataend) { if (*data != 0xA && *data != 0x8F) { *odata++ = (((*data&0x3F)<<2)|((*data&0xC0)>>6))^0xA7; ++data; } else
/* AudioEntryPanel::openMod * Opens a Module file for playback *******************************************************************/ bool AudioEntryPanel::openMod(MemChunk& data) { // Attempt to load the mod if (mod->loadFromMemory(data.getData(), data.getSize())) { audio_type = AUTYPE_MOD; // Enable playback controls slider_volume->Enable(); btn_play->Enable(); btn_pause->Enable(); btn_stop->Enable(); setAudioDuration(mod->getDuration().asMilliseconds()); return true; } else { // Disable playback controls slider_volume->Enable(); btn_play->Enable(); btn_pause->Enable(); btn_stop->Enable(); setAudioDuration(0); return false; } return false; }
/* HogArchive::isHogArchive * Checks if the given data is a valid Descent hog archive *******************************************************************/ bool HogArchive::isHogArchive(MemChunk& mc) { // Check size size_t size = mc.getSize(); if (size < 3) return false; // Check magic header if (mc[0] != 'D' || mc[1] != 'H' || mc[2] != 'F') return false; // Iterate through files to see if the size seems okay size_t offset = 3; while (offset < size) { // Enough room for the header? if (offset + 17 > size) return false; // Read entry size to compute next offset offset += 17 + READ_L32(mc, offset + 13); } // We should end on at exactly the end of the file return (offset == size); }
/* 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; }
// ---------------------------------------------------------------------------- // Tokenizer::openMem // // Opens text from a MemChunk [mc] // ---------------------------------------------------------------------------- bool Tokenizer::openMem(const MemChunk& mc, const string& source) { source_ = source; data_.assign(mc.getData(), mc.getData() + mc.getSize()); reset(); return true; }
bool isThisFormat(MemChunk& mc) { FIMEMORY* mem = FreeImage_OpenMemory((BYTE*)mc.getData(), mc.getSize()); FREE_IMAGE_FORMAT fif = FreeImage_GetFileTypeFromMemory(mem, 0); FreeImage_CloseMemory(mem); if (fif == FIF_UNKNOWN) return false; else return true; }
/* ArchiveEntry::importMemChunk * Imports data from a MemChunk object into the entry, resizing it * and clearing any currently existing data. * Returns false if the MemChunk has no data, or true otherwise. *******************************************************************/ bool ArchiveEntry::importMemChunk(MemChunk& mc) { // Check that the given MemChunk has data if (mc.hasData()) { // Copy the data from the MemChunk into the entry return importMem(mc.getData(), mc.getSize()); } else return false; }
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; }
/* 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 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; }
/* 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; }
SImage::info_t getInfo(MemChunk& mc, int index) { SImage::info_t info; unsigned size = mc.getSize(); // Determine dimensions bool valid_size = false; for (uint32_t a = 0; a < n_valid_flat_sizes; a++) { uint32_t w = valid_flat_size[a][0]; uint32_t h = valid_flat_size[a][1]; if (size == w * h || size - 4 == w * h) { info.width = w; info.height = h; valid_size = true; break; } } if (size == 8776) // Inkworks and its signature at the end of COLORMAPS { size = 8704; } if (!valid_size) { if (size % 320 == 0) // This should handle any custom AUTOPAGE { info.width = 320; info.height = size / 320; } else if (size % 256 == 0) // This allows display of COLORMAPS { info.width = 256; info.height = size / 256; } } // Setup other info info.colformat = PALMASK; info.format = "raw"; return info; }
/* AudioEntryPanel::openAudio * Opens an audio file for playback (SFML 2.x+) *******************************************************************/ bool AudioEntryPanel::openAudio(MemChunk& audio, string filename) { // Stop if sound currently playing resetStream(); // (Re)create sound buffer if (sound_buffer) delete sound_buffer; sound_buffer = new sf::SoundBuffer(); audio_type = AUTYPE_INVALID; // Load into buffer if (sound_buffer->loadFromMemory((const char*)audio.getData(), audio.getSize())) { LOG_MESSAGE(3, "opened as sound"); // Bind to sound sound->setBuffer(*sound_buffer); audio_type = AUTYPE_SOUND; // Enable play controls #if (SFML_VERSION_MAJOR == 2 && SFML_VERSION_MINOR < 2) // SFML before 2.2 has a bug where it reports an incorrect value for long sounds, so compute it ourselves then setAudioDuration((sound_buffer->getSampleCount() / sound_buffer->getSampleRate())*(1000/sound_buffer->getChannelCount())); #else setAudioDuration(sound_buffer->getDuration().asMilliseconds()); #endif btn_play->Enable(); btn_pause->Enable(); btn_stop->Enable(); return true; } else if (music->openFromMemory((const char*)audio.getData(), audio.getSize())) { LOG_MESSAGE(3, "opened as music"); // Couldn't open the audio as a sf::SoundBuffer, try sf::Music instead audio_type = AUTYPE_MUSIC; // Enable play controls setAudioDuration(music->getDuration().asMilliseconds()); btn_play->Enable(); btn_stop->Enable(); return true; } else { // Couldn't open as sound or music, try the wxMediaCtrl LOG_MESSAGE(3, "opened as media"); // Dump audio to temp file audio.exportFile(filename); if (openMedia(filename)) return true; } // Unable to open audio, disable play controls setAudioDuration(0); btn_play->Enable(false); btn_pause->Enable(false); btn_stop->Enable(false); return false; }
/* 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; }
/* ADatArchive::write * Writes the dat archive to a MemChunk * Returns true if successful, false otherwise *******************************************************************/ bool ADatArchive::write(MemChunk& mc, bool update) { // Clear current data mc.clear(); MemChunk directory; MemChunk compressed; // Get archive tree as a list vector<ArchiveEntry*> entries; getEntryTreeAsList(entries); // Write header long dir_offset = wxINT32_SWAP_ON_BE(16); long dir_size = wxINT32_SWAP_ON_BE(0); char pack[4] = { 'A', 'D', 'A', 'T' }; uint32_t version = wxINT32_SWAP_ON_BE(9); mc.seek(0, SEEK_SET); mc.write(pack, 4); mc.write(&dir_offset, 4); mc.write(&dir_size, 4); mc.write(&version, 4); // Write entry data for (unsigned a = 0; a < entries.size(); a++) { // Skip folders if (entries[a]->getType() == EntryType::folderType()) continue; // Create compressed version of the lump MemChunk * entry = NULL; if (Compression::ZlibDeflate(entries[a]->getMCData(), compressed, 9)) { entry = &compressed; } else { entry = &(entries[a]->getMCData()); wxLogMessage("Entry %s couldn't be deflated", CHR(entries[a]->getName())); } // Update entry int offset = mc.currentPos(); if (update) { entries[a]->setState(0); entries[a]->exProp("Offset") = (int)offset; } /////////////////////////////////// // Step 1: Write directory entry // /////////////////////////////////// // Check entry name string name = entries[a]->getPath(true); name.Remove(0, 1); // Remove leading / if (name.Len() > 128) { wxLogMessage("Warning: Entry %s path is too long (> 128 characters), putting it in the root directory", CHR(name)); wxFileName fn(name); name = fn.GetFullName(); if (name.Len() > 128) name.Truncate(128); } // Write entry name char name_data[128]; memset(name_data, 0, 128); memcpy(name_data, CHR(name), name.Length()); directory.write(name_data, 128); // Write entry offset long myoffset = wxINT32_SWAP_ON_BE(offset); directory.write(&myoffset, 4); // Write full entry size long decsize = wxINT32_SWAP_ON_BE(entries[a]->getSize()); directory.write(&decsize, 4); // Write compressed entry size long compsize = wxINT32_SWAP_ON_BE(entry->getSize()); directory.write(&compsize, 4); // Write whatever it is that should be there // TODO: Reverse engineer what exactly it is // and implement something valid for the game. long whatever = 0; directory.write(&whatever, 4); ////////////////////////////// // Step 2: Write entry data // ////////////////////////////// mc.write(entry->getData(), entry->getSize()); } // Write directory dir_offset = wxINT32_SWAP_ON_BE(mc.currentPos()); dir_size = wxINT32_SWAP_ON_BE(directory.getSize()); mc.write(directory.getData(), directory.getSize()); // Update directory offset and size in header mc.seek(4, SEEK_SET); mc.write(&dir_offset, 4); mc.write(&dir_size, 4); // Yay! Finished! 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; }
bool isThisFormat(MemChunk& mc) { // Just check the size return validSize(mc.getSize()); }
/* 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; }
/* 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; }
/* 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; }
/* 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; }
// ----------------------------------------------------------------------------- // 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; }
/* 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; }