public void Load()
        {
            lock (sync)
            {
                packStream.Seek(0, SeekOrigin.Begin);
                BinaryReader br = new BinaryReader(packStream, Encoding.Unicode);

                Warnings.Clear();
                entries.Clear();

                int count = br.ReadInt32();
                for (int i = 0; i < count; ++i)
                {
                    PackageEntry entry = new PackageEntry();
                    int nameLength = br.ReadInt32();
                    byte[] nameBytes = br.ReadBytes(nameLength);
                    entry.Name = Encoding.Unicode.GetString(nameBytes);
                    entry.Length = br.ReadInt32();
                    entry.Offset = br.ReadInt64();
                    entry.SampleLength = br.ReadSingle();
                    entries.Add(entry);
                }
            }
        }
        public byte[] GetFile(PackageEntry entry)
        {
            if (entry == null) return null;
            if (!entries.Contains(entry)) throw new InvalidOperationException("Entry is not from this package.");

            if (entry.Offset == -1)
            {
                return File.ReadAllBytes(entry.Name);
            }
            else
            {
                lock (sync)
                {
                    BinaryReader br = new BinaryReader(packStream);
                    packStream.Seek(entry.Offset, SeekOrigin.Begin);
                    return br.ReadBytes(entry.Length);
                }
            }
        }
 public void RemoveEntry(PackageEntry entry)
 {
     lock (sync)
     {
         entries.Remove(entry);
     }
 }
        public void Save(string path, Action<string, int, int> progressCallback = null)
        {
            lock (sync)
            {
                if (progressCallback != null) progressCallback("Validating file names...", 0, 0);

                // Validate file names for dupes
                List<string> names = new List<string>();

                // We're going to assume there is only one level of files
                foreach (PackageEntry entry in entries)
                {
                    string normalizedName = Path.GetFileName(entry.Name).ToLowerInvariant();
                    if (names.Contains(normalizedName))
                        throw new InvalidOperationException(string.Format("Package contains multiple entries with the file name \"{0}\".", normalizedName));
                    names.Add(normalizedName);
                }

                PackageEntry manifestEntry = entries.Find(e => Path.GetFileName(e.Name) == "info.xml");
                if (manifestEntry != null)
                {
                    // Put manifest at the end of the file.
                    entries.Remove(manifestEntry);
                    entries.Add(manifestEntry);
                }
            }

            string tmpPath = Path.GetTempFileName();
            try
            {
                lock (sync)
                {
                    using (FileStream fs = File.Open(tmpPath, FileMode.Open, FileAccess.ReadWrite))
                    {
                        BinaryWriter bw = new BinaryWriter(fs);
                        BinaryReader br = new BinaryReader(packStream);

                        // Calculate starting offset
                        if (progressCallback != null) progressCallback("Preparing to save...", 0, 0);
                        long currOffset = 4;
                        foreach (PackageEntry entry in entries)
                        {
                            currOffset += 20 + Encoding.Unicode.GetByteCount(Path.GetFileName(entry.Name));
                        }

                            fs.SetLength(currOffset);
                            fs.Seek(currOffset, SeekOrigin.Begin);

                            // Copy data
                            int i = 0;
                            List<PackageEntry> newEntries = new List<PackageEntry>();
                            foreach (PackageEntry entry in entries)
                            {
                                if (progressCallback != null) progressCallback(string.Format("Copying {0}", entry.Name), i, entries.Count);
                                if (entry.Offset == -1)
                                {
                                    PackageEntry addEntry = new PackageEntry { Name = Path.GetFileName(entry.Name) };
                                    addEntry.Length = (int)new FileInfo(entry.Name).Length;
                                    addEntry.Offset = fs.Position;

                                    if (Path.GetExtension(entry.Name).ToLowerInvariant() == ".ogg")
                                    {
                                        using (NVorbis.VorbisReader vorb = new NVorbis.VorbisReader(entry.Name))
                                        {
                                            addEntry.SampleLength = (float)vorb.TotalTime.TotalSeconds;
                                        }
                                    }
                                    else
                                    {
                                        addEntry.SampleLength = 0;
                                    }

                                    newEntries.Add(addEntry);
                                    bw.Write(File.ReadAllBytes(entry.Name));
                                }
                                else
                                {
                                    newEntries.Add(new PackageEntry { Name = entry.Name, Length = entry.Length, Offset = fs.Position, SampleLength = entry.SampleLength });
                                    packStream.Seek(entry.Offset, SeekOrigin.Begin);
                                    bw.Write(br.ReadBytes(entry.Length));
                                }
                                ++i;
                            }

                            // Write header
                            if (progressCallback != null) progressCallback("Writing file table...", entries.Count, entries.Count);
                            fs.Seek(0, SeekOrigin.Begin);
                            bw.Write(newEntries.Count);

                            foreach (PackageEntry entry in newEntries)
                            {
                                byte[] nameBytes = Encoding.Unicode.GetBytes(entry.Name);
                                bw.Write(nameBytes.Length);
                                bw.Write(nameBytes);
                                bw.Write(entry.Length);
                                bw.Write(entry.Offset);
                                bw.Write(entry.SampleLength);
                            }

                        fs.Flush();
                        fs.Seek(0, SeekOrigin.Begin);

                        // Final copy to original
                        packStream.Close();
                        packStream = File.Open(path, FileMode.Create, FileAccess.ReadWrite);
                        if (progressCallback != null) progressCallback("Finalizing...", entries.Count, entries.Count);
                        fs.CopyTo(packStream);
                    }
                }

                Load();
            }
            finally
            {
                File.Delete(tmpPath);
            }
        }
 public PackageEntry CreateEntry(string filename)
 {
     lock (sync)
     {
         PackageEntry entry = new PackageEntry { Name = filename, Offset = -1 };
         entries.Add(entry);
         return entry;
     }
 }