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; }
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); } }
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; }
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(); }
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); }
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); }
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(); }