Mercurial > vba-linux
diff src/common/movie.cpp @ 1:f9f4f1b99eed
importing src directory
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Sat, 03 Mar 2012 10:31:27 -0600 |
parents | |
children | 44974c3e093b |
line wrap: on
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/common/movie.cpp Sat Mar 03 10:31:27 2012 -0600 1.3 @@ -0,0 +1,1772 @@ 1.4 +#include <cstdio> 1.5 +#include <cctype> 1.6 +#include <cstdlib> 1.7 +#include <cstring> 1.8 +#include <cassert> 1.9 +#include <algorithm> 1.10 + 1.11 +using namespace std; 1.12 + 1.13 +#ifdef HAVE_STRINGS_H 1.14 +# include <strings.h> 1.15 +#endif 1.16 + 1.17 +#if defined(__unix) || defined(__linux) || defined(__sun) || defined(__DJGPP) 1.18 +# include <unistd.h> 1.19 +# include <sys/types.h> 1.20 +# include <sys/stat.h> 1.21 +# include <climits> 1.22 +# define stricmp strcasecmp 1.23 +// FIXME: this is wrong, but we don't want buffer overflow 1.24 +# if defined _MAX_PATH 1.25 +# undef _MAX_PATH 1.26 +//# define _MAX_PATH 128 1.27 +# define _MAX_PATH 260 1.28 +# endif 1.29 +#endif 1.30 + 1.31 +#ifdef WIN32 1.32 +# include <io.h> 1.33 +# ifndef W_OK 1.34 +# define W_OK 2 1.35 +# endif 1.36 +# define ftruncate chsize 1.37 +#endif 1.38 + 1.39 +#include "movie.h" 1.40 +#include "System.h" 1.41 +#include "../gba/GBA.h" 1.42 +#include "../gba/GBAGlobals.h" 1.43 +#include "../gba/RTC.h" 1.44 +#include "../gb/GB.h" 1.45 +#include "../gb/gbGlobals.h" 1.46 +#include "inputGlobal.h" 1.47 +#include "unzip.h" 1.48 +#include "Util.h" 1.49 + 1.50 +#include "vbalua.h" 1.51 + 1.52 +#if (defined(WIN32) && !defined(SDL)) 1.53 +# include "../win32/stdafx.h" 1.54 +# include "../win32/MainWnd.h" 1.55 +# include "../win32/VBA.h" 1.56 +# include "../win32/WinMiscUtil.h" 1.57 +#endif 1.58 + 1.59 +extern int emulating; // from system.cpp 1.60 +extern u16 currentButtons[4]; // from System.cpp 1.61 +extern u16 lastKeys; 1.62 + 1.63 +SMovie Movie; 1.64 +bool loadingMovie = false; 1.65 + 1.66 +// probably bad idea to have so many global variables, but I hate to recompile almost everything after editing VBA.h 1.67 +bool autoConvertMovieWhenPlaying = false; 1.68 + 1.69 +static u16 initialInputs[4] = { 0 }; 1.70 + 1.71 +static bool resetSignaled = false; 1.72 +static bool resetSignaledLast = false; 1.73 + 1.74 +static int prevEmulatorType, prevBorder, prevWinBorder, prevBorderAuto; 1.75 + 1.76 +// little-endian integer pop/push functions: 1.77 +static inline uint32 Pop32(const uint8 * &ptr) 1.78 +{ 1.79 + uint32 v = (ptr[0] | (ptr[1] << 8) | (ptr[2] << 16) | (ptr[3] << 24)); 1.80 + ptr += 4; 1.81 + return v; 1.82 +} 1.83 + 1.84 +static inline uint16 Pop16(const uint8 * &ptr) /* const version */ 1.85 +{ 1.86 + uint16 v = (ptr[0] | (ptr[1] << 8)); 1.87 + ptr += 2; 1.88 + return v; 1.89 +} 1.90 + 1.91 +static inline uint16 Pop16(uint8 * &ptr) /* non-const version */ 1.92 +{ 1.93 + uint16 v = (ptr[0] | (ptr[1] << 8)); 1.94 + ptr += 2; 1.95 + return v; 1.96 +} 1.97 + 1.98 +static inline uint8 Pop8(const uint8 * &ptr) 1.99 +{ 1.100 + return *(ptr)++; 1.101 +} 1.102 + 1.103 +static inline void Push32(uint32 v, uint8 * &ptr) 1.104 +{ 1.105 + ptr[0] = (uint8)(v & 0xff); 1.106 + ptr[1] = (uint8)((v >> 8) & 0xff); 1.107 + ptr[2] = (uint8)((v >> 16) & 0xff); 1.108 + ptr[3] = (uint8)((v >> 24) & 0xff); 1.109 + ptr += 4; 1.110 +} 1.111 + 1.112 +static inline void Push16(uint16 v, uint8 * &ptr) 1.113 +{ 1.114 + ptr[0] = (uint8)(v & 0xff); 1.115 + ptr[1] = (uint8)((v >> 8) & 0xff); 1.116 + ptr += 2; 1.117 +} 1.118 + 1.119 +static inline void Push8(uint8 v, uint8 * &ptr) 1.120 +{ 1.121 + *ptr++ = v; 1.122 +} 1.123 + 1.124 +// little-endian integer read/write functions: 1.125 +static inline uint16 Read16(const uint8 *ptr) 1.126 +{ 1.127 + return ptr[0] | (ptr[1] << 8); 1.128 +} 1.129 + 1.130 +static inline void Write16(uint16 v, uint8 *ptr) 1.131 +{ 1.132 + ptr[0] = uint8(v & 0xff); 1.133 + ptr[1] = uint8((v >> 8) & 0xff); 1.134 +} 1.135 + 1.136 +static long file_length(FILE *fp) 1.137 +{ 1.138 + long cur_pos = ftell(fp); 1.139 + fseek(fp, 0, SEEK_END); 1.140 + long length = ftell(fp); 1.141 + fseek(fp, cur_pos, SEEK_SET); 1.142 + return length; 1.143 +} 1.144 + 1.145 +static int bytes_per_frame(SMovie &mov) 1.146 +{ 1.147 + int num_controllers = 0; 1.148 + 1.149 + for (int i = 0; i < MOVIE_NUM_OF_POSSIBLE_CONTROLLERS; ++i) 1.150 + if (mov.header.controllerFlags & MOVIE_CONTROLLER(i)) 1.151 + ++num_controllers; 1.152 + 1.153 + return CONTROLLER_DATA_SIZE * num_controllers; 1.154 +} 1.155 + 1.156 +static void reserve_buffer_space(uint32 space_needed) 1.157 +{ 1.158 + if (space_needed > Movie.inputBufferSize) 1.159 + { 1.160 + uint32 ptr_offset = Movie.inputBufferPtr - Movie.inputBuffer; 1.161 + uint32 alloc_chunks = (space_needed - 1) / BUFFER_GROWTH_SIZE + 1; 1.162 + uint32 old_size = Movie.inputBufferSize; 1.163 + Movie.inputBufferSize = BUFFER_GROWTH_SIZE * alloc_chunks; 1.164 + Movie.inputBuffer = (uint8 *)realloc(Movie.inputBuffer, Movie.inputBufferSize); 1.165 + // FIXME: this only fixes the random input problem during dma-frame-skip, but not the skip 1.166 + memset(Movie.inputBuffer + old_size, 0, Movie.inputBufferSize - old_size); 1.167 + Movie.inputBufferPtr = Movie.inputBuffer + ptr_offset; 1.168 + } 1.169 +} 1.170 + 1.171 +static int read_movie_header(FILE *file, SMovie &movie) 1.172 +{ 1.173 + assert(file != NULL); 1.174 + assert(VBM_HEADER_SIZE == sizeof(SMovieFileHeader)); // sanity check on the header type definition 1.175 + 1.176 + uint8 headerData [VBM_HEADER_SIZE]; 1.177 + 1.178 + if (fread(headerData, 1, VBM_HEADER_SIZE, file) != VBM_HEADER_SIZE) 1.179 + return MOVIE_WRONG_FORMAT; // if we failed to read in all VBM_HEADER_SIZE bytes of the header 1.180 + 1.181 + const uint8 * ptr = headerData; 1.182 + SMovieFileHeader &header = movie.header; 1.183 + 1.184 + header.magic = Pop32(ptr); 1.185 + if (header.magic != VBM_MAGIC) 1.186 + return MOVIE_WRONG_FORMAT; 1.187 + 1.188 + header.version = Pop32(ptr); 1.189 + if (header.version != VBM_VERSION) 1.190 + return MOVIE_WRONG_VERSION; 1.191 + 1.192 + header.uid = Pop32(ptr); 1.193 + header.length_frames = Pop32(ptr) + 1; // HACK: add 1 to the length for compatibility 1.194 + header.rerecord_count = Pop32(ptr); 1.195 + 1.196 + header.startFlags = Pop8(ptr); 1.197 + header.controllerFlags = Pop8(ptr); 1.198 + header.typeFlags = Pop8(ptr); 1.199 + header.optionFlags = Pop8(ptr); 1.200 + 1.201 + header.saveType = Pop32(ptr); 1.202 + header.flashSize = Pop32(ptr); 1.203 + header.gbEmulatorType = Pop32(ptr); 1.204 + 1.205 + for (int i = 0; i < 12; i++) 1.206 + header.romTitle[i] = Pop8(ptr); 1.207 + 1.208 + header.minorVersion = Pop8(ptr); 1.209 + 1.210 + header.romCRC = Pop8(ptr); 1.211 + header.romOrBiosChecksum = Pop16(ptr); 1.212 + header.romGameCode = Pop32(ptr); 1.213 + 1.214 + header.offset_to_savestate = Pop32(ptr); 1.215 + header.offset_to_controller_data = Pop32(ptr); 1.216 + 1.217 + return MOVIE_SUCCESS; 1.218 +} 1.219 + 1.220 +static void write_movie_header(FILE *file, const SMovie &movie) 1.221 +{ 1.222 + assert(ftell(file) == 0); // we assume file points to beginning of movie file 1.223 + 1.224 + uint8 headerData [VBM_HEADER_SIZE]; 1.225 + uint8 *ptr = headerData; 1.226 + const SMovieFileHeader &header = movie.header; 1.227 + 1.228 + Push32(header.magic, ptr); 1.229 + Push32(header.version, ptr); 1.230 + 1.231 + Push32(header.uid, ptr); 1.232 + Push32(header.length_frames - 1, ptr); // HACK: reduce the length by 1 for compatibility with certain faulty old tools 1.233 + // like TME 1.234 + Push32(header.rerecord_count, ptr); 1.235 + 1.236 + Push8(header.startFlags, ptr); 1.237 + Push8(header.controllerFlags, ptr); 1.238 + Push8(header.typeFlags, ptr); 1.239 + Push8(header.optionFlags, ptr); 1.240 + 1.241 + Push32(header.saveType, ptr); 1.242 + Push32(header.flashSize, ptr); 1.243 + Push32(header.gbEmulatorType, ptr); 1.244 + 1.245 + for (int i = 0; i < 12; ++i) 1.246 + Push8(header.romTitle[i], ptr); 1.247 + 1.248 + Push8(header.minorVersion, ptr); 1.249 + 1.250 + Push8(header.romCRC, ptr); 1.251 + Push16(header.romOrBiosChecksum, ptr); 1.252 + Push32(header.romGameCode, ptr); 1.253 + 1.254 + Push32(header.offset_to_savestate, ptr); 1.255 + Push32(header.offset_to_controller_data, ptr); 1.256 + 1.257 + fwrite(headerData, 1, VBM_HEADER_SIZE, file); 1.258 +} 1.259 + 1.260 +static void flush_movie_header() 1.261 +{ 1.262 + assert(Movie.file != 0 && "logical error!"); 1.263 + if (!Movie.file) 1.264 + return; 1.265 + 1.266 + long originalPos = ftell(Movie.file); 1.267 + 1.268 + // (over-)write the header 1.269 + fseek(Movie.file, 0, SEEK_SET); 1.270 + write_movie_header(Movie.file, Movie); 1.271 + 1.272 + fflush(Movie.file); 1.273 + 1.274 + fseek(Movie.file, originalPos, SEEK_SET); 1.275 +} 1.276 + 1.277 +static void flush_movie_frames() 1.278 +{ 1.279 + assert(Movie.file && "logical error!"); 1.280 + if (!Movie.file) 1.281 + return; 1.282 + 1.283 + long originalPos = ftell(Movie.file); 1.284 + 1.285 + // overwrite the controller data 1.286 + fseek(Movie.file, Movie.header.offset_to_controller_data, SEEK_SET); 1.287 + fwrite(Movie.inputBuffer, 1, Movie.bytesPerFrame * Movie.header.length_frames, Movie.file); 1.288 + 1.289 + fflush(Movie.file); 1.290 + 1.291 + fseek(Movie.file, originalPos, SEEK_SET); 1.292 +} 1.293 + 1.294 +static void truncate_movie(long length) 1.295 +{ 1.296 + // truncate movie to length 1.297 + // NOTE: it's certain that the savestate block is never after the 1.298 + // controller data block, because the VBM format decrees it. 1.299 + 1.300 + assert(Movie.file && length >= 0); 1.301 + if (!Movie.file || length < 0) 1.302 + return; 1.303 + 1.304 + assert(Movie.header.offset_to_savestate <= Movie.header.offset_to_controller_data); 1.305 + if (Movie.header.offset_to_savestate > Movie.header.offset_to_controller_data) 1.306 + return; 1.307 + 1.308 + Movie.header.length_frames = length; 1.309 + flush_movie_header(); 1.310 + const long truncLen = long(Movie.header.offset_to_controller_data + Movie.bytesPerFrame * length); 1.311 + if (file_length(Movie.file) != truncLen) 1.312 + { 1.313 + ftruncate(fileno(Movie.file), truncLen); 1.314 + } 1.315 +} 1.316 + 1.317 +static void remember_input_state() 1.318 +{ 1.319 + for (int i = 0; i < MOVIE_NUM_OF_POSSIBLE_CONTROLLERS; ++i) 1.320 + { 1.321 + if (systemCartridgeType == 0) 1.322 + { 1.323 + initialInputs[i] = u16(~P1 & 0x03FF); 1.324 + } 1.325 + else 1.326 + { 1.327 + extern int32 gbJoymask[4]; 1.328 + for (int i = 0; i < 4; ++i) 1.329 + initialInputs[i] = u16(gbJoymask[i] & 0xFFFF); 1.330 + } 1.331 + } 1.332 +} 1.333 + 1.334 +static void change_state(MovieState new_state) 1.335 +{ 1.336 +#if (defined(WIN32) && !defined(SDL)) 1.337 + theApp.frameSearching = false; 1.338 + theApp.frameSearchSkipping = false; 1.339 +#endif 1.340 + 1.341 + if (new_state == MOVIE_STATE_NONE) 1.342 + { 1.343 + Movie.pauseFrame = -1; 1.344 + 1.345 + if (Movie.state == MOVIE_STATE_NONE) 1.346 + return; 1.347 + 1.348 + truncate_movie(Movie.header.length_frames); 1.349 + 1.350 + fclose(Movie.file); 1.351 + Movie.file = NULL; 1.352 + Movie.currentFrame = 0; 1.353 +#if (defined(WIN32) && !defined(SDL)) 1.354 + // undo changes to border settings 1.355 + { 1.356 + gbBorderOn = prevBorder; 1.357 + theApp.winGbBorderOn = prevWinBorder; 1.358 + gbBorderAutomatic = prevBorderAuto; 1.359 + systemGbBorderOn(); 1.360 + } 1.361 +#endif 1.362 + gbEmulatorType = prevEmulatorType; 1.363 + 1.364 + extern int32 gbDMASpeedVersion; 1.365 + gbDMASpeedVersion = 1; 1.366 + 1.367 + extern int32 gbEchoRAMFixOn; 1.368 + gbEchoRAMFixOn = 1; 1.369 + 1.370 + gbNullInputHackTempEnabled = gbNullInputHackEnabled; 1.371 + 1.372 + if (Movie.inputBuffer) 1.373 + { 1.374 + free(Movie.inputBuffer); 1.375 + Movie.inputBuffer = NULL; 1.376 + } 1.377 + } 1.378 + else if (new_state == MOVIE_STATE_PLAY) 1.379 + { 1.380 + assert(Movie.file); 1.381 + 1.382 + // this would cause problems if not dealt with 1.383 + if (Movie.currentFrame >= Movie.header.length_frames) 1.384 + { 1.385 + new_state = MOVIE_STATE_END; 1.386 + Movie.inputBufferPtr = Movie.inputBuffer + Movie.bytesPerFrame * Movie.header.length_frames; 1.387 + } 1.388 + } 1.389 + else if (new_state == MOVIE_STATE_RECORD) 1.390 + { 1.391 + assert(Movie.file); 1.392 + 1.393 + // this would cause problems if not dealt with 1.394 + if (Movie.currentFrame > Movie.header.length_frames) 1.395 + { 1.396 + new_state = MOVIE_STATE_END; 1.397 + Movie.inputBufferPtr = Movie.inputBuffer + Movie.bytesPerFrame * Movie.header.length_frames; 1.398 + } 1.399 + 1.400 + fseek(Movie.file, Movie.header.offset_to_controller_data + Movie.bytesPerFrame * Movie.currentFrame, SEEK_SET); 1.401 + } 1.402 + 1.403 + if (new_state == MOVIE_STATE_END && Movie.state != MOVIE_STATE_END) 1.404 + { 1.405 +#if defined(SDL) 1.406 + systemClearJoypads(); 1.407 +#endif 1.408 + systemScreenMessage("Movie end"); 1.409 + } 1.410 + 1.411 + Movie.state = new_state; 1.412 + 1.413 + // checking for movie end 1.414 + bool willPause = false; 1.415 + 1.416 + // if the movie's been set to pause at a certain frame 1.417 + if (Movie.state != MOVIE_STATE_NONE && Movie.pauseFrame >= 0 && Movie.currentFrame == (uint32)Movie.pauseFrame) 1.418 + { 1.419 + Movie.pauseFrame = -1; 1.420 + willPause = true; 1.421 + } 1.422 + 1.423 + if (Movie.state == MOVIE_STATE_END) 1.424 + { 1.425 + if (Movie.currentFrame == Movie.header.length_frames) 1.426 + { 1.427 +#if (defined(WIN32) && !defined(SDL)) 1.428 + if (theApp.movieOnEndPause) 1.429 + { 1.430 + willPause = true; 1.431 + } 1.432 +#else 1.433 + // SDL FIXME 1.434 +#endif 1.435 + 1.436 +#if (defined(WIN32) && !defined(SDL)) 1.437 + switch (theApp.movieOnEndBehavior) 1.438 + { 1.439 + case 1: 1.440 + // the old behavior 1.441 + //VBAMovieRestart(); 1.442 + break; 1.443 + case 2: 1.444 +#else 1.445 + // SDL FIXME 1.446 +#endif 1.447 + if (Movie.RecordedThisSession) 1.448 + { 1.449 + // if user has been recording this movie since the last time it started playing, 1.450 + // they probably don't want the movie to end now during playback, 1.451 + // so switch back to recording when it reaches the end 1.452 + VBAMovieSwitchToRecording(); 1.453 + systemScreenMessage("Recording resumed"); 1.454 + willPause = true; 1.455 + } 1.456 +#if (defined(WIN32) && !defined(SDL)) 1.457 + break; 1.458 + case 3: 1.459 + // keep open 1.460 + break; 1.461 + case 0: 1.462 + // fall through 1.463 + default: 1.464 + // close movie 1.465 + //VBAMovieStop(false); 1.466 + break; 1.467 + } 1.468 +#else 1.469 + // SDL FIXME 1.470 +#endif 1.471 + } 1.472 +#if 1 1.473 + else if (Movie.currentFrame > Movie.header.length_frames) 1.474 + { 1.475 +#if (defined(WIN32) && !defined(SDL)) 1.476 + switch (theApp.movieOnEndBehavior) 1.477 + { 1.478 + case 1: 1.479 + // FIXME: this should be delayed till the current frame ends 1.480 + VBAMovieRestart(); 1.481 + break; 1.482 + case 2: 1.483 + // nothing 1.484 + break; 1.485 + case 3: 1.486 + // keep open 1.487 + break; 1.488 + case 0: 1.489 + // fall through 1.490 + default: 1.491 + // close movie 1.492 + VBAMovieStop(false); 1.493 + break; 1.494 + } 1.495 +#else 1.496 + // SDLFIXME 1.497 +#endif 1.498 + } 1.499 +#endif 1.500 + } // end if (Movie.state == MOVIE_STATE_END) 1.501 + 1.502 + if (willPause) 1.503 + { 1.504 + systemSetPause(true); 1.505 + } 1.506 +} 1.507 + 1.508 +void VBAMovieInit() 1.509 +{ 1.510 + memset(&Movie, 0, sizeof(Movie)); 1.511 + Movie.state = MOVIE_STATE_NONE; 1.512 + Movie.pauseFrame = -1; 1.513 + 1.514 + resetSignaled = false; 1.515 + resetSignaledLast = false; 1.516 +} 1.517 + 1.518 +void VBAMovieGetRomInfo(const SMovie &movieInfo, char romTitle [12], uint32 &romGameCode, uint16 &checksum, uint8 &crc) 1.519 +{ 1.520 + if (systemCartridgeType == 0) // GBA 1.521 + { 1.522 + extern u8 *bios, *rom; 1.523 + memcpy(romTitle, &rom[0xa0], 12); // GBA TITLE 1.524 + memcpy(&romGameCode, &rom[0xac], 4); // GBA ROM GAME CODE 1.525 + if ((movieInfo.header.optionFlags & MOVIE_SETTING_USEBIOSFILE) != 0) 1.526 + checksum = utilCalcBIOSChecksum(bios, 4); // GBA BIOS CHECKSUM 1.527 + else 1.528 + checksum = 0; 1.529 + crc = rom[0xbd]; // GBA ROM CRC 1.530 + } 1.531 + else // non-GBA 1.532 + { 1.533 + extern u8 *gbRom; 1.534 + memcpy(romTitle, &gbRom[0x134], 12); // GB TITLE (note this can be 15 but is truncated to 12) 1.535 + romGameCode = (uint32)gbRom[0x146]; // GB ROM UNIT CODE 1.536 + 1.537 + checksum = (gbRom[0x14e] << 8) | gbRom[0x14f]; // GB ROM CHECKSUM, read from big-endian 1.538 + crc = gbRom[0x14d]; // GB ROM CRC 1.539 + } 1.540 +} 1.541 + 1.542 +#ifdef SDL 1.543 +static void GetBatterySaveName(char *buffer) 1.544 +{ 1.545 + extern char batteryDir[2048], filename[2048]; // from SDL.cpp 1.546 + extern char *sdlGetFilename(char *name); // from SDL.cpp 1.547 + if (batteryDir[0]) 1.548 + sprintf(buffer, "%s/%s.sav", batteryDir, sdlGetFilename(filename)); 1.549 + else 1.550 + sprintf(buffer, "%s.sav", filename); 1.551 +} 1.552 + 1.553 +#endif 1.554 + 1.555 +static void SetPlayEmuSettings() 1.556 +{ 1.557 + prevEmulatorType = gbEmulatorType; 1.558 + gbEmulatorType = Movie.header.gbEmulatorType; 1.559 + 1.560 +#if (defined(WIN32) && !defined(SDL)) 1.561 +// theApp.removeIntros = false; 1.562 + theApp.skipBiosFile = (Movie.header.optionFlags & MOVIE_SETTING_SKIPBIOSFILE) != 0; 1.563 + theApp.useBiosFile = (Movie.header.optionFlags & MOVIE_SETTING_USEBIOSFILE) != 0; 1.564 +#else 1.565 + extern int saveType, sdlRtcEnable, sdlFlashSize; // from SDL.cpp 1.566 + extern bool8 useBios, skipBios, removeIntros; // from SDL.cpp 1.567 + useBios = (Movie.header.optionFlags & MOVIE_SETTING_USEBIOSFILE) != 0; 1.568 + skipBios = (Movie.header.optionFlags & MOVIE_SETTING_SKIPBIOSFILE) != 0; 1.569 + removeIntros = false /*(Movie.header.optionFlags & MOVIE_SETTING_REMOVEINTROS) != 0*/; 1.570 +#endif 1.571 + 1.572 + extern void SetPrefetchHack(bool); 1.573 + if (systemCartridgeType == 0) // lag disablement applies only to GBA 1.574 + SetPrefetchHack((Movie.header.optionFlags & MOVIE_SETTING_LAGHACK) != 0); 1.575 + 1.576 + gbNullInputHackTempEnabled = ((Movie.header.optionFlags & MOVIE_SETTING_GBINPUTHACK) != 0); 1.577 + 1.578 + // some GB/GBC games depend on the sound rate, so just use the highest one 1.579 + systemSoundSetQuality(1); 1.580 + useOldFrameTiming = false; 1.581 + 1.582 + extern int32 gbDMASpeedVersion; 1.583 + if ((Movie.header.optionFlags & MOVIE_SETTING_GBCFF55FIX) != 0) 1.584 + gbDMASpeedVersion = 1; 1.585 + else 1.586 + gbDMASpeedVersion = 0; // old CGB HDMA5 timing was used 1.587 + 1.588 + extern int32 gbEchoRAMFixOn; 1.589 + if ((Movie.header.optionFlags & MOVIE_SETTING_GBECHORAMFIX) != 0) 1.590 + gbEchoRAMFixOn = 1; 1.591 + else 1.592 + gbEchoRAMFixOn = 0; 1.593 + 1.594 +#if (defined(WIN32) && !defined(SDL)) 1.595 + rtcEnable((Movie.header.optionFlags & MOVIE_SETTING_RTCENABLE) != 0); 1.596 + theApp.winSaveType = Movie.header.saveType; 1.597 + theApp.winFlashSize = Movie.header.flashSize; 1.598 + 1.599 + prevBorder = gbBorderOn; 1.600 + prevWinBorder = theApp.winGbBorderOn; 1.601 + prevBorderAuto = gbBorderAutomatic; 1.602 + if ((gbEmulatorType == 2 || gbEmulatorType == 5) 1.603 + && !theApp.hideMovieBorder) // games played in SGB mode can have a border 1.604 + { 1.605 + gbBorderOn = true; 1.606 + theApp.winGbBorderOn = true; 1.607 + gbBorderAutomatic = false; 1.608 + } 1.609 + else 1.610 + { 1.611 + gbBorderOn = false; 1.612 + theApp.winGbBorderOn = false; 1.613 + gbBorderAutomatic = false; 1.614 + if (theApp.hideMovieBorder) 1.615 + { 1.616 + theApp.hideMovieBorder = false; 1.617 + prevBorder = false; // it might be expected behaviour that it stays hidden after the movie 1.618 + } 1.619 + } 1.620 + systemGbBorderOn(); 1.621 +#else 1.622 + sdlRtcEnable = (Movie.header.optionFlags & MOVIE_SETTING_RTCENABLE) != 0; 1.623 + saveType = Movie.header.saveType; 1.624 + sdlFlashSize = Movie.header.flashSize; 1.625 +#endif 1.626 +} 1.627 + 1.628 +static void HardResetAndSRAMClear() 1.629 +{ 1.630 +#if (defined(WIN32) && !defined(SDL)) 1.631 + winEraseBatteryFile(); // delete the damn SRAM file and keep it from being resurrected from RAM 1.632 + MainWnd *temp = ((MainWnd *)theApp.m_pMainWnd); 1.633 + if (!temp->winFileRun(true)) // restart running the game 1.634 + { 1.635 + temp->winFileClose(); 1.636 + } 1.637 +#else 1.638 + char fname [1024]; 1.639 + GetBatterySaveName(fname); 1.640 + remove(fname); // delete the damn SRAM file 1.641 + 1.642 + // Henceforth, emuCleanUp means "clear out SRAM" 1.643 + //theEmulator.emuCleanUp(); // keep it from being resurrected from RAM <--This is wrong, it'll deallocate all variables --Felipe 1.644 + 1.645 + /// FIXME the correct SDL code to call for a full restart isn't in a function yet 1.646 + theEmulator.emuReset(false); 1.647 +#endif 1.648 +} 1.649 + 1.650 +int VBAMovieOpen(const char *filename, bool8 read_only) 1.651 +{ 1.652 + loadingMovie = true; 1.653 + uint8 movieReadOnly = read_only ? 1 : 0; 1.654 + 1.655 + FILE * file; 1.656 + STREAM stream; 1.657 + int result; 1.658 + int fn; 1.659 + 1.660 + char movie_filename[_MAX_PATH]; 1.661 +#ifdef WIN32 1.662 + _fullpath(movie_filename, filename, _MAX_PATH); 1.663 +#else 1.664 + // SDL FIXME: convert to fullpath 1.665 + strncpy(movie_filename, filename, _MAX_PATH); 1.666 + movie_filename[_MAX_PATH - 1] = '\0'; 1.667 +#endif 1.668 + 1.669 + if (movie_filename[0] == '\0') 1.670 + { loadingMovie = false; return MOVIE_FILE_NOT_FOUND; } 1.671 + 1.672 + if (!emulating) 1.673 + { loadingMovie = false; return MOVIE_UNKNOWN_ERROR; } 1.674 + 1.675 +// bool alreadyOpen = (Movie.file != NULL && _stricmp(movie_filename, Movie.filename) == 0); 1.676 + 1.677 +// if (alreadyOpen) 1.678 + change_state(MOVIE_STATE_NONE); // have to stop current movie before trying to re-open it 1.679 + 1.680 + if (!(file = fopen(movie_filename, "rb+"))) 1.681 + if (!(file = fopen(movie_filename, "rb"))) 1.682 + { loadingMovie = false; return MOVIE_FILE_NOT_FOUND; } 1.683 + //else 1.684 + // movieReadOnly = 2; // we have to open the movie twice, no need to do this both times 1.685 + 1.686 +// if (!alreadyOpen) 1.687 +// change_state(MOVIE_STATE_NONE); // stop current movie when we're able to open the other one 1.688 +// 1.689 +// if (!(file = fopen(movie_filename, "rb+"))) 1.690 +// if(!(file = fopen(movie_filename, "rb"))) 1.691 +// {loadingMovie = false; return MOVIE_FILE_NOT_FOUND;} 1.692 +// else 1.693 +// movieReadOnly = 2; 1.694 + 1.695 + // clear out the current movie 1.696 + VBAMovieInit(); 1.697 + 1.698 + // read header 1.699 + if ((result = read_movie_header(file, Movie)) != MOVIE_SUCCESS) 1.700 + { 1.701 + fclose(file); 1.702 + { loadingMovie = false; return result; } 1.703 + } 1.704 + 1.705 + // set emulator settings that make the movie more likely to stay synchronized 1.706 + SetPlayEmuSettings(); 1.707 + 1.708 +// extern bool systemLoadBIOS(); 1.709 +// if (!systemLoadBIOS()) 1.710 +// { loadingMovie = false; return MOVIE_UNKNOWN_ERROR; } 1.711 + 1.712 + // read the metadata / author info from file 1.713 + fread(Movie.authorInfo, 1, MOVIE_METADATA_SIZE, file); 1.714 + fn = dup(fileno(file)); // XXX: why does this fail?? it returns -1 but errno == 0 1.715 + fclose(file); 1.716 + 1.717 + // apparently this lseek is necessary 1.718 + lseek(fn, Movie.header.offset_to_savestate, SEEK_SET); 1.719 + if (!(stream = utilGzReopen(fn, "rb"))) 1.720 + if (!(stream = utilGzOpen(movie_filename, "rb"))) 1.721 + { loadingMovie = false; return MOVIE_FILE_NOT_FOUND; } 1.722 + else 1.723 + fn = dup(fileno(file)); 1.724 + // in case the above dup failed but opening the file normally doesn't fail 1.725 + 1.726 + if (Movie.header.startFlags & MOVIE_START_FROM_SNAPSHOT) 1.727 + { 1.728 + // load the snapshot 1.729 + result = theEmulator.emuReadStateFromStream(stream) ? MOVIE_SUCCESS : MOVIE_WRONG_FORMAT; 1.730 + 1.731 + // FIXME: Kludge for conversion 1.732 + remember_input_state(); 1.733 + } 1.734 + else if (Movie.header.startFlags & MOVIE_START_FROM_SRAM) 1.735 + { 1.736 + // 'soft' reset: 1.737 + theEmulator.emuReset(false); 1.738 + 1.739 + // load the SRAM 1.740 + result = theEmulator.emuReadBatteryFromStream(stream) ? MOVIE_SUCCESS : MOVIE_WRONG_FORMAT; 1.741 + } 1.742 + else 1.743 + { 1.744 + HardResetAndSRAMClear(); 1.745 + } 1.746 + 1.747 + utilGzClose(stream); 1.748 + 1.749 + if (result != MOVIE_SUCCESS) 1.750 + { loadingMovie = false; return result; } 1.751 + 1.752 +// if (!(file = fopen(movie_filename, /*read_only ? "rb" :*/ "rb+"))) // want to be able to switch out of read-only later 1.753 +// { 1.754 +// if(!Movie.readOnly || !(file = fopen(movie_filename, "rb"))) // try read-only if failed 1.755 +// return MOVIE_FILE_NOT_FOUND; 1.756 +// } 1.757 + if (!(file = fopen(movie_filename, "rb+"))) 1.758 + if (!(file = fopen(movie_filename, "rb"))) 1.759 + { loadingMovie = false; return MOVIE_FILE_NOT_FOUND; } 1.760 + else 1.761 + movieReadOnly = 2; 1.762 + 1.763 + // recalculate length of movie from the file size 1.764 + Movie.bytesPerFrame = bytes_per_frame(Movie); 1.765 + fseek(file, 0, SEEK_END); 1.766 + long fileSize = ftell(file); 1.767 + Movie.header.length_frames = (fileSize - Movie.header.offset_to_controller_data) / Movie.bytesPerFrame; 1.768 + 1.769 + if (fseek(file, Movie.header.offset_to_controller_data, SEEK_SET)) 1.770 + { fclose(file); loadingMovie = false; return MOVIE_WRONG_FORMAT; } 1.771 + 1.772 + strcpy(Movie.filename, movie_filename); 1.773 + Movie.file = file; 1.774 + Movie.inputBufferPtr = Movie.inputBuffer; 1.775 + Movie.currentFrame = 0; 1.776 + Movie.readOnly = movieReadOnly; 1.777 + Movie.RecordedThisSession = false; 1.778 + 1.779 + // read controller data 1.780 + uint32 to_read = Movie.bytesPerFrame * Movie.header.length_frames; 1.781 + reserve_buffer_space(to_read); 1.782 + fread(Movie.inputBuffer, 1, to_read, file); 1.783 + 1.784 + change_state(MOVIE_STATE_PLAY); 1.785 + 1.786 + char messageString[64] = "Movie "; 1.787 + bool converted = false; 1.788 + if (autoConvertMovieWhenPlaying) 1.789 + { 1.790 + int result = VBAMovieConvertCurrent(); 1.791 + if (result == MOVIE_SUCCESS) 1.792 + strcat(messageString, "converted and "); 1.793 + else if (result == MOVIE_WRONG_VERSION) 1.794 + strcat(messageString, "higher revision "); 1.795 + } 1.796 + 1.797 + if (Movie.state == MOVIE_STATE_PLAY) 1.798 + strcat(messageString, "replaying "); 1.799 + else 1.800 + strcat(messageString, "finished "); 1.801 + if (Movie.readOnly) 1.802 + strcat(messageString, "(read)"); 1.803 + else 1.804 + strcat(messageString, "(edit)"); 1.805 + systemScreenMessage(messageString); 1.806 + 1.807 + VBAUpdateButtonPressDisplay(); 1.808 + VBAUpdateFrameCountDisplay(); 1.809 + systemRefreshScreen(); 1.810 + 1.811 + { loadingMovie = false; return MOVIE_SUCCESS; } 1.812 +} 1.813 + 1.814 +static void SetRecordEmuSettings() 1.815 +{ 1.816 + Movie.header.optionFlags = 0; 1.817 +#if (defined(WIN32) && !defined(SDL)) 1.818 + if (theApp.useBiosFile) 1.819 + Movie.header.optionFlags |= MOVIE_SETTING_USEBIOSFILE; 1.820 + if (theApp.skipBiosFile) 1.821 + Movie.header.optionFlags |= MOVIE_SETTING_SKIPBIOSFILE; 1.822 + if (rtcIsEnabled()) 1.823 + Movie.header.optionFlags |= MOVIE_SETTING_RTCENABLE; 1.824 + Movie.header.saveType = theApp.winSaveType; 1.825 + Movie.header.flashSize = theApp.winFlashSize; 1.826 +#else 1.827 + extern int saveType, sdlRtcEnable, sdlFlashSize; // from SDL.cpp 1.828 + extern bool8 useBios, skipBios; // from SDL.cpp 1.829 + if (useBios) 1.830 + Movie.header.optionFlags |= MOVIE_SETTING_USEBIOSFILE; 1.831 + if (skipBios) 1.832 + Movie.header.optionFlags |= MOVIE_SETTING_SKIPBIOSFILE; 1.833 + if (sdlRtcEnable) 1.834 + Movie.header.optionFlags |= MOVIE_SETTING_RTCENABLE; 1.835 + Movie.header.saveType = saveType; 1.836 + Movie.header.flashSize = sdlFlashSize; 1.837 +#endif 1.838 + prevEmulatorType = Movie.header.gbEmulatorType = gbEmulatorType; 1.839 + 1.840 + if (!memLagTempEnabled) 1.841 + Movie.header.optionFlags |= MOVIE_SETTING_LAGHACK; 1.842 + 1.843 + if (gbNullInputHackTempEnabled) 1.844 + Movie.header.optionFlags |= MOVIE_SETTING_GBINPUTHACK; 1.845 + 1.846 + Movie.header.optionFlags |= MOVIE_SETTING_GBCFF55FIX; 1.847 + extern int32 gbDMASpeedVersion; 1.848 + gbDMASpeedVersion = 1; 1.849 + 1.850 + Movie.header.optionFlags |= MOVIE_SETTING_GBECHORAMFIX; 1.851 + extern int32 gbEchoRAMFixOn; 1.852 + gbEchoRAMFixOn = 1; 1.853 + 1.854 + // some GB/GBC games depend on the sound rate, so just use the highest one 1.855 + systemSoundSetQuality(1); 1.856 + 1.857 + useOldFrameTiming = false; 1.858 + 1.859 +#if (defined(WIN32) && !defined(SDL)) 1.860 +// theApp.removeIntros = false; 1.861 + 1.862 + prevBorder = gbBorderOn; 1.863 + prevWinBorder = theApp.winGbBorderOn; 1.864 + prevBorderAuto = gbBorderAutomatic; 1.865 + if (gbEmulatorType == 2 || gbEmulatorType == 5) // only games played in SGB mode will have a border 1.866 + { 1.867 + gbBorderOn = true; 1.868 + theApp.winGbBorderOn = true; 1.869 + gbBorderAutomatic = false; 1.870 + } 1.871 + else 1.872 + { 1.873 + gbBorderOn = false; 1.874 + theApp.winGbBorderOn = false; 1.875 + gbBorderAutomatic = false; 1.876 + } 1.877 + systemGbBorderOn(); 1.878 +#else 1.879 + /// SDLFIXME 1.880 +#endif 1.881 +} 1.882 + 1.883 +uint16 VBAMovieGetCurrentInputOf(int controllerNum, bool normalOnly) 1.884 +{ 1.885 + if (controllerNum < 0 || controllerNum >= MOVIE_NUM_OF_POSSIBLE_CONTROLLERS) 1.886 + return 0; 1.887 + 1.888 + return normalOnly ? (currentButtons[controllerNum] & BUTTON_REGULAR_MASK) : currentButtons[controllerNum]; 1.889 +} 1.890 + 1.891 +int VBAMovieCreate(const char *filename, const char *authorInfo, uint8 startFlags, uint8 controllerFlags, uint8 typeFlags) 1.892 +{ 1.893 + // make sure at least one controller is enabled 1.894 + if ((controllerFlags & MOVIE_CONTROLLERS_ANY_MASK) == 0) 1.895 + return MOVIE_WRONG_FORMAT; 1.896 + 1.897 + if (!emulating) 1.898 + return MOVIE_UNKNOWN_ERROR; 1.899 + 1.900 + loadingMovie = true; 1.901 + 1.902 + FILE * file; 1.903 + STREAM stream; 1.904 + int fn; 1.905 + 1.906 + char movie_filename [_MAX_PATH]; 1.907 +#ifdef WIN32 1.908 + _fullpath(movie_filename, filename, _MAX_PATH); 1.909 +#else 1.910 + // FIXME: convert to fullpath 1.911 + strncpy(movie_filename, filename, _MAX_PATH); 1.912 + movie_filename[_MAX_PATH - 1] = '\0'; 1.913 +#endif 1.914 + 1.915 + bool alreadyOpen = (Movie.file != NULL && stricmp(movie_filename, Movie.filename) == 0); 1.916 + 1.917 + if (alreadyOpen) 1.918 + change_state(MOVIE_STATE_NONE); // have to stop current movie before trying to re-open it 1.919 + 1.920 + if (movie_filename[0] == '\0') 1.921 + { loadingMovie = false; return MOVIE_FILE_NOT_FOUND; } 1.922 + 1.923 + if (!(file = fopen(movie_filename, "wb"))) 1.924 + { loadingMovie = false; return MOVIE_FILE_NOT_FOUND; } 1.925 + 1.926 + if (!alreadyOpen) 1.927 + change_state(MOVIE_STATE_NONE); // stop current movie when we're able to open the other one 1.928 + 1.929 + // clear out the current movie 1.930 + VBAMovieInit(); 1.931 + 1.932 + // fill in the movie's header 1.933 + Movie.header.uid = (uint32)time(NULL); 1.934 + Movie.header.magic = VBM_MAGIC; 1.935 + Movie.header.version = VBM_VERSION; 1.936 + Movie.header.rerecord_count = 0; 1.937 + Movie.header.length_frames = 0; 1.938 + Movie.header.startFlags = startFlags; 1.939 + Movie.header.controllerFlags = controllerFlags; 1.940 + Movie.header.typeFlags = typeFlags; 1.941 + Movie.header.minorVersion = VBM_REVISION; 1.942 + 1.943 + // set emulator settings that make the movie more likely to stay synchronized when it's later played back 1.944 + SetRecordEmuSettings(); 1.945 + 1.946 + // set ROM and BIOS checksums and stuff 1.947 + VBAMovieGetRomInfo(Movie, Movie.header.romTitle, Movie.header.romGameCode, Movie.header.romOrBiosChecksum, Movie.header.romCRC); 1.948 + 1.949 + // write the header to file 1.950 + write_movie_header(file, Movie); 1.951 + 1.952 + // copy over the metadata / author info 1.953 + VBAMovieSetMetadata(authorInfo); 1.954 + 1.955 + // write the metadata / author info to file 1.956 + fwrite(Movie.authorInfo, 1, sizeof(char) * MOVIE_METADATA_SIZE, file); 1.957 + 1.958 + // write snapshot or SRAM if applicable 1.959 + if (Movie.header.startFlags & MOVIE_START_FROM_SNAPSHOT 1.960 + || Movie.header.startFlags & MOVIE_START_FROM_SRAM) 1.961 + { 1.962 + Movie.header.offset_to_savestate = (uint32)ftell(file); 1.963 + 1.964 + // close the file and reopen it as a stream: 1.965 + 1.966 + fn = dup(fileno(file)); 1.967 + fclose(file); 1.968 + 1.969 + if (!(stream = utilGzReopen(fn, "ab"))) // append mode to start at end, no seek necessary 1.970 + { loadingMovie = false; return MOVIE_FILE_NOT_FOUND; } 1.971 + 1.972 + // write the save data: 1.973 + if (Movie.header.startFlags & MOVIE_START_FROM_SNAPSHOT) 1.974 + { 1.975 + // save snapshot 1.976 + if (!theEmulator.emuWriteStateToStream(stream)) 1.977 + { 1.978 + utilGzClose(stream); 1.979 + { loadingMovie = false; return MOVIE_UNKNOWN_ERROR; } 1.980 + } 1.981 + } 1.982 + else if (Movie.header.startFlags & MOVIE_START_FROM_SRAM) 1.983 + { 1.984 + // save SRAM 1.985 + if (!theEmulator.emuWriteBatteryToStream(stream)) 1.986 + { 1.987 + utilGzClose(stream); 1.988 + { loadingMovie = false; return MOVIE_UNKNOWN_ERROR; } 1.989 + } 1.990 + 1.991 + // 'soft' reset: 1.992 + theEmulator.emuReset(false); 1.993 + } 1.994 + 1.995 + utilGzClose(stream); 1.996 + 1.997 + // reopen the file and seek back to the end 1.998 + 1.999 + if (!(file = fopen(movie_filename, "rb+"))) 1.1000 + { loadingMovie = false; return MOVIE_FILE_NOT_FOUND; } 1.1001 + 1.1002 + fseek(file, 0, SEEK_END); 1.1003 + } 1.1004 + else // no snapshot or SRAM 1.1005 + { 1.1006 + HardResetAndSRAMClear(); 1.1007 + } 1.1008 + 1.1009 + Movie.header.offset_to_controller_data = (uint32)ftell(file); 1.1010 + 1.1011 + strcpy(Movie.filename, movie_filename); 1.1012 + Movie.file = file; 1.1013 + Movie.bytesPerFrame = bytes_per_frame(Movie); 1.1014 + Movie.inputBufferPtr = Movie.inputBuffer; 1.1015 + Movie.currentFrame = 0; 1.1016 + Movie.readOnly = false; 1.1017 + Movie.RecordedThisSession = true; 1.1018 + 1.1019 + change_state(MOVIE_STATE_RECORD); 1.1020 + 1.1021 + systemScreenMessage("Recording movie..."); 1.1022 + { loadingMovie = false; return MOVIE_SUCCESS; } 1.1023 +} 1.1024 + 1.1025 +void VBAUpdateButtonPressDisplay() 1.1026 +{ 1.1027 + uint32 keys = currentButtons[0] & BUTTON_REGULAR_RECORDING_MASK; 1.1028 + 1.1029 + const static char KeyMap[] = { 'A', 'B', 's', 'S', '>', '<', '^', 'v', 'R', 'L', '!', '?', '{', '}', 'v', '^' }; 1.1030 + 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 { = } _ 1.1031 + // ? ! 1.1032 + char buffer[256]; 1.1033 + sprintf(buffer, " "); 1.1034 + 1.1035 +#ifndef WIN32 1.1036 + // don't bother color-coding autofire and such 1.1037 + int i; 1.1038 + for (i = 0; i < 15; i++) 1.1039 + { 1.1040 + int j = KeyOrder[i]; 1.1041 + int mask = (1 << (j)); 1.1042 + buffer[strlen(" ") + i] = ((keys & mask) != 0) ? KeyMap[j] : ' '; 1.1043 + } 1.1044 + 1.1045 + systemScreenMessage(buffer, 2, -1); 1.1046 +#else 1.1047 + const bool eraseAll = !theApp.inputDisplay; 1.1048 + uint32 autoHeldKeys = eraseAll ? 0 : theApp.autoHold & BUTTON_REGULAR_RECORDING_MASK; 1.1049 + uint32 autoFireKeys = eraseAll ? 0 : (theApp.autoFire | theApp.autoFire2) & BUTTON_REGULAR_RECORDING_MASK; 1.1050 + uint32 pressedKeys = eraseAll ? 0 : keys; 1.1051 + 1.1052 + char colorList[64]; 1.1053 + memset(colorList, 1, strlen(buffer)); 1.1054 + 1.1055 + if (!eraseAll) 1.1056 + { 1.1057 + for (int i = 0; i < 15; i++) 1.1058 + { 1.1059 + const int j = KeyOrder[i]; 1.1060 + const int mask = (1 << (j)); 1.1061 + bool pressed = (pressedKeys & mask) != 0; 1.1062 + const bool autoHeld = (autoHeldKeys & mask) != 0; 1.1063 + const bool autoFired = (autoFireKeys & mask) != 0; 1.1064 + const bool erased = (lastKeys & mask) != 0 && (!pressed && !autoHeld && !autoFired); 1.1065 + extern int textMethod; 1.1066 + if (textMethod != 2 && (autoHeld || (autoFired && !pressed) || erased)) 1.1067 + { 1.1068 + int colorNum = 1; // default is white 1.1069 + if (autoHeld) 1.1070 + colorNum += (pressed ? 2 : 1); // yellow if pressed, red if not 1.1071 + else if (autoFired) 1.1072 + colorNum += 5; // blue if autofired and not currently pressed 1.1073 + else if (erased) 1.1074 + colorNum += 8; // black on black 1.1075 + 1.1076 + colorList[strlen(" ") + i] = colorNum; 1.1077 + pressed = true; 1.1078 + } 1.1079 + buffer[strlen(" ") + i] = pressed ? KeyMap[j] : ' '; 1.1080 + } 1.1081 + } 1.1082 + 1.1083 + lastKeys = currentButtons[0]; 1.1084 + lastKeys |= theApp.autoHold & BUTTON_REGULAR_RECORDING_MASK; 1.1085 + lastKeys |= (theApp.autoFire | theApp.autoFire2) & BUTTON_REGULAR_RECORDING_MASK; 1.1086 + 1.1087 + systemScreenMessage(buffer, 2, -1, colorList); 1.1088 +#endif 1.1089 +} 1.1090 + 1.1091 +void VBAUpdateFrameCountDisplay() 1.1092 +{ 1.1093 + const int MAGICAL_NUMBER = 64; // FIXME: this won't do any better, but only to remind you of sz issues 1.1094 + char frameDisplayString[MAGICAL_NUMBER]; 1.1095 + char lagFrameDisplayString[MAGICAL_NUMBER]; 1.1096 + char extraCountDisplayString[MAGICAL_NUMBER]; 1.1097 + 1.1098 +#if (defined(WIN32) && !defined(SDL)) 1.1099 + if (theApp.frameCounter) 1.1100 +#else 1.1101 + /// SDL FIXME 1.1102 +#endif 1.1103 + { 1.1104 + switch (Movie.state) 1.1105 + { 1.1106 + case MOVIE_STATE_PLAY: 1.1107 + case MOVIE_STATE_END: 1.1108 + { 1.1109 + sprintf(frameDisplayString, "%d / %d", Movie.currentFrame, Movie.header.length_frames); 1.1110 + if (!Movie.readOnly) 1.1111 + strcat(frameDisplayString, " (edit)"); 1.1112 + break; 1.1113 + } 1.1114 + case MOVIE_STATE_RECORD: 1.1115 + { 1.1116 + sprintf(frameDisplayString, "%d (record)", Movie.currentFrame); 1.1117 + break; 1.1118 + } 1.1119 + default: 1.1120 + { 1.1121 + sprintf(frameDisplayString, "%d (no movie)", systemCounters.frameCount); 1.1122 + break; 1.1123 + } 1.1124 + } 1.1125 + 1.1126 +#if (defined(WIN32) && !defined(SDL)) 1.1127 + if (theApp.lagCounter) 1.1128 +#else 1.1129 + /// SDL FIXME 1.1130 +#endif 1.1131 + { 1.1132 +// sprintf(lagFrameDisplayString, " %c %d", systemCounters.laggedLast ? '*' : '|', systemCounters.lagCount); 1.1133 + sprintf(lagFrameDisplayString, " | %d%s", systemCounters.lagCount, systemCounters.laggedLast ? " *" : ""); 1.1134 + strcat(frameDisplayString, lagFrameDisplayString); 1.1135 + } 1.1136 + 1.1137 +#if (defined(WIN32) && !defined(SDL)) 1.1138 + if (theApp.extraCounter) 1.1139 +#else 1.1140 + /// SDL FIXME 1.1141 +#endif 1.1142 + { 1.1143 + sprintf(extraCountDisplayString, " | %d", systemCounters.frameCount - systemCounters.extraCount); 1.1144 + strcat(frameDisplayString, extraCountDisplayString); 1.1145 + } 1.1146 + } 1.1147 +#if (defined(WIN32) && !defined(SDL)) 1.1148 + else 1.1149 + { 1.1150 + frameDisplayString[0] = '\0'; 1.1151 + } 1.1152 +#else 1.1153 + /// SDL FIXME 1.1154 +#endif 1.1155 + systemScreenMessage(frameDisplayString, 1, -1); 1.1156 +} 1.1157 + 1.1158 +// this function should only be called once every frame 1.1159 +void VBAMovieUpdateState() 1.1160 +{ 1.1161 + ++Movie.currentFrame; 1.1162 + 1.1163 + if (Movie.state == MOVIE_STATE_PLAY) 1.1164 + { 1.1165 + Movie.inputBufferPtr += Movie.bytesPerFrame; 1.1166 + if (Movie.currentFrame >= Movie.header.length_frames) 1.1167 + { 1.1168 + // the movie ends anyway; what to do next depends on the settings 1.1169 + change_state(MOVIE_STATE_END); 1.1170 + } 1.1171 + } 1.1172 + else if (Movie.state == MOVIE_STATE_RECORD) 1.1173 + { 1.1174 + // use first fseek? 1.1175 + fwrite(Movie.inputBufferPtr, 1, Movie.bytesPerFrame, Movie.file); 1.1176 + Movie.header.length_frames = Movie.currentFrame; 1.1177 + Movie.inputBufferPtr += Movie.bytesPerFrame; 1.1178 + Movie.RecordedThisSession = true; 1.1179 + flush_movie_header(); 1.1180 + } 1.1181 + else if (Movie.state == MOVIE_STATE_END) 1.1182 + { 1.1183 + change_state(MOVIE_STATE_END); 1.1184 + } 1.1185 +} 1.1186 + 1.1187 +void VBAMovieRead(int i, bool /*sensor*/) 1.1188 +{ 1.1189 + if (Movie.state != MOVIE_STATE_PLAY) 1.1190 + return; 1.1191 + 1.1192 + if (i < 0 || i >= MOVIE_NUM_OF_POSSIBLE_CONTROLLERS) 1.1193 + return; // not a controller we're recognizing 1.1194 + 1.1195 + if (Movie.header.controllerFlags & MOVIE_CONTROLLER(i)) 1.1196 + { 1.1197 + currentButtons[i] = Read16(Movie.inputBufferPtr + CONTROLLER_DATA_SIZE * i); 1.1198 + } 1.1199 + else 1.1200 + { 1.1201 + currentButtons[i] = 0; // pretend the controller is disconnected 1.1202 + } 1.1203 + 1.1204 + if ((currentButtons[i] & BUTTON_MASK_NEW_RESET) != 0) 1.1205 + resetSignaled = true; 1.1206 +} 1.1207 + 1.1208 +void VBAMovieWrite(int i, bool /*sensor*/) 1.1209 +{ 1.1210 + if (Movie.state != MOVIE_STATE_RECORD) 1.1211 + return; 1.1212 + 1.1213 + if (i < 0 || i >= MOVIE_NUM_OF_POSSIBLE_CONTROLLERS) 1.1214 + return; // not a controller we're recognizing 1.1215 + 1.1216 + reserve_buffer_space((uint32)((Movie.inputBufferPtr - Movie.inputBuffer) + Movie.bytesPerFrame)); 1.1217 + 1.1218 + if (Movie.header.controllerFlags & MOVIE_CONTROLLER(i)) 1.1219 + { 1.1220 + // get the current controller data 1.1221 + uint16 buttonData = currentButtons[i]; 1.1222 + 1.1223 + // mask away the irrelevent bits 1.1224 + buttonData &= BUTTON_REGULAR_MASK | BUTTON_MOTION_MASK; 1.1225 + 1.1226 + // soft-reset "button" for 1 frame if the game is reset while recording 1.1227 + if (resetSignaled) 1.1228 + { 1.1229 + buttonData |= BUTTON_MASK_NEW_RESET; 1.1230 + } 1.1231 + 1.1232 + // backward compatibility kludge 1.1233 + if (resetSignaledLast) 1.1234 + { 1.1235 + buttonData |= BUTTON_MASK_OLD_RESET; 1.1236 + } 1.1237 + 1.1238 + Write16(buttonData, Movie.inputBufferPtr + CONTROLLER_DATA_SIZE * i); 1.1239 + 1.1240 + // and for display 1.1241 + currentButtons[i] = buttonData; 1.1242 + } 1.1243 + else 1.1244 + { 1.1245 + // pretend the controller is disconnected (otherwise input it gives could cause desync since we're not writing it to the 1.1246 + // movie) 1.1247 + currentButtons[i] = 0; 1.1248 + } 1.1249 +} 1.1250 + 1.1251 +void VBAMovieStop(bool8 suppress_message) 1.1252 +{ 1.1253 + if (Movie.state != MOVIE_STATE_NONE) 1.1254 + { 1.1255 + change_state(MOVIE_STATE_NONE); 1.1256 + if (!suppress_message) 1.1257 + systemScreenMessage("Movie stop"); 1.1258 + } 1.1259 +} 1.1260 + 1.1261 +int VBAMovieGetInfo(const char *filename, SMovie *info) 1.1262 +{ 1.1263 + assert(info != NULL); 1.1264 + if (info == NULL) 1.1265 + return -1; 1.1266 + 1.1267 + FILE * file; 1.1268 + int result; 1.1269 + SMovie &local_movie = *info; 1.1270 + 1.1271 + memset(info, 0, sizeof(*info)); 1.1272 + if (filename[0] == '\0') 1.1273 + return MOVIE_FILE_NOT_FOUND; 1.1274 + if (!(file = fopen(filename, "rb"))) 1.1275 + return MOVIE_FILE_NOT_FOUND; 1.1276 + 1.1277 + // read header 1.1278 + if ((result = (read_movie_header(file, local_movie))) != MOVIE_SUCCESS) 1.1279 + { 1.1280 + fclose(file); 1.1281 + return result; 1.1282 + } 1.1283 + 1.1284 + // read the metadata / author info from file 1.1285 + fread(local_movie.authorInfo, 1, sizeof(char) * MOVIE_METADATA_SIZE, file); 1.1286 + 1.1287 + strncpy(local_movie.filename, filename, _MAX_PATH); 1.1288 + local_movie.filename[_MAX_PATH - 1] = '\0'; 1.1289 + 1.1290 + if (Movie.file != NULL && stricmp(local_movie.filename, Movie.filename) == 0) // alreadyOpen 1.1291 + { 1.1292 + local_movie.bytesPerFrame = Movie.bytesPerFrame; 1.1293 + local_movie.header.length_frames = Movie.header.length_frames; 1.1294 + } 1.1295 + else 1.1296 + { 1.1297 + // recalculate length of movie from the file size 1.1298 + local_movie.bytesPerFrame = bytes_per_frame(local_movie); 1.1299 + fseek(file, 0, SEEK_END); 1.1300 + int fileSize = ftell(file); 1.1301 + local_movie.header.length_frames = 1.1302 + (fileSize - local_movie.header.offset_to_controller_data) / local_movie.bytesPerFrame; 1.1303 + } 1.1304 + 1.1305 + fclose(file); 1.1306 + 1.1307 + if (access(filename, W_OK)) 1.1308 + info->readOnly = true; 1.1309 + 1.1310 + return MOVIE_SUCCESS; 1.1311 +} 1.1312 + 1.1313 +bool8 VBAMovieActive() 1.1314 +{ 1.1315 + return (Movie.state != MOVIE_STATE_NONE); 1.1316 +} 1.1317 + 1.1318 +bool8 VBAMovieLoading() 1.1319 +{ 1.1320 + return loadingMovie; 1.1321 +} 1.1322 + 1.1323 +bool8 VBAMoviePlaying() 1.1324 +{ 1.1325 + return (Movie.state == MOVIE_STATE_PLAY); 1.1326 +} 1.1327 + 1.1328 +bool8 VBAMovieRecording() 1.1329 +{ 1.1330 + return (Movie.state == MOVIE_STATE_RECORD); 1.1331 +} 1.1332 + 1.1333 +bool8 VBAMovieReadOnly() 1.1334 +{ 1.1335 + if (!VBAMovieActive()) 1.1336 + return false; 1.1337 + 1.1338 + return Movie.readOnly; 1.1339 +} 1.1340 + 1.1341 +void VBAMovieToggleReadOnly() 1.1342 +{ 1.1343 + if (!VBAMovieActive()) 1.1344 + return; 1.1345 + 1.1346 + if (Movie.readOnly != 2) 1.1347 + { 1.1348 + Movie.readOnly = !Movie.readOnly; 1.1349 + 1.1350 + systemScreenMessage(Movie.readOnly ? "Movie now read-only" : "Movie now editable"); 1.1351 + } 1.1352 + else 1.1353 + { 1.1354 + systemScreenMessage("Can't toggle read-only movie"); 1.1355 + } 1.1356 +} 1.1357 + 1.1358 +uint32 VBAMovieGetVersion() 1.1359 +{ 1.1360 + if (!VBAMovieActive()) 1.1361 + return 0; 1.1362 + 1.1363 + return Movie.header.version; 1.1364 +} 1.1365 + 1.1366 +uint32 VBAMovieGetMinorVersion() 1.1367 +{ 1.1368 + if (!VBAMovieActive()) 1.1369 + return 0; 1.1370 + 1.1371 + return Movie.header.minorVersion; 1.1372 +} 1.1373 + 1.1374 +uint32 VBAMovieGetId() 1.1375 +{ 1.1376 + if (!VBAMovieActive()) 1.1377 + return 0; 1.1378 + 1.1379 + return Movie.header.uid; 1.1380 +} 1.1381 + 1.1382 +uint32 VBAMovieGetLength() 1.1383 +{ 1.1384 + if (!VBAMovieActive()) 1.1385 + return 0; 1.1386 + 1.1387 + return Movie.header.length_frames; 1.1388 +} 1.1389 + 1.1390 +uint32 VBAMovieGetFrameCounter() 1.1391 +{ 1.1392 + if (!VBAMovieActive()) 1.1393 + return 0; 1.1394 + 1.1395 + return Movie.currentFrame; 1.1396 +} 1.1397 + 1.1398 +uint32 VBAMovieGetRerecordCount() 1.1399 +{ 1.1400 + if (!VBAMovieActive()) 1.1401 + return 0; 1.1402 + 1.1403 + return Movie.header.rerecord_count; 1.1404 +} 1.1405 + 1.1406 +uint32 VBAMovieSetRerecordCount(uint32 newRerecordCount) 1.1407 +{ 1.1408 + uint32 oldRerecordCount = 0; 1.1409 + if (!VBAMovieActive()) 1.1410 + return 0; 1.1411 + 1.1412 + oldRerecordCount = Movie.header.rerecord_count; 1.1413 + Movie.header.rerecord_count = newRerecordCount; 1.1414 + return oldRerecordCount; 1.1415 +} 1.1416 + 1.1417 +std::string VBAMovieGetAuthorInfo() 1.1418 +{ 1.1419 + if (!VBAMovieActive()) 1.1420 + return ""; 1.1421 + 1.1422 + return Movie.authorInfo; 1.1423 +} 1.1424 + 1.1425 +std::string VBAMovieGetFilename() 1.1426 +{ 1.1427 + if (!VBAMovieActive()) 1.1428 + return ""; 1.1429 + 1.1430 + return Movie.filename; 1.1431 +} 1.1432 + 1.1433 +void VBAMovieFreeze(uint8 * *buf, uint32 *size) 1.1434 +{ 1.1435 + // sanity check 1.1436 + if (!VBAMovieActive()) 1.1437 + { 1.1438 + return; 1.1439 + } 1.1440 + 1.1441 + *buf = NULL; 1.1442 + *size = 0; 1.1443 + 1.1444 + // compute size needed for the buffer 1.1445 + // room for header.uid, currentFrame, and header.length_frames 1.1446 + uint32 size_needed = sizeof(Movie.header.uid) + sizeof(Movie.currentFrame) + sizeof(Movie.header.length_frames); 1.1447 + size_needed += (uint32)(Movie.bytesPerFrame * Movie.header.length_frames); 1.1448 + *buf = new uint8[size_needed]; 1.1449 + *size = size_needed; 1.1450 + 1.1451 + uint8 *ptr = *buf; 1.1452 + if (!ptr) 1.1453 + { 1.1454 + return; 1.1455 + } 1.1456 + 1.1457 + Push32(Movie.header.uid, ptr); 1.1458 + Push32(Movie.currentFrame, ptr); 1.1459 + Push32(Movie.header.length_frames - 1, ptr); // HACK: shorten the length by 1 for backward compatibility 1.1460 + 1.1461 + memcpy(ptr, Movie.inputBuffer, Movie.bytesPerFrame * Movie.header.length_frames); 1.1462 +} 1.1463 + 1.1464 +int VBAMovieUnfreeze(const uint8 *buf, uint32 size) 1.1465 +{ 1.1466 + // sanity check 1.1467 + if (!VBAMovieActive()) 1.1468 + { 1.1469 + return MOVIE_NOT_FROM_A_MOVIE; 1.1470 + } 1.1471 + 1.1472 + const uint8 *ptr = buf; 1.1473 + if (size < sizeof(Movie.header.uid) + sizeof(Movie.currentFrame) + sizeof(Movie.header.length_frames)) 1.1474 + { 1.1475 + return MOVIE_WRONG_FORMAT; 1.1476 + } 1.1477 + 1.1478 + uint32 movie_id = Pop32(ptr); 1.1479 + uint32 current_frame = Pop32(ptr); 1.1480 + uint32 end_frame = Pop32(ptr) + 1; // HACK: restore the length for backward compatibility 1.1481 + uint32 space_needed = Movie.bytesPerFrame * end_frame; 1.1482 + 1.1483 + if (movie_id != Movie.header.uid) 1.1484 + return MOVIE_NOT_FROM_THIS_MOVIE; 1.1485 + 1.1486 + if (space_needed > size) 1.1487 + return MOVIE_WRONG_FORMAT; 1.1488 + 1.1489 + if (Movie.readOnly) 1.1490 + { 1.1491 + // here, we are going to keep the input data from the movie file 1.1492 + // and simply rewind to the currentFrame pointer 1.1493 + // this will cause a desync if the savestate is not in sync // <-- NOT ANYMORE 1.1494 + // with the on-disk recording data, but it's easily solved 1.1495 + // by loading another savestate or playing the movie from the beginning 1.1496 + 1.1497 + // don't allow loading a state inconsistent with the current movie 1.1498 + uint32 length_history = min(current_frame, Movie.header.length_frames); 1.1499 + if (end_frame < length_history) 1.1500 + return MOVIE_SNAPSHOT_INCONSISTENT; 1.1501 + 1.1502 + uint32 space_shared = Movie.bytesPerFrame * length_history; 1.1503 + if (memcmp(Movie.inputBuffer, ptr, space_shared)) 1.1504 + return MOVIE_SNAPSHOT_INCONSISTENT; 1.1505 + 1.1506 + Movie.currentFrame = current_frame; 1.1507 + Movie.inputBufferPtr = Movie.inputBuffer + Movie.bytesPerFrame * min(current_frame, Movie.header.length_frames); 1.1508 + } 1.1509 + else 1.1510 + { 1.1511 + // here, we are going to take the input data from the savestate 1.1512 + // and make it the input data for the current movie, then continue 1.1513 + // writing new input data at the currentFrame pointer 1.1514 + Movie.currentFrame = current_frame; 1.1515 + Movie.header.length_frames = end_frame; 1.1516 + if (!VBALuaRerecordCountSkip()) 1.1517 + ++Movie.header.rerecord_count; 1.1518 + 1.1519 + Movie.RecordedThisSession = true; 1.1520 + 1.1521 + // do this before calling reserve_buffer_space() 1.1522 + Movie.inputBufferPtr = Movie.inputBuffer + Movie.bytesPerFrame * min(current_frame, Movie.header.length_frames); 1.1523 + reserve_buffer_space(space_needed); 1.1524 + memcpy(Movie.inputBuffer, ptr, space_needed); 1.1525 + 1.1526 + // for consistency, no auto movie conversion here since we don't auto convert the corresponding savestate 1.1527 + flush_movie_header(); 1.1528 + flush_movie_frames(); 1.1529 + } 1.1530 + 1.1531 + change_state(MOVIE_STATE_PLAY); // check for movie end 1.1532 + 1.1533 + // necessary! 1.1534 + resetSignaled = false; 1.1535 + resetSignaledLast = false; 1.1536 + 1.1537 + // necessary to check if there's a reset signal at the previous frame 1.1538 + if (current_frame > 0) 1.1539 + { 1.1540 + const u8 NEW_RESET = u8(BUTTON_MASK_NEW_RESET >> 8); 1.1541 + for (int i = 0; i < MOVIE_NUM_OF_POSSIBLE_CONTROLLERS; ++i) 1.1542 + { 1.1543 + if ((Movie.header.controllerFlags & MOVIE_CONTROLLER(i)) && (*(Movie.inputBufferPtr+1- Movie.bytesPerFrame) & NEW_RESET)) 1.1544 + { 1.1545 + resetSignaledLast = true; 1.1546 + break; 1.1547 + } 1.1548 + } 1.1549 + } 1.1550 + 1.1551 + return MOVIE_SUCCESS; 1.1552 +} 1.1553 + 1.1554 +bool VBAMovieEnded() 1.1555 +{ 1.1556 + return (Movie.state == MOVIE_STATE_END); 1.1557 +// return (Movie.state != MOVIE_STATE_NONE && Movie.currentFrame >= Movie.header.length_frames); 1.1558 +} 1.1559 + 1.1560 +bool VBAMovieAllowsRerecording() 1.1561 +{ 1.1562 + bool allows = (Movie.state != MOVIE_STATE_NONE) && (Movie.currentFrame <= Movie.header.length_frames); 1.1563 + return /*!VBAMovieReadOnly() &&*/ allows; 1.1564 +} 1.1565 + 1.1566 +bool VBAMovieSwitchToPlaying() 1.1567 +{ 1.1568 + if (!VBAMovieActive()) 1.1569 + return false; 1.1570 + 1.1571 + if (!Movie.readOnly) 1.1572 + { 1.1573 + VBAMovieToggleReadOnly(); 1.1574 + } 1.1575 + 1.1576 + change_state(MOVIE_STATE_PLAY); 1.1577 + if (Movie.state == MOVIE_STATE_PLAY) 1.1578 + systemScreenMessage("Movie replay (continue)"); 1.1579 + else 1.1580 + systemScreenMessage("Movie end"); 1.1581 + 1.1582 + return true; 1.1583 +} 1.1584 + 1.1585 +bool VBAMovieSwitchToRecording() 1.1586 +{ 1.1587 + if (!VBAMovieAllowsRerecording()) 1.1588 + return false; 1.1589 + 1.1590 + if (Movie.readOnly) 1.1591 + { 1.1592 + VBAMovieToggleReadOnly(); 1.1593 + } 1.1594 + 1.1595 + if (!VBALuaRerecordCountSkip()) 1.1596 + ++Movie.header.rerecord_count; 1.1597 + 1.1598 + change_state(MOVIE_STATE_RECORD); 1.1599 + systemScreenMessage("Movie re-record"); 1.1600 + 1.1601 + //truncate_movie(Movie.currentFrame); 1.1602 + 1.1603 + return true; 1.1604 +} 1.1605 + 1.1606 +uint32 VBAMovieGetState() 1.1607 +{ 1.1608 + // ? 1.1609 + if (!VBAMovieActive()) 1.1610 + return MOVIE_STATE_NONE; 1.1611 + 1.1612 + return Movie.state; 1.1613 +} 1.1614 + 1.1615 +void VBAMovieSignalReset() 1.1616 +{ 1.1617 + if (VBAMovieActive()) 1.1618 + resetSignaled = true; 1.1619 +} 1.1620 + 1.1621 +void VBAMovieResetIfRequested() 1.1622 +{ 1.1623 + if (resetSignaled) 1.1624 + { 1.1625 + theEmulator.emuReset(false); 1.1626 + resetSignaled = false; 1.1627 + resetSignaledLast = true; 1.1628 + } 1.1629 + else 1.1630 + { 1.1631 + resetSignaledLast = false; 1.1632 + } 1.1633 +} 1.1634 + 1.1635 +void VBAMovieSetMetadata(const char *info) 1.1636 +{ 1.1637 + if (!memcmp(Movie.authorInfo, info, MOVIE_METADATA_SIZE)) 1.1638 + return; 1.1639 + 1.1640 + memcpy(Movie.authorInfo, info, MOVIE_METADATA_SIZE); // strncpy would omit post-0 bytes 1.1641 + Movie.authorInfo[MOVIE_METADATA_SIZE - 1] = '\0'; 1.1642 + 1.1643 + if (Movie.file) 1.1644 + { 1.1645 + // (over-)write the header 1.1646 + fseek(Movie.file, 0, SEEK_SET); 1.1647 + write_movie_header(Movie.file, Movie); 1.1648 + 1.1649 + // write the metadata / author info to file 1.1650 + fwrite(Movie.authorInfo, 1, sizeof(char) * MOVIE_METADATA_SIZE, Movie.file); 1.1651 + 1.1652 + fflush(Movie.file); 1.1653 + } 1.1654 +} 1.1655 + 1.1656 +void VBAMovieRestart() 1.1657 +{ 1.1658 + if (VBAMovieActive()) 1.1659 + { 1.1660 + systemSoundClearBuffer(); 1.1661 + 1.1662 + bool8 modified = Movie.RecordedThisSession; 1.1663 + 1.1664 + VBAMovieStop(true); 1.1665 + 1.1666 + char movieName [_MAX_PATH]; 1.1667 + strncpy(movieName, Movie.filename, _MAX_PATH); 1.1668 + movieName[_MAX_PATH - 1] = '\0'; 1.1669 + VBAMovieOpen(movieName, Movie.readOnly); // can't just pass in Movie.filename, since VBAMovieOpen clears out Movie's 1.1670 + // variables 1.1671 + 1.1672 + Movie.RecordedThisSession = modified; 1.1673 + 1.1674 + systemScreenMessage("Movie replay (restart)"); 1.1675 + } 1.1676 +} 1.1677 + 1.1678 +int VBAMovieGetPauseAt() 1.1679 +{ 1.1680 + return Movie.pauseFrame; 1.1681 +} 1.1682 + 1.1683 +void VBAMovieSetPauseAt(int at) 1.1684 +{ 1.1685 + Movie.pauseFrame = at; 1.1686 +} 1.1687 + 1.1688 +/////////////////////// 1.1689 +// movie tools 1.1690 + 1.1691 +// FIXME: is it safe to convert/flush a movie while recording it (considering fseek() problem)? 1.1692 +int VBAMovieConvertCurrent() 1.1693 +{ 1.1694 + if (!VBAMovieActive()) 1.1695 + { 1.1696 + return MOVIE_NOTHING; 1.1697 + } 1.1698 + 1.1699 + if (Movie.header.minorVersion > VBM_REVISION) 1.1700 + { 1.1701 + return MOVIE_WRONG_VERSION; 1.1702 + } 1.1703 + 1.1704 + if (Movie.header.minorVersion == VBM_REVISION) 1.1705 + { 1.1706 + return MOVIE_NOTHING; 1.1707 + } 1.1708 + 1.1709 + Movie.header.minorVersion = VBM_REVISION; 1.1710 + 1.1711 + if (Movie.header.length_frames == 0) // this could happen 1.1712 + { 1.1713 + truncate_movie(0); 1.1714 + return MOVIE_SUCCESS; 1.1715 + } 1.1716 + 1.1717 + // fix movies recorded from snapshots 1.1718 + if (Movie.header.startFlags & MOVIE_START_FROM_SNAPSHOT) 1.1719 + { 1.1720 + uint8 *firstFramePtr = Movie.inputBuffer; 1.1721 + for (int i = 0; i < MOVIE_NUM_OF_POSSIBLE_CONTROLLERS; ++i) 1.1722 + { 1.1723 + if (Movie.header.controllerFlags & MOVIE_CONTROLLER(i)) 1.1724 + { 1.1725 + Push16(initialInputs[i], firstFramePtr); 1.1726 + // note: this is correct since Push16 advances the dest pointer by sizeof u16 1.1727 + } 1.1728 + } 1.1729 + } 1.1730 + 1.1731 + // convert old resets to new ones 1.1732 + const u8 OLD_RESET = u8(BUTTON_MASK_OLD_RESET >> 8); 1.1733 + const u8 NEW_RESET = u8(BUTTON_MASK_NEW_RESET >> 8); 1.1734 + for (int i = 0; i < MOVIE_NUM_OF_POSSIBLE_CONTROLLERS; ++i) 1.1735 + { 1.1736 + if (Movie.header.controllerFlags & MOVIE_CONTROLLER(i)) 1.1737 + { 1.1738 + uint8 *startPtr = Movie.inputBuffer + sizeof(u16) * i + 1; 1.1739 + uint8 *endPtr = Movie.inputBuffer + Movie.bytesPerFrame * (Movie.header.length_frames - 1); 1.1740 + for (; startPtr < endPtr; startPtr += Movie.bytesPerFrame) 1.1741 + { 1.1742 + if (startPtr[Movie.bytesPerFrame] & OLD_RESET) 1.1743 + { 1.1744 + startPtr[0] |= NEW_RESET; 1.1745 + } 1.1746 + } 1.1747 + } 1.1748 + } 1.1749 + 1.1750 + flush_movie_header(); 1.1751 + flush_movie_frames(); 1.1752 + return MOVIE_SUCCESS; 1.1753 +} 1.1754 + 1.1755 +bool VBAMovieTuncateAtCurrentFrame() 1.1756 +{ 1.1757 + if (!VBAMovieActive()) 1.1758 + return false; 1.1759 + 1.1760 + truncate_movie(Movie.currentFrame); 1.1761 + change_state(MOVIE_STATE_END); 1.1762 + systemScreenMessage("Movie truncated"); 1.1763 + 1.1764 + return true; 1.1765 +} 1.1766 + 1.1767 +bool VBAMovieFixHeader() 1.1768 +{ 1.1769 + if (!VBAMovieActive()) 1.1770 + return false; 1.1771 + 1.1772 + flush_movie_header(); 1.1773 + systemScreenMessage("Movie header fixed"); 1.1774 + return true; 1.1775 +}