📄 movie.cpp
字号:
{ S9xReset(); // save only SRAM for a from-reset snapshot result=(READ_STREAM(SRAM, 0x20000, stream) == 0x20000) ? SUCCESS : WRONG_FORMAT; } else { result=S9xUnfreezeFromStream(stream); } CLOSE_STREAM(stream); if(result!=SUCCESS) { return result; } if(!(fd=fopen(filename, read_only ? "rb" : "rb+"))) return FILE_NOT_FOUND; if(fseek(fd, Movie.ControllerDataOffset, SEEK_SET)) return WRONG_FORMAT; // read controller data Movie.File=fd; Movie.BytesPerFrame=bytes_per_frame(); Movie.InputBufferPtr=Movie.InputBuffer; uint32 to_read=Movie.BytesPerFrame * (Movie.MaxFrame+1); reserve_buffer_space(to_read); fread(Movie.InputBufferPtr, 1, to_read, fd); // read "baseline" controller data read_frame_controller_data(); strncpy(Movie.Filename, filename, _MAX_PATH); Movie.Filename[_MAX_PATH-1]='\0'; Movie.CurrentFrame=0; Movie.ReadOnly=read_only; change_state(MOVIE_STATE_PLAY); S9xMessage(S9X_INFO, S9X_MOVIE_INFO, MOVIE_INFO_REPLAY); return SUCCESS;}int S9xMovieCreate (const char* filename, uint8 controllers_mask, uint8 opts, const wchar_t* metadata, int metadata_length){ FILE* fd; STREAM stream; int fn; if(controllers_mask==0) return WRONG_FORMAT; if(!(fd=fopen(filename, "wb"))) return FILE_NOT_FOUND; // stop current movie before opening change_state(MOVIE_STATE_NONE); if(metadata_length>MOVIE_MAX_METADATA) { metadata_length=MOVIE_MAX_METADATA; } Movie.MovieId=(uint32)time(NULL); Movie.RerecordCount=0; Movie.MaxFrame=0; Movie.SaveStateOffset=SMV_HEADER_SIZE+(sizeof(uint16)*metadata_length); Movie.ControllerDataOffset=0; Movie.ControllersMask=controllers_mask; Movie.Opts=opts; if(Settings.PAL) { Movie.Opts |= MOVIE_OPT_PAL; } else { Movie.Opts &= ~MOVIE_OPT_PAL; } write_movie_header(fd, &Movie); // convert wchar_t metadata string/array to a uint16 array if(metadata_length>0) { uint8 meta_buf[MOVIE_MAX_METADATA * sizeof(uint16)]; int i; for(i=0; i<metadata_length; ++i) { uint16 c=(uint16)metadata[i]; meta_buf[i+i] =(uint8)(c&0xff); meta_buf[i+i+1]=(uint8)((c>>8)&0xff); } fwrite(meta_buf, sizeof(uint16), metadata_length, fd); } // write snapshot fn=dup(fileno(fd)); fclose(fd); // lseek(fn, Movie.SaveStateOffset, SEEK_SET); if(!(stream=REOPEN_STREAM(fn, "ab"))) return FILE_NOT_FOUND; if(opts & MOVIE_OPT_FROM_RESET) { S9xReset(); // save only SRAM for a from-reset snapshot WRITE_STREAM(SRAM, 0x20000, stream); } else { S9xFreezeToStream(stream); } CLOSE_STREAM(stream); if(!(fd=fopen(filename, "rb+"))) return FILE_NOT_FOUND; fseek(fd, 0, SEEK_END); Movie.ControllerDataOffset=(uint32)ftell(fd); // write "baseline" controller data Movie.File=fd; Movie.BytesPerFrame=bytes_per_frame(); Movie.InputBufferPtr=Movie.InputBuffer; write_frame_controller_data(); strncpy(Movie.Filename, filename, _MAX_PATH); Movie.Filename[_MAX_PATH-1]='\0'; Movie.CurrentFrame=0; Movie.ReadOnly=false; change_state(MOVIE_STATE_RECORD); S9xMessage(S9X_INFO, S9X_MOVIE_INFO, MOVIE_INFO_RECORD); return SUCCESS;}void S9xMovieUpdate (){ switch(Movie.State) { case MOVIE_STATE_PLAY: if(Movie.CurrentFrame>=Movie.MaxFrame) { change_state(MOVIE_STATE_NONE); S9xMessage(S9X_INFO, S9X_MOVIE_INFO, MOVIE_INFO_END); return; } else { if(Movie.FrameDisplay) { sprintf(Movie.FrameDisplayString, "Playing frame: %d", Movie.CurrentFrame); S9xMessage (S9X_INFO, S9X_MOVIE_INFO, Movie.FrameDisplayString); } read_frame_controller_data(); ++Movie.CurrentFrame; } break; case MOVIE_STATE_RECORD: { if(Movie.FrameDisplay) { sprintf(Movie.FrameDisplayString, "Recording frame: %d", Movie.CurrentFrame); S9xMessage (S9X_INFO, S9X_MOVIE_INFO, Movie.FrameDisplayString); } write_frame_controller_data(); ++Movie.CurrentFrame; Movie.MaxFrame=Movie.CurrentFrame; fwrite((Movie.InputBufferPtr - Movie.BytesPerFrame), 1, Movie.BytesPerFrame, Movie.File); } break; default: break; }}void S9xMovieStop (bool8 suppress_message){ if(Movie.State!=MOVIE_STATE_NONE) { change_state(MOVIE_STATE_NONE); if(!suppress_message) S9xMessage(S9X_INFO, S9X_MOVIE_INFO, MOVIE_INFO_STOP); }}int S9xMovieGetInfo (const char* filename, struct MovieInfo* info){ FILE* fd; int result; SMovie local_movie; int metadata_length; memset(info, 0, sizeof(*info)); if(!(fd=fopen(filename, "rb"))) return FILE_NOT_FOUND; if((result=(read_movie_header(fd, &local_movie)))!=SUCCESS) return result; info->TimeCreated=(time_t)local_movie.MovieId; info->LengthFrames=local_movie.MaxFrame; info->RerecordCount=local_movie.RerecordCount; info->Opts=local_movie.Opts; info->ControllersMask=local_movie.ControllersMask; if(local_movie.SaveStateOffset > SMV_HEADER_SIZE) { uint8 meta_buf[MOVIE_MAX_METADATA * sizeof(uint16)]; int i; metadata_length=((int)local_movie.SaveStateOffset-SMV_HEADER_SIZE)/sizeof(uint16); metadata_length=(metadata_length>=MOVIE_MAX_METADATA) ? MOVIE_MAX_METADATA-1 : metadata_length; metadata_length=(int)fread(meta_buf, sizeof(uint16), metadata_length, fd); for(i=0; i<metadata_length; ++i) { uint16 c=meta_buf[i+i] | (meta_buf[i+i+1] << 8); info->Metadata[i]=(wchar_t)c; } info->Metadata[i]='\0'; } else { info->Metadata[0]='\0'; } fclose(fd); if(access(filename, W_OK)) info->ReadOnly=true; return SUCCESS;}bool8 S9xMovieActive (){ return (Movie.State!=MOVIE_STATE_NONE);}bool8 S9xMovieReadOnly (){ if(!S9xMovieActive()) return false; return Movie.ReadOnly;}uint32 S9xMovieGetId (){ if(!S9xMovieActive()) return 0; return Movie.MovieId;}uint32 S9xMovieGetLength (){ if(!S9xMovieActive()) return 0; return Movie.MaxFrame;}uint32 S9xMovieGetFrameCounter (){ if(!S9xMovieActive()) return 0; return Movie.CurrentFrame;}void S9xMovieToggleFrameDisplay (){ Movie.FrameDisplay = !Movie.FrameDisplay; if(!Movie.FrameDisplay) { GFX.InfoStringTimeout = 1; }}void S9xMovieFreeze (uint8** buf, uint32* size){ // sanity check if(!S9xMovieActive()) { return; } *buf = NULL; *size = 0; // compute size needed for the buffer uint32 size_needed = 4*3; // room for MovieId, CurrentFrame, and MaxFrame size_needed += (uint32)(Movie.BytesPerFrame * (Movie.MaxFrame+1)); *buf=new uint8[size_needed]; *size=size_needed; uint8* ptr = *buf; if(!ptr) { return; } Write32(Movie.MovieId, ptr); Write32(Movie.CurrentFrame, ptr); Write32(Movie.MaxFrame, ptr); memcpy(ptr, Movie.InputBuffer, Movie.BytesPerFrame * (Movie.MaxFrame+1));}bool8 S9xMovieUnfreeze (const uint8* buf, uint32 size){ // sanity check if(!S9xMovieActive()) { return false; } const uint8* ptr = buf; if(size < 4*3) { return false; } uint32 movie_id = Read32(ptr); uint32 current_frame = Read32(ptr); uint32 max_frame = Read32(ptr); uint32 space_needed = (Movie.BytesPerFrame * (max_frame+1)); if(movie_id != Movie.MovieId || current_frame > max_frame || space_needed > size) { return false; } if(!Movie.ReadOnly) { // here, we are going to take the input data from the savestate // and make it the input data for the current movie, then continue // writing new input data at the currentframe pointer change_state(MOVIE_STATE_RECORD); S9xMessage(S9X_INFO, S9X_MOVIE_INFO, MOVIE_INFO_RERECORD); Movie.CurrentFrame = current_frame; Movie.MaxFrame = max_frame; ++Movie.RerecordCount; reserve_buffer_space(space_needed); memcpy(Movie.InputBuffer, ptr, space_needed); flush_movie(); fseek(Movie.File, Movie.ControllerDataOffset+(Movie.BytesPerFrame * (Movie.CurrentFrame+1)), SEEK_SET); } else { // here, we are going to keep the input data from the movie file // and simply rewind to the currentframe pointer // this will cause a desync if the savestate is not in sync // with the on-disk recording data, but it's easily solved // by loading another savestate or playing the movie from the beginning // and older savestate might have a currentframe pointer past // the end of the input data, so check for that here if(current_frame > Movie.MaxFrame) { return false; } change_state(MOVIE_STATE_PLAY); S9xMessage(S9X_INFO, S9X_MOVIE_INFO, MOVIE_INFO_REWIND); Movie.CurrentFrame = current_frame; } Movie.InputBufferPtr = Movie.InputBuffer + (Movie.BytesPerFrame * Movie.CurrentFrame); read_frame_controller_data(); return true;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -