public void Read(Stream stream)
        {
            this.Entries.Clear();

            byte[] headerBuffer = new byte[384];
            if (stream.ReadAligned(headerBuffer, 0, 384, 2048) != 384)
            {
                throw new NotAPackageFileException();
            }

            PackageHeader header = (PackageHeader)headerBuffer.BytesToStructure(typeof(PackageHeader));

            bool bigEndian;

            // Magic
            if (header.Magic == 0x51890ACE)
            {
                bigEndian = false;
            }
            else if (header.Magic == 0xCE0A8951)
            {
                bigEndian = true;

                header.Version              = header.Version.Swap();
                header.Flags                = header.Flags.Swap();
                header.IndexCount           = header.IndexCount.Swap();
                header.PackageSize          = header.PackageSize.Swap();
                header.IndexSize            = header.IndexSize.Swap();
                header.NamesSize            = header.NamesSize.Swap();
                header.ExtensionsSize       = header.ExtensionsSize.Swap();
                header.UncompressedDataSize = header.UncompressedDataSize.Swap();
                header.CompressedDataSize   = header.CompressedDataSize.Swap();
            }
            else
            {
                throw new NotAPackageFileException();
            }

            if (header.Version != 4)
            {
                throw new UnsupportedPackageFileVersionException();
            }

            this.Version = header.Version;

            // File Index
            byte[] indexBuffer;
            indexBuffer = new byte[header.IndexSize];
            if (stream.ReadAligned(indexBuffer, 0, header.IndexSize, 2048) != header.IndexSize)
            {
                throw new PackageFileException();
            }

            // Names
            byte[] namesBuffer;
            namesBuffer = new byte[header.NamesSize];
            if (stream.ReadAligned(namesBuffer, 0, header.NamesSize, 2048) != header.NamesSize)
            {
                throw new PackageFileException();
            }

            // Extensions
            byte[] extensionsBuffer;
            extensionsBuffer = new byte[header.ExtensionsSize];
            if (stream.ReadAligned(extensionsBuffer, 0, header.ExtensionsSize, 2048) != header.ExtensionsSize)
            {
                throw new PackageFileException();
            }

            long baseOffset = stream.Position;

            for (int i = 0; i < header.IndexCount; i++)
            {
                PackageEntry entry = new PackageEntry();

                int    offset = i * 28;              // Each index entry is 28 bytes long
                byte[] index  = new byte[28];

                int nameOffset;
                int extensionOffset;

                if (bigEndian == false)
                {
                    nameOffset             = BitConverter.ToInt32(indexBuffer, offset + 0x00);
                    extensionOffset        = BitConverter.ToInt32(indexBuffer, offset + 0x04);
                    entry.Unknown08        = BitConverter.ToUInt32(indexBuffer, offset + 0x08);
                    entry.Offset           = BitConverter.ToInt32(indexBuffer, offset + 0x0C);                              // + baseOffset;
                    entry.UncompressedSize = BitConverter.ToInt32(indexBuffer, offset + 0x10);
                    entry.CompressedSize   = BitConverter.ToInt32(indexBuffer, offset + 0x14);
                    entry.Unknown1C        = BitConverter.ToUInt32(indexBuffer, offset + 0x18);
                }
                else
                {
                    nameOffset             = BitConverter.ToInt32(indexBuffer, offset + 0x00).Swap();
                    extensionOffset        = BitConverter.ToInt32(indexBuffer, offset + 0x04).Swap();
                    entry.Unknown08        = BitConverter.ToUInt32(indexBuffer, offset + 0x08).Swap();
                    entry.Offset           = BitConverter.ToInt32(indexBuffer, offset + 0x0C).Swap();
                    entry.UncompressedSize = BitConverter.ToInt32(indexBuffer, offset + 0x10).Swap();
                    entry.CompressedSize   = BitConverter.ToInt32(indexBuffer, offset + 0x14).Swap();
                    entry.Unknown1C        = BitConverter.ToUInt32(indexBuffer, offset + 0x18).Swap();
                }

                // package is compressed with zlib, offsets are not correct, fix 'em
                // compression occurs in the 360 version of Saints Row 2 packages
                // this should work (I hope)
                if ((header.Flags & 1) == 1)
                {
                    entry.Offset = baseOffset;
                    baseOffset  += entry.CompressedSize.Align(2048);
                }
                else
                {
                    entry.Offset += baseOffset;
                }

                entry.Name      = namesBuffer.GetASCIIZ(nameOffset);
                entry.Extension = extensionsBuffer.GetASCIIZ(extensionOffset);

                if (entry.Unknown08 != 0 || entry.Unknown1C != 0)
                {
                    throw new Exception();
                }

                if ((header.Flags & 1) != 1 && entry.CompressedSize != -1)
                {
                    throw new Exception();
                }

                this.Entries.Add(entry);
            }
        }
        public void Write(Stream stream)
        {
            MemoryStream memory;

            PackageHeader header = new PackageHeader();

            header.Magic              = 0x51890ACE;
            header.Version            = 4; // this.Version
            header.CompressedDataSize = 0xFFFFFFFF;
            header.Flags              = 2; // I think this flag means 'preload' or something of that sort. Patch has it set at least.

            List <string>            names            = new List <string>();
            List <string>            extensions       = new List <string>();
            Dictionary <string, int> nameOffsets      = new Dictionary <string, int>();
            Dictionary <string, int> extensionOffsets = new Dictionary <string, int>();

            foreach (PackageEntry entry in this.Entries)
            {
                if (names.Contains(entry.Name) == false)
                {
                    names.Add(entry.Name);
                }

                if (extensions.Contains(entry.Extension) == false)
                {
                    extensions.Add(entry.Extension);
                }
            }

            names.Sort();
            extensions.Sort();

            memory = new MemoryStream();
            foreach (string name in names)
            {
                nameOffsets[name] = (int)memory.Position;
                memory.WriteASCIIZ(name);
            }
            header.NamesSize = (int)memory.Length;
            byte[] namesBuffer = memory.GetBuffer();

            memory = new MemoryStream();
            foreach (string extension in extensions)
            {
                extensionOffsets[extension] = (int)memory.Position;
                memory.WriteASCIIZ(extension);
            }
            header.ExtensionsSize = (int)memory.Length;
            byte[] extensionsBuffer = memory.GetBuffer();

            int totalSize = 0;

            memory = new MemoryStream();
            foreach (PackageEntry entry in this.Entries)
            {
                memory.WriteS32(nameOffsets[entry.Name]);
                memory.WriteS32(extensionOffsets[entry.Extension]);
                memory.WriteU32(entry.Unknown08);
                memory.WriteU32((uint)entry.Offset);
                memory.WriteS32(entry.UncompressedSize);
                memory.WriteS32(entry.CompressedSize);
                memory.WriteU32(entry.Unknown1C);
                totalSize += (int)entry.UncompressedSize.Align(16);
            }
            header.IndexSize = (int)memory.Length;
            byte[] indexBuffer = memory.GetBuffer();

            header.IndexCount = this.Entries.Count;

            header.UncompressedDataSize = totalSize;

            totalSize += 2048;                              // header
            totalSize += header.IndexSize.Align(2048);      // index
            totalSize += header.NamesSize.Align(2048);      // names
            totalSize += header.ExtensionsSize.Align(2048); // extensions

            header.PackageSize = totalSize;

            int headerSize = Marshal.SizeOf(typeof(PackageHeader));

            byte[]   headerBuffer = new byte[headerSize];
            GCHandle headerHandle = GCHandle.Alloc(headerBuffer, GCHandleType.Pinned);

            Marshal.StructureToPtr(header, headerHandle.AddrOfPinnedObject(), false);
            headerHandle.Free();

            stream.WriteAligned(headerBuffer, 0, headerBuffer.Length, 2048);
            stream.WriteAligned(indexBuffer, 0, header.IndexSize, 2048);
            stream.WriteAligned(namesBuffer, 0, header.NamesSize, 2048);
            stream.WriteAligned(extensionsBuffer, 0, header.ExtensionsSize, 2048);
        }