/* ArchiveManager::addArchive * Adds an archive to the archive list *******************************************************************/ bool ArchiveManager::addArchive(Archive* archive) { // Only add if archive is a valid pointer if (archive) { // Add to the list archive_t n_archive; n_archive.archive = archive; n_archive.resource = true; open_archives.push_back(n_archive); // Listen to the archive listenTo(archive); // Announce the addition announce("archive_added"); // Add to resource manager theResourceManager->addArchive(archive); // ZDoom also loads any WADs found in the root of a PK3 or directory if ((archive->getType() == ARCHIVE_ZIP || archive->getType() == ARCHIVE_FOLDER) && auto_open_wads_root) { ArchiveTreeNode* root = archive->getRoot(); ArchiveEntry* entry; EntryType* type; for (unsigned a = 0; a < root->numEntries(); a++) { entry = root->getEntry(a); if (entry->getType() == EntryType::unknownType()) EntryType::detectEntryType(entry); type = entry->getType(); if (type->getId() == "wad") // First true: yes, manage this // Second true: open silently, don't open a tab for it openArchive(entry, true, true); } } return true; } else return false; }
/* EntryOperations::setGfxOffsets * Changes the offsets of the given gfx entry. Returns false if the * entry is invalid or not an offset-supported format, true otherwise *******************************************************************/ bool EntryOperations::setGfxOffsets(ArchiveEntry* entry, int x, int y) { if (entry == NULL || entry->getType() == NULL) return false; // Check entry type EntryType* type = entry->getType(); string entryformat = type->getFormat(); if (!(entryformat == "img_doom" || entryformat == "img_doom_arah" || entryformat == "img_doom_alpha" || entryformat == "img_doom_beta" || entryformat == "img_png")) { wxLogMessage("Entry \"%s\" is of type \"%s\" which does not support offsets", entry->getName(), entry->getType()->getName()); return false; } // Doom gfx format, normal and beta version. // Also arah format from alpha 0.2 because it uses the same header format. if (entryformat == "img_doom" || entryformat == "img_doom_beta" || entryformat == "image_doom_arah") { // Get patch header patch_header_t header; entry->seek(0, SEEK_SET); entry->read(&header, 8); // Apply new offsets header.left = wxINT16_SWAP_ON_BE((int16_t)x); header.top = wxINT16_SWAP_ON_BE((int16_t)y); // Write new header to entry entry->seek(0, SEEK_SET); entry->write(&header, 8); } // Doom alpha gfx format else if (entryformat == "img_doom_alpha") { // Get patch header entry->seek(0, SEEK_SET); oldpatch_header_t header; entry->read(&header, 4); // Apply new offsets header.left = (int8_t)x; header.top = (int8_t)y; // Write new header to entry entry->seek(0, SEEK_SET); entry->write(&header, 4); } // PNG format else if (entryformat == "img_png") { // Find existing grAb chunk const uint8_t* data = entry->getData(true); uint32_t grab_start = 0; for (uint32_t a = 0; a < entry->getSize(); a++) { // Check for 'grAb' header if (data[a] == 'g' && data[a + 1] == 'r' && data[a + 2] == 'A' && data[a + 3] == 'b') { grab_start = a - 4; break; } // Stop when we get to the 'IDAT' chunk if (data[a] == 'I' && data[a + 1] == 'D' && data[a + 2] == 'A' && data[a + 3] == 'T') break; } // Create new grAb chunk uint32_t csize = wxUINT32_SWAP_ON_LE(8); grab_chunk_t gc ={ { 'g', 'r', 'A', 'b' }, wxINT32_SWAP_ON_LE(x), wxINT32_SWAP_ON_LE(y) }; uint32_t dcrc = wxUINT32_SWAP_ON_LE(Misc::crc((uint8_t*)&gc, 12)); // Build new PNG from the original w/ the new grAb chunk MemChunk npng; uint32_t rest_start = 33; // Init new png data size if (grab_start == 0) npng.reSize(entry->getSize() + 20); else npng.reSize(entry->getSize()); // Write PNG header and IHDR chunk npng.write(data, 33); // If no existing grAb chunk was found, write new one here if (grab_start == 0) { npng.write(&csize, 4); npng.write(&gc, 12); npng.write(&dcrc, 4); } else { // Otherwise write any other data before the existing grAb chunk uint32_t to_write = grab_start - 33; npng.write(data + 33, to_write); rest_start = grab_start + 20; // And now write the new grAb chunk npng.write(&csize, 4); npng.write(&gc, 12); npng.write(&dcrc, 4); } // Write the rest of the PNG data uint32_t to_write = entry->getSize() - rest_start; npng.write(data + rest_start, to_write); // Load new png data to the entry entry->importMemChunk(npng); // Set its type back to png entry->setType(type); } else return false; return true; }
/* EntryType::loadEntryTypes * Loads all built-in and custom user entry types *******************************************************************/ bool EntryType::loadEntryTypes() { EntryDataFormat* fmt_any = EntryDataFormat::anyFormat(); // Setup unknown type etype_unknown.format = fmt_any; etype_unknown.icon = "e_unknown"; etype_unknown.detectable = false; etype_unknown.reliability = 0; etype_unknown.addToList(); // Setup folder type etype_folder.format = fmt_any; etype_folder.icon = "e_folder"; etype_folder.name = "Folder"; etype_folder.detectable = false; etype_folder.addToList(); // Setup marker type etype_marker.format = fmt_any; etype_marker.icon = "e_marker"; etype_marker.name = "Marker"; etype_marker.detectable = false; etype_marker.category = ""; // No category, markers only appear when 'All' categories shown etype_marker.addToList(); // Setup map marker type etype_map.format = fmt_any; etype_map.icon = "e_map"; etype_map.name = "Map Marker"; etype_map.category = "Maps"; // Should appear with maps etype_map.detectable = false; etype_map.colour = rgba_t(0, 255, 0); etype_map.addToList(); // -------- READ BUILT-IN TYPES --------- // Get builtin entry types from resource archive Archive* res_archive = theArchiveManager->programResourceArchive(); // Check resource archive exists if (!res_archive) { wxLogMessage("Error: No resource archive open!"); return false; } // Get entry types directory ArchiveTreeNode* et_dir = res_archive->getDir("config/entry_types/"); // Check it exists if (!et_dir) { wxLogMessage("Error: config/entry_types does not exist in slade.pk3"); return false; } // Read in each file in the directory bool etypes_read = false; for (unsigned a = 0; a < et_dir->numEntries(); a++) { if (readEntryTypeDefinition(et_dir->getEntry(a)->getMCData())) etypes_read = true; } // Warn if no types were read (this shouldn't happen unless the resource archive is corrupted) if (!etypes_read) wxLogMessage("Warning: No built-in entry types could be loaded from slade.pk3"); // -------- READ CUSTOM TYPES --------- // If the directory doesn't exist create it if (!wxDirExists(appPath("entry_types", DIR_USER))) wxMkdir(appPath("entry_types", DIR_USER)); // Open the custom palettes directory wxDir res_dir; res_dir.Open(appPath("entry_types", DIR_USER)); // Go through each file in the directory string filename = wxEmptyString; bool files = res_dir.GetFirst(&filename, wxEmptyString, wxDIR_FILES); while (files) { // Load file data MemChunk mc; mc.importFile(res_dir.GetName() + "/" + filename); // Parse file readEntryTypeDefinition(mc); // Next file files = res_dir.GetNext(&filename); } return true; }
/* EntryType::readEntryTypeDefinition * Reads in a block of entry type definitions. Returns false if there * was a parsing error, true otherwise *******************************************************************/ bool EntryType::readEntryTypeDefinition(MemChunk& mc) { // Parse the definition Parser p; p.parseText(mc); // Get entry_types tree ParseTreeNode* pt_etypes = (ParseTreeNode*)(p.parseTreeRoot()->getChild("entry_types")); // Check it exists if (!pt_etypes) return false; // Go through all parsed types for (unsigned a = 0; a < pt_etypes->nChildren(); a++) { // Get child as ParseTreeNode ParseTreeNode* typenode = (ParseTreeNode*)pt_etypes->getChild(a); // Create new entry type EntryType* ntype = new EntryType(typenode->getName().Lower()); // Copy from existing type if inherited if (!typenode->getInherit().IsEmpty()) { EntryType* parent_type = EntryType::getType(typenode->getInherit().Lower()); if (parent_type != EntryType::unknownType()) parent_type->copyToType(ntype); else wxLogMessage("Warning: Entry type %s inherits from unknown type %s", CHR(ntype->getId()), CHR(typenode->getInherit())); } // Go through all parsed fields for (unsigned b = 0; b < typenode->nChildren(); b++) { // Get child as ParseTreeNode ParseTreeNode* fieldnode = (ParseTreeNode*)typenode->getChild(b); // Process it if (S_CMPNOCASE(fieldnode->getName(), "name")) // Name field { ntype->name = fieldnode->getStringValue(); } else if (S_CMPNOCASE(fieldnode->getName(), "detectable")) // Detectable field { ntype->detectable = fieldnode->getBoolValue(); } else if (S_CMPNOCASE(fieldnode->getName(), "export_ext")) // Export Extension field { ntype->extension = fieldnode->getStringValue(); } else if (S_CMPNOCASE(fieldnode->getName(), "format")) // Format field { string format_string = fieldnode->getStringValue(); ntype->format = EntryDataFormat::getFormat(format_string); // Warn if undefined format if (ntype->format == EntryDataFormat::anyFormat()) wxLogMessage("Warning: Entry type %s requires undefined format %s", CHR(ntype->getId()), CHR(format_string)); } else if (S_CMPNOCASE(fieldnode->getName(), "icon")) // Icon field { ntype->icon = fieldnode->getStringValue(); } else if (S_CMPNOCASE(fieldnode->getName(), "editor")) // Editor field (to be removed) { ntype->editor = fieldnode->getStringValue(); } else if (S_CMPNOCASE(fieldnode->getName(), "section")) // Section field { ntype->section = fieldnode->getStringValue(); } else if (S_CMPNOCASE(fieldnode->getName(), "match_ext")) // Match Extension field { for (unsigned v = 0; v < fieldnode->nValues(); v++) ntype->match_extension.push_back(fieldnode->getStringValue(v).Lower()); } else if (S_CMPNOCASE(fieldnode->getName(), "match_name")) // Match Name field { for (unsigned v = 0; v < fieldnode->nValues(); v++) ntype->match_name.push_back(fieldnode->getStringValue(v).Lower()); } else if (S_CMPNOCASE(fieldnode->getName(), "match_extorname")) // Match name or extension { ntype->matchextorname = fieldnode->getBoolValue(); } else if (S_CMPNOCASE(fieldnode->getName(), "size")) // Size field { for (unsigned v = 0; v < fieldnode->nValues(); v++) ntype->match_size.push_back(fieldnode->getIntValue(v)); } else if (S_CMPNOCASE(fieldnode->getName(), "min_size")) // Min Size field { ntype->size_limit[0] = fieldnode->getIntValue(); } else if (S_CMPNOCASE(fieldnode->getName(), "max_size")) // Max Size field { ntype->size_limit[1] = fieldnode->getIntValue(); } else if (S_CMPNOCASE(fieldnode->getName(), "size_multiple")) // Size Multiple field { for (unsigned v = 0; v < fieldnode->nValues(); v++) ntype->size_multiple.push_back(fieldnode->getIntValue(v)); } else if (S_CMPNOCASE(fieldnode->getName(), "reliability")) // Reliability field { ntype->reliability = fieldnode->getIntValue(); } else if (S_CMPNOCASE(fieldnode->getName(), "match_archive")) // Archive field { for (unsigned v = 0; v < fieldnode->nValues(); v++) ntype->match_archive.push_back(fieldnode->getStringValue(v).Lower()); } else if (S_CMPNOCASE(fieldnode->getName(), "extra")) // Extra properties { for (unsigned v = 0; v < fieldnode->nValues(); v++) ntype->extra.addFlag(fieldnode->getStringValue(v)); } else if (S_CMPNOCASE(fieldnode->getName(), "category")) // Type category { ntype->category = fieldnode->getStringValue(); // Add to category list if needed bool exists = false; for (unsigned b = 0; b < entry_categories.size(); b++) { if (S_CMPNOCASE(entry_categories[b], ntype->category)) { exists = true; break; } } if (!exists) entry_categories.push_back(ntype->category); } else if (S_CMPNOCASE(fieldnode->getName(), "image_format")) // Image format hint ntype->extra["image_format"] = fieldnode->getStringValue(0); else if (S_CMPNOCASE(fieldnode->getName(), "colour")) // Colour { if (fieldnode->nValues() >= 3) ntype->colour = rgba_t(fieldnode->getIntValue(0), fieldnode->getIntValue(1), fieldnode->getIntValue(2)); else wxLogMessage("Not enough colour components defined for entry type %s", CHR(ntype->getId())); } else { // Unhandled properties can go into 'extra', only their first value is kept ntype->extra[fieldnode->getName()] = fieldnode->getStringValue(); } } //ntype->dump(); ntype->addToList(); } return true; }
Result match(EntryType const& entry) { return match(entry.chrom(), Region(entry.start(), entry.stop())); }