protected override void Dispose(bool disposing) { if (disposed) { return; } if (disposing) { ArcFile?.Dispose(); } disposed = true; base.Dispose(disposing); }
public override void Create(Stream output, IEnumerable <Entry> list, ResourceOptions options, EntryCallback callback) { ArcFile base_archive = null; var ami_options = GetOptions <AmiOptions> (options); if (null != ami_options && ami_options.UseBaseArchive && !string.IsNullOrEmpty(ami_options.BaseArchive)) { var base_file = new ArcView(ami_options.BaseArchive); try { if (base_file.View.ReadUInt32(0) == Signature) { base_archive = TryOpen(base_file); } if (null == base_archive) { throw new InvalidFormatException(string.Format("{0}: base archive could not be read", Path.GetFileName(ami_options.BaseArchive))); } base_file = null; } finally { if (null != base_file) { base_file.Dispose(); } } } try { var file_table = new SortedDictionary <uint, PackedEntry>(); if (null != base_archive) { foreach (AmiEntry entry in base_archive.Dir) { file_table[entry.Id] = entry; } } int update_count = UpdateFileTable(file_table, list); if (0 == update_count) { throw new InvalidFormatException(arcStrings.AMINoFiles); } uint file_count = (uint)file_table.Count; if (null != callback) { callback((int)file_count + 1, null, null); } int callback_count = 0; long start_offset = output.Position; uint data_offset = file_count * 16 + 16; output.Seek(data_offset, SeekOrigin.Current); foreach (var entry in file_table) { if (null != callback) { callback(callback_count++, entry.Value, arcStrings.MsgAddingFile); } long current_offset = output.Position; if (current_offset > uint.MaxValue) { throw new FileSizeException(); } if (entry.Value is AmiEntry) { CopyAmiEntry(base_archive, entry.Value, output); } else { entry.Value.Size = WriteAmiEntry(entry.Value, output); } entry.Value.Offset = (uint)current_offset; } if (null != callback) { callback(callback_count++, null, arcStrings.MsgWritingIndex); } output.Position = start_offset; using (var header = new BinaryWriter(output, Encoding.ASCII, true)) { header.Write(Signature); header.Write(file_count); header.Write(data_offset); header.Write((uint)0); foreach (var entry in file_table) { header.Write(entry.Key); header.Write((uint)entry.Value.Offset); header.Write((uint)entry.Value.UnpackedSize); header.Write((uint)entry.Value.Size); } } } finally { if (null != base_archive) { base_archive.Dispose(); } } }
public override ArcFile TryOpen(ArcView file) { long base_offset = 0; if (0x5a4d == file.View.ReadUInt16(0)) // 'MZ' { base_offset = SkipExeHeader(file, s_xp3_header); } if (!file.View.BytesEqual(base_offset, s_xp3_header)) { return(null); } long dir_offset = base_offset + file.View.ReadInt64(base_offset + 0x0b); if (dir_offset < 0x13 || dir_offset >= file.MaxOffset) { return(null); } if (0x80 == file.View.ReadUInt32(dir_offset)) { dir_offset = base_offset + file.View.ReadInt64(dir_offset + 9); if (dir_offset < 0x13 || dir_offset >= file.MaxOffset) { return(null); } } int header_type = file.View.ReadByte(dir_offset); if (0 != header_type && 1 != header_type) { return(null); } Stream header_stream; if (0 == header_type) // read unpacked header { long header_size = file.View.ReadInt64(dir_offset + 1); if (header_size > uint.MaxValue) { return(null); } header_stream = file.CreateStream(dir_offset + 9, (uint)header_size); } else // read packed header { long packed_size = file.View.ReadInt64(dir_offset + 1); if (packed_size > uint.MaxValue) { return(null); } long header_size = file.View.ReadInt64(dir_offset + 9); using (var input = file.CreateStream(dir_offset + 17, (uint)packed_size)) header_stream = ZLibCompressor.DeCompress(input); } var crypt_algorithm = new Lazy <ICrypt> (() => QueryCryptAlgorithm(file), false); var dir = new List <Entry>(); dir_offset = 0; using (var header = new BinaryReader(header_stream, Encoding.Unicode)) using (var filename_map = new FilenameMap()) { while (-1 != header.PeekChar()) { uint entry_signature = header.ReadUInt32(); long entry_size = header.ReadInt64(); if (entry_size < 0) { return(null); } dir_offset += 12 + entry_size; if (0x656C6946 == entry_signature) // "File" { var entry = new Xp3Entry(); while (entry_size > 0) { uint section = header.ReadUInt32(); long section_size = header.ReadInt64(); entry_size -= 12; if (section_size > entry_size) { break; } entry_size -= section_size; long next_section_pos = header.BaseStream.Position + section_size; switch (section) { case 0x6f666e69: // "info" if (entry.Size != 0 || !string.IsNullOrEmpty(entry.Name)) { goto NextEntry; // ambiguous entry, ignore } entry.IsEncrypted = 0 != header.ReadUInt32(); long file_size = header.ReadInt64(); long packed_size = header.ReadInt64(); if (file_size >= uint.MaxValue || packed_size > uint.MaxValue || packed_size > file.MaxOffset) { goto NextEntry; } entry.IsPacked = file_size != packed_size; entry.Size = (uint)packed_size; entry.UnpackedSize = (uint)file_size; if (entry.IsEncrypted || ForceEncryptionQuery) { entry.Cipher = crypt_algorithm.Value; } else { entry.Cipher = NoCryptAlgorithm; } var name = entry.Cipher.ReadName(header); if (null == name) { goto NextEntry; } if (entry.Cipher.ObfuscatedIndex && ObfuscatedPathRe.IsMatch(name)) { goto NextEntry; } if (filename_map.Count > 0) { name = filename_map.Get(entry.Hash, name); } if (name.Length > 0x100) { goto NextEntry; } entry.Name = name; entry.Type = FormatCatalog.Instance.GetTypeFromName(name); entry.IsEncrypted = !(entry.Cipher is NoCrypt) && !(entry.Cipher.StartupTjsNotEncrypted && "startup.tjs" == name); break; case 0x6d676573: // "segm" int segment_count = (int)(section_size / 0x1c); if (segment_count > 0) { for (int i = 0; i < segment_count; ++i) { bool compressed = 0 != header.ReadInt32(); long segment_offset = base_offset + header.ReadInt64(); long segment_size = header.ReadInt64(); long segment_packed_size = header.ReadInt64(); if (segment_offset > file.MaxOffset || segment_packed_size > file.MaxOffset) { goto NextEntry; } var segment = new Xp3Segment { IsCompressed = compressed, Offset = segment_offset, Size = (uint)segment_size, PackedSize = (uint)segment_packed_size }; entry.Segments.Add(segment); } entry.Offset = entry.Segments.First().Offset; } break; case 0x726c6461: // "adlr" if (4 == section_size) { entry.Hash = header.ReadUInt32(); } break; default: // unknown section break; } header.BaseStream.Position = next_section_pos; } if (!string.IsNullOrEmpty(entry.Name) && entry.Segments.Any()) { if (entry.Cipher.ObfuscatedIndex) { DeobfuscateEntry(entry); } dir.Add(entry); } } else if (entry_size > 7) { // 0x6E666E68 == entry_signature // "hnfn" // 0x6C696D73 == entry_signature // "smil" // 0x46696C65 == entry_signature // "eliF" // 0x757A7559 == entry_signature // "Yuzu" uint hash = header.ReadUInt32(); int name_size = header.ReadInt16(); if (name_size > 0) { entry_size -= 6; if (name_size * 2 <= entry_size) { var filename = new string (header.ReadChars(name_size)); filename_map.Add(hash, filename); } } } NextEntry: header.BaseStream.Position = dir_offset; } } if (0 == dir.Count) { return(null); } var arc = new ArcFile(file, this, dir); try { if (crypt_algorithm.IsValueCreated) { crypt_algorithm.Value.Init(arc); } return(arc); } catch { arc.Dispose(); throw; } }
public override ImageData Read(IBinaryStream file, ImageMetaData info) { var meta = (DrefMetaData)info; ArcFile dpak = null; try { int layers_count = meta.Layers.Count(); WriteableBitmap canvas = null; foreach (var path in meta.Layers) { if (null == dpak || dpak.File.Name != path.Item1) { if (dpak != null) { dpak.Dispose(); dpak = null; } var view = VFS.OpenView(path.Item1); try { dpak = Psb.Value.TryOpen(view); if (null == dpak) { throw new InvalidFormatException(); } } catch { view.Dispose(); throw; } } var entry = dpak.Dir.FirstOrDefault(e => e.Name == path.Item2); if (null == entry) { throw new InvalidFormatException(); } using (var decoder = dpak.OpenImage(entry)) { if (1 == layers_count) { return(decoder.Image); } if (null == canvas) { canvas = new WriteableBitmap(decoder.Image.Bitmap); meta.Width = decoder.Info.Width; meta.Height = decoder.Info.Height; meta.OffsetX = decoder.Info.OffsetX; meta.OffsetY = decoder.Info.OffsetY; } else { BlendLayer(canvas, decoder.Image); } } } if (null == canvas) { throw new InvalidFormatException(); } canvas.Freeze(); return(new ImageData(canvas, meta)); } finally { if (dpak != null) { dpak.Dispose(); } } }