private static RDAFolder NavigateTo(RDAFolder root, string FullPath, string CurrentPos) { FullPath = FullPath.Replace("\\", "/"); CurrentPos = CurrentPos.Replace("\\", "/"); FullPath = FullPath.Trim('/'); CurrentPos = CurrentPos.Trim('/'); List <string> list = Enumerable.ToList(FullPath.Split('/')); string str = list[0]; RDAFolder root1 = null; foreach (RDAFolder rdaFolder in root.Folders) { if (rdaFolder.Name == str) { root1 = rdaFolder; break; } } if (root1 == null) { RDAFolder rdaFolder = new RDAFolder(root); rdaFolder.Name = str; rdaFolder.FullPath = CurrentPos + "/" + str; root.Folders.Add(rdaFolder); root1 = rdaFolder; } if (list.Count == 1) { return(root1); } list.RemoveAt(0); return(NavigateTo(root1, StringExtension.PutTogether(list, '/'), CurrentPos + "/" + str)); }
public static RDAFolder GenerateFrom(List<RDAFile> file, FileHeader.Version version) { RDAFolder root = new RDAFolder(version); root.Files.AddRange(file.FindAll(f => !f.FileName.Contains("/"))); foreach (RDAFile rdaFile in file.FindAll(f => f.FileName.Contains("/"))) NavigateTo(root, Path.GetDirectoryName(rdaFile.FileName), "").Files.Add(rdaFile); return root; }
public void Dispose() { if (read != null) read.Close(); rdaFileEntries.Clear(); rdaFolder = null; foreach (Stream stream in RDAFileStreamCache.Cache.Values) stream.Close(); RDAFileStreamCache.Cache.Clear(); GC.Collect(); }
public static RDAFolder GenerateFrom(List <RDAFile> file, FileHeader.Version version) { RDAFolder root = new RDAFolder(version); root.Files.AddRange(file.FindAll(f => !f.FileName.Contains("/"))); foreach (RDAFile rdaFile in file.FindAll(f => f.FileName.Contains("/"))) { NavigateTo(root, Path.GetDirectoryName(rdaFile.FileName), "").Files.Add(rdaFile); } return(root); }
public void Dispose() { if (read != null) { read.Close(); } rdaFileEntries.Clear(); rdaFolder = null; foreach (Stream stream in RDAFileStreamCache.Cache.Values) { stream.Close(); } RDAFileStreamCache.Cache.Clear(); GC.Collect(); }
public static List<RDAFolder> GenerateOf(RDAFolder root) { Dictionary<string, RDAFolder> dictionary = new Dictionary<string, RDAFolder>(); foreach (RDAFile rdaFile in root.GetAllFiles()) { string key = Path.GetExtension(rdaFile.FileName).ToLower(); if (!dictionary.ContainsKey(key)) dictionary.Add(key, new RDAFolder(root.Version)); dictionary[key].Files.Add(rdaFile); } foreach (KeyValuePair<string, RDAFolder> keyValuePair in dictionary) keyValuePair.Value.RDABlockCreator_FileType_IsCompressable = new bool?(FileType_CompressedExtensions.Contains(keyValuePair.Key)); return Enumerable.ToList(dictionary.Values); }
public static List <RDAFolder> GenerateOf(RDAFolder root) { Dictionary <string, RDAFolder> dictionary = new Dictionary <string, RDAFolder>(); foreach (RDAFile rdaFile in root.GetAllFiles()) { string key = Path.GetExtension(rdaFile.FileName).ToLower(); if (!dictionary.ContainsKey(key)) { dictionary.Add(key, new RDAFolder(root.Version)); } dictionary[key].Files.Add(rdaFile); } foreach (KeyValuePair <string, RDAFolder> keyValuePair in dictionary) { keyValuePair.Value.RDABlockCreator_FileType_IsCompressable = new bool?(FileType_CompressedExtensions.Contains(keyValuePair.Key)); } return(Enumerable.ToList(dictionary.Values)); }
public void ReadRDAFile() { read = new BinaryReader(new FileStream(FileName, FileMode.Open)); byte[] firstTwoBytes = read.ReadBytes(2); read.BaseStream.Position = 0; if (firstTwoBytes[0] == 'R' && firstTwoBytes[1] == '\0') { fileHeader = ReadFileHeader(read, FileHeader.Version.Version_2_0); } else if (firstTwoBytes[0] == 'R' && firstTwoBytes[1] == 'e') { fileHeader = ReadFileHeader(read, FileHeader.Version.Version_2_2); } else { throw new Exception("Invalid or unsupported RDA file!"); } rdaReadBlocks = 0; skippedDataSections.Clear(); ulong beginningOfDataSection = (ulong)read.BaseStream.Position; ulong currentBlockOffset = fileHeader.firstBlockOffset; while (currentBlockOffset < (ulong)read.BaseStream.Length) { ulong nextBlockOffset = ReadBlock(currentBlockOffset, beginningOfDataSection); beginningOfDataSection = currentBlockOffset + BlockInfo.GetSize(fileHeader.version); currentBlockOffset = nextBlockOffset; } // When writing we need to make sure that the section that is latest in the file is the last one in this list. skippedDataSections.Sort((a, b) => a.offset.CompareTo(b.offset)); rdaFolder = RDAFolder.GenerateFrom(rdaFileEntries, fileHeader.version); UpdateOutput("Done. " + rdaFileEntries.Count + " files. " + rdaReadBlocks + " blocks read, " + NumSkippedBlocks + " encrypted blocks skipped (" + NumSkippedFiles + " files)."); }
public RDAWriter(RDAFolder folder) { Folder = folder; }
public RDAFolder(RDAFolder parent) { this.Parent = parent; this.Version = parent.Version; }
private static RDAFolder NavigateTo(RDAFolder root, string FullPath, string CurrentPos) { FullPath = FullPath.Replace("\\", "/"); CurrentPos = CurrentPos.Replace("\\", "/"); FullPath = FullPath.Trim('/'); CurrentPos = CurrentPos.Trim('/'); List<string> list = Enumerable.ToList(FullPath.Split('/')); string str = list[0]; RDAFolder root1 = null; foreach (RDAFolder rdaFolder in root.Folders) { if (rdaFolder.Name == str) { root1 = rdaFolder; break; } } if (root1 == null) { RDAFolder rdaFolder = new RDAFolder(root); rdaFolder.Name = str; rdaFolder.FullPath = CurrentPos + "/" + str; root.Folders.Add(rdaFolder); root1 = rdaFolder; } if (list.Count == 1) return root1; list.RemoveAt(0); return NavigateTo(root1, StringExtension.PutTogether(list, '/'), CurrentPos + "/" + str); }
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(); }