broken movie support
authornotaz <notasas@gmail.com>
Sat, 14 Apr 2007 23:15:32 +0000 (23:15 +0000)
committernotaz <notasas@gmail.com>
Sat, 14 Apr 2007 23:15:32 +0000 (23:15 +0000)
git-svn-id: file:///home/notaz/opt/svn/fceu@104 be3aeb3a-fb24-0410-a615-afba39da0efa

Makefile.gp2x
fce.c
input.c
movie.c [new file with mode: 0644]
movie.h [new file with mode: 0644]
state.c
state.h

index 5071037..d9868b3 100644 (file)
@@ -27,7 +27,7 @@ include zlib/Makefile
 
 OBJDRIVER       = ${B}minimal.o ${B}squidgehack.o ${B}asmutils.o ${B}gp2x.o ${B}main.o ${B}throttle.o \
                ${B}unix-netplay.o ${B}gp2x-sound.o ${B}gp2x-video.o ${B}lnx-joystick.o \
-               drivers/common/cheat.o drivers/common/config.o drivers/common/args.o drivers/common/vidblit.o ${UNZIPOBJS} ppu.o
+               drivers/common/cheat.o drivers/common/config.o drivers/common/args.o drivers/common/vidblit.o ${UNZIPOBJS} ppu.o movie.o
 LDRIVER                += -L /mnt/sd/lib  -L/mnt/sd/gp2x/usr/lib -lm -lz -static -Wl,-Map=fceu.map
 
 ifeq ($(asm_6502),1)
diff --git a/fce.c b/fce.c
index 64bb8dc..bec2511 100644 (file)
--- a/fce.c
+++ b/fce.c
@@ -48,6 +48,8 @@
 #include       "crc32.h"
 #include        "ppu.h"
 
+#include        "movie.h"
+
 #define Pal     (PALRAM)
 
 
@@ -1026,28 +1028,54 @@ void ResetGameLoaded(void)
        FCEUGameInfo.inputfc=-1;
 }
 
+char lastLoadedGameName [2048];
+
 FCEUGI *FCEUI_LoadGame(char *name)
 {
+       char name2[512];
+       int have_movie = 0;
         int fp;
 
         Exit=1;
         ResetGameLoaded();
 
-       fp=FCEU_fopen(name,"rb");
+       strncpy(name2, name, sizeof(name2));
+       name2[sizeof(name2)-1] = 0;
+
+       fp=FCEU_fopen(name2,"rb");
        if(!fp)
         {
         FCEU_PrintError("Error opening \"%s\"!",name);
         return 0;
        }
 
-        GetFileBase(name);
-        if(iNESLoad(name,fp))
+        {
+        char *p = name2 + strlen(name2) - 4;
+        if (strcmp(p, ".fcm") == 0)
+        {
+         // movie detected
+         printf("movie detected\n");
+         FCEU_fclose(fp);
+         *p = 0;
+         fp=FCEU_fopen(name2,"rb");
+         if (!fp) {
+          printf("no ROM for movie\n");
+          return 0;
+         }
+         have_movie = 1;
+        }
+       }
+
+       strcpy(lastLoadedGameName, name2);
+
+        GetFileBase(name2);
+        if(iNESLoad(name2,fp))
          goto endlseq;
         if(NSFLoad(fp))
          goto endlseq;
-        if(FDSLoad(name,fp))
+        if(FDSLoad(name2,fp))
          goto endlseq;
-        if(UNIFLoad(name,fp))
+        if(UNIFLoad(name2,fp))
          goto endlseq;
 
         FCEU_PrintError("An error occurred while loading the file.");
@@ -1076,6 +1104,9 @@ FCEUGI *FCEUI_LoadGame(char *name)
 
        FCEU_ResetPalette();
         Exit=0;
+
+       if (have_movie)
+               FCEUI_LoadMovie(name, 1);
         return(&FCEUGameInfo);
 }
 
@@ -1336,8 +1367,6 @@ void PowerNES(void)
 
         GeniePower();
 
-printf("X.DB offs: %02x\n", (int)&X.DB - (int)&X);
-
         memset(RAM,0x00,0x800);
         ResetMapping();
        GameInterface(GI_POWER);
diff --git a/input.c b/input.c
index 5bf8e96..40c0ff8 100644 (file)
--- a/input.c
+++ b/input.c
@@ -1,7 +1,7 @@
 /* FCE Ultra - NES/Famicom Emulator
  *
  * Copyright notice for this file:
- *  Copyright (C) 1998 BERO 
+ *  Copyright (C) 1998 BERO
  *  Copyright (C) 2002 Ben Parnell
  *
  * This program is free software; you can redistribute it and/or modify
@@ -28,6 +28,7 @@
 #include "svga.h"
 
 #include "input.h"
+#include "movie.h"
 
 extern INPUTC *FCEU_InitZapper(int w);
 extern INPUTC *FCEU_InitPowerpad(int w);
@@ -37,7 +38,7 @@ extern INPUTCFC *FCEU_InitArkanoidFC(void);
 extern INPUTCFC *FCEU_InitSpaceShadow(void);
 extern INPUTCFC *FCEU_InitFKB(void);
 static uint8 joy_readbit[2];
-static uint16 joy[2]={0,0};
+static uint16 joy[4]={0,0,0,0};
 
 extern int coinon;
 
@@ -73,7 +74,7 @@ static uint8 FP_FASTAPASS(1) ReadGPVS(int w)
 static uint8 FP_FASTAPASS(1) ReadGP(int w)
 {
                 uint8 ret;
-                //if(JoyMulti) 
+                //if(JoyMulti)
                //{
                  //ret = ((joy[w]>>(joy_readbit[w]))&1)|
                  //(((joy[w]>>(joy_readbit[w]+8))&1)<<1);
@@ -98,7 +99,7 @@ static DECLFR(JPRead)
 
        if(JPorts[A&1]->Read)
         ret|=JPorts[A&1]->Read(A&1);
-       
+
        if(FCExp)
         if(FCExp->Read)
          ret=FCExp->Read(A&1,ret);
@@ -188,6 +189,7 @@ void UpdateInput(void)
        #ifdef NETWORK
        if(netplay) NetplayUpdate(&joy[0],&joy[1]);
        #endif
+       if (current < 0) FCEUMOV_AddJoy(joy);
        FlushCommandQueue();
 }
 
@@ -221,7 +223,7 @@ static void SLHLHook(uint8 *buf, int line)
  for(x=0;x<2;x++)
   if(JPorts[x]->SLHook)
    JPorts[x]->SLHook(x,buf,line);
- if(FCExp) 
+ if(FCExp)
   if(FCExp->SLHook)
    FCExp->SLHook(buf,line);
 }
@@ -290,7 +292,7 @@ static void SetInputStuffFC(void)
 // rewrite code to make this more sane?
 
 void InitializeInput(void)
-{ 
+{
        memset(joy_readbit,0,sizeof(joy_readbit));
        memset(joy,0,sizeof(joy));
 
diff --git a/movie.c b/movie.c
new file mode 100644 (file)
index 0000000..d3fc1ef
--- /dev/null
+++ b/movie.c
@@ -0,0 +1,1451 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#ifdef WIN32
+#include <windows.h>
+#endif
+
+#include "types.h"
+#include "endian.h"
+#include "input.h"
+#include "fce.h"
+#include "svga.h"
+//#include "palette.h"
+#include "driver.h"
+#include "state.h"
+#include "general.h"
+#include "video.h"
+#include "file.h"
+#include "movie.h"
+
+#define MOVIE_MAGIC             0x1a4d4346      // FCM\x1a
+#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)
+
+#define MOVIE_FLAG_NOSYNCHACK          (1<<4) // set in newer version, used for old movie compatibility
+
+
+#define read32le read32
+
+typedef struct
+{
+ int    movie_version;                                 // version of the movie format in the file
+ uint32 num_frames;
+ uint32 rerecord_count;
+ uint8  flags;
+ int    read_only;
+ uint32 emu_version_used;                              // 9813 = 0.98.13
+ char*  metadata;                                              // caller-supplied buffer to store metadata.  can be NULL.
+ int    metadata_size;                                 // size of the buffer pointed to by metadata
+ uint8  md5_of_rom_used[16];
+ int    md5_of_rom_used_present;               // v1 movies don't have md5 info available
+ char*  name_of_rom_used;                              // caller-supplied buffer to store metadata.  can be NULL.
+ int    name_of_rom_used_size;                 // size of the buffer pointer to by name_of_rom_used
+} MOVIE_INFO;
+
+
+
+// backwards compat
+static void FCEUI_LoadMovie_v1(char *fname, int _read_only);
+static int FCEUI_MovieGetInfo_v1(const char* fname, MOVIE_INFO* info);
+
+extern char FileBase[];
+
+/*
+struct MovieHeader
+{
+ uint32 magic;                                         // +0
+ uint32 version=2;                                     // +4
+ uint8 flags[4];                                       // +8
+ uint32 length_frames;                         // +12
+ uint32 rerecord_count;                                // +16
+ uint32 movie_data_size;                       // +20
+ uint32 offset_to_savestate;           // +24, should be 4-byte-aligned
+ uint32 offset_to_movie_data;          // +28, should be 4-byte-aligned
+ uint8 md5_of_rom_used[16];                    // +32
+ uint32 version_of_emu_used                    // +48
+ char name_of_rom_used[]                       // +52, utf-8, null-terminated
+ char metadata[];                                      //      utf-8, null-terminated
+ uint8 padding[];
+ uint8 savestate[];                                    //      always present, even in a "from reset" recording
+ uint8 padding[];                                      //      used for byte-alignment
+ uint8 movie_data[];
+}
+*/
+
+//static
+int current = 0;     // > 0 for recording, < 0 for playback
+static FILE *slots[10]={0};
+static uint8 joop[4];
+static uint32 framets = 0;
+static uint32 frameptr = 0;
+static uint8* moviedata = NULL;
+static uint32 moviedatasize = 0;
+static uint32 firstframeoffset = 0;
+static uint32 savestate_offset = 0;
+static uint32 framecount = 0;
+static uint32 rerecord_count = 0;
+/*static*/ int movie_readonly = 1;
+int frame_display = 0;
+static uint32 last_frame_display = ~0;
+int input_display = 0;
+static uint32 cur_input_display = 0;
+static uint32 last_input_display = ~0;
+
+int resetDMCacc=0;
+
+/* Cache variables used for playback. */
+static uint32 nextts = 0;
+static int32 nextd = 0;
+
+#define FCEUSTATE_RLSB            0x80000000
+
+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 = 1;
+static int MovieShow = 0;
+
+static int MovieStatus[10];
+
+static void DoEncode(int joy, int button, int);
+
+int FCEUMOV_IsPlaying(void)
+{
+ if(current < 0) return(1);
+ else return(0);
+}
+
+int FCEUMOV_IsRecording(void)
+{
+ if(current > 0) return(1);
+ else return(0);
+}
+
+int suppressMovieStop=0;
+int movieConvertOffset1=0, movieConvertOffset2=0,movieConvertOK=0,movieSyncHackOn=0;
+
+static void StopPlayback(void)
+{
+  if(suppressMovieStop)
+   return;
+  resetDMCacc=movieSyncHackOn=0;
+  fclose(slots[-1 - current]);
+  current=0;
+  FCEU_DispMessage("Movie playback stopped.");
+}
+
+#if 0
+static void FlushHeader(void)
+{
+       if(current <= 0)
+               return;// only write header data if recording
+
+       FILE* fp = slots[current - 1];
+       if(fp == 0)
+               return;
+
+       unsigned long loc = ftell(fp);
+       fseek(fp, 4, SEEK_SET);
+       write32le(MOVIE_VERSION, fp);
+       fseek(fp, 12, SEEK_SET);
+       write32le(framecount, fp);
+       write32le(rerecord_count, fp);
+       write32le(frameptr, fp);
+       fseek(fp, 32, SEEK_SET);
+       fwrite(FCEUGameInfo->MD5, 1, 16, fp);   // write ROM checksum
+       write32le(FCEU_VERSION_NUMERIC, fp);    // write emu version used
+
+       // write ROM name used
+       fseek(fp, 52, SEEK_SET);
+       char str[512];
+       fgets(str,512,fp);
+       str[511]='\0';
+       int strdiff=strlen(FileBase)-strlen(str);
+       if(strdiff)
+       {
+               // resize the whole damn movie because the ROM name in the header is of variable length
+               int off=52;
+               fseek(fp, 52, SEEK_SET);
+               do { off++;
+               } while(fgetc(fp) && !feof(fp) && !ferror(fp));
+
+               if(feof(fp) || ferror(fp))
+               {
+                       fseek(fp, loc, SEEK_SET);
+                       return;
+               }
+
+               fseek(fp, 0, SEEK_END);
+               uint32 fsize=ftell(fp)-off;
+               char* ctemp=(char*)FCEU_malloc(fsize*sizeof(char)+4);
+               if(!ctemp)
+               {
+                       fseek(fp, loc, SEEK_SET);
+                       return;
+               }
+               fseek(fp, off, SEEK_SET);
+               fread(ctemp, 1,fsize, fp);
+               fseek(fp, 52+strlen(FileBase)+1, SEEK_SET);
+               int wrote = fwrite(ctemp, fsize,1, fp);
+               FCEU_free(ctemp);
+               if(!wrote)
+               {
+                       fseek(fp, loc, SEEK_SET);
+                       return;
+               }
+
+               if(loc >= firstframeoffset)
+                       loc += strdiff;
+               savestate_offset += strdiff;
+               firstframeoffset += strdiff;
+               fseek(fp, 24, SEEK_SET);
+               write32le(savestate_offset, fp);
+               write32le(firstframeoffset, fp);
+       }
+       fseek(fp, 52, SEEK_SET);
+       fputs(FileBase, fp);
+       fputc('\0', fp);
+
+       fseek(fp, loc, SEEK_SET);
+}
+
+static void StopRecording(void)
+{
+  if(suppressMovieStop)
+   return;
+  resetDMCacc=movieSyncHackOn=0;
+ DoEncode(0,0,1);   /* Write a dummy timestamp value so that the movie will keep
+                       "playing" after user input has stopped. */
+ // finish header
+ FlushHeader();
+
+ // FIXME:  truncate movie to length
+ // ftruncate();
+ fclose(slots[current - 1]);
+ MovieStatus[current - 1] = 1;
+ current=0;
+ FCEU_DispMessage("Movie recording stopped.");
+}
+#endif
+
+void FCEUI_StopMovie(void)
+{
+ if(current < 0) StopPlayback();
+#if 0
+ if(current > 0) StopRecording();
+#endif
+}
+
+#ifdef WIN32
+#include "process.h"
+void executeCommand(const char* cmd)
+{
+       if(!cmd || !*cmd)
+               return;
+
+       const char *argv[4];
+       argv[0] = getenv("COMSPEC");
+       argv[1] = "/c";
+       argv[2] = cmd;
+       argv[3] = NULL;
+       if(*argv && *(*argv))
+               _spawnve(_P_WAIT, argv[0], argv, NULL);
+}
+#endif
+
+int justAutoConverted=0;
+static const char* convertToFCM(const char *fname, char *buffer)
+{
+#ifdef WIN32
+       justAutoConverted=0;
+
+       // convert to fcm if not already
+       const char* dot = strrchr(fname, '.');
+       if(dot)
+       {
+               int fmv = !stricmp(dot, ".fmv");
+               int nmv = !stricmp(dot, ".nmv");
+               int vmv = !stricmp(dot, ".vmv");
+               if(fmv || nmv || vmv)
+               {
+                       strcpy(buffer, fname);
+                       buffer[dot-fname]='\0';
+                       strcat(buffer,"-autoconverted.fcm");
+
+                       int fceuver=0;
+                       if(fmv)
+                               fceuver=1;
+                       else if(nmv)
+                               fceuver=2;
+                       else if(vmv)
+                               fceuver=3;
+
+                       extern char lastLoadedGameName [2048];
+                       char cmd [1024], offset[64], romInfo[1024];
+                       if(movieConvertOK)
+                               sprintf(romInfo, "-smd5=\"%s\" -sromname=\"%s (MAYBE)\" -s", lastLoadedGameName, FileBase);
+                       else
+                               sprintf(romInfo, "-sromname=\"(unknown)\" -s");
+                       if(movieConvertOffset2) sprintf(offset, "-o %d:%d", movieConvertOffset2,movieConvertOffset1);
+                       else sprintf(offset, "-o %d", movieConvertOffset1);
+                       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);
+//                             FCEU_PrintError(cmd);
+                       executeCommand(cmd);
+
+                       FILE* file = FCEUD_UTF8fopen(buffer,"rb");
+                       if(file)
+                       {
+                               fseek(file, 12, SEEK_SET);
+                               int frames=0;
+                               read32le(&frames, file);
+                               if(frames)
+                               {
+                                       fname = buffer;
+                                       justAutoConverted=1;
+                               }
+                               else
+                               {
+                                       static int errAlready=0;
+                                       if(!errAlready)
+                                       {
+                                               errAlready=1;
+                                               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);
+                                       }
+                               }
+                               fclose(file);
+                       }
+                       else
+                       {
+                               char str [512];
+                               str[0] = '\0';
+                               GetCurrentDirectory(512,str);
+                               strcat(str, "\\util\\nesmock\\nesmock.exe");
+                               file = FCEUD_UTF8fopen(str, "rb");
+                               if(file)
+                               {
+                                       static int errAlready=0;
+                                       if(!errAlready)
+                                       {
+                                               errAlready=1;
+                                               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);
+                                               fclose(file);
+                                       }
+                               }
+                               else
+                               {
+                                       static int errAlready=0;
+                                       if(!errAlready)
+                                       {
+                                               errAlready=1;
+                                               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);
+                                       }
+                               }
+                       }
+               }
+       }
+#endif
+       return fname;
+}
+
+static void ResetInputTypes()
+{
+#ifdef WIN32
+ extern int UsrInputType[3];
+ UsrInputType[0] = SI_GAMEPAD;
+ UsrInputType[1] = SI_GAMEPAD;
+ UsrInputType[2] = SIFC_NONE;
+
+ ParseGIInput(NULL/*FCEUGameInfo*/);
+ extern int cspec, gametype;
+ cspec=FCEUGameInfo->cspecial;
+ gametype=FCEUGameInfo->type;
+
+ InitOtherInput();
+#endif
+}
+
+char curMovieFilename[512];
+
+
+// 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("trying movie v1\n");
+         FCEUI_LoadMovie_v1(fname, _read_only);
+         return;
+  }
+  else if(version == MOVIE_VERSION)
+  {}
+  else
+  {
+         // unsupported 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;
+  }
+
+//  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
+
+ 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
+}
+
+#if 0
+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);
+}
+
+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]);
+}
+#endif
+
+static int movie_readchar()
+{
+ if(frameptr >= moviedatasize)
+ {
+  return -1;
+ }
+ return (int)(moviedata[frameptr++]);
+}
+
+#if 0
+static void DoEncode(int joy, int button, int dummy)
+{
+ uint8 d;
+
+ d = 0;
+
+ if(framets >= 65536)
+  d = 3 << 5;
+ else if(framets >= 256)
+  d = 2 << 5;
+ else if(framets > 0)
+  d = 1 << 5;
+
+ if(dummy) d|=0x80;
+
+ d |= joy << 3;
+ d |= button;
+
+ movie_writechar(d);
+ //printf("Wr: %02x, %d\n",d,slots[current-1]);
+ while(framets)
+ {
+  movie_writechar(framets & 0xff);
+  //printf("Wrts: %02x\n",framets & 0xff);
+  framets >>= 8;
+ }
+}
+#endif
+
+// TODO: make this function legible! (what are all these magic numbers and weirdly named variables and crazy unexplained loops?)
+void FCEUMOV_AddJoy(uint8 *js)
+{
+ int x,y;
+
+// if(!current) return;   // Not playback nor recording.
+
+ if(current < 0)    // Playback
+ {
+  while(nextts == framets || nextd == -1)
+  {
+   int tmp,ti;
+   uint8 d;
+
+   if(nextd != -1)
+   {
+    if(nextd&0x80)
+    {
+    //puts("Egads");
+     // TODO?
+#if 0
+     FCEU_DoSimpleCommand(nextd&0x1F);
+#endif
+    }
+    else
+     joop[(nextd >> 3)&0x3] ^= 1 << (nextd&0x7);
+   }
+
+
+   tmp = movie_readchar();
+   d = tmp;
+
+   if(tmp < 0)
+   {
+    StopPlayback();
+    memcpy(&cur_input_display,js,4);
+    return;
+   }
+
+   nextts = 0;
+   tmp >>= 5;
+   tmp &= 0x3;
+   ti=0;
+
+   int tmpfix = tmp;
+   while(tmp--) { nextts |= movie_readchar() << (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 |= movie_readchar()<<8; }
+   else if(tmpfix == 2 && !nextts) {nextts |= movie_readchar()<<16; }
+
+   if(nextd != -1)
+    framets = 0;
+   nextd = d;
+  }
+
+  memcpy(js,joop,4);
+ }
+#if 0
+ else if(current > 0)                  // Recording
+ {
+    // flush header info every 300 frames in case of crash
+       {
+        static int fcounter=0;
+        fcounter++;
+        if(!(fcounter%300))
+                FlushHeader();
+       }
+
+  for(x=0;x<4;x++)
+  {
+   if(js[x] != joop[x])
+   {
+    for(y=0;y<8;y++)
+     if((js[x] ^ joop[x]) & (1 << y))
+      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.
+  }
+ }
+#endif
+
+ if(current)
+ {
+  framets++;
+  framecount++;
+ }
+
+ memcpy(&cur_input_display,js,4);
+}
+
+#if 0
+void FCEUMOV_AddCommand(int cmd)
+{
+ if(current <= 0) return;   // Return if not recording a movie
+ //printf("%d\n",cmd);
+ DoEncode((cmd>>3)&0x3,cmd&0x7,1);
+}
+#endif
+
+#if 0
+void FCEUMOV_CheckMovies(void)
+{
+        FILE *st=NULL;
+        char *fn;
+        int ssel;
+
+        for(ssel=0;ssel<10;ssel++)
+        {
+         st=FCEUD_UTF8fopen(fn=FCEU_MakeFName(FCEUMKF_MOVIE,ssel,0),"rb");
+         free(fn);
+         if(st)
+         {
+          MovieStatus[ssel]=1;
+          fclose(st);
+         }
+         else
+          MovieStatus[ssel]=0;
+        }
+
+}
+
+void FCEUI_SelectMovieNext(int n)
+{
+       if(n>0)
+               CurrentMovie=(CurrentMovie+1)%10;
+       else
+               CurrentMovie=(CurrentMovie+9)%10;
+       FCEUI_SelectMovie(CurrentMovie, 1);
+}
+
+
+int FCEUI_SelectMovie(int w, int show)
+{
+ int oldslot=CurrentMovie;
+ if(w == -1) { MovieShow = 0; return; }
+ FCEUI_SelectState(-1,0);
+
+ CurrentMovie=w;
+ MovieShow=180;
+
+ if(show)
+ {
+  MovieShow=180;
+  if(current > 0)
+   FCEU_DispMessage("-recording movie %d-",current-1);
+  else if (current < 0)
+   FCEU_DispMessage("-playing movie %d-",-1 - current);
+  else
+   FCEU_DispMessage("-select movie-");
+ }
+}
+
+int movcounter=0;
+
+void FCEU_DrawMovies(uint8 *XBuf)
+{
+       int frameDisplayOn = current != 0 && frame_display;
+       extern int howlong;
+#if WIN32
+       extern int32 fps_scale;
+#else
+       int32 fps_scale=256;
+#endif
+       int howl=(180-(FCEUI_EmulationPaused()?(60):(20*fps_scale/256)));
+       if(howl>176) howl=180;
+       if(howl<1) howl=1;
+       if((howlong<howl || movcounter)
+       && (frameDisplayOn && (!movcounter || last_frame_display!=framecount) || input_display && (!movcounter || last_input_display!=cur_input_display)))
+       {
+               char inputstr [32];
+               if(input_display)
+               {
+                       uint32 c = cur_input_display;
+                       sprintf(inputstr, "%c%c%c%c%c%c%c%c %c%c%c%c%c%c%c%c",
+                               (c&0x40)?'<':' ', (c&0x10)?'^':' ', (c&0x80)?'>':' ', (c&0x20)?'v':' ',
+                               (c&0x01)?'A':' ', (c&0x02)?'B':' ', (c&0x08)?'S':' ', (c&0x04)?'s':' ',
+                               (c&0x4000)?'<':' ', (c&0x1000)?'^':' ', (c&0x8000)?'>':' ', (c&0x2000)?'v':' ',
+                               (c&0x0100)?'A':' ', (c&0x0200)?'B':' ', (c&0x0800)?'S':' ', (c&0x0400)?'s':' ');
+                       if(!(c&0xff00))
+                               inputstr[8] = '\0';
+               }
+               if(frameDisplayOn && !input_display)
+                       FCEU_DispMessage("%s frame %u",current >= 0?"Recording":"Playing",framecount);
+               else if(input_display && !frameDisplayOn)
+                       FCEU_DispMessage("Input: %s",inputstr);
+               else //if(input_display && frame_display)
+                       FCEU_DispMessage("%s %u %s",current >= 0?"Recording":"Playing",framecount,inputstr);
+
+               last_frame_display = framecount;
+               last_input_display = cur_input_display;
+               movcounter=180-1;
+               return;
+       }
+
+       if(movcounter) movcounter--;
+
+       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;
+}
+
+static int load_successful;
+
+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 || (!movie_readonly && current < 0))            /* Recording or Playback (!read-only) */
+ if(current!=0 && !movie_readonly)
+ {
+  // copy movie data from savestate
+  moviedata = (uint8*)realloc(moviedata, size);
+  moviedatasize = size;
+  if(size && fread(moviedata, 1, size, st)<size)
+   return 0;
+  if(current < 0)                   // switch to recording
+   current = -current;
+  fseek(slots[current - 1], firstframeoffset, SEEK_SET);
+  fwrite(moviedata, 1, frameptr, slots[current - 1]);
+  rerecord_count++;
+ }
+// else if(current < 0)       /* Playback (read-only) */
+ else if(current!=0 && movie_readonly)
+ {
+  if(current > 0)                   // switch to playback
+   current = -current;
+  // allow frameptr to be updated but keep movie data
+  fseek(st, size, SEEK_CUR);
+  // prevent seeking beyond the end of the movie
+  if(frameptr > moviedatasize)
+   frameptr = moviedatasize;
+ }
+ else                                          /* Neither recording or replaying */
+ {
+  // skip movie data
+  fseek(st, size, SEEK_CUR);
+ }
+
+ load_successful=1;
+ return 1;
+}
+
+void FCEUMOV_PreLoad(void)
+{
+       load_successful=0;
+}
+
+int FCEUMOV_PostLoad(void)
+{
+       if(!FCEUI_IsMovieActive())
+               return 1;
+       else
+               return load_successful;
+}
+
+char* FCEUI_MovieGetCurrentName(int addSlotNumber)
+{
+ return FCEU_MakeFName(FCEUMKF_MOVIE,(addSlotNumber ? CurrentMovie : -1),0);
+}
+#endif
+
+int FCEUI_IsMovieActive(void)
+{
+  return current;
+}
+
+#if 0
+void FCEUI_MovieToggleFrameDisplay(void)
+{
+ frame_display=!frame_display;
+ if(!(current != 0 && frame_display) && !input_display)
+  FCEU_ResetMessages();
+ else
+ {
+  last_frame_display = ~framecount;
+  last_input_display = ~cur_input_display;
+ }
+}
+
+void FCEUI_ToggleInputDisplay(void)
+{
+ input_display=!input_display;
+ if(!input_display && !(current != 0 && frame_display))
+  FCEU_ResetMessages();
+ else
+ {
+  last_frame_display = ~framecount;
+  last_input_display = ~cur_input_display;
+ }
+}
+
+void FCEUI_MovieToggleReadOnly(void)
+{
+       if(movie_readonly < 2)
+       {
+               movie_readonly = !movie_readonly;
+               if(movie_readonly)
+                       FCEU_DispMessage("Movie is now Read-Only.");
+               else
+                       FCEU_DispMessage("Movie is now Read+Write.");
+       }
+       else
+       {
+               FCEU_DispMessage("Movie file is Read-Only.");
+       }
+}
+
+char lastMovieInfoFilename [512] = {'\0',};
+
+int FCEUI_MovieGetInfo(const char* fname, MOVIE_INFO* info)
+{
+       FlushHeader();
+
+       char buffer [512];
+       fname = (const char*)convertToFCM(fname,buffer);
+       strncpy(lastMovieInfoFilename, fname, 512);
+
+// main get info part of function
+{
+ uint32 magic;
+ uint32 version;
+ uint8 _flags[4];
+
+ FILE* fp = FCEUD_UTF8fopen(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);
+  if(version == 1)
+   return FCEUI_MovieGetInfo_v1(fname, info);
+  else
+   return 0;
+ }
+
+ info->movie_version = MOVIE_VERSION;
+
+ fread(_flags, 1, 4, fp);
+
+ info->flags = _flags[0];
+ read32le(&info->num_frames, fp);
+ read32le(&info->rerecord_count, fp);
+
+ if(access(fname, W_OK))
+  info->read_only = 1;
+ else
+  info->read_only = 0;
+
+ fseek(fp, 12, SEEK_CUR);                      // skip movie_data_size, offset_to_savestate, and offset_to_movie_data
+
+ fread(info->md5_of_rom_used, 1, 16, fp);
+ info->md5_of_rom_used_present = 1;
+
+ read32le(&info->emu_version_used, fp);
+
+ // I probably could have planned this better...
+ {
+  char str[256];
+  size_t r;
+  int p;
+  int p2=0;
+  char last_c=32;
+
+  if(info->name_of_rom_used && info->name_of_rom_used_size)
+   info->name_of_rom_used[0]='\0';
+
+  r=fread(str, 1, 256, fp);
+  while(r > 0)
+  {
+   for(p=0; p<r && last_c != '\0'; ++p)
+   {
+    if(info->name_of_rom_used && info->name_of_rom_used_size && (p2 < info->name_of_rom_used_size-1))
+    {
+     info->name_of_rom_used[p2]=str[p];
+     p2++;
+        last_c=str[p];
+    }
+   }
+   if(p<r)
+   {
+    memmove(str, str+p, r-p);
+    r -= p;
+    break;
+   }
+   r=fread(str, 1, 256, fp);
+  }
+
+  p2=0;
+  last_c=32;
+
+  if(info->metadata && info->metadata_size)
+   info->metadata[0]='\0';
+
+  while(r > 0)
+  {
+   for(p=0; p<r && last_c != '\0'; ++p)
+   {
+    if(info->metadata && info->metadata_size && (p2 < info->metadata_size-1))
+    {
+     info->metadata[p2]=str[p];
+     p2++;
+        last_c=str[p];
+    }
+   }
+   if(p != r)
+    break;
+   r=fread(str, 1, 256, fp);
+  }
+
+  if(r<=0)
+  {
+   // somehow failed to read romname and metadata
+   fclose(fp);
+   return 0;
+  }
+ }
+
+ // check what hacks are necessary
+  fseek(fp, 24, SEEK_SET);                     // offset_to_savestate offset
+  uint32 temp_savestate_offset;
+  read32le(&temp_savestate_offset, fp);
+  if(fseek(fp, temp_savestate_offset, SEEK_SET))
+  {
+   fclose(fp);
+   return 0;
+  }
+  if(!FCEUSS_LoadFP(fp,2)) return 0; // 2 -> don't really load, just load to find what's there then load backup
+
+ fclose(fp);
+ return 1;
+}
+}
+#endif
+/*
+  Backwards compat
+*/
+
+
+/*
+struct MovieHeader_v1
+{
+ uint32 magic;
+ uint32 version=1;
+ uint8 flags[4];
+ uint32 length_frames;
+ uint32 rerecord_count;
+ uint32 movie_data_size;
+ uint32 offset_to_savestate;
+ uint32 offset_to_movie_data;
+ uint16 metadata_ucs2[];     // ucs-2, ick!  sizeof(metadata) = offset_to_savestate - MOVIE_HEADER_SIZE
+}
+*/
+
+#define MOVIE_V1_HEADER_SIZE   32
+
+static void FCEUI_LoadMovie_v1(char *fname, int _read_only)
+{
+ FILE *fp;
+ char *fn = NULL;
+
+ FCEUI_StopMovie();
+
+#if 0
+ if(!fname)
+  fname = fn = FCEU_MakeFName(FCEUMKF_MOVIE,CurrentMovie,0);
+#endif
+
+ // check movie_readonly
+ movie_readonly = _read_only;
+ if(access(fname, W_OK))
+  movie_readonly = 2;
+
+#if 0
+ fp = FCEUD_UTF8fopen(fname, (movie_readonly>=2) ? "rb" : "r+b");
+#else
+ fp = fopen(fname, (movie_readonly>=2) ? "rb" : "r+b");
+#endif
+
+ if(fn)
+ {
+  free(fn);
+  fname = NULL;
+ }
+
+ if(!fp) return;
+
+ // read header
+ {
+  uint32 magic;
+  uint32 version;
+  uint8 flags[4];
+  uint32 fc;
+
+  read32le(&magic, fp);
+  if(magic != MOVIE_MAGIC)
+  {
+   fclose(fp);
+   return;
+  }
+
+  read32le(&version, fp);
+  if(version != 1)
+  {
+   fclose(fp);
+   return;
+  }
+
+  fread(flags, 1, 4, fp);
+  read32le(&fc, 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(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
+ {
+       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
+ }
+
+ 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
+static int FCEUI_MovieGetInfo_v1(const char* fname, MOVIE_INFO* info)
+{
+ uint32 magic;
+ uint32 version;
+ uint8 _flags[4];
+ uint32 savestateoffset;
+ uint8 tmp[MOVIE_MAX_METADATA<<1];
+ int metadata_length;
+
+ FILE* fp = FCEUD_UTF8fopen(fname, "rb");
+ if(!fp)
+  return 0;
+
+ read32le(&magic, fp);
+ if(magic != MOVIE_MAGIC)
+ {
+  fclose(fp);
+  return 0;
+ }
+
+ read32le(&version, fp);
+ if(version != 1)
+ {
+  fclose(fp);
+  return 0;
+ }
+
+ info->movie_version = 1;
+ info->emu_version_used = 0;                   // unknown
+
+ fread(_flags, 1, 4, fp);
+
+ info->flags = _flags[0];
+ read32le(&info->num_frames, fp);
+ read32le(&info->rerecord_count, fp);
+
+ if(access(fname, W_OK))
+  info->read_only = 1;
+ else
+  info->read_only = 0;
+
+ fseek(fp, 4, SEEK_CUR);
+ read32le(&savestateoffset, fp);
+
+ metadata_length = (int)savestateoffset - MOVIE_V1_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_V1_HEADER_SIZE, SEEK_SET);
+  fread(tmp, 1, metadata_length<<1, fp);
+ }
+
+ // turn old ucs2 metadata into utf8
+ if(info->metadata && info->metadata_size)
+ {
+        char* ptr=info->metadata;
+        char* ptr_end=info->metadata+info->metadata_size-1;
+        int c_ptr=0;
+        while(ptr<ptr_end && c_ptr<metadata_length)
+        {
+                uint16 c=(tmp[c_ptr<<1] | (tmp[(c_ptr<<1)+1] << 8));
+                switch(c)
+                {
+                case 0 ... 0x7f:
+                        *ptr++ = (char)(c&0x7f);
+                        break;
+                case 0x80 ... 0x7ff:
+                        if(ptr+1>=ptr_end)
+                                ptr_end=ptr;
+                        else
+                        {
+                                *ptr++=(0xc0 | (c>>6));
+                                *ptr++=(0x80 | (c & 0x3f));
+                        }
+                        break;
+                case 0x800 ... 0xffff:
+                        if(ptr+2>=ptr_end)
+                                ptr_end=ptr;
+                        else
+                        {
+                                *ptr++=(0xe0 | (c>>12));
+                                *ptr++=(0x80 | ((c>>6) & 0x3f));
+                                *ptr++=(0x80 | (c & 0x3f));
+                        }
+                        break;
+                }
+                c_ptr++;
+        }
+        *ptr='\0';
+ }
+
+ // md5 info not available from v1
+ info->md5_of_rom_used_present = 0;
+ // rom name used for the movie not available from v1
+ if(info->name_of_rom_used && info->name_of_rom_used_size)
+  info->name_of_rom_used[0] = '\0';
+
+ // check what hacks are necessary
+  fseek(fp, 24, SEEK_SET);                     // offset_to_savestate offset
+  uint32 temp_savestate_offset;
+  read32le(&temp_savestate_offset, fp);
+  if(fseek(fp, temp_savestate_offset, SEEK_SET))
+  {
+   fclose(fp);
+   return 0;
+  }
+  if(!FCEUSS_LoadFP(fp,2)) return 0; // 2 -> don't really load, just load to find what's there then load backup
+
+
+ fclose(fp);
+ return 1;
+}
+#endif
+
diff --git a/movie.h b/movie.h
new file mode 100644 (file)
index 0000000..8cc5fc3
--- /dev/null
+++ b/movie.h
@@ -0,0 +1,24 @@
+#ifndef __MOVIE_H_
+#define __MOVIE_H_
+
+#include "types.h"
+
+void FCEUMOV_AddJoy(uint8 *);
+
+#if 0
+void FCEUMOV_CheckMovies(void);
+void FCEUMOV_AddCommand(int cmd);
+void FCEU_DrawMovies(uint8 *);
+int FCEUMOV_IsPlaying(void);
+int FCEUMOV_IsRecording(void);
+
+int FCEUMOV_WriteState(FILE* st);
+int FCEUMOV_ReadState(FILE* st, uint32 size);
+void FCEUMOV_PreLoad(void);
+int FCEUMOV_PostLoad(void);
+#endif
+
+extern int current;     // > 0 for recording, < 0 for playback
+void FCEUI_LoadMovie(char *fname, int _read_only);
+
+#endif /* __MOVIE_H_ */
diff --git a/state.c b/state.c
index 5e4308d..106d5ed 100644 (file)
--- a/state.c
+++ b/state.c
@@ -330,19 +330,9 @@ void SaveState(void)
 }
 
 static int LoadStateOld(FILE *st);
-void LoadState(void)
+int FCEUSS_LoadFP(FILE *st, int make_backup)
 {
        int x;
-       FILE *st=NULL;
-
-        if(geniestage==1)
-        {
-         FCEU_DispMessage("Cannot load FCS in GG screen.");
-         return;
-        }
-
-       st=fopen(FCEU_MakeFName(FCEUMKF_STATE,CurrentState,0),"rb");
-
        if(st!=NULL)
        {
         uint8 header[16];
@@ -381,9 +371,27 @@ void LoadState(void)
         lerror:
         FCEU_DispMessage("State %d load error.",CurrentState);
         SaveStateStatus[CurrentState]=0;
-        return;
+        return 0;
+       }
+       return 1;
+}
+
+void LoadState(void)
+{
+       FILE *st=NULL;
+
+        if(geniestage==1)
+        {
+         FCEU_DispMessage("Cannot load FCS in GG screen.");
+         return;
+        }
+
+       st=fopen(FCEU_MakeFName(FCEUMKF_STATE,CurrentState,0),"rb");
+       if (st)
+       {
+        FCEUSS_LoadFP(st, 0);
+        fclose(st);
        }
-       fclose(st);
 }
 
 char SaveStateStatus[10];
diff --git a/state.h b/state.h
index 61939fd..75b2623 100644 (file)
--- a/state.h
+++ b/state.h
@@ -20,6 +20,7 @@
 
 void SaveState(void);
 void LoadState(void);
+int FCEUSS_LoadFP(FILE *st, int make_backup);
 
 extern uint8 StateName[2048];
 extern uint8 StateFile[2048];