From a6ced95e07493bc8c0c4d409db226116c4dbc20c Mon Sep 17 00:00:00 2001 From: "phillip.grimsrud" Date: Mon, 18 Jun 2012 01:36:38 +0000 Subject: [PATCH] 1. Fixed tastudio memory leak. This was accomplished by dropping the oldest saved stats after hitting a size limit (1 GB). 2. Changed StopOnEnd to StopOnFrame. This expands the functionality so that you can tell the emulator to run and then stop at a predetermined point. 3. Expanded the functionality of RewindToFrame to handle more cases. Now you can go back or even forward to a frame and it handles the execution and greenzone appropriately. 4. Due to the change in structure some code was changed to check the index of the first and last saved states in the greenzone rather than relying on the size of the saved state list. 5. Changed the list of saved states in the movie log from a list of byte[] to a list of structures (the structure has an int for the index and byte[] for the state). 6. Saved an init state in the movie log. This is used to go back to the beginning if the beginning of the movie is no longer in the list of saved states. 7. Expanded the AddState and SetFrameAt logic in the movie log to account for the fact that the size of the saved state list is now capped. 8. Fixed a bug in the log interpretation for SMS. 9. Fixed a bug in the sms virtual controller, buttons 1 and 2 were hooked to the wrong objects. 10. Fixed the tastudio listview to show lag as pink. --- BizHawk.MultiClient/MainForm.cs | 22 +-- BizHawk.MultiClient/movie/Movie.cs | 70 +++++++--- BizHawk.MultiClient/movie/MovieLog.cs | 127 ++++++++++++------ .../tools/TAStudio.Designer.cs | 2 +- BizHawk.MultiClient/tools/TAStudio.cs | 25 ++-- BizHawk.MultiClient/tools/VirtualPadSMS.cs | 4 +- 6 files changed, 173 insertions(+), 77 deletions(-) diff --git a/BizHawk.MultiClient/MainForm.cs b/BizHawk.MultiClient/MainForm.cs index a3e6337776..8fd20e1716 100644 --- a/BizHawk.MultiClient/MainForm.cs +++ b/BizHawk.MultiClient/MainForm.cs @@ -32,7 +32,8 @@ namespace BizHawk.MultiClient public bool PressRewind = false; public bool FastForward = false; public bool TurboFastForward = false; - public bool StopOnEnd = true; + public int StopOnFrame = -1; + public bool RestoreReadWriteOnStop = false; public bool UpdateFrame = false; //avi/wav state @@ -1892,16 +1893,22 @@ namespace BizHawk.MultiClient session.LatchInputFromPlayer(Global.MovieInputSourceAdapter); } - if (Global.MovieSession.Movie.Mode == MOVIEMODE.PLAY) + if (-1 != StopOnFrame && StopOnFrame == Global.Emulator.Frame + 1) { - if (Global.MovieSession.Movie.LogLength() == Global.Emulator.Frame + 1 && true == StopOnEnd) + if(StopOnFrame == Global.MovieSession.Movie.LogLength()) { - if (true == Global.MovieSession.Movie.TastudioOn) - { - StopOnEnd = false; - } Global.MovieSession.Movie.SetMovieFinished(); } + if (true == Global.MovieSession.Movie.TastudioOn) + { + PauseEmulator(); + StopOnFrame = -1; + } + if(true == RestoreReadWriteOnStop) + { + Global.MovieSession.Movie.Mode = MOVIEMODE.RECORD; + RestoreReadWriteOnStop = false; + } } if (Global.MovieSession.Movie.Mode == MOVIEMODE.FINISHED) { @@ -1998,7 +2005,6 @@ namespace BizHawk.MultiClient { //The other tool updates are earlier, TAStudio needs to be later so it can display the latest //frame of execution in its list view. - Global.MovieSession.Movie.CheckValidity(); TAStudio1.UpdateValues(); } diff --git a/BizHawk.MultiClient/movie/Movie.cs b/BizHawk.MultiClient/movie/Movie.cs index 34f63e0029..59fb89f0f6 100644 --- a/BizHawk.MultiClient/movie/Movie.cs +++ b/BizHawk.MultiClient/movie/Movie.cs @@ -80,14 +80,6 @@ namespace BizHawk.MultiClient return Frames; } - public int StateLength() - { - if (Loaded) - return Log.StateLength(); - else - return Frames; - } - public void UpdateFileName(string filename) { this.Filename = filename; @@ -118,27 +110,71 @@ namespace BizHawk.MultiClient { if (frame <= Global.Emulator.Frame) { - //frame-1 because we need to go back an extra frame and then run a frame, otherwise the display doesn't get updated. - Global.Emulator.LoadStateBinary(new BinaryReader(new MemoryStream(Log.GetState(frame - 1)))); - Global.MainForm.UpdateFrame = true; + if (frame <= Log.StateFirstIndex()) + { + //Global.MainForm.LoadRom(Global.MainForm.CurrentlyOpenRom,false); + Global.Emulator.LoadStateBinary(new BinaryReader(new MemoryStream(Log.GetInitState()))); + Global.MainForm.TAStudio1.UpdateValues(); + if (true == Global.MainForm.EmulatorPaused && 0 != frame) + { + Global.MainForm.StopOnFrame = frame; + Global.MainForm.UnpauseEmulator(); + } + if (MOVIEMODE.RECORD == Mode) + { + Mode = MOVIEMODE.PLAY; + Global.MainForm.RestoreReadWriteOnStop = true; + } + } + else + { + if (0 == frame) + { + //Global.MainForm.LoadRom(Global.MainForm.CurrentlyOpenRom, false); + Global.Emulator.LoadStateBinary(new BinaryReader(new MemoryStream(Log.GetInitState()))); + //Global.MainForm.StopOnFrame = frame; + Global.MainForm.TAStudio1.UpdateValues(); + } + else + { + //frame-1 because we need to go back an extra frame and then run a frame, otherwise the display doesn't get updated. + Global.Emulator.LoadStateBinary(new BinaryReader(new MemoryStream(Log.GetState(frame - 1)))); + Global.MainForm.UpdateFrame = true; + } + } + } + else + { + Global.MainForm.StopOnFrame = frame; + Global.MainForm.UnpauseEmulator(); } } public void DeleteFrame(int frame) { - RewindToFrame(frame); + if (frame <= StateLastIndex()) + { + if (frame <= StateFirstIndex()) + { + RewindToFrame(0); + } + else + { + RewindToFrame(frame); + } + } Log.DeleteFrame(frame); Global.MainForm.TAStudio1.UpdateValues(); } - public int ValidStateCount() + public int StateFirstIndex() { - return Log.ValidStateCount(); + return Log.StateFirstIndex(); } - public void CheckValidity() + public int StateLastIndex() { - Log.CheckValidity(); + return Log.StateLastIndex(); } public void ClearSaveRAM() @@ -186,6 +222,7 @@ namespace BizHawk.MultiClient { ClearSaveRAM(); Mode = MOVIEMODE.PLAY; + Global.MainForm.StopOnFrame = LogLength(); } public void ResumeRecording() @@ -207,6 +244,7 @@ namespace BizHawk.MultiClient MnemonicsGenerator mg = new MnemonicsGenerator(); mg.SetSource(source); + Log.SetFrameAt(frameNum, mg.GetControllersAsMnemonic()); } diff --git a/BizHawk.MultiClient/movie/MovieLog.cs b/BizHawk.MultiClient/movie/MovieLog.cs index 7dcdf76897..bf7ebf82a0 100644 --- a/BizHawk.MultiClient/movie/MovieLog.cs +++ b/BizHawk.MultiClient/movie/MovieLog.cs @@ -12,10 +12,24 @@ namespace BizHawk.MultiClient public class MovieLog { //TODO: Insert(int frame) not useful for convenctional tasing but TAStudio will want it + + private struct StateRecordStruct + { + public StateRecordStruct(int index, byte[] state) + { + this.index = index; + this.state = state; + } - List MovieRecords = new List(); + public int index; + public byte[] state; + } - private List StateRecords = new List(); + private List MovieRecords = new List(); + private List StateRecords = new List(); + private byte[] InitState; + //TODO: Make this size limit configurable by the user + private int MaxStateRecordSize = 1024 * 1024 * 1024; //To limit memory usage. public MovieLog() { @@ -26,11 +40,26 @@ namespace BizHawk.MultiClient return MovieRecords.Count; } - public int StateLength() + public int StateFirstIndex() + { + return (0 == StateRecords.Count) ? -1 : StateRecords[0].index; + } + + public int StateLastIndex() + { + return (0 == StateRecords.Count) ? -1 : StateRecords[StateRecords.Count-1].index; + } + + public int StateSizeInFrames() { return StateRecords.Count; } + public int StateSizeInBytes() + { + return (0 == StateRecords.Count) ? 0 : StateRecords.Count * StateRecords[0].state.Length; + } + public void Clear() { MovieRecords.Clear(); @@ -44,14 +73,33 @@ namespace BizHawk.MultiClient public void AddState(byte[] state) { - if (Global.Emulator.Frame >= StateRecords.Count) + if (0 == Global.Emulator.Frame) { - StateRecords.Add(state); + InitState = state; + } + if (Global.Emulator.Frame < StateFirstIndex()) + { + StateRecords.Clear(); + StateRecords.Add(new StateRecordStruct(Global.Emulator.Frame, state)); + } + if (Global.Emulator.Frame > StateLastIndex()) + { + if (StateSizeInBytes() + state.Length > MaxStateRecordSize) + { + // Discard the oldest state to save space. + StateRecords.RemoveAt(0); + } + StateRecords.Add(new StateRecordStruct(Global.Emulator.Frame,state)); } } public void SetFrameAt(int frameNum, string frame) { + if (frameNum < StateLastIndex() && (frameNum < StateFirstIndex() || frame != GetFrame(frameNum))) + { + TruncateStates(frameNum+1); + } + if (MovieRecords.Count > frameNum) MovieRecords[frameNum] = frame; else @@ -61,42 +109,44 @@ namespace BizHawk.MultiClient { MovieRecords.Insert(frameNum, frame); - if (frameNum <= StateRecords.Count - 1) + if (frameNum <= StateLastIndex()) { - StateRecords.RemoveRange(frameNum, StateRecords.Count - frameNum); + if (frameNum <= StateFirstIndex()) + { + StateRecords.Clear(); + Global.MovieSession.Movie.RewindToFrame(0); + } + else + { + StateRecords.RemoveRange(frameNum - StateFirstIndex(), StateLastIndex() - frameNum + 1); + Global.MovieSession.Movie.RewindToFrame(frameNum); + } } } - public void CheckValidity() - { - byte[] state = Global.Emulator.SaveStateBinary(); - if (Global.Emulator.Frame < StateRecords.Count && !state.SequenceEqual((byte[])StateRecords[Global.Emulator.Frame])) - { - TruncateStates(Global.Emulator.Frame); - } - } - - public int CapturedStateCount() - { - return StateRecords.Count; - } - - public int ValidStateCount() - { - return StateRecords.Count; - } - public byte[] GetState(int frame) { - return StateRecords[frame]; + return StateRecords[frame-StateFirstIndex()].state; + } + + public byte[] GetInitState() + { + return InitState; } public void DeleteFrame(int frame) { MovieRecords.RemoveAt(frame); - if (frame < StateRecords.Count) + if (frame <= StateLastIndex()) { - StateRecords.RemoveAt(frame); + if (frame <= StateFirstIndex()) + { + StateRecords.Clear(); + } + else + { + StateRecords.RemoveRange(frame - StateFirstIndex(), StateLastIndex() - frame + 1); + } } } @@ -105,19 +155,18 @@ namespace BizHawk.MultiClient StateRecords.Clear(); } - public void TruncateFrames(int frame) - { - if (frame >= 0 && frame < MovieLength()) - { - MovieRecords.RemoveRange(frame, MovieLength() - frame); - } - } - public void TruncateStates(int frame) { - if (frame >= 0 && frame < StateLength()) + if (frame >= 0) { - StateRecords.RemoveRange(frame, StateLength() - frame); + if (frame < StateFirstIndex()) + { + StateRecords.Clear(); + } + else if (frame <= StateLastIndex()) + { + StateRecords.RemoveRange(frame - StateFirstIndex(), StateLastIndex() - frame+1); + } } } diff --git a/BizHawk.MultiClient/tools/TAStudio.Designer.cs b/BizHawk.MultiClient/tools/TAStudio.Designer.cs index 8503b40503..89e0776b20 100644 --- a/BizHawk.MultiClient/tools/TAStudio.Designer.cs +++ b/BizHawk.MultiClient/tools/TAStudio.Designer.cs @@ -22,7 +22,7 @@ Global.MovieSession.Movie.TastudioOn = false; Global.MovieSession.Movie.ClearStates(); - Global.MainForm.StopOnEnd = true; + Global.MainForm.StopOnFrame = Global.MovieSession.Movie.LogLength(); base.Dispose(disposing); } diff --git a/BizHawk.MultiClient/tools/TAStudio.cs b/BizHawk.MultiClient/tools/TAStudio.cs index eb74285be9..331130983f 100644 --- a/BizHawk.MultiClient/tools/TAStudio.cs +++ b/BizHawk.MultiClient/tools/TAStudio.cs @@ -70,9 +70,9 @@ namespace BizHawk.MultiClient case "SMS": case "GG": case "SG": - Pads[0].SetButtons(str.Substring(0, 6)); - Pads[1].SetButtons(str.Substring(7, 6)); - Pads[2].SetButtons(str.Substring(14, 2)); + Pads[0].SetButtons(str.Substring(1, 6)); + Pads[1].SetButtons(str.Substring(8, 6)); + Pads[2].SetButtons(str.Substring(15, 2)); break; case "PCE": case "SGX": @@ -104,9 +104,13 @@ namespace BizHawk.MultiClient private void TASView_QueryItemBkColor(int index, int column, ref Color color) { - if (index < Global.MovieSession.Movie.ValidStateCount()) + if (0 == index && 0 == Global.MovieSession.Movie.StateFirstIndex()) + color = Color.LightGreen; //special case for frame 0. Normally we need to go back an extra frame, but for frame 0 we can reload the rom. + if (index > Global.MovieSession.Movie.StateFirstIndex() && index <= Global.MovieSession.Movie.StateLastIndex()) color = Color.LightGreen; - else if ("" != Global.MovieSession.Movie.GetInputFrame(index) && Global.MovieSession.Movie.GetInputFrame(index)[1] == 'L') + if ("" != Global.MovieSession.Movie.GetInputFrame(index) && + Global.COMMANDS[Global.MovieInputSourceAdapter.Type.Name].ContainsKey("Lag") && + Global.MovieSession.Movie.GetInputFrame(index)[1] == Global.COMMANDS[Global.MovieInputSourceAdapter.Type.Name]["Lag"][0]) color = Color.Pink; if (index == Global.Emulator.Frame) { @@ -131,7 +135,7 @@ namespace BizHawk.MultiClient private void DisplayList() { TASView.ItemCount = Global.MovieSession.Movie.LogLength(); - if (Global.MovieSession.Movie.LogLength() == Global.Emulator.Frame && Global.MovieSession.Movie.StateLength() == Global.Emulator.Frame) + if (Global.MovieSession.Movie.LogLength() == Global.Emulator.Frame && Global.MovieSession.Movie.StateLastIndex() == Global.Emulator.Frame - 1) { //If we're at the end of the movie add one to show the cursor as a blank frame TASView.ItemCount++; @@ -158,7 +162,7 @@ namespace BizHawk.MultiClient Global.MovieSession.Movie.TastudioOn = true; - Global.MainForm.StopOnEnd = false; + Global.MainForm.StopOnFrame = -1; LoadConfigSettings(); ReadOnlyCheckBox.Checked = Global.MainForm.ReadOnly; @@ -367,11 +371,11 @@ namespace BizHawk.MultiClient { if (true == this.FastFowardToEnd.Checked) { - Global.MainForm.StopOnEnd = false; + Global.MainForm.StopOnFrame = -1; } else { - Global.MainForm.StopOnEnd = true; + Global.MainForm.StopOnFrame = Global.MovieSession.Movie.LogLength(); } this.FastFowardToEnd.Checked ^= true; @@ -490,7 +494,6 @@ namespace BizHawk.MultiClient { Global.MovieSession.Movie.InsertFrame(Global.MovieSession.Movie.GetInputFrame(list[index]), (int)list[index]); } - Global.MovieSession.Movie.RewindToFrame(TASView.selectedItem); } private void Delete_Click(object sender, EventArgs e) @@ -498,7 +501,7 @@ namespace BizHawk.MultiClient ListView.SelectedIndexCollection list = TASView.SelectedIndices; for (int index = 0; index < list.Count; index++) { - Global.MovieSession.Movie.DeleteFrame(TASView.selectedItem); + Global.MovieSession.Movie.DeleteFrame(list[index]); } } } diff --git a/BizHawk.MultiClient/tools/VirtualPadSMS.cs b/BizHawk.MultiClient/tools/VirtualPadSMS.cs index a4acbd8d26..d6ccf52e59 100644 --- a/BizHawk.MultiClient/tools/VirtualPadSMS.cs +++ b/BizHawk.MultiClient/tools/VirtualPadSMS.cs @@ -168,9 +168,9 @@ namespace BizHawk.MultiClient else if (sender == PR) Global.StickyXORAdapter.SetSticky(Controller + " Right", PR.Checked); else if (sender == B1) - Global.StickyXORAdapter.SetSticky(Controller + " B1", B3.Checked); + Global.StickyXORAdapter.SetSticky(Controller + " B1", B1.Checked); else if (sender == B2) - Global.StickyXORAdapter.SetSticky(Controller + " B2", B4.Checked); + Global.StickyXORAdapter.SetSticky(Controller + " B2", B2.Checked); } public override void Clear()