ArchiveType::Certainty ArchiveType_DAT_Sango::isInstance(stream::input_sptr psArchive) const { stream::pos lenArchive = psArchive->size(); // TESTED BY: fmt_dat_sango_isinstance_c01 if (lenArchive < DAT_FAT_ENTRY_LEN) return DefinitelyNo; // too short uint32_t offEndFAT; psArchive->seekg(0, stream::start); psArchive >> u32le(offEndFAT); // TESTED BY: fmt_dat_sango_isinstance_c02 if (offEndFAT > lenArchive) return DefinitelyNo; uint32_t offNext = 4; // in case of no files for (unsigned int offset = 4; offset < offEndFAT; offset += 4) { psArchive >> u32le(offNext); // TESTED BY: fmt_dat_sango_isinstance_c03 if (offNext > lenArchive) return DefinitelyNo; } // Last offset must equal file size // TESTED BY: fmt_dat_sango_isinstance_c04 if (offNext != lenArchive) return DefinitelyNo; // TESTED BY: fmt_dat_sango_isinstance_c00 return DefinitelyYes; }
Archive_DAT_Sango::Archive_DAT_Sango(stream::inout_sptr psArchive) : FATArchive(psArchive, DAT_FIRST_FILE_OFFSET, 0) { psArchive->seekg(0, stream::end); this->lenArchive = psArchive->tellg(); if (this->lenArchive < DAT_FAT_ENTRY_LEN) throw stream::error("file too short"); uint32_t offEndFAT; psArchive->seekg(0, stream::start); psArchive >> u32le(offEndFAT); uint32_t offCur = offEndFAT, offNext; for (int i = 0; offCur < this->lenArchive; i++) { psArchive >> u32le(offNext); FATEntry *fatEntry = new FATEntry(); EntryPtr ep(fatEntry); fatEntry->iIndex = i; fatEntry->iOffset = offCur; fatEntry->lenHeader = 0; fatEntry->type = FILETYPE_GENERIC; fatEntry->fAttr = 0; fatEntry->bValid = true; fatEntry->storedSize = offNext - offCur; fatEntry->realSize = fatEntry->storedSize; this->vcFAT.push_back(ep); if (i >= DAT_SAFETY_MAX_FILECOUNT) { throw stream::error("too many files or corrupted archive"); } offCur = offNext; } }
Archive_DAT_LostVikings::Archive_DAT_LostVikings(std::unique_ptr<stream::inout> content) : Archive_FAT(std::move(content), DAT_FIRST_FILE_OFFSET, 0) { stream::pos lenArchive = this->content->size(); if (lenArchive > 0) { this->content->seekg(0, stream::start); uint32_t offNext; *this->content >> u32le(offNext) ; uint32_t numFiles = offNext / DAT_FAT_ENTRY_LEN; this->vcFAT.reserve(numFiles); for (unsigned int i = 0; i < numFiles; i++) { auto f = this->createNewFATEntry(); f->iOffset = offNext; if (i == numFiles - 1) { offNext = lenArchive; } else { *this->content >> u32le(offNext) ; } f->iIndex = i; f->lenHeader = 0; f->type = FILETYPE_GENERIC; f->fAttr = File::Attribute::Default; f->storedSize = offNext - f->iOffset; f->realSize = f->storedSize; f->bValid = true; this->vcFAT.push_back(std::move(f)); } } // else empty archive
void IFFWriter::end() { stream::pos orig = this->iff.tellp(); stream::pos start = this->chunk.back(); this->chunk.pop_back(); stream::len lenChunk = orig - (start + 8); switch (this->filetype) { case Filetype_RIFF_Unpadded: case Filetype_IFF_Unpadded: break; case Filetype_RIFF: case Filetype_IFF: if (orig % 2) { // Pad to even byte boundary this->iff.write("", 1); orig++; } break; } this->iff.seekp(start + 4, stream::start); switch (this->filetype) { case Filetype_RIFF_Unpadded: case Filetype_RIFF: this->iff << u32le(lenChunk); break; case Filetype_IFF_Unpadded: case Filetype_IFF: this->iff << u32be(lenChunk); break; } this->iff.seekp(orig, stream::start); return; }
FATArchive::FATEntry *Archive_DAT_Sango::preInsertFile(const FATEntry *idBeforeThis, FATEntry *pNewEntry) { // TESTED BY: fmt_dat_sango_insert* // Set the format-specific variables pNewEntry->lenHeader = 0; // Because the new entry isn't in the vector yet we need to shift it manually pNewEntry->iOffset += DAT_FAT_ENTRY_LEN; // Update the last FAT entry (the one that points to EOF.) this->updateLastEntry(pNewEntry->storedSize + DAT_FAT_ENTRY_LEN); this->psArchive->seekp(DAT_FATENTRY_OFFSET(pNewEntry), stream::start); this->psArchive->insert(DAT_FAT_ENTRY_LEN); this->psArchive << u32le(pNewEntry->iOffset); // Update the offsets now there's a new FAT entry taking up space. this->shiftFiles( NULL, (this->vcFAT.size() + 1) * DAT_FAT_ENTRY_LEN, DAT_FAT_ENTRY_LEN, 0 ); return pNewEntry; }
void Archive_DAT_Sango::updateLastEntry(stream::delta lenDelta) { this->lenArchive += lenDelta; this->psArchive->seekp(this->vcFAT.size() * DAT_FAT_ENTRY_LEN, stream::start); this->psArchive << u32le(this->lenArchive); return; }
Tileset_Zone66::Tileset_Zone66(stream::inout_sptr data, PaletteTablePtr pal) : Tileset_FAT(data, Z66_FIRST_TILE_OFFSET), pal(pal) { stream::pos len = this->data->size(); // We still have to perform sanity checks in case the user forced an // open even though it failed the signature check. if (len < Z66_FIRST_TILE_OFFSET) throw stream::error("file too short"); this->data->seekg(0, stream::start); uint32_t numTiles; this->data >> u32le(numTiles); this->items.reserve(numTiles); if (numTiles > Z66_SAFETY_MAX_TILES) throw stream::error("too many tiles"); if (numTiles > 0) { uint32_t firstOffset = (numTiles+1) * 4; uint32_t nextOffset; this->data >> u32le(nextOffset); nextOffset += firstOffset; for (unsigned int i = 0; i < numTiles; i++) { FATEntry *fat = new FATEntry(); EntryPtr ep(fat); fat->valid = true; fat->attr = 0; fat->index = i; fat->offset = nextOffset; fat->lenHeader = 0; if (i + 1 == numTiles) { // Last entry ends at EOF nextOffset = len; } else { this->data >> u32le(nextOffset); nextOffset += firstOffset; } fat->size = nextOffset - fat->offset; this->items.push_back(ep); } }
TilesetType_Zone66::Certainty TilesetType_Zone66::isInstance( stream::input_sptr psTileset) const { stream::pos len = psTileset->size(); // TESTED BY: tls_zone66_isinstance_c04 if (len < Z66_FIRST_TILE_OFFSET) return DefinitelyNo; // too short psTileset->seekg(0, stream::start); uint32_t numFiles; psTileset >> u32le(numFiles); if ((numFiles == 0) && (len > 8)) return DefinitelyNo; // invalid empty file uint32_t offset, lastOffset = 0; for (unsigned int i = 0; i < numFiles; i++) { psTileset >> u32le(offset); // The first file always starts at offset 0. // TESTED BY: tls_zone66_isinstance_c01 if ((i == 0) && (offset != 0)) return DefinitelyNo; // Make sure the offsets are increasing, otherwise we'd get a negative // file size (or the file has been tweaked to make opening difficult, but // then there's the -f option to gametls for that.) // TESTED BY: tls_zone66_isinstance_c02 if (offset < lastOffset) return DefinitelyNo; // Make sure the tile is contained within the file // TESTED BY: tls_zone66_isinstance_c03 if ((numFiles+1) * 4 + offset > len) return DefinitelyNo; lastOffset = offset; } // TESTED BY: tls_zone66_isinstance_c00 return DefinitelyYes; }
TilesetPtr TilesetType_Zone66::create(stream::inout_sptr psTileset, SuppData& suppData) const { psTileset->truncate(4); psTileset->seekp(0, stream::start); psTileset << u32le(0); PaletteTablePtr pal; // Only load the palette if one was given if (suppData.find(SuppItem::Palette) != suppData.end()) { ImagePtr palFile(new Palette_VGA(suppData[SuppItem::Palette], 6)); pal = palFile->getPalette(); pal->at(Z66_TRANSPARENT_COLOUR).alpha = 0; } return TilesetPtr(new Tileset_Zone66(psTileset, pal)); }
void IFFReader::loadChunks(stream::len lenChunk) { this->chunks.clear(); while (lenChunk > 8) { lenChunk -= 8; // ID and chunk size fields Chunk c; c.start = this->iff.tellg() + 8; this->iff >> fixedLength(c.name, 4); switch (this->filetype) { case Filetype_RIFF_Unpadded: case Filetype_RIFF: this->iff >> u32le(c.len); break; case Filetype_IFF_Unpadded: case Filetype_IFF: default: this->iff >> u32be(c.len); break; } unsigned int pad; switch (this->filetype) { case Filetype_RIFF: case Filetype_IFF: pad = (c.len % 2) ? 1 : 0; break; case Filetype_RIFF_Unpadded: case Filetype_IFF_Unpadded: default: pad = 0; break; } stream::len lenPaddedSub = c.len + pad; if (lenChunk < c.len) c.len = lenChunk; // truncated if (lenChunk < lenPaddedSub) lenPaddedSub = lenChunk; // final pad truncated this->chunks.push_back(c); lenChunk -= lenPaddedSub; this->iff.seekg(lenPaddedSub, stream::cur); } return; }
void Archive_DAT_Sango::updateFileOffset(const FATEntry *pid, stream::delta offDelta) { this->psArchive->seekp(DAT_FATENTRY_OFFSET(pid), stream::start); this->psArchive << u32le(pid->iOffset); return; }