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 | |
21 | class OSystem; |
22 | class 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 | */ |
46 | class 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 | |