diff --git a/Patcher/.idea/.idea.Patcher/.idea/avalonia.xml b/Patcher/.idea/.idea.Patcher/.idea/avalonia.xml
new file mode 100644
index 0000000..49f50b2
--- /dev/null
+++ b/Patcher/.idea/.idea.Patcher/.idea/avalonia.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="AvaloniaProject">
+    <option name="projectPerEditor">
+      <map>
+        <entry key="PatchClient/Views/MainWindow.axaml" value="PatchClient/PatchClient.csproj" />
+        <entry key="PatchGenerator/Views/MainWindow.axaml" value="PatchGenerator/PatchGenerator.csproj" />
+      </map>
+    </option>
+  </component>
+</project>
\ No newline at end of file
diff --git a/Patcher/PatcherUtils/Helpers/XdeltaProcessHelper.cs b/Patcher/PatcherUtils/Helpers/XdeltaProcessHelper.cs
new file mode 100644
index 0000000..cd4af72
--- /dev/null
+++ b/Patcher/PatcherUtils/Helpers/XdeltaProcessHelper.cs
@@ -0,0 +1,151 @@
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Text;
+using System.Threading;
+using PatcherUtils.Model;
+
+namespace PatcherUtils.Helpers;
+
+public class XdeltaProcessHelper
+{
+    private readonly int _timeout = (int)TimeSpan.FromMinutes(5).TotalMilliseconds;
+    private string _args;
+    private string _sourcePath;
+    private string _deltaPath;
+    private string _decodedPath;
+    private bool _isDebug;
+    
+    public XdeltaProcessHelper(string args, string sourcePath, string deltaPath, string decodedPath, bool isDebug)
+    {
+        _args = args;
+        _sourcePath = sourcePath;
+        _deltaPath = deltaPath;
+        _decodedPath = decodedPath;
+        _isDebug = isDebug;
+    }
+
+    public bool Run() => _isDebug ? RunDebug() : RunNormal();
+    
+    private bool RunNormal()
+    {
+        using var proc = new Process();
+
+        proc.StartInfo = new ProcessStartInfo
+        {
+            FileName = LazyOperations.XDelta3Path,
+            Arguments = $"{_args} \"{_sourcePath}\" \"{_deltaPath}\" \"{_decodedPath}\"",
+            CreateNoWindow = true
+        };
+
+        if (proc.WaitForExit(_timeout))
+        {
+            PatchLogger.LogError("xdelta3 process timed out");
+            PatchLogger.LogDebug($"xdelta exit code: {proc.ExitCode}");
+            return false;
+        }
+
+        PatchLogger.LogDebug($"xdelta exit code: {proc.ExitCode}");
+        return true;
+    }
+
+    private bool DebugPathsCheck()
+    {
+        try
+        {
+            var stream = File.Open(_sourcePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None);
+            stream.Close();
+            stream.Dispose();
+            PatchLogger.LogDebug($"File is openable: {_sourcePath}");
+        }
+        catch (Exception ex)
+        {
+            PatchLogger.LogException(ex);
+            return false;
+        }
+
+        try
+        {
+            var stream = File.Open(_deltaPath, FileMode.Open, FileAccess.ReadWrite, FileShare.None);
+            stream.Close();
+            stream.Dispose();
+            PatchLogger.LogDebug($"File is openable: {_deltaPath}");
+        }
+        catch (Exception ex)
+        {
+            PatchLogger.LogException(ex);
+            return false;
+        }
+
+        return true;
+    }
+
+    private bool RunDebug()
+    {
+        if (!DebugPathsCheck())
+        {
+            return false;
+        }
+        
+        using var proc = new Process();
+
+        proc.StartInfo = new ProcessStartInfo
+        {
+            FileName = LazyOperations.XDelta3Path,
+            Arguments = $"{_args} \"{_sourcePath}\" \"{_deltaPath}\" \"{_decodedPath}\"",
+            RedirectStandardOutput = true,
+            RedirectStandardError = true,
+            CreateNoWindow = true
+        };
+
+        var outputBuilder = new StringBuilder();
+        var errorBuilder = new StringBuilder();
+
+        using AutoResetEvent outputWaitHandle = new AutoResetEvent(false);
+        using AutoResetEvent errorWaitHandle = new AutoResetEvent(false);
+
+        proc.OutputDataReceived += (s, e) =>
+        {
+            if (e.Data == null)
+            {
+                outputWaitHandle.Set();
+            }
+            else
+            {
+                outputBuilder.AppendLine(e.Data);
+            }
+        };
+
+        proc.ErrorDataReceived += (s, e) =>
+        {
+            if (e.Data == null)
+            {
+                errorWaitHandle.Set();
+            }
+            else
+            {
+                errorBuilder.AppendLine(e.Data);
+            }
+        };
+
+        proc.Start();
+
+        proc.BeginOutputReadLine();
+        proc.BeginErrorReadLine();
+
+        if (!proc.WaitForExit(_timeout) || !outputWaitHandle.WaitOne(_timeout) || !errorWaitHandle.WaitOne(_timeout))
+        {
+            PatchLogger.LogError("xdelta3 process timed out");
+            PatchLogger.LogDebug($"xdelta exit code: {proc.ExitCode}");
+            return false;
+        }
+        
+        PatchLogger.LogDebug("__xdelta stdout__");
+        PatchLogger.LogDebug(outputBuilder.ToString());
+        PatchLogger.LogDebug("__xdelta stderr__");
+        PatchLogger.LogDebug(errorBuilder.ToString());
+        PatchLogger.LogDebug($"xdelta exit code: {proc.ExitCode}");
+
+        return true;
+    }
+}
\ No newline at end of file
diff --git a/Patcher/PatcherUtils/PatchHelper.cs b/Patcher/PatcherUtils/PatchHelper.cs
index 76682ac..6710e21 100644
--- a/Patcher/PatcherUtils/PatchHelper.cs
+++ b/Patcher/PatcherUtils/PatchHelper.cs
@@ -6,6 +6,7 @@ using System.Diagnostics;
 using System.IO;
 using System.Linq;
 using System.Security.Cryptography;
+using PatcherUtils.Helpers;
 
 namespace PatcherUtils
 {
@@ -104,71 +105,12 @@ namespace PatcherUtils
 
             var xdeltaArgs = $"-d {(debugOutput ? "-v -v" : "")} -f -s";
 
-            if (debugOutput)
+            var xdeltaHelper =
+                new XdeltaProcessHelper(xdeltaArgs, SourceFilePath, DeltaFilePath, decodedPath, debugOutput);
+
+            if (!xdeltaHelper.Run())
             {
-                try
-                {
-                    var stream = File.Open(SourceFilePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None);
-                    stream.Close();
-                    stream.Dispose();
-                    PatchLogger.LogDebug($"File is openable: {SourceFilePath}");
-                }
-                catch (Exception ex)
-                {
-                    PatchLogger.LogException(ex);
-                }
-
-                try
-                {
-                    var stream = File.Open(SourceFilePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None);
-                    stream.Close();
-                    stream.Dispose();
-                    PatchLogger.LogDebug($"File is openable: {DeltaFilePath}");
-                }
-                catch (Exception ex)
-                {
-                    PatchLogger.LogException(ex);
-                }
-            }
-
-            var proc = Process.Start(new ProcessStartInfo
-            {
-                FileName = LazyOperations.XDelta3Path,
-                Arguments = $"{xdeltaArgs} \"{SourceFilePath}\" \"{DeltaFilePath}\" \"{decodedPath}\"",
-                RedirectStandardError = true,
-                RedirectStandardOutput = true,
-                CreateNoWindow = true
-            });
-
-            if (proc == null)
-            {
-                PatchLogger.LogError($"xdelta3 process failed to start: {nameof(proc)} is null");
-                return (false, "xdelta3 process failed to start");
-            }
-            
-            proc.WaitForExit();
-
-            if (debugOutput)
-            {
-                try
-                {
-                    PatchLogger.LogDebug($"xdelta exit code :: {proc.ExitCode}");
-                    PatchLogger.LogDebug("___Dumping xdelta stdout___");
-                    while (!proc.StandardOutput.EndOfStream)
-                    {
-                        PatchLogger.LogDebug(proc.StandardOutput.ReadLine());
-                    }
-
-                    PatchLogger.LogDebug("___Dumping xdelta stderr___");
-                    while (!proc.StandardError.EndOfStream)
-                    {
-                        PatchLogger.LogDebug(proc.StandardError.ReadLine());
-                    }
-                }
-                catch (Exception ex)
-                {
-                    PatchLogger.LogException(ex);
-                }
+                return (false, "something went wrong during the xdelta3 process");
             }
 
             if (File.Exists(decodedPath))