diff -NaHudr fceu-0.98.12/src/driver.h fceu/src/driver.h --- fceu-0.98.12/src/driver.h 2004-08-18 01:04:37.000000000 +0300 +++ fceu/src/driver.h 2004-08-29 22:32:08.000000000 +0300 @@ -166,9 +166,17 @@ void FCEUI_SaveState(char *fname); void FCEUI_LoadState(char *fname); +#define MOVIE_FLAG_FROM_RESET (1<<1) +#define MOVIE_MAX_METADATA 512 + void FCEUI_SelectMovie(int); -void FCEUI_SaveMovie(char *fname); -void FCEUI_LoadMovie(char *fname); +void FCEUI_SaveMovie(char *fname, uint8 flags, const wchar_t* metadata); +void FCEUI_LoadMovie(char *fname, int read_only); +void FCEUI_StopMovie(void); +int FCEUI_IsMovieActive(void); +int FCEUI_MovieGetInfo(const char* fname, uint32* num_frames, uint8* flags, uint32* rerecord_count, int* read_only, wchar_t *metadata); +char* FCEUI_MovieGetCurrentName(void); +void FCEUI_MovieToggleFrameDisplay(void); int32 FCEUI_GetDesiredFPS(void); void FCEUI_SaveSnapshot(void); @@ -234,6 +242,10 @@ int FCEUI_DatachSet(const uint8 *rcode); +int FCEUI_EmulationPaused(void); +void FCEUI_ToggleEmulationPause(void); +void FCEUI_FrameAdvance(void); + #ifdef __cplusplus } #endif diff -NaHudr fceu-0.98.12/src/drivers/pc/input.c fceu/src/drivers/pc/input.c --- fceu-0.98.12/src/drivers/pc/input.c 2004-08-18 01:10:09.000000000 +0300 +++ fceu/src/drivers/pc/input.c 2004-08-29 22:32:08.000000000 +0300 @@ -177,14 +177,14 @@ if(keyonly(F5)) { if(is_shift) - FCEUI_SaveMovie(NULL); + FCEUI_SaveMovie(NULL,0,NULL); else FCEUI_SaveState(NULL); } if(keyonly(F7)) { if(is_shift) - FCEUI_LoadMovie(NULL); + FCEUI_LoadMovie(NULL,0); else FCEUI_LoadState(NULL); } @@ -192,6 +192,12 @@ if(keyonly(F1)) FCEUI_ToggleTileView(); + if(keyonly(MINUS)) DecreaseEmulationSpeed(); + if(keyonly(EQUAL)) IncreaseEmulationSpeed(); + if(keyonly(BACKSPACE)) FCEUI_MovieToggleFrameDisplay(); + if(keyonly(BACKSLASH)) FCEUI_ToggleEmulationPause(); + if(keyonly(RIGHTCONTROL)) FCEUI_FrameAdvance(); + if(keyonly(F10)) FCEUI_ResetNES(); if(keyonly(F11)) FCEUI_PowerNES(); diff -NaHudr fceu-0.98.12/src/drivers/pc/input.h fceu/src/drivers/pc/input.h --- fceu-0.98.12/src/drivers/pc/input.h 2004-08-18 01:10:22.000000000 +0300 +++ fceu/src/drivers/pc/input.h 2004-08-29 22:33:14.000000000 +0300 @@ -31,5 +31,8 @@ extern ButtConfig FTrainerButtons[12]; #endif +void IncreaseEmulationSpeed(void); +void DecreaseEmulationSpeed(void); + void FCEUD_UpdateInput(void); #endif diff -NaHudr fceu-0.98.12/src/drivers/pc/main.c fceu/src/drivers/pc/main.c --- fceu-0.98.12/src/drivers/pc/main.c 2004-08-18 01:08:44.000000000 +0300 +++ fceu/src/drivers/pc/main.c 2004-08-29 22:38:25.000000000 +0300 @@ -37,6 +37,7 @@ #include "input.h" #include "dface.h" +extern int32 fps_scale; int CloseGame(void); @@ -331,6 +332,7 @@ int32 *sound; int32 ssize; static int fskipc=0; + static int opause=0; #ifdef FRAMESKIP fskipc=(fskipc+1)%(frameskip+1); @@ -339,6 +341,12 @@ if(NoWaiting) {gfx=0;} FCEUI_Emulate(&gfx, &sound, &ssize, fskipc); FCEUD_Update(gfx, sound, ssize); + + if(opause!=FCEUI_EmulationPaused()) + { + opause=FCEUI_EmulationPaused(); + SilenceSound(opause); + } } int CLImain(int argc, char *argv[]) @@ -446,6 +454,9 @@ extern int FCEUDnetplay; #endif + int ocount = Count; + // apply frame scaling to Count + Count = (Count<<8)/fps_scale; //Count = 0; if(Count) { @@ -453,7 +464,8 @@ static int uflow=0; int32 tmpcan; - if(can >= GetMaxSound()) uflow=1; /* Go into massive underflow mode. */ + // don't underflow when scaling fps + if(can >= GetMaxSound() && fps_scale<=256) uflow=1; /* Go into massive underflow mode. */ if(can > Count) can=Count; else uflow=0; @@ -462,7 +474,8 @@ //if(uflow) puts("Underflow"); tmpcan = GetWriteSound(); - if((tmpcan < Count*0.90) && !uflow) + // don't underflow when scaling fps + if(fps_scale>256 || ((tmpcan < Count*0.90) && !uflow)) { if(XBuf && (inited&4) && !(NoWaiting & 2)) BlitScreen(XBuf); @@ -474,8 +487,16 @@ { can=GetWriteSound(); if(Count>can) Count=can; + WriteSound(Buffer,Count); + } + else + { + while(Count>0) + { + WriteSound(Buffer,(Count>8; + desiredfps=(desiredfps*fps_scale)>>8; tfreq=10000000; tfreq<<=16; /* Adjustment for fps returned from FCEUI_GetDesiredFPS(). */ } @@ -36,3 +41,26 @@ ltime+=tfreq/desiredfps; } +void IncreaseEmulationSpeed(void) +{ + int i; + for(i=1; fps_scale_table[i]>8); +} + +void DecreaseEmulationSpeed(void) +{ + int i; + for(i=1; fps_scale_table[i]>8); +} diff -NaHudr fceu-0.98.12/src/drivers/win/input.c fceu/src/drivers/win/input.c --- fceu-0.98.12/src/drivers/win/input.c 2004-08-18 00:46:52.000000000 +0300 +++ fceu/src/drivers/win/input.c 2004-08-29 22:32:08.000000000 +0300 @@ -182,14 +182,14 @@ if(keyonly(F5)) { if(is_shift) - FCEUI_SaveMovie(NULL); + FCEUI_SaveMovie(NULL,0,NULL); else FCEUI_SaveState(NULL); } if(keyonly(F7)) { if(is_shift) - FCEUI_LoadMovie(NULL); + FCEUI_LoadMovie(NULL,0); else FCEUI_LoadState(NULL); } @@ -197,6 +197,12 @@ if(keyonly(F1)) FCEUI_ToggleTileView(); + if(keyonly(MINUS)) DecreaseEmulationSpeed(); + if(keyonly(EQUAL)) IncreaseEmulationSpeed(); + if(keyonly(BACKSPACE)) FCEUI_MovieToggleFrameDisplay(); + if(keyonly(ENTER)) FCEUI_ToggleEmulationPause(); + if(keyonly(RIGHTCONTROL)) FCEUI_FrameAdvance(); + if(gametype==GIT_VSUNI) { if(keyonly(F8)) FCEUI_VSUniCoin(); @@ -223,8 +229,8 @@ if(keyonly(H)) FCEUI_NTSCSELHUE(); if(keyonly(T)) FCEUI_NTSCSELTINT(); - if(KEY(KP_MINUS) || KEY(MINUS)) FCEUI_NTSCDEC(); - if(KEY(KP_PLUS) || KEY(EQUAL)) FCEUI_NTSCINC(); + if(KEY(KP_MINUS)) FCEUI_NTSCDEC(); + if(KEY(KP_PLUS)) FCEUI_NTSCINC(); if((InputType[2] == SIFC_BWORLD) || (cspec == SIS_DATACH)) { diff -NaHudr fceu-0.98.12/src/drivers/win/input.h.rej fceu/src/drivers/win/input.h.rej --- fceu-0.98.12/src/drivers/win/input.h.rej 1970-01-01 02:00:00.000000000 +0200 +++ fceu/src/drivers/win/input.h.rej 2004-08-29 22:32:08.000000000 +0300 @@ -0,0 +1,15 @@ +*************** +*** 40,44 **** + #define FCFGD_QUIZKING 4 + + void InitOtherInput(void); + #endif + +--- 40,46 ---- + #define FCFGD_QUIZKING 4 + + void InitOtherInput(void); ++ void IncreaseEmulationSpeed(void); ++ void DecreaseEmulationSpeed(void); + #endif + diff -NaHudr fceu-0.98.12/src/drivers/win/main.c fceu/src/drivers/win/main.c --- fceu-0.98.12/src/drivers/win/main.c 2004-08-18 00:46:52.000000000 +0300 +++ fceu/src/drivers/win/main.c 2004-08-29 22:42:36.000000000 +0300 @@ -370,6 +370,9 @@ FCEUI_Emulate(&gfx, &sound, &ssize, 0); xbsave = gfx; FCEUD_Update(gfx, sound, ssize); + + if(FCEUI_EmulationPaused()) + StopSound(); } xbsave = NULL; RedrawWindow(hAppWnd,0,0,RDW_ERASE|RDW_INVALIDATE); @@ -395,6 +398,9 @@ if(NoWaiting & 1) maxskip = ffbskip; + int ocount = Count; + // apply frame scaling to Count + Count = (Count<<8)/fps_scale; if(Count) { int32 can=GetWriteSound(); @@ -402,7 +408,8 @@ int32 tmpcan; extern int FCEUDnetplay; - if(can >= GetMaxSound()) uflow=1; /* Go into massive underflow mode. */ + // don't underflow when scaling fps + if(can >= GetMaxSound() && fps_scale<=256) uflow=1; /* Go into massive underflow mode. */ if(can > Count) can=Count; else uflow=0; @@ -410,7 +417,8 @@ FCEUD_WriteSoundData(Buffer,can); tmpcan = GetWriteSound(); - if(((tmpcan < Count*0.90) && !uflow) || (skipcount >= maxskip)) + // don't underflow when scaling fps + if(fps_scale>256 || (((tmpcan < Count*0.90) && !uflow) || (skipcount >= maxskip))) { if(XBuf && (!NoWaiting || skipcount >= maxskip)) { @@ -430,8 +438,18 @@ { can=GetWriteSound(); if(Count>can) Count=can; - } - FCEUD_WriteSoundData(Buffer,Count); + FCEUD_WriteSoundData(Buffer,Count); + } + else + { + while(Count>0) + { + FCEUD_WriteSoundData(Buffer,(Count>8; + desiredfps=(desiredfps*fps_scale)>>8; } static uint64 GetCurTime(void) diff -NaHudr fceu-0.98.12/src/drivers/win/window.c fceu/src/drivers/win/window.c --- fceu-0.98.12/src/drivers/win/window.c 2004-08-18 00:46:52.000000000 +0300 +++ fceu/src/drivers/win/window.c 2004-08-29 22:44:11.000000000 +0300 @@ -37,6 +37,10 @@ static void ConfigPalette(void); static void ConfigDirectories(void); +static void DoCreateMovieDialog(void); +static void DoOpenMovieDialog(void); +static void DoStopMovie(void); + static HMENU fceumenu=0; static int tog=0; @@ -73,7 +77,7 @@ void RedoMenuGI(FCEUGI *gi) { - int simpled[]={101,111,110,200,201,204,203,0}; + int simpled[]={101,111,110,200,201,204,203,141,142,143,0}; int x; x = 0; @@ -499,6 +503,10 @@ break; case 130:DoFCEUExit();break; + case 141:DoCreateMovieDialog();break; + case 142:DoOpenMovieDialog();break; + case 143:DoStopMovie();break; + case 400:StopSound();ShowAboutBox();break; case 401:MakeLogWindow();break; } @@ -569,7 +577,7 @@ { case VK_F11:FCEUI_PowerNES();break; case VK_F12:DoFCEUExit();break; - case VK_F2:userpause^=1;break; + //case VK_F2:userpause^=1;break; case VK_F3:ToggleHideMenu();break; } goto proco; @@ -585,6 +593,9 @@ { nofocus=1; } + goto proco; + case WM_ENTERMENULOOP: + EnableMenuItem(fceumenu,143,MF_BYCOMMAND | (FCEUI_IsMovieActive()?MF_ENABLED:MF_GRAYED)); default: proco: return DefWindowProc(hWnd,msg,wParam,lParam); diff -NaHudr fceu-0.98.12/src/fceu.c fceu/src/fceu.c --- fceu-0.98.12/src/fceu.c 2004-05-17 04:43:25.000000000 +0300 +++ fceu/src/fceu.c 2004-08-29 22:32:08.000000000 +0300 @@ -61,6 +61,7 @@ static readfunc *AReadG; static writefunc *BWriteG; static int RWWrap=0; +static int EmulationPaused=0; static DECLFW(BNull) { @@ -188,6 +189,7 @@ { if(FCEUnetplay) FCEUD_NetworkClose(); + FCEUI_StopMovie(); if(FCEUGameInfo->name) { free(FCEUGameInfo->name); @@ -319,6 +321,19 @@ { int r,ssize; + if(EmulationPaused&2) + EmulationPaused &= ~1; // clear paused flag temporarily + else if(EmulationPaused&1) + { + memcpy(XBuf, XBackBuf, 256*256); + FCEU_PutImage(); + *pXBuf=XBuf; + *SoundBuf=WaveFinal; + *SoundBufSize=0; + + return; + } + FCEU_UpdateInput(); if(geniestage!=1) FCEU_ApplyPeriodicCheats(); r=FCEUPPU_Loop(skip); @@ -331,6 +346,12 @@ *pXBuf=skip?0:XBuf; *SoundBuf=WaveFinal; *SoundBufSize=ssize; + + if(EmulationPaused&2) + { + EmulationPaused = 1; // restore paused flag + *SoundBufSize=0; // keep sound muted + } } void FCEUI_CloseGame(void) @@ -503,3 +524,19 @@ else return(1008307711); // ~60.1 } + +int FCEUI_EmulationPaused(void) +{ + return (EmulationPaused&1); +} + +void FCEUI_ToggleEmulationPause(void) +{ + EmulationPaused = (EmulationPaused&1)^1; +} + +void FCEUI_FrameAdvance(void) +{ + if(EmulationPaused&1) + EmulationPaused |= 2; +} diff -NaHudr fceu-0.98.12/src/movie.c fceu/src/movie.c --- fceu-0.98.12/src/movie.c 2004-08-21 07:12:34.000000000 +0300 +++ fceu/src/movie.c 2004-08-29 22:37:09.000000000 +0300 @@ -1,24 +1,66 @@ #include #include #include +#include #include "types.h" +#include "endian.h" +#include "palette.h" #include "input.h" #include "fceu.h" #include "driver.h" #include "state.h" #include "general.h" #include "video.h" +#include "movie.h" -static int current = 0; // > 0 for recording, < 0 for playback +#define MOVIE_MAGIC 0x1a4d4346 // FCM\x1a +#define MOVIE_VERSION 1 +#define MOVIE_HEADER_SIZE 32 + +/* +struct MovieHeader +{ + uint32 magic; + uint32 version; + uint8 flags[4]; + uint32 length_frames; + uint32 rerecord_count; + uint32 movie_data_size; + uint32 offset_to_savestate; + uint32 offset_to_movie_data; + uint16 metadata[]; // sizeof(metadata) = offset_to_savestate - MOVIE_HEADER_SIZE +} +*/ + +static int current = 0; // > 0 for recording, < 0 for playback static FILE *slots[10]={0}; static uint8 joop[4]; static uint32 framets; +static uint32 frameptr = 0; +static uint8* moviedata = NULL; +static uint32 moviedatasize = 0; +static uint32 firstframeoffset; +static uint32 framecount = 0; +static uint32 rerecord_count = 0; +static int readonly = 0; +static int frame_display = 0; +static uint32 last_frame_display; /* Cache variables used for playback. */ static uint32 nextts; -static int nextd; +static int32 nextd; +SFORMAT FCEUMOV_STATEINFO[]={ + { joop, 4,"JOOP"}, + { &framets, 4|FCEUSTATE_RLSB, "FTS "}, + { &nextts, 4|FCEUSTATE_RLSB, "NXTS"}, + { &nextd, 4|FCEUSTATE_RLSB, "NXTD"}, + { &frameptr, 4|FCEUSTATE_RLSB, "FPTR"}, + { &framecount, 4|FCEUSTATE_RLSB, "FCNT"}, + + { 0 } +}; static int CurrentMovie = 0; static int MovieShow = 0; @@ -33,25 +75,43 @@ else return(0); } -void FCEUI_SaveMovie(char *fname) +static void StopPlayback(void) +{ + fclose(slots[-1 - current]); + current=0; + FCEU_DispMessage("Movie playback stopped."); +} + +static void StopRecording(void) +{ + DoEncode(0,0,1); /* Write a dummy timestamp value so that the movie will keep + "playing" after user input has stopped. */ + // finish header + fseek(slots[current - 1], 12, SEEK_SET); + write32le(framecount, slots[current - 1]); + write32le(rerecord_count, slots[current - 1]); + write32le(frameptr, slots[current - 1]); + // FIXME: truncate movie to length + // ftruncate(); + fclose(slots[current - 1]); + MovieStatus[current - 1] = 1; + current=0; + FCEU_DispMessage("Movie recording stopped."); +} + +void FCEUI_StopMovie(void) +{ + if(current < 0) StopPlayback(); + if(current > 0) StopRecording(); +} + +void FCEUI_SaveMovie(char *fname, uint8 flags, const wchar_t* metadata) { FILE *fp; char *fn; + uint32 savestate_offset; - if(current < 0) /* Can't interrupt playback.*/ - return; - - if(current > 0) /* Stop saving. */ - { - DoEncode(0,0,1); /* Write a dummy timestamp value so that the movie will keep - "playing" after user input has stopped. - */ - fclose(slots[current-1]); - MovieStatus[current - 1] = 1; - current=0; - FCEU_DispMessage("Movie recording stopped."); - return; - } + FCEUI_StopMovie(); current=CurrentMovie; @@ -65,52 +125,127 @@ if(!fp) return; + // 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 + + // write metadata + if(metadata && metadata[0]) + { + uint8 _meta[MOVIE_MAX_METADATA<<1]; + int i; + for(i=0; metadata[i] && i<(MOVIE_MAX_METADATA); i++) + { + _meta[i+i] = metadata[i] & 0xff; + _meta[i+i+1] = (metadata[i]>>8) & 0xff; + } + fwrite(_meta, 1, i<<1, fp); + } + + savestate_offset = ftell(fp); FCEUSS_SaveFP(fp); fseek(fp, 0, SEEK_END); + firstframeoffset = ftell(fp); + + // finish header + fseek(fp, MOVIE_HEADER_SIZE - 8, SEEK_SET); + write32le(savestate_offset, fp); + write32le(firstframeoffset, fp); + fseek(fp, firstframeoffset, SEEK_SET); + + readonly = 0; + frameptr = 0; + framecount = 0; + rerecord_count = 0; slots[current] = fp; memset(joop,0,sizeof(joop)); current++; framets=0; - FCEUI_SelectMovie(CurrentMovie); /* Quick hack to display status. */ + nextd = -1; + // trigger a reset + if(flags & MOVIE_FLAG_FROM_RESET) + ResetNES(); + if(!fname) + FCEUI_SelectMovie(CurrentMovie); /* Quick hack to display status. */ + else + FCEU_DispMessage("Movie recording started."); } -static void StopPlayback(void) +void FCEUI_LoadMovie(char *fname, int _read_only) { - fclose(slots[-1 - current]); - current=0; - FCEU_DispMessage("Movie playback stopped."); -} + FILE *fp; + char *fn = NULL; -void FCEUMOV_Stop(void) -{ - if(current < 0) StopPlayback(); -} + FCEUI_StopMovie(); -void FCEUI_LoadMovie(char *fname) -{ - FILE *fp; - char *fn; + if(!fname) + fname = fn = FCEU_MakeFName(FCEUMKF_MOVIE,CurrentMovie,0); - if(current > 0) /* Can't interrupt recording.*/ - return; + // check readonly + readonly = _read_only; + if(access(fname, W_OK)) + readonly = 1; - if(current < 0) /* Stop playback. */ - { - StopPlayback(); - return; - } + fp = FCEUD_UTF8fopen(fname, readonly ? "rb" : "r+b"); - if(fname) - fp = FCEUD_UTF8fopen(fname, "rb"); - else + if(fn) { - fp=FCEUD_UTF8fopen(fn=FCEU_MakeFName(FCEUMKF_MOVIE,CurrentMovie,0),"rb"); free(fn); + fname = NULL; } + if(!fp) return; + // read header + { + uint32 magic; + uint32 version; + uint8 flags[4]; + uint32 savestate_offset; + + read32le(&magic, fp); + if(magic != MOVIE_MAGIC) + { + fclose(fp); + return; + } + + read32le(&version, fp); + if(version != MOVIE_VERSION) + { + 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; + } + } + if(!FCEUSS_LoadFP(fp)) return; + fseek(fp, firstframeoffset, SEEK_SET); + moviedata = (uint8*)realloc(moviedata, moviedatasize); + fread(moviedata, 1, moviedatasize, fp); + + frameptr = 0; current = CurrentMovie; slots[current] = fp; @@ -120,9 +255,31 @@ nextts=0; nextd = -1; MovieStatus[CurrentMovie] = 1; - FCEUI_SelectMovie(CurrentMovie); /* Quick hack to display status. */ + if(!fname) + FCEUI_SelectMovie(CurrentMovie); /* Quick hack to display status. */ + else + FCEU_DispMessage("Movie playback started."); } +static void movie_writechar(int c) +{ + if(frameptr == moviedatasize) + { + moviedatasize += 4096; + moviedata = (uint8*)realloc(moviedata, moviedatasize); + } + moviedata[frameptr++] = (uint8)(c & 0xff); + fputc(c, slots[current - 1]); +} + +static int movie_readchar() +{ + if(frameptr >= moviedatasize) + { + return -1; + } + return (int)(moviedata[frameptr++]); +} static void DoEncode(int joy, int button, int dummy) { @@ -142,11 +299,11 @@ d |= joy << 3; d |= button; - fputc(d, slots[current - 1]); + movie_writechar(d); //printf("Wr: %02x, %d\n",d,slots[current-1]); while(framets) { - fputc(framets & 0xff, slots[current - 1]); + movie_writechar(framets & 0xff); //printf("Wrts: %02x\n",framets & 0xff); framets >>= 8; } @@ -156,11 +313,11 @@ { int x,y; - if(!current) return; /* Not playback nor recording. */ + if(!current) return; /* Not playback nor recording. */ - if(current < 0) /* Playback */ + if(current < 0) /* Playback */ { - while(nextts == framets) + while(nextts == framets || nextd == -1) { int tmp,ti; uint8 d; @@ -169,7 +326,7 @@ { if(nextd&0x80) { - //puts("Egads"); + //puts("Egads"); FCEU_DoSimpleCommand(nextd&0x1F); } else @@ -177,7 +334,7 @@ } - tmp = fgetc(slots[-1 - current]); + tmp = movie_readchar(); d = tmp; if(tmp < 0) @@ -191,17 +348,8 @@ tmp &= 0x3; ti=0; - int tmpfix = tmp; - while(tmp--) { nextts |= fgetc(slots[-1 - current]) << (ti * 8); ti++; } - - // This fixes a bug in movies recorded before version 0.98.11 - // It's probably not necessary, but it may keep away CRAZY PEOPLE who recorded - // movies on <= 0.98.10 and don't work on playback. - if(tmpfix == 1 && !nextts) - {nextts |= fgetc(slots[-1 - current])<<8; } - else if(tmpfix == 2 && !nextts) {nextts |= fgetc(slots[-1 - current])<<16; } - - framets = 0; + while(tmp--) { nextts |= movie_readchar() << (ti * 8); ti++; } + if(nextd != -1) framets = 0; nextd = d; } memcpy(js,joop,4); @@ -217,15 +365,16 @@ DoEncode(x, y, 0); joop[x] = js[x]; } - else if(framets == ((1<<24)-1)) DoEncode(0,0,1); /* Overflow will happen, so do dummy update. */ + else if(framets == ((1<<24)-1)) DoEncode(0,0,1); /* Overflow will happen, so do dummy update. */ } } framets++; + framecount++; } void FCEUMOV_AddCommand(int cmd) { - if(current <= 0) return; /* Return if not recording a movie */ + if(current <= 0) return; /* Return if not recording a movie */ //printf("%d\n",cmd); DoEncode((cmd>>3)&0x3,cmd&0x7,1); } @@ -269,9 +418,163 @@ void FCEU_DrawMovies(uint8 *XBuf) { + if(frame_display && current != 0 && last_frame_display!=framecount) + { + FCEU_DispMessage("%s frame %u",(current<0) ? "Playing" : "Recording", framecount); + last_frame_display = framecount; + return; + } + if(!MovieShow) return; FCEU_DrawNumberRow(XBuf,MovieStatus, CurrentMovie); MovieShow--; } +int FCEUMOV_WriteState(FILE* st) +{ + uint32 to_write = 0; + if(current < 0) + to_write = moviedatasize; + else if(current > 0) + to_write = frameptr; + + if(!st) + return to_write; + + if(to_write) + fwrite(moviedata, 1, to_write, st); + return to_write; +} + +int FCEUMOV_ReadState(FILE* st, uint32 size) +{ + // if this savestate was made while replaying, + // we need to "undo" nextd and nextts + if(nextd != -1) + { + int d = 1; + if(nextts > 65536) + d = 4; + else if(nextts > 256) + d = 3; + else if(nextts > 0) + d = 2; + frameptr -= d; + nextd = -1; + } + + if(current > 0 || (!readonly && current < 0)) /* Recording or Playback (!read-only) */ + { + // copy movie data from savestate + moviedata = (uint8*)realloc(moviedata, size); + moviedatasize = size; + if(size && fread(moviedata, 1, size, st) moviedatasize) + frameptr = moviedatasize; + } + + return 1; +} + +int FCEUI_MovieGetInfo(const char* fname, uint32* num_frames, uint8* flags, uint32* rerecord_count, int* read_only, wchar_t* metadata) +{ + uint32 magic; + uint32 version; + uint8 _flags[4]; + uint32 savestateoffset; + uint8 tmp[MOVIE_MAX_METADATA<<1]; + int metadata_length; + + *num_frames = 0; + *flags = 0; + *rerecord_count = 0; + *read_only = 1; + metadata[0] = '\0'; + + FILE* fp = fopen(fname, "rb"); + if(!fp) + return 0; + + read32le(&magic, fp); + if(magic != MOVIE_MAGIC) + { + fclose(fp); + return 0; + } + + read32le(&version, fp); + if(version != MOVIE_VERSION) + { + fclose(fp); + return 0; + } + + fread(_flags, 1, 4, fp); + + *flags = _flags[0]; + read32le(num_frames, fp); + read32le(rerecord_count, fp); + + *read_only = 0; + if(access(fname, W_OK)) + *read_only = 1; + + fseek(fp, 4, SEEK_CUR); + read32le(&savestateoffset, fp); + + metadata_length = (int)savestateoffset - MOVIE_HEADER_SIZE; + if(metadata_length > 0) + { + int i; + + metadata_length >>= 1; + if(metadata_length >= MOVIE_MAX_METADATA) + metadata_length = MOVIE_MAX_METADATA-1; + + fseek(fp, MOVIE_HEADER_SIZE, SEEK_SET); + fread(tmp, 1, metadata_length<<1, fp); + + for(i=0; i