14 //#include "palette.h"
22 #define MOVIE_MAGIC 0x1a4d4346 // FCM\x1a
23 #define MOVIE_VERSION 2 // still at 2 since the format itself is still compatible - to detect which version the movie was made with, check the fceu version stored in the movie header (e.g against FCEU_VERSION_NUMERIC)
25 #define MOVIE_FLAG_NOSYNCHACK (1<<4) // set in newer version, used for old movie compatibility
28 #define read32le read32
32 int movie_version; // version of the movie format in the file
34 uint32 rerecord_count;
37 uint32 emu_version_used; // 9813 = 0.98.13
38 char* metadata; // caller-supplied buffer to store metadata. can be NULL.
39 int metadata_size; // size of the buffer pointed to by metadata
40 uint8 md5_of_rom_used[16];
41 int md5_of_rom_used_present; // v1 movies don't have md5 info available
42 char* name_of_rom_used; // caller-supplied buffer to store metadata. can be NULL.
43 int name_of_rom_used_size; // size of the buffer pointer to by name_of_rom_used
49 static void FCEUI_LoadMovie_v1(char *fname, int _read_only);
50 //static int FCEUI_MovieGetInfo_v1(const char* fname, MOVIE_INFO* info);
52 extern char FileBase[];
58 uint32 version=2; // +4
60 uint32 length_frames; // +12
61 uint32 rerecord_count; // +16
62 uint32 movie_data_size; // +20
63 uint32 offset_to_savestate; // +24, should be 4-byte-aligned
64 uint32 offset_to_movie_data; // +28, should be 4-byte-aligned
65 uint8 md5_of_rom_used[16]; // +32
66 uint32 version_of_emu_used // +48
67 char name_of_rom_used[] // +52, utf-8, null-terminated
68 char metadata[]; // utf-8, null-terminated
70 uint8 savestate[]; // always present, even in a "from reset" recording
71 uint8 padding[]; // used for byte-alignment
77 int current = 0; // > 0 for recording, < 0 for playback
78 static FILE *slots[10]={0};
80 static uint32 framets = 0;
81 static uint32 frameptr = 0;
82 static uint8* moviedata = NULL;
83 static uint32 moviedatasize = 0;
84 static uint32 firstframeoffset = 0;
85 static uint32 savestate_offset = 0;
86 /*static*/ uint32 framecount = 0;
87 static uint32 rerecord_count = 0;
88 /*static*/ int movie_readonly = 1;
89 int frame_display = 0;
90 //static uint32 last_frame_display = ~0;
91 int input_display = 0;
92 static uint32 cur_input_display = 0;
93 //static uint32 last_input_display = ~0;
97 /* Cache variables used for playback. */
98 static uint32 nextts = 0;
99 static int32 nextd = 0;
101 //#define FCEUSTATE_RLSB 0x80000000
103 SFORMAT FCEUMOV_STATEINFO[]={
105 { &framets, 4|FCEUSTATE_RLSB, "FTS "},
106 { &nextts, 4|FCEUSTATE_RLSB, "NXTS"},
107 { &nextd, 4|FCEUSTATE_RLSB, "NXTD"},
108 { &frameptr, 4|FCEUSTATE_RLSB, "FPTR"},
109 { &framecount, 4|FCEUSTATE_RLSB, "FCNT"},
114 static int CurrentMovie = 1;
115 //static int MovieShow = 0;
117 static int MovieStatus[10];
119 //static void DoEncode(int joy, int button, int);
121 int FCEUMOV_IsPlaying(void)
123 if(current < 0) return(1);
127 int FCEUMOV_IsRecording(void)
129 if(current > 0) return(1);
133 int suppressMovieStop=0;
134 int movieConvertOffset1=0, movieConvertOffset2=0,movieConvertOK=0,movieSyncHackOn=0;
136 static void StopPlayback(void)
138 if(suppressMovieStop)
140 resetDMCacc=movieSyncHackOn=0;
141 fclose(slots[-1 - current]);
143 FCEU_DispMessage("Movie playback stopped.");
147 static void FlushHeader(void)
150 return;// only write header data if recording
152 FILE* fp = slots[current - 1];
156 unsigned long loc = ftell(fp);
157 fseek(fp, 4, SEEK_SET);
158 write32le(MOVIE_VERSION, fp);
159 fseek(fp, 12, SEEK_SET);
160 write32le(framecount, fp);
161 write32le(rerecord_count, fp);
162 write32le(frameptr, fp);
163 fseek(fp, 32, SEEK_SET);
164 fwrite(FCEUGameInfo->MD5, 1, 16, fp); // write ROM checksum
165 write32le(FCEU_VERSION_NUMERIC, fp); // write emu version used
167 // write ROM name used
168 fseek(fp, 52, SEEK_SET);
172 int strdiff=strlen(FileBase)-strlen(str);
175 // resize the whole damn movie because the ROM name in the header is of variable length
177 fseek(fp, 52, SEEK_SET);
179 } while(fgetc(fp) && !feof(fp) && !ferror(fp));
181 if(feof(fp) || ferror(fp))
183 fseek(fp, loc, SEEK_SET);
187 fseek(fp, 0, SEEK_END);
188 uint32 fsize=ftell(fp)-off;
189 char* ctemp=(char*)FCEU_malloc(fsize*sizeof(char)+4);
192 fseek(fp, loc, SEEK_SET);
195 fseek(fp, off, SEEK_SET);
196 fread(ctemp, 1,fsize, fp);
197 fseek(fp, 52+strlen(FileBase)+1, SEEK_SET);
198 int wrote = fwrite(ctemp, fsize,1, fp);
202 fseek(fp, loc, SEEK_SET);
206 if(loc >= firstframeoffset)
208 savestate_offset += strdiff;
209 firstframeoffset += strdiff;
210 fseek(fp, 24, SEEK_SET);
211 write32le(savestate_offset, fp);
212 write32le(firstframeoffset, fp);
214 fseek(fp, 52, SEEK_SET);
218 fseek(fp, loc, SEEK_SET);
221 static void StopRecording(void)
223 if(suppressMovieStop)
225 resetDMCacc=movieSyncHackOn=0;
226 DoEncode(0,0,1); /* Write a dummy timestamp value so that the movie will keep
227 "playing" after user input has stopped. */
231 // FIXME: truncate movie to length
233 fclose(slots[current - 1]);
234 MovieStatus[current - 1] = 1;
236 FCEU_DispMessage("Movie recording stopped.");
240 void FCEUI_StopMovie(void)
242 if(current < 0) StopPlayback();
244 if(current > 0) StopRecording();
250 void executeCommand(const char* cmd)
256 argv[0] = getenv("COMSPEC");
260 if(*argv && *(*argv))
261 _spawnve(_P_WAIT, argv[0], argv, NULL);
265 int justAutoConverted=0;
266 static const char* convertToFCM(const char *fname, char *buffer)
271 // convert to fcm if not already
272 const char* dot = strrchr(fname, '.');
275 int fmv = !stricmp(dot, ".fmv");
276 int nmv = !stricmp(dot, ".nmv");
277 int vmv = !stricmp(dot, ".vmv");
278 if(fmv || nmv || vmv)
280 strcpy(buffer, fname);
281 buffer[dot-fname]='\0';
282 strcat(buffer,"-autoconverted.fcm");
292 extern char lastLoadedGameName [2048];
293 char cmd [1024], offset[64], romInfo[1024];
295 sprintf(romInfo, "-smd5=\"%s\" -sromname=\"%s (MAYBE)\" -s", lastLoadedGameName, FileBase);
297 sprintf(romInfo, "-sromname=\"(unknown)\" -s");
298 if(movieConvertOffset2) sprintf(offset, "-o %d:%d", movieConvertOffset2,movieConvertOffset1);
299 else sprintf(offset, "-o %d", movieConvertOffset1);
300 sprintf(cmd, ".\\util\\nesmock\\nesmock.exe %s %s -spal=%c -sfceuver=%d \"%s\" \"%s\" ", offset, romInfo, FCEUI_GetCurrentVidSystem(0,0)?'1':'0', fceuver, fname, buffer);
301 // FCEU_PrintError(cmd);
304 FILE* file = FCEUD_UTF8fopen(buffer,"rb");
307 fseek(file, 12, SEEK_SET);
309 read32le(&frames, file);
317 static int errAlready=0;
321 FCEU_PrintError("For some reason, nesmock was unable to create a valid FCM from the given file.\nThe command given was:\n%s\nPerhaps the file specified is not a movie file or contains no input data,\nor perhaps it is a movie file of a version unsupported by nesmock.\n\n(This error message will self-destruct until you restart FCEU.)", cmd);
330 GetCurrentDirectory(512,str);
331 strcat(str, "\\util\\nesmock\\nesmock.exe");
332 file = FCEUD_UTF8fopen(str, "rb");
335 static int errAlready=0;
339 FCEU_PrintError("For some reason, nesmock was unable to convert the movie to FCM format.\nThe command given was:\n%s\n\n(This error message will self-destruct until you restart FCEU.)", cmd);
345 static int errAlready=0;
349 FCEU_PrintError("Nesmock not found, so the movie could not be converted to FCM format.\nYou must place nesmock.exe at this location so FCEU can find it:\n%s\n\n(This error message will self-destruct until you restart FCEU.)", str);
359 static void ResetInputTypes()
362 extern int UsrInputType[3];
363 UsrInputType[0] = SI_GAMEPAD;
364 UsrInputType[1] = SI_GAMEPAD;
365 UsrInputType[2] = SIFC_NONE;
367 ParseGIInput(NULL/*FCEUGameInfo*/);
368 extern int cspec, gametype;
369 cspec=FCEUGameInfo->cspecial;
370 gametype=FCEUGameInfo->type;
376 char curMovieFilename[512];
379 // PlayMovie / MoviePlay function
380 void FCEUI_LoadMovie(char *fname, int _read_only)
383 fname = (char*)convertToFCM(fname,buffer);
392 fname = fn = FCEU_MakeFName(FCEUMKF_MOVIE,CurrentMovie,0);
397 strcpy(origname,fname);
400 // check movie_readonly
401 movie_readonly = _read_only;
402 if(access(fname, W_OK))
405 fp = fopen(fname, (movie_readonly>=2) ? "rb" : "r+b");
421 read32le(&magic, fp);
422 if(magic != MOVIE_MAGIC)
427 //DEBUG_COMPARE_RAM(__LINE__);
429 read32le(&version, fp);
432 // attempt to load previous version's format
434 printf("movie: trying movie v1\n");
435 FCEUI_LoadMovie_v1(fname, _read_only);
438 else if(version == MOVIE_VERSION)
442 // unsupported version
447 fread(flags, 1, 4, fp);
448 read32le(&framecount, fp);
449 read32le(&rerecord_count, fp);
450 read32le(&moviedatasize, fp);
451 read32le(&savestate_offset, fp);
452 read32le(&firstframeoffset, fp);
453 if(fseek(fp, savestate_offset, SEEK_SET))
459 // FCEU_PrintError("flags[0] & MOVIE_FLAG_NOSYNCHACK=%d",flags[0] & MOVIE_FLAG_NOSYNCHACK);
460 if(flags[0] & MOVIE_FLAG_NOSYNCHACK)
466 // fully reload the game to reinitialize everything before playing any movie
467 // to try fixing nondeterministic playback of some games
468 #if 0 // do we need this?
470 extern char lastLoadedGameName [2048];
472 extern int disableBatteryLoading, suppressAddPowerCommand;
473 suppressAddPowerCommand=1;
477 FCEUGI * gi = FCEUI_LoadGame(lastLoadedGameName);
483 suppressAddPowerCommand=0;
488 if(!FCEUSS_LoadFP(fp,1)) return;
492 fseek(fp, firstframeoffset, SEEK_SET);
493 moviedata = (uint8*)realloc(moviedata, moviedatasize);
494 fread(moviedata, 1, moviedatasize, fp);
496 framecount = 0; // movies start at frame 0!
498 current = CurrentMovie;
501 memset(joop,0,sizeof(joop));
502 current = -1 - current;
507 MovieStatus[CurrentMovie] = 1;
510 FCEUI_SelectMovie(CurrentMovie,1); /* Quick hack to display status. */
513 FCEU_DispMessage("Movie playback started.");
516 strcpy(curMovieFilename, origname);
518 strcpy(curMovieFilename, fname);
523 void FCEUI_SaveMovie(char *fname, uint8 flags, const char* metadata)
528 uint8 padding[4] = {0,0,0,0};
536 fp = FCEUD_UTF8fopen(fname, "wb");
537 strcpy(origname,fname);
541 fp=FCEUD_UTF8fopen(fn=FCEU_MakeFName(FCEUMKF_MOVIE,CurrentMovie,0),"wb");
548 // don't need the movieSyncHackOn sync hack for newly recorded movies
549 flags |= MOVIE_FLAG_NOSYNCHACK;
550 resetDMCacc=movieSyncHackOn=0;
553 if(FCEUI_GetCurrentVidSystem(0,0))
554 flags |= MOVIE_FLAG_PAL;
556 if(flags & MOVIE_FLAG_FROM_POWERON)
559 flags &= ~MOVIE_FLAG_FROM_POWERON;
560 flags |= MOVIE_FLAG_FROM_RESET;
564 write32le(MOVIE_MAGIC, fp);
565 write32le(MOVIE_VERSION, fp);
567 fputc(0, fp); // reserved
568 fputc(0, fp); // reserved
569 fputc(0, fp); // reserved
570 write32le(0, fp); // leave room for length frames
571 write32le(0, fp); // leave room for rerecord count
572 write32le(0, fp); // leave room for movie data size
573 write32le(0, fp); // leave room for savestate_offset
574 write32le(0, fp); // leave room for offset_to_controller_data
575 fwrite(FCEUGameInfo->MD5, 1, 16, fp); // write ROM checksum
576 write32le(FCEU_VERSION_NUMERIC, fp); // write emu version used
577 fputs(FileBase, fp); // write ROM name used
581 if(strlen(metadata) < MOVIE_MAX_METADATA)
584 fwrite(metadata, 1, MOVIE_MAX_METADATA-1, fp);
589 n_padding = (4 - (ftell(fp) & 0x3)) & 0x3;
590 fwrite(padding, 1, n_padding, fp);
592 if(flags & MOVIE_FLAG_FROM_RESET)
596 // make a for-movie-recording power-on clear the game's save data, too
597 // (note: FCEU makes a save state immediately after this and that loads that on movie playback)
598 extern char lastLoadedGameName [2048];
599 extern int disableBatteryLoading, suppressAddPowerCommand;
600 suppressAddPowerCommand=1;
601 disableBatteryLoading=1;
604 // NOTE: this will NOT write an FCEUNPCMD_POWER into the movie file
605 FCEUGI * gi = FCEUI_LoadGame(lastLoadedGameName);
607 PowerNES(); // and neither will this, if it can even happen
610 disableBatteryLoading=0;
611 suppressAddPowerCommand=0;
615 savestate_offset = ftell(fp);
617 fseek(fp, 0, SEEK_END);
622 n_padding = (4 - (ftell(fp) & 0x3)) & 0x3;
623 fwrite(padding, 1, n_padding, fp);
625 firstframeoffset = ftell(fp);
628 fseek(fp, 24, SEEK_SET); // offset_to_savestate offset
629 write32le(savestate_offset, fp);
630 write32le(firstframeoffset, fp);
632 fseek(fp, firstframeoffset, SEEK_SET);
634 // set recording flag
635 current=CurrentMovie;
642 memset(joop,0,sizeof(joop));
648 if(flags & MOVIE_FLAG_FROM_RESET)
652 PowerNES(); // NOTE: this will write an FCEUNPCMD_POWER into the movie file
655 ResetNES(); // NOTE: this will write an FCEUNPCMD_RESET into the movie file
658 FCEUI_SelectMovie(CurrentMovie,1); /* Quick hack to display status. */
660 FCEU_DispMessage("Movie recording started.");
662 strcpy(curMovieFilename, origname);
665 static void movie_writechar(int c)
667 if(frameptr == moviedatasize)
669 moviedatasize += 4096;
670 moviedata = (uint8*)realloc(moviedata, moviedatasize);
672 moviedata[frameptr++] = (uint8)(c & 0xff);
673 fputc(c, slots[current - 1]);
677 static int movie_readchar()
679 if(frameptr >= moviedatasize)
683 return (int)(moviedata[frameptr++]);
687 static void DoEncode(int joy, int button, int dummy)
695 else if(framets >= 256)
706 //printf("Wr: %02x, %d\n",d,slots[current-1]);
709 movie_writechar(framets & 0xff);
710 //printf("Wrts: %02x\n",framets & 0xff);
716 // TODO: make this function legible! (what are all these magic numbers and weirdly named variables and crazy unexplained loops?)
717 void FCEUMOV_AddJoy(uint8 *js)
721 if(!current) return; // Not playback nor recording.
723 if(current < 0) // Playback
725 while(nextts == framets || nextd == -1)
735 FCEU_DoSimpleCommand(nextd&0x1F);
738 joop[(nextd >> 3)&0x3] ^= 1 << (nextd&0x7);
742 tmp = movie_readchar();
748 memcpy(&cur_input_display,js,4);
758 while(tmp--) { nextts |= movie_readchar() << (ti * 8); ti++; }
760 // This fixes a bug in movies recorded before version 0.98.11
761 // It's probably not necessary, but it may keep away CRAZY PEOPLE who recorded
762 // movies on <= 0.98.10 and don't work on playback.
763 if(tmpfix == 1 && !nextts)
764 {nextts |= movie_readchar()<<8; }
765 else if(tmpfix == 2 && !nextts) {nextts |= movie_readchar()<<16; }
775 else if(current > 0) // Recording
777 // flush header info every 300 frames in case of crash
779 static int fcounter=0;
790 if((js[x] ^ joop[x]) & (1 << y))
794 else if(framets == ((1<<24)-1)) DoEncode(0,0,1); // Overflow will happen, so do dummy update.
805 memcpy(&cur_input_display,js,4);
809 void FCEUMOV_AddCommand(int cmd)
811 if(current <= 0) return; // Return if not recording a movie
812 //printf("%d\n",cmd);
813 DoEncode((cmd>>3)&0x3,cmd&0x7,1);
818 void FCEUMOV_CheckMovies(void)
824 for(ssel=0;ssel<10;ssel++)
826 st=FCEUD_UTF8fopen(fn=FCEU_MakeFName(FCEUMKF_MOVIE,ssel,0),"rb");
839 void FCEUI_SelectMovieNext(int n)
842 CurrentMovie=(CurrentMovie+1)%10;
844 CurrentMovie=(CurrentMovie+9)%10;
845 FCEUI_SelectMovie(CurrentMovie, 1);
849 int FCEUI_SelectMovie(int w, int show)
851 int oldslot=CurrentMovie;
852 if(w == -1) { MovieShow = 0; return; }
853 FCEUI_SelectState(-1,0);
862 FCEU_DispMessage("-recording movie %d-",current-1);
863 else if (current < 0)
864 FCEU_DispMessage("-playing movie %d-",-1 - current);
866 FCEU_DispMessage("-select movie-");
872 void FCEU_DrawMovies(uint8 *XBuf)
874 int frameDisplayOn = current != 0 && frame_display;
877 extern int32 fps_scale;
881 int howl=(180-(FCEUI_EmulationPaused()?(60):(20*fps_scale/256)));
882 if(howl>176) howl=180;
884 if((howlong<howl || movcounter)
885 && (frameDisplayOn && (!movcounter || last_frame_display!=framecount) || input_display && (!movcounter || last_input_display!=cur_input_display)))
890 uint32 c = cur_input_display;
891 sprintf(inputstr, "%c%c%c%c%c%c%c%c %c%c%c%c%c%c%c%c",
892 (c&0x40)?'<':' ', (c&0x10)?'^':' ', (c&0x80)?'>':' ', (c&0x20)?'v':' ',
893 (c&0x01)?'A':' ', (c&0x02)?'B':' ', (c&0x08)?'S':' ', (c&0x04)?'s':' ',
894 (c&0x4000)?'<':' ', (c&0x1000)?'^':' ', (c&0x8000)?'>':' ', (c&0x2000)?'v':' ',
895 (c&0x0100)?'A':' ', (c&0x0200)?'B':' ', (c&0x0800)?'S':' ', (c&0x0400)?'s':' ');
899 if(frameDisplayOn && !input_display)
900 FCEU_DispMessage("%s frame %u",current >= 0?"Recording":"Playing",framecount);
901 else if(input_display && !frameDisplayOn)
902 FCEU_DispMessage("Input: %s",inputstr);
903 else //if(input_display && frame_display)
904 FCEU_DispMessage("%s %u %s",current >= 0?"Recording":"Playing",framecount,inputstr);
906 last_frame_display = framecount;
907 last_input_display = cur_input_display;
912 if(movcounter) movcounter--;
914 if(!MovieShow) return;
916 FCEU_DrawNumberRow(XBuf,MovieStatus, CurrentMovie);
920 int FCEUMOV_WriteState(FILE* st)
924 to_write = moviedatasize;
932 fwrite(moviedata, 1, to_write, st);
936 static int load_successful;
938 int FCEUMOV_ReadState(FILE* st, uint32 size)
940 // if this savestate was made while replaying,
941 // we need to "undo" nextd and nextts
947 else if(nextts > 256)
955 // if(current > 0 || (!movie_readonly && current < 0)) /* Recording or Playback (!read-only) */
956 if(current!=0 && !movie_readonly)
958 // copy movie data from savestate
959 moviedata = (uint8*)realloc(moviedata, size);
960 moviedatasize = size;
961 if(size && fread(moviedata, 1, size, st)<size)
963 if(current < 0) // switch to recording
965 fseek(slots[current - 1], firstframeoffset, SEEK_SET);
966 fwrite(moviedata, 1, frameptr, slots[current - 1]);
969 // else if(current < 0) /* Playback (read-only) */
970 else if(current!=0 && movie_readonly)
972 if(current > 0) // switch to playback
974 // allow frameptr to be updated but keep movie data
975 fseek(st, size, SEEK_CUR);
976 // prevent seeking beyond the end of the movie
977 if(frameptr > moviedatasize)
978 frameptr = moviedatasize;
980 else /* Neither recording or replaying */
983 fseek(st, size, SEEK_CUR);
990 void FCEUMOV_PreLoad(void)
995 int FCEUMOV_PostLoad(void)
997 if(!FCEUI_IsMovieActive())
1000 return load_successful;
1003 char* FCEUI_MovieGetCurrentName(int addSlotNumber)
1005 return FCEU_MakeFName(FCEUMKF_MOVIE,(addSlotNumber ? CurrentMovie : -1),0);
1009 int FCEUI_IsMovieActive(void)
1015 void FCEUI_MovieToggleFrameDisplay(void)
1017 frame_display=!frame_display;
1018 if(!(current != 0 && frame_display) && !input_display)
1019 FCEU_ResetMessages();
1022 last_frame_display = ~framecount;
1023 last_input_display = ~cur_input_display;
1027 void FCEUI_ToggleInputDisplay(void)
1029 input_display=!input_display;
1030 if(!input_display && !(current != 0 && frame_display))
1031 FCEU_ResetMessages();
1034 last_frame_display = ~framecount;
1035 last_input_display = ~cur_input_display;
1039 void FCEUI_MovieToggleReadOnly(void)
1041 if(movie_readonly < 2)
1043 movie_readonly = !movie_readonly;
1045 FCEU_DispMessage("Movie is now Read-Only.");
1047 FCEU_DispMessage("Movie is now Read+Write.");
1051 FCEU_DispMessage("Movie file is Read-Only.");
1055 char lastMovieInfoFilename [512] = {'\0',};
1057 int FCEUI_MovieGetInfo(const char* fname, MOVIE_INFO* info)
1062 fname = (const char*)convertToFCM(fname,buffer);
1063 strncpy(lastMovieInfoFilename, fname, 512);
1065 // main get info part of function
1071 FILE* fp = FCEUD_UTF8fopen(fname, "rb");
1075 read32le(&magic, fp);
1076 if(magic != MOVIE_MAGIC)
1082 read32le(&version, fp);
1083 if(version != MOVIE_VERSION)
1087 return FCEUI_MovieGetInfo_v1(fname, info);
1092 info->movie_version = MOVIE_VERSION;
1094 fread(_flags, 1, 4, fp);
1096 info->flags = _flags[0];
1097 read32le(&info->num_frames, fp);
1098 read32le(&info->rerecord_count, fp);
1100 if(access(fname, W_OK))
1101 info->read_only = 1;
1103 info->read_only = 0;
1105 fseek(fp, 12, SEEK_CUR); // skip movie_data_size, offset_to_savestate, and offset_to_movie_data
1107 fread(info->md5_of_rom_used, 1, 16, fp);
1108 info->md5_of_rom_used_present = 1;
1110 read32le(&info->emu_version_used, fp);
1112 // I probably could have planned this better...
1120 if(info->name_of_rom_used && info->name_of_rom_used_size)
1121 info->name_of_rom_used[0]='\0';
1123 r=fread(str, 1, 256, fp);
1126 for(p=0; p<r && last_c != '\0'; ++p)
1128 if(info->name_of_rom_used && info->name_of_rom_used_size && (p2 < info->name_of_rom_used_size-1))
1130 info->name_of_rom_used[p2]=str[p];
1137 memmove(str, str+p, r-p);
1141 r=fread(str, 1, 256, fp);
1147 if(info->metadata && info->metadata_size)
1148 info->metadata[0]='\0';
1152 for(p=0; p<r && last_c != '\0'; ++p)
1154 if(info->metadata && info->metadata_size && (p2 < info->metadata_size-1))
1156 info->metadata[p2]=str[p];
1163 r=fread(str, 1, 256, fp);
1168 // somehow failed to read romname and metadata
1174 // check what hacks are necessary
1175 fseek(fp, 24, SEEK_SET); // offset_to_savestate offset
1176 uint32 temp_savestate_offset;
1177 read32le(&temp_savestate_offset, fp);
1178 if(fseek(fp, temp_savestate_offset, SEEK_SET))
1183 if(!FCEUSS_LoadFP(fp,2)) return 0; // 2 -> don't really load, just load to find what's there then load backup
1196 struct MovieHeader_v1
1201 uint32 length_frames;
1202 uint32 rerecord_count;
1203 uint32 movie_data_size;
1204 uint32 offset_to_savestate;
1205 uint32 offset_to_movie_data;
1206 uint16 metadata_ucs2[]; // ucs-2, ick! sizeof(metadata) = offset_to_savestate - MOVIE_HEADER_SIZE
1210 #define MOVIE_V1_HEADER_SIZE 32
1212 static void FCEUI_LoadMovie_v1(char *fname, int _read_only)
1221 fname = fn = FCEU_MakeFName(FCEUMKF_MOVIE,CurrentMovie,0);
1224 // check movie_readonly
1225 movie_readonly = _read_only;
1226 if(access(fname, W_OK))
1230 fp = FCEUD_UTF8fopen(fname, (movie_readonly>=2) ? "rb" : "r+b");
1232 fp = fopen(fname, (movie_readonly>=2) ? "rb" : "r+b");
1250 read32le(&magic, fp);
1251 if(magic != MOVIE_MAGIC)
1257 read32le(&version, fp);
1264 fread(flags, 1, 4, fp);
1266 read32le(&rerecord_count, fp);
1267 read32le(&moviedatasize, fp);
1268 read32le(&savestate_offset, fp);
1269 read32le(&firstframeoffset, fp);
1270 if(fseek(fp, savestate_offset, SEEK_SET))
1276 if(flags[0] & MOVIE_FLAG_NOSYNCHACK)
1282 // fully reload the game to reinitialize everything before playing any movie
1283 // to try fixing nondeterministic playback of some games
1285 extern char lastLoadedGameName [2048];
1287 extern int disableBatteryLoading, suppressAddPowerCommand;
1288 suppressAddPowerCommand=1;
1289 suppressMovieStop=1;
1292 FCEUGI * gi = FCEUI_LoadGame(lastLoadedGameName);
1297 suppressMovieStop=0;
1298 suppressAddPowerCommand=0;
1302 if(!FCEUSS_LoadFP(fp,1)) return;
1306 fseek(fp, firstframeoffset, SEEK_SET);
1307 moviedata = (uint8*)realloc(moviedata, moviedatasize);
1308 fread(moviedata, 1, moviedatasize, fp);
1310 framecount = 0; // movies start at frame 0!
1312 current = CurrentMovie;
1313 slots[current] = fp;
1315 memset(joop,0,sizeof(joop));
1316 current = -1 - current;
1320 MovieStatus[CurrentMovie] = 1;
1323 FCEUI_SelectMovie(CurrentMovie,1); /* Quick hack to display status. */
1326 FCEU_DispMessage("Movie playback started.");
1330 static int FCEUI_MovieGetInfo_v1(const char* fname, MOVIE_INFO* info)
1335 uint32 savestateoffset;
1336 uint8 tmp[MOVIE_MAX_METADATA<<1];
1337 int metadata_length;
1339 FILE* fp = FCEUD_UTF8fopen(fname, "rb");
1343 read32le(&magic, fp);
1344 if(magic != MOVIE_MAGIC)
1350 read32le(&version, fp);
1357 info->movie_version = 1;
1358 info->emu_version_used = 0; // unknown
1360 fread(_flags, 1, 4, fp);
1362 info->flags = _flags[0];
1363 read32le(&info->num_frames, fp);
1364 read32le(&info->rerecord_count, fp);
1366 if(access(fname, W_OK))
1367 info->read_only = 1;
1369 info->read_only = 0;
1371 fseek(fp, 4, SEEK_CUR);
1372 read32le(&savestateoffset, fp);
1374 metadata_length = (int)savestateoffset - MOVIE_V1_HEADER_SIZE;
1375 if(metadata_length > 0)
1379 metadata_length >>= 1;
1380 if(metadata_length >= MOVIE_MAX_METADATA)
1381 metadata_length = MOVIE_MAX_METADATA-1;
1383 fseek(fp, MOVIE_V1_HEADER_SIZE, SEEK_SET);
1384 fread(tmp, 1, metadata_length<<1, fp);
1387 // turn old ucs2 metadata into utf8
1388 if(info->metadata && info->metadata_size)
1390 char* ptr=info->metadata;
1391 char* ptr_end=info->metadata+info->metadata_size-1;
1393 while(ptr<ptr_end && c_ptr<metadata_length)
1395 uint16 c=(tmp[c_ptr<<1] | (tmp[(c_ptr<<1)+1] << 8));
1399 *ptr++ = (char)(c&0x7f);
1401 case 0x80 ... 0x7ff:
1406 *ptr++=(0xc0 | (c>>6));
1407 *ptr++=(0x80 | (c & 0x3f));
1410 case 0x800 ... 0xffff:
1415 *ptr++=(0xe0 | (c>>12));
1416 *ptr++=(0x80 | ((c>>6) & 0x3f));
1417 *ptr++=(0x80 | (c & 0x3f));
1426 // md5 info not available from v1
1427 info->md5_of_rom_used_present = 0;
1428 // rom name used for the movie not available from v1
1429 if(info->name_of_rom_used && info->name_of_rom_used_size)
1430 info->name_of_rom_used[0] = '\0';
1432 // check what hacks are necessary
1433 fseek(fp, 24, SEEK_SET); // offset_to_savestate offset
1434 uint32 temp_savestate_offset;
1435 read32le(&temp_savestate_offset, fp);
1436 if(fseek(fp, temp_savestate_offset, SEEK_SET))
1441 if(!FCEUSS_LoadFP(fp,2)) return 0; // 2 -> don't really load, just load to find what's there then load backup