rlm@1
|
1 #include <cstdio>
|
rlm@1
|
2 #include <cctype>
|
rlm@1
|
3 #include <cstdlib>
|
rlm@1
|
4 #include <cstring>
|
rlm@1
|
5 #include <cassert>
|
rlm@1
|
6 #include <algorithm>
|
rlm@1
|
7
|
rlm@1
|
8 using namespace std;
|
rlm@1
|
9
|
rlm@1
|
10 #ifdef HAVE_STRINGS_H
|
rlm@1
|
11 # include <strings.h>
|
rlm@1
|
12 #endif
|
rlm@1
|
13
|
rlm@1
|
14 #if defined(__unix) || defined(__linux) || defined(__sun) || defined(__DJGPP)
|
rlm@1
|
15 # include <unistd.h>
|
rlm@1
|
16 # include <sys/types.h>
|
rlm@1
|
17 # include <sys/stat.h>
|
rlm@1
|
18 # include <climits>
|
rlm@1
|
19 # define stricmp strcasecmp
|
rlm@1
|
20 // FIXME: this is wrong, but we don't want buffer overflow
|
rlm@1
|
21 # if defined _MAX_PATH
|
rlm@1
|
22 # undef _MAX_PATH
|
rlm@1
|
23 //# define _MAX_PATH 128
|
rlm@1
|
24 # define _MAX_PATH 260
|
rlm@1
|
25 # endif
|
rlm@1
|
26 #endif
|
rlm@1
|
27
|
rlm@1
|
28 #ifdef WIN32
|
rlm@1
|
29 # include <io.h>
|
rlm@1
|
30 # ifndef W_OK
|
rlm@1
|
31 # define W_OK 2
|
rlm@1
|
32 # endif
|
rlm@1
|
33 # define ftruncate chsize
|
rlm@1
|
34 #endif
|
rlm@1
|
35
|
rlm@1
|
36 #include "movie.h"
|
rlm@1
|
37 #include "System.h"
|
rlm@1
|
38 #include "../gba/GBA.h"
|
rlm@1
|
39 #include "../gba/GBAGlobals.h"
|
rlm@1
|
40 #include "../gba/RTC.h"
|
rlm@1
|
41 #include "../gb/GB.h"
|
rlm@1
|
42 #include "../gb/gbGlobals.h"
|
rlm@1
|
43 #include "inputGlobal.h"
|
rlm@1
|
44 #include "unzip.h"
|
rlm@1
|
45 #include "Util.h"
|
rlm@1
|
46
|
rlm@1
|
47 #include "vbalua.h"
|
rlm@1
|
48
|
rlm@1
|
49 #if (defined(WIN32) && !defined(SDL))
|
rlm@1
|
50 # include "../win32/stdafx.h"
|
rlm@1
|
51 # include "../win32/MainWnd.h"
|
rlm@1
|
52 # include "../win32/VBA.h"
|
rlm@1
|
53 # include "../win32/WinMiscUtil.h"
|
rlm@1
|
54 #endif
|
rlm@1
|
55
|
rlm@1
|
56 extern int emulating; // from system.cpp
|
rlm@36
|
57 extern u16 currentButtons[4]; // from SDL.cpp
|
rlm@1
|
58 extern u16 lastKeys;
|
rlm@1
|
59
|
rlm@1
|
60 SMovie Movie;
|
rlm@1
|
61 bool loadingMovie = false;
|
rlm@1
|
62
|
rlm@1
|
63 // probably bad idea to have so many global variables, but I hate to recompile almost everything after editing VBA.h
|
rlm@1
|
64 bool autoConvertMovieWhenPlaying = false;
|
rlm@1
|
65
|
rlm@36
|
66 static u16 initialInputs[4] = { 0, 0, 0, 0 };
|
rlm@1
|
67
|
rlm@1
|
68 static bool resetSignaled = false;
|
rlm@1
|
69 static bool resetSignaledLast = false;
|
rlm@1
|
70
|
rlm@1
|
71 static int prevEmulatorType, prevBorder, prevWinBorder, prevBorderAuto;
|
rlm@1
|
72
|
rlm@1
|
73 // little-endian integer pop/push functions:
|
rlm@1
|
74 static inline uint32 Pop32(const uint8 * &ptr)
|
rlm@1
|
75 {
|
rlm@33
|
76 uint32 v = (ptr[0] | (ptr[1] << 8) | (ptr[2] << 16) | (ptr[3] << 24));
|
rlm@33
|
77 ptr += 4;
|
rlm@33
|
78 return v;
|
rlm@1
|
79 }
|
rlm@1
|
80
|
rlm@1
|
81 static inline uint16 Pop16(const uint8 * &ptr) /* const version */
|
rlm@1
|
82 {
|
rlm@33
|
83 uint16 v = (ptr[0] | (ptr[1] << 8));
|
rlm@33
|
84 ptr += 2;
|
rlm@33
|
85 return v;
|
rlm@1
|
86 }
|
rlm@1
|
87
|
rlm@1
|
88 static inline uint16 Pop16(uint8 * &ptr) /* non-const version */
|
rlm@1
|
89 {
|
rlm@33
|
90 uint16 v = (ptr[0] | (ptr[1] << 8));
|
rlm@33
|
91 ptr += 2;
|
rlm@33
|
92 return v;
|
rlm@1
|
93 }
|
rlm@1
|
94
|
rlm@1
|
95 static inline uint8 Pop8(const uint8 * &ptr)
|
rlm@1
|
96 {
|
rlm@33
|
97 return *(ptr)++;
|
rlm@1
|
98 }
|
rlm@1
|
99
|
rlm@1
|
100 static inline void Push32(uint32 v, uint8 * &ptr)
|
rlm@1
|
101 {
|
rlm@33
|
102 ptr[0] = (uint8)(v & 0xff);
|
rlm@33
|
103 ptr[1] = (uint8)((v >> 8) & 0xff);
|
rlm@33
|
104 ptr[2] = (uint8)((v >> 16) & 0xff);
|
rlm@33
|
105 ptr[3] = (uint8)((v >> 24) & 0xff);
|
rlm@33
|
106 ptr += 4;
|
rlm@1
|
107 }
|
rlm@1
|
108
|
rlm@1
|
109 static inline void Push16(uint16 v, uint8 * &ptr)
|
rlm@1
|
110 {
|
rlm@33
|
111 ptr[0] = (uint8)(v & 0xff);
|
rlm@33
|
112 ptr[1] = (uint8)((v >> 8) & 0xff);
|
rlm@33
|
113 ptr += 2;
|
rlm@1
|
114 }
|
rlm@1
|
115
|
rlm@1
|
116 static inline void Push8(uint8 v, uint8 * &ptr)
|
rlm@1
|
117 {
|
rlm@33
|
118 *ptr++ = v;
|
rlm@1
|
119 }
|
rlm@1
|
120
|
rlm@1
|
121 // little-endian integer read/write functions:
|
rlm@1
|
122 static inline uint16 Read16(const uint8 *ptr)
|
rlm@1
|
123 {
|
rlm@33
|
124 return ptr[0] | (ptr[1] << 8);
|
rlm@1
|
125 }
|
rlm@1
|
126
|
rlm@1
|
127 static inline void Write16(uint16 v, uint8 *ptr)
|
rlm@1
|
128 {
|
rlm@33
|
129 ptr[0] = uint8(v & 0xff);
|
rlm@33
|
130 ptr[1] = uint8((v >> 8) & 0xff);
|
rlm@1
|
131 }
|
rlm@1
|
132
|
rlm@1
|
133 static long file_length(FILE *fp)
|
rlm@1
|
134 {
|
rlm@33
|
135 long cur_pos = ftell(fp);
|
rlm@33
|
136 fseek(fp, 0, SEEK_END);
|
rlm@33
|
137 long length = ftell(fp);
|
rlm@33
|
138 fseek(fp, cur_pos, SEEK_SET);
|
rlm@33
|
139 return length;
|
rlm@1
|
140 }
|
rlm@1
|
141
|
rlm@1
|
142 static int bytes_per_frame(SMovie &mov)
|
rlm@1
|
143 {
|
rlm@33
|
144 int num_controllers = 0;
|
rlm@1
|
145
|
rlm@33
|
146 for (int i = 0; i < MOVIE_NUM_OF_POSSIBLE_CONTROLLERS; ++i)
|
rlm@33
|
147 if (mov.header.controllerFlags & MOVIE_CONTROLLER(i))
|
rlm@33
|
148 ++num_controllers;
|
rlm@1
|
149
|
rlm@33
|
150 return CONTROLLER_DATA_SIZE * num_controllers;
|
rlm@1
|
151 }
|
rlm@1
|
152
|
rlm@1
|
153 static void reserve_buffer_space(uint32 space_needed)
|
rlm@1
|
154 {
|
rlm@33
|
155 if (space_needed > Movie.inputBufferSize)
|
rlm@33
|
156 {
|
rlm@33
|
157 uint32 ptr_offset = Movie.inputBufferPtr - Movie.inputBuffer;
|
rlm@33
|
158 uint32 alloc_chunks = (space_needed - 1) / BUFFER_GROWTH_SIZE + 1;
|
rlm@33
|
159 uint32 old_size = Movie.inputBufferSize;
|
rlm@33
|
160 Movie.inputBufferSize = BUFFER_GROWTH_SIZE * alloc_chunks;
|
rlm@33
|
161 Movie.inputBuffer = (uint8 *)realloc(Movie.inputBuffer, Movie.inputBufferSize);
|
rlm@33
|
162 // FIXME: this only fixes the random input problem during dma-frame-skip, but not the skip
|
rlm@33
|
163 memset(Movie.inputBuffer + old_size, 0, Movie.inputBufferSize - old_size);
|
rlm@33
|
164 Movie.inputBufferPtr = Movie.inputBuffer + ptr_offset;
|
rlm@33
|
165 }
|
rlm@1
|
166 }
|
rlm@1
|
167
|
rlm@1
|
168 static int read_movie_header(FILE *file, SMovie &movie)
|
rlm@1
|
169 {
|
rlm@33
|
170 assert(file != NULL);
|
rlm@33
|
171 assert(VBM_HEADER_SIZE == sizeof(SMovieFileHeader)); // sanity check on the header type definition
|
rlm@1
|
172
|
rlm@33
|
173 uint8 headerData [VBM_HEADER_SIZE];
|
rlm@1
|
174
|
rlm@33
|
175 if (fread(headerData, 1, VBM_HEADER_SIZE, file) != VBM_HEADER_SIZE)
|
rlm@33
|
176 return MOVIE_WRONG_FORMAT; // if we failed to read in all VBM_HEADER_SIZE bytes of the header
|
rlm@1
|
177
|
rlm@33
|
178 const uint8 * ptr = headerData;
|
rlm@33
|
179 SMovieFileHeader &header = movie.header;
|
rlm@1
|
180
|
rlm@33
|
181 header.magic = Pop32(ptr);
|
rlm@33
|
182 if (header.magic != VBM_MAGIC)
|
rlm@33
|
183 return MOVIE_WRONG_FORMAT;
|
rlm@1
|
184
|
rlm@33
|
185 header.version = Pop32(ptr);
|
rlm@33
|
186 if (header.version != VBM_VERSION)
|
rlm@33
|
187 return MOVIE_WRONG_VERSION;
|
rlm@1
|
188
|
rlm@33
|
189 header.uid = Pop32(ptr);
|
rlm@33
|
190 header.length_frames = Pop32(ptr) + 1; // HACK: add 1 to the length for compatibility
|
rlm@33
|
191 header.rerecord_count = Pop32(ptr);
|
rlm@1
|
192
|
rlm@33
|
193 header.startFlags = Pop8(ptr);
|
rlm@33
|
194 header.controllerFlags = Pop8(ptr);
|
rlm@33
|
195 header.typeFlags = Pop8(ptr);
|
rlm@33
|
196 header.optionFlags = Pop8(ptr);
|
rlm@1
|
197
|
rlm@33
|
198 header.saveType = Pop32(ptr);
|
rlm@33
|
199 header.flashSize = Pop32(ptr);
|
rlm@33
|
200 header.gbEmulatorType = Pop32(ptr);
|
rlm@1
|
201
|
rlm@33
|
202 for (int i = 0; i < 12; i++)
|
rlm@33
|
203 header.romTitle[i] = Pop8(ptr);
|
rlm@1
|
204
|
rlm@33
|
205 header.minorVersion = Pop8(ptr);
|
rlm@1
|
206
|
rlm@33
|
207 header.romCRC = Pop8(ptr);
|
rlm@33
|
208 header.romOrBiosChecksum = Pop16(ptr);
|
rlm@33
|
209 header.romGameCode = Pop32(ptr);
|
rlm@1
|
210
|
rlm@33
|
211 header.offset_to_savestate = Pop32(ptr);
|
rlm@33
|
212 header.offset_to_controller_data = Pop32(ptr);
|
rlm@1
|
213
|
rlm@33
|
214 return MOVIE_SUCCESS;
|
rlm@1
|
215 }
|
rlm@1
|
216
|
rlm@1
|
217 static void write_movie_header(FILE *file, const SMovie &movie)
|
rlm@1
|
218 {
|
rlm@33
|
219 assert(ftell(file) == 0); // we assume file points to beginning of movie file
|
rlm@1
|
220
|
rlm@33
|
221 uint8 headerData [VBM_HEADER_SIZE];
|
rlm@33
|
222 uint8 *ptr = headerData;
|
rlm@33
|
223 const SMovieFileHeader &header = movie.header;
|
rlm@1
|
224
|
rlm@33
|
225 Push32(header.magic, ptr);
|
rlm@33
|
226 Push32(header.version, ptr);
|
rlm@1
|
227
|
rlm@33
|
228 Push32(header.uid, ptr);
|
rlm@33
|
229 Push32(header.length_frames - 1, ptr); // HACK: reduce the length by 1 for compatibility with certain faulty old tools
|
rlm@33
|
230 // like TME
|
rlm@33
|
231 Push32(header.rerecord_count, ptr);
|
rlm@1
|
232
|
rlm@33
|
233 Push8(header.startFlags, ptr);
|
rlm@33
|
234 Push8(header.controllerFlags, ptr);
|
rlm@33
|
235 Push8(header.typeFlags, ptr);
|
rlm@33
|
236 Push8(header.optionFlags, ptr);
|
rlm@1
|
237
|
rlm@33
|
238 Push32(header.saveType, ptr);
|
rlm@33
|
239 Push32(header.flashSize, ptr);
|
rlm@33
|
240 Push32(header.gbEmulatorType, ptr);
|
rlm@1
|
241
|
rlm@33
|
242 for (int i = 0; i < 12; ++i)
|
rlm@33
|
243 Push8(header.romTitle[i], ptr);
|
rlm@1
|
244
|
rlm@33
|
245 Push8(header.minorVersion, ptr);
|
rlm@1
|
246
|
rlm@33
|
247 Push8(header.romCRC, ptr);
|
rlm@33
|
248 Push16(header.romOrBiosChecksum, ptr);
|
rlm@33
|
249 Push32(header.romGameCode, ptr);
|
rlm@1
|
250
|
rlm@33
|
251 Push32(header.offset_to_savestate, ptr);
|
rlm@33
|
252 Push32(header.offset_to_controller_data, ptr);
|
rlm@1
|
253
|
rlm@33
|
254 fwrite(headerData, 1, VBM_HEADER_SIZE, file);
|
rlm@1
|
255 }
|
rlm@1
|
256
|
rlm@1
|
257 static void flush_movie_header()
|
rlm@1
|
258 {
|
rlm@33
|
259 assert(Movie.file != 0 && "logical error!");
|
rlm@33
|
260 if (!Movie.file)
|
rlm@33
|
261 return;
|
rlm@1
|
262
|
rlm@33
|
263 long originalPos = ftell(Movie.file);
|
rlm@1
|
264
|
rlm@33
|
265 // (over-)write the header
|
rlm@33
|
266 fseek(Movie.file, 0, SEEK_SET);
|
rlm@33
|
267 write_movie_header(Movie.file, Movie);
|
rlm@1
|
268
|
rlm@33
|
269 fflush(Movie.file);
|
rlm@1
|
270
|
rlm@33
|
271 fseek(Movie.file, originalPos, SEEK_SET);
|
rlm@1
|
272 }
|
rlm@1
|
273
|
rlm@1
|
274 static void flush_movie_frames()
|
rlm@1
|
275 {
|
rlm@33
|
276 assert(Movie.file && "logical error!");
|
rlm@33
|
277 if (!Movie.file)
|
rlm@33
|
278 return;
|
rlm@1
|
279
|
rlm@33
|
280 long originalPos = ftell(Movie.file);
|
rlm@1
|
281
|
rlm@33
|
282 // overwrite the controller data
|
rlm@33
|
283 fseek(Movie.file, Movie.header.offset_to_controller_data, SEEK_SET);
|
rlm@33
|
284 fwrite(Movie.inputBuffer, 1, Movie.bytesPerFrame * Movie.header.length_frames, Movie.file);
|
rlm@1
|
285
|
rlm@33
|
286 fflush(Movie.file);
|
rlm@1
|
287
|
rlm@33
|
288 fseek(Movie.file, originalPos, SEEK_SET);
|
rlm@1
|
289 }
|
rlm@1
|
290
|
rlm@1
|
291 static void truncate_movie(long length)
|
rlm@1
|
292 {
|
rlm@33
|
293 // truncate movie to length
|
rlm@33
|
294 // NOTE: it's certain that the savestate block is never after the
|
rlm@33
|
295 // controller data block, because the VBM format decrees it.
|
rlm@1
|
296
|
rlm@33
|
297 assert(Movie.file && length >= 0);
|
rlm@33
|
298 if (!Movie.file || length < 0)
|
rlm@33
|
299 return;
|
rlm@1
|
300
|
rlm@33
|
301 assert(Movie.header.offset_to_savestate <= Movie.header.offset_to_controller_data);
|
rlm@33
|
302 if (Movie.header.offset_to_savestate > Movie.header.offset_to_controller_data)
|
rlm@33
|
303 return;
|
rlm@1
|
304
|
rlm@33
|
305 Movie.header.length_frames = length;
|
rlm@33
|
306 flush_movie_header();
|
rlm@33
|
307 const long truncLen = long(Movie.header.offset_to_controller_data + Movie.bytesPerFrame * length);
|
rlm@33
|
308 if (file_length(Movie.file) != truncLen)
|
rlm@33
|
309 {
|
rlm@33
|
310 ftruncate(fileno(Movie.file), truncLen);
|
rlm@33
|
311 }
|
rlm@1
|
312 }
|
rlm@1
|
313
|
rlm@1
|
314 static void remember_input_state()
|
rlm@1
|
315 {
|
rlm@33
|
316 for (int i = 0; i < MOVIE_NUM_OF_POSSIBLE_CONTROLLERS; ++i)
|
rlm@33
|
317 {
|
rlm@33
|
318 if (systemCartridgeType == 0)
|
rlm@1
|
319 {
|
rlm@33
|
320 initialInputs[i] = u16(~P1 & 0x03FF);
|
rlm@1
|
321 }
|
rlm@33
|
322 else
|
rlm@33
|
323 {
|
rlm@33
|
324 extern int32 gbJoymask[4];
|
rlm@33
|
325 for (int i = 0; i < 4; ++i)
|
rlm@33
|
326 initialInputs[i] = u16(gbJoymask[i] & 0xFFFF);
|
rlm@33
|
327 }
|
rlm@33
|
328 }
|
rlm@1
|
329 }
|
rlm@1
|
330
|
rlm@1
|
331 static void change_state(MovieState new_state)
|
rlm@1
|
332 {
|
rlm@1
|
333 #if (defined(WIN32) && !defined(SDL))
|
rlm@33
|
334 theApp.frameSearching = false;
|
rlm@33
|
335 theApp.frameSearchSkipping = false;
|
rlm@1
|
336 #endif
|
rlm@1
|
337
|
rlm@33
|
338 if (new_state == MOVIE_STATE_NONE)
|
rlm@33
|
339 {
|
rlm@33
|
340 Movie.pauseFrame = -1;
|
rlm@33
|
341
|
rlm@33
|
342 if (Movie.state == MOVIE_STATE_NONE)
|
rlm@33
|
343 return;
|
rlm@33
|
344
|
rlm@33
|
345 truncate_movie(Movie.header.length_frames);
|
rlm@33
|
346
|
rlm@33
|
347 fclose(Movie.file);
|
rlm@33
|
348 Movie.file = NULL;
|
rlm@33
|
349 Movie.currentFrame = 0;
|
rlm@33
|
350 #if (defined(WIN32) && !defined(SDL))
|
rlm@33
|
351 // undo changes to border settings
|
rlm@33
|
352 {
|
rlm@33
|
353 gbBorderOn = prevBorder;
|
rlm@33
|
354 theApp.winGbBorderOn = prevWinBorder;
|
rlm@33
|
355 gbBorderAutomatic = prevBorderAuto;
|
rlm@33
|
356 systemGbBorderOn();
|
rlm@33
|
357 }
|
rlm@33
|
358 #endif
|
rlm@33
|
359 gbEmulatorType = prevEmulatorType;
|
rlm@33
|
360
|
rlm@33
|
361 extern int32 gbDMASpeedVersion;
|
rlm@33
|
362 gbDMASpeedVersion = 1;
|
rlm@33
|
363
|
rlm@33
|
364 extern int32 gbEchoRAMFixOn;
|
rlm@33
|
365 gbEchoRAMFixOn = 1;
|
rlm@33
|
366
|
rlm@33
|
367 gbNullInputHackTempEnabled = gbNullInputHackEnabled;
|
rlm@35
|
368
|
rlm@33
|
369 if (Movie.inputBuffer)
|
rlm@1
|
370 {
|
rlm@33
|
371 free(Movie.inputBuffer);
|
rlm@33
|
372 Movie.inputBuffer = NULL;
|
rlm@33
|
373 }
|
rlm@35
|
374
|
rlm@33
|
375 }
|
rlm@33
|
376 else if (new_state == MOVIE_STATE_PLAY)
|
rlm@33
|
377 {
|
rlm@33
|
378 assert(Movie.file);
|
rlm@1
|
379
|
rlm@33
|
380 // this would cause problems if not dealt with
|
rlm@33
|
381 if (Movie.currentFrame >= Movie.header.length_frames)
|
rlm@33
|
382 {
|
rlm@33
|
383 new_state = MOVIE_STATE_END;
|
rlm@33
|
384 Movie.inputBufferPtr = Movie.inputBuffer + Movie.bytesPerFrame * Movie.header.length_frames;
|
rlm@33
|
385 }
|
rlm@33
|
386 }
|
rlm@33
|
387 else if (new_state == MOVIE_STATE_RECORD)
|
rlm@33
|
388 {
|
rlm@33
|
389 assert(Movie.file);
|
rlm@1
|
390
|
rlm@33
|
391 // this would cause problems if not dealt with
|
rlm@33
|
392 if (Movie.currentFrame > Movie.header.length_frames)
|
rlm@1
|
393 {
|
rlm@33
|
394 new_state = MOVIE_STATE_END;
|
rlm@33
|
395 Movie.inputBufferPtr = Movie.inputBuffer + Movie.bytesPerFrame * Movie.header.length_frames;
|
rlm@1
|
396 }
|
rlm@1
|
397
|
rlm@33
|
398 fseek(Movie.file, Movie.header.offset_to_controller_data + Movie.bytesPerFrame * Movie.currentFrame, SEEK_SET);
|
rlm@33
|
399 }
|
rlm@33
|
400
|
rlm@33
|
401 if (new_state == MOVIE_STATE_END && Movie.state != MOVIE_STATE_END)
|
rlm@33
|
402 {
|
rlm@33
|
403 #if defined(SDL)
|
rlm@33
|
404 systemClearJoypads();
|
rlm@33
|
405 #endif
|
rlm@33
|
406 systemScreenMessage("Movie end");
|
rlm@33
|
407 }
|
rlm@33
|
408
|
rlm@33
|
409 Movie.state = new_state;
|
rlm@33
|
410
|
rlm@33
|
411 // checking for movie end
|
rlm@33
|
412 bool willPause = false;
|
rlm@33
|
413
|
rlm@33
|
414 // if the movie's been set to pause at a certain frame
|
rlm@33
|
415 if (Movie.state != MOVIE_STATE_NONE && Movie.pauseFrame >= 0 && Movie.currentFrame == (uint32)Movie.pauseFrame)
|
rlm@33
|
416 {
|
rlm@33
|
417 Movie.pauseFrame = -1;
|
rlm@33
|
418 willPause = true;
|
rlm@33
|
419 }
|
rlm@33
|
420
|
rlm@33
|
421 if (Movie.state == MOVIE_STATE_END)
|
rlm@33
|
422 {
|
rlm@33
|
423 if (Movie.currentFrame == Movie.header.length_frames)
|
rlm@1
|
424 {
|
rlm@1
|
425 #if (defined(WIN32) && !defined(SDL))
|
rlm@33
|
426 if (theApp.movieOnEndPause)
|
rlm@33
|
427 {
|
rlm@33
|
428 willPause = true;
|
rlm@33
|
429 }
|
rlm@1
|
430 #else
|
rlm@33
|
431 // SDL FIXME
|
rlm@1
|
432 #endif
|
rlm@1
|
433
|
rlm@1
|
434 #if (defined(WIN32) && !defined(SDL))
|
rlm@33
|
435 switch (theApp.movieOnEndBehavior)
|
rlm@33
|
436 {
|
rlm@33
|
437 case 1:
|
rlm@33
|
438 // the old behavior
|
rlm@33
|
439 //VBAMovieRestart();
|
rlm@33
|
440 break;
|
rlm@33
|
441 case 2:
|
rlm@1
|
442 #else
|
rlm@33
|
443 // SDL FIXME
|
rlm@1
|
444 #endif
|
rlm@33
|
445 if (Movie.RecordedThisSession)
|
rlm@33
|
446 {
|
rlm@33
|
447 // if user has been recording this movie since the last time it started playing,
|
rlm@33
|
448 // they probably don't want the movie to end now during playback,
|
rlm@33
|
449 // so switch back to recording when it reaches the end
|
rlm@33
|
450 VBAMovieSwitchToRecording();
|
rlm@33
|
451 systemScreenMessage("Recording resumed");
|
rlm@33
|
452 willPause = true;
|
rlm@33
|
453 }
|
rlm@1
|
454 #if (defined(WIN32) && !defined(SDL))
|
rlm@33
|
455 break;
|
rlm@33
|
456 case 3:
|
rlm@33
|
457 // keep open
|
rlm@33
|
458 break;
|
rlm@33
|
459 case 0:
|
rlm@33
|
460 // fall through
|
rlm@33
|
461 default:
|
rlm@33
|
462 // close movie
|
rlm@33
|
463 //VBAMovieStop(false);
|
rlm@33
|
464 break;
|
rlm@33
|
465 }
|
rlm@1
|
466 #else
|
rlm@33
|
467 // SDL FIXME
|
rlm@1
|
468 #endif
|
rlm@33
|
469 }
|
rlm@1
|
470 #if 1
|
rlm@33
|
471 else if (Movie.currentFrame > Movie.header.length_frames)
|
rlm@33
|
472 {
|
rlm@1
|
473 #if (defined(WIN32) && !defined(SDL))
|
rlm@33
|
474 switch (theApp.movieOnEndBehavior)
|
rlm@33
|
475 {
|
rlm@33
|
476 case 1:
|
rlm@33
|
477 // FIXME: this should be delayed till the current frame ends
|
rlm@33
|
478 VBAMovieRestart();
|
rlm@33
|
479 break;
|
rlm@33
|
480 case 2:
|
rlm@33
|
481 // nothing
|
rlm@33
|
482 break;
|
rlm@33
|
483 case 3:
|
rlm@33
|
484 // keep open
|
rlm@33
|
485 break;
|
rlm@33
|
486 case 0:
|
rlm@33
|
487 // fall through
|
rlm@33
|
488 default:
|
rlm@33
|
489 // close movie
|
rlm@33
|
490 VBAMovieStop(false);
|
rlm@33
|
491 break;
|
rlm@33
|
492 }
|
rlm@1
|
493 #else
|
rlm@33
|
494 // SDLFIXME
|
rlm@1
|
495 #endif
|
rlm@33
|
496 }
|
rlm@1
|
497 #endif
|
rlm@33
|
498 } // end if (Movie.state == MOVIE_STATE_END)
|
rlm@1
|
499
|
rlm@33
|
500 if (willPause)
|
rlm@33
|
501 {
|
rlm@33
|
502 systemSetPause(true);
|
rlm@33
|
503 }
|
rlm@1
|
504 }
|
rlm@1
|
505
|
rlm@1
|
506 void VBAMovieInit()
|
rlm@1
|
507 {
|
rlm@33
|
508 memset(&Movie, 0, sizeof(Movie));
|
rlm@33
|
509 Movie.state = MOVIE_STATE_NONE;
|
rlm@33
|
510 Movie.pauseFrame = -1;
|
rlm@1
|
511
|
rlm@33
|
512 resetSignaled = false;
|
rlm@33
|
513 resetSignaledLast = false;
|
rlm@35
|
514
|
rlm@35
|
515 // RLM: should probably add inputBuffer initialization here.
|
rlm@35
|
516 reserve_buffer_space(90001);
|
rlm@1
|
517 }
|
rlm@1
|
518
|
rlm@1
|
519 void VBAMovieGetRomInfo(const SMovie &movieInfo, char romTitle [12], uint32 &romGameCode, uint16 &checksum, uint8 &crc)
|
rlm@1
|
520 {
|
rlm@33
|
521 if (systemCartridgeType == 0) // GBA
|
rlm@33
|
522 {
|
rlm@33
|
523 extern u8 *bios, *rom;
|
rlm@33
|
524 memcpy(romTitle, &rom[0xa0], 12); // GBA TITLE
|
rlm@33
|
525 memcpy(&romGameCode, &rom[0xac], 4); // GBA ROM GAME CODE
|
rlm@33
|
526 if ((movieInfo.header.optionFlags & MOVIE_SETTING_USEBIOSFILE) != 0)
|
rlm@33
|
527 checksum = utilCalcBIOSChecksum(bios, 4); // GBA BIOS CHECKSUM
|
rlm@33
|
528 else
|
rlm@33
|
529 checksum = 0;
|
rlm@33
|
530 crc = rom[0xbd]; // GBA ROM CRC
|
rlm@33
|
531 }
|
rlm@33
|
532 else // non-GBA
|
rlm@33
|
533 {
|
rlm@33
|
534 extern u8 *gbRom;
|
rlm@33
|
535 memcpy(romTitle, &gbRom[0x134], 12); // GB TITLE (note this can be 15 but is truncated to 12)
|
rlm@33
|
536 romGameCode = (uint32)gbRom[0x146]; // GB ROM UNIT CODE
|
rlm@1
|
537
|
rlm@33
|
538 checksum = (gbRom[0x14e] << 8) | gbRom[0x14f]; // GB ROM CHECKSUM, read from big-endian
|
rlm@33
|
539 crc = gbRom[0x14d]; // GB ROM CRC
|
rlm@33
|
540 }
|
rlm@1
|
541 }
|
rlm@1
|
542
|
rlm@1
|
543 #ifdef SDL
|
rlm@1
|
544 static void GetBatterySaveName(char *buffer)
|
rlm@1
|
545 {
|
rlm@33
|
546 extern char batteryDir[2048], filename[2048]; // from SDL.cpp
|
rlm@33
|
547 extern char *sdlGetFilename(char *name); // from SDL.cpp
|
rlm@33
|
548 if (batteryDir[0])
|
rlm@33
|
549 sprintf(buffer, "%s/%s.sav", batteryDir, sdlGetFilename(filename));
|
rlm@33
|
550 else
|
rlm@33
|
551 sprintf(buffer, "%s.sav", filename);
|
rlm@1
|
552 }
|
rlm@1
|
553
|
rlm@1
|
554 #endif
|
rlm@1
|
555
|
rlm@1
|
556 static void SetPlayEmuSettings()
|
rlm@1
|
557 {
|
rlm@33
|
558 prevEmulatorType = gbEmulatorType;
|
rlm@33
|
559 gbEmulatorType = Movie.header.gbEmulatorType;
|
rlm@1
|
560
|
rlm@1
|
561 #if (defined(WIN32) && !defined(SDL))
|
rlm@33
|
562 // theApp.removeIntros = false;
|
rlm@33
|
563 theApp.skipBiosFile = (Movie.header.optionFlags & MOVIE_SETTING_SKIPBIOSFILE) != 0;
|
rlm@33
|
564 theApp.useBiosFile = (Movie.header.optionFlags & MOVIE_SETTING_USEBIOSFILE) != 0;
|
rlm@1
|
565 #else
|
rlm@33
|
566 extern int saveType, sdlRtcEnable, sdlFlashSize; // from SDL.cpp
|
rlm@33
|
567 extern bool8 useBios, skipBios, removeIntros; // from SDL.cpp
|
rlm@33
|
568 useBios = (Movie.header.optionFlags & MOVIE_SETTING_USEBIOSFILE) != 0;
|
rlm@33
|
569 skipBios = (Movie.header.optionFlags & MOVIE_SETTING_SKIPBIOSFILE) != 0;
|
rlm@33
|
570 removeIntros = false /*(Movie.header.optionFlags & MOVIE_SETTING_REMOVEINTROS) != 0*/;
|
rlm@1
|
571 #endif
|
rlm@1
|
572
|
rlm@33
|
573 extern void SetPrefetchHack(bool);
|
rlm@33
|
574 if (systemCartridgeType == 0) // lag disablement applies only to GBA
|
rlm@33
|
575 SetPrefetchHack((Movie.header.optionFlags & MOVIE_SETTING_LAGHACK) != 0);
|
rlm@1
|
576
|
rlm@33
|
577 gbNullInputHackTempEnabled = ((Movie.header.optionFlags & MOVIE_SETTING_GBINPUTHACK) != 0);
|
rlm@1
|
578
|
rlm@33
|
579 // some GB/GBC games depend on the sound rate, so just use the highest one
|
rlm@33
|
580 systemSoundSetQuality(1);
|
rlm@33
|
581 useOldFrameTiming = false;
|
rlm@1
|
582
|
rlm@33
|
583 extern int32 gbDMASpeedVersion;
|
rlm@33
|
584 if ((Movie.header.optionFlags & MOVIE_SETTING_GBCFF55FIX) != 0)
|
rlm@33
|
585 gbDMASpeedVersion = 1;
|
rlm@33
|
586 else
|
rlm@33
|
587 gbDMASpeedVersion = 0; // old CGB HDMA5 timing was used
|
rlm@1
|
588
|
rlm@33
|
589 extern int32 gbEchoRAMFixOn;
|
rlm@33
|
590 if ((Movie.header.optionFlags & MOVIE_SETTING_GBECHORAMFIX) != 0)
|
rlm@33
|
591 gbEchoRAMFixOn = 1;
|
rlm@33
|
592 else
|
rlm@33
|
593 gbEchoRAMFixOn = 0;
|
rlm@1
|
594
|
rlm@1
|
595 #if (defined(WIN32) && !defined(SDL))
|
rlm@33
|
596 rtcEnable((Movie.header.optionFlags & MOVIE_SETTING_RTCENABLE) != 0);
|
rlm@33
|
597 theApp.winSaveType = Movie.header.saveType;
|
rlm@33
|
598 theApp.winFlashSize = Movie.header.flashSize;
|
rlm@1
|
599
|
rlm@33
|
600 prevBorder = gbBorderOn;
|
rlm@33
|
601 prevWinBorder = theApp.winGbBorderOn;
|
rlm@33
|
602 prevBorderAuto = gbBorderAutomatic;
|
rlm@33
|
603 if ((gbEmulatorType == 2 || gbEmulatorType == 5)
|
rlm@33
|
604 && !theApp.hideMovieBorder) // games played in SGB mode can have a border
|
rlm@33
|
605 {
|
rlm@33
|
606 gbBorderOn = true;
|
rlm@33
|
607 theApp.winGbBorderOn = true;
|
rlm@33
|
608 gbBorderAutomatic = false;
|
rlm@33
|
609 }
|
rlm@33
|
610 else
|
rlm@33
|
611 {
|
rlm@33
|
612 gbBorderOn = false;
|
rlm@33
|
613 theApp.winGbBorderOn = false;
|
rlm@33
|
614 gbBorderAutomatic = false;
|
rlm@33
|
615 if (theApp.hideMovieBorder)
|
rlm@1
|
616 {
|
rlm@33
|
617 theApp.hideMovieBorder = false;
|
rlm@33
|
618 prevBorder = false; // it might be expected behaviour that it stays hidden after the movie
|
rlm@1
|
619 }
|
rlm@33
|
620 }
|
rlm@33
|
621 systemGbBorderOn();
|
rlm@1
|
622 #else
|
rlm@33
|
623 sdlRtcEnable = (Movie.header.optionFlags & MOVIE_SETTING_RTCENABLE) != 0;
|
rlm@33
|
624 saveType = Movie.header.saveType;
|
rlm@33
|
625 sdlFlashSize = Movie.header.flashSize;
|
rlm@1
|
626 #endif
|
rlm@1
|
627 }
|
rlm@1
|
628
|
rlm@1
|
629 static void HardResetAndSRAMClear()
|
rlm@1
|
630 {
|
rlm@1
|
631 #if (defined(WIN32) && !defined(SDL))
|
rlm@33
|
632 winEraseBatteryFile(); // delete the damn SRAM file and keep it from being resurrected from RAM
|
rlm@33
|
633 MainWnd *temp = ((MainWnd *)theApp.m_pMainWnd);
|
rlm@33
|
634 if (!temp->winFileRun(true)) // restart running the game
|
rlm@33
|
635 {
|
rlm@33
|
636 temp->winFileClose();
|
rlm@33
|
637 }
|
rlm@1
|
638 #else
|
rlm@33
|
639 char fname [1024];
|
rlm@33
|
640 GetBatterySaveName(fname);
|
rlm@33
|
641 remove(fname); // delete the damn SRAM file
|
rlm@1
|
642
|
rlm@33
|
643 // Henceforth, emuCleanUp means "clear out SRAM"
|
rlm@33
|
644 //theEmulator.emuCleanUp(); // keep it from being resurrected from RAM <--This is wrong, it'll deallocate all variables --Felipe
|
rlm@1
|
645
|
rlm@33
|
646 /// FIXME the correct SDL code to call for a full restart isn't in a function yet
|
rlm@33
|
647 theEmulator.emuReset(false);
|
rlm@1
|
648 #endif
|
rlm@1
|
649 }
|
rlm@1
|
650
|
rlm@1
|
651 int VBAMovieOpen(const char *filename, bool8 read_only)
|
rlm@1
|
652 {
|
rlm@33
|
653 loadingMovie = true;
|
rlm@33
|
654 uint8 movieReadOnly = read_only ? 1 : 0;
|
rlm@1
|
655
|
rlm@33
|
656 FILE * file;
|
rlm@33
|
657 STREAM stream;
|
rlm@33
|
658 int result;
|
rlm@33
|
659 int fn;
|
rlm@1
|
660
|
rlm@33
|
661 char movie_filename[_MAX_PATH];
|
rlm@1
|
662 #ifdef WIN32
|
rlm@33
|
663 _fullpath(movie_filename, filename, _MAX_PATH);
|
rlm@1
|
664 #else
|
rlm@33
|
665 // SDL FIXME: convert to fullpath
|
rlm@33
|
666 strncpy(movie_filename, filename, _MAX_PATH);
|
rlm@33
|
667 movie_filename[_MAX_PATH - 1] = '\0';
|
rlm@1
|
668 #endif
|
rlm@1
|
669
|
rlm@33
|
670 if (movie_filename[0] == '\0')
|
rlm@33
|
671 { loadingMovie = false; return MOVIE_FILE_NOT_FOUND; }
|
rlm@1
|
672
|
rlm@33
|
673 if (!emulating)
|
rlm@33
|
674 { loadingMovie = false; return MOVIE_UNKNOWN_ERROR; }
|
rlm@1
|
675
|
rlm@33
|
676 // bool alreadyOpen = (Movie.file != NULL && _stricmp(movie_filename, Movie.filename) == 0);
|
rlm@1
|
677
|
rlm@33
|
678 // if (alreadyOpen)
|
rlm@33
|
679 change_state(MOVIE_STATE_NONE); // have to stop current movie before trying to re-open it
|
rlm@1
|
680
|
rlm@33
|
681 if (!(file = fopen(movie_filename, "rb+")))
|
rlm@33
|
682 if (!(file = fopen(movie_filename, "rb")))
|
rlm@33
|
683 { loadingMovie = false; return MOVIE_FILE_NOT_FOUND; }
|
rlm@33
|
684 //else
|
rlm@33
|
685 // movieReadOnly = 2; // we have to open the movie twice, no need to do this both times
|
rlm@1
|
686
|
rlm@33
|
687 // if (!alreadyOpen)
|
rlm@33
|
688 // change_state(MOVIE_STATE_NONE); // stop current movie when we're able to open the other one
|
rlm@33
|
689 //
|
rlm@33
|
690 // if (!(file = fopen(movie_filename, "rb+")))
|
rlm@33
|
691 // if(!(file = fopen(movie_filename, "rb")))
|
rlm@33
|
692 // {loadingMovie = false; return MOVIE_FILE_NOT_FOUND;}
|
rlm@33
|
693 // else
|
rlm@33
|
694 // movieReadOnly = 2;
|
rlm@1
|
695
|
rlm@33
|
696 // clear out the current movie
|
rlm@33
|
697 VBAMovieInit();
|
rlm@1
|
698
|
rlm@33
|
699 // read header
|
rlm@33
|
700 if ((result = read_movie_header(file, Movie)) != MOVIE_SUCCESS)
|
rlm@33
|
701 {
|
rlm@33
|
702 fclose(file);
|
rlm@33
|
703 { loadingMovie = false; return result; }
|
rlm@33
|
704 }
|
rlm@1
|
705
|
rlm@33
|
706 // set emulator settings that make the movie more likely to stay synchronized
|
rlm@33
|
707 SetPlayEmuSettings();
|
rlm@1
|
708
|
rlm@33
|
709 // extern bool systemLoadBIOS();
|
rlm@33
|
710 // if (!systemLoadBIOS())
|
rlm@33
|
711 // { loadingMovie = false; return MOVIE_UNKNOWN_ERROR; }
|
rlm@1
|
712
|
rlm@33
|
713 // read the metadata / author info from file
|
rlm@33
|
714 fread(Movie.authorInfo, 1, MOVIE_METADATA_SIZE, file);
|
rlm@33
|
715 fn = dup(fileno(file)); // XXX: why does this fail?? it returns -1 but errno == 0
|
rlm@33
|
716 fclose(file);
|
rlm@1
|
717
|
rlm@33
|
718 // apparently this lseek is necessary
|
rlm@33
|
719 lseek(fn, Movie.header.offset_to_savestate, SEEK_SET);
|
rlm@33
|
720 if (!(stream = utilGzReopen(fn, "rb")))
|
rlm@33
|
721 if (!(stream = utilGzOpen(movie_filename, "rb")))
|
rlm@33
|
722 { loadingMovie = false; return MOVIE_FILE_NOT_FOUND; }
|
rlm@33
|
723 else
|
rlm@33
|
724 fn = dup(fileno(file));
|
rlm@33
|
725 // in case the above dup failed but opening the file normally doesn't fail
|
rlm@1
|
726
|
rlm@33
|
727 if (Movie.header.startFlags & MOVIE_START_FROM_SNAPSHOT)
|
rlm@33
|
728 {
|
rlm@33
|
729 // load the snapshot
|
rlm@33
|
730 result = theEmulator.emuReadStateFromStream(stream) ? MOVIE_SUCCESS : MOVIE_WRONG_FORMAT;
|
rlm@1
|
731
|
rlm@33
|
732 // FIXME: Kludge for conversion
|
rlm@33
|
733 remember_input_state();
|
rlm@33
|
734 }
|
rlm@33
|
735 else if (Movie.header.startFlags & MOVIE_START_FROM_SRAM)
|
rlm@33
|
736 {
|
rlm@33
|
737 // 'soft' reset:
|
rlm@33
|
738 theEmulator.emuReset(false);
|
rlm@1
|
739
|
rlm@33
|
740 // load the SRAM
|
rlm@33
|
741 result = theEmulator.emuReadBatteryFromStream(stream) ? MOVIE_SUCCESS : MOVIE_WRONG_FORMAT;
|
rlm@33
|
742 }
|
rlm@33
|
743 else
|
rlm@33
|
744 {
|
rlm@33
|
745 HardResetAndSRAMClear();
|
rlm@33
|
746 }
|
rlm@1
|
747
|
rlm@33
|
748 utilGzClose(stream);
|
rlm@1
|
749
|
rlm@33
|
750 if (result != MOVIE_SUCCESS)
|
rlm@33
|
751 { loadingMovie = false; return result; }
|
rlm@1
|
752
|
rlm@33
|
753 // if (!(file = fopen(movie_filename, /*read_only ? "rb" :*/ "rb+"))) // want to be able to switch out of read-only later
|
rlm@33
|
754 // {
|
rlm@33
|
755 // if(!Movie.readOnly || !(file = fopen(movie_filename, "rb"))) // try read-only if failed
|
rlm@33
|
756 // return MOVIE_FILE_NOT_FOUND;
|
rlm@33
|
757 // }
|
rlm@33
|
758 if (!(file = fopen(movie_filename, "rb+")))
|
rlm@33
|
759 if (!(file = fopen(movie_filename, "rb")))
|
rlm@33
|
760 { loadingMovie = false; return MOVIE_FILE_NOT_FOUND; }
|
rlm@33
|
761 else
|
rlm@33
|
762 movieReadOnly = 2;
|
rlm@1
|
763
|
rlm@33
|
764 // recalculate length of movie from the file size
|
rlm@33
|
765 Movie.bytesPerFrame = bytes_per_frame(Movie);
|
rlm@33
|
766 fseek(file, 0, SEEK_END);
|
rlm@33
|
767 long fileSize = ftell(file);
|
rlm@33
|
768 Movie.header.length_frames = (fileSize - Movie.header.offset_to_controller_data) / Movie.bytesPerFrame;
|
rlm@1
|
769
|
rlm@33
|
770 if (fseek(file, Movie.header.offset_to_controller_data, SEEK_SET))
|
rlm@33
|
771 { fclose(file); loadingMovie = false; return MOVIE_WRONG_FORMAT; }
|
rlm@1
|
772
|
rlm@33
|
773 strcpy(Movie.filename, movie_filename);
|
rlm@33
|
774 Movie.file = file;
|
rlm@33
|
775 Movie.inputBufferPtr = Movie.inputBuffer;
|
rlm@33
|
776 Movie.currentFrame = 0;
|
rlm@33
|
777 Movie.readOnly = movieReadOnly;
|
rlm@33
|
778 Movie.RecordedThisSession = false;
|
rlm@1
|
779
|
rlm@33
|
780 // read controller data
|
rlm@33
|
781 uint32 to_read = Movie.bytesPerFrame * Movie.header.length_frames;
|
rlm@33
|
782 reserve_buffer_space(to_read);
|
rlm@33
|
783 fread(Movie.inputBuffer, 1, to_read, file);
|
rlm@1
|
784
|
rlm@33
|
785 change_state(MOVIE_STATE_PLAY);
|
rlm@1
|
786
|
rlm@33
|
787 char messageString[64] = "Movie ";
|
rlm@33
|
788 bool converted = false;
|
rlm@33
|
789 if (autoConvertMovieWhenPlaying)
|
rlm@33
|
790 {
|
rlm@33
|
791 int result = VBAMovieConvertCurrent();
|
rlm@33
|
792 if (result == MOVIE_SUCCESS)
|
rlm@33
|
793 strcat(messageString, "converted and ");
|
rlm@33
|
794 else if (result == MOVIE_WRONG_VERSION)
|
rlm@33
|
795 strcat(messageString, "higher revision ");
|
rlm@33
|
796 }
|
rlm@1
|
797
|
rlm@33
|
798 if (Movie.state == MOVIE_STATE_PLAY)
|
rlm@33
|
799 strcat(messageString, "replaying ");
|
rlm@33
|
800 else
|
rlm@33
|
801 strcat(messageString, "finished ");
|
rlm@33
|
802 if (Movie.readOnly)
|
rlm@33
|
803 strcat(messageString, "(read)");
|
rlm@33
|
804 else
|
rlm@33
|
805 strcat(messageString, "(edit)");
|
rlm@33
|
806 systemScreenMessage(messageString);
|
rlm@1
|
807
|
rlm@33
|
808 VBAUpdateButtonPressDisplay();
|
rlm@33
|
809 VBAUpdateFrameCountDisplay();
|
rlm@33
|
810 systemRefreshScreen();
|
rlm@1
|
811
|
rlm@33
|
812 { loadingMovie = false; return MOVIE_SUCCESS; }
|
rlm@1
|
813 }
|
rlm@1
|
814
|
rlm@1
|
815 static void SetRecordEmuSettings()
|
rlm@1
|
816 {
|
rlm@33
|
817 Movie.header.optionFlags = 0;
|
rlm@1
|
818 #if (defined(WIN32) && !defined(SDL))
|
rlm@33
|
819 if (theApp.useBiosFile)
|
rlm@33
|
820 Movie.header.optionFlags |= MOVIE_SETTING_USEBIOSFILE;
|
rlm@33
|
821 if (theApp.skipBiosFile)
|
rlm@33
|
822 Movie.header.optionFlags |= MOVIE_SETTING_SKIPBIOSFILE;
|
rlm@33
|
823 if (rtcIsEnabled())
|
rlm@33
|
824 Movie.header.optionFlags |= MOVIE_SETTING_RTCENABLE;
|
rlm@33
|
825 Movie.header.saveType = theApp.winSaveType;
|
rlm@33
|
826 Movie.header.flashSize = theApp.winFlashSize;
|
rlm@1
|
827 #else
|
rlm@33
|
828 extern int saveType, sdlRtcEnable, sdlFlashSize; // from SDL.cpp
|
rlm@33
|
829 extern bool8 useBios, skipBios; // from SDL.cpp
|
rlm@33
|
830 if (useBios)
|
rlm@33
|
831 Movie.header.optionFlags |= MOVIE_SETTING_USEBIOSFILE;
|
rlm@33
|
832 if (skipBios)
|
rlm@33
|
833 Movie.header.optionFlags |= MOVIE_SETTING_SKIPBIOSFILE;
|
rlm@33
|
834 if (sdlRtcEnable)
|
rlm@33
|
835 Movie.header.optionFlags |= MOVIE_SETTING_RTCENABLE;
|
rlm@33
|
836 Movie.header.saveType = saveType;
|
rlm@33
|
837 Movie.header.flashSize = sdlFlashSize;
|
rlm@1
|
838 #endif
|
rlm@33
|
839 prevEmulatorType = Movie.header.gbEmulatorType = gbEmulatorType;
|
rlm@1
|
840
|
rlm@33
|
841 if (!memLagTempEnabled)
|
rlm@33
|
842 Movie.header.optionFlags |= MOVIE_SETTING_LAGHACK;
|
rlm@1
|
843
|
rlm@33
|
844 if (gbNullInputHackTempEnabled)
|
rlm@33
|
845 Movie.header.optionFlags |= MOVIE_SETTING_GBINPUTHACK;
|
rlm@1
|
846
|
rlm@33
|
847 Movie.header.optionFlags |= MOVIE_SETTING_GBCFF55FIX;
|
rlm@33
|
848 extern int32 gbDMASpeedVersion;
|
rlm@33
|
849 gbDMASpeedVersion = 1;
|
rlm@1
|
850
|
rlm@33
|
851 Movie.header.optionFlags |= MOVIE_SETTING_GBECHORAMFIX;
|
rlm@33
|
852 extern int32 gbEchoRAMFixOn;
|
rlm@33
|
853 gbEchoRAMFixOn = 1;
|
rlm@1
|
854
|
rlm@33
|
855 // some GB/GBC games depend on the sound rate, so just use the highest one
|
rlm@33
|
856 systemSoundSetQuality(1);
|
rlm@1
|
857
|
rlm@33
|
858 useOldFrameTiming = false;
|
rlm@1
|
859
|
rlm@1
|
860 #if (defined(WIN32) && !defined(SDL))
|
rlm@33
|
861 // theApp.removeIntros = false;
|
rlm@1
|
862
|
rlm@33
|
863 prevBorder = gbBorderOn;
|
rlm@33
|
864 prevWinBorder = theApp.winGbBorderOn;
|
rlm@33
|
865 prevBorderAuto = gbBorderAutomatic;
|
rlm@33
|
866 if (gbEmulatorType == 2 || gbEmulatorType == 5) // only games played in SGB mode will have a border
|
rlm@33
|
867 {
|
rlm@33
|
868 gbBorderOn = true;
|
rlm@33
|
869 theApp.winGbBorderOn = true;
|
rlm@33
|
870 gbBorderAutomatic = false;
|
rlm@33
|
871 }
|
rlm@33
|
872 else
|
rlm@33
|
873 {
|
rlm@33
|
874 gbBorderOn = false;
|
rlm@33
|
875 theApp.winGbBorderOn = false;
|
rlm@33
|
876 gbBorderAutomatic = false;
|
rlm@33
|
877 }
|
rlm@33
|
878 systemGbBorderOn();
|
rlm@1
|
879 #else
|
rlm@33
|
880 /// SDLFIXME
|
rlm@1
|
881 #endif
|
rlm@1
|
882 }
|
rlm@1
|
883
|
rlm@1
|
884 uint16 VBAMovieGetCurrentInputOf(int controllerNum, bool normalOnly)
|
rlm@1
|
885 {
|
rlm@33
|
886 if (controllerNum < 0 || controllerNum >= MOVIE_NUM_OF_POSSIBLE_CONTROLLERS)
|
rlm@33
|
887 return 0;
|
rlm@1
|
888
|
rlm@33
|
889 return normalOnly ? (currentButtons[controllerNum] & BUTTON_REGULAR_MASK) : currentButtons[controllerNum];
|
rlm@1
|
890 }
|
rlm@1
|
891
|
rlm@1
|
892 int VBAMovieCreate(const char *filename, const char *authorInfo, uint8 startFlags, uint8 controllerFlags, uint8 typeFlags)
|
rlm@1
|
893 {
|
rlm@33
|
894 // make sure at least one controller is enabled
|
rlm@33
|
895 if ((controllerFlags & MOVIE_CONTROLLERS_ANY_MASK) == 0)
|
rlm@33
|
896 return MOVIE_WRONG_FORMAT;
|
rlm@1
|
897
|
rlm@33
|
898 if (!emulating)
|
rlm@33
|
899 return MOVIE_UNKNOWN_ERROR;
|
rlm@1
|
900
|
rlm@33
|
901 loadingMovie = true;
|
rlm@1
|
902
|
rlm@33
|
903 FILE * file;
|
rlm@33
|
904 STREAM stream;
|
rlm@33
|
905 int fn;
|
rlm@1
|
906
|
rlm@33
|
907 char movie_filename [_MAX_PATH];
|
rlm@1
|
908 #ifdef WIN32
|
rlm@33
|
909 _fullpath(movie_filename, filename, _MAX_PATH);
|
rlm@1
|
910 #else
|
rlm@33
|
911 // FIXME: convert to fullpath
|
rlm@33
|
912 strncpy(movie_filename, filename, _MAX_PATH);
|
rlm@33
|
913 movie_filename[_MAX_PATH - 1] = '\0';
|
rlm@1
|
914 #endif
|
rlm@1
|
915
|
rlm@33
|
916 bool alreadyOpen = (Movie.file != NULL && stricmp(movie_filename, Movie.filename) == 0);
|
rlm@1
|
917
|
rlm@33
|
918 if (alreadyOpen)
|
rlm@33
|
919 change_state(MOVIE_STATE_NONE); // have to stop current movie before trying to re-open it
|
rlm@1
|
920
|
rlm@33
|
921 if (movie_filename[0] == '\0')
|
rlm@33
|
922 { loadingMovie = false; return MOVIE_FILE_NOT_FOUND; }
|
rlm@33
|
923
|
rlm@33
|
924 if (!(file = fopen(movie_filename, "wb")))
|
rlm@33
|
925 { loadingMovie = false; return MOVIE_FILE_NOT_FOUND; }
|
rlm@33
|
926
|
rlm@33
|
927 if (!alreadyOpen)
|
rlm@33
|
928 change_state(MOVIE_STATE_NONE); // stop current movie when we're able to open the other one
|
rlm@33
|
929
|
rlm@33
|
930 // clear out the current movie
|
rlm@39
|
931 //printf("RLM: movie init\n");
|
rlm@33
|
932
|
rlm@33
|
933 VBAMovieInit();
|
rlm@33
|
934
|
rlm@33
|
935 // fill in the movie's header
|
rlm@33
|
936 Movie.header.uid = (uint32)time(NULL);
|
rlm@33
|
937 Movie.header.magic = VBM_MAGIC;
|
rlm@33
|
938 Movie.header.version = VBM_VERSION;
|
rlm@33
|
939 Movie.header.rerecord_count = 0;
|
rlm@33
|
940 Movie.header.length_frames = 0;
|
rlm@33
|
941 Movie.header.startFlags = startFlags;
|
rlm@33
|
942 Movie.header.controllerFlags = controllerFlags;
|
rlm@33
|
943 Movie.header.typeFlags = typeFlags;
|
rlm@33
|
944 Movie.header.minorVersion = VBM_REVISION;
|
rlm@33
|
945
|
rlm@33
|
946 // set emulator settings that make the movie more likely to stay synchronized when it's later played back
|
rlm@33
|
947 SetRecordEmuSettings();
|
rlm@33
|
948
|
rlm@33
|
949 // set ROM and BIOS checksums and stuff
|
rlm@33
|
950 VBAMovieGetRomInfo(Movie, Movie.header.romTitle, Movie.header.romGameCode, Movie.header.romOrBiosChecksum, Movie.header.romCRC);
|
rlm@33
|
951
|
rlm@39
|
952 //printf("RLM: Writing movie header\n");
|
rlm@33
|
953 // write the header to file
|
rlm@33
|
954 write_movie_header(file, Movie);
|
rlm@33
|
955
|
rlm@39
|
956 //printf("RLM: setting metadata\n");
|
rlm@33
|
957
|
rlm@33
|
958 // copy over the metadata / author info
|
rlm@33
|
959 VBAMovieSetMetadata("________________Robert McIntyre______________________________________________________________________________________________________________________________________________________________________________________________________________________");
|
rlm@33
|
960
|
rlm@39
|
961 //printf("RLM: writing metadata\n");
|
rlm@33
|
962
|
rlm@33
|
963 // write the metadata / author info to file
|
rlm@33
|
964
|
rlm@33
|
965
|
rlm@33
|
966 fwrite(Movie.authorInfo, 1, sizeof(char) * MOVIE_METADATA_SIZE, file);
|
rlm@33
|
967
|
rlm@33
|
968 // write snapshot or SRAM if applicable
|
rlm@33
|
969 if (Movie.header.startFlags & MOVIE_START_FROM_SNAPSHOT
|
rlm@33
|
970 || Movie.header.startFlags & MOVIE_START_FROM_SRAM)
|
rlm@33
|
971 {
|
rlm@33
|
972 Movie.header.offset_to_savestate = (uint32)ftell(file);
|
rlm@33
|
973
|
rlm@33
|
974 // close the file and reopen it as a stream:
|
rlm@33
|
975
|
rlm@33
|
976 fn = dup(fileno(file));
|
rlm@33
|
977 fclose(file);
|
rlm@33
|
978
|
rlm@33
|
979 if (!(stream = utilGzReopen(fn, "ab"))) // append mode to start at end, no seek necessary
|
rlm@1
|
980 { loadingMovie = false; return MOVIE_FILE_NOT_FOUND; }
|
rlm@1
|
981
|
rlm@33
|
982 // write the save data:
|
rlm@33
|
983 if (Movie.header.startFlags & MOVIE_START_FROM_SNAPSHOT)
|
rlm@33
|
984 {
|
rlm@33
|
985 // save snapshot
|
rlm@33
|
986 if (!theEmulator.emuWriteStateToStream(stream))
|
rlm@33
|
987 {
|
rlm@33
|
988 utilGzClose(stream);
|
rlm@33
|
989 { loadingMovie = false; return MOVIE_UNKNOWN_ERROR; }
|
rlm@33
|
990 }
|
rlm@33
|
991 }
|
rlm@33
|
992 else if (Movie.header.startFlags & MOVIE_START_FROM_SRAM)
|
rlm@33
|
993 {
|
rlm@33
|
994 // save SRAM
|
rlm@33
|
995 if (!theEmulator.emuWriteBatteryToStream(stream))
|
rlm@33
|
996 {
|
rlm@33
|
997 utilGzClose(stream);
|
rlm@33
|
998 { loadingMovie = false; return MOVIE_UNKNOWN_ERROR; }
|
rlm@33
|
999 }
|
rlm@33
|
1000
|
rlm@33
|
1001 // 'soft' reset:
|
rlm@33
|
1002 theEmulator.emuReset(false);
|
rlm@33
|
1003 }
|
rlm@33
|
1004
|
rlm@33
|
1005 utilGzClose(stream);
|
rlm@33
|
1006
|
rlm@33
|
1007 // reopen the file and seek back to the end
|
rlm@33
|
1008
|
rlm@33
|
1009 if (!(file = fopen(movie_filename, "rb+")))
|
rlm@1
|
1010 { loadingMovie = false; return MOVIE_FILE_NOT_FOUND; }
|
rlm@1
|
1011
|
rlm@33
|
1012 fseek(file, 0, SEEK_END);
|
rlm@33
|
1013 }
|
rlm@33
|
1014 else // no snapshot or SRAM
|
rlm@33
|
1015 {
|
rlm@33
|
1016 HardResetAndSRAMClear();
|
rlm@33
|
1017 }
|
rlm@1
|
1018
|
rlm@33
|
1019 Movie.header.offset_to_controller_data = (uint32)ftell(file);
|
rlm@1
|
1020
|
rlm@33
|
1021 strcpy(Movie.filename, movie_filename);
|
rlm@33
|
1022 Movie.file = file;
|
rlm@33
|
1023 Movie.bytesPerFrame = bytes_per_frame(Movie);
|
rlm@33
|
1024 Movie.inputBufferPtr = Movie.inputBuffer;
|
rlm@33
|
1025 Movie.currentFrame = 0;
|
rlm@33
|
1026 Movie.readOnly = false;
|
rlm@33
|
1027 Movie.RecordedThisSession = true;
|
rlm@1
|
1028
|
rlm@33
|
1029 change_state(MOVIE_STATE_RECORD);
|
rlm@1
|
1030
|
rlm@33
|
1031 systemScreenMessage("Recording movie...");
|
rlm@33
|
1032 { loadingMovie = false; return MOVIE_SUCCESS; }
|
rlm@1
|
1033 }
|
rlm@1
|
1034
|
rlm@1
|
1035 void VBAUpdateButtonPressDisplay()
|
rlm@1
|
1036 {
|
rlm@33
|
1037 uint32 keys = currentButtons[0] & BUTTON_REGULAR_RECORDING_MASK;
|
rlm@1
|
1038
|
rlm@33
|
1039 const static char KeyMap[] = { 'A', 'B', 's', 'S', '>', '<', '^', 'v', 'R', 'L', '!', '?', '{', '}', 'v', '^' };
|
rlm@33
|
1040 const static int KeyOrder[] = { 5, 6, 4, 7, 0, 1, 9, 8, 3, 2, 12, 15, 13, 14, 11, 10 }; // < ^ > v A B L R S s { = } _
|
rlm@33
|
1041 // ? !
|
rlm@33
|
1042 char buffer[256];
|
rlm@33
|
1043 sprintf(buffer, " ");
|
rlm@1
|
1044
|
rlm@1
|
1045 #ifndef WIN32
|
rlm@33
|
1046 // don't bother color-coding autofire and such
|
rlm@33
|
1047 int i;
|
rlm@33
|
1048 for (i = 0; i < 15; i++)
|
rlm@33
|
1049 {
|
rlm@33
|
1050 int j = KeyOrder[i];
|
rlm@33
|
1051 int mask = (1 << (j));
|
rlm@33
|
1052 buffer[strlen(" ") + i] = ((keys & mask) != 0) ? KeyMap[j] : ' ';
|
rlm@33
|
1053 }
|
rlm@33
|
1054
|
rlm@33
|
1055 systemScreenMessage(buffer, 2, -1);
|
rlm@33
|
1056 #else
|
rlm@33
|
1057 const bool eraseAll = !theApp.inputDisplay;
|
rlm@33
|
1058 uint32 autoHeldKeys = eraseAll ? 0 : theApp.autoHold & BUTTON_REGULAR_RECORDING_MASK;
|
rlm@33
|
1059 uint32 autoFireKeys = eraseAll ? 0 : (theApp.autoFire | theApp.autoFire2) & BUTTON_REGULAR_RECORDING_MASK;
|
rlm@33
|
1060 uint32 pressedKeys = eraseAll ? 0 : keys;
|
rlm@33
|
1061
|
rlm@33
|
1062 char colorList[64];
|
rlm@33
|
1063 memset(colorList, 1, strlen(buffer));
|
rlm@33
|
1064
|
rlm@33
|
1065 if (!eraseAll)
|
rlm@33
|
1066 {
|
rlm@33
|
1067 for (int i = 0; i < 15; i++)
|
rlm@1
|
1068 {
|
rlm@33
|
1069 const int j = KeyOrder[i];
|
rlm@33
|
1070 const int mask = (1 << (j));
|
rlm@33
|
1071 bool pressed = (pressedKeys & mask) != 0;
|
rlm@33
|
1072 const bool autoHeld = (autoHeldKeys & mask) != 0;
|
rlm@33
|
1073 const bool autoFired = (autoFireKeys & mask) != 0;
|
rlm@33
|
1074 const bool erased = (lastKeys & mask) != 0 && (!pressed && !autoHeld && !autoFired);
|
rlm@33
|
1075 extern int textMethod;
|
rlm@33
|
1076 if (textMethod != 2 && (autoHeld || (autoFired && !pressed) || erased))
|
rlm@33
|
1077 {
|
rlm@33
|
1078 int colorNum = 1; // default is white
|
rlm@33
|
1079 if (autoHeld)
|
rlm@33
|
1080 colorNum += (pressed ? 2 : 1); // yellow if pressed, red if not
|
rlm@33
|
1081 else if (autoFired)
|
rlm@33
|
1082 colorNum += 5; // blue if autofired and not currently pressed
|
rlm@33
|
1083 else if (erased)
|
rlm@33
|
1084 colorNum += 8; // black on black
|
rlm@33
|
1085
|
rlm@33
|
1086 colorList[strlen(" ") + i] = colorNum;
|
rlm@33
|
1087 pressed = true;
|
rlm@33
|
1088 }
|
rlm@33
|
1089 buffer[strlen(" ") + i] = pressed ? KeyMap[j] : ' ';
|
rlm@1
|
1090 }
|
rlm@33
|
1091 }
|
rlm@1
|
1092
|
rlm@33
|
1093 lastKeys = currentButtons[0];
|
rlm@33
|
1094 lastKeys |= theApp.autoHold & BUTTON_REGULAR_RECORDING_MASK;
|
rlm@33
|
1095 lastKeys |= (theApp.autoFire | theApp.autoFire2) & BUTTON_REGULAR_RECORDING_MASK;
|
rlm@1
|
1096
|
rlm@33
|
1097 systemScreenMessage(buffer, 2, -1, colorList);
|
rlm@1
|
1098 #endif
|
rlm@1
|
1099 }
|
rlm@1
|
1100
|
rlm@1
|
1101 void VBAUpdateFrameCountDisplay()
|
rlm@1
|
1102 {
|
rlm@33
|
1103 const int MAGICAL_NUMBER = 64; // FIXME: this won't do any better, but only to remind you of sz issues
|
rlm@33
|
1104 char frameDisplayString[MAGICAL_NUMBER];
|
rlm@33
|
1105 char lagFrameDisplayString[MAGICAL_NUMBER];
|
rlm@33
|
1106 char extraCountDisplayString[MAGICAL_NUMBER];
|
rlm@1
|
1107
|
rlm@1
|
1108 #if (defined(WIN32) && !defined(SDL))
|
rlm@33
|
1109 if (theApp.frameCounter)
|
rlm@33
|
1110 #else
|
rlm@33
|
1111 /// SDL FIXME
|
rlm@33
|
1112 #endif
|
rlm@33
|
1113 {
|
rlm@33
|
1114 switch (Movie.state)
|
rlm@33
|
1115 {
|
rlm@33
|
1116 case MOVIE_STATE_PLAY:
|
rlm@33
|
1117 case MOVIE_STATE_END:
|
rlm@33
|
1118 {
|
rlm@33
|
1119 sprintf(frameDisplayString, "%d / %d", Movie.currentFrame, Movie.header.length_frames);
|
rlm@33
|
1120 if (!Movie.readOnly)
|
rlm@33
|
1121 strcat(frameDisplayString, " (edit)");
|
rlm@33
|
1122 break;
|
rlm@33
|
1123 }
|
rlm@33
|
1124 case MOVIE_STATE_RECORD:
|
rlm@33
|
1125 {
|
rlm@33
|
1126 sprintf(frameDisplayString, "%d (record)", Movie.currentFrame);
|
rlm@33
|
1127 break;
|
rlm@33
|
1128 }
|
rlm@33
|
1129 default:
|
rlm@33
|
1130 {
|
rlm@33
|
1131 sprintf(frameDisplayString, "%d (no movie)", systemCounters.frameCount);
|
rlm@33
|
1132 break;
|
rlm@33
|
1133 }
|
rlm@33
|
1134 }
|
rlm@33
|
1135
|
rlm@33
|
1136 #if (defined(WIN32) && !defined(SDL))
|
rlm@33
|
1137 if (theApp.lagCounter)
|
rlm@1
|
1138 #else
|
rlm@1
|
1139 /// SDL FIXME
|
rlm@1
|
1140 #endif
|
rlm@1
|
1141 {
|
rlm@33
|
1142 // sprintf(lagFrameDisplayString, " %c %d", systemCounters.laggedLast ? '*' : '|', systemCounters.lagCount);
|
rlm@33
|
1143 sprintf(lagFrameDisplayString, " | %d%s", systemCounters.lagCount, systemCounters.laggedLast ? " *" : "");
|
rlm@33
|
1144 strcat(frameDisplayString, lagFrameDisplayString);
|
rlm@33
|
1145 }
|
rlm@1
|
1146
|
rlm@1
|
1147 #if (defined(WIN32) && !defined(SDL))
|
rlm@33
|
1148 if (theApp.extraCounter)
|
rlm@1
|
1149 #else
|
rlm@1
|
1150 /// SDL FIXME
|
rlm@1
|
1151 #endif
|
rlm@33
|
1152 {
|
rlm@33
|
1153 sprintf(extraCountDisplayString, " | %d", systemCounters.frameCount - systemCounters.extraCount);
|
rlm@33
|
1154 strcat(frameDisplayString, extraCountDisplayString);
|
rlm@33
|
1155 }
|
rlm@33
|
1156 }
|
rlm@33
|
1157 #if (defined(WIN32) && !defined(SDL))
|
rlm@33
|
1158 else
|
rlm@33
|
1159 {
|
rlm@33
|
1160 frameDisplayString[0] = '\0';
|
rlm@33
|
1161 }
|
rlm@33
|
1162 #else
|
rlm@33
|
1163 /// SDL FIXME
|
rlm@33
|
1164 #endif
|
rlm@33
|
1165 systemScreenMessage(frameDisplayString, 1, -1);
|
rlm@1
|
1166 }
|
rlm@1
|
1167
|
rlm@1
|
1168 // this function should only be called once every frame
|
rlm@1
|
1169 void VBAMovieUpdateState()
|
rlm@1
|
1170 {
|
rlm@33
|
1171 ++Movie.currentFrame;
|
rlm@39
|
1172 //printf("RLM: inside updateState\n");
|
rlm@33
|
1173 if (Movie.state == MOVIE_STATE_PLAY)
|
rlm@33
|
1174 {
|
rlm@33
|
1175 Movie.inputBufferPtr += Movie.bytesPerFrame;
|
rlm@33
|
1176 if (Movie.currentFrame >= Movie.header.length_frames)
|
rlm@1
|
1177 {
|
rlm@33
|
1178 // the movie ends anyway; what to do next depends on the settings
|
rlm@33
|
1179 change_state(MOVIE_STATE_END);
|
rlm@1
|
1180 }
|
rlm@33
|
1181 }
|
rlm@33
|
1182 else if (Movie.state == MOVIE_STATE_RECORD)
|
rlm@33
|
1183 {
|
rlm@39
|
1184 //printf("RLM: Movie_STATE_RECORD\n");
|
rlm@36
|
1185 VBAMovieWrite(0,true);
|
rlm@33
|
1186 // use first fseek?
|
rlm@33
|
1187 //TODO: THis is the problem.
|
rlm@35
|
1188 if (Movie.inputBuffer){
|
rlm@35
|
1189 fwrite(Movie.inputBufferPtr, 1, Movie.bytesPerFrame, Movie.file);
|
rlm@35
|
1190 }
|
rlm@39
|
1191 //printf("RLM: write successful.\n");
|
rlm@33
|
1192 Movie.header.length_frames = Movie.currentFrame;
|
rlm@33
|
1193 Movie.inputBufferPtr += Movie.bytesPerFrame;
|
rlm@33
|
1194 Movie.RecordedThisSession = true;
|
rlm@33
|
1195 flush_movie_header();
|
rlm@33
|
1196 }
|
rlm@33
|
1197 else if (Movie.state == MOVIE_STATE_END)
|
rlm@33
|
1198 {
|
rlm@33
|
1199 change_state(MOVIE_STATE_END);
|
rlm@33
|
1200 }
|
rlm@1
|
1201 }
|
rlm@1
|
1202
|
rlm@1
|
1203 void VBAMovieRead(int i, bool /*sensor*/)
|
rlm@1
|
1204 {
|
rlm@33
|
1205 if (Movie.state != MOVIE_STATE_PLAY)
|
rlm@33
|
1206 return;
|
rlm@1
|
1207
|
rlm@33
|
1208 if (i < 0 || i >= MOVIE_NUM_OF_POSSIBLE_CONTROLLERS)
|
rlm@33
|
1209 return; // not a controller we're recognizing
|
rlm@1
|
1210
|
rlm@33
|
1211 if (Movie.header.controllerFlags & MOVIE_CONTROLLER(i))
|
rlm@33
|
1212 {
|
rlm@33
|
1213 currentButtons[i] = Read16(Movie.inputBufferPtr + CONTROLLER_DATA_SIZE * i);
|
rlm@33
|
1214 }
|
rlm@33
|
1215 else
|
rlm@33
|
1216 {
|
rlm@33
|
1217 currentButtons[i] = 0; // pretend the controller is disconnected
|
rlm@33
|
1218 }
|
rlm@39
|
1219
|
rlm@39
|
1220 printf("RLM: button %d\n",currentButtons[i]);
|
rlm@39
|
1221 if ((currentButtons[i] & BUTTON_MASK_NEW_RESET) != 0){
|
rlm@39
|
1222 printf("RLM: reset signaled\n");
|
rlm@33
|
1223 resetSignaled = true;
|
rlm@39
|
1224 }
|
rlm@1
|
1225 }
|
rlm@1
|
1226
|
rlm@1
|
1227 void VBAMovieWrite(int i, bool /*sensor*/)
|
rlm@1
|
1228 {
|
rlm@33
|
1229 if (Movie.state != MOVIE_STATE_RECORD)
|
rlm@33
|
1230 return;
|
rlm@1
|
1231
|
rlm@33
|
1232 if (i < 0 || i >= MOVIE_NUM_OF_POSSIBLE_CONTROLLERS)
|
rlm@33
|
1233 return; // not a controller we're recognizing
|
rlm@1
|
1234
|
rlm@33
|
1235 reserve_buffer_space((uint32)((Movie.inputBufferPtr - Movie.inputBuffer) + Movie.bytesPerFrame));
|
rlm@1
|
1236
|
rlm@36
|
1237 if (Movie.header.controllerFlags)
|
rlm@33
|
1238 {
|
rlm@33
|
1239 // get the current controller data
|
rlm@33
|
1240 uint16 buttonData = currentButtons[i];
|
rlm@39
|
1241 //printf("RLM: currentButtons %i\n", currentButtons[0]);
|
rlm@33
|
1242
|
rlm@33
|
1243 // mask away the irrelevent bits
|
rlm@33
|
1244 buttonData &= BUTTON_REGULAR_MASK | BUTTON_MOTION_MASK;
|
rlm@33
|
1245
|
rlm@33
|
1246 // soft-reset "button" for 1 frame if the game is reset while recording
|
rlm@33
|
1247 if (resetSignaled)
|
rlm@1
|
1248 {
|
rlm@33
|
1249 buttonData |= BUTTON_MASK_NEW_RESET;
|
rlm@33
|
1250 }
|
rlm@1
|
1251
|
rlm@33
|
1252 // backward compatibility kludge
|
rlm@33
|
1253 if (resetSignaledLast)
|
rlm@33
|
1254 {
|
rlm@33
|
1255 buttonData |= BUTTON_MASK_OLD_RESET;
|
rlm@33
|
1256 }
|
rlm@1
|
1257
|
rlm@33
|
1258 Write16(buttonData, Movie.inputBufferPtr + CONTROLLER_DATA_SIZE * i);
|
rlm@1
|
1259
|
rlm@33
|
1260 // and for display
|
rlm@33
|
1261 currentButtons[i] = buttonData;
|
rlm@33
|
1262 }
|
rlm@33
|
1263 else
|
rlm@33
|
1264 {
|
rlm@33
|
1265 // pretend the controller is disconnected (otherwise input it gives could cause desync since we're not writing it to the
|
rlm@33
|
1266 // movie)
|
rlm@33
|
1267 currentButtons[i] = 0;
|
rlm@33
|
1268 }
|
rlm@1
|
1269 }
|
rlm@1
|
1270
|
rlm@1
|
1271 void VBAMovieStop(bool8 suppress_message)
|
rlm@1
|
1272 {
|
rlm@33
|
1273 if (Movie.state != MOVIE_STATE_NONE)
|
rlm@33
|
1274 {
|
rlm@33
|
1275 change_state(MOVIE_STATE_NONE);
|
rlm@33
|
1276 if (!suppress_message)
|
rlm@33
|
1277 systemScreenMessage("Movie stop");
|
rlm@33
|
1278 }
|
rlm@1
|
1279 }
|
rlm@1
|
1280
|
rlm@1
|
1281 int VBAMovieGetInfo(const char *filename, SMovie *info)
|
rlm@1
|
1282 {
|
rlm@33
|
1283 assert(info != NULL);
|
rlm@33
|
1284 if (info == NULL)
|
rlm@33
|
1285 return -1;
|
rlm@1
|
1286
|
rlm@33
|
1287 FILE * file;
|
rlm@33
|
1288 int result;
|
rlm@33
|
1289 SMovie &local_movie = *info;
|
rlm@1
|
1290
|
rlm@33
|
1291 memset(info, 0, sizeof(*info));
|
rlm@33
|
1292 if (filename[0] == '\0')
|
rlm@33
|
1293 return MOVIE_FILE_NOT_FOUND;
|
rlm@33
|
1294 if (!(file = fopen(filename, "rb")))
|
rlm@33
|
1295 return MOVIE_FILE_NOT_FOUND;
|
rlm@1
|
1296
|
rlm@33
|
1297 // read header
|
rlm@33
|
1298 if ((result = (read_movie_header(file, local_movie))) != MOVIE_SUCCESS)
|
rlm@33
|
1299 {
|
rlm@33
|
1300 fclose(file);
|
rlm@33
|
1301 return result;
|
rlm@33
|
1302 }
|
rlm@1
|
1303
|
rlm@33
|
1304 // read the metadata / author info from file
|
rlm@33
|
1305 fread(local_movie.authorInfo, 1, sizeof(char) * MOVIE_METADATA_SIZE, file);
|
rlm@1
|
1306
|
rlm@33
|
1307 strncpy(local_movie.filename, filename, _MAX_PATH);
|
rlm@33
|
1308 local_movie.filename[_MAX_PATH - 1] = '\0';
|
rlm@1
|
1309
|
rlm@33
|
1310 if (Movie.file != NULL && stricmp(local_movie.filename, Movie.filename) == 0) // alreadyOpen
|
rlm@33
|
1311 {
|
rlm@33
|
1312 local_movie.bytesPerFrame = Movie.bytesPerFrame;
|
rlm@33
|
1313 local_movie.header.length_frames = Movie.header.length_frames;
|
rlm@33
|
1314 }
|
rlm@33
|
1315 else
|
rlm@33
|
1316 {
|
rlm@33
|
1317 // recalculate length of movie from the file size
|
rlm@33
|
1318 local_movie.bytesPerFrame = bytes_per_frame(local_movie);
|
rlm@33
|
1319 fseek(file, 0, SEEK_END);
|
rlm@33
|
1320 int fileSize = ftell(file);
|
rlm@33
|
1321 local_movie.header.length_frames =
|
rlm@33
|
1322 (fileSize - local_movie.header.offset_to_controller_data) / local_movie.bytesPerFrame;
|
rlm@33
|
1323 }
|
rlm@1
|
1324
|
rlm@33
|
1325 fclose(file);
|
rlm@1
|
1326
|
rlm@33
|
1327 if (access(filename, W_OK))
|
rlm@33
|
1328 info->readOnly = true;
|
rlm@1
|
1329
|
rlm@33
|
1330 return MOVIE_SUCCESS;
|
rlm@1
|
1331 }
|
rlm@1
|
1332
|
rlm@1
|
1333 bool8 VBAMovieActive()
|
rlm@1
|
1334 {
|
rlm@33
|
1335 return (Movie.state != MOVIE_STATE_NONE);
|
rlm@1
|
1336 }
|
rlm@1
|
1337
|
rlm@1
|
1338 bool8 VBAMovieLoading()
|
rlm@1
|
1339 {
|
rlm@33
|
1340 return loadingMovie;
|
rlm@1
|
1341 }
|
rlm@1
|
1342
|
rlm@1
|
1343 bool8 VBAMoviePlaying()
|
rlm@1
|
1344 {
|
rlm@33
|
1345 return (Movie.state == MOVIE_STATE_PLAY);
|
rlm@1
|
1346 }
|
rlm@1
|
1347
|
rlm@1
|
1348 bool8 VBAMovieRecording()
|
rlm@1
|
1349 {
|
rlm@33
|
1350 return (Movie.state == MOVIE_STATE_RECORD);
|
rlm@1
|
1351 }
|
rlm@1
|
1352
|
rlm@1
|
1353 bool8 VBAMovieReadOnly()
|
rlm@1
|
1354 {
|
rlm@33
|
1355 if (!VBAMovieActive())
|
rlm@33
|
1356 return false;
|
rlm@1
|
1357
|
rlm@33
|
1358 return Movie.readOnly;
|
rlm@1
|
1359 }
|
rlm@1
|
1360
|
rlm@1
|
1361 void VBAMovieToggleReadOnly()
|
rlm@1
|
1362 {
|
rlm@33
|
1363 if (!VBAMovieActive())
|
rlm@33
|
1364 return;
|
rlm@1
|
1365
|
rlm@33
|
1366 if (Movie.readOnly != 2)
|
rlm@33
|
1367 {
|
rlm@33
|
1368 Movie.readOnly = !Movie.readOnly;
|
rlm@1
|
1369
|
rlm@33
|
1370 systemScreenMessage(Movie.readOnly ? "Movie now read-only" : "Movie now editable");
|
rlm@33
|
1371 }
|
rlm@33
|
1372 else
|
rlm@33
|
1373 {
|
rlm@33
|
1374 systemScreenMessage("Can't toggle read-only movie");
|
rlm@33
|
1375 }
|
rlm@1
|
1376 }
|
rlm@1
|
1377
|
rlm@1
|
1378 uint32 VBAMovieGetVersion()
|
rlm@1
|
1379 {
|
rlm@33
|
1380 if (!VBAMovieActive())
|
rlm@33
|
1381 return 0;
|
rlm@1
|
1382
|
rlm@33
|
1383 return Movie.header.version;
|
rlm@1
|
1384 }
|
rlm@1
|
1385
|
rlm@1
|
1386 uint32 VBAMovieGetMinorVersion()
|
rlm@1
|
1387 {
|
rlm@33
|
1388 if (!VBAMovieActive())
|
rlm@33
|
1389 return 0;
|
rlm@1
|
1390
|
rlm@33
|
1391 return Movie.header.minorVersion;
|
rlm@1
|
1392 }
|
rlm@1
|
1393
|
rlm@1
|
1394 uint32 VBAMovieGetId()
|
rlm@1
|
1395 {
|
rlm@33
|
1396 if (!VBAMovieActive())
|
rlm@33
|
1397 return 0;
|
rlm@1
|
1398
|
rlm@33
|
1399 return Movie.header.uid;
|
rlm@1
|
1400 }
|
rlm@1
|
1401
|
rlm@1
|
1402 uint32 VBAMovieGetLength()
|
rlm@1
|
1403 {
|
rlm@33
|
1404 if (!VBAMovieActive())
|
rlm@33
|
1405 return 0;
|
rlm@1
|
1406
|
rlm@33
|
1407 return Movie.header.length_frames;
|
rlm@1
|
1408 }
|
rlm@1
|
1409
|
rlm@1
|
1410 uint32 VBAMovieGetFrameCounter()
|
rlm@1
|
1411 {
|
rlm@33
|
1412 if (!VBAMovieActive())
|
rlm@33
|
1413 return 0;
|
rlm@1
|
1414
|
rlm@33
|
1415 return Movie.currentFrame;
|
rlm@1
|
1416 }
|
rlm@1
|
1417
|
rlm@1
|
1418 uint32 VBAMovieGetRerecordCount()
|
rlm@1
|
1419 {
|
rlm@33
|
1420 if (!VBAMovieActive())
|
rlm@33
|
1421 return 0;
|
rlm@1
|
1422
|
rlm@33
|
1423 return Movie.header.rerecord_count;
|
rlm@1
|
1424 }
|
rlm@1
|
1425
|
rlm@1
|
1426 uint32 VBAMovieSetRerecordCount(uint32 newRerecordCount)
|
rlm@1
|
1427 {
|
rlm@33
|
1428 uint32 oldRerecordCount = 0;
|
rlm@33
|
1429 if (!VBAMovieActive())
|
rlm@33
|
1430 return 0;
|
rlm@1
|
1431
|
rlm@33
|
1432 oldRerecordCount = Movie.header.rerecord_count;
|
rlm@33
|
1433 Movie.header.rerecord_count = newRerecordCount;
|
rlm@33
|
1434 return oldRerecordCount;
|
rlm@1
|
1435 }
|
rlm@1
|
1436
|
rlm@1
|
1437 std::string VBAMovieGetAuthorInfo()
|
rlm@1
|
1438 {
|
rlm@33
|
1439 if (!VBAMovieActive())
|
rlm@33
|
1440 return "";
|
rlm@1
|
1441
|
rlm@33
|
1442 return Movie.authorInfo;
|
rlm@1
|
1443 }
|
rlm@1
|
1444
|
rlm@1
|
1445 std::string VBAMovieGetFilename()
|
rlm@1
|
1446 {
|
rlm@33
|
1447 if (!VBAMovieActive())
|
rlm@33
|
1448 return "";
|
rlm@1
|
1449
|
rlm@33
|
1450 return Movie.filename;
|
rlm@1
|
1451 }
|
rlm@1
|
1452
|
rlm@1
|
1453 void VBAMovieFreeze(uint8 * *buf, uint32 *size)
|
rlm@1
|
1454 {
|
rlm@33
|
1455 // sanity check
|
rlm@33
|
1456 if (!VBAMovieActive())
|
rlm@33
|
1457 {
|
rlm@33
|
1458 return;
|
rlm@33
|
1459 }
|
rlm@1
|
1460
|
rlm@33
|
1461 *buf = NULL;
|
rlm@33
|
1462 *size = 0;
|
rlm@1
|
1463
|
rlm@33
|
1464 // compute size needed for the buffer
|
rlm@33
|
1465 // room for header.uid, currentFrame, and header.length_frames
|
rlm@33
|
1466 uint32 size_needed = sizeof(Movie.header.uid) + sizeof(Movie.currentFrame) + sizeof(Movie.header.length_frames);
|
rlm@33
|
1467 size_needed += (uint32)(Movie.bytesPerFrame * Movie.header.length_frames);
|
rlm@33
|
1468 *buf = new uint8[size_needed];
|
rlm@33
|
1469 *size = size_needed;
|
rlm@1
|
1470
|
rlm@33
|
1471 uint8 *ptr = *buf;
|
rlm@33
|
1472 if (!ptr)
|
rlm@33
|
1473 {
|
rlm@33
|
1474 return;
|
rlm@33
|
1475 }
|
rlm@1
|
1476
|
rlm@33
|
1477 Push32(Movie.header.uid, ptr);
|
rlm@33
|
1478 Push32(Movie.currentFrame, ptr);
|
rlm@33
|
1479 Push32(Movie.header.length_frames - 1, ptr); // HACK: shorten the length by 1 for backward compatibility
|
rlm@1
|
1480
|
rlm@33
|
1481 memcpy(ptr, Movie.inputBuffer, Movie.bytesPerFrame * Movie.header.length_frames);
|
rlm@1
|
1482 }
|
rlm@1
|
1483
|
rlm@1
|
1484 int VBAMovieUnfreeze(const uint8 *buf, uint32 size)
|
rlm@1
|
1485 {
|
rlm@33
|
1486 // sanity check
|
rlm@33
|
1487 if (!VBAMovieActive())
|
rlm@33
|
1488 {
|
rlm@33
|
1489 return MOVIE_NOT_FROM_A_MOVIE;
|
rlm@33
|
1490 }
|
rlm@33
|
1491
|
rlm@33
|
1492 const uint8 *ptr = buf;
|
rlm@33
|
1493 if (size < sizeof(Movie.header.uid) + sizeof(Movie.currentFrame) + sizeof(Movie.header.length_frames))
|
rlm@33
|
1494 {
|
rlm@33
|
1495 return MOVIE_WRONG_FORMAT;
|
rlm@33
|
1496 }
|
rlm@33
|
1497
|
rlm@33
|
1498 uint32 movie_id = Pop32(ptr);
|
rlm@33
|
1499 uint32 current_frame = Pop32(ptr);
|
rlm@33
|
1500 uint32 end_frame = Pop32(ptr) + 1; // HACK: restore the length for backward compatibility
|
rlm@33
|
1501 uint32 space_needed = Movie.bytesPerFrame * end_frame;
|
rlm@33
|
1502
|
rlm@33
|
1503 if (movie_id != Movie.header.uid)
|
rlm@33
|
1504 return MOVIE_NOT_FROM_THIS_MOVIE;
|
rlm@33
|
1505
|
rlm@33
|
1506 if (space_needed > size)
|
rlm@33
|
1507 return MOVIE_WRONG_FORMAT;
|
rlm@33
|
1508
|
rlm@33
|
1509 if (Movie.readOnly)
|
rlm@33
|
1510 {
|
rlm@33
|
1511 // here, we are going to keep the input data from the movie file
|
rlm@33
|
1512 // and simply rewind to the currentFrame pointer
|
rlm@33
|
1513 // this will cause a desync if the savestate is not in sync // <-- NOT ANYMORE
|
rlm@33
|
1514 // with the on-disk recording data, but it's easily solved
|
rlm@33
|
1515 // by loading another savestate or playing the movie from the beginning
|
rlm@33
|
1516
|
rlm@33
|
1517 // don't allow loading a state inconsistent with the current movie
|
rlm@33
|
1518 uint32 length_history = min(current_frame, Movie.header.length_frames);
|
rlm@33
|
1519 if (end_frame < length_history)
|
rlm@33
|
1520 return MOVIE_SNAPSHOT_INCONSISTENT;
|
rlm@33
|
1521
|
rlm@33
|
1522 uint32 space_shared = Movie.bytesPerFrame * length_history;
|
rlm@33
|
1523 if (memcmp(Movie.inputBuffer, ptr, space_shared))
|
rlm@33
|
1524 return MOVIE_SNAPSHOT_INCONSISTENT;
|
rlm@33
|
1525
|
rlm@33
|
1526 Movie.currentFrame = current_frame;
|
rlm@33
|
1527 Movie.inputBufferPtr = Movie.inputBuffer + Movie.bytesPerFrame * min(current_frame, Movie.header.length_frames);
|
rlm@33
|
1528 }
|
rlm@33
|
1529 else
|
rlm@33
|
1530 {
|
rlm@33
|
1531 // here, we are going to take the input data from the savestate
|
rlm@33
|
1532 // and make it the input data for the current movie, then continue
|
rlm@33
|
1533 // writing new input data at the currentFrame pointer
|
rlm@33
|
1534 Movie.currentFrame = current_frame;
|
rlm@33
|
1535 Movie.header.length_frames = end_frame;
|
rlm@33
|
1536 if (!VBALuaRerecordCountSkip())
|
rlm@33
|
1537 ++Movie.header.rerecord_count;
|
rlm@33
|
1538
|
rlm@33
|
1539 Movie.RecordedThisSession = true;
|
rlm@33
|
1540
|
rlm@33
|
1541 // do this before calling reserve_buffer_space()
|
rlm@33
|
1542 Movie.inputBufferPtr = Movie.inputBuffer + Movie.bytesPerFrame * min(current_frame, Movie.header.length_frames);
|
rlm@33
|
1543 reserve_buffer_space(space_needed);
|
rlm@33
|
1544 memcpy(Movie.inputBuffer, ptr, space_needed);
|
rlm@33
|
1545
|
rlm@33
|
1546 // for consistency, no auto movie conversion here since we don't auto convert the corresponding savestate
|
rlm@33
|
1547 flush_movie_header();
|
rlm@33
|
1548 flush_movie_frames();
|
rlm@33
|
1549 }
|
rlm@33
|
1550
|
rlm@33
|
1551 change_state(MOVIE_STATE_PLAY); // check for movie end
|
rlm@33
|
1552
|
rlm@33
|
1553 // necessary!
|
rlm@33
|
1554 resetSignaled = false;
|
rlm@33
|
1555 resetSignaledLast = false;
|
rlm@33
|
1556
|
rlm@33
|
1557 // necessary to check if there's a reset signal at the previous frame
|
rlm@33
|
1558 if (current_frame > 0)
|
rlm@33
|
1559 {
|
rlm@33
|
1560 const u8 NEW_RESET = u8(BUTTON_MASK_NEW_RESET >> 8);
|
rlm@33
|
1561 for (int i = 0; i < MOVIE_NUM_OF_POSSIBLE_CONTROLLERS; ++i)
|
rlm@1
|
1562 {
|
rlm@33
|
1563 if ((Movie.header.controllerFlags & MOVIE_CONTROLLER(i)) && (*(Movie.inputBufferPtr+1- Movie.bytesPerFrame) & NEW_RESET))
|
rlm@33
|
1564 {
|
rlm@33
|
1565 resetSignaledLast = true;
|
rlm@33
|
1566 break;
|
rlm@33
|
1567 }
|
rlm@1
|
1568 }
|
rlm@33
|
1569 }
|
rlm@1
|
1570
|
rlm@33
|
1571 return MOVIE_SUCCESS;
|
rlm@1
|
1572 }
|
rlm@1
|
1573
|
rlm@1
|
1574 bool VBAMovieEnded()
|
rlm@1
|
1575 {
|
rlm@33
|
1576 return (Movie.state == MOVIE_STATE_END);
|
rlm@33
|
1577 // return (Movie.state != MOVIE_STATE_NONE && Movie.currentFrame >= Movie.header.length_frames);
|
rlm@1
|
1578 }
|
rlm@1
|
1579
|
rlm@1
|
1580 bool VBAMovieAllowsRerecording()
|
rlm@1
|
1581 {
|
rlm@33
|
1582 bool allows = (Movie.state != MOVIE_STATE_NONE) && (Movie.currentFrame <= Movie.header.length_frames);
|
rlm@33
|
1583 return /*!VBAMovieReadOnly() &&*/ allows;
|
rlm@1
|
1584 }
|
rlm@1
|
1585
|
rlm@1
|
1586 bool VBAMovieSwitchToPlaying()
|
rlm@1
|
1587 {
|
rlm@33
|
1588 if (!VBAMovieActive())
|
rlm@33
|
1589 return false;
|
rlm@1
|
1590
|
rlm@33
|
1591 if (!Movie.readOnly)
|
rlm@33
|
1592 {
|
rlm@33
|
1593 VBAMovieToggleReadOnly();
|
rlm@33
|
1594 }
|
rlm@1
|
1595
|
rlm@33
|
1596 change_state(MOVIE_STATE_PLAY);
|
rlm@33
|
1597 if (Movie.state == MOVIE_STATE_PLAY)
|
rlm@33
|
1598 systemScreenMessage("Movie replay (continue)");
|
rlm@33
|
1599 else
|
rlm@33
|
1600 systemScreenMessage("Movie end");
|
rlm@1
|
1601
|
rlm@33
|
1602 return true;
|
rlm@1
|
1603 }
|
rlm@1
|
1604
|
rlm@1
|
1605 bool VBAMovieSwitchToRecording()
|
rlm@1
|
1606 {
|
rlm@33
|
1607 if (!VBAMovieAllowsRerecording())
|
rlm@33
|
1608 return false;
|
rlm@1
|
1609
|
rlm@33
|
1610 if (Movie.readOnly)
|
rlm@33
|
1611 {
|
rlm@33
|
1612 VBAMovieToggleReadOnly();
|
rlm@33
|
1613 }
|
rlm@1
|
1614
|
rlm@33
|
1615 if (!VBALuaRerecordCountSkip())
|
rlm@33
|
1616 ++Movie.header.rerecord_count;
|
rlm@1
|
1617
|
rlm@33
|
1618 change_state(MOVIE_STATE_RECORD);
|
rlm@33
|
1619 systemScreenMessage("Movie re-record");
|
rlm@1
|
1620
|
rlm@33
|
1621 //truncate_movie(Movie.currentFrame);
|
rlm@1
|
1622
|
rlm@33
|
1623 return true;
|
rlm@1
|
1624 }
|
rlm@1
|
1625
|
rlm@1
|
1626 uint32 VBAMovieGetState()
|
rlm@1
|
1627 {
|
rlm@33
|
1628 // ?
|
rlm@33
|
1629 if (!VBAMovieActive())
|
rlm@33
|
1630 return MOVIE_STATE_NONE;
|
rlm@1
|
1631
|
rlm@33
|
1632 return Movie.state;
|
rlm@1
|
1633 }
|
rlm@1
|
1634
|
rlm@1
|
1635 void VBAMovieSignalReset()
|
rlm@1
|
1636 {
|
rlm@33
|
1637 if (VBAMovieActive())
|
rlm@33
|
1638 resetSignaled = true;
|
rlm@1
|
1639 }
|
rlm@1
|
1640
|
rlm@1
|
1641 void VBAMovieResetIfRequested()
|
rlm@1
|
1642 {
|
rlm@33
|
1643 if (resetSignaled)
|
rlm@33
|
1644 {
|
rlm@33
|
1645 theEmulator.emuReset(false);
|
rlm@33
|
1646 resetSignaled = false;
|
rlm@33
|
1647 resetSignaledLast = true;
|
rlm@33
|
1648 }
|
rlm@33
|
1649 else
|
rlm@33
|
1650 {
|
rlm@33
|
1651 resetSignaledLast = false;
|
rlm@33
|
1652 }
|
rlm@1
|
1653 }
|
rlm@1
|
1654
|
rlm@1
|
1655 void VBAMovieSetMetadata(const char *info)
|
rlm@1
|
1656 {
|
rlm@33
|
1657 if (!memcmp(Movie.authorInfo, info, MOVIE_METADATA_SIZE))
|
rlm@33
|
1658 return;
|
rlm@1
|
1659
|
rlm@33
|
1660 memcpy(Movie.authorInfo, info, MOVIE_METADATA_SIZE); // strncpy would omit post-0 bytes
|
rlm@33
|
1661 Movie.authorInfo[MOVIE_METADATA_SIZE - 1] = '\0';
|
rlm@1
|
1662
|
rlm@33
|
1663 if (Movie.file)
|
rlm@33
|
1664 {
|
rlm@33
|
1665 // (over-)write the header
|
rlm@33
|
1666 fseek(Movie.file, 0, SEEK_SET);
|
rlm@1
|
1667
|
rlm@33
|
1668 write_movie_header(Movie.file, Movie);
|
rlm@1
|
1669
|
rlm@33
|
1670 // write the metadata / author info to file
|
rlm@33
|
1671 fwrite(Movie.authorInfo, 1, sizeof(char) * MOVIE_METADATA_SIZE, Movie.file);
|
rlm@33
|
1672
|
rlm@33
|
1673 fflush(Movie.file);
|
rlm@33
|
1674 }
|
rlm@39
|
1675 //printf("RLM: setMetadata called\n");
|
rlm@33
|
1676
|
rlm@1
|
1677 }
|
rlm@1
|
1678
|
rlm@1
|
1679 void VBAMovieRestart()
|
rlm@1
|
1680 {
|
rlm@33
|
1681 if (VBAMovieActive())
|
rlm@33
|
1682 {
|
rlm@33
|
1683 systemSoundClearBuffer();
|
rlm@1
|
1684
|
rlm@33
|
1685 bool8 modified = Movie.RecordedThisSession;
|
rlm@1
|
1686
|
rlm@33
|
1687 VBAMovieStop(true);
|
rlm@1
|
1688
|
rlm@33
|
1689 char movieName [_MAX_PATH];
|
rlm@33
|
1690 strncpy(movieName, Movie.filename, _MAX_PATH);
|
rlm@33
|
1691 movieName[_MAX_PATH - 1] = '\0';
|
rlm@33
|
1692 VBAMovieOpen(movieName, Movie.readOnly); // can't just pass in Movie.filename, since VBAMovieOpen clears out Movie's
|
rlm@33
|
1693 // variables
|
rlm@1
|
1694
|
rlm@33
|
1695 Movie.RecordedThisSession = modified;
|
rlm@1
|
1696
|
rlm@33
|
1697 systemScreenMessage("Movie replay (restart)");
|
rlm@33
|
1698 }
|
rlm@1
|
1699 }
|
rlm@1
|
1700
|
rlm@1
|
1701 int VBAMovieGetPauseAt()
|
rlm@1
|
1702 {
|
rlm@33
|
1703 return Movie.pauseFrame;
|
rlm@1
|
1704 }
|
rlm@1
|
1705
|
rlm@1
|
1706 void VBAMovieSetPauseAt(int at)
|
rlm@1
|
1707 {
|
rlm@33
|
1708 Movie.pauseFrame = at;
|
rlm@1
|
1709 }
|
rlm@1
|
1710
|
rlm@1
|
1711 ///////////////////////
|
rlm@1
|
1712 // movie tools
|
rlm@1
|
1713
|
rlm@1
|
1714 // FIXME: is it safe to convert/flush a movie while recording it (considering fseek() problem)?
|
rlm@1
|
1715 int VBAMovieConvertCurrent()
|
rlm@1
|
1716 {
|
rlm@33
|
1717 if (!VBAMovieActive())
|
rlm@33
|
1718 {
|
rlm@33
|
1719 return MOVIE_NOTHING;
|
rlm@33
|
1720 }
|
rlm@33
|
1721
|
rlm@33
|
1722 if (Movie.header.minorVersion > VBM_REVISION)
|
rlm@33
|
1723 {
|
rlm@33
|
1724 return MOVIE_WRONG_VERSION;
|
rlm@33
|
1725 }
|
rlm@33
|
1726
|
rlm@33
|
1727 if (Movie.header.minorVersion == VBM_REVISION)
|
rlm@33
|
1728 {
|
rlm@33
|
1729 return MOVIE_NOTHING;
|
rlm@33
|
1730 }
|
rlm@33
|
1731
|
rlm@33
|
1732 Movie.header.minorVersion = VBM_REVISION;
|
rlm@33
|
1733
|
rlm@33
|
1734 if (Movie.header.length_frames == 0) // this could happen
|
rlm@33
|
1735 {
|
rlm@33
|
1736 truncate_movie(0);
|
rlm@33
|
1737 return MOVIE_SUCCESS;
|
rlm@33
|
1738 }
|
rlm@33
|
1739
|
rlm@33
|
1740 // fix movies recorded from snapshots
|
rlm@33
|
1741 if (Movie.header.startFlags & MOVIE_START_FROM_SNAPSHOT)
|
rlm@33
|
1742 {
|
rlm@33
|
1743 uint8 *firstFramePtr = Movie.inputBuffer;
|
rlm@33
|
1744 for (int i = 0; i < MOVIE_NUM_OF_POSSIBLE_CONTROLLERS; ++i)
|
rlm@1
|
1745 {
|
rlm@33
|
1746 if (Movie.header.controllerFlags & MOVIE_CONTROLLER(i))
|
rlm@33
|
1747 {
|
rlm@33
|
1748 Push16(initialInputs[i], firstFramePtr);
|
rlm@33
|
1749 // note: this is correct since Push16 advances the dest pointer by sizeof u16
|
rlm@33
|
1750 }
|
rlm@1
|
1751 }
|
rlm@33
|
1752 }
|
rlm@1
|
1753
|
rlm@33
|
1754 // convert old resets to new ones
|
rlm@33
|
1755 const u8 OLD_RESET = u8(BUTTON_MASK_OLD_RESET >> 8);
|
rlm@33
|
1756 const u8 NEW_RESET = u8(BUTTON_MASK_NEW_RESET >> 8);
|
rlm@33
|
1757 for (int i = 0; i < MOVIE_NUM_OF_POSSIBLE_CONTROLLERS; ++i)
|
rlm@33
|
1758 {
|
rlm@33
|
1759 if (Movie.header.controllerFlags & MOVIE_CONTROLLER(i))
|
rlm@1
|
1760 {
|
rlm@33
|
1761 uint8 *startPtr = Movie.inputBuffer + sizeof(u16) * i + 1;
|
rlm@33
|
1762 uint8 *endPtr = Movie.inputBuffer + Movie.bytesPerFrame * (Movie.header.length_frames - 1);
|
rlm@33
|
1763 for (; startPtr < endPtr; startPtr += Movie.bytesPerFrame)
|
rlm@33
|
1764 {
|
rlm@33
|
1765 if (startPtr[Movie.bytesPerFrame] & OLD_RESET)
|
rlm@33
|
1766 {
|
rlm@33
|
1767 startPtr[0] |= NEW_RESET;
|
rlm@33
|
1768 }
|
rlm@33
|
1769 }
|
rlm@1
|
1770 }
|
rlm@33
|
1771 }
|
rlm@1
|
1772
|
rlm@33
|
1773 flush_movie_header();
|
rlm@33
|
1774 flush_movie_frames();
|
rlm@33
|
1775 return MOVIE_SUCCESS;
|
rlm@1
|
1776 }
|
rlm@1
|
1777
|
rlm@1
|
1778 bool VBAMovieTuncateAtCurrentFrame()
|
rlm@1
|
1779 {
|
rlm@33
|
1780 if (!VBAMovieActive())
|
rlm@33
|
1781 return false;
|
rlm@1
|
1782
|
rlm@33
|
1783 truncate_movie(Movie.currentFrame);
|
rlm@33
|
1784 change_state(MOVIE_STATE_END);
|
rlm@33
|
1785 systemScreenMessage("Movie truncated");
|
rlm@1
|
1786
|
rlm@33
|
1787 return true;
|
rlm@1
|
1788 }
|
rlm@1
|
1789
|
rlm@1
|
1790 bool VBAMovieFixHeader()
|
rlm@1
|
1791 {
|
rlm@33
|
1792 if (!VBAMovieActive())
|
rlm@33
|
1793 return false;
|
rlm@1
|
1794
|
rlm@33
|
1795 flush_movie_header();
|
rlm@33
|
1796 systemScreenMessage("Movie header fixed");
|
rlm@33
|
1797 return true;
|
rlm@1
|
1798 }
|