more properly handling new haa semantics
[drnoksnes] / movie.cpp
1 /*******************************************************************************
2   Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
3  
4   (c) Copyright 1996 - 2002 Gary Henderson (gary.henderson@ntlworld.com) and
5                             Jerremy Koot (jkoot@snes9x.com)
6
7   (c) Copyright 2001 - 2004 John Weidman (jweidman@slip.net)
8
9   (c) Copyright 2002 - 2004 Brad Jorsch (anomie@users.sourceforge.net),
10                             funkyass (funkyass@spam.shaw.ca),
11                             Joel Yliluoma (http://iki.fi/bisqwit/)
12                             Kris Bleakley (codeviolation@hotmail.com),
13                             Matthew Kendora,
14                             Nach (n-a-c-h@users.sourceforge.net),
15                             Peter Bortas (peter@bortas.org) and
16                             zones (kasumitokoduck@yahoo.com)
17
18   C4 x86 assembler and some C emulation code
19   (c) Copyright 2000 - 2003 zsKnight (zsknight@zsnes.com),
20                             _Demo_ (_demo_@zsnes.com), and Nach
21
22   C4 C++ code
23   (c) Copyright 2003 Brad Jorsch
24
25   DSP-1 emulator code
26   (c) Copyright 1998 - 2004 Ivar (ivar@snes9x.com), _Demo_, Gary Henderson,
27                             John Weidman, neviksti (neviksti@hotmail.com),
28                             Kris Bleakley, Andreas Naive
29
30   DSP-2 emulator code
31   (c) Copyright 2003 Kris Bleakley, John Weidman, neviksti, Matthew Kendora, and
32                      Lord Nightmare (lord_nightmare@users.sourceforge.net
33
34   OBC1 emulator code
35   (c) Copyright 2001 - 2004 zsKnight, pagefault (pagefault@zsnes.com) and
36                             Kris Bleakley
37   Ported from x86 assembler to C by sanmaiwashi
38
39   SPC7110 and RTC C++ emulator code
40   (c) Copyright 2002 Matthew Kendora with research by
41                      zsKnight, John Weidman, and Dark Force
42
43   S-DD1 C emulator code
44   (c) Copyright 2003 Brad Jorsch with research by
45                      Andreas Naive and John Weidman
46  
47   S-RTC C emulator code
48   (c) Copyright 2001 John Weidman
49   
50   ST010 C++ emulator code
51   (c) Copyright 2003 Feather, Kris Bleakley, John Weidman and Matthew Kendora
52
53   Super FX x86 assembler emulator code 
54   (c) Copyright 1998 - 2003 zsKnight, _Demo_, and pagefault 
55
56   Super FX C emulator code 
57   (c) Copyright 1997 - 1999 Ivar, Gary Henderson and John Weidman
58
59
60   SH assembler code partly based on x86 assembler code
61   (c) Copyright 2002 - 2004 Marcus Comstedt (marcus@mc.pp.se) 
62
63   Input recording/playback code
64   (c) Copyright 2004 blip
65  
66   Specific ports contains the works of other authors. See headers in
67   individual files.
68  
69   Snes9x homepage: http://www.snes9x.com
70  
71   Permission to use, copy, modify and distribute Snes9x in both binary and
72   source form, for non-commercial purposes, is hereby granted without fee,
73   providing that this license information and copyright notice appear with
74   all copies and any derived work.
75  
76   This software is provided 'as-is', without any express or implied
77   warranty. In no event shall the authors be held liable for any damages
78   arising from the use of this software.
79  
80   Snes9x is freeware for PERSONAL USE only. Commercial users should
81   seek permission of the copyright holders first. Commercial use includes
82   charging money for Snes9x or software derived from Snes9x.
83  
84   The copyright holders request that bug fixes and improvements to the code
85   should be forwarded to them so everyone can benefit from the modifications
86   in future versions.
87  
88   Super NES and Super Nintendo Entertainment System are trademarks of
89   Nintendo Co., Limited and its subsidiary companies.
90 *******************************************************************************/
91 #include "port.h"
92 #include <string.h>
93 #ifdef HAVE_STRINGS_H
94 #include <strings.h>
95 #endif
96 #include <ctype.h>
97 #include <stdlib.h>
98
99 #if defined(__unix) || defined(__linux) || defined(__sun) || defined(__DJGPP)
100 #include <unistd.h>
101 #include <sys/types.h>
102 #include <sys/stat.h>
103 #endif
104 #include <time.h>
105
106 #ifdef WIN32
107 #include <io.h>
108 #ifndef W_OK
109 #define W_OK 2
110 #endif
111 #endif
112
113 #include "movie.h"
114 #include "snes9x.h"
115 #include "cpuexec.h"
116 #include "snapshot.h"
117
118 #define SMV_MAGIC       0x1a564d53              // SMV0x1a
119 #define SMV_VERSION     1
120 #define SMV_HEADER_SIZE 32
121 #define CONTROLLER_DATA_SIZE    2
122 #define BUFFER_GROWTH_SIZE      4096
123
124 enum MovieState
125 {
126         MOVIE_STATE_NONE=0,
127         MOVIE_STATE_PLAY,
128         MOVIE_STATE_RECORD
129 };
130
131 static struct SMovie
132 {
133         enum MovieState State;
134         char   Filename [_MAX_PATH];
135         FILE*  File;
136         uint32 SaveStateOffset;
137         uint32 ControllerDataOffset;
138         uint32 MovieId;
139         uint32 CurrentFrame;
140         uint32 MaxFrame;
141         uint32 RerecordCount;
142         uint8  ControllersMask;
143         uint8  Opts;
144         bool8  ReadOnly;
145         uint32 BytesPerFrame;
146         uint8* InputBuffer;
147         uint32 InputBufferSize;
148         uint8* InputBufferPtr;
149         bool8  FrameDisplay;
150         char   FrameDisplayString[256];
151 } Movie;
152
153 /*
154         For illustration:
155 struct MovieFileHeader
156 {
157         uint32  magic;          // SMV0x1a
158         uint32  version;
159         uint32  uid;                    // used to match savestates to a particular movie
160         uint32  rerecord_count;
161         uint32  length_frames;
162         uint8   flags[4];
163         uint32  offset_to_savestate;    // smvs have an embedded savestate
164         uint32  offset_to_controller_data;
165         // after the header comes extra metadata
166         // sizeof(metadata) = offset_to_savestate - sizeof(MovieFileHeader)
167 };
168 */
169
170 static int bytes_per_frame()
171 {
172         int i;
173         int num_controllers;
174
175         num_controllers=0;
176         for(i=0; i<5; ++i)
177         {
178                 if(Movie.ControllersMask & (1<<i))
179                 {
180                         ++num_controllers;
181                 }
182         }
183
184         return CONTROLLER_DATA_SIZE*num_controllers;
185 }
186
187 static inline uint32 Read32(const uint8*& ptr)
188 {
189         uint32 v=(ptr[0] | (ptr[1]<<8) | (ptr[2]<<16) | (ptr[3]<<24));
190         ptr += 4;
191         return v;
192 }
193
194 static inline uint16 Read16(const uint8*& ptr) /* const version */
195 {
196         uint16 v=(ptr[0] | (ptr[1]<<8));
197         ptr += 2;
198         return v;
199 }
200
201 static inline uint16 Read16(uint8*& ptr) /* non-const version */
202 {
203         uint16 v=(ptr[0] | (ptr[1]<<8));
204         ptr += 2;
205         return v;
206 }
207
208 static void Write32(uint32 v, uint8*& ptr)
209 {
210         ptr[0]=(uint8)(v&0xff);
211         ptr[1]=(uint8)((v>>8)&0xff);
212         ptr[2]=(uint8)((v>>16)&0xff);
213         ptr[3]=(uint8)((v>>24)&0xff);
214         ptr += 4;
215 }
216
217 static void Write16(uint16 v, uint8*& ptr)
218 {
219         ptr[0]=(uint8)(v&0xff);
220         ptr[1]=(uint8)((v>>8)&0xff);
221         ptr += 2;
222 }
223
224 static int read_movie_header(FILE* fd, SMovie* movie)
225 {
226         uint8 header[SMV_HEADER_SIZE];
227         if(fread(header, 1, SMV_HEADER_SIZE, fd) != SMV_HEADER_SIZE)
228                 return WRONG_FORMAT;
229
230         const uint8* ptr=header;
231         uint32 magic=Read32(ptr);
232         if(magic!=SMV_MAGIC)
233                 return WRONG_FORMAT;
234
235         uint32 version=Read32(ptr);
236         if(version!=SMV_VERSION)
237                 return WRONG_VERSION;
238
239         movie->MovieId=Read32(ptr);
240         movie->RerecordCount=Read32(ptr);
241         movie->MaxFrame=Read32(ptr);
242
243         movie->ControllersMask=*ptr++;
244         movie->Opts=*ptr++;
245         ptr += 2;
246
247         movie->SaveStateOffset=Read32(ptr);
248         movie->ControllerDataOffset=Read32(ptr);
249
250         return SUCCESS;
251 }
252
253 static void write_movie_header(FILE* fd, const SMovie* movie)
254 {
255         uint8 header[SMV_HEADER_SIZE];
256         uint8* ptr=header;
257
258         Write32(SMV_MAGIC, ptr);
259         Write32(SMV_VERSION, ptr);
260         Write32(movie->MovieId, ptr);
261         Write32(movie->RerecordCount, ptr);
262         Write32(movie->MaxFrame, ptr);
263
264         *ptr++=movie->ControllersMask;
265         *ptr++=movie->Opts;
266         *ptr++=0;
267         *ptr++=0;
268
269         Write32(movie->SaveStateOffset, ptr);
270         Write32(movie->ControllerDataOffset, ptr);
271
272         fwrite(header, 1, SMV_HEADER_SIZE, fd);
273 }
274
275 static void flush_movie()
276 {
277         fseek(Movie.File, 0, SEEK_SET);
278         write_movie_header(Movie.File, &Movie);
279         fseek(Movie.File, Movie.ControllerDataOffset, SEEK_SET);
280         fwrite(Movie.InputBuffer, 1, Movie.BytesPerFrame*(Movie.MaxFrame+1), Movie.File);
281 }
282
283 static void change_state(MovieState new_state)
284 {
285         if(new_state==Movie.State)
286                 return;
287
288         if(Movie.State==MOVIE_STATE_RECORD)
289         {
290                 flush_movie();
291         }
292
293         Movie.State=new_state;
294
295         if(new_state==MOVIE_STATE_NONE)
296         {
297                 fclose(Movie.File);
298                 Movie.File=NULL;
299                 // FIXME: truncate movie to MaxFrame length
300                 /* truncate() could be used, if it's certain
301                  * that the savestate block is never after
302                  * the controller data block. It is not guaranteed
303                  * by the format.
304                  */
305         }
306 }
307
308 static void reserve_buffer_space(uint32 space_needed)
309 {
310         if(space_needed > Movie.InputBufferSize)
311         {
312                 uint32 ptr_offset = Movie.InputBufferPtr - Movie.InputBuffer;
313                 uint32 alloc_chunks = space_needed / BUFFER_GROWTH_SIZE;
314                 Movie.InputBufferSize = BUFFER_GROWTH_SIZE * (alloc_chunks+1);
315                 Movie.InputBuffer = (uint8*)realloc(Movie.InputBuffer, Movie.InputBufferSize);
316                 Movie.InputBufferPtr = Movie.InputBuffer + ptr_offset;
317         }
318 }
319
320 static void read_frame_controller_data()
321 {
322         int i;
323         for(i=0; i<5; ++i)
324         {
325                 if(Movie.ControllersMask & (1<<i))
326                 {
327                         IPPU.Joypads[i]=(uint32)(Read16(Movie.InputBufferPtr)) | 0x80000000L;
328                 }
329                 else
330                 {
331                         IPPU.Joypads[i]=0;              // pretend the controller is disconnected
332                 }
333         }
334 }
335
336 static void write_frame_controller_data()
337 {
338         reserve_buffer_space((uint32)((Movie.InputBufferPtr+Movie.BytesPerFrame)-Movie.InputBuffer));
339
340         int i;
341         for(i=0; i<5; ++i)
342         {
343                 if(Movie.ControllersMask & (1<<i))
344                 {
345                         Write16((uint16)(IPPU.Joypads[i] & 0xffff), Movie.InputBufferPtr);
346                 }
347                 else
348                 {
349                         IPPU.Joypads[i]=0;              // pretend the controller is disconnected
350                 }
351         }
352 }
353
354 void S9xMovieInit ()
355 {
356         memset(&Movie, 0, sizeof(Movie));
357         Movie.State = MOVIE_STATE_NONE;
358 }
359
360 int S9xMovieOpen (const char* filename, bool8 read_only)
361 {
362         FILE* fd;
363         STREAM stream;
364         int result;
365         int fn;
366
367    return FILE_NOT_FOUND;
368 #if 0
369
370         if(!(fd=fopen(filename, read_only ? "rb" : "rb+")))
371                 return FILE_NOT_FOUND;
372
373         // stop current movie before opening
374         change_state(MOVIE_STATE_NONE);
375
376         // read header
377         if((result=read_movie_header(fd, &Movie))!=SUCCESS)
378         {
379                 fclose(fd);
380                 return result;
381         }
382
383         fn=dup(fileno(fd));
384         fclose(fd);
385
386         // apparently this lseek is necessary
387         lseek(fn, Movie.SaveStateOffset, SEEK_SET);
388         if(!(stream=REOPEN_STREAM(fn, "rb")))
389                 return FILE_NOT_FOUND;
390
391         if(Movie.Opts & MOVIE_OPT_FROM_RESET)
392         {
393                 S9xReset();
394                 // save only SRAM for a from-reset snapshot
395                 result=(READ_STREAM(SRAM, 0x20000, stream) == 0x20000) ? SUCCESS : WRONG_FORMAT;
396         }
397         else
398         {
399                 result=S9xUnfreezeFromStream(stream);
400         }
401         CLOSE_STREAM(stream);
402
403         if(result!=SUCCESS)
404         {
405                 return result;
406         }
407
408         if(!(fd=fopen(filename, read_only ? "rb" : "rb+")))
409                 return FILE_NOT_FOUND;
410
411         if(fseek(fd, Movie.ControllerDataOffset, SEEK_SET))
412                 return WRONG_FORMAT;
413
414         // read controller data
415         Movie.File=fd;
416         Movie.BytesPerFrame=bytes_per_frame();
417         Movie.InputBufferPtr=Movie.InputBuffer;
418         uint32 to_read=Movie.BytesPerFrame * (Movie.MaxFrame+1);
419         reserve_buffer_space(to_read);
420         fread(Movie.InputBufferPtr, 1, to_read, fd);
421
422         // read "baseline" controller data
423         read_frame_controller_data();
424
425         strncpy(Movie.Filename, filename, _MAX_PATH);
426         Movie.Filename[_MAX_PATH-1]='\0';
427         Movie.CurrentFrame=0;
428         Movie.ReadOnly=read_only;
429         change_state(MOVIE_STATE_PLAY);
430
431         S9xMessage(S9X_INFO, S9X_MOVIE_INFO, MOVIE_INFO_REPLAY);
432         return SUCCESS;
433 #endif
434 }
435
436 int S9xMovieCreate (const char* filename, uint8 controllers_mask, uint8 opts, const wchar_t* metadata, int metadata_length)
437 {
438         FILE* fd;
439         STREAM stream;
440         int fn;
441
442    return FILE_NOT_FOUND;
443 #if 0
444
445         if(controllers_mask==0)
446                 return WRONG_FORMAT;
447
448         if(!(fd=fopen(filename, "wb")))
449                 return FILE_NOT_FOUND;
450
451         // stop current movie before opening
452         change_state(MOVIE_STATE_NONE);
453
454         if(metadata_length>MOVIE_MAX_METADATA)
455         {
456                 metadata_length=MOVIE_MAX_METADATA;
457         }
458
459         Movie.MovieId=(uint32)time(NULL);
460         Movie.RerecordCount=0;
461         Movie.MaxFrame=0;
462         Movie.SaveStateOffset=SMV_HEADER_SIZE+(sizeof(uint16)*metadata_length);
463         Movie.ControllerDataOffset=0;
464         Movie.ControllersMask=controllers_mask;
465         Movie.Opts=opts;
466         if(Settings.PAL)
467         {
468                 Movie.Opts |= MOVIE_OPT_PAL;
469         }
470         else
471         {
472                 Movie.Opts &= ~MOVIE_OPT_PAL;
473         }
474
475         write_movie_header(fd, &Movie);
476
477         // convert wchar_t metadata string/array to a uint16 array
478         if(metadata_length>0)
479         {
480                 uint8 meta_buf[MOVIE_MAX_METADATA * sizeof(uint16)];
481                 int i;
482
483                 for(i=0; i<metadata_length; ++i)
484                 {
485                         uint16 c=(uint16)metadata[i];
486                         meta_buf[i+i]  =(uint8)(c&0xff);
487                         meta_buf[i+i+1]=(uint8)((c>>8)&0xff);
488                 }
489
490                 fwrite(meta_buf, sizeof(uint16), metadata_length, fd);
491         }
492
493         // write snapshot
494         fn=dup(fileno(fd));
495         fclose(fd);
496
497         // lseek(fn, Movie.SaveStateOffset, SEEK_SET);
498         if(!(stream=REOPEN_STREAM(fn, "ab")))
499                 return FILE_NOT_FOUND;
500
501         if(opts & MOVIE_OPT_FROM_RESET)
502         {
503                 S9xReset();
504                 // save only SRAM for a from-reset snapshot
505                 WRITE_STREAM(SRAM, 0x20000, stream);
506         }
507         else
508         {
509                 S9xFreezeToStream(stream);
510         }
511         CLOSE_STREAM(stream);
512
513         if(!(fd=fopen(filename, "rb+")))
514                 return FILE_NOT_FOUND;
515
516         fseek(fd, 0, SEEK_END);
517         Movie.ControllerDataOffset=(uint32)ftell(fd);
518
519         // write "baseline" controller data
520         Movie.File=fd;
521         Movie.BytesPerFrame=bytes_per_frame();
522         Movie.InputBufferPtr=Movie.InputBuffer;
523         write_frame_controller_data();
524
525         strncpy(Movie.Filename, filename, _MAX_PATH);
526         Movie.Filename[_MAX_PATH-1]='\0';
527         Movie.CurrentFrame=0;
528         Movie.ReadOnly=false;
529         change_state(MOVIE_STATE_RECORD);
530
531         S9xMessage(S9X_INFO, S9X_MOVIE_INFO, MOVIE_INFO_RECORD);
532         return SUCCESS;
533 #endif
534 }
535
536 void S9xMovieUpdate ()
537 {
538         switch(Movie.State)
539         {
540         case MOVIE_STATE_PLAY:
541                 if(Movie.CurrentFrame>=Movie.MaxFrame)
542                 {
543                         change_state(MOVIE_STATE_NONE);
544                         S9xMessage(S9X_INFO, S9X_MOVIE_INFO, MOVIE_INFO_END);
545                         return;
546                 }
547                 else
548                 {
549                         if(Movie.FrameDisplay)
550                         {
551                                 sprintf(Movie.FrameDisplayString, "Playing frame: %d", Movie.CurrentFrame);
552                                 S9xMessage (S9X_INFO, S9X_MOVIE_INFO, Movie.FrameDisplayString);
553                         }
554                         read_frame_controller_data();
555                         ++Movie.CurrentFrame;
556                 }
557                 break;
558
559         case MOVIE_STATE_RECORD:
560                 {
561                         if(Movie.FrameDisplay)
562                         {
563                                 sprintf(Movie.FrameDisplayString, "Recording frame: %d", Movie.CurrentFrame);
564                                 S9xMessage (S9X_INFO, S9X_MOVIE_INFO, Movie.FrameDisplayString);
565                         }
566                         write_frame_controller_data();
567                         ++Movie.CurrentFrame;
568                         Movie.MaxFrame=Movie.CurrentFrame;
569                         fwrite((Movie.InputBufferPtr - Movie.BytesPerFrame), 1, Movie.BytesPerFrame, Movie.File);
570                 }
571                 break;
572
573         default:
574                 break;
575         }
576 }
577
578 void S9xMovieStop (bool8 suppress_message)
579 {
580         if(Movie.State!=MOVIE_STATE_NONE)
581         {
582                 change_state(MOVIE_STATE_NONE);
583
584                 if(!suppress_message)
585                         S9xMessage(S9X_INFO, S9X_MOVIE_INFO, MOVIE_INFO_STOP);
586         }
587 }
588
589 int S9xMovieGetInfo (const char* filename, struct MovieInfo* info)
590 {
591         FILE* fd;
592         int result;
593         SMovie local_movie;
594         int metadata_length;
595
596    return FILE_NOT_FOUND;
597 #if 0
598
599         memset(info, 0, sizeof(*info));
600         if(!(fd=fopen(filename, "rb")))
601                 return FILE_NOT_FOUND;
602
603         if((result=(read_movie_header(fd, &local_movie)))!=SUCCESS)
604                 return result;
605
606         info->TimeCreated=(time_t)local_movie.MovieId;
607         info->LengthFrames=local_movie.MaxFrame;
608         info->RerecordCount=local_movie.RerecordCount;
609         info->Opts=local_movie.Opts;
610         info->ControllersMask=local_movie.ControllersMask;
611
612         if(local_movie.SaveStateOffset > SMV_HEADER_SIZE)
613         {
614                 uint8 meta_buf[MOVIE_MAX_METADATA * sizeof(uint16)];
615                 int i;
616
617                 metadata_length=((int)local_movie.SaveStateOffset-SMV_HEADER_SIZE)/sizeof(uint16);
618                 metadata_length=(metadata_length>=MOVIE_MAX_METADATA) ? MOVIE_MAX_METADATA-1 : metadata_length;
619                 metadata_length=(int)fread(meta_buf, sizeof(uint16), metadata_length, fd);
620
621                 for(i=0; i<metadata_length; ++i)
622                 {
623                         uint16 c=meta_buf[i+i] | (meta_buf[i+i+1] << 8);
624                         info->Metadata[i]=(wchar_t)c;
625                 }
626                 info->Metadata[i]='\0';
627         }
628         else
629         {
630                 info->Metadata[0]='\0';
631         }
632
633         fclose(fd);
634
635         if(access(filename, W_OK))
636                 info->ReadOnly=true;
637
638         return SUCCESS;
639 #endif
640 }
641
642 bool8 S9xMovieActive ()
643 {
644         return (Movie.State!=MOVIE_STATE_NONE);
645 }
646
647 bool8 S9xMovieReadOnly ()
648 {
649         if(!S9xMovieActive())
650                 return false;
651
652         return Movie.ReadOnly;
653 }
654
655 uint32 S9xMovieGetId ()
656 {
657         if(!S9xMovieActive())
658                 return 0;
659
660         return Movie.MovieId;
661 }
662
663 uint32 S9xMovieGetLength ()
664 {
665         if(!S9xMovieActive())
666                 return 0;
667
668         return Movie.MaxFrame;
669 }
670
671 uint32 S9xMovieGetFrameCounter ()
672 {
673         if(!S9xMovieActive())
674                 return 0;
675
676         return Movie.CurrentFrame;
677 }
678
679 void S9xMovieToggleFrameDisplay ()
680 {
681         Movie.FrameDisplay = !Movie.FrameDisplay;
682         if(!Movie.FrameDisplay)
683         {
684                 GFX.InfoStringTimeout = 1;
685         }
686 }
687
688 void S9xMovieFreeze (uint8** buf, uint32* size)
689 {
690         // sanity check
691         if(!S9xMovieActive())
692         {
693                 return;
694         }
695
696         *buf = NULL;
697         *size = 0;
698
699         // compute size needed for the buffer
700         uint32 size_needed = 4*3;                       // room for MovieId, CurrentFrame, and MaxFrame
701         size_needed += (uint32)(Movie.BytesPerFrame * (Movie.MaxFrame+1));
702         *buf=new uint8[size_needed];
703         *size=size_needed;
704
705         uint8* ptr = *buf;
706         if(!ptr)
707         {
708                 return;
709         }
710
711         Write32(Movie.MovieId, ptr);
712         Write32(Movie.CurrentFrame, ptr);
713         Write32(Movie.MaxFrame, ptr);
714
715         memcpy(ptr, Movie.InputBuffer, Movie.BytesPerFrame * (Movie.MaxFrame+1));
716 }
717
718 bool8 S9xMovieUnfreeze (const uint8* buf, uint32 size)
719 {
720         // sanity check
721         if(!S9xMovieActive())
722         {
723                 return false;
724         }
725
726         const uint8* ptr = buf;
727         if(size < 4*3)
728         {
729                 return false;
730         }
731
732         uint32 movie_id = Read32(ptr);
733         uint32 current_frame = Read32(ptr);
734         uint32 max_frame = Read32(ptr);
735         uint32 space_needed = (Movie.BytesPerFrame * (max_frame+1));
736
737         if(movie_id != Movie.MovieId ||
738                 current_frame > max_frame ||
739                 space_needed > size)
740         {
741                 return false;
742         }
743
744         if(!Movie.ReadOnly)
745         {
746                 // here, we are going to take the input data from the savestate
747                 // and make it the input data for the current movie, then continue
748                 // writing new input data at the currentframe pointer
749                 change_state(MOVIE_STATE_RECORD);
750                 S9xMessage(S9X_INFO, S9X_MOVIE_INFO, MOVIE_INFO_RERECORD);
751
752                 Movie.CurrentFrame = current_frame;
753                 Movie.MaxFrame = max_frame;
754                 ++Movie.RerecordCount;
755
756                 reserve_buffer_space(space_needed);
757                 memcpy(Movie.InputBuffer, ptr, space_needed);
758                 flush_movie();
759                 fseek(Movie.File, Movie.ControllerDataOffset+(Movie.BytesPerFrame * (Movie.CurrentFrame+1)), SEEK_SET);
760         }
761         else
762         {
763                 // here, we are going to keep the input data from the movie file
764                 // and simply rewind to the currentframe pointer
765                 // this will cause a desync if the savestate is not in sync
766                 // with the on-disk recording data, but it's easily solved
767                 // by loading another savestate or playing the movie from the beginning
768
769                 // and older savestate might have a currentframe pointer past
770                 // the end of the input data, so check for that here
771                 if(current_frame > Movie.MaxFrame)
772                 {
773                         return false;
774                 }
775
776                 change_state(MOVIE_STATE_PLAY);
777                 S9xMessage(S9X_INFO, S9X_MOVIE_INFO, MOVIE_INFO_REWIND);
778
779                 Movie.CurrentFrame = current_frame;
780         }
781
782         Movie.InputBufferPtr = Movie.InputBuffer + (Movie.BytesPerFrame * Movie.CurrentFrame);
783         read_frame_controller_data();
784
785         return true;
786 }