fe2a6db786
rust / build / test / clippy / fmt (push) Has been cancelled
Layout:
- src/ .NET 10 x64 reference: MxNativeCodec, MxNativeClient,
MxAsbClient, probes, tests, harnesses. Executable spec.
- design/ Architectural plan for the Rust port (M0–M6), error
model, protocol invariants, risks (R1–R16), adversarial
review log (review.md).
- rust/ Rust workspace. M0 skeleton + M1 codec parity.
mxaccess-codec: 215 unit tests + 2 cross-implementation
parity tests (byte-identical against .NET reference).
Other crates are M0 stubs awaiting M2+.
- captures/ Frida + netsh + pcap evidence per CLAUDE.md
("captures are evidence, not throwaway logs").
- analysis/ Decompiled C# (frida/proxy/decompiled-*),
Ghidra exports for native DLLs (`exports/` only —
working state at `projects/` and AVEVA's input
binaries at `input/` are gitignored).
- docs/ Reverse-engineering reference docs.
- tools/ Setup-LiveProbeEnv.ps1 (Infisical credential fetcher),
Compute-Crc.ps1 (.NET parity helper).
- .github/workflows/ Rust CI: fmt + build + test + clippy on Windows.
- LICENSE MIT (Joseph Doherty, 2026).
Verified:
- cargo test --workspace → 217 passed (215 unit + 2 .NET parity), 0 failed
- cargo clippy --workspace -- -D warnings → clean
- cargo fmt --all -- --check → clean
- cargo publish --dry-run -p mxaccess-codec → packages cleanly
Excluded from history (see .gitignore):
- **/bin, **/obj, **/target — build artifacts
- analysis/ghidra/projects/ — Ghidra working state (regenerable)
- analysis/ghidra/input/ — AVEVA proprietary DLLs (vendor IP)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1394 lines
30 KiB
C#
1394 lines
30 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Text;
|
|
using System.Xml.Serialization;
|
|
using ArchestrAServices.ASBContract;
|
|
using ArchestrAServices.Common;
|
|
using Asb.Base.V2.Serialization;
|
|
|
|
namespace Asb.Base.V2;
|
|
|
|
[DebuggerDisplay("{Item}")]
|
|
public sealed class Value : IBaseV2CustomSerializable, IEquatable<Value>
|
|
{
|
|
[XmlIgnore]
|
|
public object Item { get; private set; }
|
|
|
|
[XmlIgnore]
|
|
public ValueItemType ItemType { get; private set; }
|
|
|
|
[XmlIgnore]
|
|
public bool IsArray { get; private set; }
|
|
|
|
[XmlIgnore]
|
|
public ushort MxLegacyItemType { get; private set; }
|
|
|
|
[XmlIgnore]
|
|
public int MxLegacyLength { get; private set; }
|
|
|
|
[XmlElement(ElementName = "Payload", DataType = "base64Binary", IsNullable = true, Order = 0)]
|
|
public byte[] Payload
|
|
{
|
|
get
|
|
{
|
|
using MemoryStream memoryStream = new MemoryStream();
|
|
using BinaryWriter writer = new BinaryWriter(memoryStream);
|
|
((IBaseV2CustomSerializable)this).WriteToStream(writer);
|
|
return memoryStream.ToArray();
|
|
}
|
|
set
|
|
{
|
|
using MemoryStream input = new MemoryStream(value);
|
|
using BinaryReader reader = new BinaryReader(input);
|
|
((IBaseV2CustomSerializable)this).InitializeFromStream(reader);
|
|
}
|
|
}
|
|
|
|
[XmlIgnore]
|
|
public DataType MxLegacyDataType
|
|
{
|
|
get
|
|
{
|
|
switch (ItemType)
|
|
{
|
|
case ValueItemType.Boolean:
|
|
if (!IsArray)
|
|
{
|
|
return DataType.TypeBool;
|
|
}
|
|
return DataType.TypeBoolArray;
|
|
case ValueItemType.Byte:
|
|
if (!IsArray)
|
|
{
|
|
return DataType.TypeByte;
|
|
}
|
|
return DataType.TypeByteArray;
|
|
case ValueItemType.SignedByte:
|
|
if (!IsArray)
|
|
{
|
|
return DataType.TypeSByte;
|
|
}
|
|
return DataType.TypeSByteArray;
|
|
case ValueItemType.Short:
|
|
if (!IsArray)
|
|
{
|
|
return DataType.TypeInt16;
|
|
}
|
|
return DataType.TypeInt16Array;
|
|
case ValueItemType.UnsignedShort:
|
|
if (!IsArray)
|
|
{
|
|
return DataType.TypeUInt16;
|
|
}
|
|
return DataType.TypeUInt16Array;
|
|
case ValueItemType.Int:
|
|
if (!IsArray)
|
|
{
|
|
return DataType.TypeInt32;
|
|
}
|
|
return DataType.TypeInt32Array;
|
|
case ValueItemType.UnsignedInt:
|
|
if (!IsArray)
|
|
{
|
|
return DataType.TypeUInt32;
|
|
}
|
|
return DataType.TypeUInt32Array;
|
|
case ValueItemType.Long:
|
|
if (!IsArray)
|
|
{
|
|
return DataType.TypeInt64;
|
|
}
|
|
return DataType.TypeInt64Array;
|
|
case ValueItemType.UnsignedLong:
|
|
if (!IsArray)
|
|
{
|
|
return DataType.TypeUInt64;
|
|
}
|
|
return DataType.TypeUInt64Array;
|
|
case ValueItemType.Single:
|
|
if (!IsArray)
|
|
{
|
|
return DataType.TypeFloat;
|
|
}
|
|
return DataType.TypeFloatArray;
|
|
case ValueItemType.Double:
|
|
if (!IsArray)
|
|
{
|
|
return DataType.TypeDouble;
|
|
}
|
|
return DataType.TypeDoubleArray;
|
|
case ValueItemType.DateTime:
|
|
if (!IsArray)
|
|
{
|
|
return DataType.TypeDateTime;
|
|
}
|
|
return DataType.TypeDateTimeArray;
|
|
case ValueItemType.Duration:
|
|
if (!IsArray)
|
|
{
|
|
return DataType.TypeDuration;
|
|
}
|
|
return DataType.TypeDurationArray;
|
|
case ValueItemType.Guid:
|
|
if (!IsArray)
|
|
{
|
|
return DataType.TypeGuid;
|
|
}
|
|
return DataType.TypeGuidArray;
|
|
case ValueItemType.String:
|
|
if (!IsArray)
|
|
{
|
|
return DataType.TypeString;
|
|
}
|
|
return DataType.TypeStringArray;
|
|
default:
|
|
return ASBEnumFactory.IntToDataType(MxLegacyItemType);
|
|
}
|
|
}
|
|
}
|
|
|
|
public static Value Create(object value)
|
|
{
|
|
if (value == null)
|
|
{
|
|
return new Value();
|
|
}
|
|
ValueItemType? valueItemType = ClrTypeToValueType(value.GetType());
|
|
if (!valueItemType.HasValue)
|
|
{
|
|
throw new ArgumentException("value is not a supported type", "value");
|
|
}
|
|
return new Value
|
|
{
|
|
Item = value,
|
|
ItemType = valueItemType.Value,
|
|
IsArray = (value is Array)
|
|
};
|
|
}
|
|
|
|
public static Value Create(bool value)
|
|
{
|
|
return new Value
|
|
{
|
|
Item = value,
|
|
ItemType = ValueItemType.Boolean,
|
|
IsArray = false
|
|
};
|
|
}
|
|
|
|
public static Value Create(byte value)
|
|
{
|
|
return new Value
|
|
{
|
|
Item = value,
|
|
ItemType = ValueItemType.Byte,
|
|
IsArray = false
|
|
};
|
|
}
|
|
|
|
public static Value Create(sbyte value)
|
|
{
|
|
return new Value
|
|
{
|
|
Item = value,
|
|
ItemType = ValueItemType.SignedByte,
|
|
IsArray = false
|
|
};
|
|
}
|
|
|
|
public static Value Create(short value)
|
|
{
|
|
return new Value
|
|
{
|
|
Item = value,
|
|
ItemType = ValueItemType.Short,
|
|
IsArray = false
|
|
};
|
|
}
|
|
|
|
public static Value Create(ushort value)
|
|
{
|
|
return new Value
|
|
{
|
|
Item = value,
|
|
ItemType = ValueItemType.UnsignedShort,
|
|
IsArray = false
|
|
};
|
|
}
|
|
|
|
public static Value Create(int value)
|
|
{
|
|
return new Value
|
|
{
|
|
Item = value,
|
|
ItemType = ValueItemType.Int,
|
|
IsArray = false
|
|
};
|
|
}
|
|
|
|
public static Value Create(uint value)
|
|
{
|
|
return new Value
|
|
{
|
|
Item = value,
|
|
ItemType = ValueItemType.UnsignedInt,
|
|
IsArray = false
|
|
};
|
|
}
|
|
|
|
public static Value Create(long value)
|
|
{
|
|
return new Value
|
|
{
|
|
Item = value,
|
|
ItemType = ValueItemType.Long,
|
|
IsArray = false
|
|
};
|
|
}
|
|
|
|
public static Value Create(ulong value)
|
|
{
|
|
return new Value
|
|
{
|
|
Item = value,
|
|
ItemType = ValueItemType.UnsignedLong,
|
|
IsArray = false
|
|
};
|
|
}
|
|
|
|
public static Value Create(float value)
|
|
{
|
|
return new Value
|
|
{
|
|
Item = value,
|
|
ItemType = ValueItemType.Single,
|
|
IsArray = false
|
|
};
|
|
}
|
|
|
|
public static Value Create(double value)
|
|
{
|
|
return new Value
|
|
{
|
|
Item = value,
|
|
ItemType = ValueItemType.Double,
|
|
IsArray = false
|
|
};
|
|
}
|
|
|
|
public static Value Create(decimal value)
|
|
{
|
|
return new Value
|
|
{
|
|
Item = value,
|
|
ItemType = ValueItemType.Decimal,
|
|
IsArray = false
|
|
};
|
|
}
|
|
|
|
public static Value Create(string value)
|
|
{
|
|
if (value == null)
|
|
{
|
|
throw new ArgumentNullException("value");
|
|
}
|
|
return new Value
|
|
{
|
|
Item = value,
|
|
ItemType = ValueItemType.String,
|
|
IsArray = false
|
|
};
|
|
}
|
|
|
|
public static Value Create(DateTime value)
|
|
{
|
|
return new Value
|
|
{
|
|
Item = value,
|
|
ItemType = ValueItemType.DateTime,
|
|
IsArray = false
|
|
};
|
|
}
|
|
|
|
public static Value Create(TimeSpan value)
|
|
{
|
|
return new Value
|
|
{
|
|
Item = value,
|
|
ItemType = ValueItemType.Duration,
|
|
IsArray = false
|
|
};
|
|
}
|
|
|
|
public static Value Create(Guid value)
|
|
{
|
|
return new Value
|
|
{
|
|
Item = value,
|
|
ItemType = ValueItemType.Guid,
|
|
IsArray = false
|
|
};
|
|
}
|
|
|
|
public static Value Create(bool[] value)
|
|
{
|
|
return new Value
|
|
{
|
|
Item = value,
|
|
ItemType = ValueItemType.Boolean,
|
|
IsArray = true
|
|
};
|
|
}
|
|
|
|
public static Value Create(byte[] value)
|
|
{
|
|
return new Value
|
|
{
|
|
Item = value,
|
|
ItemType = ValueItemType.Byte,
|
|
IsArray = true
|
|
};
|
|
}
|
|
|
|
public static Value Create(sbyte[] value)
|
|
{
|
|
return new Value
|
|
{
|
|
Item = value,
|
|
ItemType = ValueItemType.SignedByte,
|
|
IsArray = true
|
|
};
|
|
}
|
|
|
|
public static Value Create(short[] value)
|
|
{
|
|
return new Value
|
|
{
|
|
Item = value,
|
|
ItemType = ValueItemType.Short,
|
|
IsArray = true
|
|
};
|
|
}
|
|
|
|
public static Value Create(ushort[] value)
|
|
{
|
|
return new Value
|
|
{
|
|
Item = value,
|
|
ItemType = ValueItemType.UnsignedShort,
|
|
IsArray = true
|
|
};
|
|
}
|
|
|
|
public static Value Create(int[] value)
|
|
{
|
|
return new Value
|
|
{
|
|
Item = value,
|
|
ItemType = ValueItemType.Int,
|
|
IsArray = true
|
|
};
|
|
}
|
|
|
|
public static Value Create(uint[] value)
|
|
{
|
|
return new Value
|
|
{
|
|
Item = value,
|
|
ItemType = ValueItemType.UnsignedInt,
|
|
IsArray = true
|
|
};
|
|
}
|
|
|
|
public static Value Create(long[] value)
|
|
{
|
|
return new Value
|
|
{
|
|
Item = value,
|
|
ItemType = ValueItemType.Long,
|
|
IsArray = true
|
|
};
|
|
}
|
|
|
|
public static Value Create(ulong[] value)
|
|
{
|
|
return new Value
|
|
{
|
|
Item = value,
|
|
ItemType = ValueItemType.UnsignedLong,
|
|
IsArray = true
|
|
};
|
|
}
|
|
|
|
public static Value Create(float[] value)
|
|
{
|
|
return new Value
|
|
{
|
|
Item = value,
|
|
ItemType = ValueItemType.Single,
|
|
IsArray = true
|
|
};
|
|
}
|
|
|
|
public static Value Create(double[] value)
|
|
{
|
|
return new Value
|
|
{
|
|
Item = value,
|
|
ItemType = ValueItemType.Double,
|
|
IsArray = true
|
|
};
|
|
}
|
|
|
|
public static Value Create(decimal[] value)
|
|
{
|
|
return new Value
|
|
{
|
|
Item = value,
|
|
ItemType = ValueItemType.Decimal,
|
|
IsArray = true
|
|
};
|
|
}
|
|
|
|
public static Value Create(string[] value)
|
|
{
|
|
return new Value
|
|
{
|
|
Item = value,
|
|
ItemType = ValueItemType.String,
|
|
IsArray = true
|
|
};
|
|
}
|
|
|
|
public static Value Create(DateTime[] value)
|
|
{
|
|
return new Value
|
|
{
|
|
Item = value,
|
|
ItemType = ValueItemType.DateTime,
|
|
IsArray = true
|
|
};
|
|
}
|
|
|
|
public static Value Create(TimeSpan[] value)
|
|
{
|
|
return new Value
|
|
{
|
|
Item = value,
|
|
ItemType = ValueItemType.Duration,
|
|
IsArray = true
|
|
};
|
|
}
|
|
|
|
public static Value Create(Guid[] value)
|
|
{
|
|
return new Value
|
|
{
|
|
Item = value,
|
|
ItemType = ValueItemType.Guid,
|
|
IsArray = true
|
|
};
|
|
}
|
|
|
|
public static Value CreateMxLegacy(ushort mxLegacyType, int mxLegacyLength, byte[] mxLegacyPayload)
|
|
{
|
|
return new Value
|
|
{
|
|
Item = mxLegacyPayload,
|
|
ItemType = ValueItemType.MxLegacy,
|
|
IsArray = false,
|
|
MxLegacyLength = mxLegacyLength,
|
|
MxLegacyItemType = mxLegacyType
|
|
};
|
|
}
|
|
|
|
public static Value Create(CustomEnum customEnum)
|
|
{
|
|
if (customEnum == null)
|
|
{
|
|
throw new ArgumentNullException("customEnum");
|
|
}
|
|
short ordinal = customEnum.Ordinal;
|
|
string ordinalValue = customEnum.OrdinalValue;
|
|
ushort mxLegacyType = ASBEnumFactory.DataTypeToInt(DataType.TypeEnum);
|
|
List<byte> list = new List<byte>();
|
|
list.AddRange(BitConverter.GetBytes(Convert.ToInt16(ordinal)));
|
|
list.AddRange(BitConverter.GetBytes(Convert.ToUInt32(ordinalValue.Length)));
|
|
list.AddRange(Encoding.Unicode.GetBytes(ordinalValue));
|
|
int mxLegacyLength = 6 + ordinalValue.Length;
|
|
byte[] mxLegacyPayload = list.ToArray();
|
|
return CreateMxLegacy(mxLegacyType, mxLegacyLength, mxLegacyPayload);
|
|
}
|
|
|
|
public static Value Create(CustomEnum[] customEnums)
|
|
{
|
|
int num = 0;
|
|
List<byte> list = new List<byte>();
|
|
if (customEnums != null && customEnums.Length != 0)
|
|
{
|
|
CustomEnum[] array = customEnums;
|
|
foreach (CustomEnum obj in array)
|
|
{
|
|
short ordinal = obj.Ordinal;
|
|
string ordinalValue = obj.OrdinalValue;
|
|
num += 6 + ordinalValue.Length;
|
|
list.AddRange(BitConverter.GetBytes(Convert.ToInt16(ordinal)));
|
|
list.AddRange(BitConverter.GetBytes(Convert.ToUInt32(ordinalValue.Length)));
|
|
list.AddRange(Encoding.Unicode.GetBytes(ordinalValue));
|
|
}
|
|
int mxLegacyLength = num;
|
|
byte[] mxLegacyPayload = list.ToArray();
|
|
return CreateMxLegacy(ASBEnumFactory.DataTypeToInt(DataType.TypeEnumArray), mxLegacyLength, mxLegacyPayload);
|
|
}
|
|
return CreateMxLegacy(ASBEnumFactory.DataTypeToInt(DataType.TypeEnum), 0, new byte[0]);
|
|
}
|
|
|
|
public bool Equals(Value other)
|
|
{
|
|
if (other == null)
|
|
{
|
|
return false;
|
|
}
|
|
if (other == this)
|
|
{
|
|
return true;
|
|
}
|
|
if (ItemType != other.ItemType || IsArray != other.IsArray)
|
|
{
|
|
return false;
|
|
}
|
|
if (IsArray)
|
|
{
|
|
if (Item == other.Item)
|
|
{
|
|
return true;
|
|
}
|
|
if (Item == null || other.Item == null)
|
|
{
|
|
return false;
|
|
}
|
|
return ((IStructuralEquatable)Item).Equals(other.Item, StructuralComparisons.StructuralEqualityComparer);
|
|
}
|
|
return object.Equals(Item, other.Item);
|
|
}
|
|
|
|
public override bool Equals(object obj)
|
|
{
|
|
return Equals(obj as Value);
|
|
}
|
|
|
|
public override int GetHashCode()
|
|
{
|
|
return Item?.GetHashCode() ?? 0;
|
|
}
|
|
|
|
void IBaseV2CustomSerializable.InitializeFromStream(BinaryReader reader)
|
|
{
|
|
if (reader == null)
|
|
{
|
|
throw new ArgumentNullException("reader");
|
|
}
|
|
ItemType = (ValueItemType)reader.ReadInt32();
|
|
IsArray = reader.ReadBoolean();
|
|
if (IsArray)
|
|
{
|
|
if (reader.ReadBoolean())
|
|
{
|
|
Item = null;
|
|
return;
|
|
}
|
|
int num = reader.ReadInt32();
|
|
Array array = Array.CreateInstance(ValueTypeToClrType(ItemType), num);
|
|
for (int i = 0; i < num; i++)
|
|
{
|
|
array.SetValue(ReadContent(reader, ItemType), i);
|
|
}
|
|
Item = array;
|
|
}
|
|
else if (ItemType == ValueItemType.MxLegacy)
|
|
{
|
|
Tuple<ushort, int, byte[]> tuple = ReadMxLegacyContent(reader);
|
|
MxLegacyItemType = tuple.Item1;
|
|
MxLegacyLength = tuple.Item2;
|
|
Item = tuple.Item3;
|
|
}
|
|
else
|
|
{
|
|
Item = ReadContent(reader, ItemType);
|
|
}
|
|
}
|
|
|
|
void IBaseV2CustomSerializable.WriteToStream(BinaryWriter writer)
|
|
{
|
|
if (writer == null)
|
|
{
|
|
throw new ArgumentNullException("writer");
|
|
}
|
|
writer.Write((int)ItemType);
|
|
writer.Write(IsArray);
|
|
if (IsArray)
|
|
{
|
|
Array array = (Array)Item;
|
|
if (array != null)
|
|
{
|
|
writer.Write(value: false);
|
|
writer.Write(array.Length);
|
|
{
|
|
foreach (object item in array)
|
|
{
|
|
WriteContent(writer, item, ItemType);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
writer.Write(value: true);
|
|
}
|
|
else if (ItemType == ValueItemType.MxLegacy)
|
|
{
|
|
WriteMxLegacyContent(writer, MxLegacyItemType, MxLegacyLength, (byte[])Item);
|
|
}
|
|
else
|
|
{
|
|
WriteContent(writer, Item, ItemType);
|
|
}
|
|
}
|
|
|
|
void IBaseV2CustomSerializable.WriteArrayToStream(object graph, BinaryWriter writer)
|
|
{
|
|
if (graph == null || writer == null)
|
|
{
|
|
return;
|
|
}
|
|
try
|
|
{
|
|
if (graph is Value[] array)
|
|
{
|
|
writer.Write(array.Length);
|
|
Value[] array2 = array;
|
|
for (int i = 0; i < array2.Length; i++)
|
|
{
|
|
((IBaseV2CustomSerializable)array2[i]).WriteToStream(writer);
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
ServiceTrace.LogError("Exception caught serializing an array of Value objects: {0}", ex.Message);
|
|
}
|
|
}
|
|
|
|
object IBaseV2CustomSerializable.InitializeArrayFromStream(BinaryReader reader, int arrayLength)
|
|
{
|
|
if (reader == null)
|
|
{
|
|
return null;
|
|
}
|
|
Value[] result = null;
|
|
try
|
|
{
|
|
Value[] array = new Value[arrayLength];
|
|
for (int i = 0; i < arrayLength; i++)
|
|
{
|
|
((IBaseV2CustomSerializable)array[i]).InitializeFromStream(reader);
|
|
}
|
|
result = array;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
ServiceTrace.LogError("Exception caught deserializing an array of ASBStatus objects: {0}", ex.Message);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
public object ReadByte()
|
|
{
|
|
if (ItemType == ValueItemType.Byte)
|
|
{
|
|
return (byte)Item;
|
|
}
|
|
return MxValueHelper.ReadByte(MxLegacyItemType, MxLegacyLength, (byte[])Item);
|
|
}
|
|
|
|
public object ReadShort()
|
|
{
|
|
if (ItemType == ValueItemType.Short)
|
|
{
|
|
return (short)Item;
|
|
}
|
|
return MxValueHelper.ReadShort(MxLegacyItemType, MxLegacyLength, (byte[])Item);
|
|
}
|
|
|
|
public object ReadUnsignedShort()
|
|
{
|
|
if (ItemType == ValueItemType.UnsignedShort)
|
|
{
|
|
return (ushort)Item;
|
|
}
|
|
return MxValueHelper.ReadUnsignedShort(MxLegacyItemType, MxLegacyLength, (byte[])Item);
|
|
}
|
|
|
|
public object ReadInt()
|
|
{
|
|
if (ItemType == ValueItemType.Int)
|
|
{
|
|
return (int)Item;
|
|
}
|
|
return MxValueHelper.ReadInt(MxLegacyItemType, MxLegacyLength, (byte[])Item);
|
|
}
|
|
|
|
public object ReadUnsignedInt()
|
|
{
|
|
if (ItemType == ValueItemType.UnsignedInt)
|
|
{
|
|
return (uint)Item;
|
|
}
|
|
return MxValueHelper.ReadUnsignedInt(MxLegacyItemType, MxLegacyLength, (byte[])Item);
|
|
}
|
|
|
|
public object ReadLong()
|
|
{
|
|
if (ItemType == ValueItemType.Long)
|
|
{
|
|
return (long)Item;
|
|
}
|
|
return MxValueHelper.ReadLong(MxLegacyItemType, MxLegacyLength, (byte[])Item);
|
|
}
|
|
|
|
public object ReadUnsignedLong()
|
|
{
|
|
if (ItemType == ValueItemType.UnsignedLong)
|
|
{
|
|
return (ulong)Item;
|
|
}
|
|
return MxValueHelper.ReadUnsignedLong(MxLegacyItemType, MxLegacyLength, (byte[])Item);
|
|
}
|
|
|
|
public object ReadSingle()
|
|
{
|
|
if (ItemType == ValueItemType.Single)
|
|
{
|
|
return (float)Item;
|
|
}
|
|
return MxValueHelper.ReadSingle(MxLegacyItemType, MxLegacyLength, (byte[])Item);
|
|
}
|
|
|
|
public object ReadDouble()
|
|
{
|
|
if (ItemType == ValueItemType.Double)
|
|
{
|
|
return (double)Item;
|
|
}
|
|
return MxValueHelper.ReadDouble(MxLegacyItemType, MxLegacyLength, (byte[])Item);
|
|
}
|
|
|
|
public object ReadString()
|
|
{
|
|
if (ItemType == ValueItemType.String)
|
|
{
|
|
return (string)Item;
|
|
}
|
|
return MxValueHelper.ReadDouble(MxLegacyItemType, MxLegacyLength, (byte[])Item);
|
|
}
|
|
|
|
public object ReadBoolean()
|
|
{
|
|
if (ItemType == ValueItemType.Boolean)
|
|
{
|
|
return (bool)Item;
|
|
}
|
|
return MxValueHelper.ReadBoolean(MxLegacyItemType, MxLegacyLength, (byte[])Item);
|
|
}
|
|
|
|
public object ReadSignedByte()
|
|
{
|
|
if (ItemType == ValueItemType.SignedByte)
|
|
{
|
|
return (sbyte)Item;
|
|
}
|
|
return MxValueHelper.ReadSignedByte(MxLegacyItemType, MxLegacyLength, (byte[])Item);
|
|
}
|
|
|
|
public object ReadDateTime()
|
|
{
|
|
if (ItemType == ValueItemType.DateTime)
|
|
{
|
|
return (DateTime)Item;
|
|
}
|
|
return MxValueHelper.ReadDateTime(MxLegacyItemType, MxLegacyLength, (byte[])Item);
|
|
}
|
|
|
|
public object ReadDuration()
|
|
{
|
|
if (ItemType == ValueItemType.Duration)
|
|
{
|
|
return (TimeSpan)Item;
|
|
}
|
|
return MxValueHelper.ReadDuration(MxLegacyItemType, MxLegacyLength, (byte[])Item);
|
|
}
|
|
|
|
public object ReadGuid()
|
|
{
|
|
if (ItemType == ValueItemType.Guid)
|
|
{
|
|
return (Guid)Item;
|
|
}
|
|
return MxValueHelper.ReadGuid(MxLegacyItemType, MxLegacyLength, (byte[])Item);
|
|
}
|
|
|
|
public byte[] ReadByteString()
|
|
{
|
|
return (byte[])Item;
|
|
}
|
|
|
|
public string ReadLocalizedText()
|
|
{
|
|
return (string)ReadString();
|
|
}
|
|
|
|
public object GetScalarValue(DataType targetType)
|
|
{
|
|
object obj = null;
|
|
try
|
|
{
|
|
switch (ASBEnumFactory.IntToDataType(MxLegacyItemType))
|
|
{
|
|
case DataType.TypeBool:
|
|
obj = ReadBoolean();
|
|
return (DataType.TypeBool == targetType) ? obj : MxValueHelper.GetValueInTargetType(targetType, obj);
|
|
case DataType.TypeByte:
|
|
obj = ReadByte();
|
|
return (targetType == DataType.TypeByte) ? obj : MxValueHelper.GetValueInTargetType(targetType, obj);
|
|
case DataType.TypeDateTime:
|
|
obj = ReadDateTime();
|
|
return (targetType == DataType.TypeByte) ? obj : MxValueHelper.GetValueInTargetType(targetType, obj);
|
|
case DataType.TypeDouble:
|
|
obj = ReadDouble();
|
|
return (DataType.TypeDouble == targetType) ? obj : MxValueHelper.GetValueInTargetType(targetType, obj);
|
|
case DataType.TypeDuration:
|
|
obj = ReadDuration();
|
|
return (DataType.TypeDuration == targetType) ? obj : MxValueHelper.GetValueInTargetType(targetType, obj);
|
|
case DataType.TypeFloat:
|
|
obj = ReadSingle();
|
|
return (DataType.TypeFloat == targetType) ? obj : MxValueHelper.GetValueInTargetType(targetType, obj);
|
|
case DataType.TypeGuid:
|
|
obj = ReadGuid();
|
|
return (DataType.TypeGuid == targetType) ? obj : MxValueHelper.GetValueInTargetType(targetType, obj);
|
|
case DataType.TypeInt16:
|
|
obj = ReadShort();
|
|
return (DataType.TypeInt16 == targetType) ? obj : MxValueHelper.GetValueInTargetType(targetType, obj);
|
|
case DataType.TypeInt32:
|
|
obj = ReadInt();
|
|
return (DataType.TypeInt32 == targetType) ? obj : MxValueHelper.GetValueInTargetType(targetType, obj);
|
|
case DataType.TypeInt64:
|
|
obj = ReadLong();
|
|
return (DataType.TypeInt64 == targetType) ? obj : MxValueHelper.GetValueInTargetType(targetType, obj);
|
|
case DataType.TypeLocalizedText:
|
|
obj = ReadLocalizedText();
|
|
return (DataType.TypeLocalizedText == targetType) ? obj : MxValueHelper.GetValueInTargetType(targetType, obj);
|
|
case DataType.TypeSByte:
|
|
obj = ReadSignedByte();
|
|
return (DataType.TypeSByte == targetType) ? obj : MxValueHelper.GetValueInTargetType(targetType, obj);
|
|
case DataType.TypeString:
|
|
obj = ReadString();
|
|
return (DataType.TypeString == targetType) ? obj : MxValueHelper.GetValueInTargetType(targetType, obj);
|
|
case DataType.TypeUInt16:
|
|
obj = ReadUnsignedShort();
|
|
return (DataType.TypeUInt16 == targetType) ? obj : MxValueHelper.GetValueInTargetType(targetType, obj);
|
|
case DataType.TypeUInt32:
|
|
obj = ReadUnsignedInt();
|
|
return (DataType.TypeUInt32 == targetType) ? obj : MxValueHelper.GetValueInTargetType(targetType, obj);
|
|
case DataType.TypeUInt64:
|
|
obj = ReadUnsignedLong();
|
|
return (DataType.TypeUInt64 == targetType) ? obj : MxValueHelper.GetValueInTargetType(targetType, obj);
|
|
default:
|
|
return ReadString();
|
|
}
|
|
}
|
|
catch (Exception)
|
|
{
|
|
return obj;
|
|
}
|
|
}
|
|
|
|
public object GetValueInTargetArrayType(DataType targetType)
|
|
{
|
|
switch (targetType)
|
|
{
|
|
case DataType.TypeBool:
|
|
{
|
|
bool[] array14 = (bool[])ReadBooleanArray();
|
|
if (array14 == null || array14.Length == 0)
|
|
{
|
|
return null;
|
|
}
|
|
return array14;
|
|
}
|
|
case DataType.TypeByte:
|
|
{
|
|
byte[] array8 = (byte[])ReadByteArray();
|
|
if (array8 == null || array8.Length == 0)
|
|
{
|
|
return null;
|
|
}
|
|
return array8;
|
|
}
|
|
case DataType.TypeDateTime:
|
|
{
|
|
DateTime[] array15 = (DateTime[])ReadDateTimeArray();
|
|
if (array15 == null || array15.Length == 0)
|
|
{
|
|
return null;
|
|
}
|
|
return array15;
|
|
}
|
|
case DataType.TypeDouble:
|
|
{
|
|
double[] array11 = (double[])ReadDoubleArray();
|
|
if (array11 == null || array11.Length == 0)
|
|
{
|
|
return null;
|
|
}
|
|
return array11;
|
|
}
|
|
case DataType.TypeDuration:
|
|
{
|
|
TimeSpan[] array6 = (TimeSpan[])ReadDurationArray();
|
|
if (array6 == null || array6.Length == 0)
|
|
{
|
|
return null;
|
|
}
|
|
return array6;
|
|
}
|
|
case DataType.TypeFloat:
|
|
{
|
|
float[] array12 = (float[])ReadSingleArray();
|
|
if (array12 == null || array12.Length == 0)
|
|
{
|
|
return null;
|
|
}
|
|
return array12;
|
|
}
|
|
case DataType.TypeGuid:
|
|
{
|
|
Guid[] array10 = (Guid[])ReadGuidArray();
|
|
if (array10 == null || array10.Length == 0)
|
|
{
|
|
return null;
|
|
}
|
|
return array10;
|
|
}
|
|
case DataType.TypeInt16:
|
|
{
|
|
short[] array3 = (short[])ReadShortArray();
|
|
if (array3 == null || array3.Length == 0)
|
|
{
|
|
return null;
|
|
}
|
|
return array3;
|
|
}
|
|
case DataType.TypeInt32:
|
|
{
|
|
int[] array4 = (int[])ReadIntegerArray();
|
|
if (array4 == null || array4.Length == 0)
|
|
{
|
|
return null;
|
|
}
|
|
return array4;
|
|
}
|
|
case DataType.TypeInt64:
|
|
{
|
|
long[] array7 = (long[])ReadLongArray();
|
|
if (array7 == null || array7.Length == 0)
|
|
{
|
|
return null;
|
|
}
|
|
return array7;
|
|
}
|
|
case DataType.TypeSByte:
|
|
{
|
|
sbyte[] array2 = (sbyte[])ReadSignedByteArray();
|
|
if (array2 == null || array2.Length == 0)
|
|
{
|
|
return null;
|
|
}
|
|
return array2;
|
|
}
|
|
case DataType.TypeUInt16:
|
|
{
|
|
ushort[] array13 = (ushort[])ReadUnsignedShortArray();
|
|
if (array13 == null || array13.Length == 0)
|
|
{
|
|
return null;
|
|
}
|
|
return array13;
|
|
}
|
|
case DataType.TypeUInt32:
|
|
{
|
|
uint[] array9 = (uint[])ReadUnsignedIntArray();
|
|
if (array9 == null || array9.Length == 0)
|
|
{
|
|
return null;
|
|
}
|
|
return array9;
|
|
}
|
|
case DataType.TypeUInt64:
|
|
{
|
|
ulong[] array5 = (ulong[])ReadUnsignedLongArray();
|
|
if (array5 == null || array5.Length == 0)
|
|
{
|
|
return null;
|
|
}
|
|
return array5;
|
|
}
|
|
default:
|
|
{
|
|
string[] array = (string[])ReadStringArray();
|
|
if (array == null || array.Length == 0)
|
|
{
|
|
return null;
|
|
}
|
|
return array;
|
|
}
|
|
}
|
|
}
|
|
|
|
public object GetArrayValue(DataType targetType)
|
|
{
|
|
try
|
|
{
|
|
return GetValueInTargetArrayType(targetType);
|
|
}
|
|
catch (Exception)
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public object ReadEnum()
|
|
{
|
|
return MxValueHelper.ReadEnum(MxLegacyItemType, MxLegacyLength, (byte[])Item);
|
|
}
|
|
|
|
public object ReadIntegerArray()
|
|
{
|
|
if (ItemType == ValueItemType.Int && IsArray)
|
|
{
|
|
return (int[])Item;
|
|
}
|
|
return MxValueHelper.ReadIntegerArray(MxLegacyItemType, MxLegacyLength, (byte[])Item);
|
|
}
|
|
|
|
public object ReadByteArray()
|
|
{
|
|
if (ItemType == ValueItemType.Byte && IsArray)
|
|
{
|
|
return (byte[])Item;
|
|
}
|
|
return MxValueHelper.ReadByteArray(MxLegacyItemType, MxLegacyLength, (byte[])Item);
|
|
}
|
|
|
|
public char[] ReadCharArray()
|
|
{
|
|
return MxValueHelper.ReadCharArray(MxLegacyItemType, MxLegacyLength, (byte[])Item);
|
|
}
|
|
|
|
public object ReadShortArray()
|
|
{
|
|
if (ItemType == ValueItemType.Short && IsArray)
|
|
{
|
|
return (short[])Item;
|
|
}
|
|
return MxValueHelper.ReadShortArray(MxLegacyItemType, MxLegacyLength, (byte[])Item);
|
|
}
|
|
|
|
public object ReadUnsignedShortArray()
|
|
{
|
|
if (ItemType == ValueItemType.UnsignedShort && IsArray)
|
|
{
|
|
return (ushort[])Item;
|
|
}
|
|
return MxValueHelper.ReadUnsignedShortArray(MxLegacyItemType, MxLegacyLength, (byte[])Item);
|
|
}
|
|
|
|
public object ReadUnsignedIntArray()
|
|
{
|
|
if (ItemType == ValueItemType.UnsignedInt && IsArray)
|
|
{
|
|
return (uint[])Item;
|
|
}
|
|
return MxValueHelper.ReadUnsignedIntArray(MxLegacyItemType, MxLegacyLength, (byte[])Item);
|
|
}
|
|
|
|
public object ReadLongArray()
|
|
{
|
|
if (ItemType == ValueItemType.Long && IsArray)
|
|
{
|
|
return (long[])Item;
|
|
}
|
|
return MxValueHelper.ReadLongArray(MxLegacyItemType, MxLegacyLength, (byte[])Item);
|
|
}
|
|
|
|
public object ReadUnsignedLongArray()
|
|
{
|
|
if (ItemType == ValueItemType.UnsignedLong && IsArray)
|
|
{
|
|
return (ulong[])Item;
|
|
}
|
|
return MxValueHelper.ReadUnsignedLongArray(MxLegacyItemType, MxLegacyLength, (byte[])Item);
|
|
}
|
|
|
|
public object ReadSingleArray()
|
|
{
|
|
if (ItemType == ValueItemType.Single && IsArray)
|
|
{
|
|
return (float[])Item;
|
|
}
|
|
return MxValueHelper.ReadSingleArray(MxLegacyItemType, MxLegacyLength, (byte[])Item);
|
|
}
|
|
|
|
public object ReadDoubleArray()
|
|
{
|
|
if (ItemType == ValueItemType.Double && IsArray)
|
|
{
|
|
return (double[])Item;
|
|
}
|
|
return MxValueHelper.ReadDoubleArray(MxLegacyItemType, MxLegacyLength, (byte[])Item);
|
|
}
|
|
|
|
public object ReadStringArray()
|
|
{
|
|
if (ItemType == ValueItemType.String && IsArray)
|
|
{
|
|
return (string[])Item;
|
|
}
|
|
return MxValueHelper.ReadStringArray(MxLegacyItemType, MxLegacyLength, (byte[])Item);
|
|
}
|
|
|
|
public object ReadDateTimeArray()
|
|
{
|
|
if (ItemType == ValueItemType.DateTime && IsArray)
|
|
{
|
|
return (DateTime[])Item;
|
|
}
|
|
return MxValueHelper.ReadDateTimeArray(MxLegacyItemType, MxLegacyLength, (byte[])Item);
|
|
}
|
|
|
|
public object ReadDurationArray()
|
|
{
|
|
if (ItemType == ValueItemType.Duration && IsArray)
|
|
{
|
|
return (TimeSpan[])Item;
|
|
}
|
|
return MxValueHelper.ReadDurationArray(MxLegacyItemType, MxLegacyLength, (byte[])Item);
|
|
}
|
|
|
|
public object ReadGuidArray()
|
|
{
|
|
if (ItemType == ValueItemType.Guid && IsArray)
|
|
{
|
|
return (Guid[])Item;
|
|
}
|
|
return MxValueHelper.ReadGuidArray(MxLegacyItemType, MxLegacyLength, (byte[])Item);
|
|
}
|
|
|
|
public object ReadBooleanArray()
|
|
{
|
|
if (ItemType == ValueItemType.Boolean && IsArray)
|
|
{
|
|
return (bool[])Item;
|
|
}
|
|
return MxValueHelper.ReadBooleanArray(MxLegacyItemType, MxLegacyLength, (byte[])Item);
|
|
}
|
|
|
|
public object ReadSignedByteArray()
|
|
{
|
|
if (ItemType == ValueItemType.SignedByte && IsArray)
|
|
{
|
|
return (sbyte[])Item;
|
|
}
|
|
return MxValueHelper.ReadSignedByteArray(MxLegacyItemType, MxLegacyLength, (byte[])Item);
|
|
}
|
|
|
|
public object ReadEnumArray()
|
|
{
|
|
return MxValueHelper.ReadEnumArray(MxLegacyItemType, MxLegacyLength, (byte[])Item);
|
|
}
|
|
|
|
private static object ReadContent(BinaryReader reader, ValueItemType valueType)
|
|
{
|
|
return valueType switch
|
|
{
|
|
ValueItemType.None => null,
|
|
ValueItemType.Boolean => reader.ReadBoolean(),
|
|
ValueItemType.Byte => reader.ReadByte(),
|
|
ValueItemType.SignedByte => reader.ReadSByte(),
|
|
ValueItemType.Short => reader.ReadInt16(),
|
|
ValueItemType.UnsignedShort => reader.ReadUInt16(),
|
|
ValueItemType.Int => reader.ReadInt32(),
|
|
ValueItemType.UnsignedInt => reader.ReadUInt32(),
|
|
ValueItemType.Long => reader.ReadInt64(),
|
|
ValueItemType.UnsignedLong => reader.ReadUInt64(),
|
|
ValueItemType.Single => reader.ReadSingle(),
|
|
ValueItemType.Double => reader.ReadDouble(),
|
|
ValueItemType.Decimal => reader.ReadDecimal(),
|
|
ValueItemType.DateTime => DateTime.FromFileTimeUtc(reader.ReadInt64()),
|
|
ValueItemType.Duration => TimeSpan.FromTicks(reader.ReadInt64()),
|
|
ValueItemType.Guid => new Guid(reader.ReadBytes(16)),
|
|
ValueItemType.String => reader.ReadStringSafe(),
|
|
_ => throw new NotSupportedException("Unsupported ItemType"),
|
|
};
|
|
}
|
|
|
|
private static void WriteContent(BinaryWriter writer, object value, ValueItemType valueType)
|
|
{
|
|
switch (valueType)
|
|
{
|
|
case ValueItemType.Boolean:
|
|
writer.Write((bool)value);
|
|
break;
|
|
case ValueItemType.Byte:
|
|
writer.Write((byte)value);
|
|
break;
|
|
case ValueItemType.SignedByte:
|
|
writer.Write((sbyte)value);
|
|
break;
|
|
case ValueItemType.Short:
|
|
writer.Write((short)value);
|
|
break;
|
|
case ValueItemType.UnsignedShort:
|
|
writer.Write((ushort)value);
|
|
break;
|
|
case ValueItemType.Int:
|
|
writer.Write((int)value);
|
|
break;
|
|
case ValueItemType.UnsignedInt:
|
|
writer.Write((uint)value);
|
|
break;
|
|
case ValueItemType.Long:
|
|
writer.Write((long)value);
|
|
break;
|
|
case ValueItemType.UnsignedLong:
|
|
writer.Write((ulong)value);
|
|
break;
|
|
case ValueItemType.Single:
|
|
writer.Write((float)value);
|
|
break;
|
|
case ValueItemType.Double:
|
|
writer.Write((double)value);
|
|
break;
|
|
case ValueItemType.Decimal:
|
|
writer.Write((decimal)value);
|
|
break;
|
|
case ValueItemType.DateTime:
|
|
writer.Write(((DateTime)value).ToFileTimeUtc());
|
|
break;
|
|
case ValueItemType.Duration:
|
|
writer.Write(((TimeSpan)value).Ticks);
|
|
break;
|
|
case ValueItemType.Guid:
|
|
writer.Write(((Guid)value).ToByteArray());
|
|
break;
|
|
case ValueItemType.String:
|
|
writer.WriteStringSafe((string)value);
|
|
break;
|
|
default:
|
|
throw new NotSupportedException("Unsupported ItemType");
|
|
case ValueItemType.None:
|
|
break;
|
|
}
|
|
}
|
|
|
|
private static void WriteMxLegacyContent(BinaryWriter writer, ushort mxLegacyDataType, int mxLegacyLength, byte[] mxLegacyPayload)
|
|
{
|
|
writer.Write(mxLegacyDataType);
|
|
writer.Write(mxLegacyLength);
|
|
writer.Write(mxLegacyPayload.Length);
|
|
writer.Write(mxLegacyPayload);
|
|
}
|
|
|
|
private static Tuple<ushort, int, byte[]> ReadMxLegacyContent(BinaryReader reader)
|
|
{
|
|
ushort item = reader.ReadUInt16();
|
|
int item2 = reader.ReadInt32();
|
|
int count = reader.ReadInt32();
|
|
return Tuple.Create(item, item2, reader.ReadBytes(count));
|
|
}
|
|
|
|
private static Type ValueTypeToClrType(ValueItemType valueType)
|
|
{
|
|
return valueType switch
|
|
{
|
|
ValueItemType.Boolean => typeof(bool),
|
|
ValueItemType.Byte => typeof(byte),
|
|
ValueItemType.SignedByte => typeof(sbyte),
|
|
ValueItemType.Int => typeof(int),
|
|
ValueItemType.UnsignedInt => typeof(uint),
|
|
ValueItemType.Short => typeof(short),
|
|
ValueItemType.UnsignedShort => typeof(ushort),
|
|
ValueItemType.Long => typeof(long),
|
|
ValueItemType.UnsignedLong => typeof(ulong),
|
|
ValueItemType.Single => typeof(float),
|
|
ValueItemType.Decimal => typeof(decimal),
|
|
ValueItemType.Double => typeof(double),
|
|
ValueItemType.DateTime => typeof(DateTime),
|
|
ValueItemType.Duration => typeof(TimeSpan),
|
|
ValueItemType.Guid => typeof(Guid),
|
|
ValueItemType.String => typeof(string),
|
|
_ => throw new NotSupportedException("Unsupported type of the item"),
|
|
};
|
|
}
|
|
|
|
private static ValueItemType? ClrTypeToValueType(Type type)
|
|
{
|
|
if (type.IsArray)
|
|
{
|
|
type = type.GetElementType();
|
|
}
|
|
switch (Type.GetTypeCode(type))
|
|
{
|
|
case TypeCode.Boolean:
|
|
return ValueItemType.Boolean;
|
|
case TypeCode.Byte:
|
|
return ValueItemType.Byte;
|
|
case TypeCode.SByte:
|
|
return ValueItemType.SignedByte;
|
|
case TypeCode.Int16:
|
|
return ValueItemType.Short;
|
|
case TypeCode.UInt16:
|
|
return ValueItemType.UnsignedShort;
|
|
case TypeCode.Int32:
|
|
return ValueItemType.Int;
|
|
case TypeCode.UInt32:
|
|
return ValueItemType.UnsignedInt;
|
|
case TypeCode.Int64:
|
|
return ValueItemType.Long;
|
|
case TypeCode.UInt64:
|
|
return ValueItemType.UnsignedLong;
|
|
case TypeCode.Single:
|
|
return ValueItemType.Single;
|
|
case TypeCode.Double:
|
|
return ValueItemType.Double;
|
|
case TypeCode.Decimal:
|
|
return ValueItemType.Decimal;
|
|
case TypeCode.DateTime:
|
|
return ValueItemType.DateTime;
|
|
case TypeCode.String:
|
|
return ValueItemType.String;
|
|
case TypeCode.Object:
|
|
if (type == typeof(TimeSpan))
|
|
{
|
|
return ValueItemType.Duration;
|
|
}
|
|
if (type == typeof(Guid))
|
|
{
|
|
return ValueItemType.Guid;
|
|
}
|
|
break;
|
|
}
|
|
return null;
|
|
}
|
|
}
|