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