/* 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; }
bool writeImage(SImage& image, MemChunk& data, Palette* pal, int index) { // Can't write if RGBA if (image.getType() == RGBA) return false; // Check size if (!validSize(image.getWidth(), image.getHeight())) return false; // Just dump image data to memchunk data.clear(); data.write(imageData(image), image.getWidth() * image.getHeight()); 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; }
// ----------------------------------------------------------------------------- // 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; }
/* 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; }
/* TextEditor::getRawText * Writes the raw ASCII text to [mc] *******************************************************************/ void TextEditor::getRawText(MemChunk& mc) { mc.clear(); string text = GetText(); bool result = mc.importMem((const uint8_t*)text.ToUTF8().data(), text.ToUTF8().length()); }
/* 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; }
/* WadArchive::write * Writes the wad archive to a MemChunk * Returns true if successful, false otherwise *******************************************************************/ bool WadArchive::write(MemChunk& mc, bool update) { // Don't write if iwad if (iwad && iwad_lock) { Global::error = "IWAD saving disabled"; return false; } // Determine directory offset & individual lump offsets uint32_t dir_offset = 12; ArchiveEntry* entry = NULL; for (uint32_t l = 0; l < numEntries(); l++) { entry = getEntry(l); setEntryOffset(entry, dir_offset); dir_offset += entry->getSize(); } // Clear/init MemChunk mc.clear(); mc.seek(0, SEEK_SET); mc.reSize(dir_offset + numEntries() * 16); // Setup wad type char wad_type[4] = { 'P', 'W', 'A', 'D' }; if (iwad) wad_type[0] = 'I'; // Write the header uint32_t num_lumps = numEntries(); mc.write(wad_type, 4); mc.write(&num_lumps, 4); mc.write(&dir_offset, 4); // Write the lumps for (uint32_t l = 0; l < num_lumps; l++) { entry = getEntry(l); mc.write(entry->getData(), entry->getSize()); } // Write the directory for (uint32_t l = 0; l < num_lumps; l++) { entry = getEntry(l); char name[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; long offset = getEntryOffset(entry); long size = entry->getSize(); for (size_t c = 0; c < entry->getName().length() && c < 8; c++) name[c] = entry->getName()[c]; mc.write(&offset, 4); mc.write(&size, 4); mc.write(name, 8); if (update) { entry->setState(0); entry->exProp("Offset") = (int)offset; } } return true; }
/* GZipArchive::write * Writes the gzip archive to a MemChunk * Returns true if successful, false otherwise *******************************************************************/ bool GZipArchive::write(MemChunk& mc, bool update) { // Clear current data mc.clear(); if (numEntries() == 1) { MemChunk stream; if (Compression::GZipDeflate(getEntry(0)->getMCData(), stream, 9)) { const uint8_t* data = stream.getData(); uint32_t working = 0; size_t size = stream.getSize(); if (size < 18) return false; // zlib will have given us a minimal header, so we make our own uint8_t header[4]; header[0] = GZIP_ID1; header[1] = GZIP_ID2; header[2] = GZIP_DEFLATE; header[3] = flags; mc.write(header, 4); // Update mtime if the file was modified if (getEntry(0)->getState()) { mtime = ::wxGetLocalTime(); } // Write mtime working = wxUINT32_SWAP_ON_BE(mtime); mc.write(&working, 4); // Write other stuff mc.write(&xfl, 1); mc.write(&os, 1); // Any extra content that may have been there if (flags & GZIP_FLG_FXTRA) { uint16_t xlen = wxUINT16_SWAP_ON_BE(xtra.getSize()); mc.write(&xlen, 2); mc.write(xtra.getData(), xtra.getSize()); } // File name, if not extrapolated from archive name if (flags & GZIP_FLG_FNAME) { mc.write(CHR(getEntry(0)->getName()), getEntry(0)->getName().length()); uint8_t zero = 0; mc.write(&zero, 1); // Terminate string } // Comment, if there were actually one if (flags & GZIP_FLG_FCMNT) { mc.write(CHR(comment), comment.length()); uint8_t zero = 0; mc.write(&zero, 1); // Terminate string } // And finally, the half CRC, which we recalculate if (flags & GZIP_FLG_FHCRC) { uint32_t fullcrc = Misc::crc(mc.getData(), mc.getSize()); uint16_t hcrc = (fullcrc & 0x0000FFFF); hcrc = wxUINT16_SWAP_ON_BE(hcrc); mc.write(&hcrc, 2); } // Now that the pleasantries are dispensed with, // let's get with the meat of the matter return mc.write(data + 10, size - 10); } } return false; }
/* LfdArchive::write * Writes the lfd archive to a MemChunk * Returns true if successful, false otherwise *******************************************************************/ bool LfdArchive::write(MemChunk& mc, bool update) { // Determine total size uint32_t dir_size = (numEntries() + 1)<<4; uint32_t total_size = dir_size; ArchiveEntry* entry = NULL; for (uint32_t l = 0; l < numEntries(); l++) { entry = getEntry(l); total_size += 16; setEntryOffset(entry, total_size); if (update) { entry->setState(0); entry->exProp("Offset") = (int)total_size; } total_size += entry->getSize(); } // Clear/init MemChunk mc.clear(); mc.seek(0, SEEK_SET); mc.reSize(total_size); // Variables char type[5] = "RMAP"; char name[9] = "resource"; size_t size = wxINT32_SWAP_ON_BE(numEntries()<<4); // Write the resource map first mc.write(type, 4); mc.write(name, 8); mc.write(&size,4); for (uint32_t l = 0; l < numEntries(); l++) { entry = getEntry(l); for (int t = 0; t < 5; ++t) type[t] = 0; for (int n = 0; n < 9; ++n) name[n] = 0; size = wxINT32_SWAP_ON_BE(entry->getSize()); wxFileName fn(entry->getName()); for (size_t c = 0; c < fn.GetName().length() && c < 9; c++) name[c] = fn.GetName()[c]; for (size_t c = 0; c < fn.GetExt().length() && c < 5; c++) type[c] = fn.GetExt()[c]; mc.write(type, 4); mc.write(name, 8); mc.write(&size,4); } // Write the lumps for (uint32_t l = 0; l < numEntries(); l++) { entry = getEntry(l); for (int t = 0; t < 5; ++t) type[t] = 0; for (int n = 0; n < 9; ++n) name[n] = 0; size = wxINT32_SWAP_ON_BE(entry->getSize()); wxFileName fn(entry->getName()); for (size_t c = 0; c < fn.GetName().length() && c < 9; c++) name[c] = fn.GetName()[c]; for (size_t c = 0; c < fn.GetExt().length() && c < 5; c++) type[c] = fn.GetExt()[c]; mc.write(type, 4); mc.write(name, 8); mc.write(&size,4); mc.write(entry->getData(), entry->getSize()); } return true; }
/* PakArchive::write * Writes the pak archive to a MemChunk * Returns true if successful, false otherwise *******************************************************************/ bool PakArchive::write(MemChunk& mc, bool update) { // Clear current data mc.clear(); // Get archive tree as a list vector<ArchiveEntry*> entries; getEntryTreeAsList(entries); // Process entry list long dir_offset = 12; long dir_size = 0; for (unsigned a = 0; a < entries.size(); a++) { // Ignore folder entries if (entries[a]->getType() == EntryType::folderType()) continue; // Increment directory offset and size dir_offset += entries[a]->getSize(); dir_size += 64; } // Init data size mc.reSize(dir_offset + dir_size, false); // Write header char pack[4] = { 'P', 'A', 'C', 'K' }; mc.seek(0, SEEK_SET); mc.write(pack, 4); mc.write(&dir_offset, 4); mc.write(&dir_size, 4); // Write directory mc.seek(dir_offset, SEEK_SET); long offset = 12; for (unsigned a = 0; a < entries.size(); a++) { // Skip folders if (entries[a]->getType() == EntryType::folderType()) continue; // Update entry if (update) { entries[a]->setState(0); entries[a]->exProp("Offset") = (int)offset; } // Check entry name string name = entries[a]->getPath(true); name.Remove(0, 1); // Remove leading / if (name.Len() > 56) { wxLogMessage("Warning: Entry %s path is too long (> 56 characters), putting it in the root directory", name); wxFileName fn(name); name = fn.GetFullName(); if (name.Len() > 56) name.Truncate(56); } // Write entry name char name_data[56]; memset(name_data, 0, 56); memcpy(name_data, CHR(name), name.Length()); mc.write(name_data, 56); // Write entry offset mc.write(&offset, 4); // Write entry size long size = entries[a]->getSize(); mc.write(&size, 4); // Increment/update offset offset += size; } // Write entry data mc.seek(12, SEEK_SET); for (unsigned a = 0; a < entries.size(); a++) { // Skip folders if (entries[a]->getType() == EntryType::folderType()) continue; // Write data mc.write(entries[a]->getData(), entries[a]->getSize()); } return true; }
// ----------------------------------------------------------------------------- // Writes the pod archive to a MemChunk // Returns true if successful, false otherwise // ----------------------------------------------------------------------------- bool PodArchive::write(MemChunk& mc, bool update) { // Get all entries vector<ArchiveEntry*> entries; putEntryTreeAsList(entries); // Process entries int ndirs = 0; uint32_t data_size = 0; for (auto& entry : entries) { if (entry->type() == EntryType::folderType()) ndirs++; else data_size += entry->size(); } // Init MemChunk mc.clear(); mc.reSize(4 + 80 + (entries.size() * 40) + data_size, false); LOG_MESSAGE(5, "MC size %d", mc.size()); // Write no. entries uint32_t n_entries = entries.size() - ndirs; LOG_MESSAGE(5, "n_entries %d", n_entries); mc.write(&n_entries, 4); // Write id LOG_MESSAGE(5, "id %s", id_); mc.write(id_, 80); // Write directory FileEntry fe; fe.offset = 4 + 80 + (n_entries * 40); for (auto& entry : entries) { if (entry->type() == EntryType::folderType()) continue; // Name memset(fe.name, 0, 32); string path = entry->path(true); path.Replace("/", "\\"); path = path.AfterFirst('\\'); // LOG_MESSAGE(2, path); memcpy(fe.name, CHR(path), path.Len()); // Size fe.size = entry->size(); // Write directory entry mc.write(fe.name, 32); mc.write(&fe.size, 4); mc.write(&fe.offset, 4); LOG_MESSAGE( 5, "entry %s: old=%d new=%d size=%d", fe.name, entry->exProp("Offset").intValue(), fe.offset, entry->size()); // Next offset fe.offset += fe.size; } // Write entry data for (auto& entry : entries) if (entry->type() != EntryType::folderType()) mc.write(entry->rawData(), entry->size()); return true; }
// ----------------------------------------------------------------------------- // Writes the disk archive to a MemChunk. // Returns true if successful, false otherwise // ----------------------------------------------------------------------------- bool DiskArchive::write(MemChunk& mc, bool update) { // Clear current data mc.clear(); // Get archive tree as a list vector<ArchiveEntry*> entries; getEntryTreeAsList(entries); // Process entry list uint32_t num_entries = 0; uint32_t size_entries = 0; for (unsigned a = 0; a < entries.size(); a++) { // Ignore folder entries if (entries[a]->getType() == EntryType::folderType()) continue; // Increment directory offset and size size_entries += entries[a]->getSize(); ++num_entries; } // Init data size uint32_t start_offset = 8 + (num_entries * 72); uint32_t offset = start_offset; mc.reSize(size_entries + start_offset, false); // Write header num_entries = wxUINT32_SWAP_ON_LE(num_entries); mc.seek(0, SEEK_SET); mc.write(&num_entries, 4); // Write directory for (unsigned a = 0; a < entries.size(); a++) { // Skip folders if (entries[a]->getType() == EntryType::folderType()) continue; // Update entry if (update) { entries[a]->setState(0); entries[a]->exProp("Offset") = (int)offset; } // Check entry name string name = entries[a]->getPath(true); name.Replace("/", "\\"); // The leading "GAME:\" part of the name means there is only 58 usable characters for path if (name.Len() > 58) { LOG_MESSAGE( 1, "Warning: Entry %s path is too long (> 58 characters), putting it in the root directory", name); wxFileName fn(name); name = fn.GetFullName(); if (name.Len() > 57) name.Truncate(57); // Add leading "\" name = "\\" + name; } name = "GAME:" + name; DiskEntry dent; // Write entry name // The names field are padded with FD for doom.disk, FE for doom2.disk. No idea whether // a non-null padding is actually required, though. It probably should work with anything. memset(dent.name, 0xFE, 64); memcpy(dent.name, CHR(name), name.Length()); dent.name[name.Length()] = 0; // Write entry offset dent.offset = wxUINT32_SWAP_ON_LE(offset - start_offset); // Write entry size dent.length = wxUINT32_SWAP_ON_LE(entries[a]->getSize()); // Actually write stuff mc.write(&dent, 72); // Increment/update offset offset += wxUINT32_SWAP_ON_LE(dent.length); } // Finish writing header size_entries = wxUINT32_SWAP_ON_LE(size_entries); mc.write(&size_entries, 4); // Write entry data for (unsigned a = 0; a < entries.size(); a++) { // Skip folders if (entries[a]->getType() == EntryType::folderType()) continue; // Write data mc.write(entries[a]->getData(), entries[a]->getSize()); } return true; }