예제 #1
0
        public void Write(Stream stream)
        {
            if (this.Filename == "")
            {
                throw new Exception("Can't save");
            }
            // Get all the entries
            List<Entry> entries = new List<Entry>();
            this.Root.AddToList(entries);

            Dictionary<Entry, Structs.RPF7EntryInfoTemplate> entriesInfo = new Dictionary<Entry, Structs.RPF7EntryInfoTemplate>();
            foreach (Entry entry in entries)
            {
                Structs.RPF7EntryInfoTemplate entryInfo = new Structs.RPF7EntryInfoTemplate();
                // update the is resource field
                entryInfo.Field1 = (entry is ResourceEntry) ? 1U : 0U;
                entriesInfo[entry] = entryInfo;
            }

            IEnumerable<string> entriesNames = entries.Select(entry => entry.Name);

            int namesLength = entriesNames.Sum(name => name.Length);
            int shiftNameAccessBy = -1;
            for (int i = 0; i < 8; ++i)
            {
                // the multipcation is the maximum number of nulls, TODO: it can be done better, because it is not always the worst case
                // does the offset should be signed or unsigned?
                if (namesLength + entriesNames.Count() * (i + 1) < (1 << (16 + i)))
                {
                    shiftNameAccessBy = i;
                    break;
                }
            }

            if (shiftNameAccessBy == -1)
            {
                throw new Exception("Too many entries!");
            }

            int currentPos = 0;
            // Write the names
            stream.Seek(currentPos + 0x10 * (entries.Count + 1), SeekOrigin.Begin);

            using (BinaryWriter writer = new BinaryWriter(AES.EncryptStream(new StreamKeeper(stream), sixteenRoundsDecrypt)))
            {
                foreach (Entry entry in entries)
                {
                    // Update name offset in entry info
                    Structs.RPF7EntryInfoTemplate entryInfo = entriesInfo[entry];
                    entryInfo.Field4 = (uint)currentPos;
                    entriesInfo[entry] = entryInfo;
                    // write name and null, and keep the position
                    writer.Write(entry.Name.ToArray<char>());
                    writer.Write((byte)0);
                    currentPos += entry.Name.Length + 1;
                    // Make the next entry alligned
                    while ((currentPos % (1 << shiftNameAccessBy)) != 0)
                    {
                        writer.Write((byte)0);
                        currentPos += 1;
                    }
                }
                // Align to 0x10 byte
                while (currentPos % 0x10 != 0)
                {
                    writer.Write((byte)0);
                    currentPos += 1;
                }
            }

            // Fill the header
            Info.EntriesCount = entries.Count;
            Info.ShiftNameAccessBy = shiftNameAccessBy;
            Info.EntriesNamesLength = currentPos;

            long[] dataOffsets = new long[entries.Count];
            long[] dataSizes = new long[entries.Count];
            // align to 0x200
            if (stream.Position % (1 << 9) != 0)
            {
                stream.Write(new byte[(1 << 9) - ((int)stream.Position % (1 << 9))], 0, (1 << 9) - ((int)stream.Position % (1 << 9)));
            }
            // Write data and fill entry info
            // TODO: I don't like that the reading logic happens in Entry and the writing logic here
            // I think that I should move the reading logic to here, and split things to functions
            foreach (Entry entry in entries)
            {
                if (entry is FileEntry)
                {
                    long entryOffset = stream.Position;
                    (entry as FileEntry).Write(stream);
                    int entrySize = (int)(stream.Position - entryOffset);
                    // align to 0x200
                    if (stream.Position % (1 << 9) != 0)
                    {
                        stream.Write(new byte[(1 << 9) - ((int)stream.Position % (1 << 9))], 0, (1 << 9) - ((int)stream.Position % (1 << 9)));
                    }

                    // Update the entry offset and entry size
                    Structs.RPF7EntryInfoTemplate entryInfo = entriesInfo[entry];
                    entryInfo.Field2 = (uint)((entryOffset >> 9) & 0x7FFFFF);

                    if (entry is ResourceEntry)
                    {
                        entryInfo.Field3 = (uint)entrySize & 0xFFFFFF;
                        entryInfo.Field5 = (entry as ResourceEntry).SystemFlag;
                        entryInfo.Field6 = (entry as ResourceEntry).GraphicsFlag;
                    }
                    else if (entry is RegularFileEntry)
                    {
                        if ((entry as RegularFileEntry).Compressed)
                        {
                            // The compressed size
                            entryInfo.Field3 = (uint)entrySize & 0xFFFFFF;
                            // The uncompress size
                            entryInfo.Field5 = (uint)(entry as RegularFileEntry).Data.GetSize();
                            // The is encrypted flag
                            entryInfo.Field6 = 1;
                        }
                        else
                        {
                            entryInfo.Field3 = 0;
                            entryInfo.Field5 = (uint)entrySize;
                            // The is encrypted flag
                            entryInfo.Field6 = 0;
                        }
                    }

                    entriesInfo[entry] = entryInfo;

                }
                else
                {
                    Structs.RPF7EntryInfoTemplate entryInfo = entriesInfo[entry];
                    // The offset is "-1" in case of file
                    entryInfo.Field2 = 0x7FFFFF;
                    entriesInfo[entry] = entryInfo;
                }
            }

            using (Stream writer = AES.EncryptStream(new StreamKeeper(stream), sixteenRoundsDecrypt))
            {
                int currentFreeIndex = 1;
                // Last finish to build and write the file
                stream.Seek(0, SeekOrigin.Begin);
                Info.Write(stream);
                Queue<Entry> entriesToProcess = new Queue<Entry>();
                entriesToProcess.Enqueue(this.Root);
                while (entriesToProcess.Count > 0)
                {
                    Entry entry = entriesToProcess.Dequeue();
                    if (entry is DirectoryEntry)
                    {
                        Structs.RPF7EntryInfoTemplate entryInfo = entriesInfo[entry];
                        IList<Entry> subentries = (entry as DirectoryEntry).GetEntries();
                        // Update the info on the sub entries
                        entryInfo.Field5 = (uint)currentFreeIndex;
                        entryInfo.Field6 = (uint)subentries.Count;
                        entriesInfo[entry] = entryInfo;
                        // Process the sub entries
                        foreach (Entry subentry in subentries)
                        {
                            entriesToProcess.Enqueue(subentry);
                        }
                        currentFreeIndex += subentries.Count;
                    }
                    // Write the entry info
                    entriesInfo[entry].Write(writer);
                }
            }
            // Done!
        }
예제 #2
0
        public void Write(Stream stream)
        {
            if (this.Filename == "")
            {
                throw new Exception("Can't save");
            }
            // Get all the entries
            List <Entry> entries = new List <Entry>();

            this.Root.AddToList(entries);


            Dictionary <Entry, Structs.RPF7EntryInfoTemplate> entriesInfo = new Dictionary <Entry, Structs.RPF7EntryInfoTemplate>();

            foreach (Entry entry in entries)
            {
                Structs.RPF7EntryInfoTemplate entryInfo = new Structs.RPF7EntryInfoTemplate();
                // update the is resource field
                entryInfo.Field1   = (entry is ResourceEntry) ? 1U : 0U;
                entriesInfo[entry] = entryInfo;
            }

            IEnumerable <string> entriesNames = entries.Select(entry => entry.Name);

            int namesLength       = entriesNames.Sum(name => name.Length);
            int shiftNameAccessBy = -1;

            for (int i = 0; i < 8; ++i)
            {
                // the multipcation is the maximum number of nulls, TODO: it can be done better, because it is not always the worst case
                // does the offset should be signed or unsigned?
                if (namesLength + entriesNames.Count() * (i + 1) < (1 << (16 + i)))
                {
                    shiftNameAccessBy = i;
                    break;
                }
            }

            if (shiftNameAccessBy == -1)
            {
                throw new Exception("Too many entries!");
            }

            int currentPos = 0;

            // Write the names
            stream.Seek(currentPos + 0x10 * (entries.Count + 1), SeekOrigin.Begin);

            using (BinaryWriter writer = new BinaryWriter(AES.EncryptStream(new StreamKeeper(stream), sixteenRoundsDecrypt)))
            {
                foreach (Entry entry in entries)
                {
                    // Update name offset in entry info
                    Structs.RPF7EntryInfoTemplate entryInfo = entriesInfo[entry];
                    entryInfo.Field4   = (uint)currentPos;
                    entriesInfo[entry] = entryInfo;
                    // write name and null, and keep the position
                    writer.Write(entry.Name.ToArray <char>());
                    writer.Write((byte)0);
                    currentPos += entry.Name.Length + 1;
                    // Make the next entry alligned
                    while ((currentPos % (1 << shiftNameAccessBy)) != 0)
                    {
                        writer.Write((byte)0);
                        currentPos += 1;
                    }
                }
                // Align to 0x10 byte
                while (currentPos % 0x10 != 0)
                {
                    writer.Write((byte)0);
                    currentPos += 1;
                }
            }

            // Fill the header
            Info.EntriesCount       = entries.Count;
            Info.ShiftNameAccessBy  = shiftNameAccessBy;
            Info.EntriesNamesLength = currentPos;

            long[] dataOffsets = new long[entries.Count];
            long[] dataSizes   = new long[entries.Count];
            // align to 0x200
            if (stream.Position % (1 << 9) != 0)
            {
                stream.Write(new byte[(1 << 9) - ((int)stream.Position % (1 << 9))], 0, (1 << 9) - ((int)stream.Position % (1 << 9)));
            }
            // Write data and fill entry info
            // TODO: I don't like that the reading logic happens in Entry and the writing logic here
            // I think that I should move the reading logic to here, and split things to functions
            foreach (Entry entry in entries)
            {
                if (entry is FileEntry)
                {
                    long entryOffset = stream.Position;
                    (entry as FileEntry).Write(stream);
                    int entrySize = (int)(stream.Position - entryOffset);
                    // align to 0x200
                    if (stream.Position % (1 << 9) != 0)
                    {
                        stream.Write(new byte[(1 << 9) - ((int)stream.Position % (1 << 9))], 0, (1 << 9) - ((int)stream.Position % (1 << 9)));
                    }

                    // Update the entry offset and entry size
                    Structs.RPF7EntryInfoTemplate entryInfo = entriesInfo[entry];
                    entryInfo.Field2 = (uint)((entryOffset >> 9) & 0x7FFFFF);

                    if (entry is ResourceEntry)
                    {
                        entryInfo.Field3 = (uint)entrySize & 0xFFFFFF;
                        entryInfo.Field5 = (entry as ResourceEntry).SystemFlag;
                        entryInfo.Field6 = (entry as ResourceEntry).GraphicsFlag;
                    }
                    else if (entry is RegularFileEntry)
                    {
                        if ((entry as RegularFileEntry).Compressed)
                        {
                            // The compressed size
                            entryInfo.Field3 = (uint)entrySize & 0xFFFFFF;
                            // The uncompress size
                            entryInfo.Field5 = (uint)(entry as RegularFileEntry).Data.GetSize();
                            // The is encrypted flag
                            entryInfo.Field6 = 1;
                        }
                        else
                        {
                            entryInfo.Field3 = 0;
                            entryInfo.Field5 = (uint)entrySize;
                            // The is encrypted flag
                            entryInfo.Field6 = 0;
                        }
                    }

                    entriesInfo[entry] = entryInfo;
                }
                else
                {
                    Structs.RPF7EntryInfoTemplate entryInfo = entriesInfo[entry];
                    // The offset is "-1" in case of file
                    entryInfo.Field2   = 0x7FFFFF;
                    entriesInfo[entry] = entryInfo;
                }
            }

            using (Stream writer = AES.EncryptStream(new StreamKeeper(stream), sixteenRoundsDecrypt))
            {
                int currentFreeIndex = 1;
                // Last finish to build and write the file
                stream.Seek(0, SeekOrigin.Begin);
                Info.Write(stream);
                Queue <Entry> entriesToProcess = new Queue <Entry>();
                entriesToProcess.Enqueue(this.Root);
                while (entriesToProcess.Count > 0)
                {
                    Entry entry = entriesToProcess.Dequeue();
                    if (entry is DirectoryEntry)
                    {
                        Structs.RPF7EntryInfoTemplate entryInfo = entriesInfo[entry];
                        IList <Entry> subentries = (entry as DirectoryEntry).GetEntries();
                        // Update the info on the sub entries
                        entryInfo.Field5   = (uint)currentFreeIndex;
                        entryInfo.Field6   = (uint)subentries.Count;
                        entriesInfo[entry] = entryInfo;
                        // Process the sub entries
                        foreach (Entry subentry in subentries)
                        {
                            entriesToProcess.Enqueue(subentry);
                        }
                        currentFreeIndex += subentries.Count;
                    }
                    // Write the entry info
                    entriesInfo[entry].Write(writer);
                }
            }
            // Done!
        }