92 lines
3.3 KiB
C#
92 lines
3.3 KiB
C#
using System.IO.Compression;
|
|
using System.Linq;
|
|
using System.Text.Encodings.Web;
|
|
using System.Text.Json.Serialization;
|
|
using ParadoxSaveParser.WebAPI.SaveDataFilters;
|
|
|
|
namespace ParadoxSaveParser.WebAPI.BackgroundTasks;
|
|
|
|
public class SaveParsingOperation
|
|
{
|
|
public readonly long OperationId;
|
|
|
|
private readonly SaveFileMetadata _meta;
|
|
private readonly ISaveDataFilter _filter;
|
|
private readonly ContextLogger _logger;
|
|
private readonly CancellationToken _cancelToken;
|
|
|
|
private static readonly JsonSerializerOptions _saveSerializerOptions = new()
|
|
{
|
|
WriteIndented = false,
|
|
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
|
|
MaxDepth = 1024,
|
|
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
|
|
};
|
|
|
|
public SaveParsingOperation(long operationId, SaveFileMetadata meta, ISaveDataFilter filter,
|
|
ContextLogger logger, CancellationToken cancelToken)
|
|
{
|
|
OperationId = operationId;
|
|
_meta = meta;
|
|
_filter = filter;
|
|
_logger = logger;
|
|
_cancelToken = cancelToken;
|
|
}
|
|
|
|
public async void StartAsync()
|
|
{
|
|
try
|
|
{
|
|
_logger.LogInfo($"Starting background parsing operation of {_meta.game} save {_meta.id}");
|
|
switch (_meta.game)
|
|
{
|
|
case Game.EU4:
|
|
await ParseSaveEU4();
|
|
break;
|
|
default:
|
|
throw new ArgumentOutOfRangeException(_meta.game.ToString());
|
|
}
|
|
_logger.LogInfo($"Finished parsing operation of {_meta.game} save {_meta.id}");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
string errorMesage = ex.ToStringDemystified();
|
|
_logger.LogError(errorMesage);
|
|
_meta.errorMessage = errorMesage;
|
|
}
|
|
finally
|
|
{
|
|
GC.Collect(GC.MaxGeneration, GCCollectionMode.Aggressive, true, true);
|
|
}
|
|
}
|
|
|
|
private async Task ParseSaveEU4()
|
|
{
|
|
// wait for save file closing
|
|
await Task.Delay(200, _cancelToken);
|
|
string extractedGamestatePath = PathHelper.GetSaveFilePath(_meta.id) + ".gamestate";
|
|
using (var zipArchive = ZipFile.Open(PathHelper.GetSaveFilePath(_meta.id).Str, ZipArchiveMode.Read))
|
|
{
|
|
var zipEntry = zipArchive.Entries.FirstOrDefault(e => e.Name == "gamestate");
|
|
if (zipEntry is null)
|
|
throw new Exception("Invalid save format: no 'gamestate' file found");
|
|
|
|
zipEntry.ExtractToFile(extractedGamestatePath, true);
|
|
}
|
|
|
|
var gamestateStream = File.OpenRead(extractedGamestatePath);
|
|
|
|
_meta.status = SaveFileProcessingStatus.Parsing;
|
|
var parser = new SaveParserEU4(gamestateStream, _filter.SearchExpression);
|
|
var result = parser.Parse();
|
|
_filter.Apply(result);
|
|
|
|
_meta.status = SaveFileProcessingStatus.SavingResults;
|
|
var resultFilePath = PathHelper.GetParsedSaveFilePath(_meta.id);
|
|
await using var resultFile = File.OpenWrite(resultFilePath);
|
|
await JsonSerializer.SerializeAsync(resultFile, result,
|
|
_saveSerializerOptions, _cancelToken);
|
|
_meta.status = SaveFileProcessingStatus.Done;
|
|
_meta.SaveToFile();
|
|
}
|
|
} |