private ErrCode ExDir(PRNode n, DirectoryInfo di, ProgressTracker pt) { Log($"Sel to Ext: {n.TableNode.DisplayName}", LogType.GUI); DirectoryInfo tDir = di; ErrCode result = ErrCode.SUCCESS; foreach (PRTableNode tn in FileTable.GetChildrenOf(n.GUID)) { Log($"Child: {tn.DisplayName}", LogType.Parse); if (tn.Flags.HasFlag(NodeFlag.Directory)) { Log($"ExDir {tn.DisplayName}", LogType.IO); if (!Utils.DebugFlags.HasFlag(Debug.NO_RECURSION)) { result &= ExDir(tn.Node, Directory.CreateDirectory(tDir.FullName + @"\" + Utils.BaseToString(tn.Name)), pt); } } else if (tn.Flags.HasFlag(NodeFlag.File)) { //Log($"ExFile {tn.DisplayName}", LogType.IO); result &= ExFile(tn.Node, tDir, pt); } } if (result == ErrCode.SUCCESS) { Log("No errors on extraction!", LogType.IO); return(ErrCode.SUCCESS); } else { return(result); } }
public ErrCode Extract(PRNode n, DirectoryInfo di, out ProgressTracker pt, string altName = "") { pt = new ProgressTracker(); if (n.Flags.HasFlag(NodeFlag.Virtual)) { return(ErrCode.NODE_INVALID_OPERATION); } ErrCode result = ErrCode.SUCCESS; if (n.Flags.HasFlag(NodeFlag.Directory)) { Log($"DIR: {n.TableNode.DisplayName}", LogType.Info); if (!Directory.Exists(di.FullName + @"\" + n.TableNode.DisplayName)) { DirectoryInfo tDir = Directory.CreateDirectory(di.FullName + @"\" + n.TableNode.DisplayName); Log($"CreateDir '{tDir.FullName}'", LogType.IO); result &= ExDir(n, tDir, pt); } } else { Log($"FILE: {n.TableNode.DisplayName}", LogType.Info); result &= ExFile(n, di, pt, altName); } if (result != ErrCode.SUCCESS) { Log($"ERR: {pt[pt.Count - 1]}", LogType.Error); } return(result); }
/// <summary> /// Only to be called to instantiate a ROOT node /// </summary> public static PRNode CreateRootNode() { PRNode rootNode = new PRNode(Utils.StringToBase("/"), Guid.Empty, Attrib.Root); rootNode.Parent = rootNode.GUID; return(rootNode); }
/// <summary> /// Primary entry point /// </summary> public Archive() { Log("Initializing PRX manager", LogType.Init); Utils.InitDriveInfo(); ErrCode initErr = ErrCode.NULL; initErr = SetTempFile(); Log("Creating root node", LogType.Info); RootNode = PRNode.CreateRootNode(); FileTable = new PRTable(); PackRatUI.Program.Archive = this; MasterFileTable = FileTable; if (initErr > ErrCode.SUCCESS) { Error(initErr); if (initErr.HasFlag(ErrCode.EX_ACCESS_DENIED)) { System.Windows.Forms.MessageBox.Show("ERR_ACCESS_DENIED" + Environment.NewLine + "Please try running as Administrator."); Environment.Exit(0); } } }
/// <summary> /// Create a new PRTableNode /// </summary> /// <param name="directoryInfo" type="DirectoryInfo">DirectoryInfo object</param> /// <param name="node">PRNode representing the directory</param> /// <param name="name">Name of the directory</param> public PRTableNode(DirectoryInfo dir, PRNode node, string name, Guid parent) : this(node, name, parent) { this.Attribs = Utils.GetAttribs(dir); this.SizeUncompressed = 0; this.SizeCompressed = 0; this.DateCreated = Utils.DateTimeToUnixTimestamp(dir.CreationTime); this.DateModified = Utils.DateTimeToUnixTimestamp(dir.LastWriteTime); //Log($"New {node.Flags} {name} in {Utils.BaseToString(FileTable?[node.Parent]?.Name)}", LogType.FTable); }
/// <summary> /// Add a list<string> of files to the archive, a parent node may be specified /// </summary> /// <param name="files">List of files</param> /// <param name="parent">[Optional] Parent node (Will use current directory if none specified)</param> /// <returns></returns> public async virtual void AddFiles(List <string> files, PRNode parent = null) { //TODO Parallelize this string list = ""; foreach (string s in files) { list = list + $"{s} "; } if (parent == null) { parent = RootNode; } Log($"List of Files to add:" + Environment.NewLine + $"{list}", LogType.IO); List <string> filesToAdd = new List <string>(); foreach (string f in files) { if (!File.Exists(f) && !Directory.Exists(f)) { Log($"{ErrCode.EX_FILE_NOT_FOUND}: {f}", LogType.Error); } else { filesToAdd.Add(f); } } files = filesToAdd; if (files.Count == 0) { Log($"ERR: No files to add!", LogType.Warning); return;// ErrCode.APP_FAIL_TO_ADD; } ErrCode result = ErrCode.SUCCESS; PRXBusy = true; Task t = Task.Factory.StartNew(() => { result = this._AddFiles(files, parent); OnTaskComplete(result == ErrCode.SUCCESS ? true : false, result); }); await t; if (t.IsCompleted) { PRXBusy = false; return;// result | ErrCode.APP_THREAD_COMPLETED; } else { PRXBusy = false; Error(ErrCode.APP_THREAD_FAILED); } }
/// <summary> /// Recurses a directory structure on disk /// </summary> /// <param name="path">The root path from which to begin recursion</param> /// <returns>ErrCode</returns> private ErrCode RecurseTree(DirectoryInfo directory, ProgressTracker progress, PRNode parent) { if (parent == null) { parent = RootNode; } FileInfo[] files = directory.GetFiles(); DirectoryInfo[] dirs = directory.GetDirectories(); try { foreach (FileInfo fi in files) { tfi = fi; ErrCode result = AddFile(fi.FullName, progress, parent); } //BUG WHAT THE F**K IS GOING ON-- FIX THIS UNDEF SHIT // we get random NullRef exceptions if the files ownership or permissions aren't perfectly set.. can't track it down. foreach (DirectoryInfo di in dirs) { tdi = di; //AddDirectory(); PRNode thisDir = new PRNode(di, parent.GUID); FileTable.Add(new PRTableNode(di, thisDir, di.Name, parent.GUID)); progress.FoldersProcessed++; if (!Utils.DebugFlags.HasFlag(Debug.NO_RECURSION)) { RecurseTree(di, progress, thisDir); } } } catch (NullReferenceException ex) { Log($"NullRef: {Utils.MapExceptionName(ex).ToString()}", LogType.Critical); Log($"{ex.Message}", LogType.Exception); Log($"{tdi.Name}", LogType.Exception); Log($"{tfi.Name}", LogType.Exception); } catch (UnauthorizedAccessException ex) { Log($"AccessEx: {Utils.MapExceptionName(ex).ToString()}", LogType.Critical); Log($"{ex.Message}", LogType.Exception); Log($"{tdi.Name}", LogType.Exception); Log($"{tfi.Name}", LogType.Exception); } catch (Exception ex) { Log($"UnkEX: {Utils.MapExceptionName(ex).ToString()}", LogType.Critical); Log($"{ex.Message}", LogType.Exception); Log($"{tdi.Name}", LogType.Exception); Log($"{tfi.Name}", LogType.Exception); } return(ErrCode.SUCCESS); }
/// <summary> /// Adds one node to another. (eg; Parent adopts Child) /// </summary> /// <param name="parent">Parent node</param> /// <param name="child">Child to be adopted</param> /// <returns><ErrCode>ErrCode</ErrCode></returns> public ErrCode AddChildTo(PRNode parent, PRNode child) { Log($"Setting {Nodes[child.GUID].Name}'s parent to {Nodes[parent.GUID].Name}", LogType.Info); if (Nodes.ContainsKey(child.GUID)) { Nodes[child.GUID].Parent = parent.GUID; return(ErrCode.SUCCESS); } return(ErrCode.APP_NONEXISTANT_NODE); }
public ErrCode AddChild(PRNode n) { if (this.Node == n) { Log("Can't set ourself as our own parent!", LogType.Error); return(ErrCode.NODE_INVALID_OPERATION); } n.TableNode.Parent = this.GUID; return(ErrCode.SUCCESS); }
/// <summary> /// Helper function routing data from string[] to List<string&rt; and passed to AddFiles(List, PRNode) /// </summary> /// <param name="files">string[] of files and/or directories</param> /// <param name="parent">PRNode of which these nodes will descend</param> public virtual void AddFiles(string[] files, PRNode parent = null) { List <string> fileList = new List <string>(); foreach (string s in files) { fileList.Add(s); } this.AddFiles(fileList, parent); }
/// <summary> /// Creates a Master FileTable node, shouldn't be called by outsiders /// </summary> private PRTableNode(PRNode node, string name, Guid parent) { Encoding enc = System.Text.Encoding.UTF8; Parent = parent; this.GUID = node.GUID; this.Parent = node.Parent; this.NameLength = Convert.ToBase64String(enc.GetBytes(name)).Length; this.Name = Convert.ToBase64String(enc.GetBytes(name)); this.Node = node; this.Flags = node.Flags; }
/// <summary> /// Create a new FILE PRTableNode /// </summary> /// <param name="fileInfo" type="FileInfo">FileInfo object</param> /// <param name="node">PRNode representing the file</param> /// <param name="name">Name of the file</param> public PRTableNode(FileInfo file, PRNode node, string name, Guid parent) : this(node, name, parent) { try { this.Attribs = Utils.GetAttribs(file); this.SizeCompressed = file.Length; this.SizeUncompressed = 0; this.DateCreated = Utils.DateTimeToUnixTimestamp(file.CreationTime); this.DateModified = Utils.DateTimeToUnixTimestamp(file.LastWriteTime); //Log($"New {node.Flags} {name} in {Utils.BaseToString(FileTable?[node.Parent]?.Name)}", LogType.FTable); } catch (Exception ex) { Log($"Exception: {Utils.MapExceptionName(ex).ToString()}", LogType.Critical); } }
protected virtual string NameConflict(PRNode parent, string newname) { if (OnNameConflict != null && !AutoRenameConflicts) { List <string> kidsByName = FileTable[parent.GUID].ChildrenByName; while (kidsByName.Contains(newname, StringComparer.OrdinalIgnoreCase)) { newname = OnNameConflict(parent, newname); } return(newname); } else if (OnNameConflict == null & !AutoRenameConflicts) { Error(ErrCode.APP_UNHANDLED_EVENT | ErrCode.APP_NAME_CONFLICT); throw new MissingMethodException("RequestPWD event is not handled."); Environment.Exit(SystemErrorCodes.ERROR_INVALID_EVENT_COUNT); // pragma'd unreachable code because users can 'continue' exceptions } else { string altname = newname; string ext = ""; int fileExtPos = altname.LastIndexOf("."); if (fileExtPos >= 0) { altname = altname.Substring(0, fileExtPos); ext = altname.Substring(fileExtPos, altname.Length - fileExtPos - 1); } int count = 1; List <PRTableNode> nodes; nodes = FileTable.GetChildrenOf(parent.GUID); List <string> names = new List <string>(); foreach (PRTableNode n in nodes) { names.Add(n.DisplayName); } while (names.Contains(altname, StringComparer.OrdinalIgnoreCase)) { // this should HOPEFULLY append "(incrementing#)" to the filename PRE-extension string tempFileName = string.Format("{0} ({1})", altname, count++); altname = tempFileName + ext; } return(altname); } }
/// <summary> /// Extract specified PRNode to specified file/path /// </summary> /// <param name="node">Node to extract</param> /// <param name="path"></param> /// <returns><errcode>Result</errcode></returns> private ErrCode ExFile(PRNode node, DirectoryInfo path, ProgressTracker pt, string altName = "") { Log($"Creating streams for IO ..", LogType.Init); PRTableNode prt = node.TableNode; string fName = altName == "" ? prt.DisplayName : altName; FileStream output = new FileStream(Path.Combine(path.FullName, fName), FileMode.Create); BinaryWriter bw = new BinaryWriter(output); PRXSwapFile.Seek(prt.Offset, 0); // create a hash algorithm to be used in the while(read) loop HashAlgorithm hasher = new MD5CryptoServiceProvider(); long bytesRead; long tbr = 0; long bytesToRead = prt.SizeUncompressed; var buffer = new byte[Utils.IO_BUFFER_SIZE]; Log($"Writing data..", LogType.IO); long bytesSoFar = 0; while (bytesToRead > 0) { long bytesLeft = prt.SizeUncompressed - bytesSoFar; long bufSize = bytesToRead > Utils.IO_BUFFER_SIZE ? Utils.IO_BUFFER_SIZE : bytesLeft; bytesRead = PRXSwapFile.Read(buffer, 0, (int)bufSize); bytesSoFar += bytesRead; hasher.TransformBlock(buffer, 0, (int)bufSize, null, 0); bytesToRead -= bytesRead; tbr += bytesRead; bw.Write(buffer, 0, (int)bytesRead); } hasher.TransformFinalBlock(new byte[0], 0, 0); byte[] hash = hasher.Hash; if (HashToHex(hash) == prt.Hash) { Log($"Ex OK [{prt.HashString}]", LogType.Info); pt[node] = ErrCode.SUCCESS; return(ErrCode.SUCCESS); } else { Log($"Fail! [{prt.Hash}]", LogType.Critical); pt[node] = ErrCode.APP_FAIL_HASH; return(ErrCode.APP_FAIL_HASH); } }
public ErrCode this[PRNode prn] { get { if (Errors.ContainsKey(prn)) { return(Errors[prn]); } else { return(ErrCode.NULL); } } set { if (Errors.ContainsKey(prn)) { Errors[prn] = value; } else { Errors.Add(prn, value); } } }
/// <summary> /// Add a file to the prx archive beneath specified node /// </summary> /// <param name="file">File to be added</param> /// <param name="parent">Parent node of added file</param> /// <returns><errcode>Result</errcode></returns> private ErrCode AddFile(string file, ProgressTracker progress, PRNode parent) { ErrCode err = ErrCode.NULL; if (!File.Exists(file)) { err = ErrCode.APP_FAIL_TO_READ; return(err); } // forcefully demand read access to the target file, returning ErrCode.EX_ACCESS_DENIED if this fails try { new FileIOPermission(FileIOPermissionAccess.Read, file).Demand(); } catch (SecurityException se) { err = ErrCode.EX_ACCESS_DENIED; return(MapExceptionName(se)); } catch (NullReferenceException ex) { Log($"Ex: {MapExceptionName(ex)}", LogType.Error); } long startOfWrite = 0; FileInfo fi = new FileInfo(file); PRNode node = new PRNode(fi.Name, parent.GUID); PRTableNode prtn = new PRTableNode(fi, node, fi.Name, parent.GUID); FileStream fs = null; node.AssignTableNode(prtn); //TODO Refactor this to a BinaryWriter extension: BinaryWriter.Write(PRNode) try { BinaryWriter bw = new BinaryWriter(PRXSwapFile); fs = new FileStream(fi.FullName, FileMode.Open); // jump to the end of the swap file bw.Seek(0, SeekOrigin.End); startOfWrite = bw.BaseStream.Position; node.TableNode.SizeUncompressed = fi.Length; // write the data header bw.Write(node.GUID.ToByteArray()); bw.Write(new string('X', 16)); // we write a spacer for the hash that'll be inserted after we actually parse the data bw.Write((ulong)fi.Length); long dataOffset = bw.BaseStream.Position; node.TableNode.Offset = (long)dataOffset; // create a hash algorithm to be used in the while(read) loop HashAlgorithm hasher = new MD5CryptoServiceProvider(); int bytesRead; var buffer = new byte[Utils.IO_BUFFER_SIZE]; progress.BytesProcessedInFile = 0; progress.TotalBytesInFile = fi.Length; while ((bytesRead = fs.Read(buffer, 0, buffer.Length)) > 0) { progress.BytesProcessed += bytesRead; progress.BytesProcessedInFile += bytesRead; //TODO encrypt/compress data [optional] bw.Write(buffer, 0, bytesRead); hasher.TransformBlock(buffer, 0, bytesRead, null, 0); ProgressReport(progress); } hasher.TransformFinalBlock(new byte[0], 0, 0); byte[] hash = hasher.Hash; prtn.Hash = HashToHex(hash); PRXSwapFile.Seek(dataOffset - 24, SeekOrigin.Begin); //bw.Seek(dataOffset - 24, SeekOrigin.Begin); PRXSwapFile.Write(hash, 0, hash.Length); //bw.Write(hash); //Log($"Wrote {progress.BytesProcessedInFile}b [{prtn.HashString}] at offset: {dataOffset}, EOD: {bw.BaseStream.Position}", LogType.IO); // move back to the end of the stream just for safety bw.Seek(0, SeekOrigin.End); PRXSwapFile.Seek(0, SeekOrigin.End); //BUG Not properly inserting all files anymore.. WTF //INFO Have to close the BinaryWriter without colosing the underlying stream.. F**K. //bw.Dispose(); // supposedly the binarywriter is closed *WITHOUT* nuking PRXSwapFile.. } catch (Exception ex) { //TODO remove whatever may have been written in the event of a failure // this'll be added at a later date when I'm able to produce an error //Log($"EX: {MapExceptionName(ex)} on {fi.Name} in {fi.DirectoryName}", LogType.Exception); return(MapExceptionName(ex)); } finally { fs.Dispose(); //GC.Collect(1); //Cardinal sin, I know.. But I can't dispose of the binarywriter, has to be collected so it won't close PRXSwapFile } // increment the number of files processed in our tracker progress.FilesProcessed++; // finally, if and only if this is ErrCode.SUCCESS- add the node as a TableNode to our FileTable if (err == ErrCode.NULL) { FileTable.Add(prtn); } return(ErrCode.SUCCESS); }
/// <summary> /// Adds a directory entry to the archive /// </summary> /// <param name="dir">Directory to be added</param> /// <param name="progress">ProgressTracker</param> /// <param name="parent">Parent node of which this node belongs</param> /// <returns>ErrCode</returns> private PRNode AddDirectory(DirectoryInfo di, ProgressTracker progress, PRNode parent) { PRNode retVal = new PRNode(di, parent.GUID); PRTableNode prtn = new PRTableNode(di, retVal, di.Name, parent.GUID); FileTable.Add(prtn); //BUG Directories aren't actually written to the archive, dumbass. #region failure //long startOfWrite = 0; //retVal.AssignTableNode(prtn); //try { // BinaryWriter bw = new BinaryWriter(PRXSwapFile); // bw.Seek(0, SeekOrigin.End); // startOfWrite = bw.BaseStream.Position; // bw.Write(retVal.GUID.ToByteArray()); // bw.Write(new string('X', 16)); // we write a spacer for the hash that'll be inserted after we actually parse the data // bw.Write((short)retVal.TableNode.DisplayName.Length); // long dataOffset = bw.BaseStream.Position; // retVal.TableNode.Offset = (long)dataOffset; // // create a hash algorithm to be used in the while(read) loop // HashAlgorithm hasher = new MD5CryptoServiceProvider(); // int bytesRead; // var buffer = new byte[Utils.IO_BUFFER_SIZE]; // progress.BytesProcessedInFile = 0; // progress.TotalBytesInFile = fi.Length; // while ((bytesRead = fs.Read(buffer, 0, buffer.Length)) > 0) { // progress.BytesProcessed += bytesRead; // progress.BytesProcessedInFile += bytesRead; // //TODO encrypt/compress data [optional] // bw.Write(buffer, 0, bytesRead); // hasher.TransformBlock(buffer, 0, bytesRead, null, 0); // ProgressReport(progress); // } // hasher.TransformFinalBlock(new byte[0], 0, 0); // byte[] hash = hasher.Hash; // prtn.Hash = HashToHex(hash); // PRXSwapFile.Seek(dataOffset - 24, SeekOrigin.Begin); // //bw.Seek(dataOffset - 24, SeekOrigin.Begin); // PRXSwapFile.Write(hash, 0, hash.Length); // //bw.Write(hash); // //Log($"Wrote {progress.BytesProcessedInFile}b [{prtn.HashString}] at offset: {dataOffset}, EOD: {bw.BaseStream.Position}", LogType.IO); // // move back to the end of the stream just for safety // bw.Seek(0, SeekOrigin.End); // PRXSwapFile.Seek(0, SeekOrigin.End); // //BUG Not properly inserting all files anymore.. WTF // //INFO Have to close the BinaryWriter without colosing the underlying stream.. F**K. // //bw.Dispose(); // // supposedly the binarywriter is closed *WITHOUT* nuking PRXSwapFile.. // } //catch (Exception ex) { // //TODO remove whatever may have been written in the event of a failure // // this'll be added at a later date when I'm able to produce an error // //Log($"EX: {MapExceptionName(ex)} on {fi.Name} in {fi.DirectoryName}", LogType.Exception); // return MapExceptionName(ex); // } //finally { // fs.Dispose(); // //GC.Collect(1); //Cardinal sin, I know.. But I can't dispose of the binarywriter, has to be collected so it won't close PRXSwapFile // } #endregion failure return(retVal); }
/// <summary> /// Creates a virtual UP table node for navigation purposes /// </summary> /// <param name="n">Virual PRNode from which to base this</param> /// <returns>Virtual PRTableNode (that doesn't exist in the file table)</returns> public static PRTableNode CreateUp(PRNode n) { PRTableNode prtn = new PRTableNode(n); return(prtn); }
/// <summary> /// Recurses a directory tree on disk to prime the ProgressTracker /// </summary> /// <param name="progress">ProgressTracker to update</param> /// <param name="directory">Directory to crawl</param> /// <param name="parent">Top level node</param> /// <returns></returns> private ErrCode RecurseForStatus(ProgressTracker progress, DirectoryInfo directory, PRNode parent = null) { foreach (FileInfo fi in directory.GetFiles()) { progress.TotalBytes += (ulong)fi.Length; progress.TotalFiles++; } foreach (DirectoryInfo di in directory.GetDirectories()) { progress.TotalFolders++; //PRNode thisDir = new PRNode(di, parent.GUID); //FileTable.Add(new PRTableNode(di, thisDir, di.Name, parent.GUID)); RecurseForStatus(progress, di, parent); } return(ErrCode.SUCCESS); }
/// <summary> /// Get a MemoryStream containing the data of the specified node /// </summary> /// <param name="node">Requested PRNode</param> /// <returns><memorystream></memorystream></returns> public MemoryStream GetData(PRNode node) { return(new MemoryStream()); }
/// <summary> /// Get a list of children of the specified PRNode /// </summary> /// <param name="parent">Parent Node</param> /// <returns><list type="PRTableNode">List of PRTableNodes</list></returns> public virtual List <PRTableNode> GetChildren(PRNode n) { return(this.GetChildren(n.GUID)); }
private ErrCode _AddFiles(List <string> files, PRNode parent = null) { ProgressTree = new Dictionary <PRNode, ErrCode>(); ErrCode results = ErrCode.NULL; if (FileTable == null || FileTable?.Count == 0) { ErrCode header = ErrCode.NULL; try { header = WriteHeader(PRXSwapFile); } catch (Exception ex) { results = Utils.MapExceptionName(ex); ErrCode ecex = Utils.MapExceptionName(ex); Log($"{header}, " + $"{ecex}", LogType.Critical); } } ProgressTracker progress = new ProgressTracker(); Log("List-gen recursion", LogType.Info); Dictionary <string, PRNode> cache = new Dictionary <string, PRNode>(); foreach (string f in files) { if (File.Exists(f)) { progress.TotalBytes += (ulong)f.Length; progress.TotalFiles++; } else if (Directory.Exists(f)) { DirectoryInfo di = new DirectoryInfo(f); PRNode nprn = new PRNode(di, parent.GUID); FileTable.Add(new PRTableNode(di, nprn, di.Name, parent.GUID)); cache.Add(BaseToString(nprn.TableNode.Name), nprn); RecurseForStatus(progress, di, nprn); } else { results &= ErrCode.EX_FILE_NOT_FOUND; Log($"{ErrCode.EX_FILE_NOT_FOUND}: {f}", LogType.Critical); } } Log("IO cycle start", LogType.IO); try { foreach (string f in files) { if (File.Exists(f)) { results &= AddFile(f, progress, parent); } if (Directory.Exists(f)) { DirectoryInfo tdi = new DirectoryInfo(f); results &= RecurseTree(tdi, progress, cache[tdi.Name]); } } } catch (Exception ex) { results &= Utils.MapExceptionName(ex); Log($"Exception: {Utils.MapExceptionName(ex).ToString()}", LogType.Critical); } //OnTaskComplete(false); if (results == ErrCode.NULL) { results = ErrCode.SUCCESS; } return(results); }
private PRTableNode(PRNode n) { this.Node = n; }