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