// ----------------------------------------------------------------------------- // 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; }
// ----------------------------------------------------------------------------- // Reads saved colour configuration [name] // ----------------------------------------------------------------------------- bool ColourConfiguration::readConfiguration(std::string_view name) { // TODO: search custom folder // Search resource pk3 auto res = App::archiveManager().programResourceArchive(); auto dir = res->dir("config/colours"); for (unsigned a = 0; a < dir->numEntries(); a++) { if (StrUtil::equalCI(dir->entryAt(a)->nameNoExt(), name)) return readConfiguration(dir->entryAt(a)->data()); } return false; }
// ----------------------------------------------------------------------------- // Writes the BZip2 archive to a MemChunk // Returns true if successful, false otherwise // ----------------------------------------------------------------------------- bool BZip2Archive::write(MemChunk& mc, bool update) { if (numEntries() == 1) return Compression::bzip2Compress(entryAt(0)->data(), mc); return false; }
// ----------------------------------------------------------------------------- // Returns all entries matching the search criteria in [options] // ----------------------------------------------------------------------------- vector<ArchiveEntry*> BZip2Archive::findAll(SearchOptions& options) { // Init search variables options.match_name = options.match_name.Lower(); vector<ArchiveEntry*> ret; if (findFirst(options)) ret.push_back(entryAt(0)); return ret; }
// ----------------------------------------------------------------------------- // 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; }
// ----------------------------------------------------------------------------- // Adds all available colour configuration names to [names] // ----------------------------------------------------------------------------- void ColourConfiguration::putConfigurationNames(vector<std::string>& names) { // TODO: search custom folder // Search resource pk3 auto res = App::archiveManager().programResourceArchive(); auto dir = res->dir("config/colours"); for (unsigned a = 0; a < dir->numEntries(); a++) names.emplace_back(dir->entryAt(a)->nameNoExt()); }
int Object::indexOf(const QString &key, bool *exists) { int min = 0; int n = length; while (n > 0) { int half = n >> 1; int middle = min + half; if (*entryAt(middle) >= key) { n = half; } else { min = middle + 1; n -= half + 1; } } if (min < (int)length && *entryAt(min) == key) { *exists = true; return min; } *exists = false; return min; }
bool doRedo() override { // Get entry parent dir auto dir = archive_->dir(entry_path_); if (dir) { // Rename entry auto entry = dir->entryAt(entry_index_); return archive_->renameEntry(entry, new_name_); } return false; }
QVariant PgnGameEntryModel::data(const QModelIndex& index, int role) const { if (!index.isValid()) return QVariant(); if (role == Qt::DisplayRole || role == Qt::EditRole) { PgnGameEntry::TagType tagType = PgnGameEntry::TagType(index.column()); return entryAt(index.row())->tagValue(tagType); } return QVariant(); }
// sets the value of each segment based on an int64 value; // does not constrain the value void NumericValControl::_set_value_fixed( int64 fixed) { // PRINT(( // "### NumericValControl::_set_value_fixed(%Ld)\n", fixed)); // constrain if(fixed > m_maxFixed) fixed = m_maxFixed; if(fixed < m_minFixed) fixed = m_minFixed; int64 scaleBase = m_fractionalDigits; // set segments for(int n = countEntries(); n > 0; --n) { const ValCtrlLayoutEntry& e = entryAt(n-1); if(e.type == ValCtrlLayoutEntry::SEGMENT_ENTRY) { ValControlDigitSegment* digitSegment = dynamic_cast<ValControlDigitSegment*>(e.pView); ASSERT(digitSegment); // PRINT(( // "\tsegment %d: %d digits at %d:\n", // n-1, // digitSegment->digitCount(), // digitSegment->scaleFactor())); // subtract higher-magnitude segments' value int64 hiCut = fixed % (int64)pow( 10, scaleBase + digitSegment->scaleFactor() + digitSegment->digitCount()); // PRINT(( // "\t [] %Ld\n", hiCut)); // shift value int64 segmentValue = hiCut / (int64)pow( 10, scaleBase + digitSegment->scaleFactor()); // PRINT(( // "\t -> %Ld\n\n", segmentValue)); // set digitSegment->setValue(segmentValue, fixed < 0); } } }
// ----------------------------------------------------------------------------- // Loads all text language definitions from slade.pk3 // ----------------------------------------------------------------------------- bool TextLanguage::loadLanguages() { // Get slade resource archive auto res_archive = App::archiveManager().programResourceArchive(); // Read language definitions from resource archive if (res_archive) { // Get 'config/languages' directly auto dir = res_archive->dir("config/languages"); if (dir) { // Read all entries in this dir for (unsigned a = 0; a < dir->numEntries(); a++) readLanguageDefinition(dir->entryAt(a)->data(), dir->entryAt(a)->name()); } else Log::warning( 1, "Warning: 'config/languages' not found in slade.pk3, no builtin text language definitions loaded"); } return true; }
bool Object::isValid() const { if (tableOffset + length*sizeof(offset) > size) return false; for (uint i = 0; i < length; ++i) { offset entryOffset = table()[i]; if (entryOffset + sizeof(Entry) >= tableOffset) return false; Entry *e = entryAt(i); int s = e->size(); if (table()[i] + s > tableOffset) return false; if (!e->value.isValid(this)) return false; } return true; }
// ----------------------------------------------------------------------------- // Returns the entry if it matches the search criteria in [options], // or null otherwise // ----------------------------------------------------------------------------- ArchiveEntry* BZip2Archive::findFirst(SearchOptions& options) { // Init search variables options.match_name = options.match_name.Lower(); auto entry = entryAt(0); if (entry == nullptr) return entry; // Check type if (options.match_type) { if (entry->type() == EntryType::unknownType()) { if (!options.match_type->isThisType(entry)) { return nullptr; } } else if (options.match_type != entry->type()) { return nullptr; } } // Check name if (!options.match_name.IsEmpty()) { if (!options.match_name.Matches(entry->name().Lower())) { return nullptr; } } // Entry passed all checks so far, so we found a match return entry; }
int64 NumericValControl::_value_fixed() const { // PRINT(( // "### NumericValControl::_value_fixed()\n", value)); int64 acc = 0LL; int64 scaleBase = m_fractionalDigits; // walk segments, adding the value of each for(int n = countEntries(); n > 0; --n) { const ValCtrlLayoutEntry& e = entryAt(n-1); if(e.type == ValCtrlLayoutEntry::SEGMENT_ENTRY) { const ValControlDigitSegment* digitSegment = dynamic_cast<ValControlDigitSegment*>(e.pView); ASSERT(digitSegment); // PRINT(( // "\t...segment %d: %d digits at %d: %Ld\n", // n-1, // digitSegment->digitCount(), // digitSegment->scaleFactor(), // digitSegment->value())); // acc += digitSegment->value() * (int64)pow( 10, scaleBase + digitSegment->scaleFactor()); // // PRINT(( // "\t-> %Ld\n\n", acc)); } } return acc; }
// ----------------------------------------------------------------------------- // 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; for (uint32_t l = 0; l < numEntries(); l++) { entry = entryAt(l); total_size += 16; setEntryOffset(entry, total_size); if (update) { entry->setState(ArchiveEntry::State::Unmodified); entry->exProp("Offset") = (int)total_size; } total_size += entry->size(); } // 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 = entryAt(l); for (char& t : type) t = 0; for (char& n : name) n = 0; size = wxINT32_SWAP_ON_BE(entry->size()); wxFileName fn(entry->name()); 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 = entryAt(l); for (char& t : type) t = 0; for (char& n : name) n = 0; size = wxINT32_SWAP_ON_BE(entry->size()); wxFileName fn(entry->name()); 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->rawData(), entry->size()); } return true; }
// ----------------------------------------------------------------------------- // Reads wad format data from a MemChunk // Returns true if successful, false otherwise // ----------------------------------------------------------------------------- bool Wad2Archive::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[0] != 'W' || wad_type[1] != 'A' || wad_type[2] != 'D' || (wad_type[3] != '2' && wad_type[3] != '3')) { Log::error("Wad2Archive::open: Invalid header"); Global::error = "Invalid wad2 header"; return false; } if (wad_type[3] == '3') wad3_ = true; // 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 wad archive data"); for (uint32_t d = 0; d < num_lumps; d++) { // Update splash window progress UI::setSplashProgress(((float)d / (float)num_lumps)); // Read lump info Wad2Entry info; mc.read(&info, 32); // Byteswap values for big endian if needed info.offset = wxINT32_SWAP_ON_BE(info.offset); info.size = wxINT32_SWAP_ON_BE(info.size); info.dsize = wxINT32_SWAP_ON_BE(info.dsize); // If the lump data goes past the end of the file, // the wadfile is invalid if ((unsigned)(info.offset + info.dsize) > mc.size()) { Log::error("Wad2Archive::open: Wad2 archive is invalid or corrupt"); Global::error = "Archive is invalid and/or corrupt"; setMuted(false); return false; } // Create & setup lump auto nlump = std::make_shared<ArchiveEntry>(wxString::FromAscii(info.name, 16), info.dsize); nlump->setLoaded(false); nlump->exProp("Offset") = (int)info.offset; nlump->exProp("W2Type") = info.type; nlump->exProp("W2Size") = (int)info.size; nlump->exProp("W2Comp") = !!(info.cmprs); nlump->setState(ArchiveEntry::State::Unmodified); // Add to entry list rootDir()->addEntry(nlump); } // Detect all entry types MemChunk edata; UI::setSplashProgressMessage("Detecting entry types"); for (size_t a = 0; a < numEntries(); a++) { // Update splash window progress UI::setSplashProgress((((float)a / (float)num_lumps))); // Get entry auto entry = entryAt(a); // Read entry data if it isn't zero-sized if (entry->size() > 0) { // Read the entry data mc.exportMemChunk(edata, (int)entry->exProp("Offset"), entry->size()); 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(ArchiveEntry::State::Unmodified); } // Detect maps (will detect map entry types) UI::setSplashProgressMessage("Detecting maps"); detectMaps(); // Setup variables setMuted(false); setModified(false); announce("opened"); UI::setSplashProgressMessage(""); return true; }
// ----------------------------------------------------------------------------- // Reads grp format data from a MemChunk // Returns true if successful, false otherwise // ----------------------------------------------------------------------------- bool GrpArchive::open(MemChunk& mc) { // Check data was given if (!mc.hasData()) return false; // Read grp header 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::FromAscii(ken_magic), "KenSilverman"))) { Log::error(S_FMT("GrpArchive::openFile: File %s has invalid header", filename_)); Global::error = "Invalid grp header"; return false; } // Stop announcements (don't want to be announcing modification due to entries being added etc) setMuted(true); // The header takes as much space as a directory entry uint32_t entryoffset = 16 * (1 + num_lumps); // Read the directory UI::setSplashProgressMessage("Reading grp archive data"); for (uint32_t d = 0; d < num_lumps; d++) { // Update splash window progress UI::setSplashProgress(((float)d / (float)num_lumps)); // Read lump info char name[13] = ""; uint32_t offset = entryoffset; uint32_t size = 0; mc.read(name, 12); // Name mc.read(&size, 4); // Size name[12] = '\0'; // Byteswap values for big endian if needed size = wxINT32_SWAP_ON_BE(size); // Increase offset of next entry by this entry's size entryoffset += size; // If the lump data goes past the end of the file, // the grpfile is invalid if (offset + size > mc.size()) { Log::error("GrpArchive::open: grp archive is invalid or corrupt"); Global::error = "Archive is invalid and/or corrupt"; setMuted(false); return false; } // Create & setup lump auto nlump = std::make_shared<ArchiveEntry>(wxString::FromAscii(name), size); nlump->setLoaded(false); nlump->exProp("Offset") = (int)offset; nlump->setState(ArchiveEntry::State::Unmodified); // Add to entry list rootDir()->addEntry(nlump); } // Detect all entry types MemChunk edata; UI::setSplashProgressMessage("Detecting entry types"); for (size_t a = 0; a < numEntries(); a++) { // Update splash window progress UI::setSplashProgress((((float)a / (float)num_lumps))); // Get entry auto entry = entryAt(a); // Read entry data if it isn't zero-sized if (entry->size() > 0) { // Read the entry data mc.exportMemChunk(edata, getEntryOffset(entry), entry->size()); 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(ArchiveEntry::State::Unmodified); } // Setup variables setMuted(false); setModified(false); announce("opened"); UI::setSplashProgressMessage(""); return true; }
// ----------------------------------------------------------------------------- // Returns the last entry matching the search criteria in [options], or null if // no matching entry was found // ----------------------------------------------------------------------------- ArchiveEntry* Archive::findLast(SearchOptions& options) { // Init search variables auto dir = options.dir; if (!dir) dir = &dir_root_; StrUtil::upperIP(options.match_name); // Force case-insensitive // Begin search // Search entries (bottom-up) for (int a = dir->numEntries() - 1; a >= 0; a--) { auto entry = dir->entryAt(a); // Check type if (options.match_type) { if (entry->type() == EntryType::unknownType()) { if (!options.match_type->isThisType(entry)) continue; } else if (options.match_type != entry->type()) continue; } // Check name if (!options.match_name.empty()) { // Cut extension if ignoring auto check_name = options.ignore_ext ? entry->upperNameNoExt() : entry->upperName(); if (!StrUtil::matches(check_name, options.match_name)) continue; } // Check namespace if (!options.match_namespace.empty()) { if (!StrUtil::equalCI(detectNamespace(entry), options.match_namespace)) continue; } // Entry passed all checks so far, so we found a match return entry; } // Search subdirectories (if needed) (bottom-up) if (options.search_subdirs) { for (int a = dir->nChildren() - 1; a >= 0; a--) { auto opt = options; opt.dir = (ArchiveTreeNode*)dir->child(a); auto match = findLast(opt); // If a match was found in this subdir, return it if (match) return match; } } // No matches found return nullptr; }
status_t StringPool::writeStringBlock(const sp<AaptFile>& pool) { // Allow appending. Sorry this is a little wacky. if (pool->getSize() > 0) { sp<AaptFile> block = createStringBlock(); if (block == NULL) { return UNKNOWN_ERROR; } ssize_t res = pool->writeData(block->getData(), block->getSize()); return (res >= 0) ? (status_t)NO_ERROR : res; } // First we need to add all style span names to the string pool. // We do this now (instead of when the span is added) so that these // will appear at the end of the pool, not disrupting the order // our client placed their own strings in it. const size_t STYLES = mEntryStyleArray.size(); size_t i; for (i=0; i<STYLES; i++) { entry_style& style = mEntryStyleArray.editItemAt(i); const size_t N = style.spans.size(); for (size_t i=0; i<N; i++) { entry_style_span& span = style.spans.editItemAt(i); ssize_t idx = add(span.name, true); if (idx < 0) { fprintf(stderr, "Error adding span for style tag '%s'\n", String8(span.name).string()); return idx; } span.span.name.index = (uint32_t)idx; } } const size_t ENTRIES = size(); // Now build the pool of unique strings. const size_t STRINGS = mEntries.size(); const size_t preSize = sizeof(ResStringPool_header) + (sizeof(uint32_t)*ENTRIES) + (sizeof(uint32_t)*STYLES); if (pool->editData(preSize) == NULL) { fprintf(stderr, "ERROR: Out of memory for string pool\n"); return NO_MEMORY; } const size_t charSize = mUTF8 ? sizeof(uint8_t) : sizeof(char16_t); size_t strPos = 0; for (i=0; i<STRINGS; i++) { entry& ent = mEntries.editItemAt(i); const size_t strSize = (ent.value.size()); const size_t lenSize = strSize > (size_t)(1<<((charSize*8)-1))-1 ? charSize*2 : charSize; String8 encStr; if (mUTF8) { encStr = String8(ent.value); } const size_t encSize = mUTF8 ? encStr.size() : 0; const size_t encLenSize = mUTF8 ? (encSize > (size_t)(1<<((charSize*8)-1))-1 ? charSize*2 : charSize) : 0; ent.offset = strPos; const size_t totalSize = lenSize + encLenSize + ((mUTF8 ? encSize : strSize)+1)*charSize; void* dat = (void*)pool->editData(preSize + strPos + totalSize); if (dat == NULL) { fprintf(stderr, "ERROR: Out of memory for string pool\n"); return NO_MEMORY; } dat = (uint8_t*)dat + preSize + strPos; if (mUTF8) { uint8_t* strings = (uint8_t*)dat; ENCODE_LENGTH(strings, sizeof(uint8_t), strSize) ENCODE_LENGTH(strings, sizeof(uint8_t), encSize) strncpy((char*)strings, encStr, encSize+1); } else { uint16_t* strings = (uint16_t*)dat; ENCODE_LENGTH(strings, sizeof(uint16_t), strSize) strcpy16_htod(strings, ent.value); } strPos += totalSize; } // Pad ending string position up to a uint32_t boundary. if (strPos&0x3) { size_t padPos = ((strPos+3)&~0x3); uint8_t* dat = (uint8_t*)pool->editData(preSize + padPos); if (dat == NULL) { fprintf(stderr, "ERROR: Out of memory padding string pool\n"); return NO_MEMORY; } memset(dat+preSize+strPos, 0, padPos-strPos); strPos = padPos; } // Build the pool of style spans. size_t styPos = strPos; for (i=0; i<STYLES; i++) { entry_style& ent = mEntryStyleArray.editItemAt(i); const size_t N = ent.spans.size(); const size_t totalSize = (N*sizeof(ResStringPool_span)) + sizeof(ResStringPool_ref); ent.offset = styPos-strPos; uint8_t* dat = (uint8_t*)pool->editData(preSize + styPos + totalSize); if (dat == NULL) { fprintf(stderr, "ERROR: Out of memory for string styles\n"); return NO_MEMORY; } ResStringPool_span* span = (ResStringPool_span*)(dat+preSize+styPos); for (size_t i=0; i<N; i++) { span->name.index = htodl(ent.spans[i].span.name.index); span->firstChar = htodl(ent.spans[i].span.firstChar); span->lastChar = htodl(ent.spans[i].span.lastChar); span++; } span->name.index = htodl(ResStringPool_span::END); styPos += totalSize; } if (STYLES > 0) { // Add full terminator at the end (when reading we validate that // the end of the pool is fully terminated to simplify error // checking). size_t extra = sizeof(ResStringPool_span)-sizeof(ResStringPool_ref); uint8_t* dat = (uint8_t*)pool->editData(preSize + styPos + extra); if (dat == NULL) { fprintf(stderr, "ERROR: Out of memory for string styles\n"); return NO_MEMORY; } uint32_t* p = (uint32_t*)(dat+preSize+styPos); while (extra > 0) { *p++ = htodl(ResStringPool_span::END); extra -= sizeof(uint32_t); } styPos += extra; } // Write header. ResStringPool_header* header = (ResStringPool_header*)pool->padData(sizeof(uint32_t)); if (header == NULL) { fprintf(stderr, "ERROR: Out of memory for string pool\n"); return NO_MEMORY; } memset(header, 0, sizeof(*header)); header->header.type = htods(RES_STRING_POOL_TYPE); header->header.headerSize = htods(sizeof(*header)); header->header.size = htodl(pool->getSize()); header->stringCount = htodl(ENTRIES); header->styleCount = htodl(STYLES); if (mSorted) { header->flags |= htodl(ResStringPool_header::SORTED_FLAG); } if (mUTF8) { header->flags |= htodl(ResStringPool_header::UTF8_FLAG); } header->stringsStart = htodl(preSize); header->stylesStart = htodl(STYLES > 0 ? (preSize+strPos) : 0); // Write string index array. uint32_t* index = (uint32_t*)(header+1); if (mSorted) { for (i=0; i<ENTRIES; i++) { entry& ent = const_cast<entry&>(entryAt(i)); ent.indices.clear(); ent.indices.add(i); *index++ = htodl(ent.offset); } } else { for (i=0; i<ENTRIES; i++) { entry& ent = mEntries.editItemAt(mEntryArray[i]); *index++ = htodl(ent.offset); NOISY(printf("Writing entry #%d: \"%s\" ent=%d off=%d\n", i, String8(ent.value).string(), mEntryArray[i], ent.offset)); } } // Write style index array. if (mSorted) { for (i=0; i<STYLES; i++) { LOG_ALWAYS_FATAL("Shouldn't be here!"); } } else { for (i=0; i<STYLES; i++) { *index++ = htodl(mEntryStyleArray[i].offset); } } return NO_ERROR; }
// ----------------------------------------------------------------------------- // Returns a list of entries matching the search criteria in [options] // ----------------------------------------------------------------------------- vector<ArchiveEntry*> Archive::findAll(SearchOptions& options) { // Init search variables auto dir = options.dir; if (!dir) dir = &dir_root_; vector<ArchiveEntry*> ret; StrUtil::upperIP(options.match_name); // Force case-insensitive // Begin search // Search entries for (unsigned a = 0; a < dir->numEntries(); a++) { auto entry = dir->entryAt(a); // Check type if (options.match_type) { if (entry->type() == EntryType::unknownType()) { if (!options.match_type->isThisType(entry)) continue; } else if (options.match_type != entry->type()) continue; } // Check name if (!options.match_name.empty()) { // Cut extension if ignoring auto check_name = options.ignore_ext ? entry->upperNameNoExt() : entry->upperName(); if (!StrUtil::matches(check_name, options.match_name)) continue; } // Check namespace if (!options.match_namespace.empty()) { if (!StrUtil::equalCI(detectNamespace(entry), options.match_namespace)) continue; } // Entry passed all checks so far, so we found a match ret.push_back(entry); } // Search subdirectories (if needed) if (options.search_subdirs) { for (unsigned a = 0; a < dir->nChildren(); a++) { auto opt = options; opt.dir = (ArchiveTreeNode*)dir->child(a); // Add any matches to the list auto vec = findAll(opt); ret.insert(ret.end(), vec.begin(), vec.end()); } } // Return matches return ret; }
bool deleteEntry() const { // Get parent dir auto dir = archive_->dir(path_); return dir ? archive_->removeEntry(dir->entryAt(index_)) : false; }
// ----------------------------------------------------------------------------- // Reads lfd format data from a MemChunk // Returns true if successful, false otherwise // ----------------------------------------------------------------------------- bool LfdArchive::open(MemChunk& mc) { // Check data was given if (!mc.hasData()) return false; // Check size if (mc.size() < 16) return false; // Check magic header if (mc[0] != 'R' || mc[1] != 'M' || mc[2] != 'A' || mc[3] != 'P') return false; // Get directory length uint32_t dir_len = 0; mc.seek(12, SEEK_SET); mc.read(&dir_len, 4); dir_len = wxINT32_SWAP_ON_BE(dir_len); // Check size if ((unsigned)mc.size() < (dir_len) || dir_len % 16) return false; // Guess number of lumps uint32_t num_lumps = dir_len / 16; // Stop announcements (don't want to be announcing modification due to entries being added etc) setMuted(true); // Read each entry UI::setSplashProgressMessage("Reading lfd archive data"); size_t offset = dir_len + 16; size_t size = mc.size(); for (uint32_t d = 0; offset < size; d++) { // Update splash window progress UI::setSplashProgress(((float)d / (float)num_lumps)); // Read lump info uint32_t length = 0; char type[5] = ""; char name[9] = ""; mc.read(type, 4); // Type mc.read(name, 8); // Name mc.read(&length, 4); // Size name[8] = '\0'; type[4] = 0; // Move past the header offset += 16; // Byteswap values for big endian if needed length = wxINT32_SWAP_ON_BE(length); // If the lump data goes past the end of the file, // the gobfile is invalid if (offset + length > size) { LOG_MESSAGE(1, "LfdArchive::open: lfd archive is invalid or corrupt"); Global::error = "Archive is invalid and/or corrupt"; setMuted(false); return false; } // Create & setup lump wxFileName fn(name); fn.SetExt(type); auto nlump = std::make_shared<ArchiveEntry>(fn.GetFullName(), length); nlump->setLoaded(false); nlump->exProp("Offset") = (int)offset; nlump->setState(ArchiveEntry::State::Unmodified); // Add to entry list rootDir()->addEntry(nlump); // Move to next entry offset += length; mc.seek(offset, SEEK_SET); } if (num_lumps != numEntries()) LOG_MESSAGE(1, "Warning: computed %i lumps, but actually %i entries", num_lumps, numEntries()); // Detect all entry types MemChunk edata; UI::setSplashProgressMessage("Detecting entry types"); for (size_t a = 0; a < numEntries(); a++) { // Update splash window progress UI::setSplashProgress((((float)a / (float)num_lumps))); // Get entry auto entry = entryAt(a); // Read entry data if it isn't zero-sized if (entry->size() > 0) { // Read the entry data mc.exportMemChunk(edata, getEntryOffset(entry), entry->size()); 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(ArchiveEntry::State::Unmodified); } // Setup variables setMuted(false); setModified(false); announce("opened"); UI::setSplashProgressMessage(""); return true; }