diff --git a/UndertaleModLib/Models/UndertaleEmbeddedImage.cs b/UndertaleModLib/Models/UndertaleEmbeddedImage.cs new file mode 100644 index 000000000..add02aa8e --- /dev/null +++ b/UndertaleModLib/Models/UndertaleEmbeddedImage.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace UndertaleModLib.Models +{ + // Not to be confused with the other "embedded" resources, this is a bit separate. + // GMS2 only, see https://github.com/krzys-h/UndertaleModTool/issues/4#issuecomment-421844420 for rough structure, but doesn't appear commonly used + public class UndertaleEmbeddedImage : UndertaleObject + { + public UndertaleString Name; + public UndertaleTexturePageItem TextureEntry; + + public UndertaleEmbeddedImage() + { + } + + public void Serialize(UndertaleWriter writer) + { + writer.WriteUndertaleString(Name); + writer.WriteUndertaleObjectPointer(TextureEntry); + } + + public void Unserialize(UndertaleReader reader) + { + Name = reader.ReadUndertaleString(); + TextureEntry = reader.ReadUndertaleObjectPointer(); + } + } +} diff --git a/UndertaleModLib/Models/UndertaleEmbeddedTexture.cs b/UndertaleModLib/Models/UndertaleEmbeddedTexture.cs index b0bd59cf7..22066d590 100644 --- a/UndertaleModLib/Models/UndertaleEmbeddedTexture.cs +++ b/UndertaleModLib/Models/UndertaleEmbeddedTexture.cs @@ -10,29 +10,29 @@ namespace UndertaleModLib.Models { public class UndertaleEmbeddedTexture : UndertaleResource, INotifyPropertyChanged { - private uint _GMS2Unknown; - private uint _UnknownAlwaysZero = 0; + private uint _GeneratedMips; + private uint _Scaled = 0; private TexData _TextureData = new TexData(); - public uint GMS2Unknown { get => _GMS2Unknown; set { _GMS2Unknown = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("GMS2Unknown")); } } - public uint UnknownAlwaysZero { get => _UnknownAlwaysZero; set { _UnknownAlwaysZero = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("UnknownAlwaysZero")); } } + public uint Scaled { get => _Scaled; set { _Scaled = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Scaled")); } } + public uint GeneratedMips { get => _GeneratedMips; set { _GeneratedMips = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("GeneratedMips")); } } public TexData TextureData { get => _TextureData; set { _TextureData = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("TextureData")); } } public event PropertyChangedEventHandler PropertyChanged; public void Serialize(UndertaleWriter writer) { + writer.Write(Scaled); if (writer.undertaleData.GeneralInfo.Major >= 2) - writer.Write(GMS2Unknown); - writer.Write(UnknownAlwaysZero); + writer.Write(GeneratedMips); writer.WriteUndertaleObjectPointer(TextureData); } public void Unserialize(UndertaleReader reader) { + Scaled = reader.ReadUInt32(); if (reader.undertaleData.GeneralInfo.Major >= 2) - GMS2Unknown = reader.ReadUInt32(); - UnknownAlwaysZero = reader.ReadUInt32(); + GeneratedMips = reader.ReadUInt32(); TextureData = reader.ReadUndertaleObjectPointer(); } diff --git a/UndertaleModLib/Models/UndertaleFont.cs b/UndertaleModLib/Models/UndertaleFont.cs index a2cedc6b2..f80a245f2 100644 --- a/UndertaleModLib/Models/UndertaleFont.cs +++ b/UndertaleModLib/Models/UndertaleFont.cs @@ -21,6 +21,7 @@ public class UndertaleFont : UndertaleNamedResource, INotifyPropertyChanged private UndertaleTexturePageItem _Texture; private float _ScaleX; private float _ScaleY; + private int _AscenderOffset; public UndertaleString Name { get => _Name; set { _Name = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Name")); } } public UndertaleString DisplayName { get => _DisplayName; set { _DisplayName = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("DisplayName")); } } @@ -35,6 +36,7 @@ public class UndertaleFont : UndertaleNamedResource, INotifyPropertyChanged public float ScaleX { get => _ScaleX; set { _ScaleX = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("ScaleX")); } } public float ScaleY { get => _ScaleY; set { _ScaleY = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("ScaleY")); } } public UndertalePointerList Glyphs { get; private set; } = new UndertalePointerList(); + public int AscenderOffset { get => _AscenderOffset; set { _AscenderOffset = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("AscenderOffset")); } } public event PropertyChangedEventHandler PropertyChanged; @@ -45,16 +47,18 @@ public class Glyph : UndertaleObject, INotifyPropertyChanged private ushort _SourceY; private ushort _SourceWidth; private ushort _SourceHeight; - private ushort _Shift; - private uint _Offset; // TODO: probably signed + private short _Shift; + private int _Offset; + private UndertaleSimpleListShort _Kerning; public ushort Character { get => _Character; set { _Character = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Character")); } } public ushort SourceX { get => _SourceX; set { _SourceX = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("SourceX")); } } public ushort SourceY { get => _SourceY; set { _SourceY = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("SourceY")); } } public ushort SourceWidth { get => _SourceWidth; set { _SourceWidth = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("SourceWidth")); } } public ushort SourceHeight { get => _SourceHeight; set { _SourceHeight = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("SourceHeight")); } } - public ushort Shift { get => _Shift; set { _Shift = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Shift")); } } - public uint Offset { get => _Offset; set { _Offset = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Offset")); } } + public short Shift { get => _Shift; set { _Shift = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Shift")); } } + public int Offset { get => _Offset; set { _Offset = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Offset")); } } + public UndertaleSimpleListShort Kerning { get => _Kerning; set { _Kerning = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Kerning")); } } public event PropertyChangedEventHandler PropertyChanged; @@ -66,7 +70,14 @@ public void Serialize(UndertaleWriter writer) writer.Write(SourceWidth); writer.Write(SourceHeight); writer.Write(Shift); - writer.Write(Offset); + if (writer.undertaleData.GeneralInfo?.Major >= 2 || (writer.undertaleData.GeneralInfo?.Major == 1 && writer.undertaleData.GeneralInfo?.Build == 9999)) + { + writer.Write((short)Offset); + writer.WriteUndertaleObject(Kerning); + } else + { + writer.Write(Offset); + } } public void Unserialize(UndertaleReader reader) @@ -76,8 +87,33 @@ public void Unserialize(UndertaleReader reader) SourceY = reader.ReadUInt16(); SourceWidth = reader.ReadUInt16(); SourceHeight = reader.ReadUInt16(); - Shift = reader.ReadUInt16(); - Offset = reader.ReadUInt32(); + Shift = reader.ReadInt16(); + if (reader.undertaleData.GeneralInfo?.Major >= 2 || (reader.undertaleData.GeneralInfo?.Major == 1 && reader.undertaleData.GeneralInfo?.Build == 9999)) + { + Offset = reader.ReadInt16(); + Kerning = reader.ReadUndertaleObject>(); + } else + { + Offset = reader.ReadInt32(); // Maybe? I don't really know, but this definitely used to work + } + } + + public class GlyphKerning : UndertaleObject + { + public short Other; + public short Amount; + + public void Serialize(UndertaleWriter writer) + { + writer.Write(Other); + writer.Write(Amount); + } + + public void Unserialize(UndertaleReader reader) + { + Other = reader.ReadInt16(); + Amount = reader.ReadInt16(); + } } } @@ -95,6 +131,8 @@ public void Serialize(UndertaleWriter writer) writer.WriteUndertaleObjectPointer(Texture); writer.Write(ScaleX); writer.Write(ScaleY); + if (writer.undertaleData.GeneralInfo?.BytecodeVersion >= 17) + writer.Write(AscenderOffset); writer.WriteUndertaleObject(Glyphs); } @@ -112,6 +150,8 @@ public void Unserialize(UndertaleReader reader) Texture = reader.ReadUndertaleObjectPointer(); ScaleX = reader.ReadSingle(); ScaleY = reader.ReadSingle(); + if (reader.undertaleData.GeneralInfo?.BytecodeVersion >= 17) + AscenderOffset = reader.ReadInt32(); Glyphs = reader.ReadUndertaleObject>(); } diff --git a/UndertaleModLib/Models/UndertaleGeneralInfo.cs b/UndertaleModLib/Models/UndertaleGeneralInfo.cs index e164f8c01..ba1444eef 100644 --- a/UndertaleModLib/Models/UndertaleGeneralInfo.cs +++ b/UndertaleModLib/Models/UndertaleGeneralInfo.cs @@ -159,7 +159,7 @@ public void Unserialize(UndertaleReader reader) GMS2AllowStatistics = reader.ReadBoolean(); GMS2GameGUID = reader.ReadBytes(16); } - reader.undertaleData.UnsupportedBytecodeVersion = (BytecodeVersion != 15 && BytecodeVersion != 16); + reader.undertaleData.UnsupportedBytecodeVersion = (BytecodeVersion != 15 && BytecodeVersion != 16 && BytecodeVersion != 17); } public override string ToString() diff --git a/UndertaleModLib/Models/UndertaleRoom.cs b/UndertaleModLib/Models/UndertaleRoom.cs index 2523cab33..dd0c7a23b 100644 --- a/UndertaleModLib/Models/UndertaleRoom.cs +++ b/UndertaleModLib/Models/UndertaleRoom.cs @@ -628,7 +628,7 @@ public class LayerTilesData : LayerData, INotifyPropertyChanged public void Serialize(UndertaleWriter writer) { - writer.WriteUndertaleObject(_Background); + _Background.Serialize(writer); // see comment below writer.Write(TilesX); writer.Write(TilesY); if (TileData.Length != TilesY) @@ -644,7 +644,8 @@ public void Serialize(UndertaleWriter writer) public void Unserialize(UndertaleReader reader) { - _Background = reader.ReadUndertaleObject>(); + _Background = new UndertaleResourceById(); // see comment in UndertaleGlobalInit.Unserialize + _Background.Unserialize(reader); _TileData = null; // prevent unnecessary resizes TilesX = reader.ReadUInt32(); TilesY = reader.ReadUInt32(); diff --git a/UndertaleModLib/Models/UndertaleTextureGroupInfo.cs b/UndertaleModLib/Models/UndertaleTextureGroupInfo.cs new file mode 100644 index 000000000..452a1be40 --- /dev/null +++ b/UndertaleModLib/Models/UndertaleTextureGroupInfo.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace UndertaleModLib.Models +{ + public class UndertaleTextureGroupInfo : UndertaleObject + { + public UndertaleString GroupName; + public UndertaleSimpleResourcesList TexturePages; + public UndertaleSimpleResourcesList Sprites; + public UndertaleSimpleResourcesList SpineSprites; + public UndertaleSimpleResourcesList Fonts; + public UndertaleSimpleResourcesList Tilesets; + + public UndertaleTextureGroupInfo() + { + TexturePages = new UndertaleSimpleResourcesList(); + Sprites = new UndertaleSimpleResourcesList(); + SpineSprites = new UndertaleSimpleResourcesList(); + Fonts = new UndertaleSimpleResourcesList(); + Tilesets = new UndertaleSimpleResourcesList(); + } + + public void Serialize(UndertaleWriter writer) + { + writer.WriteUndertaleString(GroupName); + + writer.WriteUndertaleObjectPointer(TexturePages); + writer.WriteUndertaleObjectPointer(Sprites); + writer.WriteUndertaleObjectPointer(SpineSprites); + writer.WriteUndertaleObjectPointer(Fonts); + writer.WriteUndertaleObjectPointer(Tilesets); + + writer.WriteUndertaleObject(TexturePages); + writer.WriteUndertaleObject(Sprites); + writer.WriteUndertaleObject(SpineSprites); + writer.WriteUndertaleObject(Fonts); + writer.WriteUndertaleObject(Tilesets); + } + + public void Unserialize(UndertaleReader reader) + { + GroupName = reader.ReadUndertaleString(); + + // Read the pointers + TexturePages = reader.ReadUndertaleObjectPointer>(); + Sprites = reader.ReadUndertaleObjectPointer>(); + SpineSprites = reader.ReadUndertaleObjectPointer>(); + Fonts = reader.ReadUndertaleObjectPointer>(); + Tilesets = reader.ReadUndertaleObjectPointer>(); + + // Read the objects, throwing an error if the pointers are invalid + if (reader.ReadUndertaleObject>() != TexturePages || + reader.ReadUndertaleObject>() != Sprites || + reader.ReadUndertaleObject>() != SpineSprites || + reader.ReadUndertaleObject>() != Fonts || + reader.ReadUndertaleObject>() != Tilesets) + { + throw new UndertaleSerializationException("Invalid pointer to SimpleResourcesList"); + } + } + } +} diff --git a/UndertaleModLib/Models/UndertaleUnused.cs b/UndertaleModLib/Models/UndertaleUnused.cs index 6e2201b25..6a131e347 100644 --- a/UndertaleModLib/Models/UndertaleUnused.cs +++ b/UndertaleModLib/Models/UndertaleUnused.cs @@ -7,22 +7,5 @@ namespace UndertaleModLib.Models { - // GMS2 only, see https://github.com/krzys-h/UndertaleModTool/issues/4#issuecomment-421844420 for rough structure, but doesn't appear commonly used - public class UndertaleEmbeddedISomething : UndertaleObject - { - public UndertaleEmbeddedISomething() - { - throw new NotImplementedException(); - } - public void Serialize(UndertaleWriter writer) - { - throw new NotImplementedException(); - } - - public void Unserialize(UndertaleReader reader) - { - throw new NotImplementedException(); - } - } } diff --git a/UndertaleModLib/UndertaleChunks.cs b/UndertaleModLib/UndertaleChunks.cs index 755a1ab66..0dfa7d93b 100644 --- a/UndertaleModLib/UndertaleChunks.cs +++ b/UndertaleModLib/UndertaleChunks.cs @@ -34,7 +34,9 @@ public class UndertaleChunkFORM : UndertaleChunk public UndertaleChunkOBJT OBJT => Chunks.GetValueOrDefault("OBJT") as UndertaleChunkOBJT; public UndertaleChunkROOM ROOM => Chunks.GetValueOrDefault("ROOM") as UndertaleChunkROOM; public UndertaleChunkDAFL DAFL => Chunks.GetValueOrDefault("DAFL") as UndertaleChunkDAFL; + public UndertaleChunkEMBI EMBI => Chunks.GetValueOrDefault("EMBI") as UndertaleChunkEMBI; public UndertaleChunkTPAG TPAG => Chunks.GetValueOrDefault("TPAG") as UndertaleChunkTPAG; + public UndertaleChunkTGIN TGIN => Chunks.GetValueOrDefault("TGIN") as UndertaleChunkTGIN; public UndertaleChunkCODE CODE => Chunks.GetValueOrDefault("CODE") as UndertaleChunkCODE; public UndertaleChunkVARI VARI => Chunks.GetValueOrDefault("VARI") as UndertaleChunkVARI; public UndertaleChunkFUNC FUNC => Chunks.GetValueOrDefault("FUNC") as UndertaleChunkFUNC; @@ -107,9 +109,12 @@ internal override void UnserializeChunk(UndertaleReader reader) // Strange data for each extension, some kind of unique identifier based on // the product ID for each of them productIdData = new List(); - for (int i = 0; i < List.Count; i++) + if (reader.undertaleData.GeneralInfo?.Major >= 2 || (reader.undertaleData.GeneralInfo?.Major == 1 && reader.undertaleData.GeneralInfo?.Build >= 9999)) { - productIdData.Add(reader.ReadBytes(16)); + for (int i = 0; i < List.Count; i++) + { + productIdData.Add(reader.ReadBytes(16)); + } } } @@ -403,9 +408,9 @@ public class UndertaleChunkAUDO : UndertaleListChunk { public override string Name => "AUDO"; } - + // GMS2 only - public class UndertaleChunkEMBI : UndertaleSimpleListChunk + public class UndertaleChunkEMBI : UndertaleSimpleListChunk { public override string Name => "EMBI"; @@ -422,7 +427,30 @@ internal override void UnserializeChunk(UndertaleReader reader) if (reader.undertaleData.GeneralInfo.Major < 2) throw new InvalidOperationException(); if (reader.ReadUInt32() != 1) - throw new Exception("Should be hardcoded 1"); + throw new Exception("Expected EMBI version 1"); + base.UnserializeChunk(reader); + } + } + + // GMS2.2.1+ only + public class UndertaleChunkTGIN : UndertaleListChunk + { + public override string Name => "TGIN"; + + internal override void SerializeChunk(UndertaleWriter writer) + { + if (writer.undertaleData.GeneralInfo.Major < 2) + throw new InvalidOperationException(); + writer.Write((uint)1); // Hardcoded 1 + base.SerializeChunk(writer); + } + + internal override void UnserializeChunk(UndertaleReader reader) + { + if (reader.undertaleData.GeneralInfo.Major < 2) + throw new InvalidOperationException(); + if (reader.ReadUInt32() != 1) + throw new Exception("Expected TGIN version 1"); base.UnserializeChunk(reader); } } diff --git a/UndertaleModLib/UndertaleData.cs b/UndertaleModLib/UndertaleData.cs index 0d4aac548..8fbfaa186 100644 --- a/UndertaleModLib/UndertaleData.cs +++ b/UndertaleModLib/UndertaleData.cs @@ -39,7 +39,9 @@ public class UndertaleData public IList CodeLocals => FORM.FUNC?.CodeLocals; public IList Strings => FORM.STRG?.List; public IList EmbeddedTextures => FORM.TXTR?.List; + public IList EmbeddedImages => FORM.EMBI?.List; public IList EmbeddedAudio => FORM.AUDO?.List; + public IList TextureGroupInfo => FORM.TGIN?.List; public bool UnsupportedBytecodeVersion = false; diff --git a/UndertaleModLib/UndertaleLists.cs b/UndertaleModLib/UndertaleLists.cs index ed396ad44..882f836c3 100644 --- a/UndertaleModLib/UndertaleLists.cs +++ b/UndertaleModLib/UndertaleLists.cs @@ -2,6 +2,7 @@ using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Collections.Specialized; using System.IO; using System.Linq; using System.Text; @@ -39,7 +40,54 @@ public void Unserialize(UndertaleReader reader) } catch (UndertaleSerializationException e) { - throw new UndertaleSerializationException(e.Message + "\nwhile reading item " + (i+1) + " of " + count + " in a list of " + typeof(T).FullName, e); + throw new UndertaleSerializationException(e.Message + "\nwhile reading item " + (i + 1) + " of " + count + " in a list of " + typeof(T).FullName, e); + } + } + } + } + + public class UndertaleSimpleListShort : ObservableCollection, UndertaleObject where T : UndertaleObject, new() + { + public UndertaleSimpleListShort() + { + base.CollectionChanged += EnsureShortCount; + } + + private void EnsureShortCount(object sender, NotifyCollectionChangedEventArgs e) + { + if (e.NewItems != null && e.NewItems.Count > Int16.MaxValue) + throw new InvalidOperationException("Count of short SimpleList exceeds maximum number allowed."); + } + + public void Serialize(UndertaleWriter writer) + { + writer.Write((ushort)Count); + for (int i = 0; i < Count; i++) + { + try + { + writer.WriteUndertaleObject(this[i]); + } + catch (UndertaleSerializationException e) + { + throw new UndertaleSerializationException(e.Message + "\nwhile writing item " + (i + 1) + " of " + Count + " in a list of " + typeof(T).FullName, e); + } + } + } + + public void Unserialize(UndertaleReader reader) + { + ushort count = reader.ReadUInt16(); + Clear(); + for (ushort i = 0; i < count; i++) + { + try + { + Add(reader.ReadUndertaleObject()); + } + catch (UndertaleSerializationException e) + { + throw new UndertaleSerializationException(e.Message + "\nwhile reading item " + (i + 1) + " of " + count + " in a list of " + typeof(T).FullName, e); } } } @@ -112,6 +160,7 @@ public void Unserialize(UndertaleReader reader) { try { + (this[(int)i] as PrePaddedObject)?.UnserializePrePadding(reader); T obj = reader.ReadUndertaleObject(); if (!obj.Equals(this[(int)i])) throw new UndertaleSerializationException("Something got misaligned..."); diff --git a/UndertaleModLib/UndertaleModLib.csproj b/UndertaleModLib/UndertaleModLib.csproj index 92d50ad3a..dd11dbaea 100644 --- a/UndertaleModLib/UndertaleModLib.csproj +++ b/UndertaleModLib/UndertaleModLib.csproj @@ -44,6 +44,7 @@ + @@ -61,6 +62,7 @@ + diff --git a/UndertaleModTool/Editors/UndertaleEmbeddedTextureEditor.xaml b/UndertaleModTool/Editors/UndertaleEmbeddedTextureEditor.xaml index 13d7e21df..3d3469964 100644 --- a/UndertaleModTool/Editors/UndertaleEmbeddedTextureEditor.xaml +++ b/UndertaleModTool/Editors/UndertaleEmbeddedTextureEditor.xaml @@ -18,11 +18,11 @@ - Unknown (added in GMS2) - + Scaled + - Unknown - + Generated mips + diff --git a/UndertaleModTool/Editors/UndertaleFontEditor.xaml b/UndertaleModTool/Editors/UndertaleFontEditor.xaml index d89fe5591..37c0720af 100644 --- a/UndertaleModTool/Editors/UndertaleFontEditor.xaml +++ b/UndertaleModTool/Editors/UndertaleFontEditor.xaml @@ -161,7 +161,7 @@ - Note that the glyphs need to be specified in ascending order. Press the button below to sort them appropariately. + Note that the glyphs need to be specified in ascending order. Press the button below to sort them appropriately. diff --git a/UndertaleModTool/MainWindow.xaml.cs b/UndertaleModTool/MainWindow.xaml.cs index b3083d6e9..1943f8998 100644 --- a/UndertaleModTool/MainWindow.xaml.cs +++ b/UndertaleModTool/MainWindow.xaml.cs @@ -237,7 +237,7 @@ private async Task LoadFile(string filename) { if (data.UnsupportedBytecodeVersion) { - MessageBox.Show("Only bytecode versions 15 and 16 are supported for now, you are trying to load " + data.GeneralInfo.BytecodeVersion + ". A lot of code is disabled and will likely break something. Saving/exporting is disabled.", "Unsupported bytecode version", MessageBoxButton.OK, MessageBoxImage.Warning); + MessageBox.Show("Only bytecode versions 15, 16, and (partially) 17 are supported for now, you are trying to load " + data.GeneralInfo.BytecodeVersion + ". A lot of code is disabled and will likely break something. Saving/exporting is disabled.", "Unsupported bytecode version", MessageBoxButton.OK, MessageBoxImage.Warning); CanSave = false; } else if (hadWarnings) @@ -253,6 +253,10 @@ private async Task LoadFile(string filename) { MessageBox.Show("Game Maker: Studio 2 game loaded! I just hacked this together quickly for the Nintendo Switch release of Undertale. Most things work, but some editors don't display all the data.", "GMS2 game loaded", MessageBoxButton.OK, MessageBoxImage.Warning); } + if (data.GeneralInfo?.BytecodeVersion == 17) + { + MessageBox.Show("Bytecode version 17 has been loaded. There may be some problems remaining, as thorough research into the changes are yet to be done.", "Bytecode version 17", MessageBoxButton.OK, MessageBoxImage.Warning); + } if (System.IO.Path.GetDirectoryName(FilePath) != System.IO.Path.GetDirectoryName(filename)) CloseChildFiles(); this.Data = data;