/* 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; }
/* DatArchive::open * Reads wad format data from a MemChunk * Returns true if successful, false otherwise *******************************************************************/ bool DatArchive::open(MemChunk& mc) { // Check data was given if (!mc.hasData()) return false; const uint8_t* mcdata = mc.getData(); // Read dat header mc.seek(0, SEEK_SET); uint16_t num_lumps; uint32_t dir_offset, unknown; mc.read(&num_lumps, 2); // Size mc.read(&dir_offset, 4); // Directory offset mc.read(&unknown, 4); // Unknown value num_lumps = wxINT16_SWAP_ON_BE(num_lumps); dir_offset = wxINT32_SWAP_ON_BE(dir_offset); unknown = wxINT32_SWAP_ON_BE(unknown); string lastname(wxString::FromAscii("-noname-")); size_t namecount = 0; // Stop announcements (don't want to be announcing modification due to entries being added etc) setMuted(true); // Read the directory mc.seek(dir_offset, SEEK_SET); theSplashWindow->setProgressMessage("Reading dat archive data"); for (uint32_t d = 0; d < num_lumps; d++) { // Update splash window progress theSplashWindow->setProgress(((float)d / (float)num_lumps)); // Read lump info uint32_t offset = 0; uint32_t size = 0; uint16_t nameofs = 0; uint16_t flags = 0; mc.read(&offset, 4); // Offset mc.read(&size, 4); // Size mc.read(&nameofs, 2); // Name offset mc.read(&flags, 2); // Flags (only one: RLE encoded) // Byteswap values for big endian if needed offset = wxINT32_SWAP_ON_BE(offset); size = wxINT32_SWAP_ON_BE(size); nameofs = wxINT16_SWAP_ON_BE(nameofs); flags = wxINT16_SWAP_ON_BE(flags); // If the lump data goes past the directory, // the data file is invalid if (offset + size > mc.getSize()) { wxLogMessage("DatArchive::open: Dat archive is invalid or corrupt at entry %i", d); Global::error = "Archive is invalid and/or corrupt"; setMuted(false); return false; } string myname; if (nameofs != 0) { size_t len = 1; size_t start = nameofs+dir_offset; for (size_t i = start; mcdata[i] != 0; ++i) { ++len; } lastname = myname = wxString::FromAscii(mcdata+start, len); namecount = 0; } else { myname = S_FMT("%s+%d", lastname, ++namecount); } // Create & setup lump ArchiveEntry* nlump = new ArchiveEntry(myname, size); nlump->setLoaded(false); nlump->exProp("Offset") = (int)offset; nlump->setState(0); if (flags & 1) nlump->setEncryption(ENC_SCRLE0); // Check for markers if (!nlump->getName().Cmp("startflats")) flats[0] = d; if (!nlump->getName().Cmp("endflats")) flats[1] = d; if (!nlump->getName().Cmp("startsprites")) sprites[0] = d; if (!nlump->getName().Cmp("endmonsters")) sprites[1] = d; if (!nlump->getName().Cmp("startwalls")) walls[0] = d; if (!nlump->getName().Cmp("endwalls")) walls[1] = d; // Add to entry list getRoot()->addEntry(nlump); } // Detect all entry types MemChunk edata; theSplashWindow->setProgressMessage("Detecting entry types"); for (size_t a = 0; a < numEntries(); a++) { // Update splash window progress theSplashWindow->setProgress((((float)a / (float)num_lumps))); // Get entry ArchiveEntry* entry = getEntry(a); // Read entry data if it isn't zero-sized if (entry->getSize() > 0) { // Read the entry data mc.exportMemChunk(edata, getEntryOffset(entry), entry->getSize()); entry->importMemChunk(edata); } // Detect entry type EntryType::detectEntryType(entry); // Set entry to unchanged entry->setState(0); } // Detect maps (will detect map entry types) //theSplashWindow->setProgressMessage("Detecting maps"); //detectMaps(); // Setup variables setMuted(false); setModified(false); announce("opened"); theSplashWindow->setProgressMessage(""); return true; }
/* WadArchive::open * Reads wad format data from a MemChunk * Returns true if successful, false otherwise *******************************************************************/ bool WadArchive::open(MemChunk& mc) { // Check data was given if (!mc.hasData()) return false; // Read wad header uint32_t num_lumps = 0; uint32_t dir_offset = 0; char wad_type[4] = ""; mc.seek(0, SEEK_SET); mc.read(&wad_type, 4); // Wad type mc.read(&num_lumps, 4); // No. of lumps in wad mc.read(&dir_offset, 4); // Offset to directory // Byteswap values for big endian if needed num_lumps = wxINT32_SWAP_ON_BE(num_lumps); dir_offset = wxINT32_SWAP_ON_BE(dir_offset); // Check the header if (wad_type[1] != 'W' || wad_type[2] != 'A' || wad_type[3] != 'D') { wxLogMessage("WadArchive::openFile: File %s has invalid header", filename); Global::error = "Invalid wad header"; return false; } // Check for iwad if (wad_type[0] == 'I') iwad = true; // Stop announcements (don't want to be announcing modification due to entries being added etc) setMuted(true); vector<uint32_t> offsets; // Read the directory mc.seek(dir_offset, SEEK_SET); theSplashWindow->setProgressMessage("Reading wad archive data"); for (uint32_t d = 0; d < num_lumps; d++) { // Update splash window progress theSplashWindow->setProgress(((float)d / (float)num_lumps)); // Read lump info char name[9] = ""; uint32_t offset = 0; uint32_t size = 0; mc.read(&offset, 4); // Offset mc.read(&size, 4); // Size mc.read(name, 8); // Name name[8] = '\0'; // Byteswap values for big endian if needed offset = wxINT32_SWAP_ON_BE(offset); size = wxINT32_SWAP_ON_BE(size); // Check to catch stupid shit if (size > 0) { if (offset == 0) { LOG_MESSAGE(2, "No."); continue; } if (VECTOR_EXISTS(offsets, offset)) { LOG_MESSAGE(1, "Ignoring entry %d: %s, is a clone of a previous entry", d, name); continue; } offsets.push_back(offset); } // Hack to open Operation: Rheingold WAD files if (size == 0 && offset > mc.getSize()) offset = 0; // Is there a compression/encryption thing going on? bool jaguarencrypt = !!(name[0] & 0x80); // look at high bit name[0] = name[0] & 0x7F; // then strip it away // Look for encryption shenanigans size_t actualsize = size; if (jaguarencrypt) { if (d < num_lumps - 1) { size_t pos = mc.currentPos(); uint32_t nextoffset = 0; for (int i = 0; i + d < num_lumps; ++i) { mc.read(&nextoffset, 4); if (nextoffset != 0) break; mc.seek(12, SEEK_CUR); } nextoffset = wxINT32_SWAP_ON_BE(nextoffset); if (nextoffset == 0) nextoffset = dir_offset; mc.seek(pos, SEEK_SET); actualsize = nextoffset - offset; } else { if (offset > dir_offset) { actualsize = mc.getSize() - offset; } else { actualsize = dir_offset - offset; } } } // If the lump data goes past the end of the file, // the wadfile is invalid if (offset + actualsize > mc.getSize()) { wxLogMessage("WadArchive::open: Wad archive is invalid or corrupt"); Global::error = S_FMT("Archive is invalid and/or corrupt (lump %d: %s data goes past end of file)", d, name); setMuted(false); return false; } // Create & setup lump ArchiveEntry* nlump = new ArchiveEntry(wxString::FromAscii(name), size); nlump->setLoaded(false); nlump->exProp("Offset") = (int)offset; nlump->setState(0); if (jaguarencrypt) { nlump->setEncryption(ENC_JAGUAR); nlump->exProp("FullSize") = (int)size; } // Add to entry list getRoot()->addEntry(nlump); } // Detect namespaces (needs to be done before type detection as some types // rely on being within certain namespaces) updateNamespaces(); // Detect all entry types MemChunk edata; theSplashWindow->setProgressMessage("Detecting entry types"); for (size_t a = 0; a < numEntries(); a++) { // Update splash window progress theSplashWindow->setProgress((((float)a / (float)numEntries()))); // Get entry ArchiveEntry* entry = getEntry(a); // Read entry data if it isn't zero-sized if (entry->getSize() > 0) { // Read the entry data mc.exportMemChunk(edata, getEntryOffset(entry), entry->getSize()); if (entry->isEncrypted()) { if (entry->exProps().propertyExists("FullSize") && (unsigned)(int)(entry->exProp("FullSize")) > entry->getSize()) edata.reSize((int)(entry->exProp("FullSize")), true); if (!JaguarDecode(edata)) wxLogMessage("%i: %s (following %s), did not decode properly", a, entry->getName(), a>0?getEntry(a-1)->getName():"nothing"); } entry->importMemChunk(edata); } // Detect entry type EntryType::detectEntryType(entry); // Unload entry data if needed if (!archive_load_data) entry->unloadData(); // Set entry to unchanged entry->setState(0); } // Identify #included lumps (DECORATE, GLDEFS, etc.) detectIncludes(); // Detect maps (will detect map entry types) theSplashWindow->setProgressMessage("Detecting maps"); detectMaps(); // Setup variables setMuted(false); setModified(false); //if (iwad && iwad_lock) read_only = true; announce("opened"); theSplashWindow->setProgressMessage(""); return true; }