예제 #1
0
 public static RDAFile FromUnmanaged(FileHeader.Version version, DirEntry dir, BlockInfo block, BinaryReader reader, RDAMemoryResidentHelper mrm)
 {
     RDAFile rdaFile = new RDAFile();
     rdaFile.FileName = dir.filename;
     rdaFile.Version = version;
     if ((block.flags & 4) != 4)
     {
         if ((block.flags & 1) == 1)
             rdaFile.Flags |= Flag.Compressed;
         if ((block.flags & 2) == 2)
             rdaFile.Flags |= Flag.Encrypted;
     }
     if ((block.flags & 4) == 4)
         rdaFile.Flags |= Flag.MemoryResident;
     if ((block.flags & 8) == 8)
         rdaFile.Flags |= Flag.Deleted;
     rdaFile.Offset = dir.offset;
     rdaFile.UncompressedSize = dir.filesize;
     rdaFile.CompressedSize = dir.compressed;
     rdaFile.TimeStamp = DateTimeExtension.FromTimeStamp((int)dir.timestamp);
     rdaFile.BinaryFile = mrm == null ? reader : new BinaryReader(mrm.Data);
     return rdaFile;
 }
예제 #2
0
        private void ReadDirEntries(byte[] buffer, BlockInfo block, RDAMemoryResidentHelper mrm)
        {
            MemoryStream memoryStream = new MemoryStream(buffer);
            BinaryReader reader = new BinaryReader(memoryStream);

            for (uint fileId = 0; fileId < block.fileCount; ++fileId)
            {
                byte[] fileNameBytes = reader.ReadBytes((int)DirEntry.GetFilenameSize());
                string fileNameString = Encoding.Unicode.GetString(fileNameBytes).Replace("\0", "");

                DirEntry dirEntry = new DirEntry
                {
                    filename = fileNameString,
                    offset = ReadUIntVersionAware(reader),
                    compressed = ReadUIntVersionAware(reader),
                    filesize = ReadUIntVersionAware(reader),
                    timestamp = ReadUIntVersionAware(reader),
                    unknown = ReadUIntVersionAware(reader),
                };

                RDAFile rdaFile = RDAFile.FromUnmanaged(fileHeader.version, dirEntry, block, read, mrm);
                rdaFileEntries.Add(rdaFile);
            }
        }
예제 #3
0
        private ulong ReadBlock(ulong Offset, ulong beginningOfDataSection)
        {
            UpdateOutput("----- Reading Block at " + Offset);
            read.BaseStream.Position = (long)Offset;

            BlockInfo blockInfo = new BlockInfo
            {
                flags = read.ReadUInt32(),
                fileCount = read.ReadUInt32(),
                directorySize = ReadUIntVersionAware(read),
                decompressedSize = ReadUIntVersionAware(read),
                nextBlock = ReadUIntVersionAware(read),
            };

            if ((blockInfo.flags & 8) != 8)
            {
                bool isMemoryResident = false;
                bool isEncrypted = false;
                bool isCompressed = false;
                if ((blockInfo.flags & 4) == 4)
                {
                    UpdateOutput("MemoryResident");
                    isMemoryResident = true;
                }
                if ((blockInfo.flags & 2) == 2)
                {
                    UpdateOutput("Encrypted");
                    isEncrypted = true;
                }
                if ((blockInfo.flags & 1) == 1)
                {
                    UpdateOutput("Compressed");
                    isCompressed = true;
                }
                if (blockInfo.flags == 0)
                    UpdateOutput("No Flags");

                int decryptionSeed = 0;
                if (isEncrypted)
                {
                    try {
                        decryptionSeed = BinaryExtension.GetDecryptionSeed(fileHeader.version);
                    } catch (ArgumentException e) {
                        UpdateOutput("Skipping (" + blockInfo.fileCount + " files) -- " + e.Message);
                        skippedDataSections.Add(new RDASkippedDataSection()
                        {
                            blockInfo = blockInfo,
                            offset = beginningOfDataSection,
                            size = (Offset - beginningOfDataSection),
                        });
                        return blockInfo.nextBlock;
                    }
                }
                read.BaseStream.Position = (long)(Offset - blockInfo.directorySize);
                if (isMemoryResident)
                    read.BaseStream.Position -= GetUIntSizeVersionAware() * 2;
                byte[] numArray2 = read.ReadBytes((int)blockInfo.directorySize);
                if (isEncrypted)
                    numArray2 = BinaryExtension.Decrypt(numArray2, decryptionSeed);
                if (isCompressed)
                    numArray2 = ZLib.ZLib.Uncompress(numArray2, (int)blockInfo.decompressedSize);

                RDAMemoryResidentHelper mrm = null;
                if (isMemoryResident)
                {
                    ulong beginningOfHeader = (ulong)read.BaseStream.Position;
                    ulong compressedSize = ReadUIntVersionAware(read);
                    ulong uncompressedSize = ReadUIntVersionAware(read);
                    mrm = new RDAMemoryResidentHelper(beginningOfHeader - blockInfo.directorySize - compressedSize, uncompressedSize, compressedSize, read.BaseStream, blockInfo, fileHeader.version);
                }

                uint dirEntrySize = DirEntry.GetSize(fileHeader.version);
                if (blockInfo.fileCount * dirEntrySize != blockInfo.decompressedSize)
                    throw new Exception("Unexpected directory entry size or count");

                ++rdaReadBlocks;
                UpdateOutput("-- DirEntries:");
                ReadDirEntries(numArray2, blockInfo, mrm);
            }

            return blockInfo.nextBlock;
        }
예제 #4
0
        public void Write(string Filename, FileHeader.Version version, bool compress, RDAReader originalReader, BackgroundWorker wrk)
        {
            FileStream fileStream = new FileStream(Filename, FileMode.Create);
            BinaryWriter writer = new BinaryWriter(fileStream);

            // we'll write the header at the end, when we know the offset to the first block
            writer.BaseStream.Position = FileHeader.GetSize(version);

            // blocks are organized by file type. there is one RDAFolder per block
            List<RDAFolder> blockFolders = RDABlockCreator.GenerateOf(Folder);
            int numBlocks = (int)originalReader.NumSkippedBlocks + blockFolders.Count;
            BlockInfo[] blockInfos = new BlockInfo[numBlocks];
            ulong[] blockInfoOffsets = new ulong[numBlocks];
            int writeBlockIndex = 0;

            // Write blocks skipped when reading. They have to appear at exactly the place where they came
            // from, because the file data offsets are encrypted and can therefore not be changed.
            for (int skippedBlockIndex = 0; skippedBlockIndex < originalReader.NumSkippedBlocks; ++skippedBlockIndex)
            {
                RDASkippedDataSection skippedBlock = originalReader.SkippedDataSections[skippedBlockIndex];

                if (wrk != null)
                {
                    UI_LastMessage = "Writing  Block " + (writeBlockIndex + 1) + "/" + numBlocks + " => ??? files (encrypted)";
                    wrk.ReportProgress((int)((double)writeBlockIndex / numBlocks * 100.0));
                }

                // Skip ahead to the correct position.
                // This will create "holes" in the file if the skipped sections are not contiguous or
                // don't start at the beginning of the file, but we'll have to live with it to some extent
                // anyway (we won't fit our "own" data in perfectly). And I'm just too afraid to get the
                // bin-packing wrong.
                writer.BaseStream.WriteBytes((skippedBlock.offset - (ulong)writer.BaseStream.Position), 0);

                // write the data
                originalReader.CopySkippedDataSextion(skippedBlock.offset, skippedBlock.size, writer.BaseStream);

                // generate the new block info
                BlockInfo blockInfo = skippedBlock.blockInfo.Clone();
                blockInfos[writeBlockIndex] = blockInfo;
                blockInfoOffsets[writeBlockIndex] = (ulong)writer.BaseStream.Position;

                if (writeBlockIndex > 0)
                {
                    blockInfos[writeBlockIndex - 1].nextBlock = blockInfoOffsets[writeBlockIndex];
                }

                // we'll write the block info at the end, once we know the next block offset
                writer.BaseStream.Position += BlockInfo.GetSize(version);
                ++writeBlockIndex;
            }

            // write regular blocks
            for (int blockFolderIndex = 0; blockFolderIndex < blockFolders.Count; ++blockFolderIndex)
            {
                RDAFolder blockFolder = blockFolders[blockFolderIndex];

                bool compressBlock = compress && blockFolder.RDABlockCreator_FileType_IsCompressable.GetValueOrDefault(false);

                if (wrk != null)
                {
                    UI_LastMessage = "Writing Block " + (writeBlockIndex + 1) + "/" + numBlocks + " => " + blockFolder.Files.Count + " files";
                    wrk.ReportProgress((int)((double)writeBlockIndex / numBlocks * 100.0));
                }

                Dictionary<RDAFile, ulong> dirEntryOffsets = new Dictionary<RDAFile, ulong>();
                Dictionary<RDAFile, ulong> dirEntryCompressedSizes = new Dictionary<RDAFile, ulong>();
                foreach (RDAFile file in blockFolder.Files)
                {
                    byte[] dataToWrite = file.GetData();
                    if (compressBlock)
                        dataToWrite = ZLib.ZLib.Compress(dataToWrite);
                    dirEntryOffsets.Add(file, (ulong)writer.BaseStream.Position);
                    dirEntryCompressedSizes.Add(file, (ulong)dataToWrite.Length);
                    writer.Write(dataToWrite);
                }

                int dirEntrySize = (int)DirEntry.GetSize(version);
                int decompressedDirEntriesSize = blockFolder.Files.Count * dirEntrySize;
                byte[] decompressedDirEntries = new byte[decompressedDirEntriesSize];
                for (int dirEntryIndex = 0; dirEntryIndex < blockFolder.Files.Count; ++dirEntryIndex)
                {
                    RDAFile file = blockFolder.Files[dirEntryIndex];
                    DirEntry dirEntry = new DirEntry() {
                        compressed = dirEntryCompressedSizes[file],
                        filesize = file.UncompressedSize,
                        filename = file.FileName,
                        timestamp = file.TimeStamp.ToTimeStamp(),
                        unknown = 0,
                        offset = dirEntryOffsets[file],
                    };
                    byte[] dirEntryBytes = CreateDirEntryBytes(dirEntry, version);
                    Buffer.BlockCopy(dirEntryBytes, 0, decompressedDirEntries, dirEntryIndex * dirEntrySize, dirEntrySize);
                }
                byte[] compressedDirEntries = compressBlock ? ZLib.ZLib.Compress(decompressedDirEntries) : decompressedDirEntries;
                writer.Write(compressedDirEntries);

                BlockInfo blockInfo = new BlockInfo()
                {
                    flags = compressBlock ? 1u : 0u,
                    fileCount = (uint)blockFolder.Files.Count,
                    directorySize = (ulong)compressedDirEntries.Length,
                    decompressedSize = (ulong)decompressedDirEntriesSize,
                    nextBlock = 0, // will set this at the end of the next block
                };
                blockInfos[writeBlockIndex] = blockInfo;
                blockInfoOffsets[writeBlockIndex] = (ulong)writer.BaseStream.Position;

                if (writeBlockIndex > 0)
                {
                    blockInfos[writeBlockIndex - 1].nextBlock = blockInfoOffsets[writeBlockIndex];
                }

                // we'll write the block info at the end, once we know the next block offset
                writer.BaseStream.Position += BlockInfo.GetSize(version);
                ++writeBlockIndex;
            }
            // the last block gets nextBlockOffset after end of file
            blockInfos[blockInfos.Length - 1].nextBlock = blockInfoOffsets[blockInfos.Length - 1] + BlockInfo.GetSize(version);

            // now write all block infos
            for (int index = 0; index < blockInfos.Length; ++index)
                WriteBlockInfo(writer, blockInfoOffsets[index], blockInfos[index], version);

            // now write the header
            FileHeader fileHeader = FileHeader.Create(version);
            fileHeader.firstBlockOffset = blockInfoOffsets[0];
            WriteHeader(writer, 0, fileHeader, version);

            fileStream.Close();
        }
예제 #5
0
        private static void WriteBlockInfo(BinaryWriter writer, ulong offset, BlockInfo blockInfo, FileHeader.Version version)
        {
            writer.BaseStream.Position = (long)offset;

            writer.Write((System.UInt32)blockInfo.flags);
            writer.Write((System.UInt32)blockInfo.fileCount);
            FileHeader.WriteUIntVersionAware(writer, blockInfo.directorySize, version);
            FileHeader.WriteUIntVersionAware(writer, blockInfo.decompressedSize, version);
            FileHeader.WriteUIntVersionAware(writer, blockInfo.nextBlock, version);
        }
예제 #6
0
        public static RDAFile FromUnmanaged(FileHeader.Version version, DirEntry dir, BlockInfo block, BinaryReader reader, RDAMemoryResidentHelper mrm)
        {
            RDAFile rdaFile = new RDAFile();

            rdaFile.FileName = dir.filename;
            rdaFile.Version  = version;
            if ((block.flags & 4) != 4)
            {
                if ((block.flags & 1) == 1)
                {
                    rdaFile.Flags |= Flag.Compressed;
                }
                if ((block.flags & 2) == 2)
                {
                    rdaFile.Flags |= Flag.Encrypted;
                }
            }
            if ((block.flags & 4) == 4)
            {
                rdaFile.Flags |= Flag.MemoryResident;
            }
            if ((block.flags & 8) == 8)
            {
                rdaFile.Flags |= Flag.Deleted;
            }
            rdaFile.Offset           = dir.offset;
            rdaFile.UncompressedSize = dir.filesize;
            rdaFile.CompressedSize   = dir.compressed;
            rdaFile.TimeStamp        = DateTimeExtension.FromTimeStamp(dir.timestamp);
            rdaFile.BinaryFile       = mrm == null ? reader : new BinaryReader(mrm.Data);
            return(rdaFile);
        }
예제 #7
0
        public void Write(string Filename, FileHeader.Version version, bool compress, RDAReader originalReader, BackgroundWorker wrk)
        {
            FileStream   fileStream = new FileStream(Filename, FileMode.Create);
            BinaryWriter writer     = new BinaryWriter(fileStream);

            // we'll write the header at the end, when we know the offset to the first block
            writer.BaseStream.Position = FileHeader.GetSize(version);

            // blocks are organized by file type. there is one RDAFolder per block
            List <RDAFolder> blockFolders = RDABlockCreator.GenerateOf(Folder);
            int numBlocks = (int)originalReader.NumSkippedBlocks + blockFolders.Count;

            BlockInfo[] blockInfos       = new BlockInfo[numBlocks];
            ulong[]     blockInfoOffsets = new ulong[numBlocks];
            int         writeBlockIndex  = 0;

            // Write blocks skipped when reading. They have to appear at exactly the place where they came
            // from, because the file data offsets are encrypted and can therefore not be changed.
            for (int skippedBlockIndex = 0; skippedBlockIndex < originalReader.NumSkippedBlocks; ++skippedBlockIndex)
            {
                RDASkippedDataSection skippedBlock = originalReader.SkippedDataSections[skippedBlockIndex];

                if (wrk != null)
                {
                    UI_LastMessage = "Writing  Block " + (writeBlockIndex + 1) + "/" + numBlocks + " => ??? files (encrypted)";
                    wrk.ReportProgress((int)((double)writeBlockIndex / numBlocks * 100.0));
                }

                // Skip ahead to the correct position.
                // This will create "holes" in the file if the skipped sections are not contiguous or
                // don't start at the beginning of the file, but we'll have to live with it to some extent
                // anyway (we won't fit our "own" data in perfectly). And I'm just too afraid to get the
                // bin-packing wrong.
                writer.BaseStream.WriteBytes((skippedBlock.offset - (ulong)writer.BaseStream.Position), 0);

                // write the data
                originalReader.CopySkippedDataSextion(skippedBlock.offset, skippedBlock.size, writer.BaseStream);

                // generate the new block info
                BlockInfo blockInfo = skippedBlock.blockInfo.Clone();
                blockInfos[writeBlockIndex]       = blockInfo;
                blockInfoOffsets[writeBlockIndex] = (ulong)writer.BaseStream.Position;

                if (writeBlockIndex > 0)
                {
                    blockInfos[writeBlockIndex - 1].nextBlock = blockInfoOffsets[writeBlockIndex];
                }

                // we'll write the block info at the end, once we know the next block offset
                writer.BaseStream.Position += BlockInfo.GetSize(version);
                ++writeBlockIndex;
            }

            // write regular blocks
            for (int blockFolderIndex = 0; blockFolderIndex < blockFolders.Count; ++blockFolderIndex)
            {
                RDAFolder blockFolder = blockFolders[blockFolderIndex];

                bool compressBlock = compress && blockFolder.RDABlockCreator_FileType_IsCompressable.GetValueOrDefault(false);

                if (wrk != null)
                {
                    UI_LastMessage = "Writing Block " + (writeBlockIndex + 1) + "/" + numBlocks + " => " + blockFolder.Files.Count + " files";
                    wrk.ReportProgress((int)((double)writeBlockIndex / numBlocks * 100.0));
                }

                Dictionary <RDAFile, ulong> dirEntryOffsets         = new Dictionary <RDAFile, ulong>();
                Dictionary <RDAFile, ulong> dirEntryCompressedSizes = new Dictionary <RDAFile, ulong>();
                foreach (RDAFile file in blockFolder.Files)
                {
                    byte[] dataToWrite = file.GetData();
                    if (compressBlock)
                    {
                        dataToWrite = ZLib.ZLib.Compress(dataToWrite);
                    }
                    dirEntryOffsets.Add(file, (ulong)writer.BaseStream.Position);
                    dirEntryCompressedSizes.Add(file, (ulong)dataToWrite.Length);
                    writer.Write(dataToWrite);
                }

                int    dirEntrySize = (int)DirEntry.GetSize(version);
                int    decompressedDirEntriesSize = blockFolder.Files.Count * dirEntrySize;
                byte[] decompressedDirEntries     = new byte[decompressedDirEntriesSize];
                for (int dirEntryIndex = 0; dirEntryIndex < blockFolder.Files.Count; ++dirEntryIndex)
                {
                    RDAFile  file     = blockFolder.Files[dirEntryIndex];
                    DirEntry dirEntry = new DirEntry()
                    {
                        compressed = dirEntryCompressedSizes[file],
                        filesize   = file.UncompressedSize,
                        filename   = file.FileName,
                        timestamp  = file.TimeStamp.ToTimeStamp(),
                        unknown    = 0,
                        offset     = dirEntryOffsets[file],
                    };
                    byte[] dirEntryBytes = CreateDirEntryBytes(dirEntry, version);
                    Buffer.BlockCopy(dirEntryBytes, 0, decompressedDirEntries, dirEntryIndex * dirEntrySize, dirEntrySize);
                }
                byte[] compressedDirEntries = compressBlock ? ZLib.ZLib.Compress(decompressedDirEntries) : decompressedDirEntries;
                writer.Write(compressedDirEntries);

                BlockInfo blockInfo = new BlockInfo()
                {
                    flags            = compressBlock ? 1u : 0u,
                    fileCount        = (uint)blockFolder.Files.Count,
                    directorySize    = (ulong)compressedDirEntries.Length,
                    decompressedSize = (ulong)decompressedDirEntriesSize,
                    nextBlock        = 0, // will set this at the end of the next block
                };
                blockInfos[writeBlockIndex]       = blockInfo;
                blockInfoOffsets[writeBlockIndex] = (ulong)writer.BaseStream.Position;

                if (writeBlockIndex > 0)
                {
                    blockInfos[writeBlockIndex - 1].nextBlock = blockInfoOffsets[writeBlockIndex];
                }

                // we'll write the block info at the end, once we know the next block offset
                writer.BaseStream.Position += BlockInfo.GetSize(version);
                ++writeBlockIndex;
            }
            // the last block gets nextBlockOffset after end of file
            blockInfos[blockInfos.Length - 1].nextBlock = blockInfoOffsets[blockInfos.Length - 1] + BlockInfo.GetSize(version);

            // now write all block infos
            for (int index = 0; index < blockInfos.Length; ++index)
            {
                WriteBlockInfo(writer, blockInfoOffsets[index], blockInfos[index], version);
            }

            // now write the header
            FileHeader fileHeader = FileHeader.Create(version);

            fileHeader.firstBlockOffset = blockInfoOffsets[0];
            WriteHeader(writer, 0, fileHeader, version);

            fileStream.Close();
        }