Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix/finish EMBI, add TGIN internal support, fix EXTN potential crash #76

Merged
merged 10 commits into from
Nov 19, 2018
32 changes: 32 additions & 0 deletions UndertaleModLib/Models/UndertaleEmbeddedImage.cs
Original file line number Diff line number Diff line change
@@ -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<UndertaleTexturePageItem>();
}
}
}
16 changes: 8 additions & 8 deletions UndertaleModLib/Models/UndertaleEmbeddedTexture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<TexData>();
}

Expand Down
54 changes: 47 additions & 7 deletions UndertaleModLib/Models/UndertaleFont.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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")); } }
Expand All @@ -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<Glyph> Glyphs { get; private set; } = new UndertalePointerList<Glyph>();
public int AscenderOffset { get => _AscenderOffset; set { _AscenderOffset = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("AscenderOffset")); } }

public event PropertyChangedEventHandler PropertyChanged;

Expand All @@ -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<GlyphKerning> _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<GlyphKerning> Kerning { get => _Kerning; set { _Kerning = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Kerning")); } }

public event PropertyChangedEventHandler PropertyChanged;

Expand All @@ -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)
Expand All @@ -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<UndertaleSimpleListShort<GlyphKerning>>();
} else
{
Offset = reader.ReadInt32(); // Maybe? I don't really know, but this definitely used to work
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it was two bytes of data and two bytes of padding, or maybe the list always existed but was empty?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess maybe it can assume there's always a list? No real harm in doing so.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hard to tell when we don't even have any examples that make use of it

}
}

public class GlyphKerning : UndertaleObject
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I guess the editor needs an update...
How many entries of this do you usually get? I'm just wondering what controls would be best for this, since now every table row can have subrows...
(and font editing for translators just got harder, ugh)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, I feel stupid because I just realized none of the things I've tested on use the list at all. It doesn't really seem necessary to allocate too much space for them (if any).

{
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();
}
}
}

Expand All @@ -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);
}

Expand All @@ -112,6 +150,8 @@ public void Unserialize(UndertaleReader reader)
Texture = reader.ReadUndertaleObjectPointer<UndertaleTexturePageItem>();
ScaleX = reader.ReadSingle();
ScaleY = reader.ReadSingle();
if (reader.undertaleData.GeneralInfo?.BytecodeVersion >= 17)
AscenderOffset = reader.ReadInt32();
Glyphs = reader.ReadUndertaleObject<UndertalePointerList<Glyph>>();
}

Expand Down
2 changes: 1 addition & 1 deletion UndertaleModLib/Models/UndertaleGeneralInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
5 changes: 3 additions & 2 deletions UndertaleModLib/Models/UndertaleRoom.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -644,7 +644,8 @@ public void Serialize(UndertaleWriter writer)

public void Unserialize(UndertaleReader reader)
{
_Background = reader.ReadUndertaleObject<UndertaleResourceById<UndertaleBackground, UndertaleChunkBGND>>();
_Background = new UndertaleResourceById<UndertaleBackground, UndertaleChunkBGND>(); // see comment in UndertaleGlobalInit.Unserialize
krzys-h marked this conversation as resolved.
Show resolved Hide resolved
_Background.Unserialize(reader);
_TileData = null; // prevent unnecessary resizes
TilesX = reader.ReadUInt32();
TilesY = reader.ReadUInt32();
Expand Down
67 changes: 67 additions & 0 deletions UndertaleModLib/Models/UndertaleTextureGroupInfo.cs
Original file line number Diff line number Diff line change
@@ -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<UndertaleEmbeddedTexture, UndertaleChunkTXTR> TexturePages;
public UndertaleSimpleResourcesList<UndertaleSprite, UndertaleChunkSPRT> Sprites;
public UndertaleSimpleResourcesList<UndertaleSprite, UndertaleChunkSPRT> SpineSprites;
public UndertaleSimpleResourcesList<UndertaleFont, UndertaleChunkFONT> Fonts;
public UndertaleSimpleResourcesList<UndertaleBackground, UndertaleChunkBGND> Tilesets;

public UndertaleTextureGroupInfo()
{
TexturePages = new UndertaleSimpleResourcesList<UndertaleEmbeddedTexture, UndertaleChunkTXTR>();
Sprites = new UndertaleSimpleResourcesList<UndertaleSprite, UndertaleChunkSPRT>();
SpineSprites = new UndertaleSimpleResourcesList<UndertaleSprite, UndertaleChunkSPRT>();
Fonts = new UndertaleSimpleResourcesList<UndertaleFont, UndertaleChunkFONT>();
Tilesets = new UndertaleSimpleResourcesList<UndertaleBackground, UndertaleChunkBGND>();
}

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<UndertaleSimpleResourcesList<UndertaleEmbeddedTexture, UndertaleChunkTXTR>>();
Sprites = reader.ReadUndertaleObjectPointer<UndertaleSimpleResourcesList<UndertaleSprite, UndertaleChunkSPRT>>();
SpineSprites = reader.ReadUndertaleObjectPointer<UndertaleSimpleResourcesList<UndertaleSprite, UndertaleChunkSPRT>>();
Fonts = reader.ReadUndertaleObjectPointer<UndertaleSimpleResourcesList<UndertaleFont, UndertaleChunkFONT>>();
Tilesets = reader.ReadUndertaleObjectPointer<UndertaleSimpleResourcesList<UndertaleBackground, UndertaleChunkBGND>>();

// Read the objects, throwing an error if the pointers are invalid
if (reader.ReadUndertaleObject<UndertaleSimpleResourcesList<UndertaleEmbeddedTexture, UndertaleChunkTXTR>>() != TexturePages ||
reader.ReadUndertaleObject<UndertaleSimpleResourcesList<UndertaleSprite, UndertaleChunkSPRT>>() != Sprites ||
reader.ReadUndertaleObject<UndertaleSimpleResourcesList<UndertaleSprite, UndertaleChunkSPRT>>() != SpineSprites ||
reader.ReadUndertaleObject<UndertaleSimpleResourcesList<UndertaleFont, UndertaleChunkFONT>>() != Fonts ||
reader.ReadUndertaleObject<UndertaleSimpleResourcesList<UndertaleBackground, UndertaleChunkBGND>>() != Tilesets)
{
throw new UndertaleSerializationException("Invalid pointer to SimpleResourcesList");
}
}
}
}
17 changes: 0 additions & 17 deletions UndertaleModLib/Models/UndertaleUnused.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
}
}
38 changes: 33 additions & 5 deletions UndertaleModLib/UndertaleChunks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<byte[]>();
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));
}
}
}

Expand Down Expand Up @@ -403,9 +408,9 @@ public class UndertaleChunkAUDO : UndertaleListChunk<UndertaleEmbeddedAudio>
{
public override string Name => "AUDO";
}

// GMS2 only
public class UndertaleChunkEMBI : UndertaleSimpleListChunk<UndertaleEmbeddedISomething>
public class UndertaleChunkEMBI : UndertaleSimpleListChunk<UndertaleEmbeddedImage>
{
public override string Name => "EMBI";

Expand All @@ -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<UndertaleTextureGroupInfo>
{
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);
}
}
Expand Down
2 changes: 2 additions & 0 deletions UndertaleModLib/UndertaleData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@ public class UndertaleData
public IList<UndertaleCodeLocals> CodeLocals => FORM.FUNC?.CodeLocals;
public IList<UndertaleString> Strings => FORM.STRG?.List;
public IList<UndertaleEmbeddedTexture> EmbeddedTextures => FORM.TXTR?.List;
public IList<UndertaleEmbeddedImage> EmbeddedImages => FORM.EMBI?.List;
public IList<UndertaleEmbeddedAudio> EmbeddedAudio => FORM.AUDO?.List;
public IList<UndertaleTextureGroupInfo> TextureGroupInfo => FORM.TGIN?.List;

public bool UnsupportedBytecodeVersion = false;

Expand Down
Loading