mirror of
https://github.com/oMaN-Rod/nxDumpFuse.git
synced 2025-01-15 20:21:28 +00:00
180 lines
No EOL
6.2 KiB
C#
180 lines
No EOL
6.2 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using nxDumpFuse.Events;
|
|
using nxDumpFuse.Extensions;
|
|
using nxDumpFuse.Model;
|
|
using nxDumpFuse.Model.Enums;
|
|
|
|
namespace nxDumpFuse.Services
|
|
{
|
|
public class FuseService : IFuseService
|
|
{
|
|
|
|
private CancellationTokenSource? _cts;
|
|
private string? _outputFilePath;
|
|
private readonly Stopwatch _sw = new();
|
|
|
|
public event EventHandlers.FuseUpdateEventHandler? FuseUpdateEvent;
|
|
public event EventHandlers.FuseSimpleLogEventHandler? FuseSimpleLogEvent;
|
|
|
|
protected virtual void OnFuseUpdate(FuseUpdateInfo fuseUpdateInfo)
|
|
{
|
|
FuseUpdateEvent?.Invoke(fuseUpdateInfo);
|
|
}
|
|
|
|
private void Update(int part, int parts, double progress, double progressPart, long speed, bool complete = false)
|
|
{
|
|
OnFuseUpdate(new FuseUpdateInfo
|
|
{
|
|
Part = part,
|
|
Parts = parts,
|
|
Progress = progress,
|
|
ProgressPart = progressPart,
|
|
Speed = speed.ToMb(),
|
|
Complete = complete
|
|
});
|
|
}
|
|
|
|
protected virtual void OnFuseSimpleLogEvent(FuseSimpleLog log)
|
|
{
|
|
FuseSimpleLogEvent?.Invoke(log);
|
|
}
|
|
|
|
private void Log(FuseSimpleLogType type, string message)
|
|
{
|
|
OnFuseSimpleLogEvent(new FuseSimpleLog(type, DateTime.Now, message));
|
|
}
|
|
|
|
public void Start(string inputFilePath, string outputDir)
|
|
{
|
|
_cts = new CancellationTokenSource();
|
|
|
|
if (string.IsNullOrEmpty(inputFilePath))
|
|
{
|
|
Log(FuseSimpleLogType.Error, "Input File cannot be empty");
|
|
return;
|
|
}
|
|
if (string.IsNullOrEmpty(outputDir))
|
|
{
|
|
Log(FuseSimpleLogType.Error, "Output Directory cannot be empty");
|
|
return;
|
|
}
|
|
|
|
FileCase fileCase;
|
|
(_outputFilePath, fileCase) = inputFilePath.GetOutputFilePath(outputDir);
|
|
if (string.IsNullOrEmpty(_outputFilePath) || fileCase == FileCase.Invalid)
|
|
{
|
|
Log(FuseSimpleLogType.Error, "Output path was null");
|
|
return;
|
|
}
|
|
|
|
var inputFiles = inputFilePath.GetInputFiles(fileCase);
|
|
if (inputFiles.Count == 0)
|
|
{
|
|
Log(FuseSimpleLogType.Error, "No input files found");
|
|
return;
|
|
}
|
|
|
|
FuseFiles(inputFiles);
|
|
}
|
|
|
|
public void Stop()
|
|
{
|
|
_cts?.Cancel();
|
|
_sw.Stop();
|
|
|
|
Log(FuseSimpleLogType.Information, "Fuse Stopped");
|
|
|
|
if (!string.IsNullOrEmpty(_outputFilePath) && File.Exists(_outputFilePath))
|
|
{
|
|
Task.Run((() =>
|
|
{
|
|
const int retries = 5;
|
|
for (var i = 0; i <= retries; i++)
|
|
{
|
|
try
|
|
{
|
|
File.Delete(_outputFilePath);
|
|
Avalonia.Threading.Dispatcher.UIThread.InvokeAsync(() => Log(FuseSimpleLogType.Information, $"Deleted {_outputFilePath}"));
|
|
Update(0, 0, 0, 0, 0);
|
|
break;
|
|
}
|
|
catch (IOException)
|
|
{
|
|
Thread.Sleep(1000);
|
|
}
|
|
}
|
|
}));
|
|
}
|
|
}
|
|
|
|
public async void FuseFiles(List<string> inputFiles)
|
|
{
|
|
var buffer = new byte[1024 * 1024];
|
|
var count = 0;
|
|
long totalBytes = 0;
|
|
var totalFileLength = inputFiles.GetOutputFileSize();
|
|
|
|
Log(FuseSimpleLogType.Information, $"Fusing {inputFiles.Count} parts to {_outputFilePath} ({totalFileLength.ToMb()}MB)");
|
|
|
|
_sw.Start();
|
|
try
|
|
{
|
|
await using var outputStream = File.Create(_outputFilePath!);
|
|
foreach (var inputFilePath in inputFiles)
|
|
{
|
|
if (_cts!.Token.IsCancellationRequested) return;
|
|
long currentBytes = 0;
|
|
int currentBlockSize;
|
|
long copySpeed = 0;
|
|
|
|
await using var inputStream = File.OpenRead(inputFilePath);
|
|
var fileLength = inputStream.Length;
|
|
|
|
Log(FuseSimpleLogType.Information,
|
|
$"Fusing file part {++count}-> {inputFilePath} ({fileLength.ToMb()}MB)");
|
|
|
|
while ((currentBlockSize = inputStream.Read(buffer, 0, buffer.Length)) > 0)
|
|
{
|
|
if (_cts.Token.IsCancellationRequested) return;
|
|
|
|
currentBytes += currentBlockSize;
|
|
totalBytes += currentBlockSize;
|
|
|
|
try
|
|
{
|
|
await outputStream.WriteAsync(buffer, 0, currentBlockSize, _cts.Token);
|
|
}
|
|
catch (TaskCanceledException e)
|
|
{
|
|
Log(FuseSimpleLogType.Error, e.Message);
|
|
_sw.Stop();
|
|
Update(0, 0, 0, 0, 0, true);
|
|
return;
|
|
}
|
|
|
|
var progress = totalBytes * 100.0 / totalFileLength;
|
|
var progressPart = currentBytes * 100.0 / fileLength;
|
|
if (_sw.ElapsedMilliseconds >= 1000)
|
|
copySpeed = totalBytes / _sw.ElapsedMilliseconds.ToSeconds();
|
|
Update(count, inputFiles.Count, progress, progressPart, copySpeed);
|
|
}
|
|
}
|
|
|
|
Log(FuseSimpleLogType.Information, $"Fuse Completed in {_sw.ElapsedMilliseconds.ToSeconds()}s");
|
|
_sw.Stop();
|
|
Update(0, 0, 0, 0, 0, true);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Log(FuseSimpleLogType.Error, e.Message);
|
|
_sw.Stop();
|
|
Update(0, 0, 0, 0, 0, true);
|
|
}
|
|
}
|
|
}
|
|
} |