Пример #1
0
        public void Serialize(UndertaleWriter writer)
        {
            try
            {
                Debug.Assert(Name != null);
                writer.Write(Name.ToCharArray());
                var lenWriter = writer.WriteLengthHere();

                writer.SubmitMessage("Writing chunk " + Name);
                lenWriter.FromHere();
                SerializeChunk(writer);

                if (Name != "FORM" && Name != writer.LastChunkName)
                {
                    UndertaleGeneralInfo generalInfo = Name == "GEN8" ? ((UndertaleChunkGEN8)this).Object : writer.undertaleData?.GeneralInfo;
                    // These versions introduced new padding
                    // all chunks now start on 16-byte boundaries
                    // (but the padding is included with length of previous chunk)
                    // TODO: what about the debug data??
                    if (generalInfo != null && (generalInfo.Major >= 2 || (generalInfo.Major == 1 && generalInfo.Build >= 9999)))
                    {
                        int  e   = writer.undertaleData.PaddingAlignException;
                        uint pad = (e == -1 ? 16 : (uint)e);
                        while (writer.Position % pad != 0)
                        {
                            writer.Write((byte)0);
                        }
                    }
                }

                Length = lenWriter.ToHere();
            }
            catch (UndertaleSerializationException e)
            {
                throw new UndertaleSerializationException(e.Message + " in chunk " + Name, e);
            }
            catch (Exception e)
            {
                throw new UndertaleSerializationException(e.Message + "\nat " + writer.Position.ToString("X8") + " while reading chunk " + Name, e);
            }
        }
Пример #2
0
 public void Serialize(UndertaleWriter writer)
 {
     writer.WriteUndertaleString(Name);
     writer.Write(Occurrences);
     if (Occurrences > 0)
     {
         uint addr = writer.GetAddressForUndertaleObject(FirstAddress);
         if (GMS2_3)
         {
             writer.Write((addr == 0) ? 0 : (addr + 4)); // in GMS 2.3, it points to the actual reference rather than the instruction
         }
         else
         {
             writer.Write(addr);
         }
     }
     else
     {
         writer.Write((int)-1);
     }
 }
Пример #3
0
 /// <inheritdoc />
 public void Serialize(UndertaleWriter writer)
 {
     writer.Write(Character);
     writer.Write(SourceX);
     writer.Write(SourceY);
     writer.Write(SourceWidth);
     writer.Write(SourceHeight);
     writer.Write(Shift);
     writer.Write(Offset);
     writer.WriteUndertaleObject(Kerning);
 }
Пример #4
0
        internal override void SerializeChunk(UndertaleWriter writer)
        {
            base.SerializeChunk(writer);

            if (writer.undertaleData.IsTPAG4ByteAligned)
            {
                // padding present in ARM platforms apparently
                while (writer.Position % 0x4 != 0)
                {
                    writer.Write((byte)0);
                }
            }
        }
Пример #5
0
        internal override void SerializeChunk(UndertaleWriter writer)
        {
            if (writer.undertaleData.GeneralInfo.Major < 2)
            {
                throw new InvalidOperationException();
            }

            writer.Position += (4 - (writer.Position % 4)) % 4;

            writer.Write((uint)1); // Version

            base.SerializeChunk(writer);
        }
Пример #6
0
        public void Serialize(UndertaleWriter writer)
        {
            try
            {
                Debug.Assert(Name != null);
                writer.Write(Name.ToCharArray());
                var lenWriter = writer.WriteLengthHere();

                Debug.WriteLine("Writing chunk " + Name);
                lenWriter.FromHere();
                SerializeChunk(writer);

                if (Name != "FORM" && Name != "AUDO") // TODO: needs a better way to detect last chunk
                {
                    UndertaleGeneralInfo generalInfo = Name == "GEN8" ? ((UndertaleChunkGEN8)this).Object : writer.undertaleData.GeneralInfo;
                    // These versions introduced new padding
                    // all chunks now start on 16-byte boundaries
                    // (but the padding is included with length of previous chunk)
                    if (generalInfo.Major >= 2 || (generalInfo.Major == 1 && generalInfo.Build >= 9999))
                    {
                        while (writer.Position % 16 != 0)
                        {
                            writer.Write((byte)0);
                        }
                    }
                }

                Length = lenWriter.ToHere();
            }
            catch (UndertaleSerializationException e)
            {
                throw new UndertaleSerializationException(e.Message + " in chunk " + Name);
            }
            catch (Exception e)
            {
                throw new UndertaleSerializationException(e.Message + "\nat " + writer.Position.ToString("X8") + " while reading chunk " + Name, e);
            }
        }
 public void Serialize(UndertaleWriter writer)
 {
     writer.Write((uint)Count);
     for (int i = 0; i < Count; i++)
     {
         try
         {
             writer.WriteUndertaleString(this[i]);
         }
         catch (UndertaleSerializationException e)
         {
             throw new UndertaleSerializationException(e.Message + "\nwhile writing item " + (i + 1) + " of " + Count + " in a string-list", e);
         }
     }
 }
 public void Serialize(UndertaleWriter writer)
 {
     writer.Write((ushort)Count);
     for (int i = 0; i < Count; i++)
     {
         try
         {
             writer.WriteUndertaleObject <T>(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);
         }
     }
 }
Пример #9
0
        internal override void SerializeChunk(UndertaleWriter writer)
        {
            base.SerializeChunk(writer);

            // (read above comment)
            foreach (byte[] data in productIdData)
            {
                int Len = data.Length;
                if (Len != 16)
                {
                    throw new IOException("Can't write EXTN product id data of invalid length, expected 16, got " + Len);
                }

                writer.Write(data);
            }
        }
Пример #10
0
        internal override void SerializeChunk(UndertaleWriter writer)
        {
            base.SerializeChunk(writer);

            // texture blobs
            foreach (UndertaleEmbeddedTexture obj in List)
            {
                obj.SerializeBlob(writer);
            }

            // padding
            // TODO: Maybe the padding is more global and every chunk is padded to 4 byte boundaries?
            while (writer.Position % 4 != 0)
            {
                writer.Write((byte)0);
            }
        }
Пример #11
0
 public void Serialize(UndertaleWriter writer)
 {
     writer.Write(Key);
     writer.Write(Length);
     writer.Write(Stretch);
     writer.Write(Disabled);
     writer.Write(Channels.Count);
     foreach (KeyValuePair <int, T> kvp in Channels)
     {
         writer.Write(kvp.Key);
         kvp.Value.Serialize(writer);
     }
 }
                public void Serialize(UndertaleWriter writer)
                {
                    writer.Write(X);
                    writer.Write(Value);

                    if (writer.undertaleData.GMS2_3_1)
                    {
                        writer.Write(BezierX0);
                        writer.Write(BezierY0);
                        writer.Write(BezierX1);
                        writer.Write(BezierY1);
                    }
                    else
                    {
                        writer.Write(0);
                    }
                }
Пример #13
0
        public void Serialize(UndertaleWriter writer)
        {
            writer.WriteUndertaleString(Name);
            writer.Write((uint)Playback);
            writer.Write(PlaybackSpeed);
            writer.Write((uint)PlaybackSpeedType);
            writer.Write(Length);
            writer.Write(OriginX);
            writer.Write(OriginY);
            writer.Write(Volume);

            BroadcastMessages.Serialize(writer);

            Tracks.Serialize(writer);

            writer.Write(FunctionIDs.Count);
            foreach (KeyValuePair <int, UndertaleString> kvp in FunctionIDs)
            {
                writer.Write(kvp.Key);
                writer.WriteUndertaleString(kvp.Value);
            }

            Moments.Serialize(writer);
        }
        public void Serialize(UndertaleWriter writer)
        {
            writer.Write((uint)Count);
            foreach (T obj in this)
            {
                writer.WriteUndertaleObjectPointer <T>(obj);
            }
            for (int i = 0; i < Count; i++)
            {
                try
                {
                    (this[i] as UndertaleObjectWithBlobs)?.SerializeBlobBefore(writer);
                }
                catch (UndertaleSerializationException e)
                {
                    throw new UndertaleSerializationException(e.Message + "\nwhile writing blob for item " + (i + 1) + " of " + Count + " in a list of " + typeof(T).FullName, e);
                }
            }
            for (int i = 0; i < Count; i++)
            {
                try
                {
                    (this[i] as PrePaddedObject)?.SerializePrePadding(writer);

                    writer.WriteUndertaleObject <T>(this[i]);

                    // The last object does NOT get padding (TODO: at least in AUDO)
                    if (IndexOf(this[i]) != Count - 1)
                    {
                        (this[i] as PaddedObject)?.SerializePadding(writer);
                    }
                }
                catch (UndertaleSerializationException e)
                {
                    throw new UndertaleSerializationException(e.Message + "\nwhile writing item " + (i + 1) + " of " + Count + " in a list of " + typeof(T).FullName, e);
                }
            }
        }
Пример #15
0
        public void Serialize(UndertaleWriter writer)
        {
            switch (GetInstructionType(Kind))
            {
            case InstructionType.SingleTypeInstruction:
            case InstructionType.DoubleTypeInstruction:
            case InstructionType.ComparisonInstruction:
            {
                writer.Write(DupExtra);
                writer.Write((byte)ComparisonKind);
                byte TypePair = (byte)((byte)Type2 << 4 | (byte)Type1);
                writer.Write(TypePair);
                writer.Write((byte)Kind);
            }
            break;

            case InstructionType.GotoInstruction:
            {
                // TODO: see unserialize
                // TODO: why the hell is there exactly ONE number that was NOT encoded in a weird way? If you just rewrite the file with the 'fix' it differs one one byte
                uint JumpOffsetFixed = (uint)JumpOffset;
                JumpOffsetFixed &= ~0xFF800000;
                if (JumpOffsetIsWeird)
                {
                    JumpOffsetFixed |= 0x00800000;
                }
                writer.WriteInt24((int)JumpOffsetFixed);

                writer.Write((byte)Kind);
            }
            break;

            case InstructionType.PopInstruction:
            {
                writer.Write((short)TypeInst);
                byte TypePair = (byte)((byte)Type2 << 4 | (byte)Type1);
                writer.Write(TypePair);
                writer.Write((byte)Kind);
                writer.WriteUndertaleObject(Destination);
            }
            break;

            case InstructionType.PushInstruction:
            {
                if (Type1 == DataType.Int16)
                {
                    Debug.Assert(Value.GetType() == typeof(short));
                    writer.Write((short)Value);
                }
                else if (Type1 == DataType.Variable)
                {
                    writer.Write((short)TypeInst);
                }
                else
                {
                    writer.Write((short)0);
                }
                writer.Write((byte)Type1);
                writer.Write((byte)Kind);
                switch (Type1)
                {
                case DataType.Double:
                    Debug.Assert(Value.GetType() == typeof(double));
                    writer.Write((double)Value);
                    break;

                case DataType.Float:
                    Debug.Assert(Value.GetType() == typeof(float));
                    writer.Write((float)Value);
                    break;

                case DataType.Int32:
                    Debug.Assert(Value.GetType() == typeof(int));
                    writer.Write((int)Value);
                    break;

                case DataType.Int64:
                    Debug.Assert(Value.GetType() == typeof(long));
                    writer.Write((long)Value);
                    break;

                case DataType.Boolean:
                    Debug.Assert(Value.GetType() == typeof(bool));
                    writer.Write((bool)Value ? 1 : 0);
                    break;

                case DataType.Variable:
                    Debug.Assert(Value.GetType() == typeof(Reference <UndertaleVariable>));
                    writer.WriteUndertaleObject((Reference <UndertaleVariable>)Value);
                    break;

                case DataType.String:
                    Debug.Assert(Value.GetType() == typeof(UndertaleResourceById <UndertaleString>));
                    UndertaleResourceById <UndertaleString> str = (UndertaleResourceById <UndertaleString>)Value;
                    writer.Write(str.Serialize(writer));
                    break;

                case DataType.Int16:
                    break;
                }
            }
            break;

            case InstructionType.CallInstruction:
            {
                writer.Write(ArgumentsCount);
                writer.Write((byte)Type1);
                writer.Write((byte)Kind);
                writer.WriteUndertaleObject(Function);
            }
            break;

            case InstructionType.BreakInstruction:
            {
                Debug.Assert(Value.GetType() == typeof(short));
                writer.Write((short)Value);
                writer.Write((byte)Type1);
                writer.Write((byte)Kind);
            }
            break;

            default:
                throw new IOException("Unknown opcode " + Kind.ToString().ToUpper());
            }
        }
Пример #16
0
 public void Serialize(UndertaleWriter writer)
 {
     writer.Write(X);
     writer.Write(Y);
     writer.Write(Speed);
 }
Пример #17
0
 public void Serialize(UndertaleWriter writer)
 {
     writer.Write(ID);
     Tags.Serialize(writer);
 }
 public void Serialize(UndertaleWriter writer)
 {
     if (NewFormat)
     {
         writer.Write(Unknown1);
         writer.Write(Unknown2);
         writer.Write((ulong)Info);
         writer.Write(Scale);
         writer.Write(WindowColor);
         writer.Write(ColorDepth);
         writer.Write(Resolution);
         writer.Write(Frequency);
         writer.Write(VertexSync);
         writer.Write(Priority);
         writer.WriteUndertaleObject(BackImage);
         writer.WriteUndertaleObject(FrontImage);
         writer.WriteUndertaleObject(LoadImage);
         writer.Write(LoadAlpha);
         writer.WriteUndertaleObject(Constants);
     }
     else
     {
         writer.Write((Info & OptionsFlags.FullScreen) == OptionsFlags.FullScreen);
         writer.Write((Info & OptionsFlags.InterpolatePixels) == OptionsFlags.InterpolatePixels);
         writer.Write((Info & OptionsFlags.UseNewAudio) == OptionsFlags.UseNewAudio);
         writer.Write((Info & OptionsFlags.NoBorder) == OptionsFlags.NoBorder);
         writer.Write((Info & OptionsFlags.ShowCursor) == OptionsFlags.ShowCursor);
         writer.Write(Scale);
         writer.Write((Info & OptionsFlags.Sizeable) == OptionsFlags.Sizeable);
         writer.Write((Info & OptionsFlags.StayOnTop) == OptionsFlags.StayOnTop);
         writer.Write(WindowColor);
         writer.Write((Info & OptionsFlags.ChangeResolution) == OptionsFlags.ChangeResolution);
         writer.Write(ColorDepth);
         writer.Write(Resolution);
         writer.Write(Frequency);
         writer.Write((Info & OptionsFlags.NoButtons) == OptionsFlags.NoButtons);
         writer.Write(VertexSync);
         writer.Write((Info & OptionsFlags.ScreenKey) == OptionsFlags.ScreenKey);
         writer.Write((Info & OptionsFlags.HelpKey) == OptionsFlags.HelpKey);
         writer.Write((Info & OptionsFlags.QuitKey) == OptionsFlags.QuitKey);
         writer.Write((Info & OptionsFlags.SaveKey) == OptionsFlags.SaveKey);
         writer.Write((Info & OptionsFlags.ScreenShotKey) == OptionsFlags.ScreenShotKey);
         writer.Write((Info & OptionsFlags.CloseSec) == OptionsFlags.CloseSec);
         writer.Write(Priority);
         writer.Write((Info & OptionsFlags.Freeze) == OptionsFlags.Freeze);
         writer.Write((Info & OptionsFlags.ShowProgress) == OptionsFlags.ShowProgress);
         writer.WriteUndertaleObject(BackImage);
         writer.WriteUndertaleObject(FrontImage);
         writer.WriteUndertaleObject(LoadImage);
         writer.Write((Info & OptionsFlags.LoadTransparent) == OptionsFlags.LoadTransparent);
         writer.Write(LoadAlpha);
         writer.Write((Info & OptionsFlags.ScaleProgress) == OptionsFlags.ScaleProgress);
         writer.Write((Info & OptionsFlags.DisplayErrors) == OptionsFlags.DisplayErrors);
         writer.Write((Info & OptionsFlags.WriteErrors) == OptionsFlags.WriteErrors);
         writer.Write((Info & OptionsFlags.AbortErrors) == OptionsFlags.AbortErrors);
         writer.Write((Info & OptionsFlags.VariableErrors) == OptionsFlags.VariableErrors);
         writer.Write((Info & OptionsFlags.CreationEventOrder) == OptionsFlags.CreationEventOrder);
         writer.WriteUndertaleObject(Constants);
     }
 }
        } = new byte[16];                                        // more high entropy data

        public void Serialize(UndertaleWriter writer)
        {
            writer.Write(DisableDebugger ? (byte)1 : (byte)0);
            writer.Write(BytecodeVersion);
            writer.Write(Unknown);
            writer.WriteUndertaleString(Filename);
            writer.WriteUndertaleString(Config);
            writer.Write(LastObj);
            writer.Write(LastTile);
            writer.Write(GameID);
            writer.Write(Unknown1);
            writer.Write(Unknown2);
            writer.Write(Unknown3);
            writer.Write(Unknown4);
            writer.WriteUndertaleString(Name);
            writer.Write(Major);
            writer.Write(Minor);
            writer.Write(Release);
            writer.Write(Build);
            writer.Write(DefaultWindowWidth);
            writer.Write(DefaultWindowHeight);
            writer.Write((uint)Info);
            if (LicenseMD5.Length != 16)
            {
                throw new IOException("LicenseMD5 has invalid length");
            }
            writer.Write(LicenseMD5);
            writer.Write(LicenseCRC32);
            writer.Write(Timestamp);
            writer.WriteUndertaleString(DisplayName);
            writer.Write(ActiveTargets1);
            writer.Write(ActiveTargets2);
            writer.Write(FunctionClassifications1);
            writer.Write(FunctionClassifications2);
            writer.Write(SteamAppID);
            writer.Write(DebuggerPort);
            writer.WriteUndertaleObject(RoomOrder);
            if (Major >= 2)
            {
                if (GMS2RandomUID.Length != 40)
                {
                    throw new IOException("GMS2RandomUID has invalid length");
                }
                writer.Write(GMS2RandomUID);
                writer.Write(GMS2FPS);
                if (GMS2GameGUID.Length != 16)
                {
                    throw new IOException("GMS2GameGUID has invalid length");
                }
                writer.Write(GMS2AllowStatistics);
                writer.Write(GMS2GameGUID);
            }
        }
Пример #20
0
    /// <inheritdoc />
    public void Serialize(UndertaleWriter writer)
    {
        writer.WriteUndertaleString(Name);
        writer.WriteUndertaleString(DisplayName);
        if (EmSizeIsFloat)
        {
            // cast to a float and negate.
            writer.Write(0.0f - EmSize);
        }
        else
        {
            // pre-GMS2.3
            writer.Write(EmSize);
        }

        writer.Write(Bold);
        writer.Write(Italic);
        writer.Write(RangeStart);
        writer.Write(Charset);
        writer.Write(AntiAliasing);
        writer.Write(RangeEnd);
        writer.WriteUndertaleObjectPointer(Texture);
        writer.Write(ScaleX);
        writer.Write(ScaleY);
        if (writer.undertaleData.GeneralInfo?.BytecodeVersion >= 17)
        {
            writer.Write(AscenderOffset);
        }
        if (writer.undertaleData.GMS2022_2)
        {
            writer.Write(Ascender);
        }
        writer.WriteUndertaleObject(Glyphs);
    }
Пример #21
0
 public void Serialize(UndertaleWriter writer)
 {
     writer.Write(Value);
 }
Пример #22
0
 public override void Serialize(UndertaleWriter writer)
 {
     base.Serialize(writer);
     writer.Write((int)0);
     writer.Write(Mode);
 }
Пример #23
0
 /// <inheritdoc />
 public void Serialize(UndertaleWriter writer)
 {
     writer.Write(Other);
     writer.Write(Amount);
 }
Пример #24
0
 public void Serialize(UndertaleWriter writer)
 {
     writer.Write(Unknown1);
     writer.Write(Unknown2);
     writer.Write((ulong)Info);
     writer.Write(Scale);
     writer.Write(WindowColor);
     writer.Write(ColorDepth);
     writer.Write(Resolution);
     writer.Write(Frequency);
     writer.Write(VertexSync);
     writer.Write(Priority);
     writer.WriteUndertaleObject(BackImage);
     writer.WriteUndertaleObject(FrontImage);
     writer.WriteUndertaleObject(LoadImage);
     writer.Write(LoadAlpha);
     writer.WriteUndertaleObject(Constants);
 }
Пример #25
0
 /// <inheritdoc />
 public void Serialize(UndertaleWriter writer)
 {
     writer.Write((uint)Data.Length);
     writer.Write(Data);
 }
Пример #26
0
 public void Serialize(UndertaleWriter writer)
 {
     NextOccurrenceOffset = 0xdead;
     writer.WriteInt24(NextOccurrenceOffset);
     writer.Write((byte)Type);
 }
Пример #27
0
        } = new byte[16];                                        // more high entropy data

        public void Serialize(UndertaleWriter writer)
        {
            writer.Write(DisableDebugger ? (byte)1 : (byte)0);
            writer.Write(BytecodeVersion);
            writer.Write(Unknown);
            writer.WriteUndertaleString(Filename);
            writer.WriteUndertaleString(Config);
            writer.Write(LastObj);
            writer.Write(LastTile);
            writer.Write(GameID);
            writer.Write(DirectPlayGuid.ToByteArray());
            writer.WriteUndertaleString(Name);
            writer.Write(Major);
            writer.Write(Minor);
            writer.Write(Release);
            writer.Write(Build);
            writer.Write(DefaultWindowWidth);
            writer.Write(DefaultWindowHeight);
            writer.Write((uint)Info);
            if (LicenseMD5.Length != 16)
            {
                throw new IOException("LicenseMD5 has invalid length");
            }
            writer.Write(LicenseMD5);
            writer.Write(LicenseCRC32);
            writer.Write(Timestamp);
            writer.WriteUndertaleString(DisplayName);

            // Should always be zero.
            writer.Write(ActiveTargets);

            // Very weird value. Decides if Runner should initialize certain subsystems.
            writer.Write((ulong)FunctionClassifications);

            writer.Write(SteamAppID);
            writer.Write(DebuggerPort);
            writer.WriteUndertaleObject(RoomOrder);
            if (Major >= 2)
            {
                // Write random UID
                Random random      = new Random((int)(Timestamp & 4294967295L));
                long   firstRandom = (long)random.Next() << 32 | (long)random.Next();
                long   infoNumber  = (long)(Timestamp - 1000);
                ulong  temp        = (ulong)infoNumber;
                temp = ((temp << 56 & 18374686479671623680UL) | (temp >> 8 & 71776119061217280UL) |
                        (temp << 32 & 280375465082880UL) | (temp >> 16 & 1095216660480UL) | (temp << 8 & 4278190080UL) |
                        (temp >> 24 & 16711680UL) | (temp >> 16 & 65280UL) | (temp >> 32 & 255UL));
                infoNumber  = (long)temp;
                infoNumber ^= firstRandom;
                infoNumber  = ~infoNumber;
                infoNumber ^= ((long)GameID << 32 | (long)GameID);
                infoNumber ^= ((long)(DefaultWindowWidth + (int)Info) << 48 |
                               (long)(DefaultWindowHeight + (int)Info) << 32 |
                               (long)(DefaultWindowHeight + (int)Info) << 16 |
                               (long)(DefaultWindowWidth + (int)Info));
                infoNumber ^= BytecodeVersion;
                int infoLocation = Math.Abs((int)((int)(Timestamp & 65535L) / 7 + (GameID - DefaultWindowWidth) + RoomOrder.Count)) % 4;
                GMS2RandomUID.Clear();
                writer.Write(firstRandom);
                GMS2RandomUID.Add(firstRandom);
                for (int i = 0; i < 4; i++)
                {
                    if (i == infoLocation)
                    {
                        writer.Write(infoNumber);
                        GMS2RandomUID.Add(infoNumber);
                    }
                    else
                    {
                        int first  = random.Next();
                        int second = random.Next();
                        writer.Write(first);
                        writer.Write(second);
                        GMS2RandomUID.Add(((long)first << 32) | (long)second);
                    }
                }
                writer.Write(GMS2FPS);
                if (GMS2GameGUID.Length != 16)
                {
                    throw new IOException("GMS2GameGUID has invalid length");
                }
                writer.Write(GMS2AllowStatistics);
                writer.Write(GMS2GameGUID);
            }
        }
Пример #28
0
 public override void Serialize(UndertaleWriter writer)
 {
     writer.Write(Value);
     base.Serialize(writer);
 }
 public void Serialize(UndertaleWriter writer)
 {
     writer.Write((uint)Type);
 }
Пример #30
0
            public void Serialize(UndertaleWriter writer)
            {
                writer.WriteUndertaleString(ModelName);
                writer.WriteUndertaleString(Name);
                writer.Write(BuiltinName);
                writer.Write(Traits);
                writer.Write(IsCreationTrack);

                writer.Write(Tags.Count);
                writer.Write(OwnedResources.Count);
                writer.Write(Tracks.Count);

                foreach (int i in Tags)
                {
                    writer.Write(i);
                }

                foreach (UndertaleResource res in OwnedResources)
                {
                    if (res is UndertaleAnimationCurve)
                    {
                        writer.WriteUndertaleString(GMAnimCurveString);
                        res.Serialize(writer);
                    }
                    else
                    {
                        throw new IOException("Expected an animation curve");
                    }
                }

                foreach (Track t in Tracks)
                {
                    writer.WriteUndertaleObject(t);
                }

                // Now, handle specific keyframe/etc. data
                switch (ModelName.Content)
                {
                case "GMAudioTrack":
                    writer.WriteUndertaleObject(Keyframes as AudioKeyframes);
                    break;

                case "GMInstanceTrack":
                    writer.WriteUndertaleObject(Keyframes as InstanceKeyframes);
                    break;

                case "GMGraphicTrack":
                    writer.WriteUndertaleObject(Keyframes as GraphicKeyframes);
                    break;

                case "GMSequenceTrack":
                    writer.WriteUndertaleObject(Keyframes as SequenceKeyframes);
                    break;

                case "GMSpriteFramesTrack":
                    writer.WriteUndertaleObject(Keyframes as SpriteFramesKeyframes);
                    break;

                case "GMAssetTrack":     // TODO?
                    throw new NotImplementedException("GMAssetTrack not implemented, report this");

                case "GMBoolTrack":
                    writer.WriteUndertaleObject(Keyframes as BoolKeyframes);
                    break;

                case "GMStringTrack":
                    writer.WriteUndertaleObject(Keyframes as StringKeyframes);
                    break;

                // TODO?
                //case "GMIntTrack":
                //  writer.WriteUndertaleObject(Keyframes as IntKeyframes);
                //  break;
                case "GMRealTrack":
                    writer.WriteUndertaleObject(Keyframes as RealKeyframes);
                    break;
                }
            }