/// <summary> /// Adds a multiple files, located in <paramref name="innerFolder"/>, to <see cref="Mythic.Package.MythicPackageBlock.Files"/> table. /// </summary> /// <param name="fileNames">Array of file paths on HD.</param> /// <param name="innerFolder">Relative folder within KR (destination).</param> /// <param name="flag">Compression type.</param> public void AddFiles(string[] fileNames, string innerFolder, CompressionFlag flag) { foreach (string file in fileNames) { AddFile(file, innerFolder, flag); } }
/// <summary> /// Initializes a new instance from existing Mythic package file. /// </summary> /// <param name="reader">Binary file (.uop source).</param> /// <param name="parent">Parent entity.</param> public MythicPackageFile(BinaryReader reader, MythicPackageBlock parent) { m_Parent = parent; m_DataBlockAddress = m_OldDataBlockAddress = reader.ReadInt64(); m_DataBlockLength = reader.ReadInt32(); m_CompressedSize = reader.ReadInt32(); m_DecompressedSize = reader.ReadInt32(); m_FileHash = reader.ReadUInt64(); if (m_FileHash != 0) { m_FileName = HashDictionary.Get(m_FileHash, true); } m_DataBlockHash = reader.ReadUInt32(); short flag = reader.ReadInt16(); switch (flag) { case 0x0: m_Compression = CompressionFlag.None; break; case 0x1: m_Compression = CompressionFlag.Zlib; break; default: throw new InvalidCompressionException(flag); } }
/// <summary> /// Adds all files within a certain folder to .uop package. /// </summary> /// <param name="folder">Folder, which contains files.</param> /// <param name="flag">Compression type.</param> public void AddFolder(string folder, CompressionFlag flag) { if (!Directory.Exists(folder)) { throw new ArgumentException(String.Format("'{0}' is not a folder!", folder)); } Stack <string> stack = new Stack <string>(); stack.Push(folder); while (stack.Count > 0) { string f = stack.Pop(); string inner = f.Remove(0, folder.Length); foreach (string file in Directory.GetFiles(f)) { AddFile(file, inner, flag); } foreach (string newFolder in Directory.GetDirectories(f)) { stack.Push(newFolder); } } }
/// <summary> /// Adds a file, located in <paramref name="fileName"/>, to <see cref="Mythic.Package.MythicPackageBlock.Files"/> table. /// </summary> /// <param name="fileName">Path to a file on HD.</param> /// <param name="innerFolder">Relative folder within KR (destination).</param> /// <param name="flag">Compression type.</param> public void AddFile(string fileName, string innerFolder, CompressionFlag flag) { if (String.IsNullOrEmpty(fileName)) { throw new ArgumentException("fileName"); } MythicPackageBlock block; if (m_Blocks.Count > 0) { block = m_Blocks[m_Blocks.Count - 1]; } else { block = NewBlock(); } if (block.IsFull) { block = NewBlock(); } block.AddFile(fileName, innerFolder, flag); }
/// <summary> /// Updates <see cref="Mythic.Package.MythicPackageFile.DataBlockAddress"/> within .uop file, /// <see cref="Mythic.Package.MythicPackageFile.CompressedSize"/> and <see cref="Mythic.Package.MythicPackageFile.DecompressedSize"/>. /// </summary> /// <param name="pointer">Address of <see cref="Mythic.Package.MythicPackageFile.DataBlockAddress"/>.</param> public void UpdateOffsets(ref long pointer) { m_DataBlockAddress = pointer; m_DataBlockLength = 0; // Custom .uop files don't need data header. if (m_Added || m_Modified) { if (!File.Exists(m_SourceFileName)) { throw new FileNotFoundException(); } FileInfo info = new FileInfo(m_SourceFileName); m_CompressedSize = (int)info.Length; m_DecompressedSize = (int)info.Length; byte[] sourceBuffer; using (BinaryReader reader = new BinaryReader(info.OpenRead())) { sourceBuffer = reader.ReadBytes(m_DecompressedSize); } if (sourceBuffer.Length < 4) { m_Compression = CompressionFlag.None; } switch (m_Compression) { case CompressionFlag.Zlib: { m_SourceBuffer = new byte[m_CompressedSize]; ZLibError error = Zlib.Compress(m_SourceBuffer, ref m_CompressedSize, sourceBuffer, m_DecompressedSize, ZLibQuality.Speed); if (error != ZLibError.Okay) { throw new CompressionException(error); } break; } case CompressionFlag.None: { m_SourceBuffer = sourceBuffer; break; } } } else { m_SourceBuffer = null; } pointer += m_DataBlockLength + m_CompressedSize; }
public SortedList<Interval, Block> ConvertArrayToIntervalTree(Block[] blocks, out float compressionRatio, CompressionFlag flag = CompressionFlag.None) { var scanDirection = ScanDirection.Xyz; CompressionMode compressionMode; if (flag == CompressionFlag.None) { float hilbertCompressionRatio; float linearCompressionRatio; EvaluateHilbertTree(blocks, out hilbertCompressionRatio); EvaluateLinearTree(blocks, out linearCompressionRatio, out scanDirection); compressionMode = hilbertCompressionRatio > linearCompressionRatio ? CompressionMode.Hilbert : CompressionMode.Linear; } else { switch (flag) { case CompressionFlag.LinearXyz: compressionMode = CompressionMode.Linear; scanDirection = ScanDirection.Xyz; break; case CompressionFlag.LinearXzy: compressionMode = CompressionMode.Linear; scanDirection = ScanDirection.Xzy; break; case CompressionFlag.LinearYxz: compressionMode = CompressionMode.Linear; scanDirection = ScanDirection.Yxz; break; case CompressionFlag.LinearYzx: compressionMode = CompressionMode.Linear; scanDirection = ScanDirection.Yzx; break; case CompressionFlag.LinearZxy: compressionMode = CompressionMode.Linear; scanDirection = ScanDirection.Zxy; break; case CompressionFlag.LinearZyx: compressionMode = CompressionMode.Linear; scanDirection = ScanDirection.Zyx; break; case CompressionFlag.Hilbert: compressionMode = CompressionMode.Hilbert; break; default: throw new ArgumentOutOfRangeException("flag"); } } if (compressionMode == CompressionMode.Hilbert) { //Console.Out.WriteLine(CompressionMode.Hilbert.ToString()); return ConvertArrayToIntervalTreeHilbert(blocks, out compressionRatio); } else { //Console.Out.WriteLine(scanDirection.ToString()); return ConvertArrayToIntervalTreeLinear(blocks, scanDirection, out compressionRatio); } }
/// <summary> /// Adds a multiple files, located in <paramref name="fileNames"/>, to <see cref="MythicPackageBlock.Files"/> table. /// </summary> /// <param name="fileNames">Array of file paths on HD.</param> /// <param name="innerFolder">Relative folder within the UOP file (destination).</param> /// <param name="flag">Compression type.</param> public void AddFiles(string[] fileNames, string innerFolder, CompressionFlag flag = CompressionFlag.Zlib) { // parse all the files in the array foreach (string file in fileNames) { // add the file inside the first block that can accomodate it AddFile(file, innerFolder, flag); } }
/// <summary> /// Replaces this file with another. /// </summary> /// <param name="fileName">Path to the file on HD.</param> /// <param name="packageFolder">Relative folder within KR.</param> /// <param name="flag">Compression type.</param> public void Replace(string fileName, string packageFolder, CompressionFlag flag) { m_FileName = Path.Combine(packageFolder, Path.GetFileName(fileName)).ToLower(); m_FileHash = HashDictionary.HashFileName(m_FileName); m_SourceFileName = fileName; m_Compression = flag; m_DataBlockLength = 0; m_DataBlockHash = 0; Modified = true; }
/// <summary> /// Adds all files within a certain folder to .uop package. /// </summary> /// <param name="folder">Folder, which contains files.</param> /// <param name="flag">Compression type.</param> public void AddFolder(string folder, CompressionFlag flag = CompressionFlag.Zlib) { // if the folder doesn't exist, we throw an exception if (!Directory.Exists(folder)) { throw new ArgumentException(string.Format("'{0}' is not a folder!", folder)); } // create a stack of strings Stack <string> stack = new Stack <string>(); // add the main folder to the stack stack.Push(folder); // keep looping until we parsed all folders while (stack.Count > 0) { // get the current folder string f = stack.Pop(); // remove the root path (to create the relative path to use inside the uop) string inner = f.Remove(0, folder.Length); try // we use a try because if in the folder there is something weird (like a link), we would get an exception { // add all the files in this folder foreach (string file in Directory.GetFiles(f)) { AddFile(file, inner, flag); } // add all the sub-folders of this folder inside the stack foreach (string newFolder in Directory.GetDirectories(f)) { stack.Push(newFolder); } } catch { } } }
/// <summary> /// Initializes a new instance. /// </summary> /// <param name="fileName">Absolute path to the file on HD.</param> /// <param name="innerFolder">Relative folder within KR (destination).</param> /// <param name="flag">Compression type.</param> public MythicPackageFile(string fileName, string innerFolder, CompressionFlag flag) { if (String.IsNullOrEmpty(fileName)) { throw new ArgumentException("fileName"); } m_FileName = Path.Combine(innerFolder, Path.GetFileName(fileName)).ToLower(); if (m_FileName.StartsWith("\\") || m_FileName.StartsWith("/")) { m_FileName = m_FileName.Substring(1); } m_FileName = m_FileName.Replace('\\', '/'); m_FileHash = HashDictionary.HashFileName(m_FileName); m_SourceFileName = fileName; m_Compression = flag; m_DataBlockLength = 0; m_DataBlockHash = 0; }
// -------------------------------------------------------------- #region ADD/REMOVE FILES // -------------------------------------------------------------- /// <summary> /// Adds a file, located in <paramref name="fileName"/>, to <see cref="MythicPackageBlock.Files"/> table. /// </summary> /// <param name="fileName">Path to a file on HD.</param> /// <param name="innerFolder">Relative folder within the UOP file (destination).</param> /// <param name="flag">Compression type.</param> public void AddFile(string fileName, string innerFolder, CompressionFlag flag = CompressionFlag.Zlib) { // empty file name? throw an exception if (string.IsNullOrEmpty(fileName)) { throw new ArgumentException("fileName"); } // if the file doesn't exist, throw an exception if (!File.Exists(fileName)) { throw new Exception(string.Format("Cannot find {0}!", Path.GetFileName(fileName))); } // initialize the block variable to use MythicPackageBlock block; // do we have at least one block in the file? if (Blocks.Count > 0) { // use the last block block = Blocks[Blocks.Count - 1]; } else // create a new block { block = NewBlock(); } // is the block full? create a new block then if (block.IsFull) { block = NewBlock(); } // add the file to the block block.AddFile(fileName, innerFolder, flag); }
/// <summary> /// Adds a file, located in <paramref name="fileName"/>, to <see cref="Mythic.Package.MythicPackageBlock.Files"/> table. /// </summary> /// <param name="fileName">Path to a file on HD.</param> /// <param name="innerFolder">Relative folder within KR (destination).</param> /// <param name="flag">Compression type.</param> public void AddFile(string fileName, string innerFolder, CompressionFlag flag) { if (String.IsNullOrEmpty(fileName)) { throw new ArgumentException("fileName"); } if (IsFull) { throw new BlockFullException(); } MythicPackageFile index = new MythicPackageFile(fileName, innerFolder, flag); index.Parent = this; index.Index = m_Files.Count; index.Added = true; m_FileCount += 1; m_Files.Add(index); m_Parent.Header.FileCount += 1; }
private void EvaluateCompressionMode <T>(IList <T> blocks, out CompressionFlag optimal, out int nodeCount) { var count = _chunkSize * _chunkSize * _chunkSize; var maxNodesRemoved = 0; optimal = CompressionFlag.LinearXyz; nodeCount = 0; for (var scanDirection = 0; scanDirection < (int)CompressionFlag.None; scanDirection++) { var currentNodesRemoved = 0; var current = default(T); var mappingFunction = _intervalToBlock[(CompressionFlag)scanDirection]; for (var i = 0; i < count; ++i) { var block = blocks[mappingFunction[i]]; if (Equals(block, current)) { ++currentNodesRemoved; continue; } current = block; } if (currentNodesRemoved <= maxNodesRemoved) { continue; } maxNodesRemoved = currentNodesRemoved; nodeCount = count - maxNodesRemoved; optimal = (CompressionFlag)scanDirection; //this means we are mostly uniform and can just exit out now! if (maxNodesRemoved / (float)count > 0.97f) { break; } } }
/// <summary> /// Replace the existing files inside the current package with the one with the same names provided on the files list. /// </summary> /// <param name="filesList">List of the files to replace</param> /// <param name="rootDir">Root directory of the file structure. Used to create the relative path for the files to add. Only necessary if <paramref name="addMissing"/> is true.</param> /// <param name="addMissing">Do we need to add the files that are not present in the current package?</param> /// <param name="flag">Compression type.</param> /// <param name="backupFolder">if a backup folder is specified, all files that needs replacing will be extracted there first.</param> public void ReplaceFiles(List <string> filesList, string rootDir = "", bool addMissing = false, CompressionFlag flag = CompressionFlag.Zlib, string backupFolder = null) { // create a queue for the files Queue <string> q = new Queue <string>(filesList); // keep looping until the queue is empty while (q.Count > 0) { // get the file from the list and make sure is lower case string currFile = q.Dequeue().ToLower(); // search for a file with the current file name MythicPackageFile found = (from b in Blocks from f in b.Files where !string.IsNullOrEmpty(f.FileName) && Path.GetFileName(f.FileName) == Path.GetFileName(currFile) select f).FirstOrDefault(); // if we found a file, we execute the replace if (found != null) { // if a backup is required, we unpack the current file first if (!string.IsNullOrEmpty(backupFolder)) { found.Unpack(backupFolder, true); } // replace the file found.Replace(currFile, found.FilePath, flag); } // if the file doesn't exit, we add it to the archive (if addMissing is true) else if (addMissing) { AddFile(currFile, GetRelativePath(rootDir, currFile)); } } }