/*************************************************************************** * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ * $Id$ * * Pacbox - a Pacman Emulator for Rockbox * * Based on PIE - Pacman Instructional Emulator * * Copyright (c) 1997-2003,2004 Alessandro Scotti * http://www.ascotti.org/ * * All files in this archive are subject to the GNU General Public License. * See the file COPYING in the source tree root for full license agreement. * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * ****************************************************************************/ #include "plugin.h" #include "arcade.h" #include "pacbox.h" #include "pacbox_lcd.h" #include "lib/configfile.h" PLUGIN_HEADER #ifdef USE_IRAM extern char iramcopy[]; extern char iramstart[]; extern char iramend[]; extern char iedata[]; extern char iend[]; #endif struct plugin_api* rb; struct pacman_settings { int difficulty; int numlives; int bonus; int ghostnames; int showfps; }; static struct pacman_settings settings; static struct pacman_settings old_settings; #define SETTINGS_VERSION 1 #define SETTINGS_MIN_VERSION 1 #define SETTINGS_FILENAME "pacbox.cfg" static char* difficulty_options[] = { "Normal", "Hard" }; static char* numlives_options[] = { "1", "2", "3", "5" }; static char* bonus_options[] = {"10000", "15000", "20000", "No Bonus"}; static char* ghostnames_options[] = {"Normal", "Alternate"}; static char* showfps_options[] = {"No", "Yes"}; static struct configdata config[] = { {TYPE_ENUM, 0, 2, &settings.difficulty, "Difficulty", difficulty_options, NULL}, {TYPE_ENUM, 0, 4, &settings.numlives, "Pacmen Per Game", numlives_options, NULL}, {TYPE_ENUM, 0, 4, &settings.bonus, "Bonus", bonus_options, NULL}, {TYPE_ENUM, 0, 2, &settings.ghostnames, "Ghost Names", ghostnames_options , NULL}, {TYPE_ENUM, 0, 2, &settings.showfps, "Show FPS", showfps_options, NULL}, }; static bool loadFile( const char * name, unsigned char * buf, int len ) { char filename[MAX_PATH]; rb->snprintf(filename,sizeof(filename),"/.rockbox/pacman/%s",name); int fd = rb->open( filename, O_RDONLY); if( fd < 0 ) { return false; } int n = rb->read( fd, buf, len); rb->close( fd ); if( n != len ) { return false; } return true; } static bool loadROMS( void ) { bool romsLoaded = false; romsLoaded = loadFile( "pacman.6e", ram_, 0x1000) && loadFile( "pacman.6f", ram_+0x1000, 0x1000) && loadFile( "pacman.6h", ram_+0x2000, 0x1000) && loadFile( "pacman.6j", ram_+0x3000, 0x1000) && loadFile( "pacman.5e", charset_rom_, 0x1000) && loadFile( "pacman.5f", spriteset_rom_, 0x1000); if( romsLoaded ) { decodeROMs(); reset_PacmanMachine(); } return romsLoaded; } /* A buffer to render Pacman's 244x288 screen into */ static unsigned char video_buffer[ScreenWidth*ScreenHeight] __attribute__ ((aligned (16))); static long start_time; static long video_frames = 0; static int dipDifficulty[] = { DipDifficulty_Normal, DipDifficulty_Hard }; static int dipLives[] = { DipLives_1, DipLives_2, DipLives_3, DipLives_5 }; static int dipBonus[] = { DipBonus_10000, DipBonus_15000, DipBonus_20000, DipBonus_None }; static int dipGhostNames[] = { DipGhostNames_Normal, DipGhostNames_Alternate }; static int settings_to_dip(struct pacman_settings settings) { return ( DipPlay_OneCoinOneGame | DipCabinet_Upright | DipMode_Play | DipRackAdvance_Off | dipDifficulty[settings.difficulty] | dipLives[settings.numlives] | dipBonus[settings.bonus] | dipGhostNames[settings.ghostnames] ); } static bool pacbox_menu(void) { int m; int result; int menu_quit=0; int new_setting; bool need_restart = false; static const struct opt_items noyes[2] = { { "No", -1 }, { "Yes", -1 }, }; static const struct opt_items difficulty_options[2] = { { "Normal", -1 }, { "Harder", -1 }, }; static const struct opt_items numlives_options[4] = { { "1", -1 }, { "2", -1 }, { "3", -1 }, { "5", -1 }, }; static const struct opt_items bonus_options[4] = { { "10000 points", -1 }, { "15000 points", -1 }, { "20000 points", -1 }, { "No bonus", -1 }, }; static const struct opt_items ghostname_options[2] = { { "Normal", -1 }, { "Alternate", -1 }, }; static const struct menu_item items[] = { { "Difficulty", NULL }, { "Pacmen Per Game", NULL }, { "Bonus Life", NULL }, { "Ghost Names", NULL }, { "Display FPS", NULL }, { "Restart", NULL }, { "Quit", NULL }, }; m = rb->menu_init(items, sizeof(items) / sizeof(*items), NULL, NULL, NULL, NULL); rb->button_clear_queue(); while (!menu_quit) { result=rb->menu_show(m); switch(result) { case 0: new_setting=settings.difficulty; rb->set_option("Difficulty", &new_setting, INT, difficulty_options , 2, NULL); if (new_setting != settings.difficulty) { settings.difficulty=new_setting; need_restart=true; } break; case 1: new_setting=settings.numlives; rb->set_option("Pacmen Per Game", &new_setting, INT, numlives_options , 4, NULL); if (new_setting != settings.numlives) { settings.numlives=new_setting; need_restart=true; } break; case 2: new_setting=settings.bonus; rb->set_option("Bonus Life", &new_setting, INT, bonus_options , 4, NULL); if (new_setting != settings.bonus) { settings.bonus=new_setting; need_restart=true; } break; case 3: new_setting=settings.ghostnames; rb->set_option("Ghost Names", &new_setting, INT, ghostname_options , 2, NULL); if (new_setting != settings.ghostnames) { settings.ghostnames=new_setting; need_restart=true; } break; case 4: /* Show FPS */ rb->set_option("Display FPS",&settings.showfps,INT, noyes, 2, NULL); break; case 5: /* Restart */ need_restart=true; menu_quit=1; break; default: menu_quit=1; break; } } rb->menu_exit(m); if (need_restart) { init_PacmanMachine(settings_to_dip(settings)); } /* Possible results: exit game restart game usb connected */ return (result==6); } /* Runs the game engine for one frame. */ static int gameProc( void ) { int x; int fps; char str[80]; int status; long end_time; int frame_counter = 0; int yield_counter = 0; while (1) { /* Run the machine for one frame (1/60th second) */ run(); frame_counter++; /* Check the button status */ status = rb->button_status(); #ifdef HAS_BUTTON_HOLD if (rb->button_hold()) status = PACMAN_MENU; #endif if ((status & PACMAN_MENU) == PACMAN_MENU #ifdef PACMAN_RC_MENU || status == PACMAN_RC_MENU #endif ) { end_time = *rb->current_tick; x = pacbox_menu(); rb->lcd_clear_display(); #ifdef HAVE_REMOTE_LCD rb->lcd_remote_clear_display(); rb->lcd_remote_update(); #endif if (x == 1) { return 1; } start_time += *rb->current_tick-end_time; } #ifdef PACMAN_HAS_REMOTE setDeviceMode( Joy1_Left, (status & PACMAN_LEFT || status == PACMAN_RC_LEFT) ? DeviceOn : DeviceOff); setDeviceMode( Joy1_Right, (status & PACMAN_RIGHT || status == PACMAN_RC_RIGHT) ? DeviceOn : DeviceOff); setDeviceMode( Joy1_Up, (status & PACMAN_UP || status == PACMAN_RC_UP) ? DeviceOn : DeviceOff); setDeviceMode( Joy1_Down, (status & PACMAN_DOWN || status == PACMAN_RC_DOWN) ? DeviceOn : DeviceOff); setDeviceMode( CoinSlot_1, (status & PACMAN_COIN || status == PACMAN_RC_COIN) ? DeviceOn : DeviceOff); setDeviceMode( Key_OnePlayer, (status & PACMAN_1UP || status == PACMAN_RC_1UP) ? DeviceOn : DeviceOff); setDeviceMode( Key_TwoPlayers, (status & PACMAN_2UP || status == PACMAN_RC_2UP) ? DeviceOn : DeviceOff); #else setDeviceMode( Joy1_Left, (status & PACMAN_LEFT) ? DeviceOn : DeviceOff); setDeviceMode( Joy1_Right, (status & PACMAN_RIGHT) ? DeviceOn : DeviceOff); setDeviceMode( Joy1_Up, (status & PACMAN_UP) ? DeviceOn : DeviceOff); setDeviceMode( Joy1_Down, (status & PACMAN_DOWN) ? DeviceOn : DeviceOff); setDeviceMode( CoinSlot_1, (status & PACMAN_COIN) ? DeviceOn : DeviceOff); setDeviceMode( Key_OnePlayer, (status & PACMAN_1UP) ? DeviceOn : DeviceOff); #ifdef PACMAN_2UP setDeviceMode( Key_TwoPlayers, (status & PACMAN_2UP) ? DeviceOn : DeviceOff); #endif #endif /* We only update the screen every third frame - Pacman's native framerate is 60fps, so we are attempting to display 20fps */ if (frame_counter == 60 / FPS) { frame_counter = 0; video_frames++; yield_counter ++; if (yield_counter == FPS) { yield_counter = 0; rb->yield (); } /* The following functions render the Pacman screen from the contents of the video and color ram. We first update the background, and then draw the Sprites on top. */ renderBackground( video_buffer ); renderSprites( video_buffer ); blit_display(rb->lcd_framebuffer,video_buffer); if (settings.showfps) { fps = (video_frames*HZ*100) / (*rb->current_tick-start_time); rb->snprintf(str,sizeof(str),"%d.%02d / %d fps ", fps/100,fps%100,FPS); rb->lcd_putsxy(0,0,str); } rb->lcd_update(); /* Keep the framerate at Pacman's 60fps */ end_time = start_time + (video_frames*HZ)/FPS; while (TIME_BEFORE(*rb->current_tick,end_time)) { rb->sleep(1); } } } return 0; } enum plugin_status plugin_start(struct plugin_api* api, void* parameter) { (void)parameter; #ifdef USE_IRAM void* audiobuf; int audiosize; #endif rb = api; #ifdef USE_IRAM /* We need to stop audio playback in order to use IRAM, so we grab the audio buffer - but we don't use it. */ audiobuf = rb->plugin_get_audio_buffer(&audiosize); rb->memcpy(iramstart, iramcopy, iramend-iramstart); rb->memset(iedata, 0, iend - iedata); #endif #ifdef HAVE_ADJUSTABLE_CPU_FREQ rb->cpu_boost(true); #endif rb->lcd_set_foreground(LCD_WHITE); rb->lcd_set_background(LCD_BLACK); rb->lcd_clear_display(); rb->lcd_update(); /* Set the default settings */ settings.difficulty = 0; /* Normal */ settings.numlives = 2; /* 3 lives */ settings.bonus = 0; /* 10000 points */ settings.ghostnames = 0; /* Normal names */ settings.showfps = 0; /* Do not show FPS */ configfile_init(rb); if (configfile_load(SETTINGS_FILENAME, config, sizeof(config)/sizeof(*config), SETTINGS_MIN_VERSION ) < 0) { /* If the loading failed, save a new config file (as the disk is already spinning) */ configfile_save(SETTINGS_FILENAME, config, sizeof(config)/sizeof(*config), SETTINGS_VERSION); } /* Keep a copy of the saved version of the settings - so we can check if the settings have changed when we quit */ old_settings = settings; /* Initialise the hardware */ init_PacmanMachine(settings_to_dip(settings)); /* Load the romset */ if (loadROMS()) { start_time = *rb->current_tick-1; gameProc(); /* Save the user settings if they have changed */ if (rb->memcmp(&settings,&old_settings,sizeof(settings))!=0) { rb->splash(0,true,"Saving settings..."); configfile_save(SETTINGS_FILENAME, config, sizeof(config)/sizeof(*config), SETTINGS_VERSION); } } else { rb->splash(HZ*2,true,"No ROMs in /.rockbox/pacman/"); } #ifdef HAVE_ADJUSTABLE_CPU_FREQ rb->cpu_boost(false); #endif return PLUGIN_OK; }