1 /*
2 This file is part of Yabause.
3
4 Yabause is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
8
9 Yabause is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with Yabause; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18
19 /*! \file movie.c
20 \brief Movie recording functions.
21 */
22
23 #include "peripheral.h"
24 #include "scsp.h"
25 #include "movie.h"
26 #include "cs2.h"
27 #include "vdp2.h" // for DisplayMessage() prototype
28 #include "yabause.h"
29 #include "error.h"
30
31 int RecordingFileOpened;
32 int PlaybackFileOpened;
33
34 struct MovieStruct Movie;
35
36 char MovieStatus[40];
37 int movieLoaded = 0; //Boolean value, 1 if a movie is playing or recording
38
39 //Counting
40 int framecounter;
41 int lagframecounter;
42 int LagFrameFlag;
43 int FrameAdvanceVariable=0;
44
45 int headersize=512;
46
47 //////////////////////////////////////////////////////////////////////////////
48
ReadHeader(FILE * fp)49 static void ReadHeader(FILE* fp) {
50 size_t num_read = 0;
51
52 fseek(fp, 0, SEEK_SET);
53
54 fseek(fp, 172, SEEK_SET);
55 num_read = fread(&Movie.Rerecords, sizeof(Movie.Rerecords), 1, fp);
56
57 fseek(fp, headersize, SEEK_SET);
58 }
59
60 //////////////////////////////////////////////////////////////////////////////
61
WriteHeader(FILE * fp)62 static void WriteHeader(FILE* fp) {
63
64 fseek(fp, 0, SEEK_SET);
65
66 fwrite("YMV", sizeof("YMV"), 1, fp);
67 fwrite(VERSION, sizeof(VERSION), 1, fp);
68 fwrite(cdip->cdinfo, sizeof(cdip->cdinfo), 1, fp);
69 fwrite(cdip->itemnum, sizeof(cdip->itemnum), 1, fp);
70 fwrite(cdip->version, sizeof(cdip->version), 1, fp);
71 fwrite(cdip->date, sizeof(cdip->date), 1, fp);
72 fwrite(cdip->gamename, sizeof(cdip->gamename), 1, fp);
73 fwrite(cdip->region, sizeof(cdip->region), 1, fp);
74 fwrite(&Movie.Rerecords, sizeof(Movie.Rerecords), 1, fp);
75 fwrite(&yabsys.emulatebios, sizeof(yabsys.emulatebios), 1, fp);
76 fwrite(&yabsys.IsPal, sizeof(yabsys.IsPal), 1, fp);
77
78 fseek(fp, headersize, SEEK_SET);
79 }
80
81 //////////////////////////////////////////////////////////////////////////////
82
ClearInput(void)83 static void ClearInput(void) {
84
85 //do something....
86 }
87
88 //////////////////////////////////////////////////////////////////////////////
89
90 const char* Buttons[8] = {"B", "C", "A", "S", "U", "D", "R", "L"};
91 const char* Spaces[8] = {" ", " ", " ", " ", " ", " ", " ", " "};
92 const char* Buttons2[8] = {"", "", "", "L", "Z", "Y", "X", "R"};
93 const char* Spaces2[8] = {"", "", "", " ", " ", " ", " ", " "};
94
95 char str[40];
96 char InputDisplayString[40];
97
SetInputDisplayCharacters(void)98 static void SetInputDisplayCharacters(void) {
99
100 int x;
101
102 strcpy(str, "");
103
104 for (x = 0; x < 8; x++) {
105
106 if(PORTDATA1.data[2] & (1 << x)) {
107 size_t spaces_len = strlen(Spaces[x]);
108 if (spaces_len >= 40)
109 return;
110 strcat(str, Spaces[x]);
111 }
112 else
113 {
114 size_t buttons_len = strlen(Buttons[x]);
115 if (buttons_len >= 40)
116 return;
117 strcat(str, Buttons[x]);
118 }
119
120 }
121
122 for (x = 0; x < 8; x++) {
123
124 if(PORTDATA1.data[3] & (1 << x)) {
125 size_t spaces2_len = strlen(Spaces2[x]);
126 if (spaces2_len >= 40)
127 return;
128 strcat(str, Spaces2[x]);
129 }
130 else
131 {
132 size_t buttons2_len = strlen(Buttons2[x]);
133 if (buttons2_len >= 40)
134 return;
135 strcat(str, Buttons2[x]);
136 }
137
138 }
139
140 strcpy(InputDisplayString, str);
141 }
142
143 //////////////////////////////////////////////////////////////////////////////
144
IncrementLagAndFrameCounter(void)145 static void IncrementLagAndFrameCounter(void)
146 {
147 if(LagFrameFlag == 1)
148 lagframecounter++;
149
150 framecounter++;
151 }
152
153 //////////////////////////////////////////////////////////////////////////////
154
155 int framelength=16;
156
DoMovie(void)157 void DoMovie(void) {
158
159 int x;
160 size_t num_read = 0;
161
162 if (Movie.Status == 0)
163 return;
164
165 IncrementLagAndFrameCounter();
166 LagFrameFlag=1;
167 SetInputDisplayCharacters();
168
169 //Read/Write Controller Data
170 if(Movie.Status == Recording) {
171 for (x = 0; x < 8; x++) {
172 fwrite(&PORTDATA1.data[x], 1, 1, Movie.fp);
173 }
174 for (x = 0; x < 8; x++) {
175 fwrite(&PORTDATA2.data[x], 1, 1, Movie.fp);
176 }
177 }
178
179 if(Movie.Status == Playback) {
180 for (x = 0; x < 8; x++) {
181 num_read = fread(&PORTDATA1.data[x], 1, 1, Movie.fp);
182 }
183 for (x = 0; x < 8; x++) {
184 num_read = fread(&PORTDATA2.data[x], 1, 1, Movie.fp);
185 }
186
187 //if we get to the end of the movie
188 if(((ftell(Movie.fp)-headersize)/framelength) >= Movie.Frames) {
189 fclose(Movie.fp);
190 PlaybackFileOpened=0;
191 Movie.Status = Stopped;
192 ClearInput();
193 strcpy(MovieStatus, "Playback Stopped");
194 }
195 }
196
197 //Stop Recording/Playback
198 if(Movie.Status != Recording && RecordingFileOpened) {
199 fclose(Movie.fp);
200 RecordingFileOpened=0;
201 Movie.Status = Stopped;
202 strcpy(MovieStatus, "Recording Stopped");
203 }
204
205 if(Movie.Status != Playback && PlaybackFileOpened && Movie.ReadOnly != 0) {
206 fclose(Movie.fp);
207 PlaybackFileOpened=0;
208 Movie.Status = Stopped;
209 strcpy(MovieStatus, "Playback Stopped");
210 }
211 }
212
213 //////////////////////////////////////////////////////////////////////////////
214
MovieLoadState(void)215 void MovieLoadState(void) {
216
217
218 if (Movie.ReadOnly == 1 && Movie.Status == Playback) {
219 //Movie.Status = Playback;
220 fseek (Movie.fp,headersize+(framecounter * framelength),SEEK_SET);
221 }
222
223 if(Movie.Status == Recording) {
224 fseek (Movie.fp,headersize+(framecounter * framelength),SEEK_SET);
225 Movie.Rerecords++;
226 }
227
228 if(Movie.Status == Playback && Movie.ReadOnly == 0) {
229 Movie.Status = Recording;
230 RecordingFileOpened=1;
231 strcpy(MovieStatus, "Recording Resumed");
232 TruncateMovie(Movie);
233 fseek (Movie.fp,headersize+(framecounter * framelength),SEEK_SET);
234 Movie.Rerecords++;
235 }
236 }
237
238 //////////////////////////////////////////////////////////////////////////////
239
TruncateMovie(struct MovieStruct Movie)240 void TruncateMovie(struct MovieStruct Movie) {
241
242 //when we resume recording, shorten the movie so that there isn't
243 //potential garbage data at the end
244
245 /*//TODO
246 struct MovieBufferStruct tempbuffer;
247 fseek(Movie.fp,0,SEEK_SET);
248 tempbuffer=ReadMovieIntoABuffer(Movie.fp);
249 fclose(Movie.fp);
250
251 //clear the file and write it again
252 Movie.fp=fopen(Movie.filename,"wb");
253 fwrite(tempbuffer.data,framelength,framecounter,Movie.fp);
254 fclose(Movie.fp);
255
256 Movie.fp=fopen(Movie.filename,"r+b");
257 */
258 }
259
260 //////////////////////////////////////////////////////////////////////////////
261
MovieGetSize(FILE * fp)262 static int MovieGetSize(FILE* fp) {
263 int size;
264 int fpos;
265
266 fpos = ftell(fp);//save current pos
267
268 if (fpos < 0)
269 {
270 YabSetError(YAB_ERR_OTHER, "MovieGetSize fpos is negative");
271 return 0;
272 }
273
274 fseek (fp,0,SEEK_END);
275 size=ftell(fp);
276
277 Movie.Frames=(size-headersize)/ framelength;
278
279 fseek(fp, fpos, SEEK_SET); //reset back to correct pos
280 return(size);
281 }
282
283 //////////////////////////////////////////////////////////////////////////////
284
MovieToggleReadOnly(void)285 void MovieToggleReadOnly(void) {
286
287 if(Movie.Status == Playback) {
288
289 if(Movie.ReadOnly == 1)
290 {
291 Movie.ReadOnly=0;
292 DisplayMessage("Movie is now read+write.");
293 }
294 else
295 {
296 Movie.ReadOnly=1;
297 DisplayMessage("Movie is now read only.");
298 }
299 }
300 }
301
302 //////////////////////////////////////////////////////////////////////////////
303
StopMovie(void)304 void StopMovie(void) {
305
306 if(Movie.Status == Recording && RecordingFileOpened) {
307 WriteHeader(Movie.fp);
308 fclose(Movie.fp);
309 RecordingFileOpened=0;
310 Movie.Status = Stopped;
311 ClearInput();
312 strcpy(MovieStatus, "Recording Stopped");
313 }
314
315 if(Movie.Status == Playback && PlaybackFileOpened && Movie.ReadOnly != 0) {
316 fclose(Movie.fp);
317 PlaybackFileOpened=0;
318 Movie.Status = Stopped;
319 ClearInput();
320 strcpy(MovieStatus, "Playback Stopped");
321 }
322 }
323
324 //////////////////////////////////////////////////////////////////////////////
325
SaveMovie(const char * filename)326 int SaveMovie(const char *filename) {
327
328 char* str=malloc(1024);
329
330 if(Movie.Status == Playback)
331 StopMovie();
332
333 if ((Movie.fp = fopen(filename, "w+b")) == NULL)
334 {
335 free(str);
336 return -1;
337 }
338
339 strcpy(str, filename);
340 Movie.filename=str;
341 RecordingFileOpened=1;
342 framecounter=0;
343 Movie.Status=Recording;
344 strcpy(MovieStatus, "Recording Started");
345 Movie.Rerecords=0;
346 WriteHeader(Movie.fp);
347 YabauseReset();
348 return 0;
349 }
350
351 //////////////////////////////////////////////////////////////////////////////
352
PlayMovie(const char * filename)353 int PlayMovie(const char *filename) {
354
355 char* str=malloc(1024);
356
357 if(Movie.Status == Recording)
358 StopMovie();
359
360
361 if ((Movie.fp = fopen(filename, "r+b")) == NULL)
362 {
363 free(str);
364 return -1;
365 }
366
367 strcpy(str, filename);
368 Movie.filename=str;
369 PlaybackFileOpened=1;
370 framecounter=0;
371 Movie.ReadOnly = 1;
372 Movie.Status=Playback;
373 Movie.Size = MovieGetSize(Movie.fp);
374 strcpy(MovieStatus, "Playback Started");
375 ReadHeader(Movie.fp);
376 YabauseReset();
377 return 0;
378 }
379
380 //////////////////////////////////////////////////////////////////////////////
381
SaveMovieInState(FILE * fp,IOCheck_struct check)382 void SaveMovieInState(FILE* fp, IOCheck_struct check) {
383
384 struct MovieBufferStruct tempbuffer;
385
386 fseek(fp, 0, SEEK_END);
387
388 if(Movie.Status == Recording || Movie.Status == Playback) {
389 tempbuffer=ReadMovieIntoABuffer(Movie.fp);
390
391 fwrite(&tempbuffer.size, 4, 1, fp);
392 fwrite(tempbuffer.data, tempbuffer.size, 1, fp);
393 }
394 }
395
396 //////////////////////////////////////////////////////////////////////////////
397
MovieReadState(FILE * fp)398 void MovieReadState(FILE* fp) {
399
400 ReadMovieInState(fp);
401 MovieLoadState();//file pointer and truncation
402
403 }
404
ReadMovieInState(FILE * fp)405 void ReadMovieInState(FILE* fp) {
406
407 struct MovieBufferStruct tempbuffer;
408 int fpos;
409 size_t num_read = 0;
410
411 //overwrite the main movie on disk if we are recording or read+write playback
412 if(Movie.Status == Recording || (Movie.Status == Playback && Movie.ReadOnly == 0)) {
413
414 fpos=ftell(fp);//where we are in the savestate
415
416 if (fpos < 0)
417 {
418 YabSetError(YAB_ERR_OTHER, "ReadMovieInState fpos is negative");
419 return;
420 }
421
422 num_read = fread(&tempbuffer.size, 4, 1, fp);//size
423 if ((tempbuffer.data = (char *)malloc(tempbuffer.size)) == NULL)
424 {
425 return;
426 }
427 num_read = fread(tempbuffer.data, 1, tempbuffer.size, fp);//movie
428 fseek(fp, fpos, SEEK_SET);//reset savestate position
429
430 rewind(Movie.fp);
431 fwrite(tempbuffer.data, 1, tempbuffer.size, Movie.fp);
432 rewind(Movie.fp);
433 }
434 }
435
436 //////////////////////////////////////////////////////////////////////////////
437
ReadMovieIntoABuffer(FILE * fp)438 struct MovieBufferStruct ReadMovieIntoABuffer(FILE* fp) {
439
440 int fpos;
441 struct MovieBufferStruct tempbuffer = { 0 };
442 size_t num_read = 0;
443
444 fpos = ftell(fp);//save current pos
445
446 if (fpos < 0)
447 {
448 YabSetError(YAB_ERR_OTHER, "ReadMovieIntoABuffer fpos is negative");
449 return tempbuffer;
450 }
451
452 fseek (fp,0,SEEK_END);
453 tempbuffer.size=ftell(fp); //get size
454 rewind(fp);
455
456 tempbuffer.data = (char*) malloc (sizeof(char)*tempbuffer.size);
457 num_read = fread(tempbuffer.data, 1, tempbuffer.size, fp);
458
459 fseek(fp, fpos, SEEK_SET); //reset back to correct pos
460 return(tempbuffer);
461 }
462
463 //////////////////////////////////////////////////////////////////////////////
464
MakeMovieStateName(const char * filename)465 const char *MakeMovieStateName(const char *filename) {
466
467 static char *retbuf = NULL; // Save the pointer to avoid memory leaks
468 if(Movie.Status == Recording || Movie.Status == Playback) {
469 const size_t newsize = strlen(filename) + 5 + 1;
470 free(retbuf);
471 retbuf = malloc(newsize);
472 if (!retbuf) {
473 return NULL; // out of memory
474 }
475 sprintf(retbuf, "%smovie", filename);
476 return retbuf;
477 } else {
478 return filename; // unchanged
479 }
480
481 }
482
483 //////////////////////////////////////////////////////////////////////////////
484
485 //debugging only
TestWrite(struct MovieBufferStruct tempbuffer)486 void TestWrite(struct MovieBufferStruct tempbuffer) {
487
488 FILE* tempbuffertest;
489
490 tempbuffertest=fopen("rmiab.txt", "wb");
491
492 if (!tempbuffertest)
493 return;
494
495 fwrite (tempbuffer.data, 1, tempbuffer.size, tempbuffertest);
496 fclose(tempbuffertest);
497 }
498
499 //////////////////////////////////////////////////////////////////////////////
500
PauseOrUnpause(void)501 void PauseOrUnpause(void) {
502
503 if(FrameAdvanceVariable == RunNormal) {
504 FrameAdvanceVariable=Paused;
505 ScspMuteAudio(SCSP_MUTE_SYSTEM);
506 }
507 else {
508 FrameAdvanceVariable=RunNormal;
509 ScspUnMuteAudio(SCSP_MUTE_SYSTEM);
510 }
511 }
512
513 //////////////////////////////////////////////////////////////////////////////
514
IsMovieLoaded(void)515 int IsMovieLoaded(void)
516 {
517 if (RecordingFileOpened || PlaybackFileOpened)
518 return 1;
519 else
520 return 0;
521 }
522
523 //////////////////////////////////////////////////////////////////////////////
524