/// <summary> /// /// </summary> /// <param name="Archive"></param> /// <param name="OutputFolder"></param> /// <returns></returns> public static bool UnpackArchive(string ArchivePath, string OutputFolder, ScriptBuildProgressDelegate ProgressCallback = null) { string installLocation = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location); string sevenZipPath = Path.Combine(Path.Combine(Path.Combine(installLocation, "Libraries"), "7zip"), "7za.exe"); ProgressParser Parser = new ProgressParser(); Parser.ParsePartialLines = false; Parser.LineSeperator = "\r"; Parser.AddPattern(@"^\s+(\d+)\% \- (.+)$", new ProgressMatch(ProgressMatchType.Progress, ProgressMatchFormat.Float, 100), new ProgressMatch(ProgressMatchType.CurrentFileName, ProgressMatchFormat.String)); ScriptBuildOutputCallbackDelegate OutputCallback = (string Line) => { Parser.Parse(Line); ProgressCallback?.Invoke("Decompressing " + Parser.CurrentFileName + "...", Parser.Progress); }; int exitCode = RunAndWait(sevenZipPath, installLocation, "x \"" + ArchivePath + "\" -o\"" + OutputFolder + "\" -r -y -bsp1", OutputCallback); return(exitCode == 0); /* * * // SharpCompress is waaaaaaaaaaaaaaaaaaaaaaaaaaaaaay to slow :| * try * { * using (IArchive Archive = ArchiveFactory.Open(ArchivePath)) * { * int BufferLength = 1 * 1024 * 1024; * byte[] Buffer = new byte[BufferLength]; * * long TotalUncompressed = Archive.TotalUncompressSize; * long Uncompressed = 0; * * foreach (IArchiveEntry Entry in Archive.Entries.Where(entry => !entry.IsDirectory)) * { * using (Stream ArchiveStream = Entry.OpenEntryStream()) * { * using (FileStream FileStream = new FileStream(OutputFolder + @"\" + Entry.Key, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite)) * { * int BytesRead = 0; * while ((BytesRead = ArchiveStream.Read(Buffer, 0, Buffer.Length)) > 0) * { * FileStream.Write(Buffer, 0, BytesRead); * Uncompressed += BytesRead; * * float Progress = (float)Uncompressed / (float)TotalUncompressed; * ProgressCallback?.Invoke("Unpacking " + Entry.Key + "...", Progress); * } * } * } * } * } * } * catch (Exception Ex) // Should be more specific, but sharpcompress has so little documentation and looking through the code there are so many potential exceptions... * { * Logger.Log(LogLevel.Error, LogCategory.Script, "Failed to extract archive with error: {0}", Ex.Message.ToString()); * return false; * } * * return true; */ }
/// <summary> /// Runs a process (executed via shell) with the given arguments and waits for it to finish. /// </summary> /// <param name="ExePath">Path to exe or command to run</param> /// <param name="WorkingDirectory">Directory to execute command within.</param> /// <param name="Arguments">Arguments to pass to command.</param> /// <returns>Exit code returned by the process. If process fails to start -1 is returned.</returns> public static int RunAndWait(string ExePath, string WorkingDirectory, string Arguments, ScriptBuildOutputCallbackDelegate OutputCallback = null) { Logger.Log(LogLevel.Info, LogCategory.Script, "Running (and waiting for result) '{0}' in '{1}' with arguments '{2}'.", ExePath, WorkingDirectory, Arguments); try { ProcessStartInfo StartInfo = new ProcessStartInfo(); StartInfo.FileName = ExePath; StartInfo.WorkingDirectory = WorkingDirectory; StartInfo.Arguments = Arguments; StartInfo.RedirectStandardOutput = true; StartInfo.RedirectStandardError = true; StartInfo.UseShellExecute = false; StartInfo.CreateNoWindow = true; Process process = Process.Start(StartInfo); ChildProcessTracker.AddProcess(process); /*process.OutputDataReceived += delegate (object sender, DataReceivedEventArgs e) * { * OutputLineBuilder.Add(e.Data); * while (true) * { * string Line = OutputLineBuilder.Read(); * if (Line == null) * { * break; * } * Logger.Log(LogLevel.Info, LogCategory.Main, "{0}", Line); * } * * OutputCallback?.Invoke(e.Data); * }; * process.ErrorDataReceived += delegate (object sender, DataReceivedEventArgs e) * { * while (true) * { * string Line = ErrorLineBuilder.Read(); * if (Line == null) * { * break; * } * Logger.Log(LogLevel.Info, LogCategory.Main, "{0}", Line); * } * * OutputCallback?.Invoke(e.Data); * }; * * //process.BeginErrorReadLine(); * //process.BeginOutputReadLine(); */ Func <StreamReader, ConcurrentQueue <string>, bool> QueueBuilder = (StreamReader Reader, ConcurrentQueue <string> Queue) => { int Char = Reader.Read(); if (Char < 0) { return(true); } string Value = new string((char)Char, 1); Queue.Enqueue(Value); return(false); }; Func <ConcurrentQueue <string>, LineBuilder, bool> Parser = (ConcurrentQueue <string> Queue, LineBuilder Builder) => { while (Queue.Count > 0) { string Value = ""; if (Queue.TryDequeue(out Value)) { Builder.Add(Value); while (true) { string Line = Builder.Read(); if (Line == null) { break; } Logger.Log(LogLevel.Info, LogCategory.Main, "{0}", Line); } OutputCallback?.Invoke(Value); } } return(true); }; Func <LineBuilder, bool> DrainBuilder = (LineBuilder Builder) => { Builder.End(); while (true) { string Line = Builder.Read(); if (Line == null) { break; } Logger.Log(LogLevel.Info, LogCategory.Main, "{0}", Line); } return(true); }; LineBuilder OutputLineBuilder = new LineBuilder(); LineBuilder ErrorLineBuilder = new LineBuilder(); // This is retarded, the blocking api for all of this in C# is s***e. ConcurrentQueue <string> OutputData = new ConcurrentQueue <string>(); ConcurrentQueue <string> ErrorData = new ConcurrentQueue <string>(); Task OutputTask = Task.Run(() => { while (!QueueBuilder(process.StandardOutput, OutputData)) { ; } }); Task ErrorTask = Task.Run(() => { while (!QueueBuilder(process.StandardError, ErrorData)) { ; } }); // Wait for process to exit. while (!OutputTask.IsCompleted || !ErrorTask.IsCompleted || OutputData.Count > 0 || ErrorData.Count > 0) { Parser(OutputData, OutputLineBuilder); Parser(ErrorData, ErrorLineBuilder); Thread.Sleep(1); } process.WaitForExit(); // Drain any output. DrainBuilder(OutputLineBuilder); DrainBuilder(ErrorLineBuilder); Logger.Log(LogLevel.Info, LogCategory.Script, "Finished running with exit code {0}.", process.ExitCode); return(process.ExitCode); } catch (Exception Ex) { Logger.Log(LogLevel.Error, LogCategory.Script, "Failed to run program with error: {0}", Ex.Message.ToString()); return(-1); } }