1 
2 /*
3  *   O2EM Free Odyssey2 / Videopac+ Emulator
4  *
5  *   Created by Daniel Boris <dboris@comcast.net>  (c) 1997,1998
6  *
7  *   Developed by Andre de la Rocha <adlroc@users.sourceforge.net>
8  *
9  *   http://o2em.sourceforge.net
10  *
11  */
12 
13 
14 #include <dirent.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <ctype.h>
19 #include <time.h>
20 #include "crc32.h"
21 #include "audio.h"
22 #include "vmachine.h"
23 #include "config.h"
24 #include "vdc.h"
25 #include "cpu.h"
26 #include "debug.h"
27 #include "keyboard.h"
28 #include "voice.h"
29 #include "allegro.h"
30 #ifdef ALLEGRO_WINDOWS
31 #include "winalleg.h"
32 #endif
33 
34 
35 #define MAXC 1024
36 
37 
38 static char bios[MAXC], scshot[MAXC], xrom[MAXC], xbios[MAXC], arkivo[MAXC][MAXC], biossux[MAXC],
39        romssux[MAXC], odyssey2[MAXC], g7400[MAXC], c52[MAXC], jopac[MAXC];
40 char name_f, rom_f;
41 char pathx;
42 int file_name(char *pathx);
43 int suck_bios();
44 int suck_roms();
45 int contax;
46 unsigned long crcx = ~0;
47 
48 static long filesize(FILE *stream);
49 static void load_bios(const char *biosname);
50 static void load_cart(char *file);
51 int parse_option(char *attr, char *val);
52 void read_default_config(void);
53 
54 
main(int argc,char * argv[])55 int main(int argc, char *argv[]){
56 	int i;
57 	static char file[MAXC], attr[MAXC], val[MAXC], *p, *binver;
58 
59 	#if defined(ALLEGRO_WINDOWS)
60 	binver = "Windows binary";
61 	#elif defined(ALLEGRO_DOS)
62 	binver = "DOS binary";
63 	#elif defined(ALLEGRO_LINUX)
64 	binver = "Linux binary";
65 	#elif defined(ALLEGRO_BEOS)
66 	binver = "BEOS binary";
67 	#elif defined(ALLEGRO_QNX)
68 	binver = "QNX binary";
69 	#elif defined(ALLEGRO_UNIX)
70 	binver = "UNIX binary";
71 	#elif defined(ALLEGRO_MPW)
72 	binver = "MacOS binary";
73 	#else
74 	binver = "Unknown binary";
75 	#endif
76 
77 	printf("%s %s\n","\nO2EM v" O2EM_VERSION " " RELEASE_DATE "  - ", binver);
78 	printf("Free Odyssey2 / Videopac+ Emulator - http://o2em.sourceforge.net\n");
79 	printf("Developed by:Andre de la Rocha\n");
80 	printf("Created by Daniel Boris (c)1996/1998\n");
81 	printf("\n");
82 
83 	if (argc < 2) {
84 		printf("Use: o2em <file> [options]\n");
85 		printf("<file> = file to load with extension\n");
86 		#ifndef ALLEGRO_DOS
87 		printf("-wsize=n         Window size (1-4)\n");
88 		printf("-fullscreen      Full screen mode\n");
89 		#endif
90 		printf("-scanlines       Enable scanlines\n");
91 		printf("-nosound         Turn off sound emulation\n");
92 		printf("-novoice         Turn off voice emulation\n");
93 		printf("-svolume=n       Set sound volume (0-100)\n");
94 		printf("-vvolume=n       Set voice volume (0-100)\n");
95 		printf("-filter          Enable low-pass audio filter\n");
96 		printf("-debug	         Start the emulator in debug mode\n");
97 		printf("-speed=n         Relative speed (100 = original)\n");
98 		printf("-nolimit         Turn off speed limiter\n");
99 		printf("-bios=file       Set the O2 bios file name/dir (default=o2rom.bin)\n");
100 		printf("-scshot=file     Set the screenshot file name/template\n");
101 		printf("-euro            Use European timing / 50Hz mode\n");
102 		printf("-exrom           Use special 3K program/1K data ROM mode\n");
103 		printf("-3k              Use 3K rom mapping mode\n");
104 		printf("-s<n>=mode/keys  Define stick n mode/keys (n=1-2)\n");
105 		printf("-g7400           Set the VP+ bios\n");
106 		printf("-c52             Set the French O2 bios\n");
107 		printf("-jopac           Set the French VP+ bios\n");
108 
109 		printf("\nPress Enter...");
110 		fflush(stdout);
111 		getchar();
112 		exit(EXIT_SUCCESS);
113 	}
114 
115 	app_data.debug = 0;
116 	app_data.stick[0] = app_data.stick[1] = 1;
117 	set_defjoykeys(0,0);
118 	set_defjoykeys(1,1);
119 	app_data.bank = 0;
120 	app_data.limit = 1;
121 	app_data.sound_en = 1;
122 	app_data.speed = 100;
123 	app_data.wsize = 2;
124 	#ifdef ALLEGRO_DOS
125 	app_data.fullscreen = 1;
126 	#else
127 	app_data.fullscreen = 0;
128 	#endif
129 	app_data.scanlines = 0;
130 	app_data.voice = 1;
131 	app_data.window_title = "O2EM v" O2EM_VERSION;
132 	app_data.svolume = 100;
133 	app_data.vvolume = 100;
134 	app_data.filter = 0;
135 	app_data.exrom = 0;
136 	app_data.three_k = 0;
137 	app_data.crc = 0;
138 	app_data.scshot = scshot;
139 	app_data.euro = 0;
140 	app_data.openb = 0;
141 	app_data.vpp = 0;
142 
143 	strcpy(file,"");
144 	strcpy(bios,"");
145 	strcpy(scshot,"");
146     strcpy(xrom,"");
147 
148 	read_default_config();
149     strcpy(file,"roms/");
150 	for(i=1; i<argc; i++) {
151 		if (argv[i][0] != '-') 	{
152 			strncat(file,argv[i],MAXC-1);
153 			file[MAXC-1]=0;
154 		} else {
155 			p=strtok(argv[i],"=");
156 			if (p){
157 				strncpy(attr,p+1,MAXC-1);
158 				attr[MAXC-1]=0;
159 			} else
160 				strcpy(attr,"");
161 			p=strtok(NULL,"=");
162 			if (p){
163 				strncpy(val,p,MAXC-1);
164 				val[MAXC-1]=0;
165 			} else
166 				strcpy(val,"");
167 			strlwr(attr);
168 
169 			if (!parse_option(attr, val)) exit(EXIT_FAILURE);
170 
171 		}
172 
173     }
174 
175 	if (strlen(file)==0) {
176 		fprintf(stderr,"Error: file name missing\n");
177 		exit(EXIT_FAILURE);
178 	}
179 
180 	printf("Starting emulation ...\n");
181 
182 	allegro_init();
183 	install_timer();
184 	init_audio();
185 	printf("Using Allegro %s\n",allegro_id);
186 
187     strcpy (xrom, "roms/");
188     file_name(xrom);
189 
190     if (contax < 3)
191                  {
192                  printf("\nROMs directory empty!\n");
193                  exit(EXIT_FAILURE);
194                  }
195 
196     app_data.crc = crc32_file(file);
197     crcx = app_data.crc;
198     suck_roms();
199 
200     strcpy (xbios, "bios/");
201     file_name(xbios);
202     suck_bios();
203 
204     if (contax < 3)
205                  {
206                  printf("\nBIOS directory empty!\n");
207                  exit(EXIT_FAILURE);
208                  }
209 
210 
211     if ((rom_f!=1)&& (strcmp(bios,"jopac"))) strcpy(bios,g7400);
212 
213     if (!strcmp(bios,"g7400")) strcpy(bios,g7400);
214     if (!strcmp(bios,"c52")) strcpy(bios,c52);
215     if (!strcmp(bios,"jopac")) strcpy(bios,jopac);
216     if ((!strcmp(bios,"")) || (!strcmp(bios,"o2rom"))) strcpy(bios,"bios/o2rom.bin");
217 
218 
219 
220     load_bios(bios);
221 	load_cart(file);
222 
223 	if (app_data.voice) load_voice_samples();
224 
225 	init_display();
226 	init_cpu();
227 	init_system();
228 	if (app_data.debug) key_debug=1;
229 	#ifndef _DEBUG
230 	#ifdef ALLEGRO_WINDOWS
231 	FreeConsole();
232 	#endif
233 	#endif
234 	run();
235 	exit(EXIT_SUCCESS);
236 }
237 
238 END_OF_MAIN();
239 
240 
parse_option(char * attr,char * val)241 int parse_option(char *attr, char *val){
242 	int t;
243 
244 	if (!strcmp(attr,"nolimit")) {
245 		app_data.limit = !(val[0]!='0');
246 	} else if (!strcmp(attr,"nosound")) {
247 		app_data.sound_en = !(val[0]!='0');
248 	} else if (!strcmp(attr,"novoice")) {
249 		app_data.voice = !(val[0]!='0');
250 	} else if (!strcmp(attr,"filter")) {
251 		app_data.filter = (val[0]!='0');
252 	} else if (!strcmp(attr,"debug")) {
253 		app_data.debug = (val[0]!='0');
254 	} else if ((!strcmp(attr,"s1")) || (!strcmp(attr,"s2"))) {
255 		int sn;
256 		sn = (!strcmp(attr,"s1"))? 0 : 1;
257 		if (strlen(val)<2){
258 			t = -1;
259 			sscanf(val,"%d",&t);
260 			if ((t>=0) && (t<=3)) {
261 				if ((t==1)||(t==2)){
262 					app_data.stick[sn] = 1;
263 					set_defjoykeys(sn,t-1);
264 				} else {
265 					app_data.stick[sn] = (t==0) ? 0 : 2;
266 					set_joykeys(sn,0,0,0,0,0);
267 				}
268 			} else {
269 				fprintf(stderr,"Invalid value for option %s\n",attr);
270 				return 0;
271 			}
272 		} else {
273 			char *p,*s;
274 			int i,k,code,nk,codes[5];
275 			strupr(val);
276 			nk = 0;
277 			p = strtok(val,",");
278 			while (p) {
279 				i = code = 0;
280 				k = keybtab[i].keybcode;
281 				s = keybtab[i].keybname;
282 				while (k && (code==0)) {
283 					if (strcmp(s,p)==0) code = k;
284 					i++;
285 					k = keybtab[i].keybcode;
286 					s = keybtab[i].keybname;
287 				}
288 				if (!code) {
289 					fprintf(stderr,"Invalid value for option %s : key %s unknown\n",attr,p);
290 					return 0;
291 				}
292 				codes[nk] = code;
293 				p = strtok(NULL,",");
294 				nk++;
295 				if (nk>5) {
296 					fprintf(stderr,"Invalid value for option %s : invalid number of keys\n",attr);
297 					return 0;
298 				}
299 			}
300 			if (nk != 5) {
301 				fprintf(stderr,"Invalid value for option %s : invalid number of keys\n",attr);
302 				return 0;
303 			}
304 			app_data.stick[sn] = 1;
305 			set_joykeys(sn,codes[0],codes[1],codes[2],codes[3],codes[4]);
306 		}
307 	} else if (!strcmp(attr,"speed")) {
308 		t = -1;
309 		sscanf(val,"%d",&t);
310 		if ((t>0) && (t<=10000))
311 			app_data.speed = t;
312 		else {
313 			fprintf(stderr,"Invalid value for option %s\n",attr);
314 			return 0;
315 		}
316 	} else if (!strcmp(attr,"svolume")) {
317 		t = -1;
318 		sscanf(val,"%d",&t);
319 		if ((t>=0) && (t<=100))
320 			app_data.svolume = t;
321 		else {
322 			fprintf(stderr,"Invalid value for option %s\n",attr);
323 			return 0;
324 		}
325 		if (t==0) app_data.sound_en=0;
326 	} else if (!strcmp(attr,"vvolume")) {
327 		t = -1;
328 		sscanf(val,"%d",&t);
329 		if ((t>=0) && (t<=100))
330 			app_data.vvolume = t;
331 		else {
332 			fprintf(stderr,"Invalid value for option %s\n",attr);
333 			return 0;
334 		}
335 		if (t==0) app_data.voice=0;
336 	} else if (!strcmp(attr,"wsize")) {
337 		t = -1;
338 		sscanf(val,"%d",&t);
339 		if ((t>0) && (t<5)) {
340 			app_data.wsize = t;
341 			app_data.fullscreen = 0;
342 		} else {
343 			fprintf(stderr,"Invalid value for option %s\n",attr);
344 			return 0;
345 		}
346 	} else if (!strcmp(attr,"fullscreen")) {
347 		app_data.fullscreen = (val[0]!='0');
348 	} else if (!strcmp(attr,"scanlines")) {
349 		app_data.scanlines = (val[0]!='0');
350 	} else if (!strcmp(attr,"scshot")) {
351 		strcpy(scshot,val);
352 	} else if (!strcmp(attr,"euro")) {
353 		app_data.euro = (val[0]!='0');
354 	} else if (!strcmp(attr,"exrom")) {
355 		app_data.exrom = (val[0]!='0');
356 	} else if (!strcmp(attr,"3k")) {
357 		app_data.three_k = (val[0]!='0');
358 	} else if (!strcmp(attr,"g7400")){
359 		strcpy(bios,"g7400");
360 	}else if (!strcmp(attr,"c52")) {
361 		strcpy(bios,"c52");
362     }else if (!strcmp(attr,"jopac")) {
363 		strcpy(bios,"jopac");
364     }else if (!strcmp(attr,"o2rom")) {
365 		strcpy(bios,"o2rom");
366     } else if (!strcmp(attr,"bios")) {
367 		strcpy(bios,val);
368     } else {
369 		fprintf(stderr,"Invalid option : %s\n",attr);
370 		return 0;
371 	}
372 	return 1;
373 }
374 
375 
read_default_config(void)376 void read_default_config(void){
377 	FILE *f;
378 	static char attr[MAXC], val[MAXC], s[MAXC];
379 	char *p, *fn;
380 	int i,l;
381 
382 	fn = "o2em_def.cfg";
383 	f = fopen(fn,"r");
384 	if (!f) {
385 		fn = "O2EM_DEF.CFG";
386 		f = fopen(fn,"r");
387 	}
388 	if (!f) return;
389 
390 	l=0;
391 	while (fgets(s,MAXC-1,f)){
392 		l++;
393 		p=s;
394 		while (*p && (isspace(*p))) p++;
395 		if (*p && (*p != '#')) {
396 			i=0;
397 			while (*p && (!isspace(*p)) && (*p != '=')) attr[i++] = *p++;
398 			attr[i]=0;
399 			while (*p && (isspace(*p))) p++;
400 			i=0;
401 			if (*p == '='){
402 				p++;
403 				while (*p && (isspace(*p))) p++;
404 				if (*p == '"'){
405 					p++;
406 					while (*p && (*p != '"') && (*p != '\n') && (*p != '\r')) val[i++] = *p++;
407 				} else {
408 					while (*p && (!isspace(*p))) val[i++] = *p++;
409 				}
410 			}
411 			val[i]=0;
412 			if (strlen(attr)>0) {
413 				strlwr(attr);
414 				if (!parse_option(attr,val)) {
415 					printf("Error in the %s file at line number %d !\n\n",fn,l);
416 				}
417 			}
418 		}
419 	}
420 	fclose(f);
421 }
422 
423 
filesize(FILE * stream)424 static long filesize(FILE *stream){
425    long curpos, length;
426 
427    curpos = ftell(stream);
428    fseek(stream, 0L, SEEK_END);
429    length = ftell(stream);
430    fseek(stream, curpos, SEEK_SET);
431    return length;
432 }
433 
434 /****************************************************************/
435 
load_bios(const char * biosname)436 static void load_bios(const char *biosname){
437 	FILE *fn;
438 	static char s[MAXC+10];
439 	unsigned long crc;
440 	int i;
441 
442     if (!strcmp(biosname,"g7400"))
443     {
444 	if ((!biosname) || (strlen(biosname)==0)) {
445 		strcpy(s,"o2rom.bin");
446         fn = fopen("o2rom.bin","rb");
447 		if (!fn) fn = fopen("O2ROM.BIN","rb");
448 	} else if ((biosname[strlen(biosname)-1]=='/') || (biosname[strlen(biosname)-1]=='\\') || (biosname[strlen(biosname)-1]==':')) {
449 		strcpy(s,biosname);
450 		strcat(s,"O2ROM.BIN");
451 		fn = fopen(s,"rb");
452 		if (!fn) {
453 			strcpy(s,biosname);
454 			strcat(s,"o2rom.bin");
455 			fn = fopen(s,"rb");
456 		}
457 	} else {
458 		strcpy(s,biosname);
459 		fn = fopen(biosname,"rb");
460 	}
461 
462 	if (!fn) {
463 		fprintf(stderr,"Error loading bios ROM (%s)\n",s);
464 		exit(EXIT_FAILURE);
465 	}
466 
467  	if (fread(rom_table[0],1024,1,fn) != 1) {
468  		fprintf(stderr,"Error loading bios ROM o2rom.bin\n");
469  		exit(EXIT_FAILURE);
470  	}
471     }
472 	    strcpy(s,biosname);
473 		fn = fopen(biosname,"rb");
474 
475     if (!fn) {
476 		fprintf(stderr,"Error loading bios ROM (%s)\n",s);
477 		exit(EXIT_FAILURE);
478 	}
479 
480  	if (fread(rom_table[0],1024,1,fn) != 1) {
481  		fprintf(stderr,"Error loading bios ROM o2rom.bin\n");
482  		exit(EXIT_FAILURE);
483  	}
484 
485     fclose(fn);
486 
487 	for (i=1; i<8; i++) memcpy(rom_table[i],rom_table[0],1024);
488 
489 	crc = crc32_buf(rom_table[0],1024);
490 
491 	if (crc==0x8016A315) {
492 		printf("Odyssey2 bios ROM loaded\n");
493 		app_data.vpp = 0;
494 	} else if (crc==0xE20A9F41) {
495 		printf("Videopac+ G7400 bios ROM loaded\n");
496 		app_data.vpp = 1;
497 	} else if (crc==0xA318E8D6) {
498 		printf("C52 bios ROM loaded\n");
499 		app_data.vpp = 0;
500 	} else if (crc==0x11647CA5) {
501 		printf("Jopac bios ROM loaded\n");
502 		app_data.vpp = 1;
503 	} else {
504 		printf("Bios ROM loaded (unknown version)\n");
505 		app_data.vpp = 0;
506 	}
507 }
508 
509 
load_cart(char * file)510 static void load_cart(char *file){
511 	FILE *fn;
512 	long l;
513 	int i, nb;
514 
515 	app_data.crc = crc32_file(file);
516 
517 	if (app_data.crc == 0xAFB23F89) app_data.exrom = 1;  /* Musician */
518 	if (app_data.crc == 0x3BFEF56B) app_data.exrom = 1;  /* Four in 1 Row! */
519 	if (app_data.crc == 0x9B5E9356) app_data.exrom = 1;  /* Four in 1 Row! (french) */
520 
521 	if ((app_data.crc == 0x4E2CC6D3) || (app_data.crc == 0xBCF1EFC9)) app_data.three_k = 1;  /* Kill the Attacking Aliens */
522 
523 	if (((app_data.crc == 0x975AB8DA) || (app_data.crc == 0xE246A812)) && (!app_data.debug)) {
524 		fprintf(stderr,"Error: file %s is an incomplete ROM dump\n",file);
525 		exit(EXIT_FAILURE);
526 	}
527 
528 	fn=fopen(file,"rb");
529 	if (!fn) {
530 		fprintf(stderr,"Error loading %s\n",file);
531 		exit(EXIT_FAILURE);
532 	}
533 	printf("Loading: \"%s\"  Size: ",file);
534 	l = filesize(fn);
535 
536 	if ((l % 1024) != 0) {
537 		fprintf(stderr,"Error: file %s is an invalid ROM dump\n",file);
538 		exit(EXIT_FAILURE);
539 	}
540 
541 	if (((l % 3072) == 0) && app_data.three_k) {
542 
543 		nb = l/3072;
544 
545 		for (i=nb-1; i>=0; i--) {
546 			if (fread(&rom_table[i][1024],3072,1,fn) != 1) {
547 				fprintf(stderr,"Error loading %s\n",file);
548 				exit(EXIT_FAILURE);
549 			}
550 		}
551 		printf("%dK",nb*2);
552 
553 	} else {
554 
555 		nb = l/2048;
556 
557 		if ((nb == 2) && (app_data.exrom)) {
558 
559 			if (fread(&extROM[0], 1024,1,fn) != 1) {
560 				fprintf(stderr,"Error loading %s\n",file);
561 				exit(EXIT_FAILURE);
562 			}
563 			if (fread(&rom_table[0][1024],3072,1,fn) != 1) {
564 				fprintf(stderr,"Error loading %s\n",file);
565 				exit(EXIT_FAILURE);
566 			}
567 			printf("3K EXROM");
568 
569 		} else {
570 
571 			for (i=nb-1; i>=0; i--) {
572 				if (fread(&rom_table[i][1024],2048,1,fn) != 1) {
573 					fprintf(stderr,"Error loading %s\n",file);
574 					exit(EXIT_FAILURE);
575 				}
576 				memcpy(&rom_table[i][3072],&rom_table[i][2048],1024); /* simulate missing A10 */
577 			}
578 			printf("%dK",nb*2);
579 
580 		}
581 	}
582 	fclose(fn);
583 
584 	rom = rom_table[0];
585 
586 	if (nb==1)
587 			app_data.bank = 1;
588 	else if (nb==2)
589 		app_data.bank = app_data.exrom ? 1 : 2;
590 	else if (nb==4)
591 		app_data.bank = 3;
592 	else
593 		app_data.bank = 4;
594 
595 	if ((rom_table[nb-1][1024+12]=='O') && (rom_table[nb-1][1024+13]=='P') && (rom_table[nb-1][1024+14]=='N') && (rom_table[nb-1][1024+15]=='B')) app_data.openb=1;
596 
597 	printf("  CRC: %08lX\n",app_data.crc);
598 
599 }
600 
601 /************************************[Open the directory `pathx�]****/
602 
file_name(char * pathx)603 int file_name(char *pathx)
604 {
605 
606     DIR           *dir_p;
607     struct dirent *dir_entry_p;
608     contax=0;
609 
610     dir_p = opendir(pathx);
611 
612 while(0 != (dir_entry_p = readdir(dir_p)))
613 
614     {
615 
616         strcpy(arkivo[contax], dir_entry_p->d_name);
617         contax++;
618     }
619 
620         closedir(dir_p);
621         return(0);
622 
623 }
624 
suck_bios()625 int suck_bios()
626 {
627     int i;
628     for (i=0; i<contax; ++i)
629         {
630 
631                  strcpy(biossux,"bios/");
632                  strcat(biossux,arkivo[i]);
633 
634                  app_data.crc = crc32_file(biossux);
635                  if (app_data.crc == 0x8016A315) strcpy(odyssey2, biossux);
636                  if (app_data.crc == 0xE20A9F41) strcpy(g7400, biossux);
637                  if (app_data.crc == 0xA318E8D6) strcpy(c52, biossux);
638                  if (app_data.crc == 0x11647CA5) strcpy(jopac, biossux);
639         }
640         return(0);
641 
642 }
643 
644 
suck_roms()645 int suck_roms()
646 {
647     int i;
648     rom_f = 1;
649 
650     for (i=0; i<contax; ++i)
651         {
652 
653                  strcpy(romssux,"roms/");
654                  strcat(romssux,arkivo[i]);
655                  app_data.crc = crc32_file(romssux);
656                  if (app_data.crc == crcx)
657                                   {
658                                   if ((app_data.crc == 0xD7089068)||(app_data.crc == 0xB0A7D723)||
659                                   (app_data.crc == 0x0CA26992)||(app_data.crc == 0x0B6EB25B)||
660                                   (app_data.crc == 0x06861A9C)||(app_data.crc == 0xB2F0F0B4)||
661                                   (app_data.crc == 0x68560DC7)||(app_data.crc == 0x0D2D721D)||
662                                   (app_data.crc == 0xC4134DF8)||(app_data.crc == 0xA75C42F8))
663                                   rom_f = 0;
664                                   }
665 
666         }
667 return(0);
668 }
669