comparison src/common/movie.cpp @ 33:44974c3e093b

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