Exemple #1
0
        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;
        }
Exemple #2
0
        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);
        }
Exemple #4
0
 public void SaveStateBinary(BinaryWriter bw)
 {
     _lStates.SaveStateBinary(bw);
     _rStates.SaveStateBinary(bw);
     // other variables
     SyncState(new Serializer(bw));
 }
Exemple #5
0
        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);
        }
Exemple #6
0
 public void Capture(int frame)
 {
     if (!Active)
     {
         return;
     }
     _buffer.Capture(frame, s => _stateSource.SaveStateBinary(new BinaryWriter(s)));
 }
Exemple #7
0
        public static void SaveStateText(this IStatable core, TextWriter writer)
        {
            if (core is ITextStatable textCore)
            {
                textCore.SaveStateText(writer);
            }

            var temp = core.SaveStateBinary();

            temp.SaveAsHexFast(writer);
        }
Exemple #8
0
        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);
        }
Exemple #10
0
        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);
        }
Exemple #13
0
        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));
        }
Exemple #14
0
        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)));
 }
Exemple #16
0
        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);
        }
Exemple #18
0
        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);
                });
            }
        }