Beispiel #1
0
 protected override void Dispose(bool disposing)
 {
     if (disposed)
     {
         return;
     }
     if (disposing)
     {
         ArcFile?.Dispose();
     }
     disposed = true;
     base.Dispose(disposing);
 }
Beispiel #2
0
        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();
                }
            }
        }
Beispiel #3
0
        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;
            }
        }
Beispiel #4
0
        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();
                }
            }
        }