private void StateHistorySettings_Load(object sender, EventArgs e) { _stateSizeMb = Statable.SaveStateBinary().Length / (decimal)1024 / (decimal)1024; if (Environment.Is64BitProcess) // ? { MemCapacityNumeric.Maximum = 1024 * 8; } else { MemCapacityNumeric.Maximum = 1024; } MemCapacityNumeric.Value = Settings.Capacitymb < MemCapacityNumeric.Maximum ? Settings.Capacitymb : MemCapacityNumeric.Maximum; DiskCapacityNumeric.Value = Settings.DiskCapacitymb < MemCapacityNumeric.Maximum ? Settings.DiskCapacitymb : MemCapacityNumeric.Maximum; SaveCapacityNumeric.Value = Settings.DiskSaveCapacitymb < MemCapacityNumeric.Maximum ? Settings.DiskSaveCapacitymb : MemCapacityNumeric.Maximum; SavestateSizeLabel.Text = Math.Round(_stateSizeMb, 2).ToString() + " mb"; CapacityNumeric_ValueChanged(null, null); SaveCapacityNumeric_ValueChanged(null, null); BranchStatesInTasproj.Checked = Settings.BranchStatesInTasproj; EraseBranchStatesFirst.Checked = Settings.EraseBranchStatesFirst; }
public void Capture(bool force = false) { bool shouldCapture; int frame = _emulator.Frame; if (_movie.StartsFromSavestate && frame == 0) // Never capture frame 0 on savestate anchored movies since we have it anyway { shouldCapture = false; } else if (force) { shouldCapture = true; } else if (frame == 0) // For now, long term, TasMovie should have a .StartState property, and a .tasproj file for the start state in non-savestate anchored movies { shouldCapture = true; } else if (IsMarkerState(frame)) { shouldCapture = true; // Markers should always get priority } else { shouldCapture = frame % _stateFrequency == 0; } if (shouldCapture) { SetState(frame, (byte[])_core.SaveStateBinary().Clone(), skipRemoval: false); } }
public void Capture(int frame, IStatable source, bool force = false) { if (frame <= Last) { CaptureHighPriority(frame, source); } _current.Capture(frame, s => source.SaveStateBinary(new BinaryWriter(s)), index => { var state = _current.GetState(index); _recent.Capture(state.Frame, s => state.GetReadStream().CopyTo(s), index2 => { var state2 = _recent.GetState(index2); var from = _ancient.Count > 0 ? _ancient[_ancient.Count - 1].Key : 0; if (state2.Frame - from >= _ancientInterval) { var ms = new MemoryStream(); state2.GetReadStream().CopyTo(ms); _ancient.Add(new KeyValuePair <int, byte[]>(state2.Frame, ms.ToArray())); } }); }, force); }
public void SaveStateBinary(BinaryWriter bw) { _lStates.SaveStateBinary(bw); _rStates.SaveStateBinary(bw); // other variables SyncState(new Serializer(bw)); }
private void RewindConfig_Load(object sender, EventArgs e) { if (_rewinder.HasBuffer) { FullnessLabel.Text = $"{_rewinder.FullnessRatio * 100:0.00}%"; RewindFramesUsedLabel.Text = _rewinder.Count.ToString(); } else { FullnessLabel.Text = "N/A"; RewindFramesUsedLabel.Text = "N/A"; } RewindSpeedNumeric.Value = _config.RewindSpeedMultiplier; DiskBufferCheckbox.Checked = _config.Rewind_OnDisk; RewindIsThreadedCheckbox.Checked = _config.Rewind_IsThreaded; _stateSize = _statableCore.SaveStateBinary().Length; BufferSizeUpDown.Value = Math.Max(_config.Rewind_BufferSize, BufferSizeUpDown.Minimum); _mediumStateSize = _config.Rewind_MediumStateSize; _largeStateSize = _config.Rewind_LargeStateSize; UseDeltaCompression.Checked = _config.Rewind_UseDelta; SmallSavestateNumeric.Value = _config.RewindFrequencySmall; MediumSavestateNumeric.Value = _config.RewindFrequencyMedium; LargeSavestateNumeric.Value = _config.RewindFrequencyLarge; SmallStateEnabledBox.Checked = _config.RewindEnabledSmall; MediumStateEnabledBox.Checked = _config.RewindEnabledMedium; LargeStateEnabledBox.Checked = _config.RewindEnabledLarge; SetSmallEnabled(); SetMediumEnabled(); SetLargeEnabled(); SetStateSize(); var mediumStateSizeKb = _config.Rewind_MediumStateSize / 1024; var largeStateSizeKb = _config.Rewind_LargeStateSize / 1024; MediumStateTrackbar.Value = mediumStateSizeKb; MediumStateUpDown.Value = mediumStateSizeKb; LargeStateTrackbar.Value = largeStateSizeKb; LargeStateUpDown.Value = largeStateSizeKb; nudCompression.Value = _config.SaveStateCompressionLevelNormal; rbStatesDefault.Checked = _config.SaveStateType == SaveStateTypeE.Default; rbStatesBinary.Checked = _config.SaveStateType == SaveStateTypeE.Binary; rbStatesText.Checked = _config.SaveStateType == SaveStateTypeE.Text; BackupSavestatesCheckbox.Checked = _config.BackupSavestates; ScreenshotInStatesCheckbox.Checked = _config.SaveScreenshotWithStates; LowResLargeScreenshotsCheckbox.Checked = !_config.NoLowResLargeScreenshotWithStates; BigScreenshotNumeric.Value = _config.BigScreenshotSize / 1024; ScreenshotInStatesCheckbox_CheckedChanged(null, null); }
public void Capture(int frame) { if (!Active) { return; } _buffer.Capture(frame, s => _stateSource.SaveStateBinary(new BinaryWriter(s))); }
public static void SaveStateText(this IStatable core, TextWriter writer) { if (core is ITextStatable textCore) { textCore.SaveStateText(writer); } var temp = core.SaveStateBinary(); temp.SaveAsHexFast(writer); }
public static byte[] CloneSavestate(this IStatable core) { using var ms = new MemoryStream(); using var bw = new BinaryWriter(ms); core.SaveStateBinary(bw); bw.Flush(); var stateBuffer = ms.ToArray(); bw.Close(); return(stateBuffer); }
internal void CaptureReserved(int frame, IStatable source) { if (_reserved.ContainsKey(frame)) { return; } var ms = new MemoryStream(); source.SaveStateBinary(new BinaryWriter(ms)); _reserved.Add(frame, ms.ToArray()); AddStateCache(frame); }
public void Capture(int frame) { if (!RewindActive) { return; } if (_rewindThread == null || frame % RewindFrequency != 0) { return; } _rewindThread.Capture(_statableCore.SaveStateBinary()); }
private void CaptureGap(int frame, IStatable source) { // We need to do this here for the following scenario // We are currently far enough in the game that there is a large "ancient interval" section // The user navigates to a frame after ancient interval 2, replay happens and we start filling gaps // Then the user, still without having made an edit, navigates to a frame before ancient interval 2, but after ancient interval 1 // Without this logic, we end up with out of order states // We cannot use InvalidateGaps because that does not address the state cache or check for reserved states. for (int i = _gapFiller.Count - 1; i >= 0; i--) { var lastGap = _gapFiller.GetState(i); if (lastGap.Frame < frame) { break; } if (_reserveCallback(lastGap.Frame)) { AddToReserved(lastGap); } else { StateCache.Remove(lastGap.Frame); } _gapFiller.InvalidateEnd(i); } _gapFiller.Capture( frame, s => { AddStateCache(frame); source.SaveStateBinary(new BinaryWriter(s)); }, index => { var state = _gapFiller.GetState(index); StateCache.Remove(state.Frame); if (_reserveCallback(state.Frame)) { AddToReserved(state); return; } }); }
private ZwinderStateManager CreateSmallZwinder(IStatable ss) { var zw = new ZwinderStateManager(new ZwinderStateManagerSettings { CurrentBufferSize = 1, CurrentTargetFrameLength = 10000, RecentBufferSize = 1, RecentTargetFrameLength = 100000, AncientStateInterval = 50000 }, f => false); var ms = new MemoryStream(); ss.SaveStateBinary(new BinaryWriter(ms)); zw.Engage(ms.ToArray()); return(zw); }
private void CaptureGap(int frame, IStatable source) { // We need to do this here for the following scenario // We are currently far enough in the game that there is a large "ancient interval" section // The user navigates to a frame after ancient interval 2, replay happens and we start filling gaps // Then the user, still without having made an edit, navigates to a frame before ancient interval 2, but after ancient interval 1 // Without this logic, we end up with out of order states if (_gapFiller.Count > 0 && frame < GapStates().First().Frame) { InvalidateGaps(frame); } _gapFiller.Capture( frame, s => { StateCache.Add(frame); source.SaveStateBinary(new BinaryWriter(s)); }, index => StateCache.Remove(index)); }
public TasStateManager(TasMovie movie) { _movie = movie; Settings = new TasStateManagerSettings(Global.Config.DefaultTasProjSettings); if (_movie.StartsFromSavestate) { SetState(0, _movie.BinarySavestate); } _decay = new StateManagerDecay(_movie, this); _expectedStateSize = (ulong)Core.SaveStateBinary().Length; if (_expectedStateSize == 0) { throw new InvalidOperationException("Savestate size can not be zero!"); } _states = new SortedList <int, byte[]>(MaxStates); UpdateStateFrequency(); }
public void CaptureHighPriority(int frame, IStatable source) { _highPriority.Capture(frame, s => source.SaveStateBinary(new BinaryWriter(s))); }
public unsafe void Capture(int frame) { Sync(); if (!_active) { return; } if (_masterFrame == -1) { var sss = new SaveStateStream(this); _stateSource.SaveStateBinary(new BinaryWriter(sss)); (_master, _scratch) = (_scratch, _master); _masterLength = (int)sss.Position; _masterFrame = frame; _count++; return; } if (!_buffer.WillCapture(_masterFrame)) { return; } { var sss = new SaveStateStream(this); _stateSource.SaveStateBinary(new BinaryWriter(sss)); Work(() => { _buffer.Capture(_masterFrame, underlyingStream_ => { var zeldas = SpanStream.GetOrBuild(underlyingStream_); if (_master.Length < _scratch.Length) { var replacement = new byte[_scratch.Length]; Array.Copy(_master, replacement, _master.Length); _master = replacement; } var lengthHolder = _masterLength; var lengthHolderSpan = new ReadOnlySpan <byte>(&lengthHolder, 4); zeldas.Write(lengthHolderSpan); fixed(byte *older_ = _master) fixed(byte *newer_ = _scratch) { int *older = (int *)older_; int *newer = (int *)newer_; int lastIndex = (Math.Min(_masterLength, (int)sss.Position) + 3) / 4; int lastOldIndex = (_masterLength + 3) / 4; int *olderEnd = older + lastIndex; int *from = older; int *to = older; while (older < olderEnd) { if (*older++ == *newer++) { if (to < from) { // Save on [to, from] lengthHolder = (int)(from - to); zeldas.Write(lengthHolderSpan); zeldas.Write(new ReadOnlySpan <byte>(to, lengthHolder * 4)); } to = older; } else { if (from < to) { // encode gap [from, to] lengthHolder = (int)(to - from) | IS_GAP; zeldas.Write(lengthHolderSpan); } from = older; } } if (from < to) { // encode gap [from, to] lengthHolder = (int)(to - from) | IS_GAP; zeldas.Write(lengthHolderSpan); } if (lastOldIndex > lastIndex) { from += lastOldIndex - lastIndex; } if (to < from) { // Save on [to, from] lengthHolder = (int)(from - to); zeldas.Write(lengthHolderSpan); zeldas.Write(new ReadOnlySpan <byte>(to, lengthHolder * 4)); } } (_master, _scratch) = (_scratch, _master); _masterLength = (int)sss.Position; _masterFrame = frame; _count++; }); }); } }
public void Capture(int frame, IStatable source, bool force = false) { // We already have this state, no need to capture if (StateCache.Contains(frame)) { return; } if (_reserveCallback(frame)) { CaptureReserved(frame, source); return; } // We do not want to consider reserved states for a notion of Last // reserved states can include future states in the case of branch states if ((frame <= LastRing && NeedsGap(frame)) || force) { // We use the gap buffer for forced capture to avoid crowding the "current" buffer and thus reducing it's actual span of covered frames. CaptureGap(frame, source); return; } _current.Capture(frame, s => { source.SaveStateBinary(new BinaryWriter(s)); AddStateCache(frame); }, index => { var state = _current.GetState(index); StateCache.Remove(state.Frame); // If this is a reserved state, go ahead and reserve instead of potentially trying to force it into recent, for further eviction logic later if (_reserveCallback(state.Frame)) { AddToReserved(state); return; } _recent.Capture(state.Frame, s => { state.GetReadStream().CopyTo(s); AddStateCache(state.Frame); }, index2 => { var state2 = _recent.GetState(index2); StateCache.Remove(state2.Frame); var isReserved = _reserveCallback(state2.Frame); // Add to reserved if reserved, or if it matches an "ancient" state consideration if (isReserved || !HasNearByReserved(state2.Frame)) { AddToReserved(state2); } }); }, force); }
public void Create(string filename, SaveStateConfig config) { // the old method of text savestate save is now gone. // a text savestate is just like a binary savestate, but with a different core lump using var bs = new ZipStateSaver(filename, config.CompressionLevelNormal); bs.PutVersionLumps(); using (new SimpleTime("Save Core")) { if (config.Type == SaveStateType.Text) { bs.PutLump(BinaryStateLump.CorestateText, tw => _statable.SaveStateText(tw)); } else { bs.PutLump(BinaryStateLump.Corestate, bw => _statable.SaveStateBinary(bw)); } } if (config.SaveScreenshot && _videoProvider != null) { var buff = _videoProvider.GetVideoBuffer(); if (buff.Length == 1) { // is a hacky opengl texture ID. can't handle this now! // need to discuss options // 1. cores must be able to provide a pixels VideoProvider in addition to a texture ID, on command (not very hard overall but interface changing and work per core) // 2. SavestateManager must be setup with a mechanism for resolving texture IDs (even less work, but sloppy) // There are additional problems with AVWriting. They depend on VideoProvider providing pixels. } else { int outWidth = _videoProvider.BufferWidth; int outHeight = _videoProvider.BufferHeight; // if buffer is too big, scale down screenshot if (!config.NoLowResLargeScreenshots && buff.Length >= config.BigScreenshotSize) { outWidth /= 2; outHeight /= 2; } using (new SimpleTime("Save Framebuffer")) { bs.PutLump(BinaryStateLump.Framebuffer, s => _quickBmpFile.Save(_videoProvider, s, outWidth, outHeight)); } } } if (_movieSession.Movie.IsActive()) { bs.PutLump(BinaryStateLump.Input, delegate(TextWriter tw) { // this never should have been a core's responsibility tw.WriteLine("Frame {0}", _emulator.Frame); _movieSession.HandleSaveState(tw); }); } if (_userBag.Any()) { bs.PutLump(BinaryStateLump.UserData, delegate(TextWriter tw) { var data = ConfigService.SaveWithType(_userBag); tw.WriteLine(data); }); } if (_movieSession.Movie.IsActive() && _movieSession.Movie is ITasMovie) { bs.PutLump(BinaryStateLump.LagLog, delegate(TextWriter tw) { ((ITasMovie)_movieSession.Movie).LagLog.Save(tw); }); } }