// ----------------------------------------------------------------------------- // Swaps [entry1] and [entry2]. // Returns false if either entry is invalid or if both entries are not in the // same directory, true otherwise // ----------------------------------------------------------------------------- bool Archive::swapEntries(ArchiveEntry* entry1, ArchiveEntry* entry2) { // Abort if read only if (read_only_) return false; // Check both entries if (!checkEntry(entry1) || !checkEntry(entry2)) return false; // Check neither entry is locked if (entry1->isLocked() || entry2->isLocked()) return false; // Get their directory auto dir = entry1->parentDir(); // Error if no dir if (!dir) return false; // Check they are both in the same directory if (entry2->parentDir() != dir) { Log::error("Can't swap two entries in different directories"); return false; } // Get entry indices int i1 = dir->entryIndex(entry1); int i2 = dir->entryIndex(entry2); // Check indices if (i1 < 0 || i2 < 0) return false; // Create undo step if (UndoRedo::currentlyRecording()) UndoRedo::currentManager()->recordUndoStep(std::make_unique<EntrySwapUS>(dir, i1, i2)); // Swap entries dir->swapEntries(i1, i2); // Announce the swap announce("entries_swapped"); // Set modified setModified(true); // Return success return true; }
// ----------------------------------------------------------------------------- // Removes [entry] from the archive. // If [delete_entry] is true, the entry will also be deleted. // Returns true if the removal succeeded // ----------------------------------------------------------------------------- bool Archive::removeEntry(ArchiveEntry* entry) { // Abort if read only if (read_only_) return false; // Check entry if (!checkEntry(entry)) return false; // Check if entry is locked if (entry->isLocked()) return false; // Get its directory auto dir = entry->parentDir(); // Error if entry has no parent directory if (!dir) return false; // Create undo step if (UndoRedo::currentlyRecording()) UndoRedo::currentManager()->recordUndoStep(std::make_unique<EntryCreateDeleteUS>(false, entry)); // Get the entry index int index = dir->entryIndex(entry); // Announce (before actually removing in case entry is still needed) MemChunk mc; wxUIntPtr ptr = wxPtrToUInt(entry); mc.write(&index, sizeof(int)); mc.write(&ptr, sizeof(wxUIntPtr)); announce("entry_removing", mc); // Remove it from its directory bool ok = dir->removeEntry(index); // If it was removed ok if (ok) { // Announce removed announce("entry_removed", mc); // Update variables etc setModified(true); } return ok; }
// ----------------------------------------------------------------------------- // Renames [entry] with [name]. // Returns false if the entry was invalid, true otherwise // ----------------------------------------------------------------------------- bool Archive::renameEntry(ArchiveEntry* entry, string_view name) { // Abort if read only if (read_only_) return false; // Check entry if (!checkEntry(entry)) return false; // Check if entry is locked if (entry->isLocked()) return false; // Check for directory if (entry->type() == EntryType::folderType()) return renameDir(dir(entry->path(true)), name); // Announce (before actually renaming in case old name is still needed) MemChunk mc; int index = entryIndex(entry); wxUIntPtr ptr = wxPtrToUInt(entry); mc.write(&index, sizeof(int)); mc.write(&ptr, sizeof(wxUIntPtr)); announce("entry_renaming", mc); // Create undo step if (UndoRedo::currentlyRecording()) UndoRedo::currentManager()->recordUndoStep(std::make_unique<EntryRenameUS>(entry, name)); // Rename the entry entry->setName(name); entry->formatName(formatDesc()); entry->setState(ArchiveEntry::State::Modified, true); // Announce modification entryStateChanged(entry); return true; }
/* Archive::entryStateChanged * Updates the archive variables and announces if necessary that an * entry's state has changed *******************************************************************/ void Archive::entryStateChanged(ArchiveEntry* entry) { // Check the entry is valid and part of this archive if (!checkEntry(entry)) return; // Get the entry index and announce the change MemChunk mc(8); wxUIntPtr ptr = wxPtrToUInt(entry); uint32_t index = entryIndex(entry); mc.write(&index, sizeof(uint32_t)); mc.write(&ptr, sizeof(wxUIntPtr)); announce("entry_state_changed", mc); // If entry was set to unmodified, don't set the archive to modified if (entry->getState() == 0) return; // Set the archive state to modified setModified(true); }
// ----------------------------------------------------------------------------- // Extract all sub-images as individual PNGs // ----------------------------------------------------------------------------- bool GfxEntryPanel::extractAll() const { if (image()->size() < 2) return false; // Remember where we are int imgindex = image()->index(); auto parent = entry_->parent(); if (parent == nullptr) return false; int index = parent->entryIndex(entry_, entry_->parentDir()); wxString name = wxFileName(entry_->name()).GetName(); // Loop through subimages and get things done int pos = 0; for (int i = 0; i < image()->size(); ++i) { wxString newname = wxString::Format("%s_%i.png", name, i); Misc::loadImageFromEntry(image(), entry_, i); // Only process images that actually contain some pixels if (image()->width() && image()->height()) { auto newimg = parent->addNewEntry(newname.ToStdString(), index + pos + 1, entry_->parentDir()); if (newimg == nullptr) return false; SIFormat::getFormat("png")->saveImage(*image(), newimg->data(), &gfx_canvas_->palette()); EntryType::detectEntryType(newimg); pos++; } } // Reload image of where we were Misc::loadImageFromEntry(image(), entry_, imgindex); return true; }
/* DatArchive::detectNamespace * Returns the namespace that [entry] is within *******************************************************************/ string DatArchive::detectNamespace(ArchiveEntry* entry) { return detectNamespace(entryIndex(entry)); }
/* WadArchive::updateNamespaces * Updates the namespace list *******************************************************************/ void WadArchive::updateNamespaces() { // Clear current namespace info while (namespaces.size() > 0) namespaces.pop_back(); // Go through all entries for (unsigned a = 0; a < numEntries(); a++) { ArchiveEntry* entry = getRoot()->getEntry(a); // Check for namespace begin if (entry->getName().Matches("*_START")) { // Create new namespace wad_ns_pair_t ns(entry, NULL); string name = entry->getName(); ns.name = name.Left(name.Length() - 6).Lower(); ns.start_index = entryIndex(ns.start); // Convert some special cases (because technically PP_START->P_END is a valid namespace) if (ns.name == "pp") ns.name = "p"; if (ns.name == "ff") ns.name = "f"; if (ns.name == "ss") ns.name = "s"; if (ns.name == "tt") ns.name = "t"; // Add to namespace list namespaces.push_back(ns); } // Check for namespace end else if (entry->getName().Matches("?_END") || entry->getName().Matches("??_END")) { // Get namespace 'name' int len = entry->getName().Length() - 4; string ns_name = entry->getName().Left(len).Lower(); // Convert some special cases (because technically P_START->PP_END is a valid namespace) if (ns_name == "pp") ns_name = "p"; if (ns_name == "ff") ns_name = "f"; if (ns_name == "ss") ns_name = "s"; if (ns_name == "tt") ns_name = "t"; // Check if it's the end of an existing namespace // Remember entry is getEntry(a)? index is 'a' //size_t index = entryIndex(entry); bool found = false; for (unsigned b = 0; b < namespaces.size(); b++) { // Can't close a namespace that starts afterwards if (namespaces[b].start_index > a) break; // Can't close an already-closed namespace if (namespaces[b].end != NULL) continue; if (S_CMP(ns_name, namespaces[b].name)) { found = true; namespaces[b].end = entry; namespaces[b].end_index = a; break; } } // Flat hack: closing the flat namespace without opening it if (found == false && ns_name == "f") { wad_ns_pair_t ns(getRoot()->getEntry(0), entry); ns.start_index = 0; ns.end_index = a; ns.name = "f"; namespaces.push_back(ns); } } } // ROTT stuff. The first lump in the archive is always WALLSTRT, the last lump is either // LICENSE (darkwar.wad) or VENDOR (huntbgin.wad), with TABLES just before in both cases. // The shareware version has 2091 lumps, the complete version has about 50% more. if (numEntries() > 2090 && getRoot()->getEntry(0)->getName().Matches("WALLSTRT") && getRoot()->getEntry(numEntries()-2)->getName().Matches("TABLES")) { wad_ns_pair_t ns(getRoot()->getEntry(0), getRoot()->getEntry(numEntries()-1)); ns.name = "rott"; ns.start_index = 0; ns.end_index = entryIndex(ns.end); namespaces.push_back(ns); } // Check namespaces for (unsigned a = 0; a < namespaces.size(); a++) { wad_ns_pair_t& ns = namespaces[a]; // Check the namespace has an end if (!ns.end) { // If not, remove the namespace as it is invalid namespaces.erase(namespaces.begin() + a); a--; continue; } // Check namespace name for special cases for (int n = 0; n < n_special_namespaces; n++) { if (S_CMP(ns.name, special_namespaces[n].letter)) ns.name = special_namespaces[n].name; } ns.start_index = entryIndex(ns.start); ns.end_index = entryIndex(ns.end); // Testing //wxLogMessage("Namespace %s from %s (%d) to %s (%d)", ns.name, // ns.start->getName(), ns.start_index, ns.end->getName(), ns.end_index); } }