Merge pull request #6 from mahalex/fix-objects-parsing
Fixed parsing of MATLAB objects. Added adapters for string, datetime, and duration arrays.
This commit is contained in:
commit
2c514c62f1
@ -1,5 +1,6 @@
|
|||||||
// Copyright 2017-2018 Alexander Luzgarev
|
// Copyright 2017-2018 Alexander Luzgarev
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
@ -388,6 +389,66 @@ namespace MatFileHandler.Tests
|
|||||||
Assert.That(ppp["y"].ConvertToDoubleArray(), Is.EquivalentTo(new[] { 200.0 }));
|
Assert.That(ppp["y"].ConvertToDoubleArray(), Is.EquivalentTo(new[] { 200.0 }));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Test datetime objects.
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void TestDatetime()
|
||||||
|
{
|
||||||
|
var matFile = GetTests("good")["datetime"];
|
||||||
|
var d = matFile["d"].Value as IMatObject;
|
||||||
|
var datetime = new DatetimeAdapter(d);
|
||||||
|
Assert.That(datetime.Dimensions, Is.EquivalentTo(new[] { 1, 2 }));
|
||||||
|
Assert.That(datetime[0], Is.EqualTo(new DateTimeOffset(2000, 1, 1, 0, 0, 0, TimeSpan.Zero)));
|
||||||
|
Assert.That(datetime[1], Is.EqualTo(new DateTimeOffset(1987, 1, 2, 3, 4, 5, TimeSpan.Zero)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Another test for datetime objects.
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void TestDatetime2()
|
||||||
|
{
|
||||||
|
var matFile = GetTests("good")["datetime2"];
|
||||||
|
var d = matFile["d"].Value as IMatObject;
|
||||||
|
var datetime = new DatetimeAdapter(d);
|
||||||
|
Assert.That(datetime.Dimensions, Is.EquivalentTo(new[] { 1, 1 }));
|
||||||
|
var diff = new DateTimeOffset(2, 1, 1, 1, 1, 1, 235, TimeSpan.Zero);
|
||||||
|
Assert.That(datetime[0] - diff < TimeSpan.FromMilliseconds(1));
|
||||||
|
Assert.That(diff - datetime[0] < TimeSpan.FromMilliseconds(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Test string objects.
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void TestString()
|
||||||
|
{
|
||||||
|
var matFile = GetTests("good")["string"];
|
||||||
|
var s = matFile["s"].Value as IMatObject;
|
||||||
|
var str = new StringAdapter(s);
|
||||||
|
Assert.That(str.Dimensions, Is.EquivalentTo(new[] { 4, 1 }));
|
||||||
|
Assert.That(str[0], Is.EqualTo("abc"));
|
||||||
|
Assert.That(str[1], Is.EqualTo("defgh"));
|
||||||
|
Assert.That(str[2], Is.EqualTo("абвгд"));
|
||||||
|
Assert.That(str[3], Is.EqualTo("æøå"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Test duration objects.
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void TestDuration()
|
||||||
|
{
|
||||||
|
var matFile = GetTests("good")["duration"];
|
||||||
|
var d = matFile["d"].Value as IMatObject;
|
||||||
|
var duration = new DurationAdapter(d);
|
||||||
|
Assert.That(duration.Dimensions, Is.EquivalentTo(new[] { 1, 3 }));
|
||||||
|
Assert.That(duration[0], Is.EqualTo(TimeSpan.FromTicks(12345678L)));
|
||||||
|
Assert.That(duration[1], Is.EqualTo(new TimeSpan(0, 2, 4)));
|
||||||
|
Assert.That(duration[2], Is.EqualTo(new TimeSpan(1, 3, 5)));
|
||||||
|
}
|
||||||
|
|
||||||
private static AbstractTestDataFactory<IMatFile> GetTests(string factoryName) =>
|
private static AbstractTestDataFactory<IMatFile> GetTests(string factoryName) =>
|
||||||
new MatTestDataFactory(Path.Combine(TestDirectory, factoryName));
|
new MatTestDataFactory(Path.Combine(TestDirectory, factoryName));
|
||||||
|
|
||||||
|
BIN
MatFileHandler.Tests/test-data/good/datetime.mat
Normal file
BIN
MatFileHandler.Tests/test-data/good/datetime.mat
Normal file
Binary file not shown.
BIN
MatFileHandler.Tests/test-data/good/datetime2.mat
Normal file
BIN
MatFileHandler.Tests/test-data/good/datetime2.mat
Normal file
Binary file not shown.
BIN
MatFileHandler.Tests/test-data/good/duration.mat
Normal file
BIN
MatFileHandler.Tests/test-data/good/duration.mat
Normal file
Binary file not shown.
BIN
MatFileHandler.Tests/test-data/good/string.mat
Normal file
BIN
MatFileHandler.Tests/test-data/good/string.mat
Normal file
Binary file not shown.
62
MatFileHandler/DatetimeAdapter.cs
Normal file
62
MatFileHandler/DatetimeAdapter.cs
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
// Copyright 2017-2018 Alexander Luzgarev
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Numerics;
|
||||||
|
|
||||||
|
namespace MatFileHandler
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A better interface for using datetime objects.
|
||||||
|
/// </summary>
|
||||||
|
public class DatetimeAdapter
|
||||||
|
{
|
||||||
|
private readonly double[] data;
|
||||||
|
private readonly double[] data2;
|
||||||
|
private readonly int[] dimensions;
|
||||||
|
|
||||||
|
private readonly DateTimeOffset epoch;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="DatetimeAdapter"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="array">Source datetime object.</param>
|
||||||
|
public DatetimeAdapter(IArray array)
|
||||||
|
{
|
||||||
|
var matObject = array as IMatObject;
|
||||||
|
if (matObject?.ClassName != "datetime")
|
||||||
|
{
|
||||||
|
throw new ArgumentException("The object provided is not a datetime.");
|
||||||
|
}
|
||||||
|
|
||||||
|
epoch = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
|
||||||
|
var dataArray = matObject["data", 0] as IArrayOf<double>;
|
||||||
|
if (dataArray is null)
|
||||||
|
{
|
||||||
|
var dataComplex = matObject["data", 0] as IArrayOf<Complex>;
|
||||||
|
var complexData = dataComplex.ConvertToComplexArray();
|
||||||
|
data = complexData.Select(c => c.Real).ToArray();
|
||||||
|
data2 = complexData.Select(c => c.Imaginary).ToArray();
|
||||||
|
dimensions = dataComplex.Dimensions;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
data = dataArray.ConvertToDoubleArray();
|
||||||
|
data2 = new double[data.Length];
|
||||||
|
dimensions = dataArray.Dimensions;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets datetime array dimensions.
|
||||||
|
/// </summary>
|
||||||
|
public int[] Dimensions => dimensions;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets values of datetime object at given position in the array converted to <see cref="DateTimeOffset"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="list">Indices.</param>
|
||||||
|
/// <returns>Value converted to <see cref="DateTimeOffset"/>.</returns>
|
||||||
|
public DateTimeOffset this[params int[] list] => epoch.AddMilliseconds(data[Dimensions.DimFlatten(list)]);
|
||||||
|
}
|
||||||
|
}
|
44
MatFileHandler/DurationAdapter.cs
Normal file
44
MatFileHandler/DurationAdapter.cs
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
// Copyright 2017-2018 Alexander Luzgarev
|
||||||
|
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace MatFileHandler
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A better interface for using duration objects.
|
||||||
|
/// </summary>
|
||||||
|
public class DurationAdapter
|
||||||
|
{
|
||||||
|
private readonly int[] dimensions;
|
||||||
|
private readonly double[] data;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="DurationAdapter"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="array">Source duration object.</param>
|
||||||
|
public DurationAdapter(IArray array)
|
||||||
|
{
|
||||||
|
var matObject = array as IMatObject;
|
||||||
|
if (matObject?.ClassName != "duration")
|
||||||
|
{
|
||||||
|
throw new ArgumentException("The object provided is not a duration.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var dataObject = matObject["millis", 0];
|
||||||
|
data = dataObject.ConvertToDoubleArray();
|
||||||
|
dimensions = dataObject.Dimensions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets duration array dimensions.
|
||||||
|
/// </summary>
|
||||||
|
public int[] Dimensions => dimensions;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets duration object at given position.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="list">Indices.</param>
|
||||||
|
/// <returns>Value.</returns>
|
||||||
|
public TimeSpan this[params int[] list] => TimeSpan.FromTicks((long)(10000.0 * data[Dimensions.DimFlatten(list)]));
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>netstandard2.0;net461</TargetFrameworks>
|
<TargetFrameworks>netstandard2.0;net461</TargetFrameworks>
|
||||||
<PackageVersion>1.3.0-beta2</PackageVersion>
|
<PackageVersion>1.3.0-beta3</PackageVersion>
|
||||||
<PackageId>MatFileHandler</PackageId>
|
<PackageId>MatFileHandler</PackageId>
|
||||||
<Title>A library for reading and writing MATLAB .mat files.</Title>
|
<Title>A library for reading and writing MATLAB .mat files.</Title>
|
||||||
<Authors>Alexander Luzgarev</Authors>
|
<Authors>Alexander Luzgarev</Authors>
|
||||||
|
@ -107,8 +107,8 @@ namespace MatFileHandler
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var objectPosition = IndexToObjectId[index];
|
var objectId = IndexToObjectId[index];
|
||||||
var objectInfo = subsystemData.ObjectInformation.First(pair => pair.Value.Position == objectPosition).Value;
|
var objectInfo = subsystemData.ObjectInformation[objectId];
|
||||||
var fieldId = objectInfo.FieldLinks[fieldIndex];
|
var fieldId = objectInfo.FieldLinks[fieldIndex];
|
||||||
output = subsystemData.Data[fieldId];
|
output = subsystemData.Data[fieldId];
|
||||||
return true;
|
return true;
|
||||||
|
78
MatFileHandler/StringAdapter.cs
Normal file
78
MatFileHandler/StringAdapter.cs
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
// Copyright 2017-2018 Alexander Luzgarev
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace MatFileHandler
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A better interface for using string objects.
|
||||||
|
/// </summary>
|
||||||
|
public class StringAdapter
|
||||||
|
{
|
||||||
|
private readonly int[] dimensions;
|
||||||
|
private readonly string[] strings;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="StringAdapter"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="array">Source string object.</param>
|
||||||
|
public StringAdapter(IArray array)
|
||||||
|
{
|
||||||
|
var matObject = array as IMatObject;
|
||||||
|
if (matObject?.ClassName != "string")
|
||||||
|
{
|
||||||
|
throw new ArgumentException("The object provided is not a string.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var binaryData = matObject["any", 0] as IArrayOf<ulong>;
|
||||||
|
|
||||||
|
(dimensions, strings) = ParseBinaryData(binaryData.Data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets string array dimensions.
|
||||||
|
/// </summary>
|
||||||
|
public int[] Dimensions => dimensions;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets string object at given position.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="list">Indices.</param>
|
||||||
|
/// <returns>Value.</returns>
|
||||||
|
public string this[params int[] list] => strings[Dimensions.DimFlatten(list)];
|
||||||
|
|
||||||
|
private static (int[] dimensions, string[] strings) ParseBinaryData(ulong[] binaryData)
|
||||||
|
{
|
||||||
|
var numberOfDimensions = (int)binaryData[1];
|
||||||
|
var d = new int[numberOfDimensions];
|
||||||
|
for (var i = 0; i < numberOfDimensions; i++)
|
||||||
|
{
|
||||||
|
d[i] = (int)binaryData[i + 2];
|
||||||
|
}
|
||||||
|
|
||||||
|
var numberOfElements = d.NumberOfElements();
|
||||||
|
var start = numberOfDimensions + 2;
|
||||||
|
var lengths = new int[numberOfElements];
|
||||||
|
for (var i = 0; i < numberOfElements; i++)
|
||||||
|
{
|
||||||
|
lengths[i] = (int)binaryData[start + i];
|
||||||
|
}
|
||||||
|
|
||||||
|
var strings = new string[numberOfElements];
|
||||||
|
|
||||||
|
start += numberOfElements;
|
||||||
|
var numberOfUlongsLeft = binaryData.Length - start;
|
||||||
|
var bytes = new byte[numberOfUlongsLeft * sizeof(ulong)];
|
||||||
|
Buffer.BlockCopy(binaryData, start * sizeof(ulong), bytes, 0, bytes.Length);
|
||||||
|
var counter = 0;
|
||||||
|
for (var i = 0; i < numberOfElements; i++)
|
||||||
|
{
|
||||||
|
strings[i] = Encoding.Unicode.GetString(bytes, counter * 2, lengths[i] * 2);
|
||||||
|
counter += lengths[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return (d, strings);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -125,11 +125,9 @@ namespace MatFileHandler
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="ObjectInfo"/> class.
|
/// Initializes a new instance of the <see cref="ObjectInfo"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="position">Position of object in the object information table.</param>
|
|
||||||
/// <param name="fieldLinks">A dictionary mapping the field indices to "field values" indices.</param>
|
/// <param name="fieldLinks">A dictionary mapping the field indices to "field values" indices.</param>
|
||||||
public ObjectInfo(int position, Dictionary<int, int> fieldLinks)
|
public ObjectInfo(Dictionary<int, int> fieldLinks)
|
||||||
{
|
{
|
||||||
Position = position;
|
|
||||||
this.fieldLinks = fieldLinks ?? throw new ArgumentNullException(nameof(fieldLinks));
|
this.fieldLinks = fieldLinks ?? throw new ArgumentNullException(nameof(fieldLinks));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,11 +135,6 @@ namespace MatFileHandler
|
|||||||
/// Gets mapping between the field indices and "field values" indices.
|
/// Gets mapping between the field indices and "field values" indices.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IReadOnlyDictionary<int, int> FieldLinks => fieldLinks;
|
public IReadOnlyDictionary<int, int> FieldLinks => fieldLinks;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets position of object in the object information table.
|
|
||||||
/// </summary>
|
|
||||||
public int Position { get; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -50,8 +50,19 @@ namespace MatFileHandler
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var numberOfEmbeddedObjects = (offsets[4] - offsets[3] - 8) / 16;
|
||||||
|
Dictionary<int, Dictionary<int, int>> embeddedObjectPositionsToValues = null;
|
||||||
|
using (var stream = new MemoryStream(info, offsets[3], offsets[4] - offsets[3]))
|
||||||
|
{
|
||||||
|
using (var reader = new BinaryReader(stream))
|
||||||
|
{
|
||||||
|
embeddedObjectPositionsToValues =
|
||||||
|
ReadEmbeddedObjectPositionsToValuesMapping(reader, numberOfEmbeddedObjects);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var numberOfObjects = ((offsets[5] - offsets[4]) / 24) - 1;
|
var numberOfObjects = ((offsets[5] - offsets[4]) / 24) - 1;
|
||||||
Dictionary<int, (int, int)> objectClasses = null;
|
Dictionary<int, ObjectClassInformation> objectClasses = null;
|
||||||
using (var stream = new MemoryStream(info, offsets[4], offsets[5] - offsets[4]))
|
using (var stream = new MemoryStream(info, offsets[4], offsets[5] - offsets[4]))
|
||||||
{
|
{
|
||||||
using (var reader = new BinaryReader(stream))
|
using (var reader = new BinaryReader(stream))
|
||||||
@ -60,12 +71,14 @@ namespace MatFileHandler
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Dictionary<int, Dictionary<int, int>> objectToFields = null;
|
var numberOfObjectPositions = objectClasses.Values.Count(x => x.ObjectPosition != 0);
|
||||||
|
|
||||||
|
Dictionary<int, Dictionary<int, int>> objectPositionsToValues = null;
|
||||||
using (var stream = new MemoryStream(info, offsets[5], offsets[6] - offsets[5]))
|
using (var stream = new MemoryStream(info, offsets[5], offsets[6] - offsets[5]))
|
||||||
{
|
{
|
||||||
using (var reader = new BinaryReader(stream))
|
using (var reader = new BinaryReader(stream))
|
||||||
{
|
{
|
||||||
objectToFields = ReadObjectToFieldsMapping(reader, numberOfObjects);
|
objectPositionsToValues = ReadObjectPositionsToValuesMapping(reader, numberOfObjectPositions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,7 +87,8 @@ namespace MatFileHandler
|
|||||||
classIdToName,
|
classIdToName,
|
||||||
fieldNames,
|
fieldNames,
|
||||||
objectClasses,
|
objectClasses,
|
||||||
objectToFields);
|
objectPositionsToValues,
|
||||||
|
embeddedObjectPositionsToValues);
|
||||||
|
|
||||||
var allFields = objectInformation.Values.SelectMany(obj => obj.FieldLinks.Values);
|
var allFields = objectInformation.Values.SelectMany(obj => obj.FieldLinks.Values);
|
||||||
var data = new Dictionary<int, IArray>();
|
var data = new Dictionary<int, IArray>();
|
||||||
@ -82,120 +96,77 @@ namespace MatFileHandler
|
|||||||
{
|
{
|
||||||
data[i] = TransformOpaqueData(opaqueData[i + 2], subsystemData);
|
data[i] = TransformOpaqueData(opaqueData[i + 2], subsystemData);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new SubsystemData(classInformation, objectInformation, data);
|
return new SubsystemData(classInformation, objectInformation, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IArray TransformOpaqueData(IArray array, SubsystemData subsystemData)
|
private static (Dictionary<int, SubsystemData.ClassInfo>, Dictionary<int, SubsystemData.ObjectInfo>)
|
||||||
{
|
GatherClassAndObjectInformation(
|
||||||
if (array is MatNumericalArrayOf<uint> uintArray)
|
|
||||||
{
|
|
||||||
if (uintArray.Data[0] == 3707764736u)
|
|
||||||
{
|
|
||||||
var (dimensions, indexToObjectId, classIndex) = DataElementReader.ParseOpaqueData(uintArray.Data);
|
|
||||||
return new OpaqueLink(
|
|
||||||
uintArray.Name,
|
|
||||||
string.Empty,
|
|
||||||
string.Empty,
|
|
||||||
dimensions,
|
|
||||||
array as DataElement,
|
|
||||||
indexToObjectId,
|
|
||||||
classIndex,
|
|
||||||
subsystemData);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return array;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Dictionary<int, (int, int)> ReadObjectClasses(BinaryReader reader, int numberOfObjects)
|
|
||||||
{
|
|
||||||
var result = new Dictionary<int, (int, int)>();
|
|
||||||
reader.ReadBytes(24);
|
|
||||||
for (var i = 0; i < numberOfObjects; i++)
|
|
||||||
{
|
|
||||||
var classId = reader.ReadInt32();
|
|
||||||
reader.ReadBytes(12);
|
|
||||||
var objectPosition = reader.ReadInt32();
|
|
||||||
var objectId = reader.ReadInt32();
|
|
||||||
result[objectPosition] = (objectId, classId);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static (Dictionary<int, SubsystemData.ClassInfo>, Dictionary<int, SubsystemData.ObjectInfo>) GatherClassAndObjectInformation(
|
|
||||||
Dictionary<int, string> classIdToName,
|
Dictionary<int, string> classIdToName,
|
||||||
string[] fieldNames,
|
string[] fieldNames,
|
||||||
Dictionary<int, (int, int)> objectClasses,
|
Dictionary<int, ObjectClassInformation> objectClasses,
|
||||||
Dictionary<int, Dictionary<int, int>> objectToFields)
|
Dictionary<int, Dictionary<int, int>> objectPositionsToValues,
|
||||||
|
Dictionary<int, Dictionary<int, int>> embeddedObjectPositionsToValues)
|
||||||
{
|
{
|
||||||
var classInfos = new Dictionary<int, SubsystemData.ClassInfo>();
|
var classInfos = new Dictionary<int, SubsystemData.ClassInfo>();
|
||||||
foreach (var classId in classIdToName.Keys)
|
foreach (var classId in classIdToName.Keys)
|
||||||
{
|
{
|
||||||
var className = classIdToName[classId];
|
var className = classIdToName[classId];
|
||||||
var fieldIds = new SortedSet<int>();
|
var fieldIds = new SortedSet<int>();
|
||||||
foreach (var objectPosition in objectToFields.Keys)
|
foreach (var objectPosition in objectPositionsToValues.Keys)
|
||||||
{
|
{
|
||||||
var (_, thisObjectClassId) = objectClasses[objectPosition];
|
var keyValuePair = objectClasses.First(pair => pair.Value.ObjectPosition == objectPosition);
|
||||||
if (thisObjectClassId != classId)
|
if (keyValuePair.Value.ClassId != classId)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var fieldId in objectToFields[objectPosition].Keys)
|
foreach (var fieldId in objectPositionsToValues[objectPosition].Keys)
|
||||||
{
|
{
|
||||||
fieldIds.Add(fieldId);
|
fieldIds.Add(fieldId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foreach (var objectPosition in embeddedObjectPositionsToValues.Keys)
|
||||||
|
{
|
||||||
|
var keyValuePair = objectClasses.First(pair => pair.Value.EmbeddedObjectPosition == objectPosition);
|
||||||
|
if (keyValuePair.Value.ClassId != classId)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var fieldId in embeddedObjectPositionsToValues[objectPosition].Keys)
|
||||||
|
{
|
||||||
|
fieldIds.Add(fieldId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var fieldToIndex = new Dictionary<string, int>();
|
var fieldToIndex = new Dictionary<string, int>();
|
||||||
foreach (var fieldId in fieldIds)
|
foreach (var fieldId in fieldIds)
|
||||||
{
|
{
|
||||||
fieldToIndex[fieldNames[fieldId - 1]] = fieldId;
|
fieldToIndex[fieldNames[fieldId - 1]] = fieldId;
|
||||||
}
|
}
|
||||||
|
|
||||||
classInfos[classId] = new SubsystemData.ClassInfo(className, fieldToIndex);
|
classInfos[classId] = new SubsystemData.ClassInfo(className, fieldToIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
var objectInfos = new Dictionary<int, SubsystemData.ObjectInfo>();
|
var objectInfos = new Dictionary<int, SubsystemData.ObjectInfo>();
|
||||||
foreach (var objectPosition in objectToFields.Keys)
|
foreach (var objectPosition in objectPositionsToValues.Keys)
|
||||||
{
|
{
|
||||||
var (objectId, _) = objectClasses[objectPosition];
|
var keyValuePair = objectClasses.First(pair => pair.Value.ObjectPosition == objectPosition);
|
||||||
objectInfos[objectId] = new SubsystemData.ObjectInfo(objectPosition, objectToFields[objectPosition]);
|
objectInfos[keyValuePair.Key] = new SubsystemData.ObjectInfo(objectPositionsToValues[objectPosition]);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var objectPosition in embeddedObjectPositionsToValues.Keys)
|
||||||
|
{
|
||||||
|
var keyValuePair = objectClasses.First(pair => pair.Value.EmbeddedObjectPosition == objectPosition);
|
||||||
|
objectInfos[keyValuePair.Key] =
|
||||||
|
new SubsystemData.ObjectInfo(embeddedObjectPositionsToValues[objectPosition]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (classInfos, objectInfos);
|
return (classInfos, objectInfos);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Dictionary<int, int> ReadFieldToFieldDataMapping(BinaryReader reader)
|
|
||||||
{
|
|
||||||
var length = reader.ReadInt32();
|
|
||||||
var result = new Dictionary<int, int>();
|
|
||||||
for (var i = 0; i < length; i++)
|
|
||||||
{
|
|
||||||
var x = reader.ReadInt32();
|
|
||||||
var y = reader.ReadInt32();
|
|
||||||
var index = x * y;
|
|
||||||
var link = reader.ReadInt32();
|
|
||||||
result[index] = link;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Dictionary<int, Dictionary<int, int>> ReadObjectToFieldsMapping(BinaryReader reader, int numberOfObjects)
|
|
||||||
{
|
|
||||||
var result = new Dictionary<int, Dictionary<int, int>>();
|
|
||||||
reader.ReadBytes(8);
|
|
||||||
for (var objectPosition = 1; objectPosition <= numberOfObjects; objectPosition++)
|
|
||||||
{
|
|
||||||
result[objectPosition] = ReadFieldToFieldDataMapping(reader);
|
|
||||||
var position = reader.BaseStream.Position;
|
|
||||||
if (position % 8 != 0)
|
|
||||||
{
|
|
||||||
reader.ReadBytes(8 - (int)(position % 8));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Dictionary<int, string> ReadClassNames(
|
private static Dictionary<int, string> ReadClassNames(
|
||||||
BinaryReader reader,
|
BinaryReader reader,
|
||||||
string[] fieldNames,
|
string[] fieldNames,
|
||||||
@ -219,6 +190,98 @@ namespace MatFileHandler
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Dictionary<int, Dictionary<int, int>> ReadEmbeddedObjectPositionsToValuesMapping(
|
||||||
|
BinaryReader reader, int numberOfObjects)
|
||||||
|
{
|
||||||
|
var result = new Dictionary<int, Dictionary<int, int>>();
|
||||||
|
reader.ReadBytes(8);
|
||||||
|
for (var objectPosition = 1; objectPosition <= numberOfObjects; objectPosition++)
|
||||||
|
{
|
||||||
|
var a = reader.ReadInt32();
|
||||||
|
var fieldIndex = reader.ReadInt32();
|
||||||
|
var c = reader.ReadInt32();
|
||||||
|
var valueIndex = reader.ReadInt32();
|
||||||
|
result[objectPosition] = new Dictionary<int, int> { [fieldIndex] = valueIndex };
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string[] ReadFieldNames(byte[] bytes, int startPosition, int numberOfFields)
|
||||||
|
{
|
||||||
|
var result = new string[numberOfFields];
|
||||||
|
var position = startPosition;
|
||||||
|
for (var i = 0; i < numberOfFields; i++)
|
||||||
|
{
|
||||||
|
var list = new List<byte>();
|
||||||
|
while (bytes[position] != 0)
|
||||||
|
{
|
||||||
|
list.Add(bytes[position]);
|
||||||
|
position++;
|
||||||
|
}
|
||||||
|
|
||||||
|
result[i] = Encoding.ASCII.GetString(list.ToArray());
|
||||||
|
position++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Dictionary<int, int> ReadFieldToFieldDataMapping(BinaryReader reader)
|
||||||
|
{
|
||||||
|
var length = reader.ReadInt32();
|
||||||
|
var result = new Dictionary<int, int>();
|
||||||
|
for (var i = 0; i < length; i++)
|
||||||
|
{
|
||||||
|
var x = reader.ReadInt32();
|
||||||
|
var y = reader.ReadInt32();
|
||||||
|
var index = x * y;
|
||||||
|
var link = reader.ReadInt32();
|
||||||
|
result[index] = link;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Dictionary<int, ObjectClassInformation> ReadObjectClasses(
|
||||||
|
BinaryReader reader,
|
||||||
|
int numberOfObjects)
|
||||||
|
{
|
||||||
|
var result = new Dictionary<int, ObjectClassInformation>();
|
||||||
|
reader.ReadBytes(24);
|
||||||
|
for (var i = 0; i < numberOfObjects; i++)
|
||||||
|
{
|
||||||
|
var classId = reader.ReadInt32();
|
||||||
|
reader.ReadBytes(8);
|
||||||
|
var embeddedObjectPosition = reader.ReadInt32();
|
||||||
|
var objectPosition = reader.ReadInt32();
|
||||||
|
var loadingOrder = reader.ReadInt32();
|
||||||
|
result[i + 1] =
|
||||||
|
new ObjectClassInformation(embeddedObjectPosition, objectPosition, loadingOrder, classId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Dictionary<int, Dictionary<int, int>> ReadObjectPositionsToValuesMapping(
|
||||||
|
BinaryReader reader,
|
||||||
|
int numberOfObjects)
|
||||||
|
{
|
||||||
|
var result = new Dictionary<int, Dictionary<int, int>>();
|
||||||
|
reader.ReadBytes(8);
|
||||||
|
for (var objectPosition = 1; objectPosition <= numberOfObjects; objectPosition++)
|
||||||
|
{
|
||||||
|
result[objectPosition] = ReadFieldToFieldDataMapping(reader);
|
||||||
|
var position = reader.BaseStream.Position;
|
||||||
|
if (position % 8 != 0)
|
||||||
|
{
|
||||||
|
reader.ReadBytes(8 - (int)(position % 8));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
private static (int[] offsets, int newPosition) ReadOffsets(byte[] bytes, int startPosition)
|
private static (int[] offsets, int newPosition) ReadOffsets(byte[] bytes, int startPosition)
|
||||||
{
|
{
|
||||||
var position = startPosition;
|
var position = startPosition;
|
||||||
@ -236,29 +299,52 @@ namespace MatFileHandler
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
offsets.Add(next);
|
offsets.Add(next);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (offsets.ToArray(), position);
|
return (offsets.ToArray(), position);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string[] ReadFieldNames(byte[] bytes, int startPosition, int numberOfFields)
|
private static IArray TransformOpaqueData(IArray array, SubsystemData subsystemData)
|
||||||
{
|
{
|
||||||
var result = new string[numberOfFields];
|
if (array is MatNumericalArrayOf<uint> uintArray)
|
||||||
var position = startPosition;
|
|
||||||
for (var i = 0; i < numberOfFields; i++)
|
|
||||||
{
|
{
|
||||||
var list = new List<byte>();
|
if (uintArray.Data[0] == 3707764736u)
|
||||||
while (bytes[position] != 0)
|
|
||||||
{
|
{
|
||||||
list.Add(bytes[position]);
|
var (dimensions, indexToObjectId, classIndex) = DataElementReader.ParseOpaqueData(uintArray.Data);
|
||||||
position++;
|
return new OpaqueLink(
|
||||||
|
uintArray.Name,
|
||||||
|
string.Empty,
|
||||||
|
string.Empty,
|
||||||
|
dimensions,
|
||||||
|
array as DataElement,
|
||||||
|
indexToObjectId,
|
||||||
|
classIndex,
|
||||||
|
subsystemData);
|
||||||
}
|
}
|
||||||
result[i] = Encoding.ASCII.GetString(list.ToArray());
|
|
||||||
position++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct ObjectClassInformation
|
||||||
|
{
|
||||||
|
public ObjectClassInformation(int embeddedObjectPosition, int objectPosition, int loadingOrder, int classId)
|
||||||
|
{
|
||||||
|
EmbeddedObjectPosition = embeddedObjectPosition;
|
||||||
|
ObjectPosition = objectPosition;
|
||||||
|
LoadingOrder = loadingOrder;
|
||||||
|
ClassId = classId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int ClassId { get; }
|
||||||
|
|
||||||
|
public int EmbeddedObjectPosition { get; }
|
||||||
|
|
||||||
|
public int LoadingOrder { get; }
|
||||||
|
|
||||||
|
public int ObjectPosition { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user