1//============================================================================
2//
3// SSSS tt lll lll
4// SS SS tt ll ll
5// SS tttttt eeee ll ll aaaa
6// SSSS tt ee ee ll ll aa
7// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
8// SS SS tt ee ll ll aa aa
9// SSSS ttt eeeee llll llll aaaaa
10//
11// Copyright (c) 1995-2019 by Bradford W. Mott, Stephen Anthony
12// and the Stella Team
13//
14// See the file "License.txt" for information on usage and redistribution of
15// this file, and for a DISCLAIMER OF ALL WARRANTIES.
16//============================================================================
17
18#ifndef REWIND_MANAGER_HXX
19#define REWIND_MANAGER_HXX
20
21class OSystem;
22class StateManager;
23
24#include "LinkedObjectPool.hxx"
25#include "bspf.hxx"
26
27/**
28 This class is used to save (and later 'replay') system save states.
29 In this implementation, we assume states are added at the end of the list.
30
31 Rewinding involves moving the internal iterator backwards in time (towards
32 the beginning of the list).
33
34 Unwinding involves moving the internal iterator forwards in time (towards
35 the end of the list).
36
37 Any time a new state is added, all states from the current iterator position
38 to the end of the list (aka, all future states) are removed, and the internal
39 iterator moves to the insertion point of the data (the end of the list).
40
41 If the list is full, states are either removed at the beginning (compression
42 off) or at selective positions (compression on).
43
44 @author Stephen Anthony
45*/
46class RewindManager
47{
48 public:
49 RewindManager(OSystem& system, StateManager& statemgr);
50
51 public:
52 static constexpr int NUM_INTERVALS = 7;
53 // cycle values for the intervals
54 const uInt32 INTERVAL_CYCLES[NUM_INTERVALS] = {
55 76 * 262,
56 76 * 262 * 3,
57 76 * 262 * 10,
58 76 * 262 * 30,
59 76 * 262 * 60,
60 76 * 262 * 60 * 3,
61 76 * 262 * 60 * 10
62 };
63 // settings values for the intervals
64 const string INT_SETTINGS[NUM_INTERVALS] = {
65 "1f",
66 "3f",
67 "10f",
68 "30f",
69 "1s",
70 "3s",
71 "10s"
72 };
73
74 static constexpr int NUM_HORIZONS = 8;
75 // cycle values for the horzions
76 const uInt64 HORIZON_CYCLES[NUM_HORIZONS] = {
77 76 * 262 * 60 * 3,
78 76 * 262 * 60 * 10,
79 76 * 262 * 60 * 30,
80 76 * 262 * 60 * 60,
81 76 * 262 * 60 * 60 * 3,
82 76 * 262 * 60 * 60 * 10,
83 uInt64(76) * 262 * 60 * 60 * 30,
84 uInt64(76) * 262 * 60 * 60 * 60
85 };
86 // settings values for the horzions
87 const string HOR_SETTINGS[NUM_HORIZONS] = {
88 "3s",
89 "10s",
90 "30s",
91 "1m",
92 "3m",
93 "10m",
94 "30m",
95 "60m"
96 };
97
98 /**
99 Initializes state list and calculates compression factor.
100 */
101 void setup();
102
103 /**
104 Add a new state file with the given message; this message will be
105 displayed when the state is replayed.
106
107 @param message Message to display when replaying this state
108 */
109 bool addState(const string& message, bool timeMachine = false);
110
111 /**
112 Rewind numStates levels of the state list, and display the message associated
113 with that state.
114
115 @param numStates Number of states to rewind
116 @return Number of states to rewinded
117 */
118 uInt32 rewindStates(uInt32 numStates = 1);
119
120 /**
121 Unwind numStates levels of the state list, and display the message associated
122 with that state.
123
124 @param numStates Number of states to unwind
125 @return Number of states to unwinded
126 */
127 uInt32 unwindStates(uInt32 numStates = 1);
128
129 /**
130 Rewind/unwind numStates levels of the state list, and display the message associated
131 with that state.
132
133 @param numStates Number of states to wind
134 @param unwind unwind or rewind
135 @return Number of states to winded
136 */
137 uInt32 windStates(uInt32 numStates, bool unwind);
138
139 string saveAllStates();
140 string loadAllStates();
141
142 bool atFirst() const { return myStateList.atFirst(); }
143 bool atLast() const { return myStateList.atLast(); }
144 void resize(uInt32 size) { myStateList.resize(size); }
145 void clear() {
146 myStateSize = 0;
147 myStateList.clear();
148 }
149
150 /**
151 Convert the cycles into a unit string.
152 */
153 string getUnitString(Int64 cycles);
154
155 uInt32 getCurrentIdx() { return myStateList.currentIdx(); }
156 uInt32 getLastIdx() { return myStateList.size(); }
157
158 uInt64 getFirstCycles() const;
159 uInt64 getCurrentCycles() const;
160 uInt64 getLastCycles() const;
161
162 /**
163 Get a collection of cycle timestamps, offset from the first one in
164 the list. This also determines the number of states in the list.
165 */
166 IntArray cyclesList() const;
167
168 private:
169 OSystem& myOSystem;
170 StateManager& myStateManager;
171
172 uInt32 mySize;
173 uInt32 myUncompressed;
174 uInt32 myInterval;
175 uInt64 myHorizon;
176 double myFactor;
177 bool myLastTimeMachineAdd;
178 uInt32 myStateSize;
179
180 struct RewindState {
181 Serializer data; // actual save state
182 string message; // describes save state origin
183 uInt64 cycles; // cycles since emulation started
184
185 // We do nothing on object instantiation or copy
186 // The goal of LinkedObjectPool is to not do any allocations at all
187 RewindState() : cycles(0) { }
188 RewindState(const RewindState& rs) : cycles(rs.cycles) { }
189 RewindState& operator= (const RewindState& rs) { cycles = rs.cycles; return *this; }
190
191 // Output object info; used for debugging only
192 friend ostream& operator<<(ostream& os, const RewindState& s) {
193 return os << "msg: " << s.message << " cycle: " << s.cycles;
194 }
195 };
196
197 // The linked-list to store states (internally it takes care of reducing
198 // frequent (de)-allocations)
199 Common::LinkedObjectPool<RewindState> myStateList;
200
201 /**
202 Remove a save state from the list
203 */
204 void compressStates();
205
206 /**
207 Load the current state and get the message string for the rewind/unwind
208
209 @return The message
210 */
211 string loadState(Int64 startCycles, uInt32 numStates);
212
213 private:
214 // Following constructors and assignment operators not supported
215 RewindManager() = delete;
216 RewindManager(const RewindManager&) = delete;
217 RewindManager(RewindManager&&) = delete;
218 RewindManager& operator=(const RewindManager&) = delete;
219 RewindManager& operator=(RewindManager&&) = delete;
220};
221
222#endif
223