Exemple #1
0
        public static void Create(System.IO.Stream output, IEnumerable<ArchiveCreateEntry> files, ArchiveFlags flags, CompressType compress, Action<double, string> onProgress) {
            if (!files.Any())
                throw new IrosArcException("Can't create an archive that contains no files");
            var sw = new System.Diagnostics.Stopwatch();
            sw.Start();
            double total = files.Count() + 2;
            int count = 0;

            onProgress(count / total, "");
            ArcHeader h = new ArcHeader() { Flags = flags, Version = MAX_VERSION, Directory = 16 };
            List<DirectoryEntry> entries = files.Select(f => new DirectoryEntry() {
                Filename = f.Filename, Flags = 0, 
            }).ToList();
            int dsize = entries.Select(e => (int)e.GetSize()).Sum();
            h.Save(output);
            output.WriteInt(entries.Count);
            long position = h.Directory + dsize + 4;
            onProgress(++count / total, "Wrote header");
            int index = 0;
            var combined = entries.Zip(files, (d,e) => new { Dir = d, ACE = e }).ToList();

            var cw = new CompressWork() {
                Input = new System.Collections.Concurrent.ConcurrentBag<CompressEntry>(),
                Compressed = new System.Collections.Concurrent.BlockingCollection<CompressEntry>(8),
                Compress = compress
            };
            
            foreach (var comb in combined) {
                cw.Input.Add(new CompressEntry() { ACE = comb.ACE, Dir = comb.Dir });
            }
            foreach (int _ in Enumerable.Range(0, 8)) System.Threading.ThreadPool.QueueUserWorkItem(CompressWorkThread, cw);

            int filesDone = 0;

            while (filesDone < combined.Count) {
                var entry = cw.Compressed.Take();
                entry.Dir.Offset = position;
                var data = entry.DataRec.Data;
                if (entry.DataRec.Compressed) entry.Dir.Flags |= FileFlags.CompressLZMA;
                entry.Dir.Length = data.Length;

                output.Position = position;
                output.Write(data, 0, data.Length);

                position += entry.Dir.Length;
                onProgress(++count / total, "Written " + entry.ACE.Filename);
                index++;
                filesDone++;
            }

            output.Position = h.Directory + 4;
            foreach (var entry in entries) {
                entry.Save(output);
            }
            sw.Stop();
            onProgress(++count / total, String.Format("Complete: {0} files, {1:0.0}MB in {2} seconds", entries.Count, output.Length / (1024f*1024f), sw.Elapsed.TotalSeconds));
        }
Exemple #2
0
        public void ApplyPatch(IrosArc patch, Action <double, string> onProgress)
        {
            int currentDirSize = _entries.Sum(e => e.GetSize());

            byte[] deldata = patch.GetBytes("%IrosPatch:Deleted");
            if (deldata != null)
            {
                string[] delfile = System.Text.Encoding.Unicode.GetString(deldata).Split(new[] { "\n" }, StringSplitOptions.RemoveEmptyEntries);
                foreach (string del in delfile)
                {
                    RuntimeLog.Write("Removing file {0} from archive", del);
                    _entries.RemoveAll(e => e.Filename.Equals(del, StringComparison.InvariantCultureIgnoreCase));
                }
                onProgress(0, "Removed " + delfile.Length + " deleted files");
            }
            int count = 0;
            var files = patch.AllFileNames().Where(s => !s.StartsWith("%")).ToArray();

            foreach (string file in files)
            {
                var    patchEntry = patch._lookup[file];
                byte[] data       = new byte[patchEntry.Length];
                patch._data.Position = patchEntry.Offset;
                patch._data.Read(data, 0, data.Length);
                if (HasFile(file))   //update existing
                {
                    RuntimeLog.Write("File {0} is already in archive...", file);
                    DirectoryEntry exist = _lookup[file];
                    if (exist.Length >= data.Length)   //put data in same position, woo
                    {
                        RuntimeLog.Write("...updating in place");
                        _data.Position = exist.Offset;
                    }
                    else     //stick at end of file
                    {
                        _data.Position = _data.Length;
                        exist.Offset   = _data.Position;
                        RuntimeLog.Write("...size increase: writing to end of file");
                    }
                    _data.Write(data, 0, data.Length);
                    exist.Length = data.Length;
                    exist.Flags  = patchEntry.Flags;
                }
                else     //new file, just append
                {
                    RuntimeLog.Write("File {0} is new, appending", file);
                    DirectoryEntry de = new DirectoryEntry()
                    {
                        Filename = file,
                        Flags    = patchEntry.Flags,
                        Length   = patchEntry.Length,
                        Offset   = _data.Length
                    };
                    _data.Position = de.Offset;
                    _data.Write(data, 0, data.Length);
                    _entries.Add(de);
                    _lookup[file] = de;
                }

                count++;
                onProgress(1.0 * count / files.Length, "Processed " + file);
            }
            int newDirSize = _entries.Sum(e => e.GetSize());

            if (newDirSize <= currentDirSize)
            {
                RuntimeLog.Write("Directory will fit in existing location");
                _data.Position = _header.Directory;
            }
            else
            {
                RuntimeLog.Write("Directory size increase, appending");
                if (_data.Length >= int.MaxValue)   //write forwarder
                {
                    _data.Position = _header.Directory;
                    _data.WriteInt(-1);
                    _data.WriteLong(_data.Length);
                    _data.Position = _data.Length;
                }
                else     //write direct location
                {
                    _header.Directory = (int)_data.Length;
                    _data.Position    = _header.Directory;
                }
            }
            _data.WriteInt(_entries.Count);
            foreach (var e in _entries)
            {
                e.Save(_data);
            }
            _header.Version = MAX_VERSION;
            _data.Position  = 0;
            _header.Save(_data);
            onProgress(1.0, "Wrote directory");
        } //TODO: track blank spaces in file and reuse where possible...