diff --git a/HackSources/Ghosts/ghost_loop.asm b/HackSources/Ghosts/ghost_loop.asm index 6566928f7..408c4968d 100644 --- a/HackSources/Ghosts/ghost_loop.asm +++ b/HackSources/Ghosts/ghost_loop.asm @@ -121,30 +121,40 @@ sw t1, 0x24 (RegCurrentGhost) lw t1, 0x08 (t0) sw t1, 0x28 (RegCurrentGhost) -; angles (TODO: store angles as s16 in file?) -lw t1, 0x10 (t0) +; angles +lh t1, 0x10 (t0) sh t1, 0x1A (RegCurrentGhost) -lw t1, 0x14 (t0) +lh t1, 0x12 (t0) sh t1, 0x1C (RegCurrentGhost) -lw t1, 0x18 (t0) +lh t1, 0x14 (t0) sh t1, 0x1E (RegCurrentGhost) +lw t1, 0x18 (t0) ; "graphics" in STROOP +lw t3, 0x0C (t0) ; "animation" in STROOP +bnez t1, @@TREAT_AS_OBJECT +lh t2, 0x16 (t0) ; animation frame ; do the hacky thing with Mario animations lui t8, 0x8037 sw RegCurrentGhost, 0x0580 (t8) sw RegAnimationBuffer, 0x05C0 (t8) -lh t1, 0x1C (t0) -sh t1, 0x38 (SP) +sh t2, 0x38 (SP) ori a0, t8, 0x04F8 sh r0, 0x38 (RegCurrentGhost) sw r0, 0x05BC (t8) jal set_mario_animation lw a1, 0xC (t0) -lh t1, 0x38 (SP) -sh t1, 0x40 (RegCurrentGhost) +lh t2, 0x38 (SP) +lui t1, 0x800F +ori t1, t1, 0x0860 +beq r0, r0, @@COMMON_SETTERS +addiu RegAnimationBuffer, RegAnimationBuffer, AnimationBufferSize +@@TREAT_AS_OBJECT: +sw t3, 0x3C (RegCurrentGhost) +@@COMMON_SETTERS: +sw t1, 0x14 (RegCurrentGhost) +sh t2, 0x40 (RegCurrentGhost) ; move on to next ghost -addiu RegAnimationBuffer, RegAnimationBuffer, AnimationBufferSize addiu RegPointerToCurrentGhost, RegPointerToCurrentGhost, NegativeGhostStructSize addiu RegProcessedGhostCount, RegProcessedGhostCount, 0x1 lui at, ExtendedRAMStartHi diff --git a/STROOP/Resources/Hacks/GhostHackJP.hck b/STROOP/Resources/Hacks/GhostHackJP.hck index df2629e62..30e1de1da 100644 --- a/STROOP/Resources/Hacks/GhostHackJP.hck +++ b/STROOP/Resources/Hacks/GhostHackJP.hck @@ -1,6 +1,6 @@ 8027ABD8: 0C 10 20 00 -80408000: 27 BD FF C0 3C 08 80 36 8D 08 FD E8 10 08 00 6B AF BF 00 34 AF B4 00 30 AF B3 00 2C AF B2 00 28 AF B1 00 24 AF B0 00 20 00 08 A0 25 86 88 00 02 31 09 00 40 15 20 00 06 35 09 00 40 A6 89 00 02 10 00 00 58 3C 11 80 40 10 00 00 53 36 31 7F F8 3C 13 80 50 3C 18 80 37 34 01 00 BD A7 01 05 A8 37 01 05 B8 AF 01 05 98 3C 01 80 06 24 21 40 40 AF 01 05 B8 00 00 80 25 3C 01 80 40 34 31 7F F8 80 21 7F FF 10 01 00 44 00 00 00 00 8E 28 00 00 15 00 00 0E 00 00 20 25 26 25 FF 9C 8E 86 00 14 3C 07 80 38 34 E1 5F DC AF A1 00 10 34 E1 5F E4 AF A1 00 14 0C 0D EE 78 34 E7 5F D0 AE 22 00 00 8E 84 00 0C 0C 0D F0 11 00 40 28 25 8E 32 00 00 82 89 00 18 A2 49 00 18 8E 89 00 38 AE 49 00 38 3C 01 80 33 8C 28 C6 94 31 08 00 7F 00 08 41 40 00 10 4B 00 01 09 40 21 3C 01 80 41 01 01 40 21 25 08 9B 00 8D 09 00 00 AE 49 00 20 8D 09 00 04 AE 49 00 24 8D 09 00 08 AE 49 00 28 8D 09 00 10 A6 49 00 1A 8D 09 00 14 A6 49 00 1C 8D 09 00 18 A6 49 00 1E 3C 18 80 37 AF 12 05 80 AF 13 05 C0 85 09 00 1C A7 A9 00 38 37 04 04 F8 A6 40 00 38 AF 00 05 BC 0C 09 41 FA 8D 05 00 0C 87 A9 00 38 A6 49 00 40 26 73 40 00 26 31 FF 98 26 10 00 01 3C 01 80 40 80 21 7F FF 02 01 40 2B 15 00 FF C3 A2 50 00 60 8E 32 00 00 12 40 00 06 00 12 20 25 0C 0D F0 2F AE 20 00 00 8E 24 00 00 14 80 FF FC 26 31 FF 98 8F BF 00 34 8F B4 00 30 8F B3 00 2C 8F B2 00 28 8F B1 00 24 8F B0 00 20 03 E0 00 08 27 BD 00 40 +80408000: 27 BD FF C0 3C 08 80 36 8D 08 FD E8 10 08 00 73 AF BF 00 34 AF B4 00 30 AF B3 00 2C AF B2 00 28 AF B1 00 24 AF B0 00 20 00 08 A0 25 86 88 00 02 31 09 00 40 15 20 00 06 35 09 00 40 A6 89 00 02 10 00 00 60 3C 11 80 40 10 00 00 5B 36 31 7F F8 3C 13 80 50 3C 18 80 37 34 01 00 BD A7 01 05 A8 37 01 05 B8 AF 01 05 98 3C 01 80 06 24 21 40 40 AF 01 05 B8 00 00 80 25 3C 01 80 40 34 31 7F F8 80 21 7F FF 10 01 00 4C 00 00 00 00 8E 28 00 00 15 00 00 0E 00 00 20 25 26 25 FF 9C 8E 86 00 14 3C 07 80 38 34 E1 5F DC AF A1 00 10 34 E1 5F E4 AF A1 00 14 0C 0D EE 78 34 E7 5F D0 AE 22 00 00 8E 84 00 0C 0C 0D F0 11 00 40 28 25 8E 32 00 00 82 89 00 18 A2 49 00 18 8E 89 00 38 AE 49 00 38 3C 01 80 33 8C 28 C6 94 31 08 00 7F 00 08 41 40 00 10 4B 00 01 09 40 21 3C 01 80 41 01 01 40 21 25 08 9B 00 8D 09 00 00 AE 49 00 20 8D 09 00 04 AE 49 00 24 8D 09 00 08 AE 49 00 28 85 09 00 10 A6 49 00 1A 85 09 00 12 A6 49 00 1C 85 09 00 14 A6 49 00 1E 8D 09 00 18 8D 0B 00 0C 15 20 00 0F 85 0A 00 16 3C 18 80 37 AF 12 05 80 AF 13 05 C0 A7 AA 00 38 37 04 04 F8 A6 40 00 38 AF 00 05 BC 0C 09 41 FA 8D 05 00 0C 87 AA 00 38 3C 09 80 0F 35 29 08 60 10 00 00 02 26 73 40 00 AE 4B 00 3C AE 49 00 14 A6 4A 00 40 26 31 FF 98 26 10 00 01 3C 01 80 40 80 21 7F FF 02 01 40 2B 15 00 FF BB A2 50 00 60 8E 32 00 00 12 40 00 06 00 12 20 25 0C 0D F0 2F AE 20 00 00 8E 24 00 00 14 80 FF FC 26 31 FF 98 8F BF 00 34 8F B4 00 30 8F B3 00 2C 8F B2 00 28 8F B1 00 24 8F B0 00 20 03 E0 00 08 27 BD 00 40 80408200: 27 BD FF C0 AF BF 00 14 3C 09 04 01 35 28 19 A0 3C 01 80 36 8C 21 FD E8 3C 18 80 33 8F 18 CF A0 13 01 00 02 00 00 50 25 83 0A 00 60 35 29 19 78 AF A8 00 18 AF A9 00 1C AF AA 00 20 24 01 00 01 14 24 00 1B 34 04 00 38 0C 09 E2 5F 00 00 00 00 3C 0C 06 00 AC 4C 00 10 8F A1 00 18 AC 41 00 14 3C 01 03 88 34 21 00 10 AC 41 00 18 AC 41 00 00 3C 08 80 40 35 08 83 00 8F A9 00 20 00 09 49 40 01 28 50 21 AC 4A 00 1C AC 4A 00 04 3C 01 03 86 34 21 00 10 AC 41 00 20 AC 41 00 08 25 4B 00 08 AC 4B 00 24 AC 4B 00 0C AC 4C 00 28 8F A8 00 1C AC 48 00 2C 3C 01 B8 00 AC 41 00 30 8F BF 00 14 03 E0 00 08 27 BD 00 40 diff --git a/STROOP/Resources/Hacks/GhostHackUS.hck b/STROOP/Resources/Hacks/GhostHackUS.hck index d0067db58..ef1d83a59 100644 --- a/STROOP/Resources/Hacks/GhostHackUS.hck +++ b/STROOP/Resources/Hacks/GhostHackUS.hck @@ -1,6 +1,6 @@ 8027B188: 0C 10 20 00 -80408000: 27 BD FF C0 3C 08 80 36 8D 08 11 58 10 08 00 6B AF BF 00 34 AF B4 00 30 AF B3 00 2C AF B2 00 28 AF B1 00 24 AF B0 00 20 00 08 A0 25 86 88 00 02 31 09 00 40 15 20 00 06 35 09 00 40 A6 89 00 02 10 00 00 58 3C 11 80 40 10 00 00 53 36 31 7F F8 3C 13 80 50 3C 18 80 37 34 01 00 BD A7 01 05 A8 37 01 05 B8 AF 01 05 98 3C 01 80 06 24 21 40 40 AF 01 05 B8 00 00 80 25 3C 01 80 40 34 31 7F F8 80 21 7F FF 10 01 00 44 00 00 00 00 8E 28 00 00 15 00 00 0E 00 00 20 25 26 25 FF 9C 8E 86 00 14 3C 07 80 38 34 E1 5F DC AF A1 00 10 34 E1 5F E4 AF A1 00 14 0C 0D EE 78 34 E7 5F D0 AE 22 00 00 8E 84 00 0C 0C 0D F0 11 00 40 28 25 8E 32 00 00 82 89 00 18 A2 49 00 18 8E 89 00 38 AE 49 00 38 3C 01 80 33 8C 28 D5 D4 31 08 00 7F 00 08 41 40 00 10 4B 00 01 09 40 21 3C 01 80 41 01 01 40 21 25 08 9B 00 8D 09 00 00 AE 49 00 20 8D 09 00 04 AE 49 00 24 8D 09 00 08 AE 49 00 28 8D 09 00 10 A6 49 00 1A 8D 09 00 14 A6 49 00 1C 8D 09 00 18 A6 49 00 1E 3C 18 80 37 AF 12 05 80 AF 13 05 C0 85 09 00 1C A7 A9 00 38 37 04 04 F8 A6 40 00 38 AF 00 05 BC 0C 09 42 6E 8D 05 00 0C 87 A9 00 38 A6 49 00 40 26 73 40 00 26 31 FF 98 26 10 00 01 3C 01 80 40 80 21 7F FF 02 01 40 2B 15 00 FF C3 A2 50 00 60 8E 32 00 00 12 40 00 06 00 12 20 25 0C 0D F0 2F AE 20 00 00 8E 24 00 00 14 80 FF FC 26 31 FF 98 8F BF 00 34 8F B4 00 30 8F B3 00 2C 8F B2 00 28 8F B1 00 24 8F B0 00 20 03 E0 00 08 27 BD 00 40 +80408000: 27 BD FF C0 3C 08 80 36 8D 08 11 58 10 08 00 73 AF BF 00 34 AF B4 00 30 AF B3 00 2C AF B2 00 28 AF B1 00 24 AF B0 00 20 00 08 A0 25 86 88 00 02 31 09 00 40 15 20 00 06 35 09 00 40 A6 89 00 02 10 00 00 60 3C 11 80 40 10 00 00 5B 36 31 7F F8 3C 13 80 50 3C 18 80 37 34 01 00 BD A7 01 05 A8 37 01 05 B8 AF 01 05 98 3C 01 80 06 24 21 40 40 AF 01 05 B8 00 00 80 25 3C 01 80 40 34 31 7F F8 80 21 7F FF 10 01 00 4C 00 00 00 00 8E 28 00 00 15 00 00 0E 00 00 20 25 26 25 FF 9C 8E 86 00 14 3C 07 80 38 34 E1 5F DC AF A1 00 10 34 E1 5F E4 AF A1 00 14 0C 0D EE 78 34 E7 5F D0 AE 22 00 00 8E 84 00 0C 0C 0D F0 11 00 40 28 25 8E 32 00 00 82 89 00 18 A2 49 00 18 8E 89 00 38 AE 49 00 38 3C 01 80 33 8C 28 D5 D4 31 08 00 7F 00 08 41 40 00 10 4B 00 01 09 40 21 3C 01 80 41 01 01 40 21 25 08 9B 00 8D 09 00 00 AE 49 00 20 8D 09 00 04 AE 49 00 24 8D 09 00 08 AE 49 00 28 85 09 00 10 A6 49 00 1A 85 09 00 12 A6 49 00 1C 85 09 00 14 A6 49 00 1E 8D 09 00 18 8D 0B 00 0C 15 20 00 0F 85 0A 00 16 3C 18 80 37 AF 12 05 80 AF 13 05 C0 A7 AA 00 38 37 04 04 F8 A6 40 00 38 AF 00 05 BC 0C 09 42 6E 8D 05 00 0C 87 AA 00 38 3C 09 80 0F 35 29 08 60 10 00 00 02 26 73 40 00 AE 4B 00 3C AE 49 00 14 A6 4A 00 40 26 31 FF 98 26 10 00 01 3C 01 80 40 80 21 7F FF 02 01 40 2B 15 00 FF BB A2 50 00 60 8E 32 00 00 12 40 00 06 00 12 20 25 0C 0D F0 2F AE 20 00 00 8E 24 00 00 14 80 FF FC 26 31 FF 98 8F BF 00 34 8F B4 00 30 8F B3 00 2C 8F B2 00 28 8F B1 00 24 8F B0 00 20 03 E0 00 08 27 BD 00 40 80408200: 27 BD FF C0 AF BF 00 14 3C 09 04 01 35 28 19 A0 3C 01 80 36 8C 21 11 58 3C 18 80 33 8F 18 DF 00 13 01 00 02 00 00 50 25 83 0A 00 60 35 29 19 78 AF A8 00 18 AF A9 00 1C AF AA 00 20 24 01 00 01 14 24 00 1B 34 04 00 38 0C 09 E3 CB 00 00 00 00 3C 0C 06 00 AC 4C 00 10 8F A1 00 18 AC 41 00 14 3C 01 03 88 34 21 00 10 AC 41 00 18 AC 41 00 00 3C 08 80 40 35 08 83 00 8F A9 00 20 00 09 49 40 01 28 50 21 AC 4A 00 1C AC 4A 00 04 3C 01 03 86 34 21 00 10 AC 41 00 20 AC 41 00 08 25 4B 00 08 AC 4B 00 24 AC 4B 00 0C AC 4C 00 28 8F A8 00 1C AC 48 00 2C 3C 01 B8 00 AC 41 00 30 8F BF 00 14 03 E0 00 08 27 BD 00 40 diff --git a/STROOP/Tabs/GhostTab/Ghost.cs b/STROOP/Tabs/GhostTab/Ghost.cs index e077ba224..fd0539192 100644 --- a/STROOP/Tabs/GhostTab/Ghost.cs +++ b/STROOP/Tabs/GhostTab/Ghost.cs @@ -4,6 +4,7 @@ using OpenTK; using OpenTK.Mathematics; using STROOP.Utilities; +using System.Diagnostics; namespace STROOP.Tabs.GhostTab { @@ -35,16 +36,21 @@ public GhostPositionAngle(Ghost g) : base() public void SetGlobalTimer(uint globalTimer) { GhostFrame newFrame; - if (g.playbackFrames.TryGetValue(globalTimer, out newFrame)) + if (g.frames.TryGetValue(globalTimer, out newFrame)) currentFrame = newFrame; } public override Vector4 GetArrowColor(Vector4 baseColor) => color; } + public record struct PlaybackFrame(GhostFrame frame, uint animation); + + const uint OBJECT_EXTRA_MAGIC = 0x4F424A54; + public uint playbackBaseFrame = 0; - public Dictionary playbackFrames = new Dictionary(); - public GhostFrame lastValidPlaybackFrame, currentFrame; + public Dictionary frames = new Dictionary(); + public PlaybackFrame lastValidPlaybackFrame; + public GhostFrame currentFrame; public uint originalPlaybackBaseFrame { get; private set; } public uint maxFrame { get; private set; } public uint numFrames => maxFrame + 1; @@ -53,15 +59,19 @@ public void SetGlobalTimer(uint globalTimer) public GhostPositionAngle positionAngle { get; private set; } public bool transparent = true; + /// The graphics pointer to pass to the hack - 0 is interpreted as "Mario" by the hack. + public uint nonMarioGraphics = 0; + public Dictionary animationSwitches = new(); + public Ghost() { positionAngle = new GhostPositionAngle(this); } - public Ghost(uint playbackBaseFrame, Dictionary playbackFrames) + public Ghost(uint playbackBaseFrame, Dictionary frames) { this.playbackBaseFrame = originalPlaybackBaseFrame = playbackBaseFrame; - this.playbackFrames = playbackFrames; + this.frames = frames; } public static Ghost FromFile(BinaryReader reader) @@ -75,9 +85,21 @@ public static Ghost FromFile(BinaryReader reader) { var index = reader.ReadUInt32(); var frame = GhostFrame.ReadFrom(reader); - result.playbackFrames[index] = frame; + result.frames[index] = frame; result.maxFrame = Math.Max(result.maxFrame, index); } + + if (reader.BaseStream.Position > reader.BaseStream.Length - 4 || reader.ReadUInt32() != OBJECT_EXTRA_MAGIC) + return result; + + result.nonMarioGraphics = reader.ReadUInt32(); + var numAnimationSwitches = reader.ReadUInt32(); + for (int i = 0; i < numAnimationSwitches; i++) + { + var key = reader.ReadUInt32(); + var value = reader.ReadUInt32(); + result.animationSwitches[key] = value; + } } catch (IOException) { @@ -86,6 +108,24 @@ public static Ghost FromFile(BinaryReader reader) return result; } + public void ToFile(BinaryWriter wr) + { + wr.Write(originalPlaybackBaseFrame); + wr.Write(frames.Count); + foreach (var frame in frames) + { + wr.Write(frame.Key); + frame.Value.WriteTo(wr); + } + + wr.Write(OBJECT_EXTRA_MAGIC); + wr.Write(animationSwitches.Count); + foreach (var kvp in animationSwitches) + { + wr.Write(kvp.Key); + wr.Write(kvp.Value); + } + } public override string ToString() { diff --git a/STROOP/Tabs/GhostTab/GhostTab.cs b/STROOP/Tabs/GhostTab/GhostTab.cs index a571e2f25..868f7fdc2 100644 --- a/STROOP/Tabs/GhostTab/GhostTab.cs +++ b/STROOP/Tabs/GhostTab/GhostTab.cs @@ -49,7 +49,6 @@ static void AddSpecialVariables() int lastGlobalTimer; Ghost selectedGhost => listBoxGhosts.SelectedItem as Ghost; - GhostFrame lastValidPlaybackFrame => selectedGhost?.lastValidPlaybackFrame ?? default(GhostFrame); public GhostTab() { @@ -101,22 +100,34 @@ public override void Update(bool active) int i = (tm + globalTimer) & 0x7F; GhostFrame newFrame = default(GhostFrame); var index = globalTimer + tm - ghost.playbackBaseFrame; - if (index >= 0 && ghost.playbackFrames.TryGetValue((uint)index, out newFrame)) - ghost.lastValidPlaybackFrame = newFrame; - - Array.Copy(BitConverter.GetBytes(ghost.lastValidPlaybackFrame.position.X), 0, buffer, i * 0x20 + 0x00, 4); - Array.Copy(BitConverter.GetBytes(ghost.lastValidPlaybackFrame.position.Y), 0, buffer, i * 0x20 + 0x04, 4); - Array.Copy(BitConverter.GetBytes(ghost.lastValidPlaybackFrame.position.Z), 0, buffer, i * 0x20 + 0x08, 4); - Array.Copy(BitConverter.GetBytes(ghost.lastValidPlaybackFrame.animationIndex), 0, buffer, i * 0x20 + 0x0C, 2); - Array.Copy(BitConverter.GetBytes(ghost.lastValidPlaybackFrame.oPitch), 0, buffer, i * 0x20 + 0x10, 4); - Array.Copy(BitConverter.GetBytes(ghost.lastValidPlaybackFrame.oYaw), 0, buffer, i * 0x20 + 0x14, 4); - Array.Copy(BitConverter.GetBytes(ghost.lastValidPlaybackFrame.oRoll), 0, buffer, i * 0x20 + 0x18, 4); - Array.Copy(BitConverter.GetBytes(ghost.lastValidPlaybackFrame.animationFrame), 0, buffer, i * 0x20 + 0x1E, 2); + if (index >= 0 && ghost.frames.TryGetValue((uint)index, out newFrame)) + ghost.lastValidPlaybackFrame = new( + newFrame, + ghost.animationSwitches.TryGetValue((uint)index, out var value) + ? value + : ghost.lastValidPlaybackFrame.animation == 0 + ? ghost.animationSwitches.OrderBy(x => x.Key).FirstOrDefault().Value + : ghost.lastValidPlaybackFrame.animation + ); + + (var frame, var animation) = ghost.lastValidPlaybackFrame; + animation = animation == 0 ? (uint)frame.animationIndex : animation; + + Array.Copy(BitConverter.GetBytes(frame.position.X), 0, buffer, i * 0x20 + 0x00, 4); + Array.Copy(BitConverter.GetBytes(frame.position.Y), 0, buffer, i * 0x20 + 0x04, 4); + Array.Copy(BitConverter.GetBytes(frame.position.Z), 0, buffer, i * 0x20 + 0x08, 4); + Array.Copy(BitConverter.GetBytes(animation), 0, buffer, i * 0x20 + 0x0C, 4); + Array.Copy(BitConverter.GetBytes(frame.oPitch), 0, buffer, i * 0x20 + 0x12, 2); + Array.Copy(BitConverter.GetBytes(frame.oYaw), 0, buffer, i * 0x20 + 0x10, 2); + Array.Copy(BitConverter.GetBytes(frame.oRoll), 0, buffer, i * 0x20 + 0x14, 2); + Array.Copy(BitConverter.GetBytes(ghost.nonMarioGraphics), 0, buffer, i * 0x20 + 0x18, 4); // Bob-omb demo + Array.Copy(BitConverter.GetBytes(animation), 0, buffer, i * 0x20 + 0x1C, 4); + Array.Copy(BitConverter.GetBytes(frame.animationFrame), 0, buffer, i * 0x20 + 0x16, 2); } GhostFrame currentFrame; var idx = (globalTimer - 1) - ghost.playbackBaseFrame; - if (idx >= 0 && ghost.playbackFrames.TryGetValue((uint)idx, out currentFrame)) + if (idx >= 0 && ghost.frames.TryGetValue((uint)idx, out currentFrame)) { ghost.positionAngle.SetGlobalTimer((uint)idx); ghost.currentFrame = currentFrame; @@ -152,10 +163,11 @@ public override void Update(bool active) Array.Copy(BitConverter.GetBytes(yTargetPosition + ghostIndex * 500 / numGhosts), 0, buffer, i * 0x20 + 0x04, 4); Array.Copy(BitConverter.GetBytes(z), 0, buffer, i * 0x20 + 0x08, 4); Array.Copy(BitConverter.GetBytes((ushort)0x2A), 0, buffer, i * 0x20 + 0x0C, 2); - Array.Copy(BitConverter.GetBytes((uint)0), 0, buffer, i * 0x20 + 0x10, 4); - Array.Copy(BitConverter.GetBytes((uint)(f * ushort.MaxValue + 0x8000)), 0, buffer, i * 0x20 + 0x14, 4); - Array.Copy(BitConverter.GetBytes(0xE800 + barrelRoll), 0, buffer, i * 0x20 + 0x18, 4); - Array.Copy(BitConverter.GetBytes((ushort)0), 0, buffer, i * 0x20 + 0x1E, 2); + Array.Copy(BitConverter.GetBytes((ushort)0), 0, buffer, i * 0x20 + 0x12, 2); + Array.Copy(BitConverter.GetBytes((ushort)(f * ushort.MaxValue + 0x8000)), 0, buffer, i * 0x20 + 0x10, 2); + Array.Copy(BitConverter.GetBytes((ushort)(0xE800 + barrelRoll)), 0, buffer, i * 0x20 + 0x14, 2); + Array.Copy(BitConverter.GetBytes(0), 0, buffer, i * 0x20 + 0x18, 4); + Array.Copy(BitConverter.GetBytes((ushort)(globalTimer % 32)), 0, buffer, i * 0x20 + 0x16, 2); } } @@ -315,21 +327,9 @@ private void buttonSaveGhost_Click(object sender, EventArgs e) } ); - foreach (var fn in ghostFileNames) - using (var wr = new BinaryWriter(new FileStream(fn.Item2, FileMode.Create))) - { - int missedFrameCount = 0; - uint lastFrame = 0; - wr.Write(fn.Item1.originalPlaybackBaseFrame); - wr.Write(fn.Item1.playbackFrames.Count); - foreach (var frame in fn.Item1.playbackFrames) - { - missedFrameCount += (int)(frame.Key - lastFrame - 1); - lastFrame = frame.Key; - wr.Write(frame.Key); - frame.Value.WriteTo(wr); - } - } + foreach (var (ghost, fileName) in ghostFileNames) + using (var wr = new BinaryWriter(new FileStream(fileName, FileMode.Create))) + ghost.ToFile(wr); } }