/* 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; }
/* 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; }
/* 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; }
/* 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; }
// ----------------------------------------------------------------------------- // Checks if the given data is a valid Dark Forces lfd archive // ----------------------------------------------------------------------------- bool LfdArchive::isLfdArchive(MemChunk& mc) { // Check size if (mc.size() < 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.size() < (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) != 0 || strcmp(name1, name2) != 0 || len1 != len2) return false; // If it's passed to here it's probably a lfd file return true; }
// ----------------------------------------------------------------------------- // Called when an announcement is recieved from one of the archives in the list // ----------------------------------------------------------------------------- void ArchiveManager::onAnnouncement(Announcer* announcer, const string& event_name, MemChunk& event_data) { // Reset event data for reading event_data.seek(0, SEEK_SET); // Check that the announcement came from an archive in the list int32_t index = archiveIndex((Archive*)announcer); if (index >= 0) { // If the archive was saved if (event_name == "saved") { MemChunk mc; mc.write(&index, 4); announce("archive_saved", mc); } // If the archive was modified if (event_name == "modified" || event_name == "entry_modified") { MemChunk mc; mc.write(&index, 4); announce("archive_modified", mc); } } }
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(); }
/* SwitchesEntryPanel::saveEntry * Saves any changes made to the entry *******************************************************************/ bool SwitchesEntryPanel::saveEntry() { MemChunk mc; mc.seek(0, SEEK_SET); switches_t swch; for (uint32_t a = 0; a < switches.nEntries(); a++) { SwitchesEntry* ent = switches.getEntry(a); for (size_t i = 0; i < 9; ++i) { if (ent->getOff().length() > i) swch.off[i] = ent->getOff()[i]; else swch.off[i] = 0; if (ent->getOn().length() > i) swch.on[i] = ent->getOn()[i]; else swch.on[i] = 0; } swch.type = ent->getType(); mc.write(&swch, 20); } memset(&swch, 0, 20); mc.write(&swch, 20); bool success = entry->importMemChunk(mc); if (success) { for (uint32_t a = 0; a < switches.nEntries(); a++) list_entries->setItemStatus(a, LV_STATUS_NORMAL); } return success; }
/* AnimatedEntryPanel::saveEntry * Saves any changes made to the entry *******************************************************************/ bool AnimatedEntryPanel::saveEntry() { MemChunk mc; mc.seek(0, SEEK_SET); animated_t anim; for (uint32_t a = 0; a < animated.nEntries(); a++) { AnimatedEntry* ent = animated.getEntry(a); for (size_t i = 0; i < 9; ++i) { if (ent->getFirst().length() > i) anim.first[i] = ent->getFirst()[i]; else anim.first[i] = 0; if (ent->getLast().length() > i) anim.last[i] = ent->getLast()[i]; else anim.last[i] = 0; } anim.speed = ent->getSpeed(); anim.type = ent->getType(); if (ent->getDecals()) anim.type |= ANIM_DECALS; mc.write(&anim, 23); } anim.type = 255; mc.write(&anim, 1); bool success = entry->importMemChunk(mc); if (success) { for (uint32_t a = 0; a < animated.nEntries(); a++) list_entries->setItemStatus(a, LV_STATUS_NORMAL); } return success; }
// ----------------------------------------------------------------------------- // 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; }
/* 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; }
// ----------------------------------------------------------------------------- // 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(); }
// ----------------------------------------------------------------------------- // Writes the wad archive to a MemChunk // Returns true if successful, false otherwise // ----------------------------------------------------------------------------- bool Wad2Archive::write(MemChunk& mc, bool update) { // Determine directory offset & individual lump offsets uint32_t dir_offset = 12; ArchiveEntry* entry = nullptr; for (uint32_t l = 0; l < numEntries(); l++) { entry = entryAt(l); entry->exProp("Offset") = (int)dir_offset; dir_offset += entry->size(); } // Clear/init MemChunk mc.clear(); mc.seek(0, SEEK_SET); mc.reSize(dir_offset + numEntries() * 32); // Setup wad type char wad_type[4] = { 'W', 'A', 'D', '2' }; if (wad3_) wad_type[3] = '3'; // Write the header uint32_t num_lumps = numEntries(); mc.write(wad_type, 4); mc.write(&num_lumps, 4); mc.write(&dir_offset, 4); // Write the lumps for (uint32_t l = 0; l < num_lumps; l++) { entry = entryAt(l); mc.write(entry->rawData(), entry->size()); } // Write the directory for (uint32_t l = 0; l < num_lumps; l++) { entry = entryAt(l); // Setup directory entry Wad2Entry info; memset(info.name, 0, 16); memcpy(info.name, CHR(entry->name()), entry->name().Len()); info.cmprs = (bool)entry->exProp("W2Comp"); info.dsize = entry->size(); info.size = entry->size(); info.offset = (int)entry->exProp("Offset"); info.type = (int)entry->exProp("W2Type"); // Write it mc.write(&info, 32); if (update) entry->setState(ArchiveEntry::State::Unmodified); } return true; }
/* 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; }
/* GobArchive::write * Writes the gob archive to a MemChunk * Returns true if successful, false otherwise *******************************************************************/ bool GobArchive::write(MemChunk& mc, bool update) { // Determine directory offset & individual lump offsets uint32_t dir_offset = 8; ArchiveEntry* entry = NULL; for (uint32_t l = 0; l < numEntries(); l++) { entry = getEntry(l); setEntryOffset(entry, dir_offset); dir_offset += entry->getSize(); } // Clear/init MemChunk mc.clear(); mc.seek(0, SEEK_SET); mc.reSize(dir_offset + 4 + numEntries() * 21); // Write the header uint32_t num_lumps = wxINT32_SWAP_ON_BE(numEntries()); dir_offset = wxINT32_SWAP_ON_BE(dir_offset); char header[4] = { 'G', 'O', 'B', 0xA }; mc.write(header, 4); mc.write(&dir_offset, 4); // Write the lumps for (uint32_t l = 0; l < numEntries(); l++) { entry = getEntry(l); mc.write(entry->getData(), entry->getSize()); } // Write the directory mc.write(&num_lumps, 4); for (uint32_t l = 0; l < numEntries(); l++) { entry = getEntry(l); char name[13] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; long offset = wxINT32_SWAP_ON_BE(getEntryOffset(entry)); long size = wxINT32_SWAP_ON_BE(entry->getSize()); for (size_t c = 0; c < entry->getName().length() && c < 13; c++) name[c] = entry->getName()[c]; mc.write(&offset, 4); mc.write(&size, 4); mc.write(name, 13); if (update) { entry->setState(0); entry->exProp("Offset") = (int)offset; } } return true; }
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; }
/* HogArchive::write * Writes the hog archive to a MemChunk * Returns true if successful, false otherwise *******************************************************************/ bool HogArchive::write(MemChunk& mc, bool update) { // Determine individual lump offsets uint32_t offset = 3; ArchiveEntry* entry = NULL; for (uint32_t l = 0; l < numEntries(); l++) { offset += 17; entry = getEntry(l); setEntryOffset(entry, offset); if (update) { entry->setState(0); entry->exProp("Offset") = (int)offset; } offset += entry->getSize(); } // Clear/init MemChunk mc.clear(); mc.seek(0, SEEK_SET); mc.reSize(offset); // Write the header char header[3] = { 'D', 'H', 'F' }; mc.write(header, 3); // Write the lumps for (uint32_t l = 0; l < numEntries(); l++) { entry = getEntry(l); mc.write(entry->getData(), entry->getSize()); } // Write the directory for (uint32_t l = 0; l < numEntries(); l++) { entry = getEntry(l); char name[13] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; long size = wxINT32_SWAP_ON_BE(entry->getSize()); for (size_t c = 0; c < entry->getName().length() && c < 13; c++) name[c] = entry->getName()[c]; mc.write(name, 13); mc.write(&size, 4); mc.write(entry->getData(), entry->getSize()); } return true; }
// ----------------------------------------------------------------------------- // 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; }
// ----------------------------------------------------------------------------- // Writes the grp archive to a MemChunk // Returns true if successful, false otherwise // ----------------------------------------------------------------------------- bool GrpArchive::write(MemChunk& mc, bool update) { // Clear/init MemChunk mc.clear(); mc.seek(0, SEEK_SET); mc.reSize((1 + numEntries()) * 16); ArchiveEntry* entry; // Write the header uint32_t num_lumps = numEntries(); mc.write("KenSilverman", 12); mc.write(&num_lumps, 4); // Write the directory for (uint32_t l = 0; l < num_lumps; l++) { entry = entryAt(l); char name[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; long size = entry->size(); for (size_t c = 0; c < entry->name().length() && c < 12; c++) name[c] = entry->name()[c]; mc.write(name, 12); mc.write(&size, 4); if (update) { long offset = getEntryOffset(entry); entry->setState(ArchiveEntry::State::Unmodified); entry->exProp("Offset") = (int)offset; } } // Write the lumps for (uint32_t l = 0; l < num_lumps; l++) { entry = entryAt(l); mc.write(entry->rawData(), entry->size()); } return true; }
// ----------------------------------------------------------------------------- // 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; }
bool PaletteEntryPanel::generateColormaps() { if (!entry || !entry->getParent() || ! palettes[0]) return false; MemChunk mc; SImage img; MemChunk imc; mc.reSize(34*256); mc.seek(0, SEEK_SET); imc.reSize(34*256*4); imc.seek(0, SEEK_SET); uint8_t rgba[4]; rgba[3] = 255; rgba_t rgb; float grey; // Generate 34 maps: the first 32 for diminishing light levels, // the 33th for the inverted grey map used by invulnerability. // The 34th colormap remains empty and black. for (size_t l = 0; l < 34; ++l) { for (size_t c = 0; c < 256; ++c) { rgb = palettes[0]->colour(c); if (l < 32) { // Generate light maps DIMINISH(rgb.r, l); DIMINISH(rgb.g, l); DIMINISH(rgb.b, l); #if (0) } else if (l == GREENMAP) { // Point of mostly useless trivia: the green "light amp" colormap in the Press Release beta // have colors that, on average, correspond to a bit less than (R*75/256, G*225/256, B*115/256) #endif } else if (l == GRAYMAP) { // Generate inverse map grey = ((float)rgb.r/256.0 * col_greyscale_r) + ((float)rgb.g/256.0 * col_greyscale_g) + ((float)rgb.b/256.0 * col_greyscale_b); grey = 1.0 - grey; // Clamp value: with Id Software's values, the sum is greater than 1.0 (0.299+0.587+0.144=1.030) // This means the negation above can give a negative value (for example, with RGB values of 247 or more), // which will not be converted correctly to unsigned 8-bit int in the rgba_t struct. if (grey < 0.0) grey = 0; rgb.r = rgb.g = rgb.b = grey*255; } else { // Fill with 0 rgb = palettes[0]->colour(0); } rgba[0] = rgb.r; rgba[1] = rgb.g; rgba[2] = rgb.b; imc.write(&rgba, 4); mc[(256*l)+c] = palettes[0]->nearestColour(rgb); } } #if 0 // Create truecolor image uint8_t* imd = new uint8_t[256*34*4]; memcpy(imd, imc.getData(), 256*34*4); img.setImageData(imd, 256, 34, RGBA); // imd will be freed by img's destructor ArchiveEntry* tcolormap; string name = entry->getName(true) + "-tcm.png"; tcolormap = new ArchiveEntry(name); if (tcolormap) { entry->getParent()->addEntry(tcolormap); SIFormat::getFormat("png")->saveImage(img, tcolormap->getMCData()); EntryType::detectEntryType(tcolormap); } #endif // Now override or create new entry ArchiveEntry* colormap; colormap = entry->getParent()->getEntry("COLORMAP", true); bool preexisting = colormap != NULL; if (!colormap) { // We need to create this entry colormap = new ArchiveEntry("COLORMAP.lmp", 34*256); } if (!colormap) return false; colormap->importMemChunk(mc); if (!preexisting) { entry->getParent()->addEntry(colormap); } return true; }
/* DatArchive::write * Writes the dat archive to a MemChunk * Returns true if successful, false otherwise *******************************************************************/ bool DatArchive::write(MemChunk& mc, bool update) { // Only two bytes are used for storing entry amount, // so abort for excessively large files: if (numEntries() > 65535) return false; // Determine directory offset, name offsets & individual lump offsets uint32_t dir_offset = 10; uint16_t name_offset = numEntries() * 12; uint32_t name_size = 0; string previousname = ""; uint16_t* nameoffsets = new uint16_t[numEntries()]; ArchiveEntry* entry = NULL; for (uint16_t l = 0; l < numEntries(); l++) { entry = getEntry(l); setEntryOffset(entry, dir_offset); dir_offset += entry->getSize(); // Does the entry has a name? string name = entry->getName(); if (l > 0 && previousname.length() > 0 && name.length() > previousname.length() && !previousname.compare(0, previousname.length(), name, 0, previousname.length()) && name.at(previousname.length()) == '+') { // This is a fake name name = ""; nameoffsets[l] = 0; } else { // This is a true name previousname = name; nameoffsets[l] = uint16_t(name_offset + name_size); name_size += name.length() + 1; } } // Clear/init MemChunk mc.clear(); mc.seek(0, SEEK_SET); mc.reSize(dir_offset + name_size + numEntries() * 12); // Write the header uint16_t num_lumps = wxINT16_SWAP_ON_BE(numEntries()); dir_offset = wxINT32_SWAP_ON_BE(dir_offset); uint32_t unknown = 0; mc.write(&num_lumps, 2); mc.write(&dir_offset, 4); mc.write(&unknown, 4); // Write the lumps for (uint16_t l = 0; l < numEntries(); l++) { entry = getEntry(l); mc.write(entry->getData(), entry->getSize()); } // Write the directory for (uint16_t l = 0; l < num_lumps; l++) { entry = getEntry(l); uint32_t offset = wxINT32_SWAP_ON_BE(getEntryOffset(entry)); uint32_t size = wxINT32_SWAP_ON_BE(entry->getSize()); uint16_t nameofs = wxINT16_SWAP_ON_BE(nameoffsets[l]); uint16_t flags = wxINT16_SWAP_ON_BE((entry->isEncrypted() == ENC_SCRLE0) ? 1 : 0); mc.write(&offset, 4); // Offset mc.write(&size, 4); // Size mc.write(&nameofs, 2); // Name offset mc.write(&flags, 2); // Flags if (update) { entry->setState(0); entry->exProp("Offset") = (int)wxINT32_SWAP_ON_BE(offset); } } // Write the names for (uint16_t l = 0; l < num_lumps; l++) { uint8_t zero = 0; entry = getEntry(l); if (nameoffsets[l]) { mc.write(CHR(entry->getName()), entry->getName().length()); mc.write(&zero, 1); } } // Clean-up delete[] nameoffsets; // Finished! return true; }
/* DatArchive::open * Reads wad format data from a MemChunk * Returns true if successful, false otherwise *******************************************************************/ bool DatArchive::open(MemChunk& mc) { // Check data was given if (!mc.hasData()) return false; const uint8_t* mcdata = mc.getData(); // Read dat header mc.seek(0, SEEK_SET); uint16_t num_lumps; uint32_t dir_offset, unknown; mc.read(&num_lumps, 2); // Size mc.read(&dir_offset, 4); // Directory offset mc.read(&unknown, 4); // Unknown value num_lumps = wxINT16_SWAP_ON_BE(num_lumps); dir_offset = wxINT32_SWAP_ON_BE(dir_offset); unknown = wxINT32_SWAP_ON_BE(unknown); string lastname(wxString::FromAscii("-noname-")); size_t namecount = 0; // Stop announcements (don't want to be announcing modification due to entries being added etc) setMuted(true); // Read the directory mc.seek(dir_offset, SEEK_SET); theSplashWindow->setProgressMessage("Reading dat archive data"); for (uint32_t d = 0; d < num_lumps; d++) { // Update splash window progress theSplashWindow->setProgress(((float)d / (float)num_lumps)); // Read lump info uint32_t offset = 0; uint32_t size = 0; uint16_t nameofs = 0; uint16_t flags = 0; mc.read(&offset, 4); // Offset mc.read(&size, 4); // Size mc.read(&nameofs, 2); // Name offset mc.read(&flags, 2); // Flags (only one: RLE encoded) // Byteswap values for big endian if needed offset = wxINT32_SWAP_ON_BE(offset); size = wxINT32_SWAP_ON_BE(size); nameofs = wxINT16_SWAP_ON_BE(nameofs); flags = wxINT16_SWAP_ON_BE(flags); // If the lump data goes past the directory, // the data file is invalid if (offset + size > mc.getSize()) { wxLogMessage("DatArchive::open: Dat archive is invalid or corrupt at entry %i", d); Global::error = "Archive is invalid and/or corrupt"; setMuted(false); return false; } string myname; if (nameofs != 0) { size_t len = 1; size_t start = nameofs+dir_offset; for (size_t i = start; mcdata[i] != 0; ++i) { ++len; } lastname = myname = wxString::FromAscii(mcdata+start, len); namecount = 0; } else { myname = S_FMT("%s+%d", lastname, ++namecount); } // Create & setup lump ArchiveEntry* nlump = new ArchiveEntry(myname, size); nlump->setLoaded(false); nlump->exProp("Offset") = (int)offset; nlump->setState(0); if (flags & 1) nlump->setEncryption(ENC_SCRLE0); // Check for markers if (!nlump->getName().Cmp("startflats")) flats[0] = d; if (!nlump->getName().Cmp("endflats")) flats[1] = d; if (!nlump->getName().Cmp("startsprites")) sprites[0] = d; if (!nlump->getName().Cmp("endmonsters")) sprites[1] = d; if (!nlump->getName().Cmp("startwalls")) walls[0] = d; if (!nlump->getName().Cmp("endwalls")) walls[1] = d; // Add to entry list getRoot()->addEntry(nlump); } // Detect all entry types MemChunk edata; theSplashWindow->setProgressMessage("Detecting entry types"); for (size_t a = 0; a < numEntries(); a++) { // Update splash window progress theSplashWindow->setProgress((((float)a / (float)num_lumps))); // Get entry ArchiveEntry* entry = getEntry(a); // Read entry data if it isn't zero-sized if (entry->getSize() > 0) { // Read the entry data mc.exportMemChunk(edata, getEntryOffset(entry), entry->getSize()); entry->importMemChunk(edata); } // Detect entry type EntryType::detectEntryType(entry); // Set entry to unchanged entry->setState(0); } // Detect maps (will detect map entry types) //theSplashWindow->setProgressMessage("Detecting maps"); //detectMaps(); // Setup variables setMuted(false); setModified(false); announce("opened"); theSplashWindow->setProgressMessage(""); return true; }
/* 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; }
// ----------------------------------------------------------------------------- // Writes Chasm bin archive to a MemChunk // Returns true if successful, false otherwise // ----------------------------------------------------------------------------- bool ChasmBinArchive::write(MemChunk& mc, bool update) { // Clear current data mc.clear(); // Get archive tree as a list vector<ArchiveEntry*> entries; getEntryTreeAsList(entries); // Check limit of entries count const uint16_t num_entries = static_cast<uint16_t>(entries.size()); if (num_entries > MAX_ENTRY_COUNT) { LOG_MESSAGE(1, "ChasmBinArchive::write: Bin archive can contain no more than %u entries", MAX_ENTRY_COUNT); Global::error = "Maximum number of entries exceeded for Chasm: The Rift bin archive"; return false; } // Init data size static const uint32_t HEADER_TOC_SIZE = HEADER_SIZE + ENTRY_SIZE * MAX_ENTRY_COUNT; mc.reSize(HEADER_TOC_SIZE, false); mc.fillData(0); // Write header const char magic[4] = { 'C', 'S', 'i', 'd' }; mc.seek(0, SEEK_SET); mc.write(magic, 4); mc.write(&num_entries, sizeof num_entries); // Write directory uint32_t offset = HEADER_TOC_SIZE; for (uint16_t i = 0; i < num_entries; ++i) { ArchiveEntry* const entry = entries[i]; // Update entry if (update) { entry->setState(0); entry->exProp("Offset") = static_cast<int>(offset); } // Check entry name string name = entry->getName(); uint8_t name_length = static_cast<uint8_t>(name.Length()); if (name_length > NAME_SIZE - 1) { LOG_MESSAGE(1, "Warning: Entry %s name is too long, it will be truncated", name); name.Truncate(NAME_SIZE - 1); name_length = static_cast<uint8_t>(NAME_SIZE - 1); } // Write entry name char name_data[NAME_SIZE] = {}; memcpy(name_data, &name_length, 1); memcpy(name_data + 1, CHR(name), name_length); mc.write(name_data, NAME_SIZE); // Write entry size const uint32_t size = entry->getSize(); mc.write(&size, sizeof size); // Write entry offset mc.write(&offset, sizeof offset); // Increment/update offset offset += size; } // Write entry data mc.reSize(offset); mc.seek(HEADER_TOC_SIZE, SEEK_SET); for (uint16_t i = 0; i < num_entries; ++i) { ArchiveEntry* const entry = entries[i]; mc.write(entry->getData(), entry->getSize()); } 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; }