You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
82 lines
3.0 KiB
82 lines
3.0 KiB
using System.IO.Compression; |
|
using System.Numerics; |
|
using System.Text; |
|
|
|
namespace Res2tf; |
|
|
|
public class DataTree |
|
{ |
|
public BsonValue Bson { get; } |
|
public string[] Types { get; } |
|
public DataTreeNode Root { get; } |
|
|
|
// Technically unused, but kept in case it will be needed. |
|
public Dictionary<string, DataTreeNode> Nodes { get; } = []; |
|
|
|
public DataTree(string path) : this(LoadBson(path)) { } |
|
public DataTree(Stream stream) : this(LoadBson(stream)) { } |
|
public DataTree(BsonValue bson) |
|
{ |
|
Bson = bson; |
|
Types = bson["Types"].ToArray<string>(); |
|
Root = CreateNodeRecursive(null, bson["Object"]); |
|
} |
|
|
|
public int? GetTypeId(string type) |
|
=> Array.IndexOf(Types, type) switch |
|
{ -1 => null, int i => i }; |
|
|
|
DataTreeNode CreateNodeRecursive(DataTreeNode? parent, BsonValue bson) |
|
{ |
|
var node = new DataTreeNode(this, parent, bson); |
|
foreach (var childBson in bson.GetOrNull("Children")?.AsList() ?? []) |
|
node.Children.Add(CreateNodeRecursive(node, childBson)); |
|
Nodes[node.ID] = node; |
|
return node; |
|
} |
|
|
|
static BsonValue LoadBson(string path) |
|
=> LoadBson(File.OpenRead(path)); |
|
static BsonValue LoadBson(Stream stream) |
|
{ |
|
// Read and verify file header. |
|
using var reader = new BinaryReader(stream); |
|
var magic = Encoding.ASCII.GetString(reader.ReadBytes(4)); |
|
var version = reader.ReadInt32(); |
|
var compression = reader.ReadByte(); |
|
if (magic != "FrDT") throw new Exception("Invalid magic number"); |
|
if (version != 0) throw new Exception("Unknown version"); |
|
if (compression != 3) throw new Exception("Unsupported compression"); |
|
|
|
// Decompress and parse BSON. |
|
using var brotli = new BrotliStream(stream, CompressionMode.Decompress); |
|
return BsonReader.Load(brotli); |
|
} |
|
} |
|
|
|
public record DataTreeNode(DataTree Tree, DataTreeNode? Parent, BsonValue Bson) |
|
{ |
|
public List<DataTreeNode> Children { get; } = []; |
|
|
|
public string ID => (string)Bson["ID"]; |
|
public string Name => (string)Bson["Name"]["Data"]; |
|
|
|
public Vector3 Position { get { var p = Bson["Position"]["Data"].ToArray<float>(); return new(p[0], p[1], p[2]); } } |
|
public Quaternion Rotation { get { var r = Bson["Rotation"]["Data"].ToArray<float>(); return new(r[0], r[1], r[2], r[3]); } } |
|
public Vector3 Scale { get { var s = Bson["Scale"] ["Data"].ToArray<float>(); return new(s[0], s[1], s[2]); } } |
|
|
|
public void Remove() { |
|
Tree.Nodes.Remove(ID); |
|
Parent?.Children.Remove(this); |
|
} |
|
|
|
public IEnumerable<DataTreeNode> EnumerateRecursive() |
|
{ |
|
yield return this; |
|
// Iterate children in reverse order so calling Remove on the current child is safe? |
|
// for (var i = Children.Count - 1; i >= 0; i--) |
|
foreach (var child in Children) |
|
foreach (var elem in child.EnumerateRecursive()) |
|
yield return elem; |
|
} |
|
}
|
|
|