diff --git a/CSharp/Plugins/UniversalEditor.Plugins.Microsoft.Merlin/DataFormats/Multimedia/FileSystem/Microsoft/Merlin/Mfc/CMerlinObject.cs b/CSharp/Plugins/UniversalEditor.Plugins.Microsoft.Merlin/DataFormats/Multimedia/FileSystem/Microsoft/Merlin/Mfc/CMerlinObject.cs new file mode 100644 index 00000000..6e223e69 --- /dev/null +++ b/CSharp/Plugins/UniversalEditor.Plugins.Microsoft.Merlin/DataFormats/Multimedia/FileSystem/Microsoft/Merlin/Mfc/CMerlinObject.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using UniversalEditor.IO; + +namespace UniversalEditor.DataFormats.Multimedia.FileSystem.Microsoft.Merlin.Mfc +{ + public class CMerlinObject : MfcObject + { + public string Name { get; set; } + + public override void LoadObject(Reader reader) + { + Name = reader.ReadMfcString(); + reader.ReadMfcObjectWithoutHeader(); + } + + public override void SaveObject(Writer writer) + { + // writer.WriteMfcString(Name); + // writer.WriteMfcObjectWithoutHeader(new TrailingBytes()); + } + } +} diff --git a/CSharp/Plugins/UniversalEditor.Plugins.Microsoft.Merlin/DataFormats/Multimedia/FileSystem/Microsoft/Merlin/Mfc/CMerlinTexture.cs b/CSharp/Plugins/UniversalEditor.Plugins.Microsoft.Merlin/DataFormats/Multimedia/FileSystem/Microsoft/Merlin/Mfc/CMerlinTexture.cs new file mode 100644 index 00000000..63f1f594 --- /dev/null +++ b/CSharp/Plugins/UniversalEditor.Plugins.Microsoft.Merlin/DataFormats/Multimedia/FileSystem/Microsoft/Merlin/Mfc/CMerlinTexture.cs @@ -0,0 +1,90 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Text; + +namespace UniversalEditor.DataFormats.Multimedia.FileSystem.Microsoft.Merlin.Mfc +{ + public class MipMap + { + public Size ImageDimensionsMinusOne { get; set; } + public Size ImageDimensions { get; set; } + public uint Level { get; set; } + public byte[] ImageData { get; set; } + + public List> PixelSpans { get; set; } + } + public struct PixelSpan + { + public ushort StartIndex; + public ushort EndIndex; + + public PixelSpan(ushort startIndex, ushort endIndex) + { + StartIndex = startIndex; + EndIndex = endIndex; + } + }; + + [MfcSerialisable("CMerlinTexture")] + internal class CMerlinTexture : CMerlinObject + { + public bool HasTransparency { get; private set; } + public List Mipmaps { get; private set; } + + public override void LoadObject(IO.Reader reader) + { + base.LoadObject(reader); + + var flags = reader.ReadUInt16(); + this.HasTransparency = (flags & 1) != 0; + if ((flags & ~1) != 0) + { + throw new NotImplementedException("Unexpected flag set in texture header"); + } + + ushort mipmapCount = reader.ReadUInt16(); + this.Mipmaps = new List(mipmapCount); + for (int i = 0; i < mipmapCount; i++) + { + MipMap mipmap = new MipMap(); + var nextLargestHeight = reader.ReadUInt16(); + var imageHeight = reader.ReadUInt16(); + var nextLargestWidth = reader.ReadUInt16(); + var imageWidth = reader.ReadUInt16(); + + mipmap.ImageDimensionsMinusOne = new Size(imageWidth, imageHeight); + mipmap.ImageDimensions = new Size(nextLargestWidth, nextLargestHeight); + mipmap.Level = reader.ReadUInt16(); + + var imageDataLength = reader.ReadUInt32(); + mipmap.ImageData = reader.ReadBytes((int)imageDataLength); + + int totalSpanCount = (int)reader.ReadUInt32(); + mipmap.PixelSpans = new List>(totalSpanCount); + for (int y = 0; y < nextLargestHeight; y++) + { + ushort rowSpanCount = reader.ReadUInt16(); + var rowSpans = new List(rowSpanCount); + for (int k = 0; k < rowSpanCount; k++) + { + ushort startIndex = reader.ReadUInt16(); + ushort endIndex = reader.ReadUInt16(); + rowSpans.Add(new PixelSpan(startIndex, endIndex)); + } + mipmap.PixelSpans.Add(rowSpans); + } + + reader.ReadMfcObjectWithoutHeader(); + + this.Mipmaps.Add(mipmap); + } + } + + public override void SaveObject(IO.Writer reader) + { + throw new NotImplementedException(); + } + } +} diff --git a/CSharp/Plugins/UniversalEditor.Plugins.Microsoft.Merlin/DataFormats/Multimedia/FileSystem/Microsoft/Merlin/Mfc/TrailingBytes.cs b/CSharp/Plugins/UniversalEditor.Plugins.Microsoft.Merlin/DataFormats/Multimedia/FileSystem/Microsoft/Merlin/Mfc/TrailingBytes.cs new file mode 100644 index 00000000..e973ee3d --- /dev/null +++ b/CSharp/Plugins/UniversalEditor.Plugins.Microsoft.Merlin/DataFormats/Multimedia/FileSystem/Microsoft/Merlin/Mfc/TrailingBytes.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace UniversalEditor.DataFormats.Multimedia.FileSystem.Microsoft.Merlin.Mfc +{ + [MfcSerialisable] + internal class TrailingBytes : MfcObject + { + private byte[] trailingBytes = new byte[0]; + + public override void LoadObject(IO.Reader reader) + { + ushort byteCount = reader.ReadUInt16(); + trailingBytes = reader.ReadBytes(byteCount); + } + public override void SaveObject(IO.Writer writer) + { + writer.WriteUInt16(0); + } + } +} diff --git a/CSharp/Plugins/UniversalEditor.Plugins.Microsoft.Merlin/DataFormats/Multimedia/FileSystem/Microsoft/Merlin/TEXDataFormat.cs b/CSharp/Plugins/UniversalEditor.Plugins.Microsoft.Merlin/DataFormats/Multimedia/FileSystem/Microsoft/Merlin/TEXDataFormat.cs new file mode 100644 index 00000000..196a53d9 --- /dev/null +++ b/CSharp/Plugins/UniversalEditor.Plugins.Microsoft.Merlin/DataFormats/Multimedia/FileSystem/Microsoft/Merlin/TEXDataFormat.cs @@ -0,0 +1,130 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using UniversalEditor.Accessors; +using UniversalEditor.DataFormats.Multimedia.FileSystem.Microsoft.Merlin.Mfc; +using UniversalEditor.IO; +using UniversalEditor.ObjectModels.FileSystem; +using UniversalEditor.ObjectModels.Multimedia.Palette; + +namespace UniversalEditor.DataFormats.Multimedia.FileSystem.Microsoft.Merlin +{ + /// + /// Represents a Microsoft Merlin Game Engine (Hover!) .TEX file, + /// containing a shared palette and up to 65535 textures. + /// + public class TEXDataFormat : DataFormat + { + private static DataFormatReference _dfr = null; + public override DataFormatReference MakeReference() + { + if (_dfr == null) + { + _dfr = base.MakeReference(); + _dfr.Capabilities.Add(typeof(FileSystemObjectModel), DataFormatCapabilities.All); + _dfr.Filters.Add("Microsoft Merlin Game Engine (Hover!) texture pack", new string[] { "*.tex" }); + } + return _dfr; + } + + private int mvarPaletteEntryCount = 256; + private PaletteObjectModel mvarPalette = new PaletteObjectModel(); + + protected override void LoadInternal(ref ObjectModel objectModel) + { + FileSystemObjectModel fsom = (objectModel as FileSystemObjectModel); + if (fsom == null) throw new ObjectModelNotSupportedException(); + + Reader reader = base.Accessor.Reader; + reader.Accessor.Position = 0; + fsom.Clear(); + mvarPalette.Clear(); + + for (int i = 0; i < mvarPaletteEntryCount; i++) + { + byte r = reader.ReadByte(); + byte g = reader.ReadByte(); + byte b = reader.ReadByte(); + byte a = reader.ReadByte(); + mvarPalette.Entries.Add(Color.FromRGBA(r, g, b, a)); + } + + MemoryAccessor ma = new MemoryAccessor(); + Document.Save(mvarPalette, new DataFormats.Multimedia.Palette.Adobe.ACODataFormat(), ma, true); + + fsom.Files.Add("PALETTE.ACO", ma.ToArray()); + + // Read a List + List list = reader.ReadMfcList(); + for (ushort i = 0; i < list.Count; i++) + { + /* + ushort tag = reader.ReadUInt16(); + uint objectTag = ((uint)(tag & ClassTag) << 16) | (uint)(tag & ~ClassTag); + if (tag == BigObjectTag) + { + objectTag = reader.ReadUInt32(); + } + + if (tag == NewClassTag) + { + // Not a class we've seen before; read it in + ushort schemaVersion = reader.ReadUInt16(); + string className = reader.ReadUInt16String(); + } + + Folder textureI = new Folder(); + + string name = ReadMFCString(reader); + textureI.Name = name; + + TEXTextureFlags flags = (TEXTextureFlags)reader.ReadUInt16(); + ushort mipmapCount = reader.ReadUInt16(); + for (ushort j = 0; j < mipmapCount; j++) + { + // TextureMipmap mipmap = new TextureMipmap(); + var nextLargestHeight = reader.ReadUInt16(); + var imageHeight = reader.ReadUInt16(); + var nextLargestWidth = reader.ReadUInt16(); + var imageWidth = reader.ReadUInt16(); + + // mipmap.ImageDimensionsMinusOne = new Size(imageWidth, imageHeight); + // mipmap.ImageDimensions = new Size(nextLargestWidth, nextLargestHeight); + // mipmap.Level = archive.DeserialiseUInt16(); + ushort mipmapLevel = reader.ReadUInt16(); + + var imageDataLength = reader.ReadUInt32(); + byte[] ImageData = reader.ReadBytes((int)imageDataLength); + + textureI.Files.Add("MIPMAP" + j.ToString() + ".BMP", ImageData); + + int totalSpanCount = (int)reader.ReadUInt32(); + // mipmap.PixelSpans = new List>(totalSpanCount); + for (int y = 0; y < nextLargestHeight; y++) + { + ushort rowSpanCount = reader.ReadUInt16(); + // var rowSpans = new List(rowSpanCount); + for (int k = 0; k < rowSpanCount; k++) + { + ushort startIndex = reader.ReadUInt16(); + ushort endIndex = reader.ReadUInt16(); + // rowSpans.Add(new PixelSpan(startIndex, endIndex)); + } + // mipmap.PixelSpans.Add(rowSpans); + } + + ushort trailingByteCount = reader.ReadUInt16(); + byte[] trailingBytes = reader.ReadBytes(trailingByteCount); + } + fsom.Folders.Add(textureI); + */ + } + } + + protected override void SaveInternal(ObjectModel objectModel) + { + throw new NotImplementedException(); + } + } +} diff --git a/CSharp/Plugins/UniversalEditor.Plugins.Microsoft.Merlin/DataFormats/Multimedia/FileSystem/Microsoft/Merlin/TEXTextureFlags.cs b/CSharp/Plugins/UniversalEditor.Plugins.Microsoft.Merlin/DataFormats/Multimedia/FileSystem/Microsoft/Merlin/TEXTextureFlags.cs new file mode 100644 index 00000000..6853a9d3 --- /dev/null +++ b/CSharp/Plugins/UniversalEditor.Plugins.Microsoft.Merlin/DataFormats/Multimedia/FileSystem/Microsoft/Merlin/TEXTextureFlags.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace UniversalEditor.DataFormats.Multimedia.FileSystem.Microsoft.Merlin +{ + [Flags()] + public enum TEXTextureFlags : ushort + { + None = 0, + HasTransparency = 1 + } +} diff --git a/CSharp/Plugins/UniversalEditor.Plugins.Microsoft.Merlin/IMfcSerializable.cs b/CSharp/Plugins/UniversalEditor.Plugins.Microsoft.Merlin/IMfcSerializable.cs new file mode 100644 index 00000000..3161b35e --- /dev/null +++ b/CSharp/Plugins/UniversalEditor.Plugins.Microsoft.Merlin/IMfcSerializable.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using UniversalEditor.IO; + +namespace UniversalEditor +{ + public interface IMfcSerialisable where T : IMfcSerialisable + { + void LoadObject(Reader reader); + void SaveObject(Writer writer); + } +} diff --git a/CSharp/Plugins/UniversalEditor.Plugins.Microsoft.Merlin/MfcClass.cs b/CSharp/Plugins/UniversalEditor.Plugins.Microsoft.Merlin/MfcClass.cs new file mode 100644 index 00000000..75d24c37 --- /dev/null +++ b/CSharp/Plugins/UniversalEditor.Plugins.Microsoft.Merlin/MfcClass.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace UniversalEditor +{ + public class MfcClass + { + public string Name { get; set; } + public int SchemaVersion { get; set; } + public Type RealType { get; set; } + + internal T CreateNewObject() where T : IMfcSerialisable + { + if (RealType.IsAssignableFrom(typeof(T))) + { + throw new ArgumentException("Cannot assign " + RealType.Name + " from " + typeof(T).Name); + } + return (T)RealType.Assembly.CreateInstance(RealType.FullName); + } + } +} diff --git a/CSharp/Plugins/UniversalEditor.Plugins.Microsoft.Merlin/MfcClassRegistry.cs b/CSharp/Plugins/UniversalEditor.Plugins.Microsoft.Merlin/MfcClassRegistry.cs new file mode 100644 index 00000000..3b5130b1 --- /dev/null +++ b/CSharp/Plugins/UniversalEditor.Plugins.Microsoft.Merlin/MfcClassRegistry.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; + +namespace UniversalEditor +{ + public class MfcClassRegistry + { + private Dictionary _registry; + + public MfcClassRegistry() + { + this._registry = new Dictionary(); + } + + /// + /// Registers a single serialised class name. + /// + /// The serialised class name. + /// The .Net type capable of deserialising the class data. + public void RegisterClass(string mfcName, Type realType) + { + _registry.Add(mfcName, new MfcClass + { + Name = mfcName, + SchemaVersion = 1, + RealType = realType + }); + } + + /// + /// Registers all exported classes from the specified assembly + /// that are marked with the MfcSerialisable attribute. + /// + public void AutoRegisterClasses(Assembly assembly) + { + foreach (var type in assembly.GetExportedTypes()) + { + var markers = type.GetCustomAttributes(typeof(MfcSerialisableAttribute), false).Cast(); + foreach (var marker in markers) + { + RegisterClass(marker.SerialisedName, type); + } + } + } + + public MfcClass GetMfcClass(string mfcName) + { + return _registry[mfcName]; + } + + public MfcClass GetMfcClass(Type realType) + { + var x = from c in _registry.Values where c.RealType == realType select c; + return x.Single(); + } + } +} diff --git a/CSharp/Plugins/UniversalEditor.Plugins.Microsoft.Merlin/MfcObject.cs b/CSharp/Plugins/UniversalEditor.Plugins.Microsoft.Merlin/MfcObject.cs new file mode 100644 index 00000000..3bf961ea --- /dev/null +++ b/CSharp/Plugins/UniversalEditor.Plugins.Microsoft.Merlin/MfcObject.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using UniversalEditor.IO; + +namespace UniversalEditor +{ + public abstract class MfcObject : IMfcSerialisable + { + public abstract void LoadObject(Reader reader); + public abstract void SaveObject(Writer reader); + } +} diff --git a/CSharp/Plugins/UniversalEditor.Plugins.Microsoft.Merlin/MfcSerializableAttribute.cs b/CSharp/Plugins/UniversalEditor.Plugins.Microsoft.Merlin/MfcSerializableAttribute.cs new file mode 100644 index 00000000..edac4869 --- /dev/null +++ b/CSharp/Plugins/UniversalEditor.Plugins.Microsoft.Merlin/MfcSerializableAttribute.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace UniversalEditor +{ + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)] + class MfcSerialisableAttribute : Attribute + { + private string _serialisedName; + + /// + /// Marks a type as serialisable with an explicit name. + /// + public MfcSerialisableAttribute(string serialisedName) + { + this._serialisedName = serialisedName; + } + + /// + /// Marks a type as serialisable but with no name. + /// Such types cannot be automatically deserialised; + /// you must explicitly name the type for each serialise call. + /// + public MfcSerialisableAttribute() + { + _serialisedName = string.Format("Anonymous_{0}", Guid.NewGuid().ToString()); + } + + public string SerialisedName + { + get + { + return _serialisedName; + } + } + } +} diff --git a/CSharp/Plugins/UniversalEditor.Plugins.Microsoft.Merlin/Properties/AssemblyInfo.cs b/CSharp/Plugins/UniversalEditor.Plugins.Microsoft.Merlin/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..b467225f --- /dev/null +++ b/CSharp/Plugins/UniversalEditor.Plugins.Microsoft.Merlin/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("UniversalEditor.Plugins.Microsoft.Merlin")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("City of Orlando")] +[assembly: AssemblyProduct("UniversalEditor.Plugins.Microsoft.Merlin")] +[assembly: AssemblyCopyright("Copyright © City of Orlando 2014")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("559ea7e5-9dcf-42c4-a7be-82af087449ae")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/CSharp/Plugins/UniversalEditor.Plugins.Microsoft.Merlin/ReaderExtensions.cs b/CSharp/Plugins/UniversalEditor.Plugins.Microsoft.Merlin/ReaderExtensions.cs new file mode 100644 index 00000000..1ab7d627 --- /dev/null +++ b/CSharp/Plugins/UniversalEditor.Plugins.Microsoft.Merlin/ReaderExtensions.cs @@ -0,0 +1,193 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using UniversalEditor.IO; + +namespace UniversalEditor +{ + public static class ReaderExtensions + { + const UInt16 NullTag = 0; + const UInt16 NewClassTag = 0xffff; + const UInt16 ClassTag = 0x8000; + const UInt32 BigClassTag = 0x80000000; + const UInt16 BigObjectTag = 0x7fff; + const UInt32 MaxMapCountTag = 0x3ffffffe; + + private static MfcClassRegistry _classRegistry; + private static List _loadedClasses; + private static List _loadedObjects; + + static ReaderExtensions() + { + _classRegistry = new MfcClassRegistry(); + _loadedClasses = new List(); + _loadedObjects = new List(); + + // Class index zero isn't used/represents an error + _loadedClasses.Add(null); + + // Object index zero isn't used/represents a null pointer + _loadedObjects.Add(null); + + _classRegistry.AutoRegisterClasses(typeof(ReaderExtensions).Assembly); + } + + public static MfcClass ReadNewMfcClass(this Reader reader) + { + ushort schemaVersion = reader.ReadUInt16(); + string className = reader.ReadUInt16String(); + + MfcClass mfcClass = _classRegistry.GetMfcClass(className); + + if (mfcClass == null) + { + throw new System.IO.InvalidDataException("No registered class for MfcObject " + className); + } + if (mfcClass.SchemaVersion != schemaVersion) + { + throw new System.IO.InvalidDataException("Schema mismatch: file = " + schemaVersion + ", registered = " + mfcClass.SchemaVersion); + } + + return mfcClass; + } + + public static string ReadMfcString(this Reader reader) + { + int stringLength = 0; + byte bLength = reader.ReadByte(); + if (bLength < 0xff) + { + stringLength = bLength; + } + else + { + ushort wLength = reader.ReadUInt16(); + if (wLength < 0xfffe) + { + stringLength = wLength; + } + else + { + if (wLength == 0xfffe) + { + // Unicode string prefix -- not currently handled. See + // CArchive::operator>>(CArchive& ar, CString& string) + // for details on how to implement this when needed. + } + stringLength = reader.ReadInt32(); + } + } + + return reader.ReadFixedLengthString(stringLength); + } + + /// + /// Tries to read in and return the MfcClass that is next in the stream. + /// If the next object has already been loaded then null is returned and + /// alreadyLoadedTag is set to the object ID of the object. + /// + public static MfcClass ReadMfcClass(this Reader reader, out uint alreadyLoadedTag) + { + ushort tag = reader.ReadUInt16(); + uint objectTag = ((uint)(tag & ClassTag) << 16) | (uint)(tag & ~ClassTag); + if (tag == BigObjectTag) + { + objectTag = reader.ReadUInt32(); + } + + // If it is an object tag and not a class tag then bail out -- caller will handle it + alreadyLoadedTag = 0; + if ((objectTag & BigClassTag) == 0) + { + alreadyLoadedTag = objectTag; + return null; + } + + MfcClass mfcClass; + if (tag == NewClassTag) + { + // Not a class we've seen before; read it in + mfcClass = reader.ReadNewMfcClass(); + _loadedClasses.Add(mfcClass); + return mfcClass; + } + + // A class we've seen before, look it up by index + uint classIndex = objectTag & ~BigClassTag; + if (classIndex == 0) + { + throw new System.IO.InvalidDataException("Got a invalid class index: 0"); + } + if (classIndex > _loadedClasses.Count) + { + throw new System.IO.InvalidDataException("Got a class index larger than the currently loaded count: " + classIndex); + } + + return _loadedClasses[(int)classIndex]; + } + + public static T ReadMfcObject(this Reader reader) where T : MfcObject + { + uint objectTag; + MfcClass mfcClass = reader.ReadMfcClass(out objectTag); + + if (mfcClass == null) + { + // An object we've already loaded + if (objectTag > _loadedObjects.Count) + { + throw new System.IO.InvalidDataException("Got an object tag larger than the count of loaded objects: " + objectTag); + } + + return (T)_loadedObjects[(int)objectTag]; + } + + // An object we haven't yet loaded. Create a new instance and deserialise it. + // Make sure to add it to the list of loaded objects before deserialising in + // case it has (possibly indirect) references to itself. + return reader.ReadNewMfcObject(mfcClass); + } + public static T ReadNewMfcObject(this Reader reader, MfcClass mfcClass) where T : MfcObject + { + MfcObject newObject = mfcClass.CreateNewObject(); + _loadedObjects.Add(newObject); + newObject.LoadObject(reader); + return (T)newObject; + } + + /// + /// Deserialises an object of type T without reading in the header. + /// This implies it must be a new object and not one that has already + /// been loaded since there is no object tag to reference the loaded list. + /// + public static T ReadMfcObjectWithoutHeader(this Reader reader) where T : MfcObject + { + MfcClass mfcClass = _classRegistry.GetMfcClass(typeof(T)); + return reader.ReadNewMfcObject(mfcClass); + } + + public static List ReadMfcList(this Reader reader) where T : MfcObject + { + List result = new List(); + ushort listLength = reader.ReadUInt16(); + + if (listLength >= 1) + { + // First object has a valid runtime class header + result.Add(reader.ReadMfcObject()); + } + for (int i = 1; i < listLength; i++) + { + // Subsequent objects are missing the runtime class header but have + // a UInt16 preceding them that looks like an invalid runtime class + // header. + uint unknown1 = reader.ReadUInt16(); + result.Add(reader.ReadMfcObjectWithoutHeader()); + } + + return result; + } + } +} diff --git a/CSharp/Plugins/UniversalEditor.Plugins.Microsoft.Merlin/UniversalEditor.Plugins.Microsoft.Merlin.csproj b/CSharp/Plugins/UniversalEditor.Plugins.Microsoft.Merlin/UniversalEditor.Plugins.Microsoft.Merlin.csproj new file mode 100644 index 00000000..bfe81e78 --- /dev/null +++ b/CSharp/Plugins/UniversalEditor.Plugins.Microsoft.Merlin/UniversalEditor.Plugins.Microsoft.Merlin.csproj @@ -0,0 +1,73 @@ + + + + + Debug + AnyCPU + {9F1FDC26-5F1C-4C2A-BBBF-3A597A72802D} + Library + Properties + UniversalEditor + UniversalEditor.Plugins.Microsoft.Merlin + v3.5 + 512 + + + true + full + false + ..\..\Output\Debug\Plugins\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + ..\..\Output\Release\Plugins\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + + {2d4737e6-6d95-408a-90db-8dff38147e85} + UniversalEditor.Core + + + {30467e5c-05bc-4856-aadc-13906ef4cadd} + UniversalEditor.Essential + + + {be4d0ba3-0888-42a5-9c09-fc308a4509d2} + UniversalEditor.Plugins.Multimedia + + + + + \ No newline at end of file