void FCEUI_SelectState(int w) { if(w == -1) { StateShow = 0; return; } FCEUI_SelectMovie(-1); CurrentState=w; StateShow=180; FCEU_DispMessage("-select state-"); }
void FCEUI_SaveMovie(char *fname, uint8 flags, const char* metadata) { FILE *fp; char *fn; int poweron=0; uint8 padding[4] = {0,0,0,0}; int n_padding; FCEUI_StopMovie(); char origname[512]; if(fname) { fp = FCEUD_UTF8fopen(fname, "wb"); strcpy(origname,fname); } else { fp=FCEUD_UTF8fopen(fn=FCEU_MakeFName(FCEUMKF_MOVIE,CurrentMovie,0),"wb"); strcpy(origname,fn); free(fn); } if(!fp) return; // don't need the movieSyncHackOn sync hack for newly recorded movies flags |= MOVIE_FLAG_NOSYNCHACK; resetDMCacc=movieSyncHackOn=0; // add PAL flag if(FCEUI_GetCurrentVidSystem(0,0)) flags |= MOVIE_FLAG_PAL; if(flags & MOVIE_FLAG_FROM_POWERON) { poweron=1; flags &= ~MOVIE_FLAG_FROM_POWERON; flags |= MOVIE_FLAG_FROM_RESET; } // write header write32le(MOVIE_MAGIC, fp); write32le(MOVIE_VERSION, fp); fputc(flags, fp); fputc(0, fp); // reserved fputc(0, fp); // reserved fputc(0, fp); // reserved write32le(0, fp); // leave room for length frames write32le(0, fp); // leave room for rerecord count write32le(0, fp); // leave room for movie data size write32le(0, fp); // leave room for savestate_offset write32le(0, fp); // leave room for offset_to_controller_data fwrite(FCEUGameInfo->MD5, 1, 16, fp); // write ROM checksum write32le(FCEU_VERSION_NUMERIC, fp); // write emu version used fputs(FileBase, fp); // write ROM name used fputc(0, fp); if(metadata) { if(strlen(metadata) < MOVIE_MAX_METADATA) fputs(metadata, fp); else fwrite(metadata, 1, MOVIE_MAX_METADATA-1, fp); } fputc(0, fp); // add padding n_padding = (4 - (ftell(fp) & 0x3)) & 0x3; fwrite(padding, 1, n_padding, fp); if(flags & MOVIE_FLAG_FROM_RESET) { if(poweron) { // make a for-movie-recording power-on clear the game's save data, too // (note: FCEU makes a save state immediately after this and that loads that on movie playback) extern char lastLoadedGameName [2048]; extern int disableBatteryLoading, suppressAddPowerCommand; suppressAddPowerCommand=1; disableBatteryLoading=1; suppressMovieStop=1; { // NOTE: this will NOT write an FCEUNPCMD_POWER into the movie file FCEUGI * gi = FCEUI_LoadGame(lastLoadedGameName); if(!gi) PowerNES(); // and neither will this, if it can even happen } suppressMovieStop=0; disableBatteryLoading=0; suppressAddPowerCommand=0; } } savestate_offset = ftell(fp); FCEUSS_SaveFP(fp); fseek(fp, 0, SEEK_END); ResetInputTypes(); // add padding n_padding = (4 - (ftell(fp) & 0x3)) & 0x3; fwrite(padding, 1, n_padding, fp); firstframeoffset = ftell(fp); // finish header fseek(fp, 24, SEEK_SET); // offset_to_savestate offset write32le(savestate_offset, fp); write32le(firstframeoffset, fp); fseek(fp, firstframeoffset, SEEK_SET); // set recording flag current=CurrentMovie; movie_readonly = 0; frameptr = 0; framecount = 0; rerecord_count = 0; slots[current] = fp; memset(joop,0,sizeof(joop)); current++; framets=0; nextd = -1; // trigger a reset if(flags & MOVIE_FLAG_FROM_RESET) { if(poweron) { PowerNES(); // NOTE: this will write an FCEUNPCMD_POWER into the movie file } else ResetNES(); // NOTE: this will write an FCEUNPCMD_RESET into the movie file } if(!fname) FCEUI_SelectMovie(CurrentMovie,1); /* Quick hack to display status. */ else FCEU_DispMessage("Movie recording started."); strcpy(curMovieFilename, origname); }
// PlayMovie / MoviePlay function void FCEUI_LoadMovie(char *fname, int _read_only) { char buffer [512]; fname = (char*)convertToFCM(fname,buffer); FILE *fp; char *fn = NULL; FCEUI_StopMovie(); #if 0 if(!fname) fname = fn = FCEU_MakeFName(FCEUMKF_MOVIE,CurrentMovie,0); #endif #if 0 char origname[512]; strcpy(origname,fname); #endif // check movie_readonly movie_readonly = _read_only; if(access(fname, W_OK)) movie_readonly = 2; fp = fopen(fname, (movie_readonly>=2) ? "rb" : "r+b"); if(fn) { free(fn); fname = NULL; } if(!fp) return; // read header { uint32 magic; uint32 version; uint8 flags[4]; read32le(&magic, fp); if(magic != MOVIE_MAGIC) { fclose(fp); return; } //DEBUG_COMPARE_RAM(__LINE__); read32le(&version, fp); if(version == 1) { // attempt to load previous version's format fclose(fp); printf("movie: trying movie v1\n"); FCEUI_LoadMovie_v1(fname, _read_only); return; } else if(version == MOVIE_VERSION) {} else { // unsupported version printf("movie: unsupported version\n"); fclose(fp); return; } fread(flags, 1, 4, fp); read32le(&framecount, fp); read32le(&rerecord_count, fp); read32le(&moviedatasize, fp); read32le(&savestate_offset, fp); read32le(&firstframeoffset, fp); if(fseek(fp, savestate_offset, SEEK_SET)) { fclose(fp); return; } // FCEU_PrintError("flags[0] & MOVIE_FLAG_NOSYNCHACK=%d",flags[0] & MOVIE_FLAG_NOSYNCHACK); if(flags[0] & MOVIE_FLAG_NOSYNCHACK) movieSyncHackOn=0; else movieSyncHackOn=1; } // fully reload the game to reinitialize everything before playing any movie // to try fixing nondeterministic playback of some games #if 0 // do we need this? { extern char lastLoadedGameName [2048]; #if 0 // TODO? extern int disableBatteryLoading, suppressAddPowerCommand; suppressAddPowerCommand=1; suppressMovieStop=1; #endif { FCEUGI * gi = FCEUI_LoadGame(lastLoadedGameName); if(!gi) PowerNES(); } #if 0 // TODO? suppressMovieStop=0; suppressAddPowerCommand=0; #endif } #endif // Loading new savestates doesn't work and even breaks FDS //if(!FCEUSS_LoadFP(fp,1)) return; ResetInputTypes(); fseek(fp, firstframeoffset, SEEK_SET); moviedata = (uint8*)realloc(moviedata, moviedatasize); fread(moviedata, 1, moviedatasize, fp); framecount = 0; // movies start at frame 0! frameptr = 0; current = CurrentMovie; slots[current] = fp; memset(joop,0,sizeof(joop)); current = -1 - current; framets=0; nextts=0; nextd = -1; MovieStatus[CurrentMovie] = 1; #if 0 if(!fname) FCEUI_SelectMovie(CurrentMovie,1); /* Quick hack to display status. */ else #endif FCEU_DispMessage("Movie playback started."); #if 0 strcpy(curMovieFilename, origname); #else strcpy(curMovieFilename, fname); #endif }