diff -NaHudr 1.43-wip1/snes9x/Makefile.in wip1-patched/snes9x/Makefile.in --- 1.43-wip1/snes9x/Makefile.in 2004-07-12 00:50:56.000000000 +0300 +++ wip1-patched/snes9x/Makefile.in 2004-12-24 19:24:54.000000000 +0200 @@ -192,6 +192,8 @@ endif endif +OBJECTS += unix/nesvideos-piece.o + Makefile: configure Makefile.in @echo "Makefile is older than configure or in-file. Run configure or touch Makefile." exit 1 diff -NaHudr 1.43-wip1/snes9x/gfx.cpp wip1-patched/snes9x/gfx.cpp --- 1.43-wip1/snes9x/gfx.cpp 2004-07-12 00:50:58.000000000 +0300 +++ wip1-patched/snes9x/gfx.cpp 2004-12-24 19:24:55.000000000 +0200 @@ -97,12 +97,15 @@ #include "apu.h" #include "cheats.h" #include "screenshot.h" +#include "movie.h" #define M7 19 #define M8 19 void output_png(); void ComputeClipWindows (); +static void S9xDisplayMovieInfo(); +static void S9xDisplayPressedKeys(); static void S9xDisplayFrameRate (); static void S9xDisplayString (const char *string); @@ -795,6 +798,13 @@ S9xDoScreenshot(IPPU.RenderedScreenWidth, IPPU.RenderedScreenHeight); if (Settings.DisplayFrameRate) S9xDisplayFrameRate (); + + if (Settings.DisplayMovieInfo) + S9xDisplayMovieInfo (); + + if (Settings.DisplayPressedKeys) + S9xDisplayPressedKeys (); + if (GFX.InfoString) S9xDisplayString (GFX.InfoString); @@ -3443,6 +3453,57 @@ } } +static void S9xDisplayPressedKeys () +{ + uint8 *Screen = GFX.Screen + 2 + + (IPPU.RenderedScreenHeight - 3*font_height) * GFX.Pitch2; + char KeyMap[]={'0','1','2','R','L','X','A','>','<','v','^','S','s','Y','B'}; + char string [255]; + int len; + + if ((IPPU.Joypads[0] & 0xffff) == 0) + return; + + sprintf(string, "C1: "); + + int i; + for (i=0; i < 15; i++) + { + string[strlen("C1: ")+i]=((IPPU.Joypads[0] & (1 << (i+1)))!=0)?KeyMap[i]:' '; + } + + len=strlen(string); + + for (i = 0; i < len; i++) + { + DisplayChar (Screen, string [i]); + Screen += (font_width - 1) * (Settings.SixteenBit ? 2 : 1); + } + //puts(string); +} + +static void S9xDisplayMovieInfo () +{ + uint8 *Screen = GFX.Screen + 2 + + (IPPU.RenderedScreenHeight - font_height - 1) * GFX.Pitch2; + char string [41]; + int len; + + if (!S9xMovieActive()) + return; + + snprintf (string, 40, "Movie: %05d / %05d", S9xMovieGetFrameCounter(), + S9xMovieGetLength()); + len=strlen(string); + + int i; + for (i = 0; i < len; i++) + { + DisplayChar (Screen, string [i]); + Screen += (font_width - 1) * (Settings.SixteenBit ? 2 : 1); + } +} + static void S9xDisplayFrameRate () { uint8 *Screen = GFX.Screen + 2 + diff -NaHudr 1.43-wip1/snes9x/movie.cpp wip1-patched/snes9x/movie.cpp --- 1.43-wip1/snes9x/movie.cpp 2004-07-12 00:50:59.000000000 +0300 +++ wip1-patched/snes9x/movie.cpp 2004-12-24 19:24:55.000000000 +0200 @@ -663,6 +663,16 @@ return Movie.CurrentFrame; } +void S9xMovieToggleRecState() +{ + Movie.ReadOnly=!Movie.ReadOnly; + + if (Movie.ReadOnly) + S9xMessage(S9X_INFO, S9X_MOVIE_INFO, "Movie is now: ReadOnly"); + else + S9xMessage(S9X_INFO, S9X_MOVIE_INFO, "Movie is now: ReadWrite"); +} + void S9xMovieToggleFrameDisplay () { Movie.FrameDisplay = !Movie.FrameDisplay; diff -NaHudr 1.43-wip1/snes9x/movie.h wip1-patched/snes9x/movie.h --- 1.43-wip1/snes9x/movie.h 2004-07-12 00:50:59.000000000 +0300 +++ wip1-patched/snes9x/movie.h 2004-12-24 19:24:55.000000000 +0200 @@ -124,6 +124,7 @@ int S9xMovieCreate (const char* filename, uint8 controllers_mask, uint8 opts, const wchar_t* metadata, int metadata_length); int S9xMovieGetInfo (const char* filename, struct MovieInfo* info); void S9xMovieStop (bool8 suppress_message); +void S9xMovieToggleRecState(); void S9xMovieToggleFrameDisplay (); // methods used by the emulation diff -NaHudr 1.43-wip1/snes9x/snapshot.cpp wip1-patched/snes9x/snapshot.cpp --- 1.43-wip1/snes9x/snapshot.cpp 2004-07-12 00:51:00.000000000 +0300 +++ wip1-patched/snes9x/snapshot.cpp 2004-12-24 19:24:55.000000000 +0200 @@ -1056,10 +1056,19 @@ void FreezeBlock (STREAM stream, char *name, uint8 *block, int size) { char buffer [512]; - sprintf (buffer, "%s:%06d:", name, size); + if(size <= 999999) + sprintf (buffer, "%s:%06d:", name, size); + else + { + sprintf (buffer, "%s:------:", name); + buffer[6] = (unsigned char)((unsigned)size >> 24); + buffer[7] = (unsigned char)((unsigned)size >> 16); + buffer[8] = (unsigned char)((unsigned)size >> 8); + buffer[9] = (unsigned char)((unsigned)size >> 0); + } + buffer[11] = 0; WRITE_STREAM (buffer, strlen (buffer), stream); WRITE_STREAM (block, size, stream); - } int UnfreezeStruct (STREAM stream, char *name, void *base, FreezeData *fields, @@ -1162,13 +1171,29 @@ int len = 0; int rem = 0; int rew_len; - if (READ_STREAM (buffer, 11, stream) != 11 || - strncmp (buffer, name, 3) != 0 || buffer [3] != ':' || - (len = atoi (&buffer [4])) == 0) + buffer[11] = 0; + if (READ_STREAM (buffer, 11, stream) != 11 + || strncmp (buffer, name, 3) != 0 + || buffer [3] != ':') { + err: + fprintf(stderr, "Found '%.11s', expected %s(%d)\n", buffer, name, size); REVERT_STREAM(stream, FIND_STREAM(stream)-11, 0); return (WRONG_FORMAT); } + fprintf(stderr, "Found '%.11s', expected %s(%d) - OK\n", buffer, name, size); + if(buffer[4] == '-') + { + len = (((unsigned char)buffer[6]) << 24) + | (((unsigned char)buffer[7]) << 16) + | (((unsigned char)buffer[8]) << 8) + | (((unsigned char)buffer[9]) << 0); + } + else + { + len = atoi(buffer+4); + } + if(len <= 0) goto err; if (len > size) { diff -NaHudr 1.43-wip1/snes9x/snes9x.cpp wip1-patched/snes9x/snes9x.cpp --- 1.43-wip1/snes9x/snes9x.cpp 2004-07-12 00:51:00.000000000 +0300 +++ wip1-patched/snes9x/snes9x.cpp 2004-12-24 19:24:55.000000000 +0200 @@ -92,6 +92,8 @@ #include #endif +#include + #include "snes9x.h" #include "memmap.h" #include "display.h" @@ -211,6 +213,25 @@ #endif S9xMessage (S9X_INFO, S9X_USAGE, "\ +-displayframerate or -dfr Display frame rate\n"); + S9xMessage (S9X_INFO, S9X_USAGE, "\ +-displaymovieinfo or -dmi Display movie info\n"); + S9xMessage (S9X_INFO, S9X_USAGE, "\ +-displaypressedkeys or -dpk Display pressed keys\n"); + S9xMessage (S9X_INFO, S9X_USAGE, "\ +-maxframes Auto-terminate after this many frames. Best used with -autodemo\n"); + S9xMessage (S9X_INFO, S9X_USAGE, "\ +-autodemo Automatically loads as a .smv.\n\ + A double CPU reset occurs anyways.\n"); + + S9xMessage (S9X_INFO, S9X_USAGE, (std::string("\ +-videologcmd Enable audio&video logging and set the video logging command.\n\ + Default: ")+NESVideoGetVideoCmd()+"\n").c_str()); + S9xMessage (S9X_INFO, S9X_USAGE, (std::string("\ +-audiologcmd Enable audio&video logging and set the audio logging command.\n\ + Default: ")+NESVideoGetAudioCmd()+"\n").c_str()); + + S9xMessage (S9X_INFO, S9X_USAGE, "\ \nROM image needs to be in Super MagiCom (*.smc), Super FamiCom (*.sfc),\n\ *.fig, or split (*.1, *.2, or sf32527a, sf32527b, etc) format and can be\n\ compressed with gzip or compress.\n"); @@ -398,6 +419,36 @@ { Settings.DisplayFrameRate = TRUE; } + else if (strcasecmp (argv [i], "-displaymovieinfo") == 0 || + strcasecmp (argv [i], "-dmi") == 0) + { + Settings.DisplayMovieInfo = TRUE; + } + else if (strcasecmp (argv [i], "-displaypressedkeys") == 0 || + strcasecmp (argv [i], "-dpk") == 0) + { + Settings.DisplayPressedKeys = TRUE; + } + else if (strcasecmp(argv[i], "-autodemo") == 0) + { + extern char autodemo[512]; /* from unix/x11.cpp */ + strncpy(autodemo, argv[++i], 500); + } + else if (strcasecmp(argv[i], "-videologcmd") == 0) + { + NESVideoSetVideoCmd(argv[++i]); + LoggingEnabled=1; + } + else if (strcasecmp(argv[i], "-audiologcmd") == 0) + { + NESVideoSetAudioCmd(argv[++i]); + LoggingEnabled=1; + } + else if (strcasecmp(argv[i], "-maxframes") == 0) + { + extern unsigned maxframes; + maxframes = atoi(argv[++i]); + } else if (strcasecmp (argv [i], "-s") == 0 || strcasecmp (argv [i], "-swapjoypads") == 0 || strcasecmp (argv [i], "-sw") == 0) diff -NaHudr 1.43-wip1/snes9x/snes9x.h wip1-patched/snes9x/snes9x.h --- 1.43-wip1/snes9x/snes9x.h 2004-07-12 00:51:00.000000000 +0300 +++ wip1-patched/snes9x/snes9x.h 2004-12-24 19:24:55.000000000 +0200 @@ -258,7 +258,6 @@ bool8 Paused; bool8 ForcedPause; bool8 StopEmulation; - bool8 FrameAdvance; /* Tracing options */ bool8 TraceDMA; @@ -351,6 +350,8 @@ bool8 ForceNoTransparency; bool8 DisableHDMA; bool8 DisplayFrameRate; + bool8 DisplayMovieInfo; + bool8 DisplayPressedKeys; bool8 DisableRangeTimeOver; /* XXX: unused */ /* Others */ @@ -365,6 +366,8 @@ bool8 TurboMode; uint32 TurboSkipFrames; uint32 AutoMaxSkipFrames; + uint32 FrameAdvance; + uint32 SkipNumOfFrames; /* Fixes for individual games */ bool8 StarfoxHack; @@ -424,5 +427,20 @@ void S9xSetPause (uint32 mask); void S9xClearPause (uint32 mask); +#ifdef __cplusplus +extern "C" +{ +#endif + extern int LoggingEnabled; /* 0=no, 1=yes, 2=recording! */ + extern void NESVideoLoggingVideo(const void*data, unsigned width,unsigned height); + extern void NESVideoLoggingAudio(const void*data, unsigned bytes,unsigned bytes_per_second); + extern const char* NESVideoGetVideoCmd(); + extern const char* NESVideoGetAudioCmd(); + extern void NESVideoSetVideoCmd(const char *cmd); + extern void NESVideoSetAudioCmd(const char *cmd); +#ifdef __cplusplus +} +#endif + #endif diff -NaHudr 1.43-wip1/snes9x/unix/nesvideos-piece.cpp wip1-patched/snes9x/unix/nesvideos-piece.cpp --- 1.43-wip1/snes9x/unix/nesvideos-piece.cpp 1970-01-01 02:00:00.000000000 +0200 +++ wip1-patched/snes9x/unix/nesvideos-piece.cpp 2004-12-24 19:24:55.000000000 +0200 @@ -0,0 +1,272 @@ +#include +#include +#include +#include + +extern "C" { +int LoggingEnabled; /* 0=no, 1=yes, 2=recording! */ +} + +/* Note: This module assumes everyone uses RGB15 as display depth */ + +static std::string VIDEO_CMD = + "mencoder - -o test0.avi" + " -noskip -mc 0" + " -ovc lavc" + " -lavcopts vcodec=ffv1:context=0:format=BGR32:coder=1:vstrict=-1" + " >& mencoder.log"; +static std::string AUDIO_CMD = + "cat > s.log"; + +static unsigned FPS = 60; + +namespace AVI +{ + #define BGR24 0x42475218 // BGR24 fourcc + #define BGR16 0x42475210 // BGR16 fourcc + #define BGR15 0x4247520F // BGR15 fourcc + + FILE* avifp = NULL; + + void AVIOpen() + { + avifp = popen(VIDEO_CMD.c_str(), "w"); + } + + void AVIClose() + { + pclose(avifp); + } + + #define u32(n) (n)&255,((n)>>8)&255,((n)>>16)&255,((n)>>24)&255 + #define u16(n) (n)&255,((n)>>8)&255 + #define s4(s) s[0],s[1],s[2],s[3] + + void AVISend + (const unsigned char *vidbuf, + unsigned width, + unsigned height, + bool first_frame) + { + const unsigned fourcc = BGR16; + const unsigned framesize = width*height*2; + + if(first_frame) + { + const unsigned nframes = 0; //unknown + const unsigned scale = 1000000; + const unsigned scaled_fps = (unsigned)(scale * FPS); + + const unsigned SIZE_strh_vids = 4 + 4*2 + 2*2 + 8*4 + 2*4; + const unsigned SIZE_strf_vids = 4*3 + 2*2 + 4*6; + const unsigned SIZE_strl = 4+ 4+(4+SIZE_strh_vids) + 4+(4+SIZE_strf_vids); + const unsigned SIZE_avih = 4*12; + const unsigned SIZE_hdrl = 4+4+ (4+SIZE_avih) + 4 + (4+SIZE_strl); + const unsigned SIZE_movi = 4 + nframes*(4+4+framesize); + const unsigned SIZE_avi = 4+4+ (4+SIZE_hdrl) + 4 + (4+SIZE_movi); + + const unsigned char AVIheader[] = + { + s4("RIFF"), + u32(SIZE_avi), + s4("AVI "), + s4("LIST"), + u32(SIZE_hdrl), + s4("hdrl"), + + s4("avih"), + u32(SIZE_avih), + u32(0), + u32(0), + u32(0), + u32(0), + u32(nframes), + u32(0), + u32(1), // one stream + u32(0), + u32(0), + u32(0), + u32(0), + u32(0), + + s4("LIST"), + + u32(SIZE_strl), + s4("strl"), + + s4("strh"), + u32(SIZE_strh_vids), + s4("vids"), + u32(0), + u32(0), + u16(0), + u16(0), + u32(0), + u32(scale), + u32(scaled_fps), + u32(0), + u32(0), + u32(0), + u32(0), + u32(0), + u16(0), + u16(0), + u16(0), + u16(0), + + s4("strf"), + u32(SIZE_strf_vids), + u32(0), + u32(width), + u32(height), + u16(0), + u16(0), + u32(fourcc), + u32(0), + u32(0), + u32(0), + u32(0), + u32(0), + s4("LIST"), + u32(SIZE_movi), + s4("movi") + }; + + fwrite(AVIheader, 1, sizeof(AVIheader), avifp); + } + + const unsigned char header[] = { s4("00dc"), u32(framesize) }; + + fwrite(header, 1, sizeof(header), avifp); + fwrite(vidbuf, 1, framesize, avifp); + } +}; + +static class VideoLogging +{ + bool first; +public: + VideoLogging(): first(true) + { + AVI::AVIOpen(); + } + ~VideoLogging() + { + AVI::AVIClose(); + } + void operator() (const void*data, unsigned width,unsigned height) + { + if(first) SendLogo(width,height); + + AVI::AVISend( (const unsigned char*)data, width, height, first); + first=false; + } + void SendLogo(unsigned width, unsigned height) + { + /* Bisqwit's logo addition routine. */ + /* If you don't have his files, this function does nothing + * and it does not matter at all. + */ + + const char *background = + width==320 ? "logo320_240" + : height>224 ? "logo256_240" + : "logo256_224"; + + /* Note: This should be 1 second long. */ + for(unsigned frame = 0; frame < FPS; ++frame) + { + char Buf[4096]; + sprintf(Buf, "/shares/home/bisqwit/povray/nesvlogo/%s_f%u.tga", + background, frame); + + FILE*fp = fopen(Buf, "rb"); + if(!fp) continue; /* Silently ignore missing frames */ + + int idlen = fgetc(fp); + /* Silently ignore all other header data. + * These files are assumed to be uncompressed BGR24 tga files with Y swapped. + * Even their geometry is assumed to match perfectly. + */ + fseek(fp, 1+1+2+2+1+ /*org*/2+2+ /*geo*/2+2+ 1+1+idlen, SEEK_CUR); + + bool yflip=true; + std::vector data(width*height*3); + for(unsigned y=height; y-->0; ) + fread(&data[y*width*3], 1, width*3, fp); + fclose(fp); + + std::vector result(width*height); + for(unsigned pos=0, max=result.size(); pos= 1000000) + if(WasPaused) { - /* More than a second behind means probably - * pause. The next line prevents the magic - * fast-forward effect. + /* When coming from a pause-mode, prevent + * the magic fast-forward effect. */ next1 = now; } } } + WasPaused=0; /* Delay until we're completed this frame */ @@ -1972,65 +1998,74 @@ block_generate_sound = TRUE; - /* If we need more audio samples */ - if (so.samples_mixed_so_far < sample_count) + if(LoggingEnabled) { - /* Where to put the samples to */ - unsigned byte_offset = so.play_position + - (so.sixteen_bit ? (so.samples_mixed_so_far << 1) - : so.samples_mixed_so_far); -//printf ("%d:", sample_count - so.samples_mixed_so_far); fflush (stdout); - if (Settings.SoundSync == 2) - { - /*memset (Buf + (byte_offset & SOUND_BUFFER_SIZE_MASK), 0, - sample_count - so.samples_mixed_so_far);*/ - } - else - { - /* Mix the missing samples */ - S9xMixSamplesO (Buf, sample_count - so.samples_mixed_so_far, - byte_offset & SOUND_BUFFER_SIZE_MASK); - } - so.samples_mixed_so_far = sample_count; + /* When recording AVI, we handle the audio in unix/x11.cpp + * in a non-blocking mode for both accuracy and speed. + */ +# ifdef USE_THREADS + if(Settings.ThreadSound)sleep(1); +# endif } - -// if (!so.mute_sound) + else { - unsigned bytes_to_write = sample_count; - if(so.sixteen_bit) bytes_to_write <<= 1; + /* If we need more audio samples */ + if (so.samples_mixed_so_far < sample_count) + { + /* Where to put the samples to */ + unsigned byte_offset = so.play_position + + (so.sixteen_bit ? (so.samples_mixed_so_far << 1) + : so.samples_mixed_so_far); + //printf ("%d:", sample_count - so.samples_mixed_so_far); fflush (stdout); + if (Settings.SoundSync == 2) + { + /*memset (Buf + (byte_offset & SOUND_BUFFER_SIZE_MASK), 0, + sample_count - so.samples_mixed_so_far);*/ + } + else + { + /* Mix the missing samples */ + S9xMixSamplesO (Buf, sample_count - so.samples_mixed_so_far, + byte_offset & SOUND_BUFFER_SIZE_MASK); + } + so.samples_mixed_so_far = sample_count; + } + + unsigned bytes_to_write = sample_count; + if(so.sixteen_bit) bytes_to_write <<= 1; - unsigned byte_offset = so.play_position; - so.play_position += bytes_to_write; - so.play_position &= SOUND_BUFFER_SIZE_MASK; /* wrap to beginning */ + unsigned byte_offset = so.play_position; + so.play_position += bytes_to_write; + so.play_position &= SOUND_BUFFER_SIZE_MASK; /* wrap to beginning */ #ifdef USE_THREADS - if (Settings.ThreadSound) - pthread_mutex_unlock (&mutex); + if (Settings.ThreadSound) + pthread_mutex_unlock (&mutex); #endif - block_generate_sound = FALSE; + block_generate_sound = FALSE; - /* Feed the samples to the soundcard until nothing is left */ - for(;;) - { - int I = bytes_to_write; - if (byte_offset + I > SOUND_BUFFER_SIZE) - { - I = SOUND_BUFFER_SIZE - byte_offset; - } - if(I == 0) break; - - I = write (so.sound_fd, (char *) Buf + byte_offset, I); - if (I > 0) + /* Feed the samples to the soundcard until nothing is left */ + for(;;) + { + int I = bytes_to_write; + if (byte_offset + I > SOUND_BUFFER_SIZE) { - bytes_to_write -= I; - byte_offset += I; - byte_offset &= SOUND_BUFFER_SIZE_MASK; /* wrap */ + I = SOUND_BUFFER_SIZE - byte_offset; } - /* give up if an unrecoverable error happened */ - if(I < 0 && errno != EINTR) break; - } - /* All data sent. */ - } + if(I == 0) break; + + I = write (so.sound_fd, (char *) Buf + byte_offset, I); + if (I > 0) + { + bytes_to_write -= I; + byte_offset += I; + byte_offset &= SOUND_BUFFER_SIZE_MASK; /* wrap */ + } + /* give up if an unrecoverable error happened */ + if(I < 0 && errno != EINTR) break; + } + /* All data sent. */ + } /*LoggingEnabled */ so.samples_mixed_so_far -= sample_count; diff -NaHudr 1.43-wip1/snes9x/unix/x11.cpp wip1-patched/snes9x/unix/x11.cpp --- 1.43-wip1/snes9x/unix/x11.cpp 2004-07-12 00:51:03.000000000 +0300 +++ wip1-patched/snes9x/unix/x11.cpp 2004-12-24 20:03:29.000000000 +0200 @@ -938,6 +938,9 @@ S9xSetSoundMute (TRUE); filename = S9xSelectFilename (def, S9xGetSnapshotDirectory (), "smv", title); + + extern int WasPaused; WasPaused=1; + S9xSetSoundMute (FALSE); return (filename); } @@ -985,6 +988,7 @@ FRAMESKIP_INC, FRAMESKIP_DEC, PAUSE, + FRAME_ADVANCE, DGA_FULLSCREEN, SCREENSHOT, SPC7110LOG, @@ -1005,7 +1009,10 @@ WRITE_MOVIE_ASK, LOAD_MOVIE_ASK, STOP_MOVIE, - + MOVIE_DISPLAY_FRAMES, + MOVIE_REC_STATE, + DISPLAY_PRESSED_KEYS, + SKIP_NUMOF_FRAMES, FUNC_LAST = STOP_MOVIE /* update this to match the last token */ }; static const char* GetFuncName(functiontype func) @@ -1024,6 +1031,7 @@ case FRAMETIME_DEC: return "FRAMETIME_DEC"; case FRAMESKIP_INC: return "FRAMESKIP_INC"; case FRAMESKIP_DEC: return "FRAMESKIP_DEC"; + case FRAME_ADVANCE: return "FRAME_ADVANCE"; case PAUSE: return "PAUSE"; case DGA_FULLSCREEN: return "DGA_FULLSCREEN"; case SCREENSHOT: return "SCREENSHOT"; @@ -1045,6 +1053,10 @@ case WRITE_MOVIE_ASK: return "WRITE_MOVIE_ASK"; case LOAD_MOVIE_ASK: return "LOAD_MOVIE_ASK"; case STOP_MOVIE: return "STOP_MOVIE"; + case MOVIE_DISPLAY_FRAMES: return "MOVIE_DISPLAY_FRAMES"; + case MOVIE_REC_STATE: return "MOVIE_REC_STATE"; + case DISPLAY_PRESSED_KEYS: return "DISPLAY_PRESSED_KEYS"; + case SKIP_NUMOF_FRAMES: return "SKIP_NUMOF_FRAMES"; default: return "unknown"; } } @@ -1252,6 +1264,7 @@ Define(FRAMESKIP_DEC, 0, XK_minus); // pause Define(PAUSE, 0, XK_Pause, XK_Break, XK_Scroll_Lock); + Define(FRAME_ADVANCE, 0, XK_backslash, Shift(XK_7)); // misc functions Define(DGA_FULLSCREEN, 0, Alt(XK_Return)); Define(SCREENSHOT, 0, XK_Print); @@ -1275,7 +1288,10 @@ Define(WRITE_MOVIE_ASK, 0, Shift(XK_1)); Define(LOAD_MOVIE_ASK, 0, Shift(XK_2)); Define(STOP_MOVIE, 0, Shift(XK_3)); - + Define(MOVIE_DISPLAY_FRAMES, 0,Shift(XK_4)); + Define(MOVIE_REC_STATE, 0, Shift(XK_5)); + Define(DISPLAY_PRESSED_KEYS, 0,Shift(XK_8)); + Define(SKIP_NUMOF_FRAMES, 0, Shift(XK_0)); VerifySetup(); } public: @@ -1305,8 +1321,16 @@ } } KBSetup; +char autodemo[512] = ""; /* referenced by snes9x.cpp */ +unsigned maxframes = 0; + void S9xProcessEvents (bool8 block) { + if (autodemo[0]) + { + S9xMovieOpen(autodemo,TRUE); + autodemo[0] = 0; + } #ifdef USE_AIDO if (Settings.AIDOShmId) { @@ -1572,10 +1596,19 @@ if(event.type != KeyPress) break; Settings.Paused ^= 1; + Settings.FrameAdvance = 0; S9xDisplayStateChange ("Pause", Settings.Paused); + extern int WasPaused; WasPaused=1; + break; } + case KeyboardSetup::FRAME_ADVANCE: + { + if(event.type != KeyPress) break; + + if (Settings.Paused) Settings.FrameAdvance++; + } case KeyboardSetup::DGA_FULLSCREEN: { if(event.type != KeyPress) break; @@ -1699,6 +1732,7 @@ case KeyboardSetup::TURBO_ENABLE: { Settings.TurboMode = event.type == KeyPress; + extern int WasPaused; if(!Settings.TurboMode) WasPaused=1; break; } case KeyboardSetup::SUPERSCOPE_TURBO: @@ -1734,13 +1768,33 @@ if(event.type != KeyPress) break; wchar_t name[MOVIE_MAX_METADATA] = {0}; + char Buf[512]; + int FromReset = 0; + int NumPlayers = 1; + if(S9xMovieActive()) S9xMovieStop(FALSE); + + printf("Recording a movie. From reset? (y/n, empty to cancel)\n"); + fgets(Buf,sizeof Buf,stdin); strtok("\r\n", Buf); + if(!strcmp("y", Buf)) FromReset=1; + else if(!strcmp("n", Buf)) FromReset=0; + else { printf("Cancelling.\n"); break; } + + printf("How many players? (1-5, anything else to cancel)\n"); + fgets(Buf,sizeof Buf,stdin); strtok("\r\n", Buf); + NumPlayers = atoi(Buf); + if(NumPlayers < 1 || NumPlayers > 5) { printf("Cancelling.\n"); break; } + S9xMovieCreate(S9xChooseMovieFilename(FALSE), - 0x1F, - //MOVIE_OPT_FROM_SNAPSHOT - MOVIE_OPT_FROM_RESET + (1 << NumPlayers) - 1, + FromReset + ? MOVIE_OPT_FROM_RESET + : MOVIE_OPT_FROM_SNAPSHOT , name,0); + + extern int WasPaused; WasPaused=1; + break; } case KeyboardSetup::LOAD_MOVIE_ASK: @@ -1749,6 +1803,8 @@ if(S9xMovieActive()) S9xMovieStop(FALSE); S9xMovieOpen(S9xChooseMovieFilename(TRUE), FALSE); + if(S9xMovieActive() && LoggingEnabled) + LoggingEnabled=2; /* recording! */ break; } case KeyboardSetup::STOP_MOVIE: @@ -1758,7 +1814,51 @@ if(S9xMovieActive()) S9xMovieStop(FALSE); break; } + case KeyboardSetup::MOVIE_DISPLAY_FRAMES: + { + if(event.type != KeyPress) break; + + Settings.DisplayMovieInfo=!Settings.DisplayMovieInfo; + break; + } + case KeyboardSetup::MOVIE_REC_STATE: + { + if(event.type != KeyPress) break; + if (S9xMovieActive()) S9xMovieToggleRecState(); + + break; + } + case KeyboardSetup::SKIP_NUMOF_FRAMES: + { + char Buf[512]; + unsigned frames = 0; + + if(event.type != KeyPress) break; + + S9xSetSoundMute (TRUE); + printf("Skip number of frames: "); fflush(stdout); + + fgets(Buf,sizeof Buf, stdin); + sscanf(Buf, "%u", &frames); + + printf("Skipping %u frames\n", frames); + + Settings.Paused |= 1; + Settings.FrameAdvance = 2; + Settings.SkipNumOfFrames = (frames>2)?frames-2:0; + + extern int WasPaused; WasPaused=1; + + break; + } + case KeyboardSetup::DISPLAY_PRESSED_KEYS: + { + if(event.type != KeyPress) break; + + Settings.DisplayPressedKeys=!Settings.DisplayPressedKeys; + break; + } /* Do not put a "default" case here, or you'll * miss the compiler warning about unhandled * enumeration values @@ -1810,6 +1910,36 @@ void S9xPutImage (int snes_width, int snes_height) { + if(LoggingEnabled) + { + uint8 Buf[262144]; + unsigned N = so.playback_rate * Settings.FrameTime / 1000000; + if(so.stereo) N *= 2; + S9xMixSamplesO(Buf, N, 0); + if(so.sixteen_bit) N *= 2; + + if(LoggingEnabled >= 2 && maxframes > 0) + { + if(--maxframes <= 0) S9xExit(); + } + + unsigned scale = GUI.interpolate ? 2 : 1; + NESVideoLoggingVideo(GFX.Screen, snes_width*scale, snes_height); + NESVideoLoggingAudio(Buf, N, so.playback_rate*4); + + if(Settings.TurboMode) + { + fcntl(so.sound_fd, F_SETFL, + fcntl(so.sound_fd, F_GETFL) | O_NONBLOCK); + } + else + { + fcntl(so.sound_fd, F_SETFL, + fcntl(so.sound_fd, F_GETFL) & ~O_NONBLOCK); + } + write(so.sound_fd, Buf, N); + } + #ifdef USE_GLIDE if (Settings.GlideEnable) S9xGlidePutImage (snes_width, snes_height); @@ -2850,6 +2980,12 @@ } #endif //XAutoRepeatOff (GUI.display); + + /* set some reasonable defaults */ + Settings.InterpolatedSound = 1; + Settings.SkipFrames = 1; + Settings.Mode7Interpolate = 1; + so.sound_switch = 0xFF; } void S9xParseDisplayArg (char **argv, int &ind, int)