// ----------------------------------------------------------------------------- // 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 non-UDMF thing data // ----------------------------------------------------------------------------- bool MapPreviewCanvas::readThings(ArchiveEntry* map_head, ArchiveEntry* map_end, MapFormat map_format) { // Find THINGS entry ArchiveEntry* things = nullptr; while (map_head) { // Check entry type if (map_head->type() == EntryType::fromId("map_things")) { things = map_head; break; } // Exit loop if we've reached the end of the map entries if (map_head == map_end) break; else map_head = map_head->nextEntry(); } // No things if (!things) return false; // Read things data if (map_format == MapFormat::Doom) { auto thng_data = (DoomMapFormat::Thing*)things->rawData(true); unsigned nt = things->size() / sizeof(DoomMapFormat::Thing); for (size_t a = 0; a < nt; a++) addThing(thng_data[a].x, thng_data[a].y); } else if (map_format == MapFormat::Doom64) { auto thng_data = (Doom64MapFormat::Thing*)things->rawData(true); unsigned nt = things->size() / sizeof(Doom64MapFormat::Thing); for (size_t a = 0; a < nt; a++) addThing(thng_data[a].x, thng_data[a].y); } else if (map_format == MapFormat::Hexen) { auto thng_data = (HexenMapFormat::Thing*)things->rawData(true); unsigned nt = things->size() / sizeof(HexenMapFormat::Thing); for (size_t a = 0; a < nt; a++) addThing(thng_data[a].x, thng_data[a].y); } 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; }
// ----------------------------------------------------------------------------- // Opens a map from a mapdesc_t // ----------------------------------------------------------------------------- bool MapPreviewCanvas::openMap(Archive::MapDesc map) { // All errors = invalid map Global::error = "Invalid map"; // Check if this map is a pk3 map bool map_archive = false; if (map.archive) { map_archive = true; // Attempt to open entry as wad archive temp_archive_ = new WadArchive(); if (!temp_archive_->open(map.head)) { delete temp_archive_; return false; } // Detect maps auto maps = temp_archive_->detectMaps(); // Set map if there are any in the archive if (!maps.empty()) map = maps[0]; else return false; } // Parse UDMF map if (map.format == MapFormat::UDMF) { ArchiveEntry* udmfdata = nullptr; for (auto mapentry = map.head; mapentry != map.end; mapentry = mapentry->nextEntry()) { // Check entry type if (mapentry->type() == EntryType::fromId("udmf_textmap")) { udmfdata = mapentry; break; } } if (udmfdata == nullptr) return false; // Start parsing Tokenizer tz; tz.openMem(udmfdata->data(), map.head->name()); // Get first token wxString token = tz.getToken(); size_t vertcounter = 0, linecounter = 0, thingcounter = 0; while (!token.IsEmpty()) { if (!token.CmpNoCase("namespace")) { // skip till we reach the ';' do { token = tz.getToken(); } while (token.Cmp(";")); } else if (!token.CmpNoCase("vertex")) { // Get X and Y properties bool gotx = false; bool goty = false; double x = 0.; double y = 0.; do { token = tz.getToken(); if (!token.CmpNoCase("x") || !token.CmpNoCase("y")) { bool isx = !token.CmpNoCase("x"); token = tz.getToken(); if (token.Cmp("=")) { Log::error(wxString::Format("Bad syntax for vertex %i in UDMF map data", vertcounter)); return false; } if (isx) x = tz.getDouble(), gotx = true; else y = tz.getDouble(), goty = true; // skip to end of declaration after each key do { token = tz.getToken(); } while (token.Cmp(";")); } } while (token.Cmp("}")); if (gotx && goty) addVertex(x, y); else { Log::error(wxString::Format("Wrong vertex %i in UDMF map data", vertcounter)); return false; } vertcounter++; } else if (!token.CmpNoCase("linedef")) { bool special = false; bool twosided = false; bool gotv1 = false, gotv2 = false; size_t v1 = 0, v2 = 0; do { token = tz.getToken(); if (!token.CmpNoCase("v1") || !token.CmpNoCase("v2")) { bool isv1 = !token.CmpNoCase("v1"); token = tz.getToken(); if (token.Cmp("=")) { Log::error(wxString::Format("Bad syntax for linedef %i in UDMF map data", linecounter)); return false; } if (isv1) v1 = tz.getInteger(), gotv1 = true; else v2 = tz.getInteger(), gotv2 = true; // skip to end of declaration after each key do { token = tz.getToken(); } while (token.Cmp(";")); } else if (!token.CmpNoCase("special")) { special = true; // skip to end of declaration after each key do { token = tz.getToken(); } while (token.Cmp(";")); } else if (!token.CmpNoCase("sideback")) { twosided = true; // skip to end of declaration after each key do { token = tz.getToken(); } while (token.Cmp(";")); } } while (token.Cmp("}")); if (gotv1 && gotv2) addLine(v1, v2, twosided, special); else { Log::error(wxString::Format("Wrong line %i in UDMF map data", linecounter)); return false; } linecounter++; } else if (S_CMPNOCASE(token, "thing")) { // Get X and Y properties bool gotx = false; bool goty = false; double x = 0.; double y = 0.; do { token = tz.getToken(); if (!token.CmpNoCase("x") || !token.CmpNoCase("y")) { bool isx = !token.CmpNoCase("x"); token = tz.getToken(); if (token.Cmp("=")) { Log::error(wxString::Format("Bad syntax for thing %i in UDMF map data", vertcounter)); return false; } if (isx) x = tz.getDouble(), gotx = true; else y = tz.getDouble(), goty = true; // skip to end of declaration after each key do { token = tz.getToken(); } while (token.Cmp(";")); } } while (token.Cmp("}")); if (gotx && goty) addThing(x, y); else { Log::error(wxString::Format("Wrong thing %i in UDMF map data", vertcounter)); return false; } vertcounter++; } else { // Check for side or sector definition (increase counts) if (S_CMPNOCASE(token, "sidedef")) n_sides_++; else if (S_CMPNOCASE(token, "sector")) n_sectors_++; // map preview ignores sidedefs, sectors, comments, // unknown fields, etc. so skip to end of block do { token = tz.getToken(); } while (token.Cmp("}") && !token.empty()); } // Iterate to next token token = tz.getToken(); } } // Non-UDMF map if (map.format != MapFormat::UDMF) { // Read vertices (required) if (!readVertices(map.head, map.end, map.format)) return false; // Read linedefs (required) if (!readLines(map.head, map.end, map.format)) return false; // Read things if (map.format != MapFormat::UDMF) readThings(map.head, map.end, map.format); // Read sides & sectors (count only) ArchiveEntry* sidedefs = nullptr; ArchiveEntry* sectors = nullptr; while (map.head) { // Check entry type if (map.head->type() == EntryType::fromId("map_sidedefs")) sidedefs = map.head; if (map.head->type() == EntryType::fromId("map_sectors")) sectors = map.head; // Exit loop if we've reached the end of the map entries if (map.head == map.end) break; else map.head = map.head->nextEntry(); } if (sidedefs && sectors) { // Doom64 map if (map.format != MapFormat::Doom64) { n_sides_ = sidedefs->size() / 30; n_sectors_ = sectors->size() / 26; } // Doom/Hexen map else { n_sides_ = sidedefs->size() / 12; n_sectors_ = sectors->size() / 16; } } } // Clean up if (map_archive) { temp_archive_->close(); delete temp_archive_; temp_archive_ = nullptr; } // Refresh map Refresh(); return true; }
bool TarReader::readHeader(Ref<ArchiveEntry> *nextEntry) { if (!data_) data_ = ByteArray::create(512); *nextEntry = ArchiveEntry::create(); ByteArray *data = data_; ArchiveEntry *entry = *nextEntry; if (source_->readAll(data) < data->count()) return false; i_ += data->count(); bool eoi = true; for (int i = 0; i < data->count(); ++i) { if (data->byteAt(i) != 0) { eoi = false; break; } } if (eoi) return false; bool ustarMagic = false; bool gnuMagic = false; bool readAgain = true; while (readAgain) { readAgain = false; String magic; data->scanString(&magic, "", 257, 263); ustarMagic = (magic == "ustar"); gnuMagic = (magic == "ustar "); unsigned checksum, probesum; data->scanNumber(&checksum, 8, 148, 156); entry->type_ = data->at(156); if (entry->path_ == "") data->scanString(&entry->path_, "", 0, 100); if (entry->linkPath_ == "") data->scanString(&entry->linkPath_, "", 157, 257); probesum = tarHeaderSum(data); if (checksum != probesum) throw BrokenArchive(i_ - data->count(), Format("Checksum mismatch (%% != %%), path = \"%%\"") << oct(checksum, 6) << oct(probesum, 6) << entry->path()); if (gnuMagic) { while ((entry->type_ == 'K' || entry->type_ == 'L') /*&& entry->path_ == "././@LongLink"*/) { data->scanNumber(&entry->size_, 8, 124, 136); String longPath = source_->readAll(entry->size_); if (longPath->count() < entry->size_) throw BrokenArchive(i_, "Expected GNU @LongLink data"); i_ += entry->size_; if (entry->size() % 512 != 0) { i_ += source_->skip(512 - entry->size() % 512); } if (longPath->byteAt(longPath->count() - 1) == 0) longPath->truncate(longPath->count() - 1); if (entry->type_ == 'K') entry->linkPath_ = longPath; else if (entry->type_ == 'L') entry->path_ = longPath; if (source_->readAll(data) < data->count()) throw BrokenArchive(i_, "Expected GNU @LongLink header"); i_ += data->count(); entry->type_ = data->at(156); readAgain = true; } } } if (ustarMagic || gnuMagic) { String prefix; data->scanString(&entry->userName_, "", 265, 297); data->scanString(&entry->groupName_, "", 297, 329); if (!gnuMagic) { data->scanString(&prefix, "", 345, 500); if (prefix != "") entry->path_ = prefix + "/" + entry->path_; } } data->scanNumber(&entry->mode_, 8, 100, 108); data->scanNumber(&entry->userId_, 8, 108, 116); data->scanNumber(&entry->groupId_, 8, 116, 124); data->scanNumber(&entry->size_, 8, 124, 136); data->scanNumber(&entry->lastModified_, 8, 136, 148); if (entry->type() == 0 && entry->path()->count() > 0) { if (entry->path()->at(entry->path()->count() - 1) == '/') entry->type_ = ArchiveEntry::Directory; } entry->offset_ = i_; return true; }
// ----------------------------------------------------------------------------- // 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; }