Skip to content

Instantly share code, notes, and snippets.

@foglio1024
Created November 18, 2019 13:09
Show Gist options
  • Save foglio1024/81935179f53c990226b40ac8648d10ed to your computer and use it in GitHub Desktop.
Save foglio1024/81935179f53c990226b40ac8648d10ed to your computer and use it in GitHub Desktop.
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