#include "types.h" #include "fceu.h" #include "driver.h" #include "oldmovie.h" #include "movie.h" #include using namespace std; // FCM\x1a #define MOVIE_MAGIC 0x1a4d4346 // 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_VERSION 2 //------- //this is just supposed to be a comment describing the disk format //--- //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 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; //Cache variables used for playback. static uint32 nextts = 0; static int32 nextd = 0; static int movieConvertOffset1=0, movieConvertOffset2=0,movieSyncHackOn=0; //backwards compat static void FCEUI_LoadMovie_v1(char *fname, int _read_only); //static int FCEUI_MovieGetInfo_v1(const char* fname, MOVIE_INFO* info); //int _old_FCEUI_MovieGetInfo(const char* fname, MOVIE_INFO* info) //{ // //mbg: wtf? // //MovieFlushHeader(); // // // 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; // uint32 p; //mbg merge 7/17/06 change to uint32 // 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; pname_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(pmetadata && info->metadata_size) // info->metadata[0]='\0'; // // while(r > 0) // { // for(p=0; pmetadata && 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(temp_savestate_offset != 0xFFFFFFFF) // { // if(fseek(fp, temp_savestate_offset, SEEK_SET)) // { // fclose(fp); // return 0; // } // // //don't really load, just load to find what's there then load backup // if(!FCEUSS_LoadFP(fp,SSLOADPARAM_DUMMY)) return 0; // } // // fclose(fp); // return 1; // } //} /* 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(!fname) // fname = fn = FCEU_MakeFName(FCEUMKF_MOVIE,0,0); // // // check movie_readonly // movie_readonly = _read_only; // if(access(fname, W_OK)) // movie_readonly = 2; // // fp = FCEUD_UTF8fopen(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]; // 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]; // extern int disableBatteryLoading, suppressAddPowerCommand; // suppressAddPowerCommand=1; // suppressMovieStop=true; // { // FCEUGI * gi = FCEUI_LoadGame(lastLoadedGameName, 0); // if(!gi) // PowerNES(); // } // suppressMovieStop=false; // suppressAddPowerCommand=0; // } // // if(!FCEUSS_LoadFP(fp,SSLOADPARAM_BACKUP)) 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 = 0; // slots = fp; // // memset(joop,0,sizeof(joop)); // current = -1 - current; // framets=0; // nextts=0; // nextd = -1; // FCEU_DispMessage("Movie playback started."); //} // //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; //mbg merge 7/17/06 removed // // 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) // ptr_end=ptr; // else // { // *ptr++=(0xc0 | (c>>6)); // *ptr++=(0x80 | (c & 0x3f)); // } // else // if(ptr+2>=ptr_end) // ptr_end=ptr; // else // { // *ptr++=(0xe0 | (c>>12)); // *ptr++=(0x80 | ((c>>6) & 0x3f)); // *ptr++=(0x80 | (c & 0x3f)); // } // // 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,SSLOADPARAM_DUMMY)) return 0; // 2 -> don't really load, just load to find what's there then load backup // // // fclose(fp); // return 1; //} static int movie_readchar() { if(frameptr >= moviedatasize) { return -1; } return (int)(moviedata[frameptr++]); } static void FCEUMOV_AddJoy() { while(nextts == framets || nextd == -1) { int tmp,ti; uint8 d; if(nextd != -1) { if(nextd&0x80) { //puts("Egads"); //FCEU_DoSimpleCommand(nextd&0x1F); } else joop[(nextd >> 3)&0x3] ^= 1 << (nextd&0x7); } tmp = movie_readchar(); d = tmp; if(tmp < 0) { 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; } framets++; } EFCM_CONVERTRESULT convert_fcm(MovieData& md, std::string fname) { //convert EVEN OLDER movies to fcm //fname = (char*)convertToFCM(fname,buffer); uint32 framecount; uint32 rerecord_count; ifstream* fp = (ifstream*)FCEUD_UTF8_fstream(fname, "rb"); if(!fp) false; // read header uint32 magic; uint32 version; uint8 flags[4]; read32le(&magic, fp); if(magic != MOVIE_MAGIC) { delete fp; return FCM_CONVERTRESULT_FAILOPEN; } read32le(&version, fp); if(version == 1) { // attempt to load previous version's format //TODO delete fp; //FCEUI_LoadMovie_v1(fname, _read_only); return FCM_CONVERTRESULT_OLDVERSION; } else if(version == MOVIE_VERSION) {} else { // unsupported version delete fp; return FCM_CONVERTRESULT_UNSUPPORTEDVERSION; } fp->read((char*)&flags,4); read32le(&framecount, fp); read32le(&rerecord_count, fp); read32le(&moviedatasize, fp); read32le(&savestate_offset, fp); read32le(&firstframeoffset, fp); // FCEU_PrintError("flags[0] & MOVIE_FLAG_NOSYNCHACK=%d",flags[0] & MOVIE_FLAG_NOSYNCHACK); if(flags[0] & MOVIE_FLAG_NOSYNCHACK) movieSyncHackOn=0; else movieSyncHackOn=1; //if(flags[0] & MOVIE_FLAG_PAL) //if(flags[0] & MOVIE_FLAG_FROM_POWERON) //{ // //don't need to load a savestate // //there shouldn't be a savestate! // if(savestate_offset != 0xFFFFFFFF) // FCEU_PrintError("Savestate found in a start-from-poweron movie!"); //} //else //{ // if(savestate_offset == 0xFFFFFFFF) // FCEU_PrintError("No savestate found in a start-from-savestate movie!"); // if(fseek(fp, savestate_offset, SEEK_SET)) // { // fclose(fp); // return; // } // if(!FCEUSS_LoadFP(fp,SSLOADPARAM_BACKUP)) // return; //} //analyze input types? //ResetInputTypes(); fp->seekg(firstframeoffset,ios::beg); moviedata = (uint8*)realloc(moviedata, moviedatasize); fp->read((char*)moviedata,moviedatasize); frameptr = 0; memset(joop,0,sizeof(joop)); framets=0; nextts=0; nextd = -1; //prepare output structure md.rerecordCount = rerecord_count; md.records.resize(framecount); //begin decoding. //joymask is used to look for any joystick that has input. //if joysticks 3 or 4 have input then we will enable fourscore uint8 joymask[4] = {0,0,0,0}; for(uint32 i=0;i