public static Ghost FromFile(BinaryReader reader) { Ghost result = new Ghost(); try { result.playbackBaseFrame = result.originalPlaybackBaseFrame = reader.ReadUInt32(); int numFrames = reader.ReadInt32(); for (int i = 0; i < numFrames; i++) { var index = reader.ReadUInt32(); var frame = GhostFrame.ReadFrom(reader); result.playbackFrames[index] = frame; result.maxFrame = Math.Max(result.maxFrame, index); } } catch (IOException) { return(null); } return(result); }
void RecordGhostThread() { var sleepTime = new TimeSpan(100000); //0.1ms bool hasBaseFrame = false; GhostFrame lastRecorded = default(GhostFrame); int currentTimer = Config.Stream.GetInt32(MiscConfig.GlobalTimerAddress); recordingFrames = new Dictionary <uint, GhostFrame>(); int lastCurrentTimer = 0; Config.Stream.WriteRam(new byte[] { 0xFF, 0xFF, 0xFF, 0xFF }, 0x80407FF4, EndiannessType.Little); Config.Stream.WriteRam(new byte[4], 0x80407FF8, EndiannessType.Little); while (recordingFrames != null) { boring :; System.Threading.Thread.Sleep(sleepTime); byte[] theEntireRam; Config.Stream.GetAllRam(out theEntireRam); var semaphoreState = BitConverter.ToInt32(theEntireRam, 0x00407FF8); //Wait for the game to notify us that it is about to increase global timer //The game will loop until we're done reading data, at which point we will reset the semaphore switch (semaphoreState) { case 0: continue; case 1: currentTimer = BitConverter.ToInt32(theEntireRam, (int)(MiscConfig.GlobalTimerAddress & 0x00FFFFFF)); if (currentTimer == lastCurrentTimer) { goto boring; } uint marioObjRef = BitConverter.ToUInt32(theEntireRam, (int)(MarioObjectConfig.PointerAddress & 0x00FFFFFF)) & 0x00FFFFFF; int baseOffsetYeah = (int)(marioObjRef + 0x18); //marioGfx = Config.Stream.ReadRam(marioObjRef + 0x18, 0x34, EndiannessType.Little); if (!hasBaseFrame) { hasBaseFrame = true; recordingBaseFrame = (uint)currentTimer; lastCurrentTimer = currentTimer; } lastCurrentTimer = currentTimer; short animation = BitConverter.ToInt16(theEntireRam, baseOffsetYeah + (int)MarioObjectConfig.AnimationOffset - 0x16); // Config.Stream.GetInt16(marioObjRef + MarioObjectConfig.AnimationOffset); short animationFrame = BitConverter.ToInt16(theEntireRam, baseOffsetYeah + (int)MarioObjectConfig.AnimationTimerOffset - 0x16); // Config.Stream.GetInt16(marioObjRef + MarioObjectConfig.AnimationTimerOffset); var position = new Vector3( BitConverter.ToSingle(theEntireRam, baseOffsetYeah + 0x08), //x BitConverter.ToSingle(theEntireRam, baseOffsetYeah + 0x0C), //y BitConverter.ToSingle(theEntireRam, baseOffsetYeah + 0x10) //z ); lastRecorded = new GhostFrame() { position = position, animationIndex = animation, animationFrame = animationFrame, oPitch = (uint)BitConverter.ToUInt16(theEntireRam, baseOffsetYeah + 0x0), oYaw = (uint)BitConverter.ToUInt16(theEntireRam, baseOffsetYeah + 0x6), oRoll = (uint)BitConverter.ToUInt16(theEntireRam, baseOffsetYeah + 0x4) }; if (recordingFrames != null) { recordingFrames[(uint)(currentTimer - recordingBaseFrame)] = lastRecorded; } System.Threading.Thread.Sleep(sleepTime); while (!Config.Stream.WriteRam(new byte[4], 0x80407FF8, EndiannessType.Little)) { System.Threading.Thread.Sleep(sleepTime); } break; } } Config.Stream.WriteRam(new byte[8], 0x80407FF4, EndiannessType.Little); }
public override void Update(bool active) { base.Update(active); var buffer = new byte[0x1000]; var ghostPointer = Config.Stream.GetInt32(0x80407FFC); hack.UpdateEnabledStatus(); labelHackActiveState.Text = (ghostPointer & 0xFF000000) == 0x80000000 && hack.Enabled ? "Ghost hack is enabled." : (hack.Enabled ? "Ghost hack is enabled\nbut not running.\nInside a level,\nsave state and load state,\nthen frame advance." : "Ghost hack is disabled."); var globalTimer = Config.Stream.GetInt32(MiscConfig.GlobalTimerAddress); if (selectedGhost != null) { for (int tm = 0; tm < 0x80; tm++) { int i = (tm + globalTimer) & 0x7F; GhostFrame newFrame = default(GhostFrame); var index = globalTimer + tm - selectedGhost.playbackBaseFrame; if (index >= 0 && selectedGhost.playbackFrames.TryGetValue((uint)index, out newFrame)) { selectedGhost.lastValidPlaybackFrame = newFrame; } Array.Copy(BitConverter.GetBytes(lastValidPlaybackFrame.position.X), 0, buffer, i * 0x20 + 0x00, 4); Array.Copy(BitConverter.GetBytes(lastValidPlaybackFrame.position.Y), 0, buffer, i * 0x20 + 0x04, 4); Array.Copy(BitConverter.GetBytes(lastValidPlaybackFrame.position.Z), 0, buffer, i * 0x20 + 0x08, 4); Array.Copy(BitConverter.GetBytes(lastValidPlaybackFrame.animationIndex), 0, buffer, i * 0x20 + 0x0C, 2); Array.Copy(BitConverter.GetBytes(lastValidPlaybackFrame.oPitch), 0, buffer, i * 0x20 + 0x10, 4); Array.Copy(BitConverter.GetBytes(lastValidPlaybackFrame.oYaw), 0, buffer, i * 0x20 + 0x14, 4); Array.Copy(BitConverter.GetBytes(lastValidPlaybackFrame.oRoll), 0, buffer, i * 0x20 + 0x18, 4); Array.Copy(BitConverter.GetBytes(lastValidPlaybackFrame.animationFrame), 0, buffer, i * 0x20 + 0x1E, 2); } GhostFrame currentFrame; var idx = (globalTimer - 1) - selectedGhost.playbackBaseFrame; if (idx >= 0 && selectedGhost.playbackFrames.TryGetValue((uint)idx, out currentFrame)) { this.currentGhostFrame = currentFrame; GhostPosition = currentFrame.position; GhostAngle = currentFrame.oYaw; } } else { var marioObjRef = Config.Stream.GetUInt32(MarioObjectConfig.PointerAddress); Vector3 marioPosition = new Vector3(Config.Stream.GetSingle(marioObjRef + 0x20), Config.Stream.GetSingle(marioObjRef + 0x24), Config.Stream.GetSingle(marioObjRef + 0x28)); if (globalTimer < lastGlobalTimer) { yTargetPosition = marioPosition.Y; } else { yTargetPosition += (marioPosition.Y + 200 - yTargetPosition) * EaseIn((globalTimer - lastGlobalTimer) / 10.0f); } for (int tm = 0; tm < 0x80; tm++) { int i = (tm + globalTimer) & 0x7F; var index = globalTimer + tm; float f = (index % 60) / 60.0f; float angle = f * (float)Math.PI * 2; var x = marioPosition.X + (float)Math.Cos(angle) * 200; var z = marioPosition.Z + (float)Math.Sin(angle) * -200; int barrelRoll = 0; const int rollSpeed = 0x800; int indexMod150 = index % 150; if (indexMod150 < 0x10000 / rollSpeed) { barrelRoll = indexMod150 * -rollSpeed; } Array.Copy(BitConverter.GetBytes(x), 0, buffer, i * 0x20 + 0x00, 4); Array.Copy(BitConverter.GetBytes(yTargetPosition), 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); } } if (hack.Enabled) { Config.Stream.WriteRam(buffer, bufferBaseAddress, EndiannessType.Little); lastGlobalTimer = globalTimer; } }