From f15d81d63af8ad53aa047a6c05308804c91d5fea Mon Sep 17 00:00:00 2001 From: Michael Becker Date: Sun, 25 Aug 2024 09:46:18 -0400 Subject: [PATCH] add McxMini parser and library functionality (incomplete) --- mocha-common | 2 +- mocha-dotnet.sln | 12 +- .../app/Mocha.ServerApplication/Program.cs | 13 +- mocha-dotnet/src/lib/Mocha.Core/Library.cs | 4 + .../src/lib/Mocha.Core/LibraryAttribute.cs | 22 +++ .../src/lib/Mocha.Core/LibraryInstance.cs | 80 ++++++++ .../src/lib/Mocha.Core/LibraryRelationship.cs | 21 +++ mocha-dotnet/src/lib/Mocha.Core/Oms.cs | 9 +- .../OmsImplementations/MemoryOms.cs | 118 +++++++++++- .../OmsImplementations/Mini/MiniOms.cs | 7 +- .../BinaryReaderExtensions.cs | 55 ++++++ .../EditorMini/InvalidDataFormatException.cs | 8 + .../McxMiniFlags.cs | 13 ++ .../McxMiniPlugin.cs | 173 ++++++++++++++++++ .../Mocha.Plugins.Libraries.McxMini.csproj | 14 ++ 15 files changed, 531 insertions(+), 20 deletions(-) create mode 100644 mocha-dotnet/src/lib/Mocha.Core/LibraryAttribute.cs create mode 100644 mocha-dotnet/src/lib/Mocha.Core/LibraryInstance.cs create mode 100644 mocha-dotnet/src/lib/Mocha.Core/LibraryRelationship.cs create mode 100644 mocha-dotnet/src/plugins/Mocha.Plugins.Libraries.McxMini/BinaryReaderExtensions.cs create mode 100644 mocha-dotnet/src/plugins/Mocha.Plugins.Libraries.McxMini/EditorMini/InvalidDataFormatException.cs create mode 100644 mocha-dotnet/src/plugins/Mocha.Plugins.Libraries.McxMini/McxMiniFlags.cs create mode 100644 mocha-dotnet/src/plugins/Mocha.Plugins.Libraries.McxMini/McxMiniPlugin.cs create mode 100644 mocha-dotnet/src/plugins/Mocha.Plugins.Libraries.McxMini/Mocha.Plugins.Libraries.McxMini.csproj diff --git a/mocha-common b/mocha-common index 9919125..8c13824 160000 --- a/mocha-common +++ b/mocha-common @@ -1 +1 @@ -Subproject commit 9919125a5130e5a5a9412dbed5520e5286703a43 +Subproject commit 8c138247e0ca8104f7cbecbdb54ccd7760d331c3 diff --git a/mocha-dotnet.sln b/mocha-dotnet.sln index 5337d46..22cded5 100644 --- a/mocha-dotnet.sln +++ b/mocha-dotnet.sln @@ -39,7 +39,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mocha.Core.Tests", "mocha-d EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "plugins", "plugins", "{B024FD23-7084-4DDF-A185-D58BEE7A006F}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mocha.Plugins.Libraries.Yaml", "mocha-dotnet\src\plugins\Mocha.Plugins.Libraries.Yaml\Mocha.Plugins.Libraries.Yaml.csproj", "{96400F5A-98ED-48FF-8000-6135BDF10360}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mocha.Plugins.Libraries.McxMini", "mocha-dotnet\src\plugins\Mocha.Plugins.Libraries.McxMini\Mocha.Plugins.Libraries.McxMini.csproj", "{6005DB73-30D8-4398-8B1F-5E23C0F2FFBF}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -67,10 +67,10 @@ Global {20B7D199-322C-4942-85FE-46B90C75E92A}.Debug|Any CPU.Build.0 = Debug|Any CPU {20B7D199-322C-4942-85FE-46B90C75E92A}.Release|Any CPU.ActiveCfg = Release|Any CPU {20B7D199-322C-4942-85FE-46B90C75E92A}.Release|Any CPU.Build.0 = Release|Any CPU - {96400F5A-98ED-48FF-8000-6135BDF10360}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {96400F5A-98ED-48FF-8000-6135BDF10360}.Debug|Any CPU.Build.0 = Debug|Any CPU - {96400F5A-98ED-48FF-8000-6135BDF10360}.Release|Any CPU.ActiveCfg = Release|Any CPU - {96400F5A-98ED-48FF-8000-6135BDF10360}.Release|Any CPU.Build.0 = Release|Any CPU + {6005DB73-30D8-4398-8B1F-5E23C0F2FFBF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6005DB73-30D8-4398-8B1F-5E23C0F2FFBF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6005DB73-30D8-4398-8B1F-5E23C0F2FFBF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6005DB73-30D8-4398-8B1F-5E23C0F2FFBF}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -91,7 +91,7 @@ Global {27C300F5-5172-4225-A6F7-3503B9007DD8} = {2A2B84F1-FA6B-4D67-ACB8-E277D50C98F2} {20B7D199-322C-4942-85FE-46B90C75E92A} = {27C300F5-5172-4225-A6F7-3503B9007DD8} {B024FD23-7084-4DDF-A185-D58BEE7A006F} = {66EB3261-A473-41C7-8D40-E1B4DC8ED4B3} - {96400F5A-98ED-48FF-8000-6135BDF10360} = {B024FD23-7084-4DDF-A185-D58BEE7A006F} + {6005DB73-30D8-4398-8B1F-5E23C0F2FFBF} = {B024FD23-7084-4DDF-A185-D58BEE7A006F} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {D28A9CF8-0235-4F8F-865F-C460BDCAE16D} diff --git a/mocha-dotnet/src/app/Mocha.ServerApplication/Program.cs b/mocha-dotnet/src/app/Mocha.ServerApplication/Program.cs index 45dc67c..07efa03 100644 --- a/mocha-dotnet/src/app/Mocha.ServerApplication/Program.cs +++ b/mocha-dotnet/src/app/Mocha.ServerApplication/Program.cs @@ -19,6 +19,7 @@ using System.Net; using MBS.Core; using MBS.Web; using Mocha.Core; +using Mocha.Core.OmsImplementations; using Mocha.Core.OmsImplementations.Mini; using Mocha.Core.Oop; using Mocha.Core.Oop.Methods; @@ -49,17 +50,23 @@ public class Program : MochaWebApplication protected override Oms CreateOms() { // we can override this here to provide a different default OMS - MiniOms oms = new MiniOms(); - // oms.Libraries.Add("/path/to/Mocha.System.mcl"); + Oms oms = new MemoryOms(); // new MiniOms(); + + string PJPATH = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location); + PJPATH += "/../../../../../../../"; + + // "/home/beckermj/Documents/Projects/mochapowered/mocha-dotnet"; + LibraryHandle l_System = oms.LoadLibrary(PJPATH + "mocha-common/mocha-common/output/net.alcetech.Mocha.System.mcl"); oms.TenantCreated += oms_TenantCreated; oms.Initialize(); - TenantHandle t_super = oms.GetTenantByName("super"); + TenantHandle t_super = oms.CreateTenant("super"); { // FIXME! : selecting tenant doesn't seem to be working, routes get overwritten / not created oms.SelectTenant(t_super); + oms.AddLibraryReference(l_System); InstanceHandle c_Application = oms.GetInstance(KnownInstanceGuids.Classes.Application); InstanceHandle i_Application = oms.CreateInstanceOf(c_Application, new Guid("{773f6c6e-6cef-4a04-b333-ac097c0ab705}")); diff --git a/mocha-dotnet/src/lib/Mocha.Core/Library.cs b/mocha-dotnet/src/lib/Mocha.Core/Library.cs index eb186dd..2b9b457 100644 --- a/mocha-dotnet/src/lib/Mocha.Core/Library.cs +++ b/mocha-dotnet/src/lib/Mocha.Core/Library.cs @@ -10,4 +10,8 @@ public class Library public void Link() { } + + public LibraryInstance.LibraryInstanceCollection Instances { get; } = new LibraryInstance.LibraryInstanceCollection(); + public List Attributes { get; } = new List(); + public List Relationships { get; } = new List(); } diff --git a/mocha-dotnet/src/lib/Mocha.Core/LibraryAttribute.cs b/mocha-dotnet/src/lib/Mocha.Core/LibraryAttribute.cs new file mode 100644 index 0000000..2ba9f3e --- /dev/null +++ b/mocha-dotnet/src/lib/Mocha.Core/LibraryAttribute.cs @@ -0,0 +1,22 @@ +using System; + +namespace Mocha.Core; + +public class LibraryAttribute +{ + public Guid SourceInstanceGuid { get; } + public Guid AttributeInstanceGuid { get; } + public string Value { get; } + + public LibraryAttribute(Guid sourceInstanceGuid, Guid attributeInstanceGuid, string value) + { + SourceInstanceGuid = sourceInstanceGuid; + AttributeInstanceGuid = attributeInstanceGuid; + Value = value; + } + + public override string ToString() + { + return String.Format("A: {0} . {1} = '{2}'", SourceInstanceGuid.ToString("b"), AttributeInstanceGuid.ToString("b"), Value); + } +} diff --git a/mocha-dotnet/src/lib/Mocha.Core/LibraryInstance.cs b/mocha-dotnet/src/lib/Mocha.Core/LibraryInstance.cs new file mode 100644 index 0000000..55096be --- /dev/null +++ b/mocha-dotnet/src/lib/Mocha.Core/LibraryInstance.cs @@ -0,0 +1,80 @@ + + + + +/** + * Copyright (C) 2024 Michael Becker + * + * This file is part of Mocha.NET. + * + * Mocha.NET is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Mocha.NET is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Mocha.NET. If not, see . + */ +namespace Mocha.Core; + +public class LibraryInstance +{ + public class LibraryInstanceCollection : System.Collections.ObjectModel.Collection + { + private Dictionary _itemsByGuid = new Dictionary(); + private Dictionary _itemsByKey = new Dictionary(); + + protected override void ClearItems() + { + base.ClearItems(); + _itemsByGuid.Clear(); + } + protected override void InsertItem(int index, LibraryInstance item) + { + base.InsertItem(index, item); + _itemsByGuid[item.InstanceGuid] = item; + } + protected override void RemoveItem(int index) + { + _itemsByGuid.Remove(this[index].InstanceGuid); + base.RemoveItem(index); + } + + public bool Contains(Guid globalIdentifier) + { + return _itemsByGuid.ContainsKey(globalIdentifier); + } + public LibraryInstance this[Guid globalIdentifier] + { + get + { + if (_itemsByGuid.ContainsKey(globalIdentifier)) + { + return _itemsByGuid[globalIdentifier]; + } + return null; + } + } + } + + public InstanceKey InstanceKey { get; set; } + public Guid InstanceGuid { get; set; } + public Guid ClassGuid { get; set; } + + public LibraryInstance(InstanceKey key, Guid instanceGuid, Guid classGuid) + { + this.InstanceKey = key; + this.InstanceGuid = instanceGuid; + this.ClassGuid = classGuid; + } + + public override string ToString() + { + return String.Format("I: {0} : {1} [{2}]", this.InstanceGuid.ToString("b"), this.ClassGuid.ToString("b"), this.InstanceKey.ToString()); + } +} \ No newline at end of file diff --git a/mocha-dotnet/src/lib/Mocha.Core/LibraryRelationship.cs b/mocha-dotnet/src/lib/Mocha.Core/LibraryRelationship.cs new file mode 100644 index 0000000..d687130 --- /dev/null +++ b/mocha-dotnet/src/lib/Mocha.Core/LibraryRelationship.cs @@ -0,0 +1,21 @@ +using System; + +namespace Mocha.Core; + +public class LibraryRelationship +{ + public Guid SourceInstanceGuid { get; set; } + public Guid RelationshipInstanceGuid { get; set; } + public Guid TargetInstanceGuid { get; set; } + + public LibraryRelationship(Guid sourceInstanceGuid, Guid relationshipInstanceGuid, Guid targetInstanceGuid) + { + this.SourceInstanceGuid = sourceInstanceGuid; + this.RelationshipInstanceGuid = relationshipInstanceGuid; + this.TargetInstanceGuid = targetInstanceGuid; + } + public override string ToString() + { + return String.Format("R: {0} . {1} = {2}", SourceInstanceGuid.ToString("b"), RelationshipInstanceGuid.ToString("b"), TargetInstanceGuid.ToString("b")); + } +} diff --git a/mocha-dotnet/src/lib/Mocha.Core/Oms.cs b/mocha-dotnet/src/lib/Mocha.Core/Oms.cs index c03d02f..f832f1d 100644 --- a/mocha-dotnet/src/lib/Mocha.Core/Oms.cs +++ b/mocha-dotnet/src/lib/Mocha.Core/Oms.cs @@ -356,7 +356,7 @@ public abstract class Oms public bool TryGetAttributeValue(InstanceHandle source, InstanceHandle attribute, DateTime? effectiveDate, out T value) { DateTime dt = DateTime.Now; - if (HasAttributeValueInternal(source, attribute, effectiveDate.GetValueOrDefault(dt))) + // if (HasAttributeValueInternal(source, attribute, effectiveDate.GetValueOrDefault(dt))) { object? val = GetAttributeValueInternal(source, attribute, effectiveDate.GetValueOrDefault(dt)); if (val is T) @@ -400,7 +400,7 @@ public abstract class Oms { string name = GetAttributeValue(attribute, GetInstance(KnownAttributeGuids.Text.Name)); string sourceParentClassName = GetAttributeValue(sourceParentClass, GetInstance(KnownAttributeGuids.Text.Name)); - throw new ArgumentException(String.Format("Undefined attribute `{0}` on class `{1}`", name ?? GetGlobalIdentifier(attribute).ToString("b"), sourceParentClassName)); + throw new ArgumentException(String.Format("Undefined attribute `{0}` on class `{1}`", name ?? GetGlobalIdentifier(attribute).ToString("b"), sourceParentClassName ?? GetGlobalIdentifier(sourceParentClass).ToString("b"))); } if (IsInstanceOf(attribute, a_TextAttribute)) @@ -1074,7 +1074,6 @@ public abstract class Oms // /path/to/directory containing a bunch of XML / JSON / YAML files // .mcz ZIP archive of XML / JSON / YAML files (slowest?) LibraryHandle lh = LibraryHandle.Create(); - return lh; Library lib = new Library(); // _libraries[name] = lh; @@ -1110,10 +1109,14 @@ public abstract class Oms // once all the instances have been loaded, tell the in-memory library manager // to do its second pass "linking" the objects lib.Link(); + + InitializeLibraryInternal(lh, lib); return lh; } + protected abstract void InitializeLibraryInternal(LibraryHandle lh, Library lib); + public string? GetTenantName(TenantHandle tenant) { if (_namesForTenant.ContainsKey(tenant)) diff --git a/mocha-dotnet/src/lib/Mocha.Core/OmsImplementations/MemoryOms.cs b/mocha-dotnet/src/lib/Mocha.Core/OmsImplementations/MemoryOms.cs index 7556384..9c356b1 100644 --- a/mocha-dotnet/src/lib/Mocha.Core/OmsImplementations/MemoryOms.cs +++ b/mocha-dotnet/src/lib/Mocha.Core/OmsImplementations/MemoryOms.cs @@ -15,7 +15,8 @@ // You should have received a copy of the GNU General Public License // along with Mocha.NET. If not, see . - + +using System.ComponentModel; using System.Data; namespace Mocha.Core.OmsImplementations; @@ -35,7 +36,11 @@ public class MemoryOms : Oms public Guid GetGlobalIdentifier(InstanceHandle instance) { - return _guidsByInstance[instance]; + if (_guidsByInstance.ContainsKey(instance)) + { + return _guidsByInstance[instance]; + } + return Guid.Empty; } public InstanceKey GetInstanceKey(InstanceHandle instance) { @@ -195,6 +200,8 @@ public class MemoryOms : Oms private Dictionary _tenantData = new Dictionary(); private _Tenant _CurrentTenantData = null; + private Dictionary _libraries = new Dictionary(); + private TenantHandle _CurrentTenant = TenantHandle.Empty; protected override TenantHandle CreateTenantInternal(string tenantName) @@ -234,7 +241,15 @@ public class MemoryOms : Oms if (_CurrentTenantData == null) throw new InvalidOperationException("Please select a tenant first."); - return _CurrentTenantData.GetGlobalIdentifier(instance); + Guid gid = _CurrentTenantData.GetGlobalIdentifier(instance); + if (gid == Guid.Empty) + { + if (_libraryInst_is.ContainsKey(instance)) + { + gid = _libraryInst_is[instance].GlobalIdentifier; + } + } + return gid; } protected override InstanceKey GetInstanceKeyInternal(InstanceHandle instance) { @@ -271,12 +286,73 @@ public class MemoryOms : Oms return ik; } + protected override void InitializeLibraryInternal(LibraryHandle lh, Library data) + { + _Tenant lib = new _Tenant(); + foreach (LibraryInstance inst in data.Instances) + { + lib.CreateInstance(inst.InstanceGuid); + } + foreach (LibraryAttribute att in data.Attributes) + { + if (lib.TryGetInstance(att.SourceInstanceGuid, out InstanceHandle src) + && lib.TryGetInstance(att.AttributeInstanceGuid, out InstanceHandle dst)) + { + lib.SetAttributeValue(src, dst, att.Value, DateTime.Now); + } + } + foreach (LibraryRelationship rel in data.Relationships) + { + if (lib.TryGetInstance(rel.SourceInstanceGuid, out InstanceHandle src) + && lib.TryGetInstance(rel.RelationshipInstanceGuid, out InstanceHandle dst) + && lib.TryGetInstance(rel.TargetInstanceGuid, out InstanceHandle tgt)) + { + lib.AssignRelationship(src, dst, new InstanceHandle[] { tgt }, DateTime.Now); + } + } + _libraries[lh] = lib; + } + + private struct LibraryInst_g + { + public LibraryHandle SourceLibrary; + public InstanceHandle InstanceHandle; + public Guid GlobalIdentifier; + } + private Dictionary _libraryInst_gs = new Dictionary(); + private Dictionary _libraryInst_is = new Dictionary(); + protected override bool TryGetInstanceInternal(Guid globalIdentifier, out InstanceHandle ih) { if (_CurrentTenantData == null) throw new InvalidOperationException("Please select a tenant first."); - return _CurrentTenantData.TryGetInstance(globalIdentifier, out ih); + bool v = _CurrentTenantData.TryGetInstance(globalIdentifier, out ih); + if (!v) + { + if (_libraryInst_gs.ContainsKey(globalIdentifier)) + { + ih = _libraryInst_gs[globalIdentifier].InstanceHandle; + return true; + } + else + { + foreach (LibraryHandle lh in _libraryReferences[_CurrentTenant]) + { + if (_libraries.ContainsKey(lh)) + { + if (_libraries[lh].TryGetInstance(globalIdentifier, out ih)) + { + LibraryInst_g g = new LibraryInst_g() { InstanceHandle = ih, SourceLibrary = lh, GlobalIdentifier = globalIdentifier }; + _libraryInst_gs[globalIdentifier] = g; + _libraryInst_is[ih] = g; + return true; + } + } + } + } + } + return false; } protected override bool TryGetInstanceInternal(InstanceKey ik, out InstanceHandle ih) { @@ -323,7 +399,22 @@ public class MemoryOms : Oms if (_CurrentTenantData == null) throw new InvalidOperationException("Please select a tenant first."); - return _CurrentTenantData.GetRelatedInstances(source, relationship, effectiveDate); + IReadOnlyCollection insts = _CurrentTenantData.GetRelatedInstances(source, relationship, effectiveDate); + if (insts.Count == 0) + { + foreach (LibraryHandle lh in _libraryReferences[_CurrentTenant]) + { + if (_libraries.ContainsKey(lh)) + { + IReadOnlyCollection insts2 = _libraries[lh].GetRelatedInstances(source, relationship, effectiveDate); + if (insts2.Count > 0) + { + return insts2; + } + } + } + } + return insts; } protected override bool HasAttributeValueInternal(InstanceHandle source, InstanceHandle attribute, DateTime effectiveDate) @@ -338,7 +429,22 @@ public class MemoryOms : Oms if (_CurrentTenantData == null) throw new InvalidOperationException("Please select a tenant first."); - return _CurrentTenantData.GetAttributeValue(source, attribute, effectiveDate); + object value = _CurrentTenantData.GetAttributeValue(source, attribute, effectiveDate); + if (value == null) + { + foreach (LibraryHandle lh in _libraryReferences[_CurrentTenant]) + { + if (_libraries.ContainsKey(lh)) + { + value = _libraries[lh].GetAttributeValue(source, attribute, effectiveDate); + if (value != null) + { + return value; + } + } + } + } + return value; } protected override void SetAttributeValueInternal(InstanceHandle source, InstanceHandle attribute, object value, DateTime effectiveDate) { diff --git a/mocha-dotnet/src/lib/Mocha.Core/OmsImplementations/Mini/MiniOms.cs b/mocha-dotnet/src/lib/Mocha.Core/OmsImplementations/Mini/MiniOms.cs index b3902a3..ca39589 100644 --- a/mocha-dotnet/src/lib/Mocha.Core/OmsImplementations/Mini/MiniOms.cs +++ b/mocha-dotnet/src/lib/Mocha.Core/OmsImplementations/Mini/MiniOms.cs @@ -26,6 +26,11 @@ using MBS.Core; /// public class MiniOms : MemoryOms { + private MiniOms() + { + + } + private TenantHandle t_super; private InstanceHandle c_Class, c_Attribute, c_Relationship, c_TextAttribute, c_BooleanAttribute, c_NumericAttribute, c_DateAttribute, c_WorkSet, c_Instance, c_OMS, c_File; private InstanceHandle c_CommonText, c_CommonBoolean, c_CommonNumeric, c_CommonDate, c_CommonInstanceSet; @@ -37,7 +42,7 @@ public class MiniOms : MemoryOms public List Modules { get; } = new List(); - public MiniOms(MiniOmsModule[] modules = null) + private MiniOms(MiniOmsModule[] modules = null) { if (modules == null) { diff --git a/mocha-dotnet/src/plugins/Mocha.Plugins.Libraries.McxMini/BinaryReaderExtensions.cs b/mocha-dotnet/src/plugins/Mocha.Plugins.Libraries.McxMini/BinaryReaderExtensions.cs new file mode 100644 index 0000000..319df47 --- /dev/null +++ b/mocha-dotnet/src/plugins/Mocha.Plugins.Libraries.McxMini/BinaryReaderExtensions.cs @@ -0,0 +1,55 @@ +using System; +using MBS.Core; + +namespace Mocha.Plugins.Libraries.McxMini; + +public static class BinaryReaderExtensions +{ + public static string ReadFixedString(this System.IO.BinaryReader reader, int length, System.Text.Encoding encoding = null) + { + if (encoding == null) + { + encoding = System.Text.Encoding.UTF8; + } + + byte[] bytes = reader.ReadBytes(length); + return encoding.GetString(bytes); + } + public static Guid ReadGuid(this System.IO.BinaryReader reader) + { + byte[] bytes = reader.ReadBytes(16); + return new Guid(bytes); + } + public static string ReadNullTerminatedString(this System.IO.BinaryReader reader, System.Text.Encoding encoding = null) + { + if (encoding == null) + { + encoding = System.Text.Encoding.UTF8; + } + + List bytes = new List(); + while (true) + { + try + { + byte value = reader.ReadByte(); + if (value == 0) + { + // FIXME: If we use Unicode (UTF-16), end of string is actually two zeroes (\0\0) + break; + } + + bytes.Add(value); + } + catch (EndOfStreamException ex) + { + break; + } + catch (IOException ex) + { + break; + } + } + return encoding.GetString(bytes.ToArray()); + } +} diff --git a/mocha-dotnet/src/plugins/Mocha.Plugins.Libraries.McxMini/EditorMini/InvalidDataFormatException.cs b/mocha-dotnet/src/plugins/Mocha.Plugins.Libraries.McxMini/EditorMini/InvalidDataFormatException.cs new file mode 100644 index 0000000..148c6b9 --- /dev/null +++ b/mocha-dotnet/src/plugins/Mocha.Plugins.Libraries.McxMini/EditorMini/InvalidDataFormatException.cs @@ -0,0 +1,8 @@ +using System; + +namespace Mocha.Plugins.Libraries.McxMini.EditorMini; + +public class InvalidDataFormatException : Exception +{ + +} diff --git a/mocha-dotnet/src/plugins/Mocha.Plugins.Libraries.McxMini/McxMiniFlags.cs b/mocha-dotnet/src/plugins/Mocha.Plugins.Libraries.McxMini/McxMiniFlags.cs new file mode 100644 index 0000000..b2f56cf --- /dev/null +++ b/mocha-dotnet/src/plugins/Mocha.Plugins.Libraries.McxMini/McxMiniFlags.cs @@ -0,0 +1,13 @@ +using System; + +namespace Mocha.Plugins.Libraries.McxMini; + +[Flags] +public enum McxMiniFlags +{ + None = 0x00, + HasSections = 0x01, + HasNames = 0x02, + HasCounts = 0x04, + Debug = 0x08 +} diff --git a/mocha-dotnet/src/plugins/Mocha.Plugins.Libraries.McxMini/McxMiniPlugin.cs b/mocha-dotnet/src/plugins/Mocha.Plugins.Libraries.McxMini/McxMiniPlugin.cs new file mode 100644 index 0000000..788b672 --- /dev/null +++ b/mocha-dotnet/src/plugins/Mocha.Plugins.Libraries.McxMini/McxMiniPlugin.cs @@ -0,0 +1,173 @@ +namespace Mocha.Plugins.Libraries.McxMini; + +using System.Runtime.InteropServices; +using MBS.Core; +using Mocha.Core; +using Mocha.Plugins.Libraries.McxMini.EditorMini; + +public class McxMiniLibraryPlugin : LibraryPlugin +{ + private struct McxInstance + { + public Guid ClassGuid; + public Guid InstanceGuid; + public int ClassIndex; + public int InstanceIndex; + } + private struct McxAttribute + { + public Guid SourceInstanceGuid; + public Guid AttributeInstanceGuid; + public int ValueIndex; + } + private struct McxSection + { + public string Name; + public int Offset; + public int Length; + public int Count; + } + + protected override bool SupportsFileNameInternal(string filename) + { + string ext = System.IO.Path.GetExtension(filename); + return ext.Equals(".mcx") || ext.Equals(".mcl"); + } + + protected override void LoadInternal(string filename, Library library) + { + FileStream fs = File.Open(filename, FileMode.Open); + BinaryReader r = new BinaryReader(fs); + + string signature = r.ReadFixedString(4); + if (!signature.Equals("MCX!")) + { + throw new InvalidDataFormatException(); + } + + float version = r.ReadSingle(); + if (version != 2.0f) + { + Console.Error.WriteLine(String.Format("MCX format version {0} unsupported", version)); + } + + McxMiniFlags flags = (McxMiniFlags)r.ReadInt32(); + + int sectionsCount = r.ReadInt32(); + + Dictionary sections = new Dictionary(); + for (int i = 0; i < sectionsCount; i++) + { + McxSection section = new McxSection(); + if ((flags & McxMiniFlags.HasNames) == McxMiniFlags.HasNames) + { + section.Name = r.ReadFixedString(16).TrimNull(); + } + else + { + section.Name = i.ToString(); + } + + section.Offset = r.ReadInt32(); + section.Length = r.ReadInt32(); + if ((flags & McxMiniFlags.HasCounts) == McxMiniFlags.HasCounts) + { + section.Count = r.ReadInt32(); + } + if (((flags & McxMiniFlags.HasNames) == McxMiniFlags.HasNames) + && ((flags & McxMiniFlags.HasCounts) == McxMiniFlags.HasCounts)) + { + int reserved = r.ReadInt32(); // padding + } + sections[section.Name] = section; + } + + if (sections.Count < 5) + { + throw new InvalidDataFormatException(); + } + + McxSection guidsSection = sections["GUIDTable"]; + McxSection instancesSection = sections["Instances"]; + McxSection attributesSection = sections["Attributes"]; + McxSection relationshipsSection = sections["Relationships"]; + McxSection stringTableSection = sections["StringTable"]; + McxSection resourcesSection = new McxSection(); + if (sections.ContainsKey("Resources")) + { + resourcesSection = sections["Resources"]; + } + + if (guidsSection.Count != instancesSection.Count) + { + Console.Error.WriteLine("guid count not equal to instance count?"); + } + + r.BaseStream.Seek(guidsSection.Offset, SeekOrigin.Begin); + List guids = new List(); + for (int i = 0; i < guidsSection.Count; i++) + { + Guid guid = r.ReadGuid(); + guids.Add(guid); + } + + r.BaseStream.Seek(instancesSection.Offset, SeekOrigin.Begin); + // McxInstance[] instances = new McxInstance[instancesCount]; + for (int i = 0; i < instancesSection.Count; i++) + { + int classGuidIndex = r.ReadInt32(); + int instanceGuidIndex = r.ReadInt32(); + int classIndex = r.ReadInt32(); + int instanceIndex = r.ReadInt32(); + + Guid classGuid = guids[classGuidIndex]; + Guid instanceGuid = guids[instanceGuidIndex]; + + InstanceKey key = new InstanceKey(classIndex, instanceIndex); + library.Instances.Add(new LibraryInstance(key, instanceGuid, classGuid)); + } + + r.BaseStream.Seek(attributesSection.Offset, SeekOrigin.Begin); + McxAttribute[] attributes = new McxAttribute[attributesSection.Count]; + for (int i = 0; i < attributesSection.Count; i++) + { + int sourceInstanceIndex = r.ReadInt32(); + int attributeInstanceIndex = r.ReadInt32(); + + attributes[i].SourceInstanceGuid = guids[sourceInstanceIndex]; + attributes[i].AttributeInstanceGuid = guids[attributeInstanceIndex]; + attributes[i].ValueIndex = r.ReadInt32(); + } + + r.BaseStream.Seek(relationshipsSection.Offset, SeekOrigin.Begin); + for (int i = 0; i < relationshipsSection.Count; i++) + { + int sourceInstanceIndex = r.ReadInt32(); + int relationshipInstanceIndex = r.ReadInt32(); + int targetInstanceIndex = r.ReadInt32(); + + Guid sourceInstanceGuid = guids[sourceInstanceIndex]; + Guid relationshipInstanceGuid = guids[relationshipInstanceIndex]; + Guid targetInstanceGuid = guids[targetInstanceIndex]; + + library.Relationships.Add(new LibraryRelationship(sourceInstanceGuid, relationshipInstanceGuid, targetInstanceGuid)); + } + + r.BaseStream.Seek(stringTableSection.Offset, SeekOrigin.Begin); + string[] stringTable = new string[stringTableSection.Count]; + for (int i = 0; i < stringTableSection.Count; i++) + { + stringTable[i] = r.ReadNullTerminatedString(); + } + + for (int i = 0; i < attributesSection.Count; i++) + { + library.Attributes.Add(new LibraryAttribute(attributes[i].SourceInstanceGuid, attributes[i].AttributeInstanceGuid, stringTable[attributes[i].ValueIndex])); + } + + if (resourcesSection.Length > 0) + { + + } + } +} diff --git a/mocha-dotnet/src/plugins/Mocha.Plugins.Libraries.McxMini/Mocha.Plugins.Libraries.McxMini.csproj b/mocha-dotnet/src/plugins/Mocha.Plugins.Libraries.McxMini/Mocha.Plugins.Libraries.McxMini.csproj new file mode 100644 index 0000000..516abf7 --- /dev/null +++ b/mocha-dotnet/src/plugins/Mocha.Plugins.Libraries.McxMini/Mocha.Plugins.Libraries.McxMini.csproj @@ -0,0 +1,14 @@ + + + + + + + + + net8.0 + enable + enable + + +