Skip to content

Commit

Permalink
Address reviews
Browse files Browse the repository at this point in the history
  • Loading branch information
colinator27 committed Jul 13, 2024
1 parent 374090a commit b4dd901
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 93 deletions.
16 changes: 13 additions & 3 deletions UndertaleModLib/Models/UndertaleRoom.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2069,7 +2069,7 @@ public void Unserialize(UndertaleReader reader)
if (reader.undertaleData.IsNonLTSVersionAtLeast(2023, 2))
ParticleSystems = reader.ReadUndertaleObjectPointer<UndertalePointerList<ParticleSystemInstance>>();
if (firstPointerTarget > reader.AbsPosition && !reader.undertaleData.IsVersionAtLeast(2024, 6))
reader.undertaleData.SetGMS2Version(2024, 6); // there's more data before legacy tiles, so must be 2024.6+
reader.undertaleData.SetGMS2Version(2024, 6); // There's more data before legacy tiles, so must be 2024.6+
if (reader.undertaleData.IsVersionAtLeast(2024, 6))
TextItems = reader.ReadUndertaleObjectPointer<UndertalePointerList<TextItemInstance>>();
}
Expand Down Expand Up @@ -2106,7 +2106,7 @@ public static uint UnserializeChildObjectCount(UndertaleReader reader)
if (reader.undertaleData.IsNonLTSVersionAtLeast(2023, 2))
partSystemsPtr = reader.ReadUInt32();
if (legacyTilesPtr > reader.AbsPosition && !reader.undertaleData.IsVersionAtLeast(2024, 6))
reader.undertaleData.SetGMS2Version(2024, 6); // there's more data before legacy tiles, so must be 2024.6+
reader.undertaleData.SetGMS2Version(2024, 6); // There's more data before legacy tiles, so must be 2024.6+
if (reader.undertaleData.IsVersionAtLeast(2024, 6))
textItemsPtr = reader.ReadUInt32();
}
Expand Down Expand Up @@ -2388,6 +2388,9 @@ public void Unserialize(UndertaleReader reader)
Rotation = reader.ReadSingle();
}

/// <summary>
/// Generates a random name for this instance, as a utility for room editing.
/// </summary>
//TODO: rework this method slightly.
public static UndertaleString GenerateRandomName(UndertaleData data)
{
Expand Down Expand Up @@ -2550,6 +2553,9 @@ public void Unserialize(UndertaleReader reader)
Rotation = reader.ReadSingle();
}

/// <summary>
/// Generates a random name for this instance, as a utility for room editing.
/// </summary>
public static UndertaleString GenerateRandomName(UndertaleData data)
{
return data.Strings.MakeString("particle_" + ((uint)Random.Shared.Next(-Int32.MaxValue, Int32.MaxValue)).ToString("X8"));
Expand Down Expand Up @@ -2587,6 +2593,7 @@ protected void OnPropertyChanged([CallerMemberName] string name = null)

private UndertaleResourceById<UndertaleFont, UndertaleChunkFONT> _font = new();

// TODO: document these fields; some are self-explanatory but unsure on the behavior of all of them
public UndertaleString Name { get; set; }
public int X { get; set; }
public int Y { get; set; }
Expand Down Expand Up @@ -2657,6 +2664,9 @@ public void Unserialize(UndertaleReader reader)
Wrap = reader.ReadBoolean();
}

/// <summary>
/// Generates a random name for this instance, as a utility for room editing.
/// </summary>
public static UndertaleString GenerateRandomName(UndertaleData data)
{
return data.Strings.MakeString("textitem_" + ((uint)Random.Shared.Next(-Int32.MaxValue, Int32.MaxValue)).ToString("X8"));
Expand All @@ -2665,7 +2675,7 @@ public static UndertaleString GenerateRandomName(UndertaleData data)
/// <inheritdoc />
public override string ToString()
{
return "Text item " + Name?.Content + " with text \"" + (Text?.Content ?? "?") + "\"";
return $"Text item {Name?.Content} with text \"{Text?.Content ?? "?"}\"";
}

/// <inheritdoc/>
Expand Down
14 changes: 14 additions & 0 deletions UndertaleModLib/Models/UndertaleSprite.cs
Original file line number Diff line number Diff line change
Expand Up @@ -806,6 +806,9 @@ public static uint UnserializeChildObjectCount(UndertaleReader reader)
return count;
}

/// <summary>
/// Returns the width and height of the collision mask for this sprite, which changes depending on GameMaker version.
/// </summary>
public (uint Width, uint Height) CalculateMaskDimensions(UndertaleData data)
{
if (data.IsVersionAtLeast(2024, 6))
Expand All @@ -815,11 +818,22 @@ public static uint UnserializeChildObjectCount(UndertaleReader reader)
return CalculateFullMaskDimensions(Width, Height);
}

/// <summary>
/// Calculates the width and height of a collision mask from the given margin/bounding box.
/// This method is used to calculate collision mask dimensions in GameMaker 2024.6 and above.
/// </summary>
public static (uint Width, uint Height) CalculateBboxMaskDimensions(int marginRight, int marginLeft, int marginBottom, int marginTop)
{
return ((uint)(marginRight - marginLeft + 1), (uint)(marginBottom - marginTop + 1));
}

/// <summary>
/// Calculates the width and height of a collision mask from a given sprite's full width and height.
/// This method is used to calculate collision mask dimensions prior to GameMaker 2024.6.
/// </summary>
/// <remarks>
/// This simply returns the width and height supplied, but is intended for clarity in the code.
/// </remarks>
public static (uint Width, uint Height) CalculateFullMaskDimensions(uint width, uint height)
{
return (width, height);
Expand Down
175 changes: 85 additions & 90 deletions UndertaleModLib/UndertaleChunks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -453,7 +453,7 @@ private void CheckForGM2024_6(UndertaleReader reader)
uint nextSpritePtr = 0;
if ((i + 1) < spriteCount)
nextSpritePtr = reader.ReadUInt32();
reader.AbsPosition = spritePtr + 4 /* skip name */;
reader.AbsPosition = spritePtr + 4; // Skip past "Name"

// Check if bbox size differs from width/height
uint width = reader.ReadUInt32();
Expand All @@ -472,106 +472,101 @@ private void CheckForGM2024_6(UndertaleReader reader)

reader.Position += 28;

if (reader.ReadInt32() == -1)
if (reader.ReadInt32() != -1)
{
uint sVersion = reader.ReadUInt32();
UndertaleSprite.SpriteType sSpriteType = (UndertaleSprite.SpriteType)reader.ReadUInt32();

if (sSpriteType != UndertaleSprite.SpriteType.Normal)
{
// We can't determine anything from this sprite
continue;
}
throw new IOException("Expected special sprite type");
}

reader.Position += 8; // playback speed values
uint sVersion = reader.ReadUInt32();
UndertaleSprite.SpriteType sSpriteType = (UndertaleSprite.SpriteType)reader.ReadUInt32();

if (sVersion != 3)
{
throw new IOException("Expected sprite version 3");
}
uint sequenceOffset = reader.ReadUInt32();
uint nineSliceOffset = reader.ReadUInt32();
if (sSpriteType != UndertaleSprite.SpriteType.Normal)
{
// We can't determine anything from this sprite
continue;
}

// Skip past texture pointers
uint textureCount = reader.ReadUInt32();
reader.Position += textureCount * 4;
reader.Position += 8; // Playback speed values

// Calculate how much space the "full" and "bbox" mask data take up
uint maskCount = reader.ReadUInt32();
if (maskCount == 0)
{
// We can't determine anything from this sprite
continue;
}
uint fullLength = (normalWidth + 7) / 8 * normalHeight;
fullLength *= maskCount;
if ((fullLength % 4) != 0)
fullLength += (4 - (fullLength % 4));
uint bboxLength = (bboxWidth + 7) / 8 * bboxHeight;
bboxLength *= maskCount;
if ((bboxLength % 4) != 0)
bboxLength += (4 - (bboxLength % 4));

// Calculate expected end offset
long expectedEndOffset;
bool endOffsetLenient = false;
if (sequenceOffset != 0)
{
expectedEndOffset = sequenceOffset;
}
else if (nineSliceOffset != 0)
{
expectedEndOffset = nineSliceOffset;
}
else if (nextSpritePtr != 0)
{
expectedEndOffset = nextSpritePtr;
}
else
{
// Use chunk length, and be lenient with it (due to chunk padding)
endOffsetLenient = true;
expectedEndOffset = chunkStartPos + Length;
}
if (sVersion != 3)
{
throw new IOException("Expected sprite version 3");
}
uint sequenceOffset = reader.ReadUInt32();
uint nineSliceOffset = reader.ReadUInt32();

// If the "full" mask data runs past the expected end offset, and the "bbox" mask data does not, then this is 2024.6.
// Otherwise, stop processing and assume this is not 2024.6.
long fullEndPos = (reader.AbsPosition + fullLength);
if (fullEndPos != expectedEndOffset)
{
if (endOffsetLenient && (fullEndPos % 16) != 0 && fullEndPos + (16 - (fullEndPos % 16)) == expectedEndOffset)
{
// "Full" mask data doesn't exactly line up, but works if rounded up to the next chunk padding
break;
}
// Skip past texture pointers
uint textureCount = reader.ReadUInt32();
reader.Position += textureCount * 4;

long bboxEndPos = (reader.AbsPosition + bboxLength);
if (bboxEndPos == expectedEndOffset)
{
// "Bbox" mask data is valid
reader.undertaleData.SetGMS2Version(2024, 6);
}
else if (endOffsetLenient && (bboxEndPos % 16) != 0 && bboxEndPos + (16 - (bboxEndPos % 16)) == expectedEndOffset)
{
// "Bbox" mask data doesn't exactly line up, but works if rounded up to the next chunk padding
reader.undertaleData.SetGMS2Version(2024, 6);
}
else
{
// Neither option seems to have worked...
throw new IOException("Failed to detect mask type in 2024.6 detection");
}
}
else
{
// "Full" mask data is valid
break;
}
// Calculate how much space the "full" and "bbox" mask data take up
uint maskCount = reader.ReadUInt32();
if (maskCount == 0)
{
// We can't determine anything from this sprite
continue;
}
uint fullLength = (normalWidth + 7) / 8 * normalHeight;
fullLength *= maskCount;
if ((fullLength % 4) != 0)
fullLength += (4 - (fullLength % 4));
uint bboxLength = (bboxWidth + 7) / 8 * bboxHeight;
bboxLength *= maskCount;
if ((bboxLength % 4) != 0)
bboxLength += (4 - (bboxLength % 4));

// Calculate expected end offset
long expectedEndOffset;
bool endOffsetLenient = false;
if (sequenceOffset != 0)
{
expectedEndOffset = sequenceOffset;
}
else if (nineSliceOffset != 0)
{
expectedEndOffset = nineSliceOffset;
}
else if (nextSpritePtr != 0)
{
expectedEndOffset = nextSpritePtr;
}
else
{
throw new IOException("Expected special sprite type");
// Use chunk length, and be lenient with it (due to chunk padding)
endOffsetLenient = true;
expectedEndOffset = chunkStartPos + Length;
}

// If the "full" mask data runs past the expected end offset, and the "bbox" mask data does not, then this is 2024.6.
// Otherwise, stop processing and assume this is not 2024.6.
long fullEndPos = (reader.AbsPosition + fullLength);
if (fullEndPos == expectedEndOffset)
{
// "Full" mask data is valid
break;
}
if (endOffsetLenient && (fullEndPos % 16) != 0 && fullEndPos + (16 - (fullEndPos % 16)) == expectedEndOffset)
{
// "Full" mask data doesn't exactly line up, but works if rounded up to the next chunk padding
break;
}

long bboxEndPos = (reader.AbsPosition + bboxLength);
if (bboxEndPos == expectedEndOffset)
{
// "Bbox" mask data is valid
reader.undertaleData.SetGMS2Version(2024, 6);
break;
}
if (endOffsetLenient && (bboxEndPos % 16) != 0 && bboxEndPos + (16 - (bboxEndPos % 16)) == expectedEndOffset)
{
// "Bbox" mask data doesn't exactly line up, but works if rounded up to the next chunk padding
reader.undertaleData.SetGMS2Version(2024, 6);
break;
}

// Neither option seems to have worked...
throw new IOException("Failed to detect mask type in 2024.6 detection");
}

reader.Position = returnTo;
Expand Down

0 comments on commit b4dd901

Please sign in to comment.