Created
November 18, 2019 13:09
-
-
Save foglio1024/81935179f53c990226b40ac8648d10ed to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Alkahest.Core.Data; | |
using FoglioUtils; | |
using Microsoft.Win32; | |
using Newtonsoft.Json; | |
using System; | |
using System.IO; | |
using System.Linq; | |
using System.Runtime; | |
using System.Threading; | |
using System.Threading.Tasks; | |
using System.Windows.Input; | |
using System.Xml; | |
using Alkahest.Core.Collections; | |
using TeraClientTools.Enums; | |
using TeraClientTools.Jobs.Base; | |
using WPFFolderBrowser; | |
using Formatting = Newtonsoft.Json.Formatting; | |
namespace TeraClientTools.Jobs | |
{ | |
public class FullDcDumpJob : JobBase | |
{ | |
private string _inputPath; | |
private string _outputPath; | |
private bool _parallel = true; | |
private DataCenterDumpFormat _format; | |
public override bool CanRun => File.Exists(InputPath); | |
public override string Name => "Full DC dump"; | |
public string InputPath | |
{ | |
get => _inputPath; | |
set | |
{ | |
if (_inputPath == value) return; | |
_inputPath = value; | |
N(); | |
N(nameof(CanRun)); | |
} | |
} | |
public string OutputPath | |
{ | |
get => _outputPath; | |
set | |
{ | |
if (_outputPath == value) return; | |
_outputPath = value; | |
N(); | |
} | |
} | |
public DataCenterDumpFormat Format | |
{ | |
get => _format; | |
set | |
{ | |
if (_format == value) return; | |
_format = value; | |
N(); | |
} | |
} | |
[JsonIgnore] public ICommand BrowseInput { get; } | |
[JsonIgnore] public ICommand BrowseOutput { get; } | |
public FullDcDumpJob() | |
{ | |
BrowseInput = new RelayCommand(_ => | |
{ | |
var dialog = new OpenFileDialog | |
{ | |
Filter = " TERA Decrypted Data Center (*.dec) | *.dec", | |
Title = "Open Data Center" | |
}; | |
if (dialog.ShowDialog() == true) InputPath = dialog.FileName; | |
}); | |
BrowseOutput = new RelayCommand(_ => | |
{ | |
var diag = new WPFFolderBrowserDialog("Browse output folder"); | |
if (diag.ShowDialog() == true) OutputPath = $"{diag.FileName}"; | |
}); | |
} | |
public override void InitFrom(JobBase other) | |
{ | |
base.InitFrom(other); | |
if (!(other is FullDcDumpJob j)) throw new InvalidCastException(); | |
InputPath = j.InputPath; | |
OutputPath = j.OutputPath; | |
Format = j.Format; | |
} | |
protected override async Task Run(CancellationToken ct) | |
{ | |
Ct = ct; | |
IsRunning = true; | |
try | |
{ | |
Invoke(); | |
} | |
catch (OperationCanceledException e) | |
{ | |
IsRunning = false; | |
IsStopRequested = false; | |
Log("Job canceled"); | |
throw e; | |
} | |
finally | |
{ | |
GC.Collect(GC.MaxGeneration); | |
} | |
IsStopRequested = false; | |
IsRunning = false; | |
InvokeCompleted(); | |
Log("Done"); | |
} | |
private void Invoke() | |
{ | |
Directory.CreateDirectory(_outputPath); | |
var directories = 0; | |
var files = 0; | |
var stream = File.OpenRead(InputPath); | |
var dc = new DataCenter(stream, DataCenterMode.Persistent, DataCenterStringOptions.Lazy); | |
var options = new ParallelOptions | |
{ | |
MaxDegreeOfParallelism = _parallel ? Environment.ProcessorCount : 1, | |
}; | |
var work = dc.Root.Children().GroupBy(x => x.Name, (name, elems) => elems.WithIndex().Select(x => (name, elem: x.Item2, idx: x.Item1))).SelectMany(x => x); | |
var total = work.Count(); | |
var done = 0; | |
var settings = new XmlWriterSettings | |
{ | |
Indent = true, | |
}; | |
Parallel.ForEach(work, options, item => | |
{ | |
var dir = Path.Combine(_outputPath, item.name); | |
lock (dc) | |
{ | |
if (!Directory.Exists(dir)) | |
{ | |
Directory.CreateDirectory(dir); | |
Interlocked.Increment(ref directories); | |
} | |
} | |
Ct.ThrowIfCancellationRequested(); | |
switch (_format) | |
{ | |
case DataCenterDumpFormat.XML: | |
{ | |
var writer = XmlWriter.Create(Path.Combine(dir, $"{item.name}-{item.idx}.xml"), settings); | |
Log($"Writing {item.name}-{item.idx}.xml"); | |
WriteElement(writer, item.elem); | |
break; | |
} | |
case DataCenterDumpFormat.JSON: | |
{ | |
var writer = | |
new JsonTextWriter(new StreamWriter(Path.Combine(dir, $"{item.name}-{item.idx}.json"))) | |
{ | |
Formatting = Newtonsoft.Json.Formatting.Indented, | |
}; | |
Log($"Writing {item.name}-{item.idx}.json"); | |
WriteElement(writer, item.elem); | |
break; | |
} | |
} | |
Interlocked.Increment(ref done); | |
Interlocked.Increment(ref files); | |
Progress = done * 100 / (double)total; | |
}); | |
} | |
private static void WriteElement(XmlWriter writer, DataCenterElement element) | |
{ | |
writer.WriteStartElement(element.Name); | |
foreach (var (name, value) in element.Attributes.Tuples()) | |
writer.WriteAttributeString(name, value.ToString()); | |
foreach (var elem in element.Children()) | |
WriteElement(writer, elem); | |
writer.WriteEndElement(); | |
} | |
private static void WriteElement(JsonWriter writer, DataCenterElement element) | |
{ | |
writer.WriteStartObject(); | |
foreach (var (name, value) in element.Attributes.Tuples()) | |
{ | |
writer.WritePropertyName(name); | |
switch (value.TypeCode) | |
{ | |
case DataCenterTypeCode.Int32: | |
writer.WriteValue(value.AsInt32); | |
break; | |
case DataCenterTypeCode.Single: | |
writer.WriteValue(value.AsSingle); | |
break; | |
case DataCenterTypeCode.String: | |
writer.WriteValue(value.AsString); | |
break; | |
case DataCenterTypeCode.Boolean: | |
writer.WriteValue(value.AsBoolean); | |
break; | |
} | |
} | |
foreach (var grp in element.Children().GroupBy(x => x.Name)) | |
{ | |
writer.WritePropertyName(grp.Key); | |
writer.WriteStartArray(); | |
foreach (var elem in grp) | |
WriteElement(writer, elem); | |
writer.WriteEndArray(); | |
} | |
writer.WriteEndObject(); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment