Prepare new maemo release
[maemo-rb.git] / apps / plugins / pacbox / pacbox.c
blobefba47b5763a0218117cc77e6e3e694f404ee5ed
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Pacbox - a Pacman Emulator for Rockbox
12 * Based on PIE - Pacman Instructional Emulator
14 * Copyright (c) 1997-2003,2004 Alessandro Scotti
15 * http://www.ascotti.org/
17 * This program is free software; you can redistribute it and/or
18 * modify it under the terms of the GNU General Public License
19 * as published by the Free Software Foundation; either version 2
20 * of the License, or (at your option) any later version.
22 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
23 * KIND, either express or implied.
25 ****************************************************************************/
27 #include "plugin.h"
28 #include "arcade.h"
29 #include "pacbox.h"
30 #include "pacbox_lcd.h"
31 #include "wsg3.h"
32 #include "lib/configfile.h"
33 #include "lib/playback_control.h"
35 struct pacman_settings {
36 int difficulty;
37 int numlives;
38 int bonus;
39 int ghostnames;
40 int showfps;
41 int sound;
44 static struct pacman_settings settings;
45 static struct pacman_settings old_settings;
46 static bool sound_playing = false;
48 #define SETTINGS_VERSION 1
49 #define SETTINGS_MIN_VERSION 1
50 #define SETTINGS_FILENAME "pacbox.cfg"
52 static char* difficulty_options[] = { "Normal", "Hard" };
53 static char* numlives_options[] = { "1", "2", "3", "5" };
54 static char* bonus_options[] = {"10000", "15000", "20000", "No Bonus"};
55 static char* ghostnames_options[] = {"Normal", "Alternate"};
56 static char* yesno_options[] = {"No", "Yes"};
58 static struct configdata config[] =
60 {TYPE_ENUM, 0, 2, { .int_p = &settings.difficulty }, "Difficulty",
61 difficulty_options},
62 {TYPE_ENUM, 0, 4, { .int_p = &settings.numlives }, "Pacmen Per Game",
63 numlives_options},
64 {TYPE_ENUM, 0, 4, { .int_p = &settings.bonus }, "Bonus", bonus_options},
65 {TYPE_ENUM, 0, 2, { .int_p = &settings.ghostnames }, "Ghost Names",
66 ghostnames_options},
67 {TYPE_ENUM, 0, 2, { .int_p = &settings.showfps }, "Show FPS",
68 yesno_options},
69 {TYPE_ENUM, 0, 2, { .int_p = &settings.sound }, "Sound",
70 yesno_options},
73 static bool loadFile( const char * name, unsigned char * buf, int len )
75 char filename[MAX_PATH];
77 rb->snprintf(filename,sizeof(filename), ROCKBOX_DIR "/pacman/%s",name);
79 int fd = rb->open( filename, O_RDONLY);
81 if( fd < 0 ) {
82 return false;
85 int n = rb->read( fd, buf, len);
87 rb->close( fd );
89 if( n != len ) {
90 return false;
93 return true;
96 static bool loadROMS( void )
98 bool romsLoaded = false;
100 romsLoaded = loadFile( "pacman.6e", ram_, 0x1000) &&
101 loadFile( "pacman.6f", ram_+0x1000, 0x1000) &&
102 loadFile( "pacman.6h", ram_+0x2000, 0x1000) &&
103 loadFile( "pacman.6j", ram_+0x3000, 0x1000) &&
104 loadFile( "pacman.5e", charset_rom_, 0x1000) &&
105 loadFile( "pacman.5f", spriteset_rom_, 0x1000);
107 if( romsLoaded ) {
108 decodeROMs();
109 reset_PacmanMachine();
112 return romsLoaded;
115 /* A buffer to render Pacman's 244x288 screen into */
116 static unsigned char video_buffer[ScreenWidth*ScreenHeight] __attribute__ ((aligned (16)));
118 static long start_time;
119 static long video_frames = 0;
121 static int dipDifficulty[] = { DipDifficulty_Normal, DipDifficulty_Hard };
122 static int dipLives[] = { DipLives_1, DipLives_2, DipLives_3, DipLives_5 };
123 static int dipBonus[] = { DipBonus_10000, DipBonus_15000, DipBonus_20000,
124 DipBonus_None };
125 static int dipGhostNames[] = { DipGhostNames_Normal, DipGhostNames_Alternate };
127 static int settings_to_dip(struct pacman_settings settings)
129 return ( DipPlay_OneCoinOneGame |
130 DipCabinet_Upright |
131 DipMode_Play |
132 DipRackAdvance_Off |
134 dipDifficulty[settings.difficulty] |
135 dipLives[settings.numlives] |
136 dipBonus[settings.bonus] |
137 dipGhostNames[settings.ghostnames]
141 static bool pacbox_menu(void)
143 int selected=0;
144 int result;
145 int menu_quit=0;
146 int new_setting;
147 bool need_restart = false;
149 static const struct opt_items noyes[2] = {
150 { "No", -1 },
151 { "Yes", -1 },
154 static const struct opt_items difficulty_options[2] = {
155 { "Normal", -1 },
156 { "Harder", -1 },
159 static const struct opt_items numlives_options[4] = {
160 { "1", -1 },
161 { "2", -1 },
162 { "3", -1 },
163 { "5", -1 },
166 static const struct opt_items bonus_options[4] = {
167 { "10000 points", -1 },
168 { "15000 points", -1 },
169 { "20000 points", -1 },
170 { "No bonus", -1 },
173 static const struct opt_items ghostname_options[2] = {
174 { "Normal", -1 },
175 { "Alternate", -1 },
178 enum
180 PBMI_DIFFICULTY = 0,
181 PBMI_PACMEN_PER_GAME,
182 PBMI_BONUS_LIFE,
183 PBMI_GHOST_NAMES,
184 PBMI_DISPLAY_FPS,
185 PBMI_SOUND,
186 PBMI_RESTART,
187 PBMI_QUIT,
190 MENUITEM_STRINGLIST(menu, "Pacbox Menu", NULL,
191 "Difficulty", "Pacmen Per Game", "Bonus Life",
192 "Ghost Names", "Display FPS", "Sound",
193 "Restart", "Quit");
195 rb->button_clear_queue();
197 #if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_PAL256)
198 rb->lcd_set_mode(LCD_MODE_RGB565);
199 #endif
201 while (!menu_quit) {
202 result=rb->do_menu(&menu, &selected, NULL, false);
204 switch(result)
206 case PBMI_DIFFICULTY:
207 new_setting=settings.difficulty;
208 rb->set_option("Difficulty", &new_setting, INT,
209 difficulty_options , 2, NULL);
210 if (new_setting != settings.difficulty) {
211 settings.difficulty=new_setting;
212 need_restart=true;
214 break;
215 case PBMI_PACMEN_PER_GAME:
216 new_setting=settings.numlives;
217 rb->set_option("Pacmen Per Game", &new_setting, INT,
218 numlives_options , 4, NULL);
219 if (new_setting != settings.numlives) {
220 settings.numlives=new_setting;
221 need_restart=true;
223 break;
224 case PBMI_BONUS_LIFE:
225 new_setting=settings.bonus;
226 rb->set_option("Bonus Life", &new_setting, INT,
227 bonus_options , 4, NULL);
228 if (new_setting != settings.bonus) {
229 settings.bonus=new_setting;
230 need_restart=true;
232 break;
233 case PBMI_GHOST_NAMES:
234 new_setting=settings.ghostnames;
235 rb->set_option("Ghost Names", &new_setting, INT,
236 ghostname_options , 2, NULL);
237 if (new_setting != settings.ghostnames) {
238 settings.ghostnames=new_setting;
239 need_restart=true;
241 break;
242 case PBMI_DISPLAY_FPS:
243 rb->set_option("Display FPS",&settings.showfps,INT,
244 noyes, 2, NULL);
245 break;
246 case PBMI_SOUND:
247 rb->set_option("Sound",&settings.sound, INT,
248 noyes, 2, NULL);
249 break;
250 case PBMI_RESTART:
251 need_restart=true;
252 menu_quit=1;
253 break;
254 default:
255 menu_quit=1;
256 break;
260 #if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_PAL256)
261 rb->lcd_set_mode(LCD_MODE_PAL256);
262 #endif
264 if (need_restart) {
265 init_PacmanMachine(settings_to_dip(settings));
268 /* Possible results:
269 exit game
270 restart game
271 usb connected
273 return (result==PBMI_QUIT);
276 /* Sound is emulated in ISR context, so not much is done per sound frame */
277 #define NBSAMPLES 128
278 static uint32_t sound_buf[NBSAMPLES];
279 #if CONFIG_CPU == MCF5249
280 /* Not enough to put this in IRAM */
281 static int16_t raw_buf[NBSAMPLES];
282 #else
283 static int16_t raw_buf[NBSAMPLES] IBSS_ATTR;
284 #endif
287 Audio callback
289 static void get_more(const void **start, size_t *size)
291 int32_t *out, *outend;
292 int16_t *raw;
294 /* Emulate the audio for the current register settings */
295 playSound(raw_buf, NBSAMPLES);
297 out = sound_buf;
298 outend = out + NBSAMPLES;
299 raw = raw_buf;
301 /* Convert to stereo */
304 uint32_t sample = (uint16_t)*raw++;
305 *out++ = sample | (sample << 16);
307 while (out < outend);
309 *start = sound_buf;
310 *size = NBSAMPLES*sizeof(sound_buf[0]);
314 Start the sound emulation
316 static void start_sound(void)
318 int sr_index;
320 if (sound_playing)
321 return;
323 #ifndef PLUGIN_USE_IRAM
324 /* Ensure control of PCM - stopping music isn't obligatory */
325 rb->plugin_get_audio_buffer(NULL);
326 #endif
328 /* Get the closest rate >= to what is preferred */
329 sr_index = rb->round_value_to_list32(PREFERRED_SAMPLING_RATE,
330 rb->hw_freq_sampr, HW_NUM_FREQ, false);
332 if (rb->hw_freq_sampr[sr_index] < PREFERRED_SAMPLING_RATE
333 && sr_index > 0)
335 /* Round up */
336 sr_index--;
339 wsg3_set_sampling_rate(rb->hw_freq_sampr[sr_index]);
341 rb->pcm_set_frequency(rb->hw_freq_sampr[sr_index]);
342 rb->pcm_play_data(get_more, NULL, NULL, 0);
344 sound_playing = true;
348 Stop the sound emulation
350 static void stop_sound(void)
352 if (!sound_playing)
353 return;
355 rb->pcm_play_stop();
356 rb->pcm_set_frequency(HW_SAMPR_DEFAULT);
358 sound_playing = false;
362 Runs the game engine for one frame.
364 static int gameProc( void )
366 int fps;
367 int status;
368 long end_time;
369 int frame_counter = 0;
370 int yield_counter = 0;
372 if (settings.sound)
373 start_sound();
375 while (1)
377 /* Run the machine for one frame (1/60th second) */
378 run();
380 frame_counter++;
382 /* Check the button status */
383 status = rb->button_status();
385 #ifdef HAS_BUTTON_HOLD
386 if (rb->button_hold())
387 status = PACMAN_MENU;
388 #endif
390 if ((status & PACMAN_MENU) == PACMAN_MENU
391 #ifdef PACMAN_RC_MENU
392 || status == PACMAN_RC_MENU
393 #endif
395 bool menu_res;
397 end_time = *rb->current_tick;
399 stop_sound();
401 menu_res = pacbox_menu();
403 rb->lcd_clear_display();
404 #ifdef HAVE_REMOTE_LCD
405 rb->lcd_remote_clear_display();
406 rb->lcd_remote_update();
407 #endif
408 if (menu_res)
409 return 1;
411 if (settings.sound)
412 start_sound();
414 start_time += *rb->current_tick-end_time;
417 #ifdef PACMAN_HAS_REMOTE
418 setDeviceMode( Joy1_Left, (status & PACMAN_LEFT || status == PACMAN_RC_LEFT) ? DeviceOn : DeviceOff);
419 setDeviceMode( Joy1_Right, (status & PACMAN_RIGHT || status == PACMAN_RC_RIGHT) ? DeviceOn : DeviceOff);
420 setDeviceMode( Joy1_Up, (status & PACMAN_UP || status == PACMAN_RC_UP) ? DeviceOn : DeviceOff);
421 setDeviceMode( Joy1_Down, (status & PACMAN_DOWN || status == PACMAN_RC_DOWN) ? DeviceOn : DeviceOff);
422 setDeviceMode( CoinSlot_1, (status & PACMAN_COIN || status == PACMAN_RC_COIN) ? DeviceOn : DeviceOff);
423 setDeviceMode( Key_OnePlayer, (status & PACMAN_1UP || status == PACMAN_RC_1UP) ? DeviceOn : DeviceOff);
424 setDeviceMode( Key_TwoPlayers, (status & PACMAN_2UP || status == PACMAN_RC_2UP) ? DeviceOn : DeviceOff);
425 #else
426 setDeviceMode( Joy1_Left, (status & PACMAN_LEFT) ? DeviceOn : DeviceOff);
427 setDeviceMode( Joy1_Right, (status & PACMAN_RIGHT) ? DeviceOn : DeviceOff);
428 setDeviceMode( Joy1_Up, (status & PACMAN_UP) ? DeviceOn : DeviceOff);
429 setDeviceMode( Joy1_Down, (status & PACMAN_DOWN) ? DeviceOn : DeviceOff);
430 setDeviceMode( CoinSlot_1, (status & PACMAN_COIN) ? DeviceOn : DeviceOff);
431 setDeviceMode( Key_OnePlayer, (status & PACMAN_1UP) ? DeviceOn : DeviceOff);
432 #ifdef PACMAN_2UP
433 setDeviceMode( Key_TwoPlayers, (status & PACMAN_2UP) ? DeviceOn : DeviceOff);
434 #endif
435 #endif
437 /* We only update the screen every third frame - Pacman's native
438 framerate is 60fps, so we are attempting to display 20fps */
439 if (frame_counter == 60 / FPS) {
441 frame_counter = 0;
442 video_frames++;
444 yield_counter ++;
446 if (yield_counter == FPS) {
447 yield_counter = 0;
448 rb->yield ();
451 /* The following functions render the Pacman screen from the
452 contents of the video and color ram. We first update the
453 background, and then draw the Sprites on top.
456 renderBackground( video_buffer );
457 renderSprites( video_buffer );
459 #if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_PAL256)
460 rb->lcd_blit_pal256( video_buffer, 0, 0, XOFS, YOFS,
461 ScreenWidth, ScreenHeight);
462 #else
463 blit_display(rb->lcd_framebuffer,video_buffer);
464 #endif
466 if (settings.showfps) {
467 fps = (video_frames*HZ*100) / (*rb->current_tick-start_time);
468 rb->lcd_putsxyf(0,0,"%d.%02d / %d fps ",fps/100,fps%100,FPS);
471 #if !defined(HAVE_LCD_MODES) || \
472 defined(HAVE_LCD_MODES) && !(HAVE_LCD_MODES & LCD_MODE_PAL256)
473 rb->lcd_update();
474 #endif
476 /* Keep the framerate at Pacman's 60fps */
477 end_time = start_time + (video_frames*HZ)/FPS;
478 while (TIME_BEFORE(*rb->current_tick,end_time)) {
479 rb->sleep(1);
484 stop_sound();
486 return 0;
489 enum plugin_status plugin_start(const void* parameter)
491 (void)parameter;
493 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
494 rb->cpu_boost(true);
495 #endif
496 rb->lcd_set_backdrop(NULL);
497 rb->lcd_set_foreground(LCD_WHITE);
498 rb->lcd_set_background(LCD_BLACK);
499 rb->lcd_clear_display();
500 rb->lcd_update();
502 /* Set the default settings */
503 settings.difficulty = 0; /* Normal */
504 settings.numlives = 2; /* 3 lives */
505 settings.bonus = 0; /* 10000 points */
506 settings.ghostnames = 0; /* Normal names */
507 settings.showfps = 0; /* Do not show FPS */
508 settings.sound = 0; /* Sound off by default */
510 if (configfile_load(SETTINGS_FILENAME, config,
511 sizeof(config)/sizeof(*config),
512 SETTINGS_MIN_VERSION
513 ) < 0)
515 /* If the loading failed, save a new config file (as the disk is
516 already spinning) */
517 configfile_save(SETTINGS_FILENAME, config,
518 sizeof(config)/sizeof(*config),
519 SETTINGS_VERSION);
522 /* Keep a copy of the saved version of the settings - so we can check if
523 the settings have changed when we quit */
524 old_settings = settings;
526 /* Initialise the hardware */
527 init_PacmanMachine(settings_to_dip(settings));
529 #if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_PAL256)
530 rb->lcd_set_mode(LCD_MODE_PAL256);
531 #endif
533 /* Load the romset */
534 if (loadROMS()) {
535 start_time = *rb->current_tick-1;
537 gameProc();
539 /* Save the user settings if they have changed */
540 if (rb->memcmp(&settings,&old_settings,sizeof(settings))!=0) {
541 rb->splash(0, "Saving settings...");
542 configfile_save(SETTINGS_FILENAME, config,
543 sizeof(config)/sizeof(*config),
544 SETTINGS_VERSION);
546 } else {
547 rb->splashf(HZ*2, "No ROMs in %s/pacman/", ROCKBOX_DIR);
550 #if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_PAL256)
551 rb->lcd_set_mode(LCD_MODE_RGB565);
552 #endif
554 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
555 rb->cpu_boost(false);
556 #endif
558 return PLUGIN_OK;